Tạo một thư mục được bảo vệ khỏi 'rm -rf'


9

Tôi vừa bị mất một số dữ liệu trong thư mục A nằm trong thư mục B sau khi thực hiện rm -rf B. Trước khi tôi có thể nhận ra những gì tôi đã làm, tất cả đã kết thúc. Bây giờ một bài học được rút ra, tôi muốn làm cho một số thư mục của mình trở nên ngớ ngẩn để tránh lần sau khi tôi làm điều gì đó tương tự và muốn tự sát.

Một cách tôi có thể nghĩ là viết một hàm bash và đặt bí danh cho nó rm. Hàm này sẽ xem xét từng thư mục con cho một tệp ẩn như .dontdelete. Khi tìm thấy, nó sẽ hỏi tôi có thực sự muốn tiếp tục không. Tôi không thể thực hiện được bảo vệ chống ghi vì có một quá trình liên tục ghi vào thư mục này. Có cách nào tốt hơn để làm điều đó?


2
Bạn đã cố gắng bí danh rmđể rm -i:> -i nhắc trước mỗi loại bỏ hoặc> -Tôi nhắc một lần trước khi tháo hơn ba tác phẩm, hoặc khi loại bỏ đệ quy. Ít xâm phạm hơn -i, trong khi vẫn bảo vệ chống lại hầu hết các lỗi Bạn có thể ghi lại những điều đó bằng các cờ khác bất cứ lúc nào.
IBr

2
Hãy xemsafe-rm
sr_

Có khoảng một tá cách để làm điều đó. Bạn sẽ cần phải đi vào chi tiết hơn về môi trường của bạn.
Ignacio Vazquez-Abrams


Một ý tưởng khác là đặt bí danh cho một chức năng chỉ cần di chuyển nó đến một thư mục cụ thể và sau đó tạo một cronjob chạy tmpwatchđể xóa các tệp khỏi thư mục này mỗi giờ.
Bratchley

Câu trả lời:


14

Trong nghiên cứu câu hỏi của bạn, tôi đã tìm thấy kỹ thuật này có thể giúp bạn trong tương lai.

Bạn rõ ràng có thể chạm vào một tập tin trong thư mục như vậy:

touch -- -i

Bây giờ khi bạn chạy lệnh rm -fr *trong một thư mục -icó mặt thì bạn sẽ được trình bày với lời nhắc tương tác từ đó rm.

$ ls
file1  file2  file3  file4  file5  -i

$ rm -fr *
rm: remove regular empty file `file1'? n
rm: remove regular empty file `file2'? n
rm: remove regular empty file `file3'? n
rm: remove regular empty file `file4'? n
rm: remove regular empty file `file5'? n

Điều tương tự có thể đạt được bằng cách chỉ để lại một bí danh rmđể luôn luôn làm rm -i. Điều này có thể gây phiền nhiễu. Vì vậy, thường thì những gì tôi đã thấy là đặt bí danh này, và sau đó tắt nó khi bạn thực sự muốn xóa mà không được nhắc.

alias rm='rm -i'

Bây giờ trong thư mục bạn sẽ được chào đón như thế này:

$ ls
file1  file2  file3  file4  file5

$ rm -r *
rm: remove regular empty file `file1'?

Để ghi đè bí danh:

$ \rm -r *

Điều này vẫn không dừng lại rm -frtuy nhiên. Nhưng nó cung cấp cho bạn một số bảo vệ.

Người giới thiệu


1
Đây là một giải pháp tốt, thanh lịch, hoạt động tốt nếu bạn chỉ muốn / cần bảo vệ một bộ thư mục nhỏ. Và tôi có thể lỗi thời nhưng tôi vẫn sống dưới ấn tượng rằng nếu bạn nói --forceđiều gì đó, bạn thực sự có ý đó.
CVn

Cũng lưu ý rằng rm -I(chữ hoa i) có thể hữu ích trong bí danh, vì nó ít xâm phạm hơn (theo trang man, chỉ nhắc nếu bạn xóa nhiều hơn ba tệp hoặc đệ quy).
CVn

Lưu ý rằng touch ./-ithủ thuật chỉ hoạt động với GNU rmvà chỉ khi biến POSIXLY_CORRECT không được đặt (các lệnh POSIX không nhận ra các tùy chọn sau các đối số).
Stéphane Chazelas

5

Nhiều khả năng:

  • alias rm='rm -i'- rm sẽ hỏi - trừ khi bạn chỉ định -f...
  • chmod -w dir - bảo vệ các tập tin trực tiếp trong thư mục đó.
  • chattr +i nếu bạn thực sự có ý đó
  • viết trình bao bọc của riêng bạn xung quanh rm
  • Vân vân...

Nhưng một cách tốt hơn có lẽ là có một bản sao lưu tốt và giữ dữ liệu quan trọng trong một số loại kiểm soát phiên bản (như git), điều này cũng mang lại rất nhiều lợi thế khác.


1
Đến đây để đề cập đến điều này. Tôi sẽ đề nghị chattr + i để làm cho nó hoàn toàn an toàn.
JZeolla

Tôi đã có bí danh rm rm -inhưng như người ta mong đợi, nó không hoạt động với -ftùy chọn. Tôi sử dụng git cho nhiều mục đích khác, nhưng nếu tôi vô tình làm rm -rf *trong git thì sao?
Dilawar

Với git, nếu bạn xóa chỉ một thư mục con, bạn có thể dễ dàng khôi phục nó khỏi kho lưu trữ cục bộ của mình. Nếu bạn xóa toàn bộ kho lưu trữ, bạn có thể chỉ cần sao chép lại một lần nữa - trước đó bạn đã đẩy nó đến một nơi khác.
michas

1

Sử dụng một phần mềm kiểm soát phiên bản như git để đóng gói các dự án của bạn.

Miễn là bạn không xóa toàn bộ dự án, bạn sẽ phải gõ một cách có chủ đích rm -rf .*để xóa .gitthư mục và mất tất cả dữ liệu cần thiết cho một rollback.

Điều này có thêm lợi ích là bạn có thể đẩy các bản sao lưu của công cụ của mình lên một máy chủ từ xa như github hoặc bitbucket.


1

Đây là cách rm -rf dirhoạt động:

  1. Nó mở dir , và liệt kê nội dung của nó.
  2. Đối với mỗi mục, nếu đó là một thư mục, hãy lặp lại quy trình tương tự cho nó, nếu không, hãy gọi unlinknó.

Nếu bạn có thể, đối với danh sách thư mục, trước tiên hãy trả lại tên tệp đặc biệt và nếu bạn có thể gây ra quá trình thực hiện unlink trên tệp đó bị chết, điều đó sẽ giải quyết vấn đề. Điều đó có thể được thực hiện bằng cách sử dụng một hệ thống tập tin cầu chì.

Chẳng hạn, bạn có thể điều chỉnh loopback.plví dụ từ mô-đun Fuse perl , chỉ thực hiện một hệ thống tập tin giả chỉ là một hệ thống tập tin thực bên dưới như vậy (xem thêm bản vá bên dưới):

  • khi liệt kê một thư mục, nếu nó chứa một mục có tên .{{do-not-delete}}., hãy thêm vào danh sách các mục có hai tệp: .{{do-not-delete}}!error.{{do-not-delete}}!kill
  • Khi cố gắng unlinkđầu tiên, trả lại EPERMmã đểrm hiển thị thông báo lỗi
  • Khi cố gắng đến unlinkcái thứ hai, quá trình bị giết.

$ 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

Đây là một bản vá để áp dụng trên loopback.plví dụ đó như một bằng chứng về khái niệm:

--- 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',

-1

Tôi đã tạo ra một kịch bản để làm cho nhiệm vụ này dễ dàng hơn. Kịch bản shell sửa đổi quyền đọc / ghi và cờ chattr của một danh sách các thư mục đã cho một cách tương tác, mà không hỏi mật khẩu gốc. Bạn có thể tải xuống tệp zip từ liên kết được cung cấp dưới đây.

https://drive.google.com/file/d/0B_3UYBZy2FVsMVpBSWdSWnFBYk0/edit?usp=shaming

Tôi cũng đã bao gồm tập lệnh cài đặt để làm cho việc thiết lập dễ dàng hơn. Hướng dẫn được đính kèm trong tệp zip.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.