Создание каталога, защищенного от 'rm -rf'


9

Я только что потерял некоторые данные в папке A, которая была внутри папки B после выполнения rm -rf B. Прежде чем я понял, что я сделал, все было кончено. Теперь урок усвоен, я хочу сделать часть своей папки защищенной от идиота, чтобы в следующий раз избежать этого, когда я сделаю что-то подобное и захочу покончить с собой.

Один из способов, который я могу придумать, - написать функцию bash и присвоить ей псевдоним rm. Эта функция будет искать в каждой подпапке скрытый файл, например .dontdelete. Когда его найдут, он спросит, действительно ли я хочу продолжить. Я не могу сделать это защищено от записи, так как есть процесс, который постоянно пишет в эту папку. Есть ли лучший способ сделать это?


2
Разве вы пытались псевдоним rmдля rm -i:> -i строка перед каждым удаление или> -I подскажи один раз перед удалением более трех файлов, или при удалении рекурсивно. Менее навязчив, чем -i, но при этом обеспечивает защиту от большинства ошибок. Вы можете в любое время переписать их с другими флагами.
IBr

2
Проверьтеsafe-rm
Sr -

Есть около десятка способов сделать это. Вам нужно будет более подробно рассказать о вашей среде.
Игнасио Васкес-Абрамс


Другая идея заключается в создании псевдонима для функции, которая просто перемещает ее в определенную папку, а затем создает cronjob, который запускается tmpwatchдля удаления файлов из этой папки каждый час.
Братчли

Ответы:


14

При изучении вашего вопроса я наткнулся на эту технику, которая может помочь вам в будущем.

Вы можете, очевидно, коснуться файла в каталоге, например, так:

touch -- -i

Теперь, когда вы запустите команду rm -fr *в каталоге, где -iон присутствует, вы увидите интерактивное приглашение из rm.

$ ls
file1  file2  file3  file4  file5  -i

$ rm -fr *
rm: remove regular empty file `file1'? n
rm: remove regular empty file `file2'? n
rm: remove regular empty file `file3'? n
rm: remove regular empty file `file4'? n
rm: remove regular empty file `file5'? n

То же самое можно сделать, просто оставив псевдоним на месте, rmчтобы он всегда работал rm -i. Это может стать раздражающим. Так часто я видел, что этот псевдоним был на месте, а затем отключил его, когда вы действительно хотите удалить без запроса.

alias rm='rm -i'

Теперь в каталогах вас встретят так:

$ ls
file1  file2  file3  file4  file5

$ rm -r *
rm: remove regular empty file `file1'?

Чтобы переопределить псевдоним:

$ \rm -r *

Это все еще не останавливает rm -frоднако. Но это обеспечивает вам некоторую защиту.

Ссылки


1
Это хорошее, элегантное решение, которое хорошо работает, если вам нужно / нужно только защитить небольшой набор каталогов. И я могу быть старомодным, но я все еще живу под впечатлением, что если вы говорите --forceчто-то, вы действительно это имеете в виду.
CVn

Также обратите внимание, что rm -I(верхний регистр i) может быть полезен в псевдониме, так как он немного менее навязчив (в соответствии с man-страницей, только если вы удалите более трех файлов или рекурсивно).
CVn

Обратите внимание, что touch ./-iхитрость работает только с GNU rmи только если переменная POSIXLY_CORRECT не установлена ​​(команды POSIX не распознают параметры после аргументов).
Стефан Шазелас

5

Много возможностей:

  • alias rm='rm -i'- РМ спросит - если вы не укажете -f...
  • chmod -w dir - защищает файлы непосредственно в этом каталоге.
  • chattr +i если вы действительно имеете в виду это
  • написать свою собственную обертку вокруг рм
  • так далее...

Но лучший способ, вероятно, - иметь хорошую резервную копию и хранить важные данные в некотором контроле версий (например git), что также дает много других преимуществ.


1
Пришел сюда, чтобы упомянуть об этом. Я бы предложил chattr + i, чтобы сделать его полностью безопасным.
JZeolla

У меня уже есть псевдоним, rm -iно, как и следовало ожидать, он не работает с -fопцией. Я использую Git для многих других целей, но что, если я случайно сделаю это rm -rf *в Git?
Dilawar

С помощью git, если вы удалите только подкаталог, вы можете легко восстановить его из локального репозитория. Если вы удалите весь репозиторий, вы можете просто клонировать его снова - учитывая, что вы перенесли его в другое место ранее.
Михас

1

Используйте программное обеспечение gitдля контроля версий, например, для инкапсуляции ваших проектов.

До тех пор, пока вы не удалите весь проект, вам придется целенаправленно печатать, rm -rf .*чтобы удалить .gitкаталог и потерять все данные, необходимые для отката.

Это дает дополнительное преимущество, заключающееся в том, что вы можете отправлять резервные копии своих материалов на удаленный сервер, такой как github или bitbucket.


1

Вот как это rm -rf dirработает:

  1. Он открывается dirи перечисляет его содержимое.
  2. Для каждой записи, если это каталог, повторите тот же процесс для него, если это не так, вызовите unlinkего.

Если бы вы могли, для списка каталогов сначала вернуть специальное имя файла, и если бы вы могли вызвать процесс, выполняющий unlink этот файл, умереть, это решило бы проблему. Это можно сделать с помощью файловой системы fuse.

Например, вы можете адаптировать loopback.plпример из модуля Perl Fuse, который просто реализует фиктивную файловую систему, которая является просто проходом к реальной файловой системе, как показано ниже (см. Также патч ниже):

  • при перечислении каталога, если он содержит запись с именем .{{do-not-delete}}., добавьте список записей с двумя файлами: .{{do-not-delete}}!errorи.{{do-not-delete}}!kill
  • при попытке unlinkпервого верните EPERMкод так, чтобыrm отобразилось сообщение об ошибке
  • при попытке unlinkвторого, процесс убивается.

$ ls -Ff dir/test
./  .{{do-not-delete}}.  foo/  ../  bar
$ ./rm-rf-killer dir
$ ls -Ff dir/test
.{{do-not-delete}}!error  .{{do-not-delete}}!kill  ./  .{{do-not-delete}}.   foo/  ../  bar
$ rm -rf dir/test
rm: cannot remove `dir/test/.{{do-not-delete}}!error': Operation not permitted
zsh: terminated  rm -rf dir/test
$ ls -Ff dir/test
.{{do-not-delete}}!error  .{{do-not-delete}}!kill  ./  .{{do-not-delete}}.   foo/  ../  bar

Вот патч для применения поверх этого loopback.plпримера в качестве доказательства концепции:

--- loopback.pl 2013-06-03 22:35:00.577316063 +0100
+++ rm-rf-killer    2013-06-03 22:33:41.523328427 +0100
@@ -7,2 +7,4 @@
 my $has_threads = 0;
+my $flag = ".{{do-not-delete}}";
+
 eval {
@@ -42,3 +44,4 @@

-use blib;
+#use blib;
+use File::Basename;
 use Fuse;
@@ -49,3 +52,3 @@

-my %extraopts = ( 'threaded' => 0, 'debug' => 0 );
+my %extraopts = ( 'threaded' => 0, 'debug' => 0, 'mountopts' => 'nonempty' );
 my($use_real_statfs, $pidfile);
@@ -64,3 +67,7 @@

-sub fixup { return "/tmp/fusetest-" . $ENV{LOGNAME} . shift }
+sub fixup {
+    my $f = shift;
+    $f =~ s#(/\Q$flag\E)!(error|kill)$#$1.#s;
+    return ".$f";
+}

@@ -78,3 +85,9 @@
 }
-    my (@files) = readdir(DIRHANDLE);
+    my @files;
+    
+    while (my $f = readdir(DIRHANDLE)) {
+        unshift @files, "$flag!error", "$flag!kill"
+            if ($f eq "$flag.");
+        push @files, $f;
+    }
 closedir(DIRHANDLE);
@@ -121,3 +134,12 @@
 sub x_readlink { return readlink(fixup(shift));         }
-sub x_unlink   { return unlink(fixup(shift)) ? 0 : -$!; }
+sub x_unlink   {
+    my $f = shift;
+    if (basename($f) eq "$flag!error") {return -EPERM()}
+    if (basename($f) eq "$flag!kill") {
+        my $caller_pid = Fuse::fuse_get_context()->{"pid"};
+        kill("TERM", $caller_pid);
+        return -EPERM();
+    }
+    return unlink(".$f") ? 0 : -$!;
+}

@@ -203,3 +225,2 @@
 sub daemonize {
-    chdir("/") || die "can't chdir to /: $!";
 open(STDIN, "< /dev/null") || die "can't read /dev/null: $!";
@@ -236,2 +257,3 @@

+chdir($mountpoint) or die("chdir: $!");
 daemonize();
@@ -239,3 +261,3 @@
 Fuse::main(
-    'mountpoint'    => $mountpoint,
+    'mountpoint'    => '.',
 'getattr'       => 'main::x_getattr',

-1

Я создал скрипт, чтобы облегчить эту задачу. Сценарий оболочки изменяет права на чтение / запись и флаг chattr для данного списка папок в интерактивном режиме, не запрашивая пароль root. Вы можете скачать zip-файл по ссылке ниже.

https://drive.google.com/file/d/0B_3UYBZy2FVsMVpBSWdSWnFBYk0/edit?usp=sharing

Я также включил установочный скрипт, чтобы упростить настройку. Инструкции прилагаются в zip-файле.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.