仕組みrm -rf dir
は次のとおりです。
- が開き
dir
、コンテンツが表示されます。
- 各エントリについて、それがディレクトリの場合は同じプロセスを繰り返し、そうでない場合は呼び出します
unlink
。
ディレクトリリストの場合は、最初に特別なファイル名を返すことができunlink
、そのファイルに対してを実行しているプロセスを停止させることができれば、問題は解決します。これは、fuseファイルシステムを使用して行うことができます。
たとえば、perl Fuseモジュールのloopback.pl
例を、その下の実際のファイルシステムへのパススルーであるダミーファイルシステムを実装するだけにすることができます(以下のパッチも参照)。
- ディレクトリをリストするとき、それは名前のエントリが含まれている場合、
.{{do-not-delete}}.
2つのファイルを持つエントリのリストを先頭に追加:.{{do-not-delete}}!error
と.{{do-not-delete}}!kill
unlink
最初のものにしようとすると、エラーメッセージEPERM
をrm
表示するようにコードを返します
unlink
2つ目を試みると、プロセスが強制終了されます。
$ 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
ここでは、概念の証明としてそのloopback.pl
例の上に適用するパッチを示します。
--- 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
へのrm -i
:> -iプロンプトの前にすべての除去または> -Iつ以上のファイルを削除、または再帰的に削除するときに前に一度プロンプト。-iよりも煩わしさはほとんどありませんが、ほとんどのミスに対する保護を提供します。他のフラグを使用して、いつでもそれらを作成できます。