Le système de fichier FAT12

AttentionArticle en cours de rédaction.

Introduction

La FAT (File Allocation Table = Table d'Allocation de Fichier) est un système de fichier très simple. Il s'agit d'une liste chaînée (une table) indiquant l'état de chaque cluster. Un cluster est une unité d'allocation, c'est à dire un groupe de secteurs. Sur une disquette, un cluster a pour taille un secteur (512 octets), ce qui est le minimum. L'inconvénient de ce procédé (une liste chaînée) est le temps nécessaire pour trouver un secteur particulier d'un fichier. Imaginons un fichier de 500 Ko. Si vous voulez ajouter des données en fin de fichier, il vous faut obligatoirement passer par toutes les entrées dans la FAT pour ce fichier avant de trouver le dernier cluster. Cela paraît être un handicap de taille, mais à l'époque ce procédé convenait parfaitement.
Ce système a été conçu par Microsoft en 1977 (par Bill Gates lui-même !). Il était en effet destiné aux disquettes, qui contenaient moins d'un méga de données. Sur un tel système, l'encombrement de la FAT est minime. La FAT d'une disquette de 1.44 Mo ne fait en effet que 4.5 Ko. Elle peut alors être entièrement contenue en mémoire et les accès chaînés sont alors très rapides. Mais utilisée comme système de fichiers pour disques durs, la FAT se montre beaucoup moins adaptée.

Structure

Le système de fichier FAT est découpé en 4 zones : La zone réservée contient le secteur de boot. Cette zone fait souvent 1 seul secteur. Elle contient diverses infos sur le système de fichier présent.

#pragma pack(1)
typedef struct
{
	unsigned char JumpToBoot[3];
	char OEMName[8];
	unsigned short BytePerSector;
	unsigned char SectorPerCluster;
	unsigned short ReservedSectorCount;
	unsigned char FATCopyNumber;
	unsigned short RootEntryCount;
	unsigned short TotalSector16;
	unsigned char MediaType;
	unsigned short FATSize16;
	unsigned short SectorPerTrack;
	unsigned short HeadNumber;
	unsigned long HiddenSector;
	unsigned long TotalSector32;
	unsigned char DriveNumber;
	unsigned char Reserved1;
	unsigned char BootSignature;
	unsigned long VolumeID;
	char VolumeLabel[11];
	char FileSystemType[8];
	// offset = 0x3E = 62
	unsigned char BootCode[448];
	// Magic word
	unsigned short Magic;
}boot_sector;
#pragma pack()

ReservedSectorCount vaut normalement toujours 1 pour la FAT12 et FAT16.
Apres cette zone réservée, on trouve les diverses copies de la FAT. Il y a généralement 2 copies de la FAT. Le nombre de copies est spécifié par FATCopyNumber. La taille d'une table en secteurs est spécifiée par FATSize16 pour la FAT12 et la FAT16.
FAT12 : cette taille est de 9 secteurs. Il y a 2880 secteurs dans une disquette, donc la FAT doit posséder 2880 entrées. Une entrée fait 12 bits, on peut donc stocker (512 * 8)/12 = 341 entrées par secteur. Il faut donc 9 secteurs offrant une capacité de 9 * 341 = 3069 entrées pour stocker 2880 clusters.
Apres le boot sector, les copies des FAT, vient le répertoire racine. Une entrée dans le répertoire fait 32 octets.

#pragma pack(1)
typedef struct
{
	char Name[8];
	char Ext[3];
	unsigned char Attributes;
	unsigned char Reserved;
	unsigned char CreationTimeTenth;
	unsigned short CreationTime;
	unsigned short CreationDate;
	unsigned short AccessDate;
	unsigned short FAT32Reserved;
	unsigned short ModificationTime;
	unsigned short ModificationDate;
	unsigned short Start;
	unsigned long Size;
}dir_short_entry;
#pragma pack()

Cette structure contient les fichiers / répertoires (qui sont des fichiers contenant une liste d'autres fichiers). On distingue un fichier d'un répertoire via l'attribut.
#define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02
#define ATTR_SYSTEM 0x04
#define ATTR_VOLUME_ID 0x08
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20

Les noms de fichiers

Les noms sont de type 8.3
Une extension a été apportée pour stocker les noms longs. Ceux-ci sont découpés pour pouvoir être contenus dans plusieurs structures dir_entry modifiées tout en restant compatibles.

#pragma pack(1)
typedef struct
{
	unsigned char Id;
	char Name1[5][2];
	unsigned char Attributes;
	unsigned char Reserved;
	unsigned char Checksum;
	char Name2[6][2];
	unsigned short Start;
	char Name3[2][2];
}dir_ext_entry;
#pragma pack()

On distingue une extension d'une entrée de répertoire via l'attribut qui est la combinaison suivante :

#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)

L'attribut VolumeID permet à cette entrée d'être ignorée par la plupart des anciens logiciels.
Les chaînes de caractères sont en UNICODE.
Un nom long est découpé en petits tronçons qui sont dispatchés dans des entrées étendues.
Ces entrées étendues sont placées avant l'entrée conventionnelle 8.3 du dernier tronçon au premier.
Chaque entrée reçoit un numéro (champ Id). La valeur 0x40 est additionnée a l'Id du dernier tronçon (qui est le premier de la liste).

exemple avec le fichier "My Big File.Extension which is long":

id = 0x43, characters = "h is long"
id = 0x02, characters = "xtension whic"
id = 0x01, characters = "My Big File.E"
"MYBIGFIL.EXT"

Le champ CheckSum contient un checksum calculé à partir du nom 8.3. L'algo utilisé est le suivant :

for(sum = i = 0; i < 11; i++)
{
    sum = (((sum & 1)<<7)|((sum & 0xfe)>>1)) + name[i]
}

Références

Overview of FAT, HPFS, and NTFS File Systems
FAT32 File System Specification (PDF)
How to Interpret 12-Bit FATs
A suivre ...


Sommaire