Así es como rm -rf dir
funciona:
- Se abre
dir
y enumera su contenido.
- Para cada entrada, si es un directorio, repita el mismo proceso para él, si no es así, instálelo
unlink
.
Si pudiera, para la lista del directorio, devolver primero un nombre de archivo especial, y si pudiera hacer que un proceso que realiza un unlink
archivo falle, eso resolvería el problema. Eso podría hacerse usando un sistema de archivos de fusibles.
Por ejemplo, podría adaptar el loopback.pl
ejemplo del módulo perl Fuse que solo implementa un sistema de archivos ficticio que es solo un paso a un sistema de archivos real debajo de este modo (vea también el parche a continuación):
- al enumerar un directorio, si contiene una entrada denominada
.{{do-not-delete}}.
, anteponga la lista de entradas con dos archivos: .{{do-not-delete}}!error
y.{{do-not-delete}}!kill
- cuando intente con
unlink
el primero, devuelva el EPERM
código para que rm
aparezca un mensaje de error
- Al intentar
unlink
el segundo, el proceso se mata.
$ 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
Aquí un parche para aplicar sobre ese loopback.pl
ejemplo como prueba de concepto:
--- 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
pararm -i
:> -i solicitar antes de cada eliminación o> -I solicitar una vez antes de eliminar más de tres archivos, o al eliminar de forma recursiva. Menos intrusivo que -i, a la vez que brinda protección contra la mayoría de los errores. Puede sobrescribir aquellos con otras banderas en cualquier momento.