Práce se souborovým systémem FAT16
Po zvládnutí řízení pevného disku a CF karty jsem potřeboval jednoduchý systém, jakým
ukládat soubory. Pro jednoduché operace mohou být na začátku disku uloženy adresy začátků a konců souborů.
Problém nastáva ve chvíli, kdy dojde ke smazání souboru. Pokud se další ukládaný soubor nevleze do vzniklé mezery,
tak dochází ke ztrátě části kapacity. Čím více zápisů/mazání bude provedeno, tím více kapacity bude ztraceno.
Z tohoto důvodu byl vymyšlen systém FATxx, který dělí paměťový prostor na bloky dat - takzvané clustery.
Soubor pak může být rozfragmentovaný do jednotlivých clusterů. Tímto systémem dochází ke ztrátám pouze v posledním
clusteru souboru, který většinou není plně využit. V paměťovém prostoru jsou dále tzv. vstupní body, které obsahují
jméno souboru, atributy, číslo prvního clusteru daného souboru nebo adresáře, délku souboru a pár dalších informací.
Poslední částí sytému FATxx je vlastní alokační tabulka FAT. Každá její buňka patří k jednomu clusteru a obsahuje vždy číslo následujícího
clusteru souboru nebo adresáře. Pro poslední, prázdný nebo poškozený cluster jsou v buňce speciální hodnoty.
Pro FAT12 je počet clusterů omezen na přibližně 4096 (2^12), pro FAT16 je to asi 65536 (2^16) a pro FAT32 teoreticky
až 4294967296 (2^32). FAT32 ve skutečnosti využívá pouze 2^28 clusterů a nejvyšší 4 bity by měly být maskovány.
V případě M$ Windows se pak využívá pouze 2^22 clusterů.
Velikost jednoho souboru je pro systémy FATxx omezena na 4096MB.
MBR - Master Boot Record
Je to první sektor disku (LBA adresa 0) a obsahuje informace o takzvaných partition (oddílech disku), které mohou být na disku až 4.
Mimo informace o oddílech je v tomto sektoru takzvaný "Executable code", který nás ale v souvoslosti s jednočipem nemusí zajímat.
Pokud sektor skutečně obsahuje MBR, tak poslední 2B sektoru obsahují takzvanou
"Executable mark" 55AAh. Rozmístění dat v MBR je naznačeno v tabulce:
Struktura MBR sektoru |
Ofset |
Délka |
Popis |
0h |
446B |
"Executable code" |
+1BEh |
16B |
1. partition entry - Info o 1. partition |
+1CEh |
16B |
2. partition entry - Info o 2. partition |
+1DEh |
16B |
3. partition entry - Info o 3. partition |
+1EEh |
16B |
4. partition entry - Info o 4. partition |
+1FEh |
2B |
"Executable mark" 55AAh |
Rozmístění a význam dat pro vstup do každé partition:
Vstup do partition |
Ofset |
Délka |
Popis |
0h |
1B |
Stav partition, 0h - neaktivní, 80h - aktivní |
+1h |
1B |
Začátek partition - hlava |
+2h |
2B |
Začátek partition - cylindr/sektor |
+4h |
1B |
Typ partition, 04h - FAT16 menší než 32MB, 06h - FAT16 větší než 32MB |
+5h |
1B |
Konec partition - hlava |
+6h |
2B |
Konec partition - cylindr/sektor |
+8h |
4B |
Počet sektorů mezi MBR a začátkem partition |
+Ch |
4B |
Délka partition v sektorech |
Z údajů ve vstupu pro každou partition jsou podstatné jen typ partition, podle kterého
lze identifikovat FAT16, počet sektorů mezi MBR sektorem a boot sektorem dané partition a možná by se dala využít ještě
délka partition, ale tu lze nalézt i v boot sektoru vlastní partition.
FAT16 boot record
Je umístěn na adrese udané ve vstupu partition v MBR. LBA adresa se dá vypočítat jednoduše takto:
(FAT16 boot rec.) = MBR + (Počet sektorů mezi MBR a začátkem partition)
Struktura FAT16 boot sektoru |
Ofset |
Délka |
Popis |
0h |
3B |
Skok na začátek zaváděcího sektoru |
+3h |
8B |
ASCII retezec názvu operačního systému |
+Bh |
2B |
Velikost sektoru v B |
+Dh |
1B |
Počet sektorů na cluster |
+Eh |
2B |
Počet sektorů mezi FAT16 boot rec. a začátkem 1. FAT |
+10h |
1B |
Počet alokačních tabulek FAT (prakticky vždy 2) |
+11h |
2B |
Počet vstupů do root adresáře (každý 32B) |
+13h |
2B |
Počet sektorů v partition, pouze pro FAT16 menší než 32MB ! |
+15h |
1B |
Popisovač média (u HDD je to F8h) |
+16h |
2B |
Počet sektorů v jedné FAT |
+18h |
2B |
Počet sektorů na stopu |
+1Ah |
2B |
Počet hlav |
+1Ch |
4B |
Počet skrytých sektorů ve FAT |
+20h |
4B |
Počet sektorů v partition, pouze pro FAT16 větší než 32MB ! |
+24h |
2B |
Počet sektorů na stopu |
+2Bh |
11B |
ASCII řetězec názvu disku (disk volume) |
+36h |
8B |
ASCII řetězec názvu filesystému "FAT16" |
+3Eh |
448B |
"Executable code" |
+1FEh |
2B |
"Executable mark" 55AAh |
V tomto sektoru jsou všechny potřebné informace k partition. Poslední 2B jsou opět tzv. executable mark a
jejich přítomnost znamená platnost FAT16 boot rec. Vícebytová čísla jsou ukládána vždy
v pořadí od nejméně významného (LSB byte první). ASCII řetězce jsou ukládány opět
zleva doprava (nečekaně) a jsou zarovnávny vlevo. V nevyužitých polích jsou znaky mezery (20h). Přesněji měly by být, ale
třeba firmware některých digitálních fotoaprátů se s vyplněním neobtěžuje a nechá tam znaky 00h.
Alokační tabulka FAT
Nyní se pokusím trochu přiblížit formát vlastní alokační atbulky FAT. LBA adresu začátku lze vypočítat následovně:
(Začátek 1. FAT) = (FAT16 boot rec.) + (Počet sektorů mezi FAT16 boot rec. a začátkem 1. FAT)
Délka FAT je udána ve FAT16 boot recordu a je třeba vždy respektovat její maximální možnou délku 256 sektorů.
Je složená až z 2^16 buněk o velikosti 2B. První buňka je vyhrazená (netuším co je zač, ale má hodnotu FFFFh), další buňka je u HDD FFF8h
a LSB je opět popisovač média. Od 3. buňky už začíná vlastní FAT.
Každá buňka pak náleží jednomu clusteru disku. Buňky mohou obsahovat hodnoty uvedené v tabulce.
Možné hodnoty buněk FAT |
Hodnota |
Popis |
0000h |
Volný cluster |
0002h-FFEFh |
Cluster je součást souboru nebo adresáře |
FFF0h-FFF6h |
Vyhrazený cluster |
FFF7h |
Vadný cluster |
FFF8h-FFFFh |
Poslední cluster souboru nebo adresáře |
Oblast kořenového adresáře
Oproti FAT32 je celkem neobvykle (s prominutím idiotsky) vyřešen kořenový adresář.
Nalézá se hned za poslední tabulkou alokace (většinou 2.FAT) a je pevně vyhrazen a má pevně danou délku, která je uvedena
ve FAT16 boot recordu. Standardně je to 512 vstupů neboli 512*32B, což je 16384B nebo 32 sektorů.
Začátek kořenového adresáře je tedy na LBA adrese:
(Začátek kořenového adresáře) = (začátek 1.FAT) + (délka jedné FAT)*(počet FAT)
V této oblasti tedy může být uloženo zpravidla 512 vstupů podadresářů, souborů nebo LFN. První vstup je
obvykle "disk volume", takže už zbývá jen 511 vstupů. Pokud se používají dlouhá jména, tak se tento prostor velmi rychle
zaplní (13 písmen = 1 vstup, každý soubor ještě navíc 1 vstup).
Jak už jsem naznačil, root u FAT16 má 2 hlavní omezení. Jednak je to jeho nepříjemně omezená velikost, což se dá
vyřešit vhodným naforamátováním a vyhrazením většího prostoru (fdisk ve W98 nastavuje vždy 512).
Druhá nepříjemnost je, že pro procházení položek v root musíte použít jinou rutinu, něž pro procházení podadresářů.
Důvod je prostý - root neleží v oblasti dat, takže do něj nelze vstoupit přes číslo 1.clusteru jako do libovolného podadresáře
a ani ho nelze procházet stejnou rutinou, protože podadresáře narozdíl od root mohou být fragmentovány.
U FAT32 je tento problém odstraněn tím, že root je vytvořen stejně jako každý podadresář v oblasti dat. Číslo počátečního clusteru je
uloženo přímo v boot recordu.
Oblast dat
Následuje hned za kořenovým adresářem. Její LBA adresu lze vypočítat následovně:
(Začátek dat)=(Začátek kořenového adresáře) + (počet vstupů do root)/16
Oblast dat je rozdělena do tzv. clusterů, což jsou skupiny sektorů. Počet sektorů v clusteru lze
vyčíst z FAT16 boot recordu. Počty sektorů v clusteru mohou být 1,2,4,8,16,32,64 a 128, ale i u malých disků,
kde by bylo možné použít cluster velikosti menší než 4kB, se většinou používá 4kB.
Z povolených hodnot platných clusterů a počtu sektorů v 1 clusteru pak vyplývá maximální kapacita
FAT16 - 4095,875MB.
Clustery mohou obsahovat data (části souborů) nebo položky vstupů, které
náleží jednotlivým podadresářům. LBA adresu prvního sektoru v daném clusteru lze vypočítat následovně:
(1.sektor clusteru)=(Začátek dat) + (cluster - 2)*(počet sektorů na cluster)
Vstupy (doslovný překlad "entries")
Někdy také označované jednoduše jako položky. Každý vstup je dlouhý
32B a jsou v adresářích umisťovány jedině po násobcích 32B - ofsety 00h, 20h, 40h, 60h ...
Vstupů existuje několik základních druhů. Předně je to vstup "disk volume", na který narazíte
většinou hned na začátku kořenového adresáře. Ten nemá prakticky žádný význam a obsahuje
v poli pro jmého souboru/adresáře 11 znaků dlouhý řetězec "disk volume" obsažený také v boot recordu.
Druhým typem vstupu je vstup souboru. Dalším druhem je vstup adresáře a posledním je
vstup dlouhého jména LFN. Z tabulky a grafického znázornění je vidět, co vstup obsahuje.
|
Položky vstupu souboru, adresáře a disk labelu |
Ofset |
Délka |
Popis |
0h |
8B |
ASCII řetězec názvu souboru |
+8h |
3B |
ASCII řetězec přípony souboru |
+Bh |
1B |
atribut souboru, významy bitů:
bit 0 - pouze ke čtení
bit 1 - skrytý
bit 2 - systémový soubor
bit 3 - "disk label"
bit 4 - adresář
bit 5 - archiv bit
|
+Ch |
10B |
rezervováno (něco tam je, ale nezabýval jsem se tím) |
+16h |
2B |
čas vytvoření nebo poslední změny:
bit 0-4 - vteřiny dělené dvěma
bit 5-10 - minuty
bit 11-15 - hodiny
|
+18h |
2B |
datum vytvoření nebo poslední změny:
bit 0-4 - den
bit 5-8 - měsíc
bit 9-15 - hodnota + 1980 = rok
|
+1Ah |
2B |
počáteční cluster souboru / adresáře |
+1Ch |
4B |
délka souboru v B |
V poli název a přípona souboru mohou být použity jen některé znaky. Neznám je všechny, ale jsou to
především velké znaky anglické abecedy, čísla a např. "~". Mezery uprostřed názvu nejsou povoleny, ale
zbytek nevyužitého pole je jimi vyplněn. Názvy jsou zarovnány vlevo. Pro "disk volume" je pole
přípony i názvu sloučeno do 11 znaků. Pokud dojde ke smazání adresáře nebo souboru, nebude vstup celý nahrazen nulami, ale 1. písmeno názvu (ofset 00h)
bude přepsáno znakem E5h! Proto je třeba vstupy při prohledávání podle této podmínky filtrovat.
Podle prvku atribut se rozlišují jednak jednotlivé druhy vstupů a pak také parametry souborů (skrytý, systémový, ...).
Nelze však rozlišovat jen jednotlivé bity. Vstup LFN má záměrně hodnotu atributu 0xF, což je nepovolená
hodnota, takže ji DOS ignoruje a FAT16 je tak zpětně kompatibilní. Před testováním jednotlivých bitů je tedy třeba
testovat celkovou hodnotu na 0xF. K času a datu asi není co dodat. Další pole je
číslo prvního clusteru adresáře nebo souboru. Poslední 4B obsahují délku souboru (asi i adresáře - nezkoumal jsem) v Bytech.
Vstup souboru
Ke vstupu souboru již bylo řečeno víceméně vše. Zbývá pouze zopakovat,
že vstup souboru nemůže obsahovat v atributu bitový příznak adresáře, disk labelu a celková
hodnota nesmí být 0xF. Zda se jedná o vstup souboru, lze tedy zjistit pouze z těchto 3
podmínek.
Vstup adresáře
Pro vstup adresáře platí prakticky totéž co pro soubor, pouze
atribut nesmí obsahovat příznak disk label a celková hodnota nesmí být 0xF.
Vstup LFN
Jak už bylo řečeno, jedná se o vstup určený pro ukládání dlouhých jmen.
Jméno je zde navíc uloženo v UTF16 kódování (odvar unicode - znaky společné pro CP1250 odpovídají unicode),
takže může obsahovat nejrůznější znaky. Grafické znázornění vstupu je vidět na obrázku.
Jak je vidět, tak muselo být zachováno několik standardních položek. Hlavní z nich je již
zmiňovaný atribut, který je u LFN vždy 0xF. CRC obsahuje kontrolní součet základního názvu ve formátu 8.3.
Význam položky 1.cluster mi není přesně znám (nikdy jsem ho nepoužil), ale měl by zřejmě obsahovat
číslo clusteru, kde řetězec položek LFN začíná (nejspíš :-). Vlastní řetězec znaků je v LFN vstupu rozdělěn
na několik částí, jak je vidět z obrázku. Díky UTF16 má každý znak velikost 2B. První je uložen vždy LSB.
Celý název samozřejmě není uložen v jednom vstupu, ale je rozdělěn na více částí po 13-ti znacích.
Grafické znázornění uspořádání více položek LFN je na dalším obrázku.
|
Jak si můžete všimnout, tak nejenže nás M$ obšťastnil tím, že jsou
jednotlivé části názvu řazeny pozpátku, ale navíc jsou umístěny před vlastním krátkým vstupem
adresáře nebo souboru. Toto značně komplikuje načítání.
Jediné, co se pánům od M$ nepovedlo zkazit je, že LFN položky jednoho názvu a příslušný vstup souboru/adresáře musí být u sebe a
nemůže mezi nimi být nic jiného. Z uvedeného příkladu je také vidět funkce 1. bytu označeného N.
Ten obsahuje pořadí částí názvu souboru s tím, že poslední část obsahuje pořadové číslo zvětšené o hodnotu
40h. Z toho také vyplývá, že maximální počet znaků názvu je 832. M$ sice tvrdí, že je to 255, ale to je pouze
SW omezení. Zde se také hodí připomenout, že maximální délka cesty ve Windows je tuším 260 znaků (bráno jako celá cesta např. "neco/ahoj/nictu").
Celý název je jeden řetězec obsahující jak jméno, tak i příponu a narozdíl od základního vstupu je zde i znak tečka (pokud je přítomna).
Celý řetězec je zakončen znakem s hodnotou 0000h a pak už do konce položky na místě znaků následují pouze hodnoty 0xFFFF.
Pokud je poslední písmeno názvu na poslední pozici (1Eh) LFN, tak už není zakončen 0000h a je to poslední část názvu LFN.
Při načítání se tedy musí kontrolovat i hodnota N a pokud je větší než 40h, tak jde o poslední část názvu.
Zbývá ještě doplnit jednu důležitou skutečnost - pokud název splňuje podmínky názvu 8.3 (velké znaky, 8.3, ...),
tak je vytvořen jen základní vstup, nikoliv vstupy LFN!!! Proto pokud vytvoříte rutinu čtení
LFN, tak nezapomeňte, že musíte být schopní načíst i 8.3 název.
Jak pracovat s FAT - jak číst fragmentovaná data
Na následujícím obrázku je naznačen jednoduchý příklad čtení souboru z tabulky alokace FAT. Barevně jsou označeny buňky,
které patří vždy 1 souboru nebo adresáři. Šedě jsou označeny nepoužitelné buňky FAT. Bíle jsou označeny volné.
|
Nahoře je příklad vstupu se jménem "NECO.DAT". Z atributu s hodnotou 20h se dozvíme, že je
nastaven pouze archiv bit a jde tedy o vstup souboru. Z ofsetu "1.cluster" si přečteme číslo 1.clusteru souboru, které
je v našem případě 0002h. Nyní tedy načteme z FAT buňku číslo 0002h. V této buňce je pak uloženo číslo clusteru (a také další buňky FAT), kde
soubor pokračuje a to jest 0005h. Stejným postupem pokračujeme dokud nenarazíme na buňku s hodnotou FFF8h-FFFFh, která
nám udává, že jde o poslední cluster souboru. Postupně načítané čísla buňek (včetně hodnoty ze vstupu souboru) si lze buďto ukládat do
zásobníku, nebo průběžně vypočítávat adresy clusterů v oblasti dat a data rovnou načítat. Záleží na tom, co se souborem
potřebujeme dělat. U tohoto konkrétního souboru jsme zjistili, že je uložen ve třech clusterech. Poslední cluster
však většinou není zcela zaplněn daty, a proto je třeba načíst ze vstupu souboru skutečnou délku v Bytech.
Číslo sektoru FAT s hledanou buňkou a ofset ve WORDECH v sektoru se vypočíta podle následujícího vzorce:
(Sektor hledané buňky)=(Začátek FAT) + (Hledaná buňka) div 256
(Ofset buňky v sektoru)=(Hledaná buňka) mod 256
Výpočet je jako všechny ostatní zde uvedené časově nenáročný. Dělení je celočíselné, což znamená pouze
8x rotaci čísla buňky vpravo. 8 bitů "vyrotovaných" vpravo je pak rovnou výsledek druhého vzorce,
a to ofset v sektoru ve wordech. Zbývajících 8 bitů je číslo sektrou, které se přičte k LBA adrese začátku FAT.
Zkušenějším jistě neuniklo, že v případě použití 8-bitových MCU je 16-bitové číslo vždy děleno do 2
registrů a tedy MSB registr + začátek FAT je první výsledek a LSB registr je rovnou ofset v Bytech.
Kromě součtu tedy není třeba nic počítat.
Jak fungují adresáře?
Systém FATxx pracuje s takzvanou stromovou strukturou. Začátkem "stromu" je kořenový adresář.
Ten se pak pomocí podadresářu větví na další větvě. Každý podadresář se zase může větvit libovolně dále a v kterémkoliv
adresáři mohou být uloženy soubory. Z toho mimo jiné vyplývá, že pokud přijdete o kořenový adresář, tak
se těžko dostanete k podadresářům a zbytku dat.
Podadresáře jsou v systému FATxx chápány podobně jako suobory a stejně se s nimi také pracuje.
Liší se pouze atributem.
V kořenovém adresáři si vyberete vstup požadovaného adresáře a stejně jako u souboru zjistíte, které
clustery obsahuje. Ve všech těchto clusterech se pak mohou vyskytovat položky vstupů souborů nebo dalších podadresářů
(a samozřejmě LFN vstupů). Vyhledávání položek probíha úplně stejně jako v kořenovém adresáři, ale
oblast dat adresáře je dělená na více clusterů místo jednoho bloku.
Jistě nikomu neuniklo, že v souborových manažerech je vidět adresář označený jako ".." a znamená návrat
o úroveň blíž ke kořenovému adresáři. Kdo zažil DOS nebo experimentoval v příkazovém řádku s příkazem "dir",
ten si jistě všiml, že je vypsán i adresář ".", který nás vrací zpět do adresáře, kde právě jsme - v čísle 1.clusteru obsahuje
počáteční cluster podadresáře, kde právě jsme.
Dokud jsem se však nezabýval souborovým systémem FATxx detailně, tak jsem předpokládal, že tyto 2 adresáře
jsou generovány jen virtuálně, aby se bylo možné pohybovat ve struktuře adresářů pomocí příkazu cd (např. "cd ..").
Když jsem však vytvořil rekurzivní funkci pro výpis stromu adresářů,
tak se mi program vždy v prvním adresáři zacyklil. Podíval jsem se tedy jak přesně adresář vypadá a ejhle -
adresáře "." i ".." jsou v každém adresáři fyzicky zapsány, a to hned na začátku.
Pokud je tedy při procházení položek adresářů chcete filtrovat, tak po otestování atributu ješte proveďte test na
1 znak názvu. Pokud je tam znak ".", který odpovídá hodnotě 2Eh, tak vstup vyhodnoťte jako nepltaný.
Ukázkové programy a možná i celá knihovna pro AVR přibude časem. Zatím mám na svých stránkách
pouze jednu konstrukci využívající FAT16 - PCM WAV player.
Je to můj skoro první program s FAT16, takže většina funkcí je dost "neohrabaná", ale je snad plně funkční.
Demonstrační zapojení
A nyní už konečně praktická část, bez které je dokumentace téměř k ničemu - nejké přklady.
Použité zapojení je úplně stejné jako zapojení pro
experimenty s IDE rozhraním.
K popisu tedy jen velmi stručně: HDD nebo CF karta je připojena v 16-bitovém módu k MCU ATmega32 na 16MHz.
Do zapojení je přidán LCD s řadičem HD44780 nebo kompatibilním pro zobrazování některých informací. Dále je zde externí
SRAM o velikosti 512kB, která se u FAT16 experimentů celkem dost hodí. Ta je připojena přes rozšiřující registry
74HC574 stejně jako některé signály IDE. Pro komunikaci s vhodným terminálem v PC je zde obyčejný převdník
MAX232, který umožňuje bezpečný přenos dat rychlostí až 115200bd, což bude pro naše účely plně dostačující.
Napájení musí být dostatečně tvrdé, jinak se disk nerozběhne. Napětí +12V musí zvládnout alespoň 2,5A bez poklesu napětí
pod 11V. Na +5V vyhoví proud 1A.
1. ukázkový program - rekurzivní funkce na výpis stromu adreasářů
Původně mělo jít jen o výpis stromu, ale nakonec jsem tento ukázkový program rozšířil tak,
že se na terminálu v PC vypíší všechny podstatné údaje o CF/HDD, následně o 1.partition,
pak je vypsán zmiňovaný strom a nakonec některé informace z něj vyplývající.
Na LCD je zobrazován pouze status programu a chybová hlášení. Komunikace přes COM port je
standardně nastavena na 115200bd bez řízení toku, což na většině PC nedělá problémy. Pokud byste chtěli
připojit místo MAX232 USB/COM převodník FT232, tak už je HW řízení toku nezbytné!
Výsledný TXT soubor pak vypadá
takto.
Pro zrychlení je celá FAT překopírována do externí SRAM. Využito je pouze 128kB,
takže není třeba připojovat 0,5MB paměť. Pro ukázku jsem vytvořil i podporu LFN názvů, takže je strom celkem
přehledný. LFN názvy jsou uloženy v kódování UTF16, takže jsou užitečné znaky převedeny na stravitelnější CP1250.
CP1250 bohužel neobsahuje tabulkové znaky, takže je formátování provedeno jen odsazením.
zdroják a překlad do hex
(c) 2004 - 2006, Stanislav Mašláň - tyto stránky podléhají zákonu o autorských právech, jakékoliv kopírování nebo upravování materiálů z těchto stránek bez mého svolení není povoleno.
Poslední aktualizace: 29.8.2006
|