Tworzenie katalogu chronionego przed 'rm -rf'


9

Właśnie straciłem trochę danych w folderze A, który był w folderze B po wykonaniu rm -rf B. Zanim zdałem sobie sprawę z tego, co zrobiłem, było po wszystkim. Teraz jest lekcja, chcę uczynić część mojego folderu odpornym na idiotyzm, aby uniknąć następnego razu, gdy zrobię coś podobnego i chcę się zabić.

Jednym ze sposobów jest napisanie funkcji bash i alias do niej rm. Ta funkcja przeszuka każdy podfolder w poszukiwaniu ukrytego pliku, takiego jak .dontdelete. Gdy zostanie znaleziony, zapyta, czy naprawdę chcę kontynuować. Nie mogę zrobić, jest chroniony przed zapisem, ponieważ istnieje proces, który stale zapisuje w tym folderze. Czy jest na to lepszy sposób?


2
Czy próbowałeś alias rmdo rm -i:> -i każdy wiersz przed uprowadzeniem lub> -I szybka raz przed usunięciem więcej niż trzech plików, lub przy usuwaniu rekurencyjnie. Mniej uciążliwy niż -i, a jednocześnie zapewnia ochronę przed większością błędów. Możesz je w dowolnym momencie zapisać za pomocą innych flag.
IBr

2
Sprawdźsafe-rm
sr_

Jest na to kilkanaście sposobów. Będziesz musiał szczegółowo omówić swoje środowisko.
Ignacio Vazquez-Abrams


Innym pomysłem jest alias do funkcji, która po prostu przenosi go do określonego folderu, a następnie tworzy cronjob, który uruchamia się, tmpwatchaby co godzinę usuwać pliki z tego folderu.
Bratchley,

Odpowiedzi:


14

Badając twoje pytanie, natknąłem się na tę technikę, która może ci pomóc w przyszłości.

Najwyraźniej możesz dotknąć pliku w katalogu w następujący sposób:

touch -- -i

Teraz po uruchomieniu polecenia rm -fr *w katalogu, w którym -ijest obecny, zostanie wyświetlony interaktywny monit z 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

To samo można osiągnąć poprzez pozostawienie aliasu na rmzawsze rm -i. To może być denerwujące. Tak często widziałem, aby mieć ten alias na miejscu, a następnie wyłączyć go, gdy naprawdę chcesz usunąć bez monitu.

alias rm='rm -i'

Teraz w katalogach zostaniesz przywitany w następujący sposób:

$ ls
file1  file2  file3  file4  file5

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

Aby zastąpić alias:

$ \rm -r *

To jednak nie kończy się rm -frjednak. Ale zapewnia ci pewną ochronę.

Bibliografia


1
To miłe, eleganckie rozwiązanie, które działa dobrze, jeśli chcesz / potrzebujesz tylko chronić mały zestaw katalogów. Być może jestem staroświecki, ale wciąż żyję pod wrażeniem, że jeśli coś powiesz --force, to naprawdę masz na myśli.
CVn

Zauważ też, że rm -I(duże litery i) mogą być przydatne w aliasach, ponieważ są nieco mniej ingerujące (zgodnie ze stroną podręcznika, wyświetla monit tylko wtedy, gdy usuniesz więcej niż trzy pliki lub rekurencyjnie).
CVn

Zauważ, że touch ./-isztuczka działa tylko z GNU rmi tylko wtedy, gdy zmienna POSIXLY_CORRECT nie jest ustawiona (polecenia POSIX nie rozpoznają opcji po argumentach).
Stéphane Chazelas

5

Wiele możliwości:

  • alias rm='rm -i'- rm zapyta - chyba że podasz -f...
  • chmod -w dir - chroni pliki bezpośrednio w tym katalogu.
  • chattr +i jeśli naprawdę to masz na myśli
  • napisz własne opakowanie wokół rm
  • itp...

Ale lepszym sposobem jest prawdopodobnie posiadanie dobrej kopii zapasowej i przechowywanie ważnych danych w pewnego rodzaju kontroli wersji (np. git), Co ma wiele innych zalet.


1
Przybyłem tutaj, aby o tym wspomnieć. Sugerowałbym chattr + i, aby był całkowicie bezpieczny.
JZeolla

Już mam alias do rm, rm -iale jak można się spodziewać, nie działa z -fopcją. Używam git do wielu innych celów, ale co jeśli przypadkowo zrobię też rm -rf *w git?
Dilawar

Dzięki git, jeśli usuniesz tylko podkatalog, możesz łatwo przywrócić go z lokalnego repozytorium. Jeśli usuniesz całe repozytorium, możesz po prostu sklonować je ponownie - pod warunkiem, że wcześniej wypchnąłeś je w inne miejsce.
michas

1

Skorzystaj z oprogramowania gitdo kontroli wersji, takiego jak hermetyzacja projektów.

Dopóki nie usuniesz całego projektu, będziesz musiał celowo pisać, rm -rf .*aby usunąć .gitkatalog i utracić wszystkie dane potrzebne do przywrócenia.

Ma to dodatkową zaletę, że możesz przesyłać kopie zapasowe swoich rzeczy na zdalny serwer, taki jak github lub bitbucket.


1

Oto jak rm -rf dirdziała:

  1. Otwiera się diri wyświetla jego zawartość.
  2. Dla każdego wpisu, jeśli jest to katalog, powtórz dla niego ten sam proces, jeśli nie jest, wywołaj unlinkgo.

Jeśli możesz, na liście katalogów, najpierw zwrócić specjalną nazwę pliku, a jeśli możesz spowodować, że proces wykonujący polecenie unlinkdla tego pliku umrze, rozwiąże to problem. Można to zrobić za pomocą systemu plików bezpieczników.

Na przykład, możesz zaadaptować loopback.plprzykład z modułu Perla Fuse, który po prostu implementuje fikcyjny system plików, który jest po prostu przejściem do prawdziwego systemu plików poniżej (patrz także łatka poniżej):

  • przy wyświetlaniu katalogu, jeśli zawiera on wpis o nazwie .{{do-not-delete}}., dołącz listę wpisów do dwóch plików: .{{do-not-delete}}!errori.{{do-not-delete}}!kill
  • podczas próby unlinkprzejścia do pierwszego zwróć EPERMkod, aby rmwyświetlał komunikat o błędzie
  • podczas próby unlinkdrugiego proces zostaje zabity.

$ 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

Oto łatka do zastosowania na tym loopback.plprzykładzie jako dowód koncepcji:

--- 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',

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.