| =============================================================================== |
| GNU Parted FAT file system documentation |
| =============================================================================== |
| |
| by Andrew Clausen <clausen@gnu.org> |
| |
| Copyright (C) 2000, 2001 Free Software Foundation, Inc. |
| |
| Permission is granted to copy, distribute and/or modify this document |
| under the terms of the GNU Free Documentation License, Version 1.1 |
| or any later version published by the Free Software Foundation; |
| with the no Invariant Sections, with the no Front-Cover Texts, and |
| with no Back-Cover Texts. A copy of the license is included in the |
| file, COPYING.DOC. |
| |
| |
| CONTENTS |
| -------- |
| |
| PART I - THE FAT FILE SYSTEM |
| |
| 1 Introduction |
| |
| 2 Overview |
| |
| 3 The Boot Sector |
| 3.1 Data Layout |
| 3.2 Descriptions of Fields |
| 3.3 Calculating the ignored fields |
| 3.4 DOS/Windows bootstrap process |
| |
| 4 The Info Sector |
| 4.1 Data Layout |
| 4.2 Descriptions of Fields |
| |
| 5 File Allocation Tables |
| |
| 6 Directory tree |
| 6.1 Directory entries |
| |
| PART II - GNU PARTED'S FAT IMPLEMENTATION |
| |
| 7 Resizing "issues" |
| |
| 8 Overview of GNU Parted's Strategy |
| |
| =============================================================================== |
| PART I - THE FAT FILE SYSTEM |
| =============================================================================== |
| |
| ------------------------------------------------------------------------------- |
| 1 INTRODUCTION |
| ------------------------------------------------------------------------------- |
| |
| This document describes the FAT filesystem, and GNU Parted's support for it. |
| |
| Unfortunately, there are no particularly good sources of information on the FAT |
| filesystem. The information here was deduced from the source code of about 10 |
| different programs, documentation from about 20 different sources and testing. |
| There are many cases where documentation for FAT from various sources |
| (including Microsoft) are misleading, or just plain wrong. For us, |
| documentation is correct if it matches the behaviour of Microsoft's |
| implementation. |
| |
| |
| ------------------------------------------------------------------------------- |
| 2 OVERVIEW |
| ------------------------------------------------------------------------------- |
| |
| FAT is a filesystem that is mainly used by Microsoft DOS, Windows 95, |
| Windows 98 and Windows 2000. FAT also stands for File Allocation Table - a |
| part of the FAT filesystem. |
| |
| FAT comes in three flavors: FAT12, FAT16 and FAT32. |
| FAT12 is used on floppy disks, and REALLY old hard drives <32Mb. FAT16 is |
| typically used on small hard drives <500Mb, and is very inefficient for large |
| hard drives. FAT32 is used on hard drives >500Mb under Windows 95 OSR2 and |
| later and Windows 2000. These three flavors have important differences (not |
| JUST an increase in the maximum possible number of clusters). On FAT12 and |
| FAT16 cluster size directly relates to the filesystem size: the number of |
| clusters is always between 2041 and 4080 resp. 32761 and 65520. |
| |
| The FAT filesystem has these parts (on disk, in this order): |
| * a bootsector. This contains all of the information about the filesystem |
| - whether it's FAT12, FAT16 or FAT32, how big it is, etc. |
| |
| * an information sector (FAT32 only). This contains additional information |
| that couldn't fit in the boot sector. |
| |
| * file allocation tables (FATs). There are usually two identical copies. |
| This is used to store the sequence of clusters that make of files. Essentially, |
| if you want to know the number of the cluster that comes after cluster X |
| in a file, you look up the number for X. If X is a magic number, it means |
| it's the end of the file. |
| |
| * clusters. The data inside files are stored in clusters. Most directory |
| information is stored in clusters (except the root directory in FAT12 and |
| FAT16). Clusters may be 1, 2, 4, 8, etc. sectors. Clusters are numbered, |
| counting from 2. |
| |
| * directory tree. The FAT filesystem has files and directories (no named |
| pipes, links, or anything fancy like that), that are stored in clusters. The |
| root directory on FAT12 and FAT16 is stored separately. In FAT32, the root |
| directory is stored inside a normal cluster, just like everything else. |
| |
| |
| ------------------------------------------------------------------------------- |
| 3 THE BOOT SECTOR |
| ------------------------------------------------------------------------------- |
| |
| The boot sector contains all of the information about the filesystem |
| - whether it's FAT12, FAT16 or FAT32, how big it is, etc. It also contains |
| the boot loader for the operating system, if there is an operating system |
| on the file system. It is always the first thing to appear in the filesystem |
| - i.e. it's found at sector 0. |
| |
| A word of warning: while the values inside the boot sector will always be |
| consistent with the file system, many of these values are not read by |
| Microsoft's implementation - they are calculated independently. |
| |
| |
| 3.1 The Data Layout |
| ------------------------------------------------------------------------------- |
| |
| Taken from libparted/fs_fat/bootsector.h: |
| |
| struct __attribute__ ((packed)) _FatBootSector { |
| __u8 boot_jump[3]; /* 00: Boot strap short or near jump */ |
| __u8 system_id[8]; /* 03: system name */ |
| __u16 sector_size; /* 0b: bytes per logical sector */ |
| __u8 cluster_size; /* 0d: sectors/cluster */ |
| __u16 reserved; /* 0e: reserved sectors */ |
| __u8 fats; /* 10: number of FATs */ |
| __u16 dir_entries; /* 11: number of root directory entries */ |
| __u16 sectors; /* 13: if 0, sector_count supersedes */ |
| __u8 media; /* 15: media code */ |
| __u16 fat_length; /* 16: sectors/FAT for FAT12/16 */ |
| __u16 secs_track; /* 18: sectors per track */ |
| __u16 heads; /* 1a: number of heads */ |
| __u32 hidden; /* 1c: hidden sectors (partition start) */ |
| __u32 sector_count; /* 20: no. of sectors (if sectors == 0) */ |
| |
| union __attribute__ ((packed)) { |
| /* FAT16 fields */ |
| struct __attribute__ ((packed)) { |
| __u8 drive_num; /* 24: */ |
| __u8 empty_1; /* 25: */ |
| __u8 ext_signature; /* 26: always 0x29 */ |
| __u32 serial_number; /* 27: */ |
| __u8 volume_name [11]; /* 2b: */ |
| __u8 fat_name [8]; /* 37: */ |
| __u8 boot_code[448]; /* 3f: Boot code (or message) */ |
| } fat16; |
| /* FAT32 fields */ |
| struct __attribute__ ((packed)) { |
| __u32 fat_length; /* 24: size of FAT in sectors */ |
| __u16 flags; /* 28: bit8: fat mirroring, low4: active fat */ |
| __u16 version; /* 2a: minor * 256 + major */ |
| __u32 root_dir_cluster; /* 2c: */ |
| __u16 info_sector; /* 30: */ |
| __u16 backup_sector; /* 32: */ |
| __u8 empty_1 [12]; /* 34: */ |
| __u16 drive_num; /* 40: */ |
| __u8 ext_signature; /* 42: always 0x29 */ |
| __u32 serial_number; /* 43: */ |
| __u8 volume_name [11]; /* 47: */ |
| __u8 fat_name [8]; /* 52: */ |
| __u8 boot_code[420]; /* 5a: Boot code (or message) */ |
| } fat32; |
| } u; |
| |
| __u16 boot_sign; /* 1fe: always 0xAA55 */ |
| }; |
| |
| |
| 3.2 Descriptions of Fields |
| ------------------------------------------------------------------------------- |
| |
| 3.2.1 Fields common to FAT12, FAT16 and FAT32 |
| ----------------------------------------------- |
| __u8 boot_jump[3]; /* 00: Boot strap short or near jump */ |
| This contains the Intel x86 instruction to "jump" to further down in the |
| boot sector. This is necessary, because on PC systems, the first sector of |
| the disk is loaded and executed. On hard disks of PC systems, the first |
| sector of the disk is in fact the Master Boot Record - which contains the |
| partition table. The master boot record loads the first sector of the boot |
| partition, so the end result is the same for floppy's and hard disks. |
| |
| |
| __u8 system_id[8]; /* 03: system name */ |
| This contains the name of the program or operatings system that created the |
| file system. For FAT32, it seems you must have "MSWIN4.1" here. |
| If this is "MSDMF3.2" (afaik only the "MSDMF" is checked") the partition |
| can't be written under Windows 9x, Windows NT and Windows 2000. This is |
| how Microsoft realizes read-only installation or distribution floppy disks. |
| |
| |
| __u16 sector_size; /* 0b: bytes per logical sector */ |
| This is bizarre. Sectors are always 512 bytes. However, a "logical" sector |
| is a hack that allows sectors to be bigger (a multiple of 512 bytes). This |
| is rarely used, and untested in GNU Parted at the moment. (Side note: is |
| it possible to use this to avoid requiring a cluster resize?) |
| |
| |
| __u8 cluster_size; /* 0d: sectors/cluster */ |
| THIS IS IGNORED BY MICROSOFT'S IMPLEMENTATION OF FAT12 AND FAT16! (See section |
| 3.3) This contains the size of all clusters, given in sectors. This value is |
| "read-only". |
| |
| |
| __u16 reserved; /* 0e: reserved sectors */ |
| The number of sectors before the file allocation tables begin. i.e. The |
| number of the first sector of the first file allocation table. |
| |
| |
| __u8 fats; /* 10: number of FATs */ |
| The number of file allocation tables (usually 2). |
| |
| |
| __u16 dir_entries; /* 11: number of root directory entries */ |
| The size of the root directory (FAT12 and FAT16 only), in "directory entries" |
| (32 bytes). The root directory is immediately after the FATs (FAT12 and |
| FAT16 only). The first cluster (i.e. cluster number 2) starts immediately |
| after the root directory (or after the FATs for FAT32). |
| |
| |
| __u16 sectors; /* 13: if 0, sector_count supersedes */ |
| THIS IS IGNORED BY MICROSOFT'S IMPLEMENTATION! The total size of the file |
| system. If the file system is bigger than 65536 sectors, this is set to 0, |
| and a 32 bit field is used instead. Microsoft's implementation gets this |
| values from hardware (for floppy disks), or the partition table (for hard |
| disks), rather than reading it off disk. |
| |
| |
| __u8 media; /* 15: media code */ |
| For hard disks, this should always be 0xf8. |
| |
| |
| __u16 fat_length; /* 16: sectors/FAT for FAT12/16 */ |
| THIS IS IGNORED BY MICROSOFT'S IMPLEMENTATION! (See section 3.3) The size in |
| sectors of each file allocation table (FAT12 and FAT16 only). A 32-bit field |
| is used for FAT32. This value is "read-only". |
| |
| |
| |
| __u16 secs_track; /* 18: sectors per track */ |
| __u16 heads; /* 1a: number of heads */ |
| These should match the BIOS geometry. The GNU Parted README file explains |
| BIOS geometry. |
| |
| |
| __u32 hidden; /* 1c: hidden sectors (partition start) */ |
| On a hard disk, this should be the number of sectors from the start of the |
| head (in terms of BIOS geometry) to the start of the partition. i.e. the |
| S in the CHS partition start. |
| |
| |
| __u32 sector_count; /* 20: no. of sectors (if sectors == 0) */ |
| The size of the file system in sectors (if sectors is 0). |
| |
| |
| __u16 boot_sign; /* 1fe: always 0xAA55 */ |
| Boot sector signature. Don't use this exclusively to detect FAT file systems! |
| It's also the signature for partition table sectors (and it appears in the |
| same place too!) Idiots. |
| |
| 3.2.2 Fields in FAT12 and FAT16 |
| --------------------------------- |
| |
| __u8 drive_num; /* 24: */ |
| Always 0x80. |
| |
| |
| __u8 ext_signature; /* 26: always 0x29 */ |
| Always 0x29. |
| |
| |
| __u32 serial_number; /* 27: */ |
| Serial number: Used to detect media change on floppy disk and removable drives. |
| |
| |
| __u8 volume_name [11]; /* 2b: */ |
| The disk label. |
| |
| |
| __u8 fat_name [8]; /* 37: */ |
| "FAT12\0\0\0" or "FAT16\0\0\0". |
| |
| |
| 3.2.3 Fields in FAT32 |
| ----------------------- |
| |
| __u32 fat_length; /* 24: size of FAT in sectors */ |
| The size in sectors of each file allocation table. |
| |
| |
| __u16 flags; /* 28: bit8: fat mirroring, low4: active fat */ |
| No idea what these are. |
| |
| |
| __u16 version; /* 2a: minor * 256 + major */ |
| Seems to be 0 (?) |
| |
| |
| __u32 root_dir_cluster; /* 2c: */ |
| The number of the first cluster in the root directory. |
| |
| |
| __u16 info_sector; /* 30: */ |
| The number of the information sector. |
| |
| |
| __u16 backup_sector; /* 32: */ |
| The number of the backup of the boot sector (i.e. this sector). |
| |
| |
| __u16 drive_num; /* 40: */ |
| Always 0x80. |
| |
| |
| __u8 ext_signature; /* 42: always 0x29 */ |
| Always 0x29. |
| |
| |
| __u32 serial_number; /* 43: */ |
| Serial number (for Echelon, or something) |
| |
| |
| __u8 volume_name [11]; /* 47: */ |
| The disk label. |
| |
| |
| __u8 fat_name [8]; /* 52: */ |
| "FAT32\0\0\0". |
| |
| |
| 3.3 Calculating the ignored fields |
| ------------------------------------------------------------------------------- |
| The cluster_size and fat_length fields are ignored by Microsoft's |
| implementation of FAT12 and FAT16, but NOT FAT32. That is, they are written out |
| correctly, but NOT READ IN. (Note: if FAT32 file system is configured to |
| have less than 65520 clusters, then Windows assumes it's FAT16) |
| |
| Since these values don't usually change unless you resize a filesystem, this |
| causes no problems. However, if you want to resize the filesystem, you have to |
| calculate these values to what Microsoft calculates them to, from the size of |
| the filesystem. It took me 2 months to figure this out (I want to KILL |
| somebody...) |
| |
| Here's the algorithm I came up with that seemed to match all my test data: |
| (from libparted/fs_fat/calc.c) |
| |
| FatCluster |
| fat_max_cluster_count (FatType fat_type) { |
| switch (fat_type) { |
| case FAT_TYPE_FAT12: return 0xff0; |
| case FAT_TYPE_FAT16: return 0xfff0; |
| case FAT_TYPE_FAT32: return 0x0ffffff0; |
| } |
| return 0; |
| } |
| |
| FatCluster |
| fat_min_cluster_count (FatType fat_type) { |
| switch (fat_type) { |
| case FAT_TYPE_FAT12: |
| case FAT_TYPE_FAT16: |
| return fat_max_cluster_count (fat_type) / 2; |
| |
| case FAT_TYPE_FAT32: return 0xfff0; |
| } |
| return 0; |
| } |
| |
| static int |
| calc_sizes (PedGeometry* geom, PedSector align, int cluster_size, |
| PedSector root_dir_sectors, FatCluster* out_cluster_count, |
| PedSector* out_fat_size, FatType fat_type) |
| { |
| PedSector data_fat_size; |
| PedSector fat_sectors; |
| PedSector cluster_sectors; |
| FatCluster cluster_count; |
| int i; |
| |
| data_fat_size = geom->length - fat_min_reserved_sector_count (fat_type) |
| - align; |
| if (fat_type == FAT_TYPE_FAT16) |
| data_fat_size -= root_dir_sectors; |
| |
| fat_sectors = 0; |
| for (i = 0; i < 2; i++) { |
| if (fat_type == FAT_TYPE_FAT32) |
| cluster_sectors = data_fat_size - fat_sectors; |
| else |
| cluster_sectors = data_fat_size - 2 * fat_sectors; |
| |
| cluster_count = cluster_sectors / (cluster_size / 512); |
| fat_sectors = div_round_up (cluster_count + 2, |
| entries_per_sector (fat_type)); |
| } |
| |
| cluster_sectors = data_fat_size - 2 * fat_sectors; |
| cluster_count = cluster_sectors / (cluster_size / 512); |
| |
| if (cluster_count > fat_max_cluster_count (fat_type) |
| || cluster_count < fat_min_cluster_count (fat_type)) |
| return 0; |
| |
| *out_cluster_count = cluster_count; |
| *out_fat_size = fat_sectors; |
| |
| return 1; |
| } |
| |
| FIXME: this is the "trial and error" algorithm. What happened to my simple, |
| test one? |
| |
| If the implications of the above code aren't that clear, here are some of |
| them: |
| * for FAT16, the minimum number of clusters is 32760. |
| * the cluster size is completely determined by the size of the file system, |
| for FAT16. That means, if a file system is to be resized, it is quite |
| possible that the cluster size must be changed just to be compatible |
| with Microsoft's implementation (Linux, for example, doesn't calculate the |
| numbers independently, so it would work fine. So always test your code on |
| Microsoft as well as Linux) |
| |
| |
| 3.4 DOS/Windows bootstrap process |
| ------------------------------------------------------------------------------- |
| All of the information that follows is from me reverse-engineering different |
| versions of Microsoft's boot sectors. It's pretty weird code (as you can |
| imagine...), so there might be mistakes here. |
| There are many different versions of the boot sector: |
| * Windows 98/2000/ME FAT12/FAT16 (supports CHS and LBA) |
| * Windows 98/2000/ME FAT32 (supports CHS and LBA) |
| |
| (1) The MBR, LILO, or whatever loads in the first sector of the FAT |
| partition into 0000:7c00, and executes it. |
| |
| (2) The first sector of the FAT partition (the "boot sector") does: |
| (a) loads the Master Boot Record (sector 0 of the disk) using the |
| BIOS's CHS calls, and finds the boot partition. If the boot partition |
| has the LBA flag marked, it writes 0xe to [bp+2] (0000:7c02). The "read |
| sectors" function in the boot loader checks for 0xe here, and uses LBA if |
| it finds it, and CHS otherwise. |
| |
| (b) If it is the FAT32 version of the boot sector, it loads sectors 1 |
| through 3 of the partition (it finds this from the "hidden" field in the |
| FAT boot sector, that was loaded by the MBR/LILO) at address 0000:7e00, and |
| continues executing at 0000:8000. Note: the FAT16 version doesn't require |
| any more sectors to be read (they crammed it all in!), and it goes |
| directly to step 3. |
| |
| (3) The code loads IO.SYS (starting at address 0000:0700), off the same |
| partition, and executes it, beginning execution at 0070:0200. (Note: |
| According to the x86 real mode segmentation scheme, 0070:0200 refers to the |
| same physical memory as 0000:0900) |
| |
| |
| ------------------------------------------------------------------------------- |
| 4 THE INFO SECTOR |
| ------------------------------------------------------------------------------- |
| |
| The info sector is used in FAT32 to store additional information about the |
| file system. |
| |
| |
| 4.1 Data Layout |
| ------------------------------------------------------------------------------- |
| |
| struct __attribute__ ((packed)) _FatInfoSector { |
| __u32 signature_1; /* should be 0x41615252 */ |
| __u8 unused [480]; |
| __u32 signature_2; /* should be 0x61417272 */ |
| __u32 free_clusters; |
| __u32 next_cluster; /* most recently allocated cluster */ |
| __u8 unused2 [0xe]; |
| __u16 signature_3; /* should be 0xaa55 */ |
| }; |
| |
| |
| 4.2 Descriptions of Fields |
| ------------------------------------------------------------------------------- |
| |
| __u32 signature_1; /* should be 0x41615252 */ |
| Always 0x41615252 ("AaRR") |
| |
| |
| __u32 signature_2; /* should be 0x61417272 */ |
| Always 0x61417272 ("aArr") |
| |
| |
| __u32 free_clusters; |
| The number of free clusters. This could be calculated by going through the |
| FATs, but this is stored on shutdown to speed things up. |
| |
| |
| __u32 next_cluster; /* most recently allocated cluster */ |
| This contains the number of the last cluster allocated. This speeds up |
| cluster allocation, because free clusters usually come in chunks, so you |
| can scan right for free clusters in the FAT. |
| |
| |
| __u16 signature_3; /* should be 0xaa55 */ |
| Always 0xaa55. |
| |
| |
| ------------------------------------------------------------------------------- |
| 5 FILE ALLOCATION TABLES |
| ------------------------------------------------------------------------------- |
| |
| File allocation table (FAT) is a strange name, come to think of it. Perhaps it |
| should be called cluster allocation table, or something (?). Essentially, |
| it is used to represent file chains (i.e. linked lists) for files and |
| directories. There are usually two FATs (one is a backup, and should be |
| identical). |
| |
| Anyway, a FAT is essentially an array. In FAT12, each entry is 12 bits, |
| FAT16 - 16 bits, FAT32 - 32 bits. Hence the names. |
| |
| The first byte of each FAT must match the "media" field in the boot sector. |
| The rest of the first 2 entries are filled with 0xff. |
| |
| All remaining entries - from 2 onwards - correspond to a cluster. i.e. |
| the second entry corresponds to cluster 2. Clusters are numbered from 2 onwards |
| (i.e. there is no cluster 1). |
| |
| The number in each entry gives the number of the cluster that occurs next in |
| the file chain (linked list). However, there are a few magic numbers: |
| |
| * unused (0). Indicates the cluster is unused. |
| * end of file (0xff0 for FAT12, 0xfff0 for FAT16, 0x0ffffff0 for FAT32). |
| Indicates this is the last cluster in the file or directory. Obviouslly for |
| FAT32, the number of clusters must be < 0x0ffffff0. So it should be called |
| FAT28, perhaps... |
| * bad cluster (0xff7 for FAT12, 0xfff7 for FAT16, 0x0ffffff7 for FAT32). |
| Indicates the disk is physically damaged where the cluster is stored. |
| |
| |
| ------------------------------------------------------------------------------- |
| 6 DIRECTORY TREE |
| ------------------------------------------------------------------------------- |
| |
| The directory tree is simple: there are files and directories. Files and |
| directories are stored in clusters (except the root directory on FAT12 and |
| FAT16). Directories are essentially special files that contain directory |
| entries. |
| |
| In FAT12 and FAT16, the root directory is stored immediately after the FATs. |
| In FAT32, the first cluster of the root directory is given in the FAT. (To |
| get the second cluster - if there is one - you just look up the FAT) |
| |
| |
| 6.1 Directory Entries |
| ------------------------------------------------------------------------------ |
| Directories are made up of directory entries, each of which represent a file, |
| a directory or part of a file name (the VFAT extension - YUCK!!!). |
| |
| Each directory (except the root directory) contains a '.' (this directory) and |
| '..' (parent directory) entry. |
| |
| 6.1.1 Fields |
| -------------- |
| |
| From libparted/fs_fat/fat.h: |
| |
| struct __attribute__ ((packed)) _FatDirEntry { |
| __u8 name[8]; |
| __u8 extension[3]; |
| __u8 attributes; |
| __u8 is_upper_case_name; |
| __u8 creation_time_low; /* milliseconds */ |
| __u16 creation_time_high; |
| __u16 creation_date; |
| __u16 access_date; |
| __u16 first_cluster_high; /* for FAT32 */ |
| __u16 time; |
| __u16 date; |
| __u16 first_cluster; |
| __u32 length; |
| }; |
| |
| 6.1.2 Field Descriptions |
| -------------------------- |
| |
| __u8 name[8]; |
| The first part of the file name. Eg, for a file called README.TXT, this is |
| README. Files with names longer than 8 characters use the VFAT extension (not |
| described here). When a file is deleted, the first character is set to 0xe5. |
| If the first character is 0x0, then the entire directory entry is unused. |
| |
| |
| __u8 extension[3]; |
| The last part of the file name. Eg, for a file called README.TXT, this is TXT. |
| This explains all those .HTM files around the place... |
| |
| |
| __u8 attributes; |
| If this is 0x0f, then this directory entry is a VFAT entry, and stores part |
| of a file name. Otherwise, it's treated as various bit fields: |
| |
| 0x1 read-only |
| 0x2 hidden |
| 0x4 system |
| 0x8 volume label |
| 0x10 directory |
| 0x20 archived |
| |
| |
| __u8 is_upper_case_name; |
| A Microsoft cludge: create a file with 8.3 name BUT containing small letters |
| (like ReadMe.Txt) which is treated as an LFN (long file name) and occupies |
| three directory entries. Now when you rename this file to all uppercase |
| README.TXT,- under Windows NT 4 the then superfluous LFN-VFAT entries are |
| removed, resulting in a smaller directory, but under Windows 9x the LFN-VFAT |
| entries are just upd- and this flag is set. Executing DEFRAG on such entries |
| MIGHT then remove the superfluous LFN-VFAT entries and shrink the directory. |
| |
| |
| __u8 creation_time_low; /* milliseconds */ |
| __u16 creation_time_high; |
| __u16 creation_date; |
| Creation time and date. Not used wih MS-DOS <7.0! |
| |
| |
| __u16 access_date; |
| Last access date. Not used wih MS-DOS <7.0! |
| |
| |
| __u16 first_cluster_high; /* for FAT32 */ |
| High 32 bits of the first cluster in the file or directory (FAT32 only) |
| |
| |
| __u16 time; |
| __u16 date; |
| ? |
| |
| |
| __u16 first_cluster; |
| Low 16 bits of first cluster. |
| |
| |
| __u32 length; |
| Length of file in bytes. |
| |
| |
| |
| =============================================================================== |
| PART II - GNU PARTED'S FAT IMPLEMENTATION |
| =============================================================================== |
| |
| ------------------------------------------------------------------------------- |
| 7 RESIZING "ISSUES" |
| ------------------------------------------------------------------------------- |
| |
| To resize a FAT file system, a program must: |
| * copy all used clusters that lie outside of the new partition onto free |
| space on that partition. The directory tree and FATs must be updated to |
| reflect this. |
| * grow or shrink the file allocation table(s) to the size corresponding |
| to the size of the partition. |
| * convert between FAT16 and FAT32 if necessary. This involves: |
| - changing the form of the root directory (FAT16 has it's before the |
| clusters whereas FAT32 stores it in normal clusters). |
| - creating space for the backup boot sector and info sector. |
| - updating the directory tree to use 32 bit first cluster entries. |
| * align the start of the clusters (using the "reserved" field in the |
| boot sector), so that the clusters that are common to the old and new |
| partition can be preserved. |
| * re-number clusters. e.g. if you chop out some clusters from the beginning |
| (i.e. move the start forward), then the first cluster (i.e. number 2) will |
| be refer to a different cluster on the disk. The directory tree and FATs must |
| be updated to reflect this. |
| * create a new boot sector (and the info sector and backup boot sector for |
| FAT32) |
| |
| |
| ------------------------------------------------------------------------------- |
| 8 OVERVIEW OF GNU PARTED'S STRATEGY |
| ------------------------------------------------------------------------------- |
| |
| GNU Parted copies all clusters that are not accessible from the new file system |
| (either because it lies outside the file system, or file system meta-data must |
| reside there instead) to an accessible place. |
| |
| Since all clusters must be renumbered (in most cases), the entire directory |
| tree must be updated. However converting the directory tree from one numbering |
| system to another would break the file system if it was interrupted halfway |
| through. Instead, GNU Parted duplicates the directory tree (except the root |
| directory for FAT16) along with clusters that need to be copied because they |
| lie outside the new file system. The directory tree is duplicated at the same |
| time as inaccessible clusters are. The relevant function, |
| needs_duplicating() in libparted/fs_fat/clstdup.c is: |
| |
| static int |
| needs_duplicating (FatOpContext* ctx, FatCluster cluster) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); |
| |
| return (fs_info->fat_flag_map [cluster] == FAT_FLAG_FILE |
| && !fat_op_context_map_static_cluster (ctx, cluster)) |
| || fs_info->fat_flag_map [cluster] |
| == FAT_FLAG_DIRECTORY; |
| } |
| |
| |
| |
| A good overview of this implementation is in the fat_resize() function, in |
| libparted/fs_fat/resize.c (slightly edited): |
| |
| int |
| fat_resize (PedFileSystem* fs, PedGeometry* geom) |
| { |
| FatSpecific* fs_info = FAT_SPECIFIC (fs); |
| FatSpecific* new_fs_info; |
| FatOpContext* ctx; |
| PedFileSystem* new_fs; |
| |
| ctx = create_resize_context (fs, geom); |
| if (!ctx) |
| return 0; |
| new_fs = ctx->new_fs; |
| new_fs_info = FAT_SPECIFIC (new_fs); |
| |
| if (!fat_duplicate_clusters (ctx)) |
| return 0; |
| if (fs_info->fat_type == FAT_TYPE_FAT16 |
| && new_fs_info->fat_type == FAT_TYPE_FAT32) { |
| if (!alloc_root_dir (ctx)) |
| return 0; |
| } |
| if (!fat_construct_new_fat (ctx)) |
| return 0; |
| if (!fat_construct_dir_tree (ctx)) |
| return 0; |
| if (!fat_table_write_all (new_fs_info->fat, new_fs)) |
| return 0; |
| |
| if (!fat_boot_sector_generate (&new_fs_info->boot_sector, |
| new_fs)) |
| return 0; |
| if (!fat_boot_sector_write (&new_fs_info->boot_sector, new_fs)) |
| return 0; |
| if (new_fs_info->fat_type == FAT_TYPE_FAT32) { |
| if (!fat_info_sector_generate ( |
| &new_fs_info->info_sector, new_fs)) |
| return 0; |
| if (!fat_info_sector_write (&new_fs_info->info_sector, |
| new_fs)) |
| return 0; |
| } |
| |
| if (!resize_context_assimilate (ctx)) |
| return 0; |
| |
| return 1; |
| } |
| |
| |