rm -rf dir
작동 방식 은 다음과 같습니다 .
- 이 열리고
dir
내용이 나열됩니다.
- 각 항목에 대해 디렉토리 인 경우 동일한 프로세스를 반복하십시오
unlink
. 그렇지 않은 경우이를 호출 하십시오.
디렉토리 목록에 대해 먼저 특수한 파일 이름을 반환하고 unlink
해당 파일에서 수행중인 프로세스가 종료 될 수 있으면 문제가 해결됩니다. 퓨즈 파일 시스템을 사용하여 수행 할 수 있습니다.
예를 들어, 아래의 실제 파일 시스템으로 전달되는 더미 파일 시스템을 구현하는 perl Fuse 모듈 의 loopback.pl
예제 를 조정할 수 있습니다 (아래 패치 참조).
- 디렉토리를 나열 할 때 그것이라는 항목이 포함 된 경우,
.{{do-not-delete}}.
두 개의 파일이있는 항목의 목록을 앞에 추가 : .{{do-not-delete}}!error
및.{{do-not-delete}}!kill
unlink
첫 번째 시도 할 때 오류 메시지 EPERM
가 rm
표시 되도록 코드를 반환하십시오.
unlink
두 번째 시도 하면 프로세스가 종료됩니다.
$ 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보다 방해가 적습니다. 언제든지 다른 플래그를 사용하여 실수를 기록 할 수 있습니다.