Tornar um diretório protegido de 'rm -rf'

8

Acabei de perder alguns dados na pasta A que estava dentro da pasta B depois de fazer rm -rf B . Antes que eu pudesse perceber o que fiz, tudo acabou. Agora, uma lição é aprendida, eu desejo tornar algumas das minhas pastas à prova de idiotas para evitar uma próxima vez quando eu fizer algo similar e quiser me matar.

Uma maneira que posso pensar é escrever uma função bash e alias para rm . Essa função examinará cada subpasta para um arquivo oculto, como .dontdelete . Quando encontrado, perguntaria se eu realmente queria continuar. Eu não posso fazer é protegido contra gravação, pois há um processo que constantemente escreve para esta pasta. Existe alguma maneira melhor de fazer isso?

    
por Dilawar 01.06.2013 / 11:03

5 respostas

13

Ao pesquisar sua pergunta, deparei com essa técnica que pode ajudá-lo no futuro.

Você pode aparentemente tocar em um arquivo no diretório da seguinte forma:

touch -- -i

Agora, quando você executar o comando rm -fr * em um diretório no qual o -i está presente, será apresentado o prompt interativo de 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

A mesma coisa pode ser obtida apenas deixando um alias no local para que rm sempre faça rm -i . Isso pode ficar chato. Muitas vezes, o que eu vi feito é ter esse alias no lugar e, em seguida, desativá-lo quando você realmente deseja excluir sem ser solicitado.

alias rm='rm -i'

Agora nos diretórios você será saudado assim:

$ ls
file1  file2  file3  file4  file5

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

Para substituir o alias:

$ \rm -r *

Isso ainda não impede um rm -fr . Mas isso fornece alguma proteção.

Referências

por 01.06.2013 / 13:27
5

Muitas possibilidades:

  • alias rm='rm -i' - rm perguntará - a menos que você especifique -f ...
  • chmod -w dir - protege arquivos diretamente nesse diretório.
  • chattr +i se você realmente quer dizer
  • escreva seu próprio wrapper em torno de rm
  • etc ...

Mas uma maneira melhor é provavelmente ter um bom backup e manter dados importantes em algum tipo de controle de versão (como git ), o que traz muitas outras vantagens também.

    
por 01.06.2013 / 13:30
1

Use um software de controle de versão como git para encapsular seus projetos.

Contanto que você não exclua um projeto inteiro, você deve digitar propositalmente rm -rf .* para remover o diretório .git e perder todos os dados necessários para uma reversão.

Isso tem o benefício adicional de poder enviar backups de suas coisas para um servidor remoto, como github ou bitbucket.

    
por 01.06.2013 / 17:49
1

Veja como o rm -rf dir funciona:

  1. Ele abre dir e lista seu conteúdo.
  2. Para cada entrada, se for um diretório, repita o mesmo processo para ele, se não for, chame unlink nele.

Se você pudesse, para a listagem de diretório, retornar um nome de arquivo especial em primeiro lugar, e se você poderia causar um processo fazendo uma unlink sobre esse arquivo para morrer, que iria resolver o problema. Isso pode ser feito usando um sistema de arquivos de fusíveis.

Por exemplo, você pode adaptar o loopback.pl exemplo do perl Fuse módulo que apenas implementa um sistema de arquivos dummy que é apenas uma passagem para um sistema de arquivos real por baixo como assim (veja também patch abaixo):

  • ao listar um diretório, se ele contém uma entrada chamada .{{do-not-delete}}. , preceder a lista de entradas com dois arquivos: .{{do-not-delete}}!error e .{{do-not-delete}}!kill
  • ao tentar unlink do primeiro, retornar o código EPERM de modo que rm exibe uma mensagem de erro
  • ao tentar unlink o segundo, o processo é eliminado.

$ 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

Aqui um patch para aplicar em cima do que loopback.pl exemplo como prova de conceito:

--- 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',
    
por 04.06.2013 / 00:00
-1

Eu criei um script para facilitar essa tarefa. O shell script modifica a permissão de leitura / gravação e o flag chattr de uma determinada lista de pastas interativamente, sem solicitar a senha de root. Você pode baixar o arquivo zip no link abaixo.

link

Eu também incluí o script de instalação para facilitar a configuração. As instruções são colocadas no arquivo zip.

    
por 15.12.2013 / 10:53