Introducere
I se întâmplă fiecăruia mai devreme sau mai târziu: o fracţiune de secundă după ce ai apăsat Enter realizezi greşeala, dar este deja prea târziu. Tocmai ai şters un fişier sau director valoros pentru care nu este rezervă (backup). Sau poate există o rezervă, dar e veche de o lună… şi, şocat, vezi ultima lună fugind prin faţa ochilor şi realizezi dureros ce trebuie să refaci …
Din fericire, iţi aduci aminte că niciodată fişierele nu sunt cu adevărat şterse, cel mult suprascrise de un nou conţinut. Astfel, remontezi discul doar în citire. Şi acum?
Dacă căutaţi pe Google “undelete ext3″, aproape în fiecare articol găsit vor fi utilizatori care întreabă dacă e posibil iar răspunsul este acelaşi: NU
Cele mai citate pagini vin chiar de pe pagina de întrebări ext3 FAQ
Q: Cum îmi pot recupera fişierele şterse de pe o partiţie ext3?
De fapt, nu se poate! Astfel a răspuns unul din dezvoltatori, Andreas Dilger:
Pentru a avea certitudinea că ext3 poate relua în siguranţă o deconectare după un crash, goleşte de fapt block pointerii din inode, în timp ce ext2 doar marchează aceste blocuri ca nefolosite in bitmap-ul blocului, marchează inode-ul ca “şters” şi nu modifică block pointerii.
Singura ta speranţă este “grep” pentru a recupera parţial fişierele care au fost şterse şi să speri la cât mai mult.
Totuşi, nu este chiar tot adevărat. Toate informaţiile pot fi încă acolo, la fel şi block pointerii. Este doar mai puţin probabil ca sunt încă acolo (de pe ext2), deoarece acestea trebuie să fie recuperate de la jurnal. Pe deasupra, meta datele sunt mai puţin coerent legate de datele reale astfel încât algoritmii heuristici sunt necesari pentru a recupera lucrurile. De fiecare dată când un fişier este accesat, se schimba Acces Time , şi inode-ul lui este scris pe disc, împreună cu alţi 31 de inodes care rezidă în acelaşi bloc. Când se întâmplă asta, o copie a acelui bloc este scrisă în jurnal. De aici inainte, dacă partiţia nu este destul de mare în comparaţie cu jurnalul, şi dacă cel puţin ai accesat recent fişierele pe care vrei să le recuperezi, ar putea fi posibil să recuperezi block pointerii din jurnal .
Pe 7 februarie 2008, mi-am şers din greseală tot directorul home: peste 3 GB de date şterse cu rm -rf. Singura rezervă avută era din iunie 2007. Neputinţa de a reface datele era inacceptabilă. Astfel, am ignorat tot ce mi s-a spus şi am început să învăţ cum funcţionează un sistem ext3 şi exact ce anume se întâmplă când sunt şterse fişierele…
3 săptămâni şi aproape 5000 de linii de cod mai târziu recuperasem toate fişierele de pe disc.
Ce trebuie să ştiţi înainte de a începe
Programul pe care l-am scris presupune o selecţie a fişierelor recent şterse (imediat după ultimul unmount). Nu tratează fişierele de pe un sistem de fişiere corupt ci doar fişierele şterse accidental.
Totuşi, programul este în faza beta: dezvoltarea programului a fost făcută ca un hack, doar cu scopul de a îmi recupera datele. După ce mi-am recuperat datele am continuat dezvoltarea programului pentru încă o lună la adresarea bug-urilor care nu au apărut în cazul meu, dar per total programul nu este atât de robust pe cât ar putea fi. Astfel, e posibil ca lucrurile să nu meargă „ca pe roate” şi pentru voi. Am dotat programul cu asserts, care creează oportunitatea ca în cazul în care ceva nu merge cum trebuie programul să se oprească şi să nu mai încerce recuperarea. În acest caz va trebui să săpaţi mai adânc, ca să terminaţi voi programul, ca sa spun aşa.
Programul are nevoie doar de acces în citire pe sistemul de fişiere pe care au fost fişierele şterse: el nu încearcă să recupereze fişierele. În schimb, vă permite să faceţi o copie a fişierelor şterse şi le scrie într-un director proaspăt creat în directorul curent (care , desigur, ar trebui să fie pe un sistem de fişiere diferit). Toate căile sunt relative cu root-ul partiţiei, thus— daca analizaţi o partiţie /dev/md5 care a fost montată /home, atunci /home este necunoscut de partiţie şi de program deci nu e parte din cale (path). În schimb, o cale va fi ceva de genul “carlo/c++/foo.cc” , fără primul slash. Root-ul partiţiei (/home din exemplu) este şirul gol , nu ‘/’.
Numele programului, ext3grep l-am pus astfel deoarece planuiam să scriu un program mai inteligent care să fie în stare să reconstruiască fişierele căutând după blocuri care arată identic cu blocurile căutate (pe baza unui backup mai vechi sau a altor reguli). Particula grep din nume a fost pusă anticipând că citatul din partea dezvoltatorilor ex3 era adevărat: mă pregăteam pentru a lucra cu seturi de blocuri, fiecare set corespunzând unui tipar de căutare şi încărcate cu probabilităţi, asupra cărora cineva ar trebui să lucreze cu seturi de operatori pentru a reduce numărul de blocuri şi să le atribuie fişierelor aranjându-le în ordine. Oricum nimic de genul acesta nu a fost folosit. Totuşi, am păstrat numele ext3grep deoarece cineva poate doreşte să adauge o adevărată funcţionalitate grep programului (în acest moment funcţiile grep sunt limitate la şiruri fixe, listând numerele de bloc asemănătoare pe o ieşire standard).
Cum stochează EXT3 fişierele?
Mărimea blocurilor
Conţinutul fişierelor este stocat în blocuri continue de 4096 de bytes (mărimea actuală depinde de parametrii liniei de comandă dată pentru mke2fs când a fost creat sistemul de fişiere prima dată şi poate fi 1024, 2048 sau 4096 de bytes). Un hard disc este un “block device”, adică fiecare intrare / ieşire este în fucţie de aceste blocuri; o persoană poate doar citi / scrie un număr integral de blocuri odată. Această nu înseamnă neapărat că mărimea minimă a unui fragment continuu de fişier este aceeaşi (deşi poate fi doar mai mică), dar în practică este. De fapt, programul nu va funcţiona dacă mărimea fragmentelor nu este aceeaşi cu mărimea blocurilor.
Mărimea actuală a blocului precum şi mărimea actuală a fragmentelor sunt stocate într-un superbloc şi pot fi găsite cu opţiunea --superblock. De exemplu ,
$ ext3grep $IMAGE --superblock | grep 'size:' Block size: 4096 Fragment size: 4096
Aici IMAGE este o variabilă de sistem care a fost setată ca nume pentru dispozitivul (sau o copie a acestuia făcută cu dd) partiţiei care conţine sistemul de fişiere. De exemplu, /dev/sdd2 ( în general, oricare nume de dispozitiv returnat de comanda df sub numele de “Filesystem”). În mod normal doar root poate citi direct dispozitivele, dar puteţi (temporar) să le faceţi citibile de către voi, sau să faceţi o imagine backup cu dd. Ţineţi minte că, de exemplu, /dev/sdd NU este o partiţie (observaţi digitul lipsă ) şi nu va conţine date folositoare pentru scopul nostru.
Întreaga partiţie este impărţită într-un număr de blocuri integral, începând numărătoarea de la 0. Astfel, dacă doriţi vreodată să faceţi o copie a blocului cu numărul N trebuie să faceţi astfel:
$ dd if=$IMAGE bs=4096 count=1 skip=$N of=block.$N
unde N ia valoare de la 0 până la (dar nu incluzând şi) numărul de blocuri stocate în superbloc. De exemplu,
$ ext3grep $IMAGE --superblock | grep 'Blocks count:' Blocks count: 2441824
Având oricare număr de bloc, se pot tipări informaţii despre el folosind opţiunea --block în linia de comandă. De exemplu,
$ ext3grep $IMAGE --ls --block 600
[...]
Group: 0
Block 600 is Allocated. It's inside the inode table of group 0
(inodes [1 - 33>).
$ ext3grep $IMAGE --ls --block 1109
[...]
Group: 0
Block 1109 is a directory. The block is Allocated
.-- File type in dir_entry (r=regular file, d=directory, l=symlink)
| .-- D: Deleted ; R: Reallocated
Indx Next | Inode | Deletion time Mode File name
==========+==========+----------------data-from-inode------+-----------+=========
0 1 d 2 drwxr-xr-x .
1 end d 2 drwxr-xr-x ..
2 3 d 11 D 1202351093 Thu Feb 7 03:24:53 2008 drwxr-xr-x lost+found
3 end d 195457 D 1202352103 Thu Feb 7 03:41:43 2008 drwxr-xr-x carlo
Superblocul
Superblocul nu este chiar un bloc. Mărimea lui este întotdeauna de 1024 de bytes şi primul superbloc începe la multiplu de 1024. Deci, dacă mărimea blocului este 1024 atunci superblocul este blocul 1, dar dacă mărimea blocului este 2048 sau 4096 atunci superblocul este o parte din blocul 0. Există multiple copii de rezervă altundeva pe disc. ext3grep presupune că primul superbloc nu este corupt şi nu încearcă să găsească sau să citească copiile de rezervă.
Se poate citi conţinutul primului superbloc folosind dd astfel:
$ dd if=$IMAGE bs=1024 skip=1 count=1 of=superblock
Semnificaţia fiecărui byte este dată în tabelul 1.
| Bytes | type | Descriere |
|---|---|---|
| 0 .. 3 | __le32 | Inodes total |
| 4 .. 7 | __le32 | Blocuri total |
| 8 .. 11 | __le32 | Blocuri rezervate total |
| 12 .. 15 | __le32 | Blocuri libere total |
| 16 .. 19 | __le32 | Inodes liberi total |
| 20 .. 23 | __le32 | Primul bloc de date |
| 24 .. 27 | __le32 | Mărime bloc |
| 28 .. 31 | __le32 | Mărime fragment |
| 32 .. 35 | __le32 | Număr de blocuri per grup |
| 36 .. 39 | __le32 | Număr de fragmente per grup |
| 40 .. 43 | __le32 | Număr de inodes per grup |
| 44 .. 47 | __le32 | Ora montării |
| 48 .. 51 | __le32 | Ora scrierii |
| 52 .. 53 | __le16 | Montări total |
| 54 .. 55 | __le16 | Maximal mount count |
| 56 .. 57 | __le16 | Semnătura magică |
| 58 .. 59 | __le16 | Stare sistem de fişiere |
| 60 .. 61 | __le16 | Comportament la descoperire erori |
| 62 .. 63 | __le16 | Nivel minim revizie |
| 64 .. 67 | __le32 | Data ultimei verificări |
| 68 .. 71 | __le32 | Timp maxim între verificări |
| 72 .. 75 | __le32 | OS |
| 76 .. 79 | __le32 | Nivel revizie |
| 80 .. 81 | __le16 | UID iniţial pentru blockuri rezervate |
| 82 .. 83 | __le16 | GID iniţial pentru blockuri libere |
| 84 .. 87 | __le32 | Primul inode nerezervat |
| 88 .. 89 | __le16 | Mărimea structurii inode |
| 90 .. 91 | __le16 | Numărul de grupuri de blocuri pentru acest superbloc |
| 92 .. 95 | __le32 | Set de opţiuni compatibile |
| 96 .. 99 | __le32 | Set de opţiuni incompatibile |
| 100 .. 103 | __le32 | Set de opţiuni compatibile doar în citire |
| 104 .. 119 | __u8[16] | 128-bit uuid pentru volum |
| 120 .. 135 | char[16] | Numele volumului |
| 136 .. 199 | char[64] | Directorul în care a fost montat ultima oară |
| 200 .. 203 | __le32 | Pentru compresie |
| 204 | __u8 | Număr de blocuri pentru care se va încerca realocarea |
| 205 | __u8 | Număr de prealocare pentru directoare |
| 206 .. 207 | __le16 | Descriptor per grup pentru creştere online |
| 208 .. 223 | __u8[16] | uuid pentru superblocul jurnal |
| 224 .. 227 | __le32 | Numărul inode pentru fişierul jurnal |
| 228 .. 231 | __le32 | Numărul dispozitivului pentru fişierul jurnal |
| 232 .. 235 | __le32 | Începutul listei de inodes care se vor şterge |
| 236 .. 251 | __le32[4] | HTREE hash seed |
| 252 | __u8 | Versiunea iniţială de hash folosită |
| 253 .. 255 | Rezervat | |
| 256 .. 259 | __le32 | Opţiuni iniţiale de montare |
| 260 .. 263 | __le32 | Primul grup metabloc |
| 264 .. 1023 | Rezervat |
Structura C ( C-struct ) pentru superbloc este dată în fişierul header /usr/include/linux/ext3_fs.h şi a fost folosită pentru a crea tabelul 1. Datele întregilor nealocaţi sunt stocate pe disc în format Little Endian. Într-un format Little Endian procesoarele gen Intel x86, care înseamnă că __le32 este de fapt uint32_t şi __le16 este egal cu uint16_t.
Grupurile
Fiecare sistem de fişiere ext3 este împărţit în grupuri, cu un număr fix de blocuri per grup, cu excepţia ultimului grup care conţine blocurile rămase. Numărul de blocuri per grup este dat în superbloc.
$ ext3grep $IMAGE --superblock | grep 'Blocks per group' # Blocks per group: 32768
Fiecare grup foloseşte un bloc ca hartă pentru a ţine evidenţa cărui bloc din grup îi este alocat (folosit). Astfel pot fi cel mult 4096 * 8 = 32768 blocuri normale per grup.
Alt bloc este folosit ca hartă pentru numărul de inodes alocat. Inodes sunt structuri de date de 128 de bytes (ele pot fi extinse teoretic; mărimea reală este dată tot în superbloc) care sunt stocate într-un tabel (4096 / 128 = 32 inodes per bloc) în fiecare grup. Având cel mult 32768 bits în hartă, putem trage concluzia că vor fi cel mult 32768 inodes per grup şi astfel 32768 / 32 = 1024 blocuri în tabela inode pentru fiecare grup. Dimensiunea reală a tabelei inode este dată de numărul real de inodes per grup, care este şi el stocat în supergrup.
$ ext3grep $IMAGE --superblock | egrep 'Size of inode|inodes per group' Number of inodes per group: 16288 Size of inode structure: 128
Numerele de bloc atât pentru hartă cât şi pentru începutul tabelei inode este dat în “tabela descriptoare a grupului “, care se găseşte în blocul următor superblocului; deci bloc1 sau bloc2 în funcţie de mărimea blocului. Această tabelă de decriptare constă din o serie de structuri consecutive ext3_group_desc definite şi ele în /usr/include/linux/ext3_fs.h, conform tabelului 2.
| Bytes | type | Descriere |
|---|---|---|
| 0 .. 3 | __le32 | Blocul hartă pentru blocuri – Blocks bitmap block |
| 4 .. 7 | __le32 | Blocul hartă pentru inodes – Inodes bitmap block |
| 8 .. 11 | __le32 | Blocul tabelei inodes – Inodes table block |
| 12 .. 13 | __le16 | Total blocuri libere – Free blocks count |
| 14 .. 15 | __le16 | Total inodes liberi-Free inodes count |
| 16 .. 17 | __le16 | Directoare total – Directories count |
| 18 .. 31 | Rezervat |
Datorită faptului că dimensiunea acestei structuri este o putere de 2, 32 de bytes, se potriveşte exact unui număr întreg de descriptoare într-un bloc. Astfel tabela este continuă chiar dacă este întinsă pe mai multe blocuri. Ţineţi minte că un bloc de 4096 bytes este deja capabil să ţină 128 de descriptoare de grup, fiecare putând stoca 32768 de blocuri / mdash; aşadar doar o partiţie mai mare de 16GB va folosi mai mult de un bloc pentru tabela de descriptoare de grup.
Conţinutul tabelei este tipărit folosind ext3grep dacă nu sunt specificate alte acţiuni sau un grup în linia de comandă. De exemplu,
$ ext3grep $IMAGE
No action specified; implying --superblock.
[...]
Number of groups: 75
Group 0: block bitmap at 598, inodes bitmap at 599, inode table at 600
4 free blocks, 16278 free inodes, 1 used directory
Group 1: block bitmap at 33366, inodes bitmap at 33367, inode table at
33368
30510 free blocks, 16288 free inodes, 0 used directory
[...]
Group 74: block bitmap at 2424832, inodes bitmap at 2424833, inode table
at 2424834
16481 free blocks, 16288 free inodes, 0 used directory
[...]
Inodes
Inodes din tabelul de inodes al fiecărui grup conţin metadate pentru fiecare tip de date pe care îl poate stoca sistemul de fişiere. Acest tip poate fi un link simbolic, caz în care doar inodeul este suficient, poate fi un director, un fişier, un FIFO, un socket UNIX etc. În cazul fişierelor şi directoarelor datele reale sunt stocate în blocurile sistemului de fişiere din exteriorul inodeului. Primele 12 numere de blocuri sunt stocate în inode, în cazul în care este nevoie de mai multe blocuri inodeul indică un bloc indirect: un bloc cu mai multe numere de blocuri care conţin date. Astfel inodeul poate stoca un bloc indirect dublu sau triplu. Structura unui inode este dată în tabelul 3.
| Bytes | type | Descriere |
|---|---|---|
| 0 .. 1 | __le16 | Mod de fişier |
| 2 .. 3 | __le16 | Low 16 bits of Owner uid |
| 4 .. 7 | __le32 | Mărime in bytes |
| 8 .. 11 | __le32 | Access time |
| 12 .. 15 | __le32 | Ora creării |
| 16 .. 19 | __le32 | Ora modificării |
| 20 .. 23 | __le32 | Ora ştergerii |
| 24 .. 25 | __le16 | Low 16 bits of Group Id |
| 26 .. 27 | __le16 | Total Linkuri |
| 28 .. 31 | __le32 | Blocuri total |
| 32 .. 35 | __le32 | File flags |
| 36 .. 39 | linux1 | OS dependent 1 |
| 40 .. 99 | __le32[15] | Pointers to blocks |
| 100 .. 103 | __le32 | Versiune fişier (for NFS) |
| 104 .. 107 | __le32 | File ACL |
| 108 .. 111 | __le32 | Directory ACL |
| 112 .. 115 | __le32 | Fragment address |
| 116 .. 127 | linux2 | OS dependent 2 |
Structura C a unui inode struct ext3_inode, este dată în fişierul header /usr/include/linux/ext3_fs.h şi a fost utilizată pentru a crea tabelul3. Acelaşi fişier header mai defineşte un număr de constante sub forma unui macros care poate fi folosit pentru accesarea datelor. De exemplu, componenta de structură stocată de la byte 40 până la 99 este i_block, mărimea ei este EXT3_N_BLOCKS 32-bit numere de blocuri. i_block[EXT3_IND_BLOCK] indică către (conţine numărul de bloc pentru ) un bloc indirect dacă acesta există, i_block[EXT3_DIND_BLOCK] către un bloc indirect dublu şi i_block[EXT3_TIND_BLOCK] către un bloc indirect triplu. În mod normal, fiecare constantă are un macro al ei, se poate analiza fişierul header pentru mai multe detalii. ext3grep foloseşte i_reserved2 pentru a stoca numărul inodeului, astfel că tipărirea structurii unui ext3_inode în gdb arată care inode este el de fapt.
Superblocul indică câte inodes sunt în total şi câte inodes sunt per grup. Aceasta ne permite să calculăm numărul de grupuri. Deoarece inodesurile sunt stocate în tabela lor respectivă pentru grup, prima dată trebuie determinat grupul căruia îi aparţine numărul de inodes. Deoarece inodes încep a fi numărate de la 1, formula de conversie a unui număr de inodes în numărul de grup căruia îi aparţine este:
grup = (număr_inode - 1) / număr_de_inodes_per_grup
Aceasta ne dă tabela inodes corectă. Pentru aflarea index-ului inodes din acestă tabelă extragem numărul primului inodes din tabelă din numărul inodesului nostru:
index = număr_inode - (grup * număr_de_inodes_per_grup + 1)
Observaţi că acest index determină şi bitul corespunzător din harta inodes.
Atfel, grupurile au fost făcute transparente: fiecare inodes poate fi adresat cu un număr din intervalul continuu [1, număr_de_inodes], unde număr_de_inodes este dat de :
$ ext3grep $IMAGE --superblock | grep 'Inodes count' Inodes count: 1221600
În unele cazuri e posibil să doriţi să ştiţi care bloc din sistemul de fişiere aparţine tabelei de inodes care stochează un anumit inodes. Aceasta se poate face folosind în linia de comandă opţiunea --inode-to-block, de exemplu:
$ ext3grep $IMAGE --inode-to-block 2 [...] Inode 2 resides in block 600 at offset 0x80.
Inodesul cu numărul 2 (macro EXT3_ROOT_INO în ext3_fs.h) este întotdeauna folosit pentru root-ul partiţiei: tipul lui este un director. Din toate celelalte inodes speciale folosim doar EXT3_JOURNAL_INO (numărul 8).
Având numărul inodesului se poate afişa conţinutul lui folosind ext3grep, astfel:
$ ext3grep $IMAGE --inode 2 --print
Number of groups: 75
Loading group metadata... done
[...]
Hex dump of inode 2:
0000 | ed 41 00 00 00 10 00 00 97 6f aa 47 08 6c aa 47 | .A.......o.G.l.G
0010 | 08 6c aa 47 00 00 00 00 00 00 02 00 08 00 00 00 | .l.G............
0020 | 00 00 00 00 02 00 00 00 55 04 00 00 00 00 00 00 | ........U.......
0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0040 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0050 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0070 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
Inode is Allocated
Group: 0
Generation Id: 0
uid / gid: 0 / 0
mode: drwxr-xr-x
size: 4096
num of links: 2
sectors: 8 (--> 0 indirect blocks).
Inode Times:
Accessed: 1202352023 = Thu Feb 7 03:40:23 2008
File Modified: 1202351112 = Thu Feb 7 03:25:12 2008
Inode Modified: 1202351112 = Thu Feb 7 03:25:12 2008
Deletion time: 0
Direct Blocks: 1109
[...]
Inode 2 is directory "".
Directory block 1109:
.-- File type in dir_entry (r=regular file, d=directory, l=symlink)
| .-- D: Deleted ; R: Reallocated
Indx Next | Inode | Deletion time Mode File name
==========+==========+----------------data-from-inode------+-----------+=========
0 1 d 2 drwxr-xr-x .
1 end d 2 drwxr-xr-x ..
2 3 d 11 D 1202351093 Thu Feb 7 03:24:53 2008 drwxr-xr-x lost+found
3 end d 195457 D 1202352103 Thu Feb 7 03:41:43 2008 drwxr-xr-x carlo
După cum observaţi, ext3grep prima dată se descarcă conţinutul hexazecimal al tabelei inodes; apoi se interpretează şi se afişează membrii de structură terminându-se cu linia Direct Blocks: 1109. Apoi detectează că acest bloc este un director (care mai poate fi observat în câmpul MODE al inodesului) şi apoi continuă cu listarea acestui bloc ca director.
Fişierele normale
Dacă un inodes reprezintă un fişier normal, atunci blocurile la care se referă conţin doar datele fişierului. Dacă mărimea unui fişier nu este un număr întreg de ori mărimea blocului, atunci bytesii în exces din ultimul bloc vor fi scrişi cu 0 (cel puţin în Linux).
Legături simbolice
Valoarea unui link simbolic este un şir: cărarea (path) până la ţintă. Lungimea şirului este dată în i_size. Dacă i_blocks este zero, atunci i_block NU conţine numere de blocuri ci este folosit pentru stocarea efectivă a şirului. Oricum, dacă numele ţintei este mai lung decât suportă i_block, atunci i_blocks va fi non-zero şi i_block[0] va indica un bloc care conţine numele ţintei.
Directoarele
Dacă un inodes reprezintă un director atunci blocurile lui sunt legături singulare către structurile de date ale ext3_dir_entry_2. Fiecare bloc se conţine pe el: nu sunt entry point pentru directoare în exteriorul lui. Primul bloc începe întotdeauna cu intrarea directoarelor „.” şi „..”
| Bytes | type | Descriere |
|---|---|---|
| 0 .. 3 | __le32 | Număr Inodes |
| 4 .. 5 | __le16 | Lungime intrare director |
| 6 | __u8 | Lungime nume |
| 7 | __u8 | Tip fişier |
| 8 | char[] | Fişier, link simbolic sau nume director |
Folosind opţiunile --ls --inode $N, ext3grep afişează conţinutul fiecărui bloc de director pentru inodesul N. De exemplu, pentru a lista directorul rădăcină (root) al unei partiţii:
$ ext3grep $IMAGE --ls --inode 2
Number of groups: 75
Loading group metadata... done
Minimum / maximum journal block: 1115 / 35026
Loading journal descriptors... done
Journal transaction 4381435 wraps around, some data blocks might have been
lost of this transaction.
Number of descriptors in journal: 30258; min / max sequence numbers:
4379495 / 4382264
Inode is Allocated
Loading md5.ext3grep.stage2... done
The first block of the directory is 1109.
Inode 2 is directory "".
Directory block 1109:
.-- File type in dir_entry (r=regular file, d=directory, l=symlink)
| .-- D: Deleted ; R: Reallocated
Indx Next | Inode | Deletion time Mode File name
==========+==========+----------------data-from-inode------+-----------+=========
0 1 d 2 drwxr-xr-x .
1 end d 2 drwxr-xr-x ..
2 3 d 11 D 1202351093 Thu Feb 7 03:24:53 2008 drwxr-xr-x lost+found
3 end d 195457 D 1202352103 Thu Feb 7 03:41:43 2008 drwxr-xr-x carlo
Aşadar, se va folosi ext3grep --ls --inode 195457 pentru a lista directorul carlo, şi aşa mai departe.
Observaţi că ext3grep afişează toate intrările din director, şterse sau nu. Există 2 posibilităţi pentru a vedea dacă un director este şters: prima, inodesul lui va avea un Deletion Time diferit de 0, a doua – intrările directorului pot fi scoase din lista de legătură fiind omise; “Directory Entry Length”, bytes 4 şi 5 ai fiecărei intrări din director, în mod normal, indică intrarea următoare sau byte-ul care urmează blocul dacă nu sunt alte intrări de directoare. În afişarea făcută de ext2grep adresa intrărilor de directoare a fost înlocuită cu un index artifical (în prima coloană) iar “Directory Entry Length” este înlocuită cu coloana numită Next, care ori indică următoarea intrare ori conţine end când nu mai sunt alte intrări de directoare. În exemplul de mai sus 0 este prima intrare, 1 este următoarea şi ultima. Intrările cu indexul 2 şi 3 sunt omise. Totuşi încă se poate observa că intrarea 2 indica intrarea 3. De fapt, intrările 2 şi 3 sunt şterse în acelaşi timp schimbând “Directory Entry Length” a intrării 1 astfel încât să nu mai indice intrarea 2 ci sfârşitul blocului .
Deoarece ext3grep afişează şi intrările şterse este foarte posibil ca aceleaşi intrări să fie făcute de mai multe ori. În cazuri particulare, dacă un fişier este mutat, o dublură rămâne şi încă poate fi văzută. De exemplu,
$ ext3grep $IMAGE --ls --inode 195457 | grep '\.viminfo$' 7 8 r 201434 D 1202351096 Thu Feb 7 03:24:56 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 18 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 18 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 18 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 17 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 17 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 17 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195981 D 1202351097 Thu Feb 7 03:24:57 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195981 D 1202351097 Thu Feb 7 03:24:57 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195987 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195994 D 1202351111 Thu Feb 7 03:25:11 2008 rrw-r--r-- .viminfo 18 19 r 195995 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 18 19 r 195993 D 1202351097 Thu Feb 7 03:24:57 2008 rrw------- .viminfo 17 19 r 197221 D 1202351110 Thu Feb 7 03:25:10 2008 rrw-r--r-- .viminfo
Pentru a înţelege acestea, trebuie remarcat că
Întâi, aceste intrări dublate apar mai ales datorită blocurilor director dublate, care se observă deja din numărul de index al intrărilor: dacă ar fi toate din acelaşi bloc atunci toate numerele de index ar fi diferite. Desigur, fără a piping rezultatul la grep ar fi clar că fiecare intrare aparţine altui bloc director, dar acel rezultat este prea mare pentru a fi aratat aici.
În al doiela rând, trebuie să observaţi că doar numărul de inode, tipul de fişier din a 3-a coloană şi numele fişierului sunt datele din intrarea de director. Timpul ştergerii şi coloana de Mod sunt extrase din data curentă din inodeul corespunzator. Aşadar, acel inode ar fi putut fi refolosit acum mult timp de alt fişier iar datele conţinute nu ar mai fi legate de intrările acestui director. Acesta este clar cazul exemplului de mai sus deoarece este sigur faptul că acele fişiere .viminfo nu au fost şterse în aceeaşi zi. În câteva cazuri se poate detecta că un inode a fost realocat (refolosit): dacă este încă în uz (dar nu mai poate fi de această intrare de director ştearsă), sau când tipul de fişier din inode diferă de tipul de fişier din intrarea de director. În aceste cazuri a 5-a coloană arată un R în loc de D şi conţinutul inodeului nu este afişat. Astfel, deoarece aceste intrări arată puţine informaţii despre utilizare, ele sunt în mod normal eliminate. Dacă doriţi să vedeţi intrările cu inodeurile realocate cunoscute trebuie să adăugaţi la linia de comandă opţiunea --reallocated. Mai mult decât atât, uneori numărul inodeului din intrarea de director este scris cu 0. Aceste intrări sunt în mod evident nefolositoare şi eliminate şi ele. Pentru a le afişa se foloseşte în linia de comandă optiunea --zeroed-inodes.
Există posibilitatea filtrării rezultatelor afişate de --ls. O prezentare generală a filtrelor disponibile este dată de rezultatul optiunii --help :
$ ext3grep $IMAGE --help
[...]
Filters:
--group grp Only process group 'grp'.
--directory Only process directory inodes.
--after dtime Only entries deleted on or after 'dtime'.
--before dtime Only entries deleted before 'dtime'.
--deleted Only show/process deleted entries.
--allocated Only show/process allocated inodes/blocks.
--unallocated Only show/process unallocated inodes/blocks.
--reallocated Do not suppress entries with reallocated inodes.
Inodes are considered 'reallocated' if the entry
is deleted but the inode is allocated, but also when
the file type in the dir entry and the inode are
different.
--zeroed-inodes Do not suppress entries with zeroed inodes. Linked
entries are always shown, regardless of this option.
--depth depth Process directories recursively up till a depth
of 'depth'.
[...]
Pentru a putea determian valorile importante pentru --after şi --before a fost adăugată acţiunea --histogram=dtime . Această opţiune a linie de comandă face ca ext3grep să listeze o histogramă a timpului faţă de numărul inodeurilor şterse. Dacă ştergeţi un număr mare de fişiere odată, de exemplu cu rm -rf, atunci ar trebui să fie simplu calculul unei ferestre de timp în care a avut loc ştergerea. De exemplu, aici am mărit o bucată din dezastrul personal în care am şters cu puţin peste 50 000 de fişiere din directorul Home:
$ ext3grep $IMAGE --histogram=dtime --after=1202351086 --before=1202351129 Only show/process deleted entries if they are deleted on or after Thu Feb 7 03:24:46 2008 and before Thu Feb 7 03:25:29 2008. Number of groups: 75 Minimum / maximum journal block: 1115 / 35026 Loading journal descriptors... done Journal transaction 4381435 wraps around, some data blocks might have been lost of this transaction. Number of descriptors in journal: 30258; min / max sequence numbers: 4379495 / 4382264 Only show/process deleted entries if they are deleted on or after 1202351086 and before 1202351129. Only showing deleted entries. Thu Feb 7 03:24:46 2008 1202351086 0 Thu Feb 7 03:24:47 2008 1202351087 1 Thu Feb 7 03:24:48 2008 1202351088 0 Thu Feb 7 03:24:49 2008 1202351089 0 Thu Feb 7 03:24:50 2008 1202351090 0 Thu Feb 7 03:24:51 2008 1202351091 0 Thu Feb 7 03:24:52 2008 1202351092 0 Thu Feb 7 03:24:53 2008 1202351093 705 ============== Thu Feb 7 03:24:54 2008 1202351094 1698 ================================== Thu Feb 7 03:24:55 2008 1202351095 2320 =============================================== Thu Feb 7 03:24:56 2008 1202351096 3652 ========================================================================== Thu Feb 7 03:24:57 2008 1202351097 3332 =================================================================== Thu Feb 7 03:24:58 2008 1202351098 2014 ========================================= Thu Feb 7 03:24:59 2008 1202351099 1160 ======================= Thu Feb 7 03:25:00 2008 1202351100 4188 ===================================================================================== Thu Feb 7 03:25:01 2008 1202351101 2480 ================================================== Thu Feb 7 03:25:02 2008 1202351102 1945 ======================================= Thu Feb 7 03:25:03 2008 1202351103 1471 ============================== Thu Feb 7 03:25:04 2008 1202351104 2724 ======================================================= Thu Feb 7 03:25:05 2008 1202351105 3090 =============================================================== Thu Feb 7 03:25:06 2008 1202351106 3360 ==================================================================== Thu Feb 7 03:25:07 2008 1202351107 4902 ==================================================================================================== Thu Feb 7 03:25:08 2008 1202351108 698 ============== Thu Feb 7 03:25:09 2008 1202351109 1612 ================================ Thu Feb 7 03:25:10 2008 1202351110 4547 ============================================================================================ Thu Feb 7 03:25:11 2008 1202351111 2651 ====================================================== Thu Feb 7 03:25:12 2008 1202351112 1513 ============================== Thu Feb 7 03:25:13 2008 1202351113 0 Thu Feb 7 03:25:14 2008 1202351114 0 Thu Feb 7 03:25:15 2008 1202351115 0 Thu Feb 7 03:25:16 2008 1202351116 0 Thu Feb 7 03:25:17 2008 1202351117 1 Thu Feb 7 03:25:18 2008 1202351118 0 Thu Feb 7 03:25:19 2008 1202351119 0 Thu Feb 7 03:25:20 2008 1202351120 0 Thu Feb 7 03:25:21 2008 1202351121 0 Thu Feb 7 03:25:22 2008 1202351122 0 Thu Feb 7 03:25:23 2008 1202351123 0 Thu Feb 7 03:25:24 2008 1202351124 0 Thu Feb 7 03:25:25 2008 1202351125 0 Thu Feb 7 03:25:26 2008 1202351126 0 Thu Feb 7 03:25:27 2008 1202351127 0 Thu Feb 7 03:25:28 2008 1202351128 0 Thu Feb 7 03:25:29 2008 1202351129 Totals: 1202351086 - 1202351128 50064
Este important setarea unei valori bune pentru --after înainte de a recupera toate fişierele, sau vor fi recuperate prea multe fişiere.
Jurnalul
Jurnalul este un fişier format dintr-un număr fix de blocuri. Inodeul lui este EXT3_JOURNAL_INO, care de obicei este 8. Inodeul actual poate fi şi el găsit în superbloc:
$ ext3grep $IMAGE --superblock | grep 'Inode number of journal file' Inode number of journal file: 8
şi mărimea i se poate afla listând inodeul 8:
$ ext3grep $IMAGE --print --inode 8 Number of groups: 75 Loading group metadata... done Minimum / maximum journal block: 1115 / 35026 Loading journal descriptors... done Journal transaction 4381435 wraps around, some data blocks might have been lost of this transaction. Number of descriptors in journal: 30258; min / max sequence numbers: 4379495 / 4382264 Hex dump of inode 8: 0000 | 80 81 00 00 00 00 00 08 00 00 00 00 62 07 57 46 | ............b.WF 0010 | 62 07 57 46 00 00 00 00 00 00 01 00 10 01 04 00 | b.WF............ 0020 | 00 00 00 00 08 00 00 00 5b 04 00 00 5c 04 00 00 | ........[...\... 0030 | 5d 04 00 00 5e 04 00 00 5f 04 00 00 60 04 00 00 | ]...^..._...`... 0040 | 61 04 00 00 62 04 00 00 63 04 00 00 64 04 00 00 | a...b...c...d... 0050 | 65 04 00 00 66 04 00 00 67 04 00 00 68 08 00 00 | e...f...g...h... 0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 0070 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ Inode is Allocated Group: 0 Generation Id: 0 uid / gid: 0 / 0 mode: rrw------- size: 134217728 num of links: 1 sectors: 262416 (--> 34 indirect blocks). Inode Times: Accessed: 0 File Modified: 1180108642 = Fri May 25 17:57:22 2007 Inode Modified: 1180108642 = Fri May 25 17:57:22 2007 Deletion time: 0 Direct Blocks: 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 Indirect Block: 1127 Double Indirect Block: 2152
unde puteţi observa că dimensiunea jurnalului meu este de 134217728 bytes, sau 32768 de blocuri. Primele 12 blocuri sunt afişate direct în inode: blocurile 1115 – 1126. Apoi un bloc indirect este plasat în 1127. Acest bloc indirect poate conţine 1024 de numere de blocuri fiecare urmând direct blocul indirect (1128 – 2151). Apoi inodeul indică un bloc indirect dublu care conţine 31 de numere de blocuri pentru blocuri indirecte adiţionale. Numărul total de blocuri indirecte (duble / triple) este calculat să fie 34 (ştiind că un sector are 512 bytes). Aşadar, dacă totul ar fi stocat continuu, ultimul block al jurnalului ar fi 1115 + 32768 + 34 – 1 = 33916. Totuşi, jurnalul nu se încadrează în întregime în grupul 0, astfel ultimele ultimele blocuri se găsesc în grupul 1 şi headerul grupului 1 (cel mai degrabă tabela de inodeuri) este inserat undeva între blocurile jurnalului făcând ultimul bloc să fie 35025. . Pe deasupra, pot exista blocuri rele oriunde în interior. Astfel, modul corect de apropiere a jurnalului este în termenii de numere de blocuri jurnal – ‘journal block numbers’.
Primul bloc al fişierului jurnal (blocul 1115 din exemplul de mai sus) conţine superblocul jurnalului. Structura lui este definită în /usr/include/linux/jbd.h ca fiind journal_superblock_t. Ea poate fi afişată folosind :
$ ext3grep $IMAGE --journal --superblock Journal Super Block: Signature: 0x3225106840 Block type: Superblock version 2 Sequence Number: 0 Journal block size: 4096 Number of journal blocks: 32768 Journal block where the journal actually starts: 1 Sequence number of first transaction: 4382265 Journal block of first transaction: 0 Error number: 0 Compatible Features: 0 Incompatible features: 1 Read only compatible features: 0 Journal UUID: 0xe3 0x88 0xd9 0x09 0x94 0xca 0x43 0x95 0x9b 0x53 0xac 0x2c 0xd8 0xe0 0x3d 0x25 Number of file systems using journal: 1 Location of superblock copy: 0 Max journal blocks per transaction: 0 Max file system blocks per transaction: 0 IDs of all file systems using the journal: 1. 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 Minimum / maximum journal block: 1115 / 35026 Loading journal descriptors... done Journal transaction 4381435 wraps around, some data blocks might have been lost of this transaction. Number of descriptors in journal: 30258; min / max sequence numbers: 4379495 / 4382264
Aici se poate observa că jurnalul începe de fapt la Journal Block Number 1, şi ultimul bloc este Journal Block Number 32768. Acestea nu sunt deci la fel cu numere de bloc ale sistemului de fişiere. Se pot afla numerele de bloc reale, astfel
$ ext3grep $IMAGE --journal --journal-block 1 [...] Group: 0 Block 1116 belongs to the journal. [...]
care arată că Journal Block Number 1 este blocul 1116 din sistemul de fişiere.
Jurnalul este umplut cu “Transactions” care au un număr secvenţial crescător. Dacă sfârşitul jurnalului este atins, scriind continuu de la început, umplând în jur. If the end of the journal is reached, writing continuous at the start, wrapping around. Aşadar, dacă un sistem de fişiere este demontat curat, atunci la următoarea montare scrierea începe mereu de la început (cred).
O singură tranzacţie constă din una sau mai multe “Descriptors”. Ultimul descriptor al tranzacţiei este un “Commit Block”, semnalând că tranzacţia s+a terminat cu succes iar datele din descriptori anteriori au fost scrise pe disc. Mai există încă 2 tipuri de descriptori: blocuri de revocare şi blocuri conţinând “tags”. Un bloc de revocare este umplut cu numere de blocuri care ar trebui să fie (sau sunt) nealocate de această tranzacţie. Un tag este o structură care atribuie blocuri secvenţiale de jurnal (nu blocuri ale sistemului de fişiere) la blocurile sistemului de fişiere: următoarele blocuri de jurnal conţin datele care ar fi trebuit să fie scrise (au fost scrise) în blocul dar de pe sistemul de fişiere.
Aceasta face ca “tags” în particular, interesante pentru noi: ele conţin copii ale datelor care au fost scrise pe disc în trecut, incluzând inodeurile vechi.
Exemplu de recuperare manuală
În exemplul următor vom recupera manual un fişier mic. Rezultatul afişat este dat doar parţial pentru a economisi spaţiu şi a face exemplul cât mai citibil.
Folosind ext3grep $IMAGE --ls --inode găsim numele fişierului pe care dorim să îl recuperăm:
$ ext3grep $IMAGE --ls --inode 2 | grep carlo 3 end d 195457 D 1202352103 Thu Feb 7 03:41:43 2008 drwxr-xr-x carlo $ ext3grep $IMAGE --ls --inode 195457 | grep ' bin$' | head -n 1 34 35 d 309540 D 1202352104 Thu Feb 7 03:41:44 2008 drwxr-xr-x bin $ ext3grep $IMAGE --ls --inode 309540 | grep start_azureus 9 10 r 309631 D 1202351093 Thu Feb 7 03:24:53 2008 rrwxr-xr-x start_azureus
evident, inode numărul 309631 este şters şi nu avem numere de bloc pentru acest fişier:
$ ext3grep $IMAGE --print --inode 309631 [...] Inode is Unallocated Group: 19 Generation Id: 2771183319 uid / gid: 1000 / 1000 mode: rrwxr-xr-x size: 0 num of links: 0 sectors: 0 (--> 0 indirect blocks). Inode Times: Accessed: 1202350961 = Thu Feb 7 03:22:41 2008 File Modified: 1202351093 = Thu Feb 7 03:24:53 2008 Inode Modified: 1202351093 = Thu Feb 7 03:24:53 2008 Deletion time: 1202351093 = Thu Feb 7 03:24:53 2008 Direct Blocks:
Aşadar, vom încerca să căutăm o copie mai veche a lui în jurnal. Mai întâi, găsim blocul sistemului de fişiere care conţine acest inode:
$ ext3grep $IMAGE --inode-to-block 309631 | grep resides Inode 309631 resides in block 622598 at offset 0xf00.
Apoi găsim toţi descriptorii jurnalului care se referă la blocul 622598:
$ ext3grep $IMAGE --journal --block 622598 [...] Journal descriptors referencing block 622598: 4381294 26582 4381311 28693 4381313 28809 4381314 28814 4381321 29308 4381348 30676 4381349 30986 4381350 31299 4381374 32718 4381707 1465 4381709 2132 4381755 2945 4381961 4606 4382098 6073 4382137 6672 4382138 7536 4382139 7984 4382140 8931
Aceasta înseamnă că tranzacţia cu numărul secvenţial 4381294 are o copie a blocului 622598 în blocul 26582 şi asa mai departe. Cel mai mare număr de secvenţă de jos, ar trebui să fie ultimele date scrise pe disc şi deci blocul 8931 ar trebui să fie identic cu blocul curent 622598. Pentru a găsi ultima copie neştearsă trebuie pornit de jos în sus.
dacă încercaţi scrierea unui astfel de bloc, ext3grep recunoaşte că este un bloc dintr-o tabelă de inodes şi va afişa conţinutul tuturor celor 32 de inodes din ea. Noi dorim să vedem doar inode cu numărul 309631; astfel că folosimt un grep inteligent:
$ ext3grep $IMAGE --print --block 8931 | grep -A15 'Inode 309631' --------------Inode 309631----------------------- Generation Id: 2771183319 uid / gid: 1000 / 1000 mode: rrwxr-xr-x size: 0 num of links: 0 sectors: 0 (--> 0 indirect blocks). Inode Times: Accessed: 1202350961 = Thu Feb 7 03:22:41 2008 File Modified: 1202351093 = Thu Feb 7 03:24:53 2008 Inode Modified: 1202351093 = Thu Feb 7 03:24:53 2008 Deletion time: 1202351093 = Thu Feb 7 03:24:53 2008 Direct Blocks:
Acesta este într-adevăr identic cu ce am văzut în blocul 622598. Apoi urmează să ne uităm la numerele secvenţiale mai mici până când găsim unul cu 0 Deletion time. Primul găsit (de jos în sus) este blocul 6073:
$ ext3grep $IMAGE --print --block 6073 | grep -A15 'Inode 309631' --------------Inode 309631----------------------- Generation Id: 2771183319 uid / gid: 1000 / 1000 mode: rrwxr-xr-x size: 40 num of links: 1 sectors: 8 (--> 0 indirect blocks). Inode Times: Accessed: 1202350961 = Thu Feb 7 03:22:41 2008 File Modified: 1189688692 = Thu Sep 13 15:04:52 2007 Inode Modified: 1189688692 = Thu Sep 13 15:04:52 2007 Deletion time: 0 Direct Blocks: 645627
Ce este mai sus este automat generat şi poate fi făcut mult mai repede folosind în linia de comandă opţiunea --show-journal-inodes. Această opţiune va găsi blocul de care aparţine inodeul, apoi găseşte toate copiile ale acelui bloc în jurnal, şi afişează în cele din urmă doar inodeul cerut din fiecare bloc gasit (fiecare conţinând 32 de inoduri, după cum ştiţi) eliminând dublurile:
$ ext3grep $IMAGE --show-journal-inodes 309631 Number of groups: 75 Minimum / maximum journal block: 1115 / 35026 Loading journal descriptors... done Journal transaction 4381435 wraps around, some data blocks might have been lost of this transaction. Number of descriptors in journal: 30258; min / max sequence numbers: 4379495 / 4382264 Copies of inode 309631 found in the journal: --------------Inode 309631----------------------- Generation Id: 2771183319 uid / gid: 1000 / 1000 mode: rrwxr-xr-x size: 0 num of links: 0 sectors: 0 (--> 0 indirect blocks). Inode Times: Accessed: 1202350961 = Thu Feb 7 03:22:41 2008 File Modified: 1202351093 = Thu Feb 7 03:24:53 2008 Inode Modified: 1202351093 = Thu Feb 7 03:24:53 2008 Deletion time: 1202351093 = Thu Feb 7 03:24:53 2008 Direct Blocks: --------------Inode 309631----------------------- Generation Id: 2771183319 uid / gid: 1000 / 1000 mode: rrwxr-xr-x size: 40 num of links: 1 sectors: 8 (--> 0 indirect blocks). Inode Times: Accessed: 1202350961 = Thu Feb 7 03:22:41 2008 File Modified: 1189688692 = Thu Sep 13 15:04:52 2007 Inode Modified: 1189688692 = Thu Sep 13 15:04:52 2007 Deletion time: 0 Direct Blocks: 645627
Fişierul este într-adevăr mic: doar un bloc. Copiem acest bloc cu dd cum s-a arătat mai devreme:
$ dd if=$IMAGE bs=4096 count=1 skip=645627 of=block.645627 1+0 records in 1+0 records out 4096 bytes (4.1 kB) copied, 0.0166104 seconds, 247 kB/s
şi apoi edităm fişierul pentru a elimina zerourile sau copiem primii 40 de bytes (mărimea dată a fişierului) :
$ dd if=block.645627 bs=1 count=40 of=start_azureus 40+0 records in 40+0 records out 40 bytes (40 B) copied, 0.000105397 seconds, 380 kB/s $ cat start_azureus cd /usr/src/azureus/azureus ./azureus &
Recuperat!
Observaţi că este posibil să vedeţi toţi descriptorii unei tranzacţii date. Tranzacţia pe care am folosit-o să recuperăm acest fişier a fost 4382098. Tranzacţia completă poate fi observată cu :
$ ext3grep $IMAGE --journal-transaction 4382098 [...] Prev / Current / Next sequences numbers: 4382097 4382098 4382099 Transaction was NOT COMMITTED! TAG: 6074=851971 6073=622598 6072=393218 6071=393395 6070=393231 6069=393409 6068=393240 6067=393371 6066=622596 REVOKE: 506451 TAG: 6056=393217 6057=1 6058=393273 6059=393232 6060=403879 6061=393216 6062=491520 6063=506302 6064=0 6065=393219
Aici observaţi, de exemplu, TAG-ul 6072=393218, însemnând că blocul 6072 conţine o copie (veche) a blocului 393218. Nu ştiu de ce este scris că tranzacţia nu a fost terminată (pare puţin probabil). Este posibil ca blocul de decizie (commit block) să fi fost suprascris şi această tranzacţie veche a jurnalului nu mai este efectiv completă.
Recuperarea fişierelor
Desigur, ar fi enervant sî recuperăm fişiere mai mari, care sunt pe mai multe blocuri, în acest mod; lasaţi-mă să recuperez manual mii de fişiere ! Aşadar tot ce s-a prezentat mai sus poate fi automatizat. Oricum, dacă recuperaţi 50.000 de fişiere atunci nu este efectiv nici o metodă să verificaţi dacă a funcţionat mai ales dacă au fost recuperate MAI MULTE fişiere decât aţi fi dorit; va fi greu să recuperaţi tot. Ar trebui să aveţi grijă să recuperaţi fişierele cât mai exact cu putinţă.
nu est enevoie de atâta grijă pentru a recupera un singur fişier, e destul să trimiteţi pathul la ext3grep:
$ ext3grep $IMAGE --restore-file carlo/bin/start_kvm [...] Restoring carlo/bin/start_kvm $ cat RESTORED_FILES/carlo/bin/start_kvm #! /bin/sh cd /usr/src/qgt/src ./host-linux 192.168.2.4 & cd /opt/kvm/winXPpro sudo modprobe kvm_intel sudo kvm -m 384 -hda vdisk6GB.img -cdrom /dev/cdrom -localtime -std-vga -net nic,vlan=0,model=rtl8139 -net tap,vlan=0 #-snapshot # -daemonize killall -9 host-linux
Observaţi că aceasta a creat directorul RESTORED_FILES/carlo/bin în directorul curent pentru a putea recupera acest fişier. Mai observaţi şi că, dacă RESTORED_FILES/carlo/bin/start_kvm există deja în directorul curent atunci NU a fost suprascris!
Pentru ca acestea să funcţioneze va trebui prima dată să executaţi pasul 1 şi 2 al analizării discului pe care ext3grep o face (vedeţi mai jos) .
Este posibil să se descarce toate numele de fişiere pe care ext3grep le poate găsi, folosind în linia de comandă opţiunea --dump-names:
$ ext3grep $IMAGE --dump-names carlo carlo/.Trash carlo/.Xauthority carlo/.Xauthority-c carlo/.Xauthority-l carlo/.Xauthority-n carlo/.alsaplayer carlo/.alsaplayer/alsaplayer.m3u carlo/.alsaplayer/config [...] carlo/www/xcw/.svn/tmp/wcprops carlo/www/xcw/index.html carlo/www/xmlwrapp-0.5.0.tar.gz lost+found lost+found/1st level admin borders (states_provinces) lost+found/1st level admin names (states_provinces) lost+found/2002 - cloud cover (0-10%) [...]
Fişierele care vor ajunge în lost+found sunt fişiere pentru care nu s-a găsit un director (dar care incă au o copie la inode în jurnal). Cel mai sigur acestea sunt fişiere care au fost şterse cu mult timp în urmă şi pot fi aruncate, oricum.
După ce sunteţi satisfăcuţi de afişarea tuturor --dump-names, puteţi înlocui --dump-names cu --restore-all, care va determina --restore-file să fie apelată pentru fiecare nume de fişier afişat de --dump-names. După cum am spus înainte, este foarte indicat să se folosească foarte bine opţiunea --after în linia de comandă pentru a evita ca ext3grep să încerce recuperarea fisierelor care efectiv sunt prea vechi. Observaţi că în acest moment rezultatul pentru --dump-names este nefiltrat, iar --restore-file (--restore-all) DOAR foloseşte opţiunea --after în linia de comandă.
De exemplu,
$ time ext3grep $IMAGE --restore-all --after=1202351117 Only show/process deleted entries if they are deleted on or after Thu Feb 7 03:25:17 2008. [...] Loading md5.ext3grep.stage2... done Not undeleting "carlo/.Trash" because it was deleted before 1202351117 (32767) Not undeleting "carlo/.Xauthority" because it was deleted before 1202351117 (32767) [...] Cannot find an undeleted inode for file "carlo/.azureus/logs/save/1176594823051_alerts_1.log". [...] Restoring carlo/bin/startx [...] real 0m3.079s user 0m1.332s sys 0m1.744s
unde carlo/bin/startx este singurul fiţier recuperat. A fost ultimul fişier care a fost şters şi am setat valoarea opţiunii --after la o secundă înainte. Observaţi că este logic să fie ultimul fişier dacă am pornit X folosind acest script; astfel, era „în folosire” până am repornit.
Considerând că a verificat peste 50.000 de fişiere de p eo partiţie de 10GB, cele 3,1 secunde scoase inseamnă extrem de repede; acest lucru fiind cauzat de diverşi factori: 1) Prima dată când este rulat ext3grep face o analiză completă a partiţiei şi scrie rezultatele într-un fişier cache ( în 2 paşi, pasul 1 şi pasul 2). Aceşti paşi trebuie facuţi o singură dată. 2) Deoarece doar un singur fişier a trebuit sa fie recuperat, nu a fost nevoie de prea mult spaţiu pe disc (pe lângă asta, eu am 4 GB de RAM – deci orice era nevoie era deja în cache). 3) Am un procesor rapid. Programul a folosit totuşi 100% CPU în aceste 3,1 secunde. Încercând să recuperez multe fişiere se poate bloca accesul la disc, dar totuşi se rezolvă destul de repede (puteţi să staţi şi să aşteptaţi).
Pasul 1
Pasul 1 scrie fişierele cache în DEVICE.ext3grep.stage1, unde DEVICE este înlocuit cu numele dispozitivului (de exemplu, dacă $IMAGE este /dev/hda2, atunci DEVICE este hda2). Puţine pot merge rău în pasul 1: doar scanează tot discul si găseşte toate blocurile care par să conţină un director.
Formatul fişierelor cache din pasul 1 este :
$ cat md5.ext3grep.stage1 # Stage 1 data for md5. # Inodes and directory start blocks that use it for dir entry '.'. # INODE : BLOCK [BLOCK ...] 2 : 1109 6592 9312 11 : 1110 195457 : 415744 195468 : 2916 4732 17783 403469 195469 : 403470 [...] 929633 : 1885254 929659 : 1885280 # Extended directory blocks. 1178 1179 1182 [...] 1884516
În prima parte, pe prima coloană sunt inodes, urmate de un spaţiu, urmat de o coloană urmată de o listă de numere de blocuri separate prin spaţii care folosesc acel inode ca intrare cu numele „.” . Evident, poate fi doar un singur director care foloseşte acest inode, deci ext3grep trebuie să determine care din aceste numere de blocuri este ultimul care a fost cel real. A doua parte listează toate numerele de bloc care conţin blocurile extinse de director, cel existent, blocurile de director care nu sunt primele blocuri şi nu conţin intrarea de director „.”. Nu se ştie cui director îi aparţin neavând inodeul original. În Pasul 2 ext3grep va încerca să găsească cări director îi aparţin.
Pasul 2
Acest pas, executat de funcţia init_directories(), conţine în general codul heuristic. Întâi determină care blocuri sunt cu adevărat blcurile de start ale directorului, apoi atribuie fiecare bloc extins de director unui astfel de director (a se vedea şi TODO, mai jos). Ca rezultat este posibil atribuirea unui path fiecărui inode (de director). În sfârşit, acest rezultat este scris într-un fişier cache (DEVICE.ext3grep.stage2). În cazul în care ceva merge foarte rău aici, puteţi fi în stare să o rezolvaţi editând acest fişier (eliminând numere incorecte sau adăugând numerele corecte de bloc), oricum, nu adăugaţi sau eliminaţi comentarii: ext3grep va deveni confuz dacă schimbaţi fişierul prea mult.
Localizarea bazei de date
Mă tem că am folosit încă ceva pe lângă datele de pe partiţie pentru a recupera fişierele: încă mai am partiţia /var deci încă mai am baza de date locate (în /var/cache/locate/locatedb). Am folosit-o pentru a face o listă cu toate numele de fişiere care au fost şterse şi am scrs-o într-un fişier locate_output, cu formatul :
carlo carlo/.Trash carlo/.Xauthority [...]
în alte cuvinte cu acelaşi format precum output-ul --dump-names. Acest fişier este deschis şi folosit de către funcţia load_locate_data, în fişierul sursă locate.cc, care umple filename_to_locatepath_map. Această hartă este ulterior folosită de funcţia parent_directory pentru a face o ghicire educativă corecta asupra directorului părinte căruia îi aparţine un fişier dat.
Chiar şi atunci, deoarece fişierul meu de bază de date de locaţii nu era complet, a trebui să adaug manual hack-uri. În special, este posibil adăugarea unei liste codate hardcoded de expresii normale în locate.cc care determină returnarea unui director părinte specific. Totuşi, am mai codat hardcoded traducerea a 3 numere de blocuri (director) la “lost+found“. Probabil este nevoie de reglajul / hackul acestei părţi de cod dacă doriţi succesul unei recuperări corecte a datelor în directorul corect.
Dacă vedeţi mesaje pe formular:
Could not find an inode for extended directory at BLOCKNR, disregarding it's contents.
citiţi vă rog acest post în întregime pentru mai multe informaţii.
Hard linkurile în plus
Deoarece inodeurile sunt refolosite, se întâmplă des ca o intrare veche de director (a unui fişier şters, a unui director şters, sau într-un bloc director care nu mai este folosit) să indice un inode care este acum folosit de altcineva. Dacă acel altcineva este de acelaşi tip (amandouă fişiere normale) atunci nu există nici o metodă de a le distinge de hardlink: două fişiere folosind acelaşi inode. Ca rezultat, recuperarea aduce multe hardlinkuri eronate.
Pentru a face pau uşoară curăţarea acestora, ext3grep oferă opţiunea în linia de comandă --show-hardlinks.
$ ext3grep $IMAGE --show-hardlinks [...] Inode 309562: carlo/bin/pc++ (309540) carlo/bin/pcc (309540) carlo/bin/pcc.unlock (309540) Inode 702474: carlo/projects/libcwd/libcwd/.svn/entries (700387) carlo/projects/libcwd/libcwd/testsuite/tst_flush.o (700609) [...]
Aici, hardlinkurile pentru inodeul 309562 sunt corecte. Hardlinkul pentru inodeul 702474 este greşit şi unul din fişiere ar trebui şters. După ce aţi determinat manual care fişier este eronat şi l-aţi şters, va reapărea dacă reluaţi comanda. Doar acele hardlinkuri sunt raportate dacă încă există în directorul output: puteţi folosi doar --show-hardlinks după ce aţi rulat --restore-all, sau nu va rezulta nici un output deoarece nu există un fişier output.
TODO
Programul a fost scris în timp ce învăţam cum funcţionează ext3. Prima funcţie nu depinde astfel de ceea ce am scris mai târziu. Un avantaj este acela că aceste funcţionalităţi sunt mai rapide şi vor merge chiar dacă codul scris după este greşit; mai există şi multe down-to-earth, pe care le puteţi folosi pentru a verifica ce anume se întâmplă exact fără a depinde de codul mult mai complex (şi heuristic) adăugat mai târziu. Oricum, sunt şi dezavantaje: codul de filtrare pe care l-am scris pentru --ls nu este folosit de codul scris mai târziu care utilizează --dump-names şi --restore-all. De asemenea , Pasul 2 este rezolvat fără a folosi jurnalul aşa cum e posibil folosind codul scris mai târziu. Nu este uşoară această schimbare deoarece acel cod foloseşte rezultatele din pasul 2. Cred că un algoritm mai bun pentru găsirea blocurilor corecte pentru ultima copie a directorului va fi la fel cu ce am făcut pentrua-mi recupera fişierele: găsind ultimul inode neşters al acelui director din jurnal. Totuşi el nu funcţionează aşa acum.
Opţiuni pentru linia de comandă
Toate opţiunile de linii de comandă sunt scrise mai jos folosind opţiunea --help:
$ ext3grep $IMAGE --help
Usage: ext3grep [options] [--] device-file
Options:
--version, -[vV] Print version and exit successfully.
--help, Print this help and exit successfully.
--superblock Print contents of superblock in addition to the rest.
If no action is specified then this option is implied.
--print Print content of block or inode, if any.
--ls Print directories with only one line per entry.
This option is often needed to turn on filtering.
--accept filen Accept 'filen' as a legal filename.
Can be used multiple times.
--journal Show content of journal.
--show-path-inodes Show the inode of each directory component in paths.
Filters:
--group grp Only process group 'grp'.
--directory Only process directory inodes.
--after dtime Only entries deleted on or after 'dtime'.
--before dtime Only entries deleted before 'dtime'.
--deleted Only show/process deleted entries.
--allocated Only show/process allocated inodes/blocks.
--unallocated Only show/process unallocated inodes/blocks.
--reallocated Do not suppress entries with reallocated inodes.
Inodes are considered 'reallocated' if the entry
is deleted but the inode is allocated, but also when
the file type in the dir entry and the inode are
different.
--zeroed-inodes Do not suppress entries with zeroed inodes. Linked
entries are always shown, regardless of this option.
--depth depth Process directories recursively up till a depth
of 'depth'.
Actions:
--inode-to-block ino Print the block that contains inode 'ino'.
--inode ino Show info on inode 'ino'.
If --ls is used and the inode is a directory, then
the filters apply to the entries of the directory.
If you do not use --ls then --print is implied.
--block blk Show info on block 'blk'.
If --ls is used and the block is the first block
of a directory, then the filters apply to entries
of the directory.
If you do not use --ls then --print is implied.
--histogram=[atime|ctime|mtime|dtime|group]
Generate a histogram based on the given specs.
Using atime, ctime or mtime will change the
meaning of --after and --before to those times.
--journal-block jblk Show info on journal block 'jblk'.
--journal-transaction seq
Show info on transaction with sequence number 'seq'.
--dump-names Write the path of files to stdout.
This implies --ls but suppresses it's output.
--search-start str Find blocks that start with the fixed string 'str'.
--search str Find blocks that contain the fixed string 'str'.
--search-inode blk Find inodes that refer to block 'blk'.
--search-zeroed-inodes Return allocated inode table entries that are zeroed.
--inode-dirblock-table dir
Print a table for directory path 'dir' of directory
block numbers found and the inodes used for each file.
--show-journal-inodes ino
Show copies of inode 'ino' still in the journal.
--restore-file 'path' Will restore file 'path'. 'path' is relative to root
of the partition and does not start with a '/' (it
must be one of the paths returned by --dump-names).
The restored directory, file or symbolic link is
created in the current directory as ./'path'.
--restore-all As --restore-file but attempts to restore everything.
The use of --after is highly recommended because the
attempt to restore very old files will only result in
them being hard linked to a more recently deleted file
and as such polute the output.
--show-hardlinks Show all inodes that are shared by two or more files.
New functionality was more or less added top down, so this also gives a historic overview of how the program was written.
Donations
Descărcări
Acesta este linkul pentru site+ul proiectului ext3grep: http://groups.google.com/group/ext3grep/web/ext3grep-source-code-and-overview.
Vă sfătuiesc să vă alăturaţi acestui grup şi să citiţi arhiva de comentarii: the archives of the mailinglist.
Pentru a citi arhiva pe email daca aveţi un cont gmail:
http://groups.google.com/group/ext3grep/subscribe



