Ecco come rm -rf dir
funziona:
- Si apre
dir
ed 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 unlink
file su quel file, ciò risolverebbe il problema. Ciò potrebbe essere fatto usando un filesystem con fusibili.
Ad esempio, potresti adattare l' loopback.pl
esempio 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}}!error
e.{{do-not-delete}}!kill
- quando si tenta
unlink
il primo, restituire il EPERM
codice in modo che rm
visualizzi un messaggio di errore
- quando si tenta
unlink
il 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.pl
ad 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',
rm
arm -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.