Whenever we format a disk volume, it is a good idea to name the label so it will be easier to categorize. To label a volume, we can use LABEL command or UI depends on your preference. Windows CE does provide FAT driver and support various format (FAT12, FAT16,FAT32, ExFAT and TFAT - transaction-safe FAT) and many feature to let you scan and even defrag the volume but not labeling.
At any time you format a volume in CE and then mount it on PC, the label is always empty! Of course, you can always label the volume on PC, even it is formatted in CE. So looks like CE does not care about the volume label at all, neither report the label to OS nor changing the label on FAT.So how can we set the volume label in CE?
To Answer this question, we need to know how does FAT stores the volume label. Here are some on-line resources are handy for parsing FAT.
http://en.wikipedia.org/wiki/File_Allocation_Table
http://www.pjrc.com/tech/8051/ide/fat32.html
http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
You can refer to PUBLIC\COMMON\OAK\DRIVERS\FSD\FATUTIL\MAIN\bootsec.h and dosbpb.h or the above links for the fields we discuss here.
The first sector of a FAT Volume (it is not necessary to be the first sector of a disk.) is a FAT boot sector and BPB (BIOS Parameter Block). And at offset 43, bgbsVolumeLabel (or bsVolumeLabel on FAT16) is for storing the volume lable, but note in the spec also indicates "FAT file system drivers should make sure that they update this field when the volume label file in the root directory has its name changed or created.". So we can't just simply update the bgbsVolumeLabel but also need to create a volume lable file in root directory. The volume lable file is not a real file but just a file entry in root directory with zero file lenth and a very special file attribute, ATTR_VOLUME_ID. (defined in public\common\oak\drivers\fsd\fatutil\MAIN\fatutilp.h)
Locating and accessing bootsector is quite straight forward, as long as we know the starting sector of a FAT volume, that's it. But where is the root directory? The layout of a typical FAT is like this
Boot sector (Volume ID in the figure) followed by Reserved Sectors (1 on FAT12/16 and 32 on FAT32), then FAT chain table(s) (can be 1 or 2), after that is the root directory (FAT12/16 and not shows in the figure) then begining of the File and Directories. In FAT12/16, the root directory is placed right after FAT so it is not hard to caculate the offset in the volume. But in FAT32, this rule is no longer true: the first cluster of the root directory is determined by BGBPB_RootDirStrtClus (or offset 44 in boot sector).
Although this field is usually 0x00000002 (it is how CE initial the root directory after formating a volume. Note we should never assume it is always true) which means the first cluster contains data but not like the root directory is contiguous in FAT12/16, it is just like a regular file can be fragmented. So we need to access the root directory (of FAT32) hopping one cluster to another by traversing FAT table.
Let's trace the code now. Although the source of FAT driver is not available in CE Shared Source program, but the formatter, Fatutil.dll, is available in public\common\oak\drivers\fsd\fatutil\MAIN\formatdisk.cpp. Be aware the public code only provides formatter for FAT12/16/32 for ExFAT it is still not available. FormatVolumeInternal is the main worker function. With the knowledge here, you should be able the trace the code easily. But I would like to discuss the following code pieces
dwReservedSectors = (fo.dwFatVersion == 32) ? 32 : 1;
dwRootEntries = (fo.dwFatVersion == 32) ? 0 : fo.dwRootEntries;
Note the dwReservedSectors is 32 in FAT32 and 1 in FAT12/16. Root Entries is another different mentioned in previous paragraph, 0 for FAT32 (dynamic allocated) and fixed size (usually 512, defined in DEFAULT_ROOT_ENTRIES in public\common\sdk\inc\fatutil.h)
And then here
memset(pBootSec->bsVolumeLabel, 0x20, sizeof(pBootSec->bsVolumeLabel));
It sets the Volume Label as empty string.
Now let's carry on to the next section - write the root directory.
if (fo.dwFatVersion == 32) {
if (!(fo.dwFlags & FATUTIL_FORMAT_TFAT)) {
dwRootSectors = dwSectorsPerCluster;
}
else {
DIRENTRY dirEntry;
DWORD offset;
int iVolumeNo;
memset(pbBlock, 0, pdi->di_bytes_per_sect);
memset(&dirEntry, 0, sizeof(DIRENTRY));
dirEntry.de_attr = ATTR_VOLUME_ID;
// the first one is volume label
memcpy(dirEntry.de_name, "TFAT ", sizeof (dirEntry.de_name));
memcpy(pbBlock, &dirEntry, sizeof(dirEntry));
...
// Skip the next step of zeroing out clusters
dwCurrentSec += dwSectorsPerCluster;
dwRootSectors = 0;
}
}
// Each new root directory sector needs to be zeroed.
memset(pbBlock, 0, cbSizeBlk);
iRootSec=0;
while ( iRootSec < dwRootSectors) {
Basically, the code zero out the each entry in root directory depends on dwRootSectors. In FAT12/16, the dwRootSectors is calculated as the sectors we need for the root entries (512 for most of the case) and in FAT32 it just zero out the one cluster. Please note that, if it is a TFAT volume, it initialize the root directory with special volume label entries for some special purpose. Despite to its unusual initialization process for TFAT, it does provide a example for how to create a volume entry. With some minor modification, we can assign the volume label in FAT formatter and also remember to sync the volume label with bsVolumeLabel or bgbsVolumeLabel in boot sector.