Ein Verzeichnis vor 'rm -rf' schützen

9

Ich habe gerade einige Daten in Ordner A verloren, der sich danach in Ordner B befand rm -rf B. Bevor ich erkennen konnte, was ich getan habe, war alles vorbei. Jetzt ist eine Lektion gelernt, ich möchte einige meiner Ordner idiotensicher machen, um ein nächstes Mal zu vermeiden, wenn ich etwas Ähnliches mache und mich umbringen möchte.

Eine Möglichkeit, die ich mir vorstellen kann, besteht darin, eine Bash-Funktion zu schreiben und sie als Alias ​​zu verwenden rm. Diese Funktion sucht in jedem Unterordner nach einer versteckten Datei wie z .dontdelete. Wenn es gefunden wird, würde es fragen, ob ich wirklich weitermachen möchte. Ich kann nicht machen ist schreibgeschützt, da es einen Prozess gibt, der ständig in diesen Ordner schreibt. Gibt es einen besseren Weg, dies zu tun?

Dilawar
quelle
2
Haben Sie versucht rm, rm -ivor jedem Entfernen einen Alias zu erstellen:> -i Eingabeaufforderung oder> -I einmal vor dem Entfernen von mehr als drei Dateien oder beim rekursiven Entfernen. Weniger aufdringlich als -i, bietet aber dennoch Schutz vor den meisten Fehlern. Sie können diese jederzeit mit anderen Flags überschreiben.
IBr
2
Check outsafe-rm
sr_
Es gibt ungefähr ein Dutzend Möglichkeiten, dies zu tun. Sie müssen detaillierter auf Ihre Umgebung eingehen.
Ignacio Vazquez-Abrams
Eine andere Idee ist, es auf eine Funktion zu aliasen, die es nur in einen bestimmten Ordner verschiebt, und dann einen Cronjob tmpwatchzu erstellen, der ausgeführt wird, um stündlich Dateien aus diesem Ordner zu löschen.
Bratchley

Antworten:

14

Bei der Recherche Ihrer Frage bin ich auf diese Technik gestoßen, die Ihnen in Zukunft helfen könnte.

Sie können anscheinend eine Datei im Verzeichnis wie folgt berühren:

touch -- -i

Wenn Sie nun den Befehl rm -fr *in einem Verzeichnis ausführen, in dem das -ivorhanden ist, wird die interaktive Eingabeaufforderung von angezeigt 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

Das Gleiche kann erreicht werden, indem Sie einfach einen Alias ​​an Ort und Stelle lassen, um dies rmimmer zu tun rm -i. Das kann nervig werden. Ich habe so oft gesehen, dass dieser Alias ​​vorhanden ist und dann deaktiviert wird, wenn Sie wirklich löschen möchten, ohne dazu aufgefordert zu werden.

alias rm='rm -i'

Jetzt werden Sie in Verzeichnissen folgendermaßen begrüßt:

$ ls
file1  file2  file3  file4  file5

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

So überschreiben Sie den Alias:

$ \rm -r *

Dies hört jedoch immer noch nicht auf rm -fr. Aber es bietet Ihnen einen gewissen Schutz.

Verweise

slm
quelle
1
Dies ist eine schöne, elegante Lösung, die gut funktioniert, wenn Sie nur einen kleinen Satz von Verzeichnissen schützen möchten / müssen. Und ich mag altmodisch sein, aber ich lebe immer noch unter dem Eindruck, dass man --forcees wirklich ernst meint , wenn man etwas sagt .
ein CVn
Beachten Sie auch, dass rm -I(Großbuchstaben i) in einem Alias ​​nützlich sein können, da es etwas weniger aufdringlich ist (laut Manpage werden Sie nur aufgefordert, wenn Sie mehr als drei Dateien entfernen oder rekursiv).
ein CVn
Beachten Sie, dass der touch ./-iTrick nur mit GNU funktioniert rmund nur, wenn die Variable POSIXLY_CORRECT nicht festgelegt ist (POSIX-Befehle erkennen keine Optionen nach Argumenten).
Stéphane Chazelas
5

Viele Möglichkeiten:

  • alias rm='rm -i'- rm wird fragen - es sei denn, Sie geben an -f...
  • chmod -w dir - schützt Dateien direkt in diesem Verzeichnis.
  • chattr +i wenn du es wirklich so meinst
  • Schreiben Sie Ihren eigenen Wrapper um rm
  • usw...

Aber ein besserer Weg ist wahrscheinlich, ein gutes Backup zu haben und wichtige Daten in einer Art Versionskontrolle (wie git) zu speichern , was auch viele andere Vorteile bringt.

michas
quelle
1
Kam hierher, um dies zu erwähnen. Ich würde chattr + i vorschlagen, um es völlig sicher zu machen.
JZeolla
Ich habe bereits ein Alias ​​für rm, rm -iaber wie zu erwarten ist, funktioniert es nicht mit -fOption. Ich benutze Git für viele andere Zwecke, aber was ist, wenn ich es versehentlich auch rm -rf *in Git mache ?
Dilawar
Wenn Sie mit git nur ein Unterverzeichnis entfernen, können Sie es einfach aus Ihrem lokalen Repository wiederherstellen. Wenn Sie das gesamte Repository entfernen, können Sie es einfach erneut klonen - vorausgesetzt, Sie haben es zuvor an einen anderen Ort verschoben.
Michas
1

Verwenden Sie eine Versionskontrollsoftware git, um Ihre Projekte zu kapseln.

Solange Sie nicht ein ganzes Projekt löschen, müssen Sie absichtlich eingeben rm -rf .*, um das .gitVerzeichnis zu entfernen und alle für ein Rollback erforderlichen Daten zu verlieren.

Dies hat den zusätzlichen Vorteil, dass Sie Backups Ihrer Daten auf einen Remote-Server wie Github oder Bitbucket übertragen können.

JoshRagem
quelle
1

So rm -rf dirfunktioniert es:

  1. Es wird geöffnet dirund der Inhalt aufgelistet.
  2. Wenn es sich um ein Verzeichnis handelt, wiederholen Sie für jeden Eintrag den gleichen Vorgang. Wenn dies nicht der Fall ist, rufen unlinkSie ihn auf.

Wenn Sie für die Verzeichnisliste zuerst einen speziellen Dateinamen zurückgeben könnten und wenn Sie einen Prozess, der eine unlinkDatei ausführt, zum Erliegen bringen könnten , würde dies das Problem lösen. Dies könnte mit einem Sicherungsdateisystem erfolgen.

Zum Beispiel könnten Sie das loopback.plBeispiel aus dem Perl-Fuse-Modul anpassen, das nur ein Dummy-Dateisystem implementiert, das nur ein Durchgang zu einem realen Dateisystem darunter ist (siehe auch Patch unten):

  • Wenn Sie ein Verzeichnis auflisten und einen Eintrag mit dem Namen enthalten .{{do-not-delete}}., stellen Sie der Liste der Einträge zwei Dateien voran: .{{do-not-delete}}!errorund.{{do-not-delete}}!kill
  • Wenn Sie zum unlinkersten versuchen , geben Sie den EPERMCode zurück, sodass rmeine Fehlermeldung angezeigt wird
  • Beim Versuch zum unlinkzweiten wird der Prozess abgebrochen.

$ 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

Hier ein Patch, der als Proof of Concept auf dieses loopback.plBeispiel angewendet werden soll:

--- 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',
Stéphane Chazelas
quelle
-1

Ich habe ein Skript erstellt, um diese Aufgabe zu vereinfachen. Das Shell-Skript ändert die Lese- / Schreibberechtigung und das Chattr-Flag einer bestimmten Ordnerliste interaktiv, ohne nach dem Root-Passwort zu fragen. Sie können die Zip-Datei über den unten angegebenen Link herunterladen.

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

Ich habe auch ein Installationsskript beigefügt, um die Einrichtung zu vereinfachen. Anweisungen sind in der Zip-Datei enthalten.

Liju G. Chacko
quelle