Voici comment ça rm -rf dir
marche:
- Il s'ouvre
dir
et 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-
unlink
le.
Si vous pouviez, pour la liste des répertoires, renvoyer d'abord un nom de fichier spécial, et si vous pouviez faire unlink
mourir 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.pl
exemple 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}}!error
et.{{do-not-delete}}!kill
- lors de
unlink
la première tentative , renvoyer le EPERM
code pour rm
afficher un message d'erreur
- en essayant
unlink
le 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.pl
exemple 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',
rm
pourrm -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.