Ecco come rm -rf dirfunziona:
- Si apre
dired elenca il suo contenuto.
- Per ogni voce, se si tratta di una directory, ripetere la stessa procedura per essa, in caso contrario, chiamarla
unlink.
Se fosse possibile, per l'elenco delle directory, restituire prima un nome file speciale e se si potesse causare la morte di un processo che esegue un unlinkfile su quel file, ciò risolverebbe il problema. Ciò potrebbe essere fatto usando un filesystem con fusibili.
Ad esempio, potresti adattare l' loopback.plesempio dal modulo perl Fuse che implementa semplicemente un filesystem fittizio che è solo un pass-through a un vero file system sottostante (vedi anche la patch sotto):
- quando si elenca una directory, se contiene una voce denominata
.{{do-not-delete}}., anteporre l'elenco delle voci con due file: .{{do-not-delete}}!errore.{{do-not-delete}}!kill
- quando si tenta
unlinkil primo, restituire il EPERMcodice in modo che rmvisualizzi un messaggio di errore
- quando si tenta
unlinkil secondo, il processo viene ucciso.
$ 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
Qui una patch da applicare in cima a quello loopback.plad esempio come una prova di concetto:
--- 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',
rmarm -i:> -i prompt prima di ogni rimozione o> -I prompt una volta prima di rimuovere più di tre file o durante la rimozione ricorsiva. Meno invadente di -i, pur offrendo protezione contro la maggior parte degli errori Puoi scrivere a quelli con altre bandiere in qualsiasi momento.