Voici comment ça rm -rf dirmarche:
- Il s'ouvre
diret répertorie son contenu.
- Pour chaque entrée, s'il s'agit d'un répertoire, répétez le même processus, sinon, appelez-
unlinkle.
Si vous pouviez, pour la liste des répertoires, renvoyer d'abord un nom de fichier spécial, et si vous pouviez faire unlinkmourir un processus faisant un sur ce fichier, cela résoudrait le problème. Cela pourrait être fait en utilisant un système de fichiers fusible.
Par exemple, vous pouvez adapter l' loopback.plexemple du module perl Fuse qui implémente simplement un système de fichiers factice qui n'est qu'un passage vers un vrai système de fichiers en dessous (voir aussi le patch ci-dessous):
- lors de l'inscription d'un répertoire, s'il contient une entrée nommée
.{{do-not-delete}}., ajoutez la liste des entrées avec deux fichiers: .{{do-not-delete}}!erroret.{{do-not-delete}}!kill
- lors de
unlinkla première tentative , renvoyer le EPERMcode pour rmafficher un message d'erreur
- en essayant
unlinkle second, le processus est tué.
$ 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
Voici un patch à appliquer au dessus de cet loopback.plexemple comme preuve de concept:
--- 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',
rmpourrm -i:> -i invite avant chaque suppression ou> -I invite une fois avant de supprimer plus de trois fichiers, ou lors de la suppression récursive. Moins intrusif que -i, tout en offrant une protection contre la plupart des erreurs. Vous pouvez à tout moment remplacer celles avec d'autres indicateurs.