Fat - Waaal/BobaOS GitHub Wiki
This document explains the Fat filesystem
Fat is a extremely simple filesystem. FAT stands for File Allocation Table. The Filesystem works with a File Allocation Table, which is a struct at the start of the disk, which holds all the clusters on the disk. It also has a Root Directory which holds all the files and subdirectories that are saved on the disk. The FAT file systems stores data with clusters. One cluster holds multiple sectors. Both the size of a sector and the Numbers of sectors per clusters are changable in the BIOS Parameter Block.
There is Fat12, Fat16 and Fat32 (also exfat but who cares about exfat).
A Fat Volume is seperated into sector. Each sector is 512 bytes long on a standard fat(12,16) disk.
Lba stands for logical block address. It is a way to index all the sectors on a disk. It starts from 0 for the first sector and goes up how many sectors a disk has.
One sector is normaly 512 bytes (0x0B in boot sector). One cluster can have multiple sectors (0x0D in boot sector).
A typically FAT disk is orderd into 4 regions:
- Boot Sector
- More Reserved (optional. Value in Boot Sector 0x0E)
- File Allocation Tables
- Root Directory
- Data
The Boot sector is 1 sector long and is split into the BIOS Parameter Block and the Extended Boot Record. The Boot sector counts as a reserved sector (0x0E index in boot sector). They need to contain the following:
BIOS Parameter Block:
| Offset | Size (in bytes) | Descritpion |
|---|---|---|
| 0x00 | 3 | The first 3 bytes dissassemble to JMP SHORT 3C NOP. The reason is: if this is a boot disk, then JMP SHORT would jump to your entry point of the bootloader. So if you boot from disk, chang 3C to your entry point (.start) |
| 0x03 | 8 | OEM identifier. Used by microsoft example: MSWIN4.1. Can now be any string with the length of 8 |
| 0x0B | 2 | Numbers of bytes per sector. Normaly 512 |
| 0x0D | 1 | Numbers of sectors per cluster |
| 0x0E | 2 | Numbers of reserved secors (at least 1 for boot sector) |
| 0x10 | 1 | Number of File allocation tables |
| 0x11 | 2 | Number of Root Directory entrys |
| 0x13 | 2 | Number of Total sectors. If 0 then number is greater than 65535 and is stored in Large sector count (0x20) |
| 0x15 | 1 | Media Type signature (0x0F0 = Floppy disk) |
| 0x16 | 2 | Numers of sectors per FAT. Means how long is File allocation table in sectors (FAT12/16 only) |
| 0x18 | 2 | Numbers of sectors per Track |
| 0x1A | 2 | Numbers of heads/sides of storage media |
| 0x1C | 4 | Numbers of hidden sectors |
| 0x20 | 4 | Large sector count |
Extendet boot Record FAT12/16:
| Offset | Size (in bytes) | Descritpion |
|---|---|---|
| 0x24 | 1 | Drive number 0x00 = Floppy; 0x80 = HDD |
| 0x25 | 1 | Windows NT flags / Resvered just keep them at 0 |
| 0x26 | 1 | Siganture 0x28 or 0x29 |
| 0x27 | 4 | VolumeID/ Serial Number of Volume |
| 0x2B | 11 | Volume lable string |
| 0x36 | 8 | System ID string. Name for this drive |
| 0x3E | 448 | Boot code/or empty |
| 0x1FE | 2 | Bootable partition signature 0xAA55 or empty |
Extendet boot Record FAT32
| Offset | Size (in bytes) | Descritpion |
|---|---|---|
| 0x24 | 4 | Sectors per Fat. The size of FAT in sectors. |
| 0x28 | 2 | Flags |
| 0x2A | 2 | FAT version number |
| 0x2C | 4 | Cluster number of root directory |
| 0x30 | 2 | Sector number of FSInfo structure |
| 0x32 | 2 | Sector number of the backup boot sector |
| 0x34 | 12 | Reserved |
| 0x40 | 1 | Drive number |
| 0x41 | 1 | Flags in WindowsNT |
| 0x42 | 1 | Signature 0x28 or 0x29 |
| 0x43 | 4 | VolumeID/ Serial Number of Volume |
| 0x47 | 11 | Volume lable string |
| 0x52 | 8 | System ID string. Name for this drive |
| 0x5A | 420 | Boot code/or empty |
| 0x1FE | 2 | Bootable partition signature 0xAA55 or empty |
Note
The Size of the end disk is oriented by the Number of Total sectors or the Large sector count. So if we have 65535 sectors with 512 bytes then the size of the disk is: 31.9 MB
Here are stored the File Allocation Table structs. This section is always placed right after the Boot Sector section and reserved sectors. It stores all the clusters and the status, if they are taken or if they are the end of a chain.
So each entry in the FAT respresents one cluster. If the entry for a specific cluster is 0 that means this cluster is free to use. If the entry is FF or a number that means this entry is in use. So for example if we want to know if cluster 10 is taken we look at the index 10 in the FAT.
Some files/directories are larger than one cluster. So one data chunk can spread over multiple clusters. But the root directory only tells us the starting cluster and the size of a file. So for this the numbers in the FAT are important.
For example if we look at cluster 3 and it is taken, but it is larget than one cluster then in the FAT at index 3 would stand a number. This number tells us the next cluster of the data chunk. If we look at the new cluster index in the FAT it would tell us the next cluster etc. If on entry in the FAT returns 0xFF8 or 0xFFF this means we have reached the final cluster of the data chunk.
On Fat12 one entry is 12 bits On Fat16 one entry is 16 bits On Fat32 one entry is 32 bits
The size of this region in sectors can be calculated by multiplying the Numbers of File Allocation Tales (0x10) with the Numers of sectors per FAT (0x16).
An entry is set to:
- FF8 or FFF: if used and if of chain
- FF0 or FF6: if reserved
- FF7: if it is a bad sector
- Number: if used and not end of chain
- 0x00: if free
On Fat12,16 the Root Directory is placed right after the FAT directory. On Fat32 its position is given by the Extendet boot Record (0x2C). The root directoy is the root directory of all the files and subdirectories.
The root directory and all subdirectories hold directory entries. In an entry is the file name, extension and other attributes saved. It also holds the information if this entry is a file or a directory. On entry is 32 bits long. Its a array out of entries.
Calculate the size of the root directory: Number of Root Directory entrys (0x11) * 32bytes
We know if we have reached the end of a directory if we have a NULL entry. Basically a entry that has all 0).
If the Filename at position 0 has 0xE5 as first character, this means this entry is not used.
Struct of a directory entry
| Offset | Size (in bytes) | Descritpion |
|---|---|---|
| 0x0 | 8 | Filename (unused bytes must be padded with spaces) |
| 0x8 | 3 | Extension (unused bytes must be padded with spaces) |
| 0xB | 1 | Attributes of File (bitmask): READ_ONLY=0x01;HIDDEN=0x02;SYSTEM_FILE=0x04;VOLUME_ID=0x08;DIRECTORY=0x10;ARCHIVE=0x20;DEVICE=0x40;RESERVED=0x80 |
| 0xC | 1 | Reserved. Used by Windows NT |
| 0xD | 1 | Creation time in tens of a second |
| 0xE | 2 | Creating Time HOUR=5bits; MINUTES=6bits; SECONDS=5bits |
| 0x10 | 2 | Creating Date YEAR=7bits; MONTHS=4bits; DAY=5bits |
| 0x12 | 2 | Last access date. Same format as Creation date |
| 0x14 | 2 | High 16 bits of the Start cluster number |
| 0x16 | 2 | Last modifiet time. Same format as Creation time |
| 0x18 | 2 | Last modifiet date. Same format as Creation date |
| 0x1A | 2 | Low 16 bit of Start cluster number |
| 0x1C | 4 | The size of the file in bytes |
Important note: The start cluster number in a dir entry is in Data cluster format. Data cluster format starts from 0. The 0 is the cluster after root dir. It is important to take this into account. If we want to calculate the absolute sector of a data cluster we need to add all the sectors bevore the data cluster (reserved, FAT and root dir) to the end result.
Number 0 and 1 of the starting cluster entry are reserved. We need to add -2 for a absolute value. For example, if the Start cluster number of a entry says 4. It its actually the 2 in the data cluster.
We first need the file name and the search for it in the Root Directory of the file. In the Directory entry we got the starting cluster. We read the content of the starting cluster and the go to the File Allocation Table. For example if the start cluster of a file is 3 we then look at the File Allocation Table at entry 3. This entry holds the next cluster and the next entry of the FAT. For example 4. We then read all the bytes from the 4th cluster and look at the FAT at entry 4. This 4th enty points us to the next cluster etc.
We know if we reacht the end of a cluster if the entry of the FAT is 0xFFF or 0xFF8. This marks the end of a chain.
We first need the directory name and search for it in the Root Directory of the directory. In the Root Directory we got the starting cluster.
Other than in a file where we read the raw binary data of the file in a directory we read another directory entry array from the disk.
We read the content of the starting cluster and the go to the File Allocation Table. For example if the start cluster of a directory is 3 we then look at the File Allocation Table at entry 3. This entry holds the next cluster and the next entry of the FAT. For example 4. We then read all the bytes from the 4th cluster and look at the FAT at entry 4. This 4th enty points us to the next cluster etc.
We know if we reacht the end of a cluster if the entry of the FAT is 0xFF. 0xFF marks the end of a chain.
If we have a directory path like this: 0:/dir1/dir2/dir3 and want dir3, we first need to load the dir1 entries, then from there the dir2 entries and here we find the dir3 entries.
FAT is little endian. Fat12 is a little special, because it has 12 bytes. In FAT16 and FAT32 the data structure is better and dont need any explenation.
So entry 0x123, 0x456 would be placed like this in a FAT:
0x123 0001 0010 0011
0x456 0100 0101 0110
0x23 0x61 0x45
0010 0011 0110 0001 0100 0101
FAT:
0: 0xFF0 1111 1111 0000
1: 0xFFF 1111 1111 1111
2: 0x005 0000 0000 0101
3: 0x009 0000 0000 1001
4: 0x00B 0000 0000 1011
5: 0x00C 0000 0000 1100
Actuall on disk:
0xF0 0xFF 0xFF 0x05 0x90 0x00 0xB 0xC0 0x00
1111 0000 1111 1111 1111 1111 0000 0101 1001 0000 0000 0000 0000 1011 1100 0000 0000 0000
We want to read index 2. 2 x 1.5 = 3(because 8 x 1.5 is 12 and FAT is 12 bits long)
We check if 2 % 2 == 0 We take 16 bit from index 3 = 0000 0101 1001 0000 = 0x05 0x90 From little endian to dec = 0x9005 Then we take the low 12 bit and got the index: 0x9005 & 0xFFF = 0x5
We want to read index 4. If index % 2 != 0: We take 16 bit from index 4 = 1001 0000 0000 0000 = 0x90 0x00 From little endian to dex = 0x0090 Then we shift right 4 bits: 0x0090 >> 4 = 0x9.
Tada. Thats how we read the 12 bit FAT table. The 16 and 32 bit are self explenatory.