Créer un répertoire protégé de 'rm -rf'

9

Je viens de perdre quelques données dans le dossier A qui se trouvait dans le dossier B après l'avoir fait rm -rf B. Avant de pouvoir réaliser ce que j'avais fait, c'était fini. Maintenant une leçon est apprise, je souhaite rendre certains de mes dossiers à l'abri des idiots pour éviter une prochaine fois quand je fais quelque chose de similaire et que je veux me tuer.

Une façon de penser est d'écrire une fonction bash et de l'aliaser rm. Cette fonction recherchera dans chaque sous-dossier un fichier caché tel que .dontdelete. Une fois trouvé, il me demanderait si je veux vraiment continuer. Je ne peux pas faire est protégé en écriture car il existe un processus qui écrit constamment dans ce dossier. Y a-t-il une meilleure façon de le faire?

Dilawar
la source
2
Avez-vous essayé l'alias rmpour rm -i:> -i invite avant chaque suppression ou> -I invite une fois avant de supprimer plus de trois fichiers, ou lors de la suppression récursive. Moins intrusif que -i, tout en offrant une protection contre la plupart des erreurs. Vous pouvez à tout moment remplacer celles avec d'autres indicateurs.
IBr
2
Départsafe-rm
sr_
Il existe une douzaine de façons de procéder. Vous devrez entrer dans plus de détails concernant votre environnement.
Ignacio Vazquez-Abrams
Une autre idée consiste à l'aliaser vers une fonction qui le déplace simplement vers un dossier particulier, puis à créer un cronjob qui s'exécute tmpwatchpour supprimer les fichiers de ce dossier toutes les heures.
Bratchley

Réponses:

14

En recherchant votre question, je suis tombé sur cette technique qui pourrait vous aider à l'avenir.

Vous pouvez apparemment toucher un fichier dans le répertoire comme ceci:

touch -- -i

Maintenant, lorsque vous exécutez la commande rm -fr *dans un répertoire où le -iest présent, l'invite interactive s'affiche 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

La même chose peut être obtenue en laissant simplement un alias en place pour rmtoujours le faire rm -i. Cela peut devenir ennuyeux. Si souvent, ce que j'ai vu, c'est d'avoir cet alias en place, puis de le désactiver lorsque vous voulez vraiment le supprimer sans y être invité.

alias rm='rm -i'

Maintenant, dans les répertoires, vous serez accueilli comme ceci:

$ ls
file1  file2  file3  file4  file5

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

Pour remplacer l'alias:

$ \rm -r *

Cela ne s'arrête rm -frcependant pas pour autant. Mais cela vous offre une certaine protection.

Références

slm
la source
1
Il s'agit d'une solution agréable et élégante qui fonctionne bien si vous voulez / devez protéger uniquement un petit ensemble de répertoires. Et je suis peut-être démodé mais j'ai toujours l'impression que si vous dites --forcequelque chose, vous le pensez vraiment.
un CVn du
Notez également que rm -I(majuscules i) peut être utile dans un alias, car il est un peu moins intrusif (selon la page de manuel, ne vous invite que si vous supprimez plus de trois fichiers ou récursivement).
un CVn du
Notez que l' touch ./-iastuce ne fonctionne qu'avec GNU rmet uniquement si la variable POSIXLY_CORRECT n'est pas définie (les commandes POSIX ne reconnaissent pas les options après les arguments).
Stéphane Chazelas
5

De nombreuses possibilités:

  • alias rm='rm -i'- rm demandera - sauf si vous spécifiez -f...
  • chmod -w dir - protège les fichiers directement dans ce répertoire.
  • chattr +i si tu le penses vraiment
  • écrivez votre propre emballage autour de rm
  • etc...

Mais un meilleur moyen est probablement d'avoir une bonne sauvegarde et de conserver les données importantes dans une sorte de contrôle de version (comme git), ce qui apporte également de nombreux autres avantages.

michas
la source
1
Je suis venu ici pour le mentionner. Je suggère chattr + i pour le rendre entièrement sûr.
JZeolla
J'ai déjà rm alias rm -imais comme on pourrait s'y attendre, cela ne fonctionne pas avec l' -foption. J'utilise git à de nombreuses autres fins, mais que se passe-t-il si je le fais accidentellement rm -rf *aussi?
Dilawar
Avec git, si vous supprimez juste un sous-répertoire, vous pouvez facilement le restaurer à partir de votre référentiel local. Si vous supprimez le référentiel entier, vous pouvez simplement le cloner à nouveau - étant donné que vous l'avez poussé à un autre endroit auparavant.
michas
1

Utilisez un logiciel de contrôle de version comme gitpour encapsuler vos projets.

Tant que vous ne supprimez pas un projet entier, vous devrez taper délibérément rm -rf .*pour supprimer le .gitrépertoire et perdre toutes les données nécessaires à une restauration.

Cela a l'avantage supplémentaire que vous pouvez pousser les sauvegardes de vos fichiers vers un serveur distant comme github ou bitbucket.

JoshRagem
la source
1

Voici comment ça rm -rf dirmarche:

  1. Il s'ouvre diret répertorie son contenu.
  2. Pour chaque entrée, s'il s'agit d'un répertoire, répétez le même processus, sinon, appelez- unlinkle.

Si vous pouviez, pour la liste des répertoires, renvoyer d'abord un nom de fichier spécial, et si vous pouviez faire unlinkmourir un processus faisant un sur ce fichier, cela résoudrait le problème. Cela pourrait être fait en utilisant un système de fichiers fusible.

Par exemple, vous pouvez adapter l' loopback.plexemple du module perl Fuse qui implémente simplement un système de fichiers factice qui n'est qu'un passage vers un vrai système de fichiers en dessous (voir aussi le patch ci-dessous):

  • lors de l'inscription d'un répertoire, s'il contient une entrée nommée .{{do-not-delete}}., ajoutez la liste des entrées avec deux fichiers: .{{do-not-delete}}!erroret.{{do-not-delete}}!kill
  • lors de unlinkla première tentative , renvoyer le EPERMcode pour rmafficher un message d'erreur
  • en essayant unlinkle second, le processus est tué.

$ 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

Voici un patch à appliquer au dessus de cet loopback.plexemple comme preuve de concept:

--- 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
la source
-1

J'ai créé un script pour faciliter cette tâche. Le script shell modifie l'interaction de lecture / écriture et l'indicateur chattr d'une liste donnée de dossiers de manière interactive, sans demander le mot de passe root. Vous pouvez télécharger le fichier zip à partir du lien ci-dessous.

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

J'ai également inclus un script d'installation pour faciliter la configuration. Les instructions sont incluses dans le fichier zip.

Liju G. Chacko
la source