// idfat.cc: Show useful information about a FAT file system. // DWF 2008-07-14 // g++ -Wall -Wextra -O2 -s -o idfat idfat.cc #define __STDC_FORMAT_MACROS #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include struct __attribute__ ((__packed__)) BPB_continuation_old { uint8_t BS_DrvNum; uint8_t BS_Reserved1; uint8_t BS_BootSig; uint32_t BS_VolID; char BS_VolLab[11]; char BS_FilSysType[8]; }; struct __attribute__ ((__packed__)) BPB_continuation_FAT32 { uint32_t BPB_FATSz32; uint8_t miscellaneous[31]; char BS_VolLab[11]; char BS_FilSysType[8]; }; struct __attribute__ ((__packed__)) BPB { uint8_t BS_jmpBoot[3]; char BS_OEMName[8]; uint16_t BPB_BytsPerSec; uint8_t BPB_SecPerClus; uint16_t BPB_RsvdSecCnt; uint8_t BPB_NumFATs; uint16_t BPB_RootEntCnt; uint16_t BPB_TotSec16; uint8_t BPB_Media; uint16_t BPB_FATSz16; uint16_t BPB_SecPerTrk; uint16_t BPB_NumHeads; uint32_t BPB_HiddSec; uint32_t BPB_TotSec32; union { BPB_continuation_old old; BPB_continuation_FAT32 FAT32; }; }; enum FATType {FAT12, FAT16, FAT32}; void bomb (char const * const fname) { fprintf (stderr, "%s does not contain a valid FAT file system.\n", fname); exit (-1); } void checkMagicNumber (FILE *fp, char const * const fname) { if (fseek (fp, 510L, SEEK_SET) == -1) bomb (fname); uint16_t magicnum; if (fread (&magicnum, sizeof(magicnum), 1, fp) != 1) bomb (fname); if (magicnum != 0xAA55) bomb (fname); rewind (fp); } bool containsFunnyChars (char const *s) { assert (s); for (; *s != '\0'; ++s) if ((*s < 0x20) || (*s & 0x80)) return true; return false; } void printSize (uintmax_t bytes) { static char const prefixes[] = {'\0', 'K', 'M', 'G', 'T', 'P', 'E', '\0'}; static uintmax_t const factor (1024); printf ("%25" PRIuMAX " B", bytes); if (bytes >= factor) { uint_fast8_t scale (0); uintmax_t divisor (1); while ((prefixes[scale+1] != '\0') && (bytes / divisor >= factor)) { divisor *= factor; ++scale; } printf (" (%.2f %ciB)", (double)bytes/divisor, prefixes[scale]); } printf ("\n"); } int main (int argc, char **argv) { if (argc != 2) { fprintf (stderr, "Usage: idfat /dev/xxxn\n"); exit (-1); } char const * const fname (argv[1]); // READ ONLY FILE *fp = fopen (fname, "r"); if (!fp) { perror (fname); exit (-1); } // Do all sanity checks BEFORE printing out any statistics. checkMagicNumber (fp, fname); BPB bpb; if (fread (&bpb, sizeof(bpb), 1, fp) != 1) bomb (fname); fclose (fp); // Avoid divide-by-zeros. if (bpb.BPB_BytsPerSec < 512 || bpb.BPB_SecPerClus == 0) bomb (fname); uint32_t FATSz; if (bpb.BPB_FATSz16) FATSz = bpb.BPB_FATSz16; // Indicates FAT12 or FAT16 else { FATSz = bpb.FAT32.BPB_FATSz32; // Indicates FAT32 if (bpb.BPB_RootEntCnt) // Indicates FAT12 or FAT16 bomb (fname); // Inconsistent } uint32_t TotSec = (bpb.BPB_TotSec16 ? bpb.BPB_TotSec16 : bpb.BPB_TotSec32); // BPB_BytsPerSec - 1 implements the specified "rounding up." uint32_t RootDirSectors = ((bpb.BPB_RootEntCnt * 32) + (bpb.BPB_BytsPerSec - 1)) / bpb.BPB_BytsPerSec; uint32_t DataSec = TotSec - (bpb.BPB_RsvdSecCnt + (bpb.BPB_NumFATs * FATSz) + RootDirSectors); uint32_t CountofClusters = DataSec / bpb.BPB_SecPerClus; FATType ft; if (CountofClusters < 4085) ft = FAT12; else if (CountofClusters < 65525) ft = FAT16; else ft = FAT32; switch (ft) { case FAT12: case FAT16: if (bpb.BPB_FATSz16 == 0) bomb (fname); // Inconsistent break; case FAT32: if (bpb.BPB_FATSz16 != 0 || bpb.BPB_RootEntCnt != 0) bomb (fname); // Inconsistent break; default: assert (false); } char FilSysType[9]; strncpy (FilSysType, (ft == FAT32 ? bpb.FAT32.BS_FilSysType : bpb.old.BS_FilSysType), 8); FilSysType[8] = '\0'; for (int i(7); i>=0; --i) if (FilSysType[i] == ' ') FilSysType[i] = '\0'; else break; if (containsFunnyChars (FilSysType)) bomb (fname); // Beyond this point we are convinced that it is a valid FAT file system. printf ("File system type (normative): %s\n", (ft == FAT12 ? "FAT12" : (ft == FAT32 ? "FAT32" : (bpb.BPB_TotSec16 ? "FAT16 (DOS 3.0-compatible)" : "FAT16 (DOS 3.31+)")))); printf ("Type string (informative): %s\n", FilSysType); printf ("\nApplicable fdisk hex code(s):\n"); switch (ft) { case FAT12: printf (" 1 FAT12\n"); break; case FAT16: if (bpb.BPB_TotSec16) printf (" 4 FAT16 <32M\n"); else printf (" 6 FAT16\n"); printf (" e W95 FAT16 (LBA)\n"); break; case FAT32: printf (" b W95 FAT32\n c W95 FAT32 (LBA)\n"); break; default: assert (false); } printf ("\nFile system size: "); printSize ((uintmax_t)TotSec * bpb.BPB_BytsPerSec); printf ("Usable space: "); printSize ((uintmax_t)CountofClusters * bpb.BPB_SecPerClus * bpb.BPB_BytsPerSec); printf ("Allocation unit size: "); printSize ((uintmax_t)bpb.BPB_SecPerClus * bpb.BPB_BytsPerSec); return 0; }