Begini cara rm -rf dir
kerjanya:
- Itu terbuka
dir
, dan daftar isinya.
- Untuk setiap entri, jika itu adalah direktori, ulangi proses yang sama untuk itu, jika tidak, panggil
unlink
saja.
Jika Anda dapat, untuk daftar direktori, kembalikan nama file khusus terlebih dahulu, dan jika Anda dapat menyebabkan proses melakukan unlink
pada file itu mati, itu akan menyelesaikan masalah. Itu bisa dilakukan dengan menggunakan sistem file sekering.
Sebagai contoh, Anda bisa mengadaptasi loopback.pl
contoh dari modul Fuse perl yang hanya mengimplementasikan sistem file dummy yang hanya pass-through ke sistem file nyata di bawahnya seperti itu (lihat juga tambalan di bawah):
- saat mendaftar direktori, jika berisi entri bernama
.{{do-not-delete}}.
, tambahkan daftar entri dengan dua file: .{{do-not-delete}}!error
dan.{{do-not-delete}}!kill
- saat mencoba
unlink
yang pertama, kembalikan EPERM
kode sehingga rm
menampilkan pesan kesalahan
- ketika mencoba
unlink
yang kedua, prosesnya terbunuh.
$ 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
Di sini tambalan untuk diterapkan di atas loopback.pl
contoh itu sebagai bukti konsep:
--- 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
untukrm -i
:> -i prompt sebelum setiap penghapusan atau> -saya minta sekali sebelum menghapus lebih dari tiga file, atau ketika menghapus secara rekursif. Tidak terlalu mengganggu daripada -i, sambil tetap memberikan perlindungan terhadap sebagian besar kesalahan. Anda dapat menuliskannya dengan flag lain kapan saja.