Зробити каталог захищеним від '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'- rm запитає - якщо ви не вкажете -f...
  • chmod -w dir - захищає файли безпосередньо в цьому каталозі.
  • chattr +i якщо ти справді це маєш на увазі
  • напишіть власну обгортку навколо rm
  • тощо ...

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


1
Сюди прийшли згадати це. Я б запропонував chattr + i, щоб зробити його повністю безпечним.
JZeolla

У мене вже є Rm, rm -iале, як можна було б очікувати, він не працює з -fваріантом. Я використовую git для багатьох інших цілей, але що робити, якщо я випадково роблю і rm -rf *в git?
Ділавар

Якщо ви видалите лише підкаталог, ви можете легко відновити його з локального сховища. Якщо ви видалите ціле сховище, його можна просто клонувати ще раз - якщо ви пересунули його в інше місце раніше.
michas

1

Використовуйте програмне забезпечення gitдля управління версіями, наприклад, щоб інкапсулювати свої проекти.

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

Це має додаткову перевагу, що ви можете пересилати резервні копії ваших речей на віддалений сервер, наприклад github або bitbucket.


1

Ось як rm -rf dirпрацює:

  1. Він відкривається dirта перераховує його вміст.
  2. Для кожного запису, якщо це каталог, повторіть той самий процес для нього, якщо його немає, зателефонуйте unlinkдо нього.

Якщо ви могли б для списку каталогів спочатку повернути спеціальне ім’я файлу, і якщо ви могли змусити процес, що робить unlinkцей файл, померти, це вирішило б проблему. Це можна зробити за допомогою файлової системи запобіжників.

Наприклад, ви можете адаптувати 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 заданого списку папок, не запитуючи корінний пароль. Ви можете завантажити поштовий файл за посиланням, поданим нижче.

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

Я також включив сценарій встановлення, щоб полегшити налаштування. Інструкції додаються до zip-файлу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.