Hacer un directorio protegido de 'rm -rf'

9

Acabo de perder algunos datos en la carpeta A que estaba dentro de la carpeta B después de hacerlo rm -rf B. Antes de que pudiera darme cuenta de lo que había hecho, todo había terminado. Ahora que se aprende una lección, deseo hacer que mi carpeta sea a prueba de idiotas para evitar la próxima vez que haga algo similar y quiera suicidarme.

Una forma en la que puedo pensar es escribir una función bash y asignarle un alias rm. Esta función buscará en cada subcarpeta un archivo oculto como .dontdelete. Cuando lo encuentre, me preguntará si realmente quiero continuar. No puedo hacer que esté protegido contra escritura ya que hay un proceso que escribe constantemente en esta carpeta. ¿Hay alguna forma mejor de hacerlo?

Dilawar
fuente
2
¿Intentó alias rmpara rm -i:> -i solicitar antes de cada eliminación o> -I solicitar una vez antes de eliminar más de tres archivos, o al eliminar de forma recursiva. Menos intrusivo que -i, a la vez que brinda protección contra la mayoría de los errores. Puede sobrescribir aquellos con otras banderas en cualquier momento.
IBr
2
Salidasafe-rm
sr_
Hay alrededor de una docena de formas de hacerlo. Tendrá que entrar en más detalles sobre su entorno.
Ignacio Vazquez-Abrams
Otra idea es asignarle un alias a una función que simplemente lo mueva a una carpeta en particular y luego crear un cronjob que se ejecute tmpwatchpara eliminar archivos de esta carpeta cada hora.
Bratchley 01 de

Respuestas:

14

Al investigar su pregunta, encontré esta técnica que podría ayudarlo en el futuro.

Aparentemente puede tocar un archivo en el directorio de esta manera:

touch -- -i

Ahora, cuando ejecuta el comando rm -fr *en un directorio donde -iestá presente, se le presentará el mensaje interactivo 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

Lo mismo se puede lograr simplemente dejando un alias en el lugar para rmhacerlo siempre rm -i. Esto puede ser molesto. Muy a menudo, lo que he visto es tener este alias en su lugar y luego deshabilitarlo cuando realmente desea eliminar sin que se lo solicite.

alias rm='rm -i'

Ahora en los directorios serás recibido así:

$ ls
file1  file2  file3  file4  file5

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

Para anular el alias:

$ \rm -r *

Sin embargo, esto todavía no se detiene rm -fr. Pero te proporciona cierta protección.

Referencias

slm
fuente
1
Esta es una solución agradable y elegante que funciona bien si solo desea / necesita proteger un pequeño conjunto de directorios. Y puedo estar pasado de moda pero todavía vivo bajo la impresión de que si dices --forcealgo, lo dices en serio.
un CVn
También tenga en cuenta que rm -I(mayúscula i) podría ser útil en un alias, ya que es un poco menos intrusivo (según la página de manual, solo se le solicita si elimina más de tres archivos o de forma recursiva).
un CVn
Tenga en cuenta que el touch ./-itruco solo funciona con GNU rmy solo si la variable POSIXLY_CORRECT no está establecida (los comandos POSIX no reconocen las opciones después de los argumentos).
Stéphane Chazelas
5

Muchas posibilidades:

  • alias rm='rm -i'- rm preguntará - a menos que especifique -f...
  • chmod -w dir - protege los archivos directamente en ese directorio.
  • chattr +i si realmente lo dices en serio
  • escribe tu propio envoltorio alrededor de rm
  • etc ...

Pero una mejor manera es probablemente tener una buena copia de seguridad y mantener datos importantes en algún tipo de control de versiones (como git), lo que también trae muchas otras ventajas.

michas
fuente
1
Vine aquí para mencionar esto. Sugeriría chattr + i para que sea completamente seguro.
JZeolla 01 de
Ya tengo un alias de rm, rm -ipero como era de esperar, no funciona con la -fopción. Uso git para muchos otros propósitos, pero ¿qué sucede si accidentalmente también lo hago rm -rf *en git?
Dilawar
Con git, si elimina solo un subdirectorio, puede restaurarlo fácilmente desde su repositorio local. Si elimina todo el repositorio, simplemente puede clonarlo nuevamente, dado que lo empujó a otro lugar antes.
michas
1

Use un software de control de versiones como gitpara encapsular sus proyectos.

Mientras no elimine un proyecto completo, deberá escribir rm -rf .*a propósito para eliminar el .gitdirectorio y perder todos los datos necesarios para una reversión.

Esto tiene el beneficio adicional de que puede enviar copias de seguridad de sus cosas a un servidor remoto como github o bitbucket.

JoshRagem
fuente
1

Así es como rm -rf dirfunciona:

  1. Se abre diry enumera su contenido.
  2. Para cada entrada, si es un directorio, repita el mismo proceso para él, si no es así, instálelo unlink.

Si pudiera, para la lista del directorio, devolver primero un nombre de archivo especial, y si pudiera hacer que un proceso que realiza un unlinkarchivo falle, eso resolvería el problema. Eso podría hacerse usando un sistema de archivos de fusibles.

Por ejemplo, podría adaptar el loopback.plejemplo del módulo perl Fuse que solo implementa un sistema de archivos ficticio que es solo un paso a un sistema de archivos real debajo de este modo (vea también el parche a continuación):

  • al enumerar un directorio, si contiene una entrada denominada .{{do-not-delete}}., anteponga la lista de entradas con dos archivos: .{{do-not-delete}}!errory.{{do-not-delete}}!kill
  • cuando intente con unlinkel primero, devuelva el EPERMcódigo para que rmaparezca un mensaje de error
  • Al intentar unlinkel segundo, el proceso se mata.

$ 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

Aquí un parche para aplicar sobre ese loopback.plejemplo como prueba de concepto:

--- 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',
Stéphane Chazelas
fuente
-1

He creado un script para facilitar esta tarea. El script de shell modifica el permiso de lectura / escritura y el indicador de chat de una lista dada de carpetas de forma interactiva, sin solicitar una contraseña de root. Puede descargar el archivo zip desde el enlace que figura a continuación.

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

También he incluido un script de instalación para facilitar la configuración. Las instrucciones están incluidas en el archivo zip.

Liju G. Chacko
fuente