Jak jest zbudowany system plik贸w FAT
W tym wpisie opisz臋 podstawowe zasady dzia艂ania systemu plik贸w z rodziny FAT. Je艣li jeste艣 ciekawy co si臋 dzieje “pod mask膮”, gdy dodajesz lub usuwasz plik na no艣niku danych z systemem plik贸w FAT, to zach臋cam do lektury tego wpisu. Na przyk艂adach i w praktyce poka偶臋 jakie zmiany zachodz膮 na dysku podczas manipulacji plikami.
Z tego wpisu dowiesz si臋
- Jakie informacje mo偶na znale藕膰 w Boot Sector
- Czym jest tablica alokacji (FAT)
- Jak reprezentowany jest plik na dysku z systemem FAT
- Jak przywr贸ci膰 plik po usuni臋ciu go z dysku z systemem FAT
Czym jest system plik贸w
System plik贸w jest to metoda przechowywania i zarz膮dzania danymi na no艣niku pami臋ci. Najmniejsz膮 porcj臋 danych u偶ytkownika w systemie plik贸w najcz臋艣ciej stanowi plik. Do opisania budowy FAT potrzebne nam b臋d膮 bardziej szczeg贸艂owe poj臋cia: bajt, sektor i klaster. Definicja ta nie jest szczeg贸lnie istotna i przydatna w 偶yciu. Je艣li potrzebujesz to wiedzie膰 do kolokwium, to zajrzyj do Wikipedii.
Po przeczytaniu tego wpisu wyczujesz czym jest system plik贸w i b臋dziesz si臋 czu艂 pewnie, aby opowiedzie膰 co taki system robi.
Budowa FAT
Zacznijmy od pokazania jak taki system plik贸w wygl膮da. Wszystkie czynno艣ci tutaj przedstawione wykonuj臋 na komputerze z systemem Linux Mint.
Utworzenie czystego systemu plik贸w FAT
B臋dziemy potrzebowali urz膮dzenia do test贸w. Takiego, na kt贸rym b臋dziemy mogli wykonywa膰 normalne operacje na plikach (utw贸rz, kopiuj, usu艅). Niezb臋dna jest r贸wnie偶 mo偶liwo艣膰 niskopoziomowego podgl膮du jak dane s膮 zapisane na no艣niku (bajt po bajcie).
Na szcz臋艣cie nie b臋dziemy u偶ywa膰 fizycznego urz膮dzenia. Stworzymy plik, kt贸ry nast臋pnie zamontujemy w systemie jako wirtualny “no艣nik danych”. Mo偶na sobie to zestawi膰 z analogi膮 do fizycznego pendrive’a. Plik to nasz pendrive, a montowanie pliku w systemie, to po prostu wpi臋cie pendriva do komputera (co艣 jak plik z formatem .iso).
Najpierw utw贸rzmy pusty plik o rozmiarze, powiedzmy 100kB. Rozmiar pliku w tym przypadku b臋dzie oznacza艂 pojemno艣膰 naszego wirtualnego urz膮dzenia. +
Uwaga: plikowi nie mo偶na tak po prostu nada膰 rozmiaru. Trzeba go czym艣 wype艂ni膰. Tworz臋 wi臋c plik o nazwie fat.img
i wype艂niam go bajtami o warto艣ci zero (nie myli膰 z wype艂nianiem pliku znakami cyfry ‘0’).
dd if=/dev/zero of=fat.img bs=1024 count=100
Utworzyli艣my w ten spos贸b plik o rozmiarze 100kB. Dane tego pliku sk艂adaj膮 si臋 z samych zer. Jest to plik binarny, wi臋c wy艣wietlanie jego zawarto艣ci programami typu less
czy cat
nie jest dobrym pomys艂em.
Zobaczmy jak wygl膮da:
$ hexdump -C fat.img
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00019000
Wida膰, 偶e plik jest wype艂niony zerami. Program hexdump inteligentnie pokazuje tylko pierwsze 16 bajt贸w, po kt贸rych wstawia znak *
oznaczaj膮cy, 偶e ta sekwencja si臋 powtarza a偶 do offsetu 00019000
, czyli do samego ko艅ca pliku (19000~16~ = 102400~10~).
Stw贸rzmy w ko艅cu system plik贸w w naszym pliku.
$ mkfs.msods fat.img
mkfs.fat 4.1 (2017-01-24)
Dlaczego u偶ywam tutaj prehistorycznego FAT12? Poniewa偶 jest najprostszy w budowie i pozwoli w prosty spos贸b pokaza膰 omawiane zagadnienia. P贸藕niejsza analiza, np. FAT32 b臋dzie o wiele 艂atwiejsza znaj膮c ju偶 podstawy.
Zerknijmy teraz jak wygl膮da nasz plik w 艣rodku.
$ hexdump -C fat.img
00000000 eb 3c 90 6d 6b 66 73 2e 66 61 74 00 02 04 01 00 |.<.mkfs.fat.....|
00000010 02 00 02 c8 00 f8 01 00 20 00 40 00 00 00 00 00 |........ .@.....|
00000020 00 00 00 00 80 00 29 9c 38 71 69 4e 4f 20 4e 41 |......).8qiNO NA|
00000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 0e 1f |ME FAT12 ..|
00000040 be 5b 7c ac 22 c0 74 0b 56 b4 0e bb 07 00 cd 10 |.[|.".t.V.......|
00000050 5e eb f0 32 e4 cd 16 cd 19 eb fe 54 68 69 73 20 |^..2.......This |
00000060 69 73 20 6e 6f 74 20 61 20 62 6f 6f 74 61 62 6c |is not a bootabl|
00000070 65 20 64 69 73 6b 2e 20 20 50 6c 65 61 73 65 20 |e disk. Please |
00000080 69 6e 73 65 72 74 20 61 20 62 6f 6f 74 61 62 6c |insert a bootabl|
00000090 65 20 66 6c 6f 70 70 79 20 61 6e 64 0d 0a 70 72 |e floppy and..pr|
000000a0 65 73 73 20 61 6e 79 20 6b 65 79 20 74 6f 20 74 |ess any key to t|
000000b0 72 79 20 61 67 61 69 6e 20 2e 2e 2e 20 0d 0a 00 |ry again ... ...|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200 f8 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000400 f8 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00019000
Otrzymali艣my pusty system plik贸w na naszym urz膮dzeniu. Gdyby艣my przy tworzeniu systemu plik贸w zamiast pliku fat.img
podali 艣cie偶k臋 do zamontowanego pendrive’a, to by艣my go w ten spos贸b sformatowali i ustawili na nim system plik贸w FAT12.
Budowa Boot Sectora w FAT12
Pierwsze 512 bajt贸w, a wi臋c do offsetu 000001f0
zajmuje tzw. Boot sector naszego urz膮dzenia. Znajduj膮 si臋 tam szczeg贸艂owe informacje o u偶ywanym systemie plik贸w na urz膮dzeniu. Boot sector w FAT zawsze ko艅czy si臋 sekwencj膮 55 aa
.
Po pe艂ny opis co oznacza kt贸ry bajt w tym sektorze mo偶esz odwiedzi膰 t臋 stron臋.
Ja wypisz臋 kilka ciekawych i najwa偶niejszych warto艣ci.
Nr bajtu | Opis |
---|---|
11-12 | Liczba bajt贸w na jeden sektor. Dozwolone warto艣ci: 512, 1024, 2048, 3096. |
13 | Liczba sektor贸w na jeden klaster. Dozwolone warto艣ci: 1, 2, 4, 8, 16, 32, 64, 128 |
16 | Liczba kopii FAT (tablicy alokacji) |
22-23 | Liczba sektor贸w na jedn膮 tablic臋 alokacji |
510-511 | Sygnatura 55 aa |
Zerknijmy jeszcze raz na Boot Sector, tym razem dla u艂atwienia z offsetem dziesi臋tnym i spr贸bujmy odczyta膰 z niego warto艣ci pos艂uguj膮c si臋 tabel膮 powy偶ej.
$ od -Ad -tx1z fat.img
0000000 eb 3c 90 6d 6b 66 73 2e 66 61 74 00 02 04 01 00 >.<.mkfs.fat.....<
0000016 02 00 02 c8 00 f8 01 00 20 00 40 00 00 00 00 00 >........ .@.....<
0000032 00 00 00 00 80 00 29 9c 38 71 69 4e 4f 20 4e 41 >......).8qiNO NA<
0000048 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 0e 1f >ME FAT12 ..<
0000064 be 5b 7c ac 22 c0 74 0b 56 b4 0e bb 07 00 cd 10 >.[|.".t.V.......<
0000080 5e eb f0 32 e4 cd 16 cd 19 eb fe 54 68 69 73 20 >^..2.......This <
0000096 69 73 20 6e 6f 74 20 61 20 62 6f 6f 74 61 62 6c >is not a bootabl<
0000112 65 20 64 69 73 6b 2e 20 20 50 6c 65 61 73 65 20 >e disk. Please <
0000128 69 6e 73 65 72 74 20 61 20 62 6f 6f 74 61 62 6c >insert a bootabl<
0000144 65 20 66 6c 6f 70 70 79 20 61 6e 64 0d 0a 70 72 >e floppy and..pr<
0000160 65 73 73 20 61 6e 79 20 6b 65 79 20 74 6f 20 74 >ess any key to t<
0000176 72 79 20 61 67 61 69 6e 20 2e 2e 2e 20 0d 0a 00 >ry again ... ...<
0000192 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
0000496 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa >..............U.<
0000512 f8 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
0000528 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
0001024 f8 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
0001040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
0102400
Bajty liczymy od zera, wi臋c bajt numer 1 ma warto艣膰 3c
, a bajt numer 16 to 02
itd.
Spr贸buj samodzielnie odczyta膰 warto艣ci z Boot Sectora. Odpowiedzi zamieszczam w tabeli poni偶ej.
Pole | raw | hex | dec |
---|---|---|---|
Liczba bajt贸w na sektor | 00 02 |
0200 |
512 |
Liczba sektor贸w na klaster | 04 |
04 |
4 |
Liczba kopii FAT | 02 |
02 |
2 |
Liczba sektor贸w na FAT | 01 00 |
0001 |
1 |
Mimo tego, 偶e Boot Sector mamy przedstawiony w postaci ci膮gu bajt贸w zapisanych szesnastkowo, musz臋 rozr贸偶nia膰 tutaj warto艣膰 odczytan膮 (raw) od warto艣ci liczbowej, kt贸r膮 reprezentuje ten zapis (hex). Warto艣膰 uzyskamy czytaj膮c bajty w odwrotnej kolejno艣ci. Je艣li nie rozumiesz dlaczego, przeczytaj o formie zapisu Little Endian.
Z opis贸w z tabel ju偶 si臋 pewnie domy艣lasz jaki stosunek do siebie maj膮 bajty, sektory i klastry.
Sektor to zbi贸r bajt贸w o sta艂ym rozmiarze. Klaster to zbi贸r sektor贸w o sta艂ym rozmiarze.
Mamy informacj臋, 偶e tablica alokacji FAT (File Allocation Table) sk艂ada si臋 z jednego sektora, a wi臋c z 512 bajt贸w. Wiemy r贸wnie偶, 偶e nasz system plik贸w przechowuje 2 kopie tablicy alokacji (jest to pewna forma zabezpieczenia). Sprawd藕my, czy to co odczytali艣my z Boot Sectora si臋 zgadza.
Dok艂adnie od offsetu 512 zaczyna si臋 pierwsza tablica alokacji FAT, kt贸ra ko艅czy si臋 512 bajt贸w dalej. Nast臋pnie zaczyna si臋 identyczna kopia FAT. S膮 tam ju偶 wpisane jakie艣 warto艣ci. Om贸wimy je za moment.
Budowa tablicy alokacji FAT
Tablica alokacji jest tak膮 map膮 po pami臋ci. Je艣li mamy jaki艣 du偶y plik, kt贸ry zajmuje kilka sektor贸w, tablica alokacji powie nam kt贸re kolejne sektory powinni艣my odczytywa膰, aby otrzyma膰 ca艂y plik.
W FAT12 jeden klaster jest kodowany na 12 bitach, a wi臋c jest to 1,5 bajta. Powoduje to troch臋 komplikacji przy pr贸bie r臋cznego odczytywania warto艣ci z postaci szesnastkowej, kt贸r膮 my mamy. Dla FAT16 i FAT32 jest to du偶o prostsze.
Odczytajmy zawarto艣膰 tablicy alokacji, bo wida膰, 偶e ju偶 co艣 si臋 tam w niej znajduje. Zaczynamy od offsetu 512. Algorytm jest nast臋puj膮cy:
- Wypisz bajty z tablicy alokacji. Zera z ko艅ca mo偶emy pomin膮膰.
f8 ff ff
- 艁膮czymy bajty w tr贸jki od lewej do prawej (w naszym przypadku mamy ju偶 tr贸jk臋, wi臋c nic nie robimy).
ff ff f8
- Przepisujemy wszystkie bajty w tr贸jkach, w odwrotnej kolejno艣ci
(Taki zapis:
ff ff 8f
nie jest poprawnym odwr贸ceniem bajt贸w).
ff ff f8
- Wybrane tr贸jki dzielimy na dwie r贸wne cz臋艣ci.
fff ff8
- Ka偶d膮 z par odczytujemy w odwr贸conej kolejno艣ci. Otrzymali艣my tablic臋 alokacji w postaci szesnastkowej.
ff8 fff
[0] [1] - numery klastr贸w
P贸藕niej b臋dziemy odczytywa膰 bardziej skomplikowane tablice alokacji, wi臋c powr贸cimy do tego algorytmu.
Wiemy zatem, 偶e klaster o numerze 0 jest zakodowany jako ff8
, a klaster 1 jako fff
. Wszystkie pozosta艂e klastry w urz膮dzeniu s膮 oznaczone jako 000
. Co te liczby oznaczaj膮?
Warto艣膰 | Opis |
---|---|
000 |
Klaster wolny |
002-fef |
Klaster zaj臋ty |
ff0-ff6 |
Klaster zarezerwowany |
ff7 |
Z艂y sektor |
ff8-fff |
Ostatni klaster |
Wiemy zatem, 偶e klastry 0 i 1 s膮 ostatnimi klastrami. Nasze urz膮dzenie nie ma jeszcze 偶adnego pliku, wi臋c klastry te nie reprezentuj膮 偶adnego pliku.
Co to znaczy, 偶e klaster jest ostatni? Poka偶emy to dok艂adnie p贸藕niej, przy omawianiu plik贸w, kt贸re zajmuj膮 wi臋cej ni偶 jeden klaster.
Root Directory Table
Dodajmy w ko艅cu jaki艣 plik do naszego urz膮dzenia i zobaczmy co si臋 zmieni. Najpierw b臋dziemy musieli zamontowa膰 nasze urz膮dzenie fat.img
w systemie.
$ mkdir fs
$ sudo mount -t msdos fat.img fs -o umask=000,loop
Po utworzeniu katalogu montujemy do niego nasze urz膮dzenie. Dodatkowe opcje, kt贸rych u偶ywamy rozwi膮zuj膮 problem uprawnie艅 (umask) i pozwalaj膮 zamontowa膰 plik jako urz膮dzenie Loop Device.
Mo偶emy teraz przej艣膰 do folderu fs
. B臋dzie pusty. Utw贸rzmy tam plik.
$ cd fs
$ echo "Witaj" > hello
Powr贸膰my do naszego fat.img
i sprawd藕my jego zawarto艣膰. Zanim to jednak zrobimy zapiszmy wszystkie oczekuj膮ce zmiany do pami臋ci trwa艂ej poprzez wykonanie sync
.
$ sync
$ od -Ax -tx1z fat.img
[...]
000200 f8 ff ff 00 f0 ff 00 00 00 00 00 00 00 00 00 00 >................<
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000400 f8 ff ff 00 f0 ff 00 00 00 00 00 00 00 00 00 00 >................<
000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000600 48 45 4c 4c 4f 20 20 20 20 20 20 20 00 00 00 00 >HELLO ....<
000610 00 00 00 00 00 00 fc 6d 07 4f 03 00 06 00 00 00 >.......m.O......<
000620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
004e00 57 69 74 61 6a 0a 00 00 00 00 00 00 00 00 00 00 >Witaj...........<
004e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
019000
Pomijam ju偶 wypisywanie Boot Sectora, bo nie ulega on zmianom. Widzimy nazw臋 naszego pliku oraz jego zawarto艣膰. Offset 600
(hex) jest to pocz膮tek tzw. Root Directory Table. Jest to, jak sama nazwa wskazuje, nasz folder g艂贸wny, kt贸re mo偶e przechowywa膰 pliki i foldery. Ilo艣膰 plik贸w i folder贸w w nim jest ograniczona. Obecne systemy plik贸w nie posiadaj膮 ju偶 takich ogranicze艅.
Dane pliku “hello” s膮 zapisane na 32 bajtach (2 wiersze). Ka偶dy bajt koduje pewn膮 informacj臋 o pliku.
Nr bajtu | Opis |
---|---|
0-10 | Nazwa pliku (8), rozszerzenie (3) |
11 | Atrybuty pliku |
12-21 | Reserved |
22-23 | Czas |
24-25 | Data |
26-27 | Pocz膮tkowy klaster (0 - pusty plik) |
28-31 | Rozmiar pliku w bajtach |
Po wi臋cej informacji zajrzyj tutaj.
Odczytywanie kr贸tkiego pliku
Spr贸bujmy odczyta膰 plik hello
z naszego urz膮dzenia w podobny spos贸b jak robi to komputer.
W Root Directory Table po nazwie odnajdujemy nasz plik hello
.
000600 48 45 4c 4c 4f 20 20 20 20 20 20 20 00 00 00 00 >HELLO ....<
000610 00 00 00 00 00 00 fc 6d 07 4f 03 00 06 00 00 00 >.......m.O......<
Odnajduj臋 rozmiar pliku na bajtach 28-31. Jest to wi臋c liczba 4-bajtowa zapisana w Little Endian. Rozmiar pliku to 6 bajt贸w
bajty | raw | hex | dec |
---|---|---|---|
28-31 | 06 00 00 00 |
00000006 |
6 |
Odczytuj臋 r贸wnie偶 na bajtach 26 i 27 pocz膮tkowy klaster, gdzie znajduje si臋 zawarto艣膰 tego pliku.
bajty | raw | hex | dec |
---|---|---|---|
26-27 | 03 00 |
0003 |
3 |
Wiemy, zatem, 偶e trzeci sektor jest sektorem, w kt贸rym zaczyna si臋 nasz plik. Udajemy si臋 do tablicy alokacji i patrzymy jak jest zakodowany trzeci sektor.
Tablica alokacji wygl膮da tak:
000200 f8 ff ff 00 f0 ff 00 00 00 00 00 00 00 00 00 00 >................<
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<.
*
Pos艂u偶ymy si臋 algorytmem opisanym wcze艣niej, aby odczyta膰 kodowania dla poszczeg贸lnych klastr贸w. Jak nabierzesz wprawy, to b臋dziesz to odczytywa膰 zwyk艂ym spojrzeniem na powy偶szy ci膮g bajt贸w (cho膰 nie wiem po co ktokolwiek mia艂by nabiera膰 wprawy w r臋cznym odczytywaniu tablicy alokacji).
f8 ff ff 00 f0 ff - (1) wypisujemy tablic臋 (bez zer na ko艅cu)
f8 ff ff 00 f0 ff - (2) 艂膮czymy bajty w tr贸jki
ff ff f8 ff f0 00 - (3) odwracamy kolejno艣膰
fff ff8 fff 000 - (4) sklejamy tr贸jki
ff8 fff 000 fff - (5) odwracamy
ff8 fff 000 fff - wynik ko艅cowy
[0] [1] [2] [3] - numery klastr贸w
Nasz plik zaczyna si臋 od klastra nr 3. Wed艂ug tablicy alokacji, klaster [3] (fff
) jest klastrem ostatnim (patrz: «tabela-alokacji-fat, Tabela z oznaczeniami w tablicy alokacji»).
Nasz plik zatem sk艂ada si臋 tylko z klastra nr 3. Znamy d艂ugo艣膰 ka偶dego klastra oraz miejsce, gdzie zaczyna si臋 klaster [0]
. Przeskakujemy wi臋c zadan膮 liczb臋 klastr贸w i zaczynamy czyta膰 klaster [3]
od bajtu 004e00~16~.
004e00 57 69 74 61 6a 0a 00 00 00 00 00 00 00 00 00 00 >Witaj...........<
004e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
Plik w systemie FAT nie mo偶e zajmowa膰 zajmowa膰 na dysku mniej ni偶 jeden klaster. Nawet je艣li zawiera kilka liter jak w naszym przypadku. Musimy zatem wiedzie膰 gdzie w klastrze ko艅cz膮 si臋 dane pliku. Odczytali艣my wcze艣niej rozmiar pliku, kt贸ry wynosi 6 bajt贸w.
6 pierwszych bajt贸w trzeciego klastra to: Witaj\n
. Koniec.
Odczytywanie d艂ugiego pliku
Przyj偶yjmy si臋 teraz co si臋 stanie, gdy plik b臋dzie d艂u偶szy ni偶 jeden klaster. Najpierw musimy taki plik utworzy膰. Zr贸bmy to inteligentnie. Odczytali艣my wcze艣niej, 偶e klaster sk艂ada si臋 z 4 sektor贸w. Jeden sektor ma 512 bajt贸w, zatem klaster pomie艣ci 2048 bajt贸w.
Plik, kt贸ry dodamy b臋dzie si臋 sk艂ada艂 z 2048 literek a
, a na koniec dokleimy bcdef
. Je艣li nasze obliczenia s膮 poprawne, to literki a
zostan膮 zapisane do jednego sektora, a dalsza cz臋艣膰 pliku bcdef
do innego.
Przechodzimy do katalogu fs
i generujemy plik.
$ python -c "print('a'*2048 + 'bcdef')" > duzy
$ sync
Zobaczmy teraz co nam powsta艂o w pliku fat.img
.
000200 f8 ff ff 00 f0 ff 05 f0 ff 00 00 00 00 00 00 00 >................<
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000400 f8 ff ff 00 f0 ff 05 f0 ff 00 00 00 00 00 00 00 >................<
000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000600 48 45 4c 4c 4f 20 20 20 20 20 20 20 00 00 00 00 >HELLO ....<
000610 00 00 00 00 00 00 fc 6d 07 4f 03 00 06 00 00 00 >.......m.O......<
000620 44 55 5a 59 20 20 20 20 20 20 20 20 00 00 00 00 >DUZY ....<
000630 00 00 00 00 00 00 dc 84 07 4f 04 00 06 08 00 00 >.........O......<
000640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
004e00 57 69 74 61 6a 0a 00 00 00 00 00 00 00 00 00 00 >Witaj...........<
004e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
005600 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 >aaaaaaaaaaaaaaaa<
*
005e00 62 63 64 65 66 0a 00 00 00 00 00 00 00 00 00 00 >bcdef...........<
005e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
019000
Jak ostatnio obci膮艂em Boot Sector.
Tym razem z Root Directory Table odczytujemy, 偶e pierwszym sektorem jest sektor [4]. Nie b臋d臋 tu powtarza艂 procedury odczytywania tego. Post臋powanie jest analogiczne co z kr贸tkim plikiem.
Odczytajmy tablic臋 alokacji, kt贸ra zdecydowanie uleg艂a zmianie. Wygl膮da ona tak:
000200 f8 ff ff 00 f0 ff 05 f0 ff 00 00 00 00 00 00 00 >................<
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
Zasady odczytu takie same jak ostatnio.
f8 ff ff 00 f0 ff 05 f0 ff - (1) wypisujemy tablic臋
f8 ff ff 00 f0 ff 05 f0 ff - (2) 艂膮czymy w tr贸jki
ff ff f8 ff f0 00 ff f0 05 - (3) odwracamy kolejno艣膰
fff ff8 fff 000 fff 005 - (4) sklejamy tr贸jki
ff8 fff 000 fff 005 fff - (5) odwracamy
ff8 fff 000 fff 005 fff- wynik ko艅cowy
[0] [1] [2] [3] [4] [5]- numery klastr贸w
Odczytujemy:
- Plik
duzy
zaczyna si臋 od klastra [4]. - Patrzymy, a klaster o tym numerze jest kodowany jako
005
. - Oznacza to, 偶e dalsza cz臋艣膰 pliku znajduje si臋 w klastrze [5].
- Patrzymy do klastra [5] i odczytujemy
fff
. Zatem jest to ostatni klaster.
Teraz wystarczy przeczyta膰 klastry w kolejno艣ci [4][5]
. Klaster [4]
zaczyna si臋 od offsetu 005600
i rzeczywi艣cie 2048 bajt贸w dalej, tam gdzie spodziewamy si臋 klastra [5]
odnajdujemy dalsz膮 cz臋艣膰 pliku bcdef
.
My艣l臋, 偶e ju偶 czujesz jak ten system dzia艂a. Oczywi艣cie s膮 to tylko proste operacje. Zach臋cam do samodzielnego eksperymentowania. Jak system plik贸w b臋dzie za艣miecony, to zawsze mo偶esz utworzy膰 nowy. Spr贸buj np. dopisa膰 co艣 do kt贸rego艣 z plik贸w i sprawd藕 co si臋 stanie (spoiler: stara wersja pliku sprzed zmiany zostanie zachowana w pami臋ci).
Usuwanie i odzyskiwanie pliku
Przechodzimy do jednej z najciekawszych rzeczy, czyli co si臋 dzieje gdy usuwamy plik i czy mo偶na go odzyska膰. Je艣li tak, to jak to zrobi膰. Tym si臋 teraz zajmiemy.
Nadal b臋dziemy pracowa膰 na pliku fat.img
, kt贸ry mamy z poprzednich paragraf贸w. Je艣li Tw贸j obecny po zabawach uleg艂 uszkodzeniu lub za艣mieceniu, to mo偶esz go odtworzy膰 w ten spos贸b:
dd if=/dev/zero of=fat.img bs=1024 count=100
mkfs.msdos fat.img
mkdir fs
sudo mount -t msdos fat.img fs -o umask=000,loop
cd fs
echo "Witaj" > hello
python -c "print('a'*2048 + 'bcdef)" > duzy
sync
Plik z kt贸rym zaczynamy wygl膮da nast臋puj膮co.
$ od -Ax -tx1z fat.img
[...]
000200 f8 ff ff 00 f0 ff 05 f0 ff 00 00 00 00 00 00 00 >................<
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000400 f8 ff ff 00 f0 ff 05 f0 ff 00 00 00 00 00 00 00 >................<
000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000600 48 45 4c 4c 4f 20 20 20 20 20 20 20 00 00 00 00 >HELLO ....<
000610 00 00 00 00 00 00 fc 6d 07 4f 03 00 06 00 00 00 >.......m.O......<
000620 44 55 5a 59 20 20 20 20 20 20 20 20 00 00 00 00 >DUZY ....<
000630 00 00 00 00 00 00 dc 84 07 4f 04 00 06 08 00 00 >.........O......<
000640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
004e00 57 69 74 61 6a 0a 00 00 00 00 00 00 00 00 00 00 >Witaj...........<
004e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
005600 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 >aaaaaaaaaaaaaaaa<
*
005e00 62 63 64 65 66 0a 00 00 00 00 00 00 00 00 00 00 >bcdef...........<
005e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
019000
Mamy zatem dwa pliki. Jeden ma艂y i du偶y. Ka偶dy z nich usuniemy i spr贸bujemy odzyska膰.
Usuwanie kr贸tkiego pliku
Sama procedura jest do艣膰 prosta. Po prostu wchodzimy do kadalogu fs
i usuwamy plik hello
.
$ rm hello
$ sync
No i posz艂o. Pliku nie ma, ale pytanie co si臋 w praktyce sta艂o? Okazuje si臋, 偶e w ca艂ym pliku fat.img
zmieni艂o si臋 tylko kilka bajt贸w. To wyja艣nia dlaczego usuwanie pliku jest du偶o szybsze ni偶 jego kopiowanie. Dane ca艂y czas siedz膮 na dysku. Poni偶ej wycinek zrzutu pami臋ci po usuni臋ciu pliku hello
.
000400 f8 ff ff 00 00 00 05 f0 ff 00 00 00 00 00 00 00 >................<
000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000600 e5 45 4c 4c 4f 20 20 20 20 20 20 20 00 00 00 00 >.ELLO ....<
000610 00 00 00 00 00 00 fc 6d 07 4f 03 00 06 00 00 00 >.......m.O......<
000620 44 55 5a 59 20 20 20 20 20 20 20 20 00 00 00 00 >DUZY ....<
000630 00 00 00 00 00 00 dc 84 07 4f 04 00 06 08 00 00 >.........O......<
000640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
004e00 57 69 74 61 6a 0a 00 00 00 00 00 00 00 00 00 00 >Witaj...........<
004e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
Co dok艂adnie zasz艂o to:
- W tablicy alokacji (offset
000400
i000200
) zwolni艂 si臋 klaster [3] zajmowany przez plikhello
- Bajt z pierwsz膮 liter膮 w nazwie pliku zmieni艂 warto艣膰 na
e5
.
Plik i jego dane nadal znajduj膮 si臋 na dysku. Oczywi艣cie mo偶e zosta膰 w ka偶dej chwili nadpisany je艣li zaczniemy tworzy膰 nowe pliki.
Odzyskiwanie kr贸tkiego pliku
Co nale偶y zrobi膰, aby odzyska膰 ten plik? W przypadku pliku, kt贸ry zajmuje tylko jeden klaster jest to proste.
- Odnajdujemy plik w Root Directory Table (lub w folderze gdzie si臋 znajdowa艂)
- Odczytujemy klaster od kt贸rego si臋 rozpoczyna ten plik.
- Je艣li klaster jest oznaczony jako zaj臋ty, to ju偶 jest za p贸藕no. Dane zosta艂y nadpisane przez inny plik.
- Je艣li klaster jest wolny
000
, to jest szansa, 偶e dane z tego pliku nadal si臋 w nim znajduj膮. - R臋cznie oznaczamy klaster jako zaj臋ty
fff
- Zmieniamy bajt w nazwie pliku
e5
na inny znak, np. liter臋.
Aby to wykona膰 mo偶na pos艂u偶y膰 si臋 jakim艣 Hexedytorem. Jest to oczywiste, gdy dane mamy zapisane w pliku.
Co je艣li chcemy to wykona膰 na urz膮dzeniu typu pendrive? Jest to troch臋 trudniejsze, ale nadal wykonalne. Wystarczy zrzuci膰 z pendrive’a pami臋膰 do pliku, podobnie jak robili艣my tworz膮c plik fat.img
programem dd
. Nie musimy nawet zrzuca膰 ca艂o艣ci, wystarczy fragment. Po dokonaniu zmian wgrywamy zmodyfikowan膮 pami臋膰 do urz膮dzenia (w to samo miejsce, z kt贸rego j膮 pobrali艣my).
Problemy si臋 zaczynaj膮, gdy chcemy odzyska膰 du偶y plik. Taki, kt贸ry zajmowa艂 kilka klastr贸w.
Usuwanie d艂ugiego pliku
Usu艅my duzy
plik i popatrzmy co si臋 sta艂o.
$ rm duzy
$ sync
A oto efekt naszych zmian.
000200 f8 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000400 f8 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
000600 e5 45 4c 4c 4f 20 20 20 20 20 20 20 00 00 00 00 >.ELLO ....<
000610 00 00 00 00 00 00 fc 6d 07 4f 03 00 06 00 00 00 >.......m.O......<
000620 e5 55 5a 59 20 20 20 20 20 20 20 20 00 00 00 00 >.UZY ....<
000630 00 00 00 00 00 00 dc 84 07 4f 04 00 06 08 00 00 >.........O......<
000640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
004e00 57 69 74 61 6a 0a 00 00 00 00 00 00 00 00 00 00 >Witaj...........<
004e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
005600 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 >aaaaaaaaaaaaaaaa<
*
005e00 62 63 64 65 66 0a 00 00 00 00 00 00 00 00 00 00 >bcdef...........<
005e10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
*
019000
Tablica alokacji jest pusta. Wszystkie klastry zajmowane przez plik zosta艂y zwolnione, a pierwszy bajt w nazwie pliku duzy
ma warto艣膰 e5
. 呕adnych zaskocze艅.
Odzyskiwanie d艂ugiego pliku
Teraz mamy problem i to powa偶ny. Co prawda z Root Directory Table nadal mo偶emy odczyta膰 pierwszy sektor zajmowany przez plik, ale gdy udamy si臋 do tablicy alokacji pod wskazany sektor, to oka偶e si臋, 偶e jest on wolny 000
. To 艣wietne wie艣ci, ale pozostaje pytanie, kt贸ry klaster jest nast臋pny?
Po rozmiarze pliku mo偶emy obliczy膰 ile klastr贸w b臋dzie on zajmowa膰. Musimy je “tylko” odnale藕膰 i to jeszcze w dobrej kolejno艣ci.
Niestety nie ma tutaj b艂yskotliwego rozwi膮zania tego problemu. Zgadywanie zwykle nie wchodzi w gr臋. Jest jednak wiele metod, kt贸re pozwol膮 zwi臋kszy膰 szans臋 na odzyskanie takiego pliku. Poni偶ej kilka z nich:
- Defragmentacja dysku cz臋sto sprawia, 偶e wszystkie klastry pliku znajduj膮 si臋 obok siebie jeden za drugim. Z drugiej strony wykonanie defragmentacji po usuni臋ciu pliku mo偶e “zapcha膰 dziury po usuni臋tych plikach”, a wi臋c nadpisa膰 ich dane.
- Stosowanie r贸偶nego rodzaju heurystyk.
- Rozpoznawanie formatu pliku po pierwszym klastrze. Wiemy wtedy jakiej nast臋pnej cz臋艣ci si臋 spodziewa膰, np. gdy odzyskujemy plik wideo, to czasami wraz z zakodowanym obrazem znajduj膮 si臋 timestamp’y okre艣laj膮ce moment w filmie.
Jest zapewne du偶o wi臋cej sposob贸w. Wiele z nich to tajemnice firm produkuj膮cych oprogramowanie do odzyskiwania danych z dysk贸w. Je艣li pr贸bowa艂e艣 kiedy艣 kt贸rego艣 z tych “darmowych” narz臋dzi do odzyskiwania plik贸w po ich usuni臋ciu, to wiesz ju偶 teraz dlaczego one z tak膮 艂atwo艣ci膮 wypisywa艂y usuni臋te pliki z nazwami, a za ich odzyskanie trzeba by艂o ju偶 zap艂aci膰. Odnalezienie usuni臋tych plik贸w jest po prostu banalnie proste.
M贸wi臋 tutaj oczywi艣cie o formacie FAT. Struktury plik贸w zapisanych w NTFS czy ext s膮 zupe艂nie inne i bardziej skomplikowane. Nawet w samym FAT32 jest ju偶 sporo zmian w stosunku do FAT12, kt贸ry przedstawia艂em.
Podsumowanie
Je艣li nigdy wcze艣niej nie mia艂e艣, czytelniku, okazji pobawi膰 si臋 z systemem plik贸w, to mam nadziej臋, 偶e wykonane tutaj eksperymenty i zaprezentowane przyk艂ady sporo nauczy艂y. To dopiero wierzcho艂ek g贸ry lodowej. O samym FAT12 mo偶naby zrobi膰 jeszcze 10 podobnej d艂ugo艣ci artyku艂贸w. Zamiast to robi膰, lepszym pomys艂em jest zapoznanie si臋 z nowszymi systemami plik贸w. Por贸wna膰 om贸wiony tutaj FAT12 z u偶ywanym jeszcze powszechnie na pendrive’ach FAT32.
Do czego mo偶e si臋 przyda膰 taka wiedza? Ma艂o kto r臋cznie grzebie w bajtach, wi臋c wiedza o dzia艂aniu systemu plik贸w przyda si臋 do napisania oprogramowania, kt贸ry na takim systemie operuje. Poza tym daje og贸lne zrozumienie wielu proces贸w, kt贸re zachodz膮 w komputerze. Mimo, 偶e o tym nie wspomnia艂em, to po przeczytaniu tego artyku艂u powiniene艣 ju偶 rozumie膰 dlaczego w Windowsie ka偶dy plik ma rozmiar i rozmiar na dysku. To tak jak nasz kr贸tki plik, kt贸ry mia艂 rozmiar 6 bajt贸w, a na dysku zajmowa艂 jeden klaster, a wi臋c 2 kB. To spore marnotrawstwo pami臋ci i wsp贸艂czesne systemy plik贸w lepiej sobie z tym radz膮.
Dygresja o NTFS
W NTFS na Windowsie jest troch臋 inaczej. Zr贸b eksperyment. Utw贸rz w Windowsie pusty plik i sprawd藕 jego rozmiar i rozmiar na dysku. Oba b臋d膮 wynosi膰 0 B. Dodaj literk臋
a
do pliku, zapisz i sprawd藕. Ile wynosi rozmiar? 1 B. Rozmiar na dysku nadal 0 B. NTFS dzia艂a troch臋 inaczej i jest w stanie dane kr贸tkich plik贸w zmie艣ci膰 wraz z nazw膮 w jednym rekordzie bez zajmowania ca艂ego klastra. Po zwi臋kszeniu pliku do 1 kB, rozmiar na dysku wyniesie 4 kB (w moim przypadku). Po zmniejszeniu pliku do 1 B, rozmiar na dysku pozostaje 4 kB. Wida膰, 偶e ten system plik贸w jest inteligentniejszy.