Tags ?>

Gelöschte Dateien retten unter Linux

Wer hat nicht schon mindestens einmal schwungvoll ein rm abgesetzt und erst nach dem Druck auf die Enter-Taste gemerkt, daß das doch nicht so beabsichtigt war?

In der SuSE Supportdatenbank ist ein Artikel zu finden, der beschreibt wie man sich in so einem Fall weiterhelfen kann. Zusammengefasst geht das so:

  1. Das betroffene Dateisystem entmounten und debugfs aufrufen
    umount /dev/hdb5
    debugfs /dev/hdb5
    

     

  2. mit lsdel die gelöschten Files (bzw. deren Inodes) anzeigen lassen
    defiant:~# debugfs /dev/hdb5
    debugfs 1.22, 22-Jun-2001 for EXT2 FS 0.5b, 95/08/09
    debugfs:  lsdel
    3 deleted inodes found.
     Inode  Owner  Mode    Size    Blocks    Time deleted
     48910      0 100644  19988    5/   5 Thu May  3 19:07:49 2001
     48912      0 100644  19697    5/   5 Thu May  3 19:07:49 2001
     48914      0 100644  19694    5/   5 Thu May  3 19:07:49 2001
    debugfs:
    

     

  3. mit dump kann man nun die gelöschten Files auf eine andere Partition zurückschreiben lassen. Die Verzeichnisstruktur in die man schreibt muß natürlich schon existieren.
    debugfs:  dump -p <48910> /root/restored/restored-file-1   
    

     

  4. Danach kann man sich dann freuen, daß man seine ach so wertvolle Datei wieder zurückbekommen hat.
    debugfs:  quit
    defiant:~# ls -l /root/restored/
    insgesamt 20
    -rw-r--r--    1 root     root        19988 19. Mär 22:13 restored-file-1
    

     

Soweit, so gut! Mit einer oder einigen wenigen Dateien geht das ja noch. Wenn man aber so intelligent war, ein Verzeichnis mit richtig vielen Dateien wegzubügeln (ich spreche da aus Erfahrung), ist es natürlich äußerst unangenehm diesen Vorgang wer weiß wie oft zu wiederholen.

Zum Glück ist debugfs aber sehr gut über die Kommandozeile zu bedienen:

debugfs --help
debugfs 1.22, 22-Jun-2001 for EXT2 FS 0.5b, 95/08/09
debugfs: invalid option -- -
debugfs: Usage: debugfs [-b blocksize] [-s superblock] [-f cmd_file] [-R request] [-V] [[-w] [-c] device]

--help versteht es zwar nicht, sagt aber trotzdem welche Parameter und Optionen es versteht. Ausführlichere Infos bekommt man natürlich über die man-page.

Interessant sind hier die Parameter -f und -R. Mit -f kann man eine Datei benennen, in der die debugfs-Kommandos stehen, während man mit -R einen einzelnen Befehl an debugfs übergibt.
Zur Sache...

Für unser Beispiel gehen wir davon aus, ich hätte dummerweise einen Haufen Dateien auf /dev/hdb5 gelöscht. Ziel ist es nun einigermaßen bequem die wiederherzustellenden Files auszuwählen und diese dann nicht alle einzeln eintippen zu müssen.

  1. Zunächst entmounten wir das betroffene Dateisystem:
    umount /dev/hdb5
    

    Falls das nun partout nicht möglich ist (z. B. weil es sich um das root-Filesystem handelt) sollte man es wenigstens read-only mounten:
    mount /dev/hdb5 / -o ro,remount
    

     

  2. Als nächstes wird eine Datei erzeugt, in der die Angaben über die gelöschten Inodes stehen:
    defiant:~# debugfs -R lsdel /dev/hdb5 > deletedfiles.txt
    debugfs 1.22, 22-Jun-2001 for EXT2 FS 0.5b, 95/08/09
    

    Dabei wird mit -R das schon bekannte lsdel übergeben und die Ausgabe mit > statt auf den Bildschirm in eine Datei geschrieben.

     

  3. Mit unserem Lieblingseditor kann nun die eben erzeugte Datei bearbeitet werden:
    80 deleted inodes found.
     Inode  Owner  Mode    Size    Blocks    Time deleted
     48910      0 100644  19988    5/   5 Thu May  3 19:07:49 2001
     48912      0 100644  19697    5/   5 Thu May  3 19:07:49 2001
     48914      0 100644  19694    5/   5 Thu May  3 19:07:49 2001
    585241   1000 100644 3915776  957/ 957 Mon Jul 16 23:11:17 2001
    585242   1000 100664 3753819  918/ 918 Mon Jul 16 23:11:17 2001
    585243   1000 100664 7203236 1762/1762 Mon Jul 16 23:11:17 2001
    585244   1000 100664 2498472  611/ 611 Mon Jul 16 23:11:17 2001
    585245   1000 100664 2492592  610/ 610 Mon Jul 16 23:11:17 2001
    585246   1000 100664 9909104 2424/2424 Mon Jul 16 23:11:17 2001
    585247   1000 100644 2893086  708/ 708 Mon Jul 16 23:11:17 2001
    585248   1000 100644 3508372  858/ 858 Mon Jul 16 23:11:17 2001
    585249   1000 100664 4312212 1056/1056 Mon Jul 16 23:11:17 2001
    585250   1000 100664 4013104  981/ 981 Mon Jul 16 23:11:17 2001
    .
    .
    .
    

    Zunächst einmal müssen die beiden Zeilen am Anfang (die Auskunft über die Anzahl der gefundenen Inodes und die Spaltenüberschrift) gelöscht werden. übrig bleibt dann eine Liste aller auf der Partition gelöschten Inodes. Nun muß man anhand der Angaben Owner (dahinter versteckt sich die UID des Eigentümers) und Time deleted raten ob man das jeweilige File zurück haben möchte. In der Regel dürfte wohl nach "Massenlöschorgien" das Datum die hilfreichste Angabe sein.
    Am Ende sollen in der Datei nur noch Zeilen mit den Angaben zu den Inodes stehen, die wiederhergestellt werden sollen.

     

  4. Da wir jetzt eine mehr oder weniger lange Liste mit Inodes haben, gilt es diese in geeigneter Weise debugfs zu übergeben. Hierbei hilft uns die bash.
    Wir pipen die Inode-Liste in eine Schleife, welche die Einzelteile jeder Zeile in Variablen stellt und daraus den richtigen debugfs-Aufruf macht:
    cat deletedfiles.txt | while read a b; do 
      debugfs /dev/hdb5 -R "dump -p <$a> /root/restored/file$a" ; 
    done
    

     

    Das sieht vielleicht auf den ersten Blick etwas kompliziert aus, darum hier ein paar erläuternde Worte:

    cat deletedfiles.txt |
    
    gibt die Datei mit den Inodenummern statt auf den Bildschirm in eine pipe.

    Auf der anderen Seite der pipe wird unsere Datei dann Zeile für Zeile von

    while read a b;
    
    gelesen. Dabei betrachtet read jede Zeile als eine Reihe von Feldern, die durch Leerzeichen separiert sind. Jedes Feld wird einer der genannten Variablen zugewiesen. Da uns nur der Wert des ersten Feldes (die Inode-Nummer) interessiert, können wir den ganzen Rest der Zeile einfach der Variablen b zuweisen. Angenommen uns würde auch noch der letzte Wert der Zeile
    585313   1000 100644 2082144  510/ 510 Mon Jul 16 23:11:18 2001
    
    interessieren (in diesem Fall also die Jahreszahl) müssten wir alle Felder einzeln mit
    read a b c d e f g h i j k
    
    einlesen.

    do debugfs /dev/hdb5 -R "dump -p <$a> /root/restored/file$a" ; done
    
    Im Schleifenkörper (der für jede Zeile aus deletedfiles.txt einmal ausgeführt wird), steht der Aufruf von debugfs. Das auszuführende Kommando wird mit -R übergeben, wobei $a durch die Inodenummer ersetzt wird. Außerdem wird $a dazu benutzt eindeutige Namen für die wiederhergestellte Datei zu erzeugen. Dabei sind die Anführungszeichen wichtig um den Befehl, der per -R übergeben wird abzugrenzen. Und so ganz nebenbei schützen sie die von debugfs geforderten spitzen Klammern, die $a umschließen davor von der Shell als Umleitungszeichen interpretiert zu werden.

     

  5. Das war der einfache Teil!

    Jetzt haben wir ein einen Haufen Dateien die file12234 usw. heissen, aber keine Ahnug in welcher Datei sich der Brief an Tante Else oder das Perl-Skript, das drei Nächte Arbeit gekostet hat befindet.

    defiant:~/restored# ls
    deletedfiles.txt  file585306  file585309  file585312
    file585304        file585307  file585310  file585313
    file585305        file585308  file585311  restored-file-1
    

    Eine einfach Möglichkeit um zumindest einen groben Überblick zu bekommen ist, per file nachzusehen um was für Daten es sich bei jeder Datei handelt. Da ich natürlich immer noch keine Lust habe, das für jedes File einzeln einzutippen, liegt wieder die Verwendung einer Schleife nahe:

    defiant:~/restored# ls | while read a; do file $a; done
    deletedfiles.txt: ASCII text
    file585304: MPEG 1.0 layer 3 audio stream data, 128 kBit/s, 44.1 kHz, jstereo
    file585305: MPEG 1.0 layer 3 audio stream data, 128 kBit/s, 44.1 kHz, jstereo
    file585306: MPEG 1.0 layer 3 audio stream data, 128 kBit/s, 44.1 kHz, jstereo
    file585307: MPEG 1.0 layer 3 audio stream data, 128 kBit/s, 44.1 kHz, jstereo
    file585308: data
    file585309: MPEG 1.0 layer 3 audio stream data, 112 kBit/s, 44.1 kHz, jstereo
    file585310: MPEG 1.0 layer 3 audio stream data,  64 kBit/s, 32 kHz, jstereo
    file585311: MPEG 1.0 layer 3 audio stream data,  64 kBit/s, 32 kHz, jstereo
    file585312: MPEG 1.0 layer 3 audio stream data,  64 kBit/s, 32 kHz, jstereo
    file585313: MPEG 1.0 layer 3 audio stream data, 128 kBit/s, 44.1 kHz, stereo
    restored-file-1: ASCII text
    

    Um genaues über den Inhalt der Files herauszubekommen muß man sie zwar immer noch einzeln öffnen und ansehen, aber wer Lust hat, kann ja das obige Kommando so modifizieren, daß Dateien deren Typ erkannt wird gleich mit der korrekten Endung umbenannt werden.

    "Und wenn sich jemand so einen Einzeiler ausgedacht hat, möge er mir bitte eine Mail schicken, ich stelle das gerne hier dazu." habe ich geschrieben, als ich diesen Text hier eingestellt habe. Michael Schamber (5-d@gmx.net) hat sich dazu folgende Lösung einfallen lassen:

    "Der Einzeiler ist bei mir:
    ls -A1 | while read FN; do EXT=`file $FN | file2ext.pl`; mv $FN $FN$EXT; done
    
    Damit werden alle, auch versteckte Dateien in einem Verzeichniss gemäss des Ausbaus von file2fext.pl (sieht Kommentare im Source) umbenannt "

    Thomas de Klein