So rm -rf dir
funktioniert es:
- Es wird geöffnet
dir
und der Inhalt aufgelistet.
- Wenn es sich um ein Verzeichnis handelt, wiederholen Sie für jeden Eintrag den gleichen Vorgang. Wenn dies nicht der Fall ist, rufen
unlink
Sie ihn auf.
Wenn Sie für die Verzeichnisliste zuerst einen speziellen Dateinamen zurückgeben könnten und wenn Sie einen Prozess, der eine unlink
Datei 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.pl
Beispiel 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}}!error
und.{{do-not-delete}}!kill
- Wenn Sie zum
unlink
ersten versuchen , geben Sie den EPERM
Code zurück, sodass rm
eine Fehlermeldung angezeigt wird
- Beim Versuch zum
unlink
zweiten 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.pl
Beispiel 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',
rm
,rm -i
vor 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.