4.1 FAT

FAT jest systemem plików, który powstawał razem z systemem DOS, a potem Windows. W tym czasie rozwinęły się jego kolejne wersje FAT12, FAT16, FAT32. Nie nastąpiła jednak zasadnicza zmiana struktury samego systemu tylko jego rozszerzenie do obsługi większych dysków z zachowaniem kompatybilności do poprzednich wersji. Jedyne różnice między tymi trzema rozszerzeniami zawierają się w nagłówku BPB (BIOS Parameter Block) i w rozmiarze wpisów w tablicy FAT.

Całą strukturę sektorów systemu można przedstawić w blokowym schamacie:

Struktura systemu plików FAT
1. Boot Sector
2. File Allocation Table
3. Root Directory
4. Dane

Boot sector

Na pierwszy sektor dysku zwany sektorem rozruchowym BS (Boot Sector) składają się najważniejsze części składowe systemu FAT. Po pierwsze jest tam struktura opisująca techniczne parametry dysku i kod ładowania do pamięci systemu operacyjnego BL (Boot Loader). Jak wiadomo sektor na dysku zajmuje 512 bajtów. Pierwsza jego część zajęta jest przez blok parametrów nośnika BPB. Struktura BPB różni się nieco w zależności od wersji systemu FAT. Jej opis znajduje się w tabeli poniżej.

Nazwa pola Adres Rozmiar Opis
0 3 Bliski skok do programu ładującego system znajdującego się w tym samym sektorze za blokiem parametrów nośnika.
SysName 3 8 Nazwa systemu, który formatował dysk lub będzie się z niego ładował. Podobno lepiej zostawić tutaj "MSWIN4.1" bo niektóre systemy mogą nie rozpoznać dysku poprawnie, ale większość normalnych systemów ignoruje to co jest tu napisane więc można tu napisać co się chce.
BytesPerSector B 2 Rozmiar sektora w bajtach. Jeżeli jest to dyskietka FDD to zwykle jest to 0x200, a jeżeli dysk twardy to pole może zawierać jeszcze 0x400, 0x800 lub 0x1000.
SectorsPerCluster D 1 Jest to ilość sektorów na jednostkę alokacji. Wartość tu wpisana powinna być potęgą 2 (2,4,8,16,...), ale powinno się też zachować nierówność BytesPerSector*SectorPerCluster < 32*1024.
ReservedSectors E 2 Ilość zarezerwowanych sektorów. Jest to ilość sektorów jaka poprzedza tablice FAT na danym nośniku. Musi to być minimalnie 1 bo tyle właśnie potrzeba na BootSector. Czasem program ładujący system używa jeszcze paru następnych sektorów dodatkowo i wtedy trzeba ustawić tu konkretną ich liczbę.
FATNumber 0x10 1 Ilość tablic FAT. Zwykle jest tu 2 i radzę tego nie zmieniać bo 2 to norma.
MaxRootEntries 0x11 2 Maksymalna ilość wpisów w RootDirectory czyli głównym katalogu dla FAT12 i FAT16. Dla FAT32 to pole powinno być równe 0.
TotalSectorNumber 0x13 2 Dla FAT12 i FAT16 łączna suma sektorów jakie posiada dany nośnik. Dla FAT32 pole równe jest 0, a ilość sektorów jest zapisana w polu TotalSectorNumber32.
Media 0x15 1 Bajt identyfikacji nośnika. Patrz następna tabela.
FATSectorNumber 0x16 2 Ile sektorów zajmuje jedna tablica FAT <- dla FAT12 i FAT16. Dla FAT32 zapisane jest to w polu FATSectorNumber32
SectorsPerTrack 0x18 2 Ilość Sektorów na ścieżkę (ścieżkę/cylinder). (Używane przez przerwanie 0x13)
HeadNumber 0x1A 2 Liczba głowic. (Używane przez przerwanie 0x13)
HiddenSectors 0x1C 4 Ilość ukrytych sektorów na danej partycji. Dla nośników nie partycjonowanych wynosi 0. (Używane przez przerwanie 0x13.)
TotalSectorNumber32 0x20 4 Łączna ilość sektorów dla FAT32.

Dalsza część różni się między FAT12/16, a FAT32.

FAT12 i FAT16 od adresu 0x24

DriveNumber 0x24 1 Numer napędu używany przy ładowaniu systemu operacyjnego. (Używane przez przerwanie 0x13.)
reserved 0 3 Używane przez Windows. Powinno się zawsze tu ustawiać wartość 0.
BootSignature 0x26 1 Dodatkowa sygnatura dysku bootowalnego. Wartość 0x29
VolumeSerial 0x27 4 Numer seryjny dysku.
VolumeLabel 0x2B 11 Etykieta dysku.
FileSystemType 0x36 8 "FAT12 ", "FAT16 " lub "FAT "

Pole tylko informacyjne, systemy MS nie używają go aby identyfikować rodzaj FATu na dysku

FAT32 od adresu 0x24

FATSectorNumber32 0x24 4 Ile sektorów zajmuje jedna tablica FAT. <- Dla FAT32 pole FATSectorSize = 0
ExtendedFlags 0x28 2 Niestety nie jestem w stanie opisać tego pola, więc odsyłam do dokumentacji.
FATVersion 0x2A 2 Wersja FAT32.
RootCluster 0x2C 4 Numer pierwszego klastra RootDir.
FSInfo 0x30 2 Znowu odsyłam do dokumentacji.
BckBootSector 0x32 2 Jeżeli inne niż 0 to numer sektora z pośród sektorów zarezerwowanych w którym jest zapisana kopia BootSectora.
reserved 0x34 12
DriveNumber32 0x40 1 Analogicznie do FAT12/16
reserved 0x41 1
BootSignature32 0x42 1 Analogicznie jak w FAT12/16
VolumeSerial32 0x43 4 Analogicznie jak w FAT12/16
VolumeLabel32 0x47 11 Analogicznie jak w FAT12/16
FileSystemType32 0x52 8 Informacyjny ciąg znaków "FAT32 "

 

Bajt identyfikacji nośnika

0xFF 2 strony, 40 ścieżek, 8 sektorów/ścieżkę
0xFE 1 strona, 40 ścieżek, 8 sektorów/ścieżkę
0xFD 2 strony, 40 ścieżek, 9 sektorów/ścieżkę
0xFC 1 strony, 40 ścieżek, 9 sektorów/ścieżkę
0xF9 2 strony, 80 ścieżek, 15 sektorów/ścieżkę
0xF9 2 strony, 80 ścieżek, 9 sektorów/ścieżkę (dysk 3.5 cala)
0xF0 2 strony, 80 ścieżek, 18 sektorów/ścieżkę (dysk 3.5 cala)
0xF8 dysk stały

Przykład pierwszego sektora dla dysku 3.5 cala:

Jmp START
		SysName				db 'MSWIN4.1'
		BytesPerSector			dw 200h
		SectorsPerCluster		db 1
		ReservedSectors			dw 1
		NumberFATs			db 2
		MaxRootEntries			dw 0E0h
		TotalSectorNumber		dw 0B40h
		Media				db 0F0h
		FATSectorsNumber		dw 9
		SectorsPerTrack			dw 12h
		HeadNumber			db 2
		HiddenSectors			dd 0
		TotalSectorsNumber32		dd 0
		DriveNumber			db 0
		Reserved1			db 0
		BootSignature			db 29h
		VolumeSerial			dd -1
		VolumeLabel			db '11111111111'
		FileSystemType			db 'FAT12 '
START:
		..............
		Kod i Dane ładujące system
		..............
dw 0AA55h

Należy pamiętać o zakończeniu wartościami 0x55 pod adresem 0x1FE i 0xAA pod adresem 0x1FF. Niezależnie od wielkości sektora te wartości muszą występować dokładnie w tych miejscach.

No więc pora na omówienie szczegółowe poszczególnych bloków całej struktury.

Struktura systemu plików FAT

- Sektory zarezerwowane (ReservedSectors)
- Tablica FAT (FAT)
- Główny katalog plików (RootDir)
- Reszta sektorów przeznaczona na dane (Data)

Cała powierzchnia nośnika jest podzielona na sektory, które łączone są logicznie w obszary zwane klastrami(Cluster). Klastry są najmniejszymi porcjami danych jakie mogą być rezerwowane na przechowywanie danych plików. Dane plików są zapisywane od początku klastra nr 2, wcześniejsze klastry są zajęte przez obszary RootDir+FAT+ReservedSectors. Na początek pokażę jak policzyć rozmiary w sektorach przedstawionych wyżej bloków struktury FAT.

Sektory zarezerwowane (ReservedSectors)

Ilość sektorów zarezerwowanych jest podana w bloku parametrów pod adresem 0x0E. Pierwszy z tych sektorów to BootSector, a ich dodatkowa ilość może oznaczać iż został tam umieszczony dodatkowy kod programu ładującego system.

Tablica FAT (FAT)

dla FAT12/16

FATSize = FATNumber * FATSectorNumber

dla FAT32 (FATSectorNumber=0)

FATSize = NumberFATs * FATSectorNumber32

RootDir

RootDirSize = (32 * MaxRootEntries + BytesPerSector -1) / BytesPerSector <- pomijając resztę

Struktura RootDir

RootDir jest miejscem przechowującym wpisy nagłówkowe plików znajdujących się w głównym katalogu dysku. Każdy wpis w zajmuje 0x20 bajtów. Opis poszczególnych pól wpisu jest przedstawiony w tabeli.

Nazwa Adres Rozmiar Opis
Name 0x00 0x0B Nazwa 8 liter nazwy i 3 litery rozszerzenia.
Attribute 0x0B 1
00000001b ReadOnly - tylko do odczytu
00000010b Hidden - ukryty
00000100b System - systemowy
00001000b VolumeId - etykieta dysku. Może istnieć tylko jeden taki plik. Jego nazwa jest etykietą dysku. FirstClusterLo = FirstClusterHI = 0
00010000b Directory - katalog
00100000b Archive - plik archiwalny
00001111b LongName - plik o długiej nazwie
00000000b (zarezerwowane) Zawsze trzeba ustawiać na 0 przy tworzeniu wpisu.
NTRes
0x0C
1
Używany przez system Windows NT. Przy tworzeniu wpisu należy to zawsze wstawić 0.
?
0x0D
1
?
CrtTime
0x0E
2
Czas utworzenia pliku.
CrtDate
0x10
2
Data utworzenia pliku.
LastAccessDate
0x12
2
Data ostatniego dostępu do pliku. Kiedy ostatnio czytano lub zmieniano jego zawartość.
FirstClusterHi
0x14
2
Dla FAT12/16 "0", a dla FAT32 starsze słowo numeru pierwszego klastra.
LastWriteTime
0x16
2
Czas ostatniej modyfikacji.
LastWriteDate
0x18
2
Data ostatniej modyfikacji.
FirstClusterLo 0x1A 2 Numer pierwszego klastra pliku.
FileSize
0x1C
4
Rozmiar pliku.

Pierwszy bajt nazwy ma kilka wartości zarezerwowanych dla specjalnego użycia.

0x00 Oznacza że obecny 0x20 bajtowy wpis jest wolny i wszystkie wpisy aż do końca tablicy RootDir też są wolne i muszą tez mieć ten bajt wyzerowany. Czyli oznacza że wpis jest wolny i cała reszta tablicy po nim też.
0x05 Kod 0xE5 jest pewnym znakiem japońskim i żeby nie występowały błędy gdyż został on użyty jako specjalny znak to zamieniono jego kod na 0x05. Ta zmiana się odnosi tylko do nazwy pliku w systemie FAT.
0xE5 Wpis jest wolny.

Należy pamiętać o tym, że dodatkowo jest lista znaków, których użycie w polu nazwy jest zabronione.

Znaki zabronione
0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C, i wszystkie znaki < 0x20 z wykluczeniem 0x05

Gdy tworzymy zwykły plik to wypełniamy każde pole według wyższej tabeli bez problemu, ale gdy ma to być katalog to trzeba pamiętać o pewnych specjalnych zastrzeżeniach. Trzeba ustawić odpowiedni bit w polu atrybutów. Ustawić długość pliku na 0. Taki katalog powinien zajmować tylko jeden klaster więc klaster na który wskazuje powinien mieć ustawiony znak końca w tablicy FAT. Należy przy tym wyzerować cały obszar tego klastra. W związku z tym, że tworzymy podkatalog trzeba uzupełnić go 2 specyficznymi wpisami. Obydwa będą całe wyzerowane, tylko niektóre pola trzeba uzupełnić.

Wpis nr Nazwa Opis
1 . Jako nazwę pierwszego wpisu trzeba ustawić ".". Adres klastra tego wpisu (pola FirstClusterLo i FirstClusterHI) powinien być taki sam jak dla katalogu, który na niego wskazuje. Powinien po prostu zawierać numer klastra w którym się znajduje.
2 .. Jako nazwę drugiego wpisu trzeba ustawić "..". Adres klastra tego wpisu (pola FirstClusterLo i FirstClusterHI) powinien wskazywać na klaster katalogu w którym znajduje się tworzony katalog, jeżeli jest to katalog główny to trzeba tą wartość wyzerować.

Format zapisu Daty:

Bity 0-4 1-31 dzień
Bity 5-8 1-12 miesiąc
Bity 9-15 0-127 rok, licząc od 1980 czyli przedział lat 1980-2107

Format zapisu Czasu:

Bity 0-4 0-29 sekundy (wartość pomnożoną przez 2 daje prawidłową wartość)
Bity 5-10 0-59 minuty
Bity 11-15 0-23 godziny

File Allocation Table

Jest to tablica przedstawiająca, które klastry składają się na konkretne pliki. Załóżmy, że mamy pewien plik dla którego mamy określony 0x20 bajtowy wpis w RootDir. Pod adresem 0x1A i ewentualnie dodatkowo 0x14 w tym wpisie jest adres pierwszego klastra z danymi pliku. Wiadomo, że rozmiar pliku jest 3 razy większy niż pojemność klastra więc na pewno ten plik zajmuje 3 klastry. Położenie kolejnych klastrów pliku można odczytać z tablicy FAT. Tablica FAT jest podzielona na komórki o odpowiedniej wielkości. Każdej z tych komórek są przyporządkowane w kolejności wszystkie klastry danych znajdujące się tuż za katalogiem RootDir. Przykładowy plik zajmuje 3 klastry o numerach 0x2B, 0x2C, 0x29. Po złożeniu pól z wpisu w RootDir jesteśmy w stanie określić numer 0x2B pierwszego klastra natomiast numer kolejnego znajduje się właśnie w komórce 0x2B tablicy FAT, a numer kolejnego w komórce 0x2C tej tablicy.

Adres komórki w FAT 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D
Wartość ... 0xFFF7 ... ... 0x2C 0x29 ...

Jest to metoda łańcuchowa w, której każda komórka ma adres następnej. Wartością kończącą łańcuch jest kod EOC (End Of Clusterchain). Są 2 typy specjalnych wpisów w komórkach FAT pierwsza to EOC, a druga to BC (Bad Cluster).

FAT12 FAT16 FAT32
Pusty klaster 0x000 0x0000 0x00000000
EOC 0xFFF 0xFFFF 0x0FFFFFF
BC 0xFF7 0xFFF7 0x0FFFFFF7
rozmiar jednego wpisu w tablicy FAT 12 bitów 16 bitów 28 bitów

Komórki tablicy FAT16 są kolejnymi słowami tej tablicy. W FAT32 elementy tablicy mają rozmiar podwójnych słów, należy jednak dodać, że 4 najstarsze bity są zarezerwowane i przy formatowaniu powinno się je wyzerować, a podczas zmian wartości komórki zachowywać ich stan. Dla FAT12 sprawa jest dość skomplikowana bo komórka ma rozmiar 12 bitów. Przykładowy fragment tablicy FAT12.

DC 14 2B 44 00 00 BF 93 93 33 C9 E8 12 C9 3D FF
FF 74 0F 3D 00 00 75 09 8B DA E8 20 34 00 74 0C

Pierwsza komórka ma wartość 0x4DC, druga 0x2B1, trzecia 0x044, czwarta 0x000, piąta 0x3BF. Na tym przykładzie łatwo zauważyć schemat na podstawie którego można napisać algorytm czytania wartości komórki.

 

Określanie typu FAT

Jak już napisałem we wcześniejszym artykule typ systemu plików nie jest opisany po przez pole o adresie 0x36 w bloku parametrów nośnika BPB. Nie wymyśliłem tego sam tylko wzorowałem się na oryginalnej dokumentacji systemu plików FAT. Więc jeżeli nie w ten sposób to jak rozpoznać typ systemu plików ?. Postaram się to teraz opisać.

Pole FileSystemType w PBP może być używane przez niektóre systemu do identyfikacji typu nośnika więc powinno się umieścić tam zgodną z typem sygnaturę np. "FAT12". Jednak to pole nie musi określać w żaden sposób systemu plików wykorzystanego do przechowania danych. Systemy firmy MS nie używają tego pola do określenia typu FAT'u tylko same sprawdzają typ za pomocą prostych przeliczeń.

Aby określić typ systemu FAT należy policzyć pewną specyficzną wartość którą nazwę count of clusters (COC).

Na początku trzeba policzyć ilość sektorów które zajmuje RootDir:

RootDirSize = (32*MaxRootEntries+BytesPerSector-1)/BytesPerSector  <- zaokrąglając w dół

Dla systemu plików FAT32 pole MaxRootEntries jest równe zero tak więc wartość RootDirSize też będzie zerowa.

Następnie należy policzyć ilość sektorów przeznaczonych na przechowywanie danych. Wykonuje się to po przez przedstawiony algorytm:

IF ( FATSectorNumber != 0 )
	FATSectors = FATSectorNumber;
ELSE
	FATSectors = FATSectorNumber32;

IF ( TotalSectorNumber != 0 )
	TotalSectors = TotalSectorNumber;
ELSE
	TotalSectors = TotalSectorNumber32;

DataSectors = TotalSectors - ReservedSectors - RootDirSize - NumberFATs * FATSectorNumber

Teraz można już policzyć wartość COC za pomocą której można bezpośrednio określić typ systemu plików.

COC = DataSectors / SectorsPerCluser     <- zaokrąglając w dół

Na podstawie wartości COC można określić typ po przez określenie przedziałów w jakich ma się znajdować ta wartość dla każdego z typów.

Jeżeli COC < 4085 system plików FAT12
Jeżeli 4085 =< COC < 65525 system plików FAT16
Jeżeli COC przyjmuje inne wartości niż opisane powyżej to system plików FAT32
 

Jest to jednoznaczny i chyba najlepszy sposób określenia typu FAT dysku. Nie możliwe jest np. żeby dysk FAT16 miał więcej klastrów niż 65524.