1 /*****************************************************************************\
3 fat.c - FAT12/FAT16 file system
5 (c) 2004 Copyright Hewlett-Packard Development Company, LP
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 Author: David Suffield
24 1. Global variables are used, therefore this library is not thread safe.
25 2. Current implementation has not been tested on a big-edian system.
27 \*****************************************************************************/
34 #include <netinet/in.h>
38 * Private data structures.
45 int RootDirStartSector;
46 int RootDirNumSectors;
47 uint8_t *Fat; /* cached FAT16 data */
48 int FatSize; /* in bytes */
49 uint8_t *Fat12; /* FAT12 backup */
50 int Fat12Size; /* in bytes */
51 uint8_t *Fat16; /* FAT16 backup */
52 int WriteProtect; /* 0=false, 1=true */
58 int StartCluster; /* equals zero if root */
62 } CURRENT_WORKING_DIRECTORY;
72 int CurrSectorNumInCluster;
75 int DirEntryNum; /* number in current directory */
76 } CURRENT_FILE_ATTRIBUTES;
80 char JumpInstruction[3]; /* offset = 0 */
81 char OEMID[8]; /* 3 */
82 uint16_t BytesPerSector; /* 11 */
83 uint8_t SectorsPerCluster; /* 13 */
84 uint16_t ReservedSectors; /* 14 */
85 uint8_t Fats; /* number of copies of the fat, 16 */
86 uint16_t RootEntries; /* 17 */
87 uint16_t SmallSectors; /* 19 */
88 uint8_t Media; /* 21 */
89 uint16_t SectorsPerFat; /* 22 */
90 uint16_t SectorsPerTrack; /* 24 */
91 uint16_t Heads; /* 26 */
92 uint32_t HiddenSectors; /* 28 */
93 uint32_t LargeSectors; /* number of sector if SmallSectors == 0, 32 */
94 uint8_t DriveNumber; /* 36 */
95 uint8_t CurrentHead; /* 37 */
96 uint8_t Signature; /* 38 */
97 uint32_t ID; /* random serial number, 39 */
98 uint8_t VolumeLabel[11]; /* 43 */
99 uint8_t SystemID[8]; /* 54 */
100 uint8_t LoadInstructions[512-64];
101 uint16_t EndSignature; /*=AA55h*/
102 } __attribute__((packed)) FAT_BOOT_SECTOR; /* 512 bytes total */
106 char Name[8],Ext[3]; /* name and extension */
107 uint8_t Attributes; /* attribute bits */
108 uint8_t Reserved[10];
111 uint16_t StartCluster;
112 uint32_t Size; /* size of the file in bytes */
113 } __attribute__((packed)) FAT_DIRECTORY; /* 32 bytes total */
115 #define FAT_IS_DIR 0x10
116 #define FAT_END_OF_DIR 0x2
117 #define FAT_FILE_DELETED 0xe5
118 #define FAT_LONG_FILENAME 0x3
121 * Private data objects. Note, static variables are initialized to zero.
123 static FAT_BOOT_SECTOR bpb; /* bios parameter block */
124 static DISK_ATTRIBUTES da;
125 static CURRENT_WORKING_DIRECTORY cwd;
126 static CURRENT_FILE_ATTRIBUTES fa;
128 /* Convert 12-bit FAT to 16-bit FAT. */
129 int ConvertFat12to16(uint8_t *dest, uint8_t *src, int maxentry)
132 uint16_t *p16 = (uint16_t *)dest;
135 for (i=0; i<maxentry; i++)
139 *p16++ = lit2hs(*(uint16_t *)p12 >> 4); /* odd fat entry */
144 *p16++ = lit2hs(*(uint16_t *)p12 & 0xfff); /* even fat entry */
151 /* Convert 16-bit FAT to 12-bit FAT. */
152 int ConvertFat16to12(uint8_t *dest, uint8_t *src, int maxentry)
155 uint16_t *p16 = (uint16_t *)src;
158 for (i=0; i<maxentry; i++, p16++)
162 *p12 = (uint8_t)h2lits(*p16 >> 4); /* odd fat entry */
167 *(uint16_t *)p12 = h2lits(*p16 | (*(p16+1) << 12)); /* even fat entry */
174 int readsect(int sector, int nsector, void *buf, int size)
176 int len=nsector, total=0;
179 /* Read 1-blksize sectors. */
180 for (i=0; i<nsector; i+=n, len-=n)
182 n = len > FAT_BLKSIZE ? FAT_BLKSIZE : len;
183 if (ReadSector(sector+i, n, buf+total, size-total) != 0)
185 total += n*FAT_HARDSECT;
194 int writesect(int sector, int nsector, void *buf, int size)
196 int len=nsector, total=0;
199 /* Write 1-blksize sectors. */
200 for (i=0; i<nsector; i+=n, len-=n)
202 n = len > FAT_BLKSIZE ? FAT_BLKSIZE : len;
203 if (WriteSector(sector+i, n, buf+total, size-total) != 0)
205 total += n*FAT_HARDSECT;
220 if (strcmp((char *)bpb.SystemID, "FAT12") == 0)
222 if ((p12 = malloc(da.Fat12Size)) == NULL)
224 ConvertFat16to12(p12, da.Fat, da.Fat12Size/1.5);
225 for (i=0; i<bpb.SectorsPerFat; i++)
227 if (memcmp(p12+total, da.Fat12+total, FAT_HARDSECT) != 0)
228 if (writesect(da.FatStartSector+i, 1, p12+total, FAT_HARDSECT) != 0)
230 total += FAT_HARDSECT;
235 for (i=0; i<bpb.SectorsPerFat; i++)
237 if (memcmp(da.Fat+total, da.Fat16+total, FAT_HARDSECT) != 0)
238 if (writesect(da.FatStartSector+i, 1, da.Fat+total, FAT_HARDSECT) != 0)
240 total += FAT_HARDSECT;
256 /* Set CWD to the root directory. */
257 cwd.Name[0] = '/'; cwd.Name[1] = 0;
258 cwd.StartSector = da.RootDirStartSector;
259 cwd.CurrSector = cwd.StartSector;
260 cwd.NumEntries = bpb.RootEntries;
261 cwd.StartCluster = 0;
266 /* Returns number of free clusters (ie: those with 0x0 as the value in FAT). */
267 int FindFreeClusters(void)
269 int16_t *pfat = (int16_t *)da.Fat;
270 int max_entry = da.FatSize/2;
271 int i, freeclusters=0;
273 for (i=0; i<max_entry; i++)
281 int ConvertClusterToSector(int cluster)
285 sector = cluster - 2; /* skip first two FAT entries */
286 sector *= bpb.SectorsPerCluster; /* find the sector number relative to data start */
287 sector += da.DataStartSector; /* find absolute sector number */
292 /* Get next cluster from FAT. */
293 int GetNextCluster(int cluster)
295 uint16_t *pfat = (uint16_t *)da.Fat;
296 return *(pfat+cluster);
299 /* Tries to load the directory entry specified by filenumber. */
300 int LoadFileInCWD(int filenumber)
302 uint8_t buf[FAT_HARDSECT];
303 FAT_DIRECTORY *pde = (FAT_DIRECTORY *)buf;
304 int i, j, fn, sector, cluster, cluster_cnt;
305 int de_per_sector = FAT_HARDSECT/sizeof(FAT_DIRECTORY);
308 sector = filenumber / de_per_sector; /* determine sector offset */
309 fn = filenumber - (sector * de_per_sector); /* determine file number in sector */
311 if (cwd.StartCluster)
312 { /* CWD = subdirectory */
314 /* Determine cluster */
315 cluster = cwd.StartCluster;
316 cluster_cnt = sector / bpb.SectorsPerCluster;
317 for (i=0; i<cluster_cnt && cluster<0xfff7 && cluster; i++)
318 cluster = GetNextCluster(cluster);
319 if (cluster >= 0xfff7 || cluster == 0)
320 return FAT_END_OF_DIR;
321 cwd.CurrSector = ConvertClusterToSector(cluster);
322 sector -= cluster_cnt * bpb.SectorsPerCluster;
326 cwd.CurrSector = cwd.StartSector;
328 /* Check for max entry. */
329 if (filenumber > (da.RootDirNumSectors * de_per_sector))
330 return FAT_END_OF_DIR;
333 cwd.CurrSector += sector;
336 fa.DirSectorNum = cwd.CurrSector;
338 /* Read the directory sector. */
341 readsect(fa.DirSectorNum, 1, buf, sizeof(buf));
345 return FAT_END_OF_DIR;
346 if (c == FAT_FILE_DELETED)
347 return FAT_FILE_DELETED;
349 /* Read file information from directory and convert to 8.3 format. */
350 for (i=0; (i<sizeof(pde->Name)) && pde->Name[i] && (pde->Name[i] != ' '); i++) /* copy charactors up to any space */
351 fa.Name[i] = pde->Name[i];
352 if (pde->Ext[0] && (pde->Ext[0] != ' '))
355 for (j=0; (pde->Ext[j] != ' ') && (j<sizeof(pde->Ext)); j++, i++) /* copy charactors up to space */
356 fa.Name[i] = pde->Ext[j];
359 fa.Name[i] = 0; /* zero terminate */
361 fa.Attr = pde->Attributes;
363 if (pde->Attributes == 0xf)
365 return FAT_LONG_FILENAME; /* ignor long filename (slot) directory entries */
368 fa.StartCluster = pde->StartCluster;
369 fa.CurrCluster = fa.StartCluster;
371 fa.CurrSectorNumInCluster = 0;
376 /* Look in CWD for file with given name. */
377 int LoadFileWithName(char *filename)
386 ret = LoadFileInCWD(filenum);
387 if (ret == FAT_END_OF_DIR)
390 if ((ret != FAT_FILE_DELETED) && (ret != FAT_LONG_FILENAME))
392 if (strcasecmp(fa.Name, filename) == 0)
394 stat = 0; /* found file */
403 void PrintCurrFileInfo(void)
405 fprintf(stdout, "%s %d bytes (cluster %d, sector %d)", fa.Name, fa.Size,
406 fa.StartCluster, ConvertClusterToSector(fa.StartCluster));
407 if (fa.Attr & FAT_IS_DIR)
408 fputs(" <DIR>\n", stdout);
413 /* Get the FAT boot sector and root directory. */
416 int bootsector_startsector, stat=1, fatsize;
417 char dummy[FAT_HARDSECT];
421 if (da.Fat12 != NULL)
426 /* Assume no MBR and boot sector starts at first sector. */
427 bootsector_startsector = 0;
429 /* Read boot sector. */
430 /*fprintf( stdout, "start=%d", bootsector_startsector );*/
431 if (readsect(bootsector_startsector, 1, &bpb, sizeof(bpb)) != 0)
434 /* TODO: take care big-endian byte ordering in bpb. */
436 if (bpb.BytesPerSector != FAT_HARDSECT)
443 fprintf(stderr, "bytes/sectors=%d\n", bpb.BytesPerSector);
444 fprintf(stderr, "sectors/cluster=%d\n", bpb.SectorsPerCluster);
445 fprintf(stderr, "reserved sectors=%d\n", bpb.ReservedSectors);
446 fprintf(stderr, "sectors/FAT=%d\n", bpb.SectorsPerFat);
447 fprintf(stderr, "root entries=%d\n", bpb.RootEntries);
448 fprintf(stderr, "small sectors=%d\n", bpb.SmallSectors);
449 fprintf(stderr, "large sectors=%d\n", bpb.LargeSectors);
450 fprintf(stderr, "system id=%s\n", bpb.SystemID);
453 /* Calculate where the fat and root directory are. */
454 da.FatStartSector = bootsector_startsector + bpb.ReservedSectors;
455 da.RootDirNumSectors = ((bpb.RootEntries * 32) + (bpb.BytesPerSector - 1)) / bpb.BytesPerSector;
456 da.RootDirStartSector = da.FatStartSector + ((int16_t)bpb.Fats * (int16_t)bpb.SectorsPerFat);
457 da.DataStartSector = da.RootDirStartSector + da.RootDirNumSectors;
460 fatsize = bpb.SectorsPerFat * FAT_HARDSECT;
462 if (strcmp((char *)bpb.SystemID, "FAT12") == 0)
464 da.Fat12Size = fatsize;
465 if ((da.Fat12 = (uint8_t *)malloc(da.Fat12Size)) == NULL)
467 if (readsect(da.FatStartSector, bpb.SectorsPerFat, da.Fat12, da.Fat12Size) != 0)
469 da.FatSize = da.Fat12Size/1.5*2;
470 if ((da.Fat = (uint8_t *)malloc(da.FatSize)) == NULL)
472 ConvertFat12to16(da.Fat, da.Fat12, da.Fat12Size/1.5);
476 da.FatSize = fatsize;
477 if ((da.Fat16 = (uint8_t *)malloc(da.FatSize)) == NULL)
479 if (readsect(da.FatStartSector, bpb.SectorsPerFat, da.Fat16, da.FatSize) != 0)
481 if ((da.Fat = (uint8_t *)malloc(da.FatSize)) == NULL)
483 memcpy(da.Fat, da.Fat16, da.FatSize);
488 fprintf(stderr, "FAT start sector=%d\n", da.FatStartSector);
489 fprintf(stderr, "root start sector=%d\n", da.RootDirStartSector);
490 fprintf(stderr, "root number of sectors=%d\n", da.RootDirNumSectors);
491 fprintf(stderr, "data start sector=%d\n", da.DataStartSector);
494 /* Check for write protected disk. Try read/write to last sector in root directory. */
496 if (readsect(da.RootDirStartSector+da.RootDirNumSectors-1, 1, dummy, sizeof(dummy)) == 0)
497 if (writesect(da.RootDirStartSector+da.RootDirNumSectors-1, 1, dummy, sizeof(dummy)) == 0)
507 if (da.Fat12 != NULL)
509 if (da.Fat16 != NULL)
515 int FatFreeSpace(void)
517 return(FindFreeClusters() * bpb.SectorsPerCluster * FAT_HARDSECT);
520 int FatDiskAttributes( PHOTO_CARD_ATTRIBUTES * pa )
522 strncpy( pa->OEMID, bpb.OEMID, 8 );
523 pa->BytesPerSector = bpb.BytesPerSector;
524 pa->SectorsPerCluster = bpb.SectorsPerCluster;
525 pa->ReservedSectors = bpb.ReservedSectors;
526 pa->SectorsPerFat = bpb.SectorsPerFat;
527 pa->RootEntries = bpb.RootEntries;
528 strncpy( pa->SystemID, (char *)bpb.SystemID, 8 );
529 strncpy( pa->VolumeLabel, (char *)bpb.VolumeLabel, 11 );
530 pa->WriteProtect = da.WriteProtect;
535 /* Prints out all entries in the current directory to stdout. */
543 freespace = FatFreeSpace();
544 fprintf(stdout, "Free Space=%d bytes\n", freespace);
550 ret = LoadFileInCWD(filenum);
551 if (ret == FAT_END_OF_DIR)
553 fputs("<EOD>\n", stdout);
556 if ((ret != FAT_FILE_DELETED) && (ret != FAT_LONG_FILENAME))
563 /* Directory List "Iterator": Begin, Next... */
564 static int fatdir_filenum = 0;
566 int FatDirBegin(FILE_ATTRIBUTES *a)
569 return FatDirNext( a );
572 int FatDirNext(FILE_ATTRIBUTES *a)
575 ret = LoadFileInCWD( fatdir_filenum );
577 if( ret == FAT_END_OF_DIR )
580 if ((ret != FAT_FILE_DELETED) && (ret != FAT_LONG_FILENAME))
582 strcpy( a->Name, fa.Name );
584 if( fa.Attr == FAT_IS_DIR )
593 strcpy( a->Name, "" );
602 /* Dump FAT file to the output file (fd). */
603 int FatReadFile(char *name, int fd)
606 int cluster, sector, old;
607 int block = bpb.SectorsPerCluster * FAT_HARDSECT;
610 if (LoadFileWithName(name) != 0)
611 goto bugout; /* file not found */
613 cluster = fa.StartCluster;
614 sector = ConvertClusterToSector(cluster);
616 if ((buf = malloc(block)) == NULL)
621 for (i=0; i<fa.Size; i+=n)
623 if (readsect(sector, bpb.SectorsPerCluster, buf, block) != 0) /* read full cluster */
629 n = fa.Size-i > block ? block : fa.Size-i;
634 cluster = GetNextCluster(cluster);
635 if (cluster >= 0xfff7 || cluster == 0)
638 sector = ConvertClusterToSector(cluster);
648 /* Dump FAT file, given the "offset" in bytes and the "len" in bytes, to the output buffer. */
649 int FatReadFileExt(char *name, int offset, int len, void *outbuf)
652 int cluster, sector, old;
653 int block = bpb.SectorsPerCluster * FAT_HARDSECT; /* cluster size in bytes */
654 int i, n, total = 0, btotal = 0;
656 int b1 = offset / block; /* first cluster to read */
657 int b2 = (offset+len) / block; /* last cluster to read */
659 if (LoadFileWithName(name) != 0)
660 goto bugout; /* file not found */
662 cluster = fa.StartCluster;
663 sector = ConvertClusterToSector(cluster);
665 if ((buf = malloc(block)) == NULL)
670 for (i=0, bn=0; i<fa.Size; i+=n, bn++)
672 /* Determine size in bytes to write for this cluster */
673 n = fa.Size-i > block ? block : fa.Size-i;
675 /* Read/write data if it falls within "offset" and "len". */
678 if (readsect(sector, bpb.SectorsPerCluster, buf, block) != 0) /* read full cluster */
685 boff = offset-total; /* cluster overlaps "offset" */
687 boff = 0; /* cluster is past "offset" */
692 blen = (offset+len)-total-boff; /* cluster overlaps "len" */
694 blen = n-boff; /* cluster is within "len" */
696 memcpy(outbuf+btotal, buf+boff, blen);
700 break; /* done writing data */
705 cluster = GetNextCluster(cluster);
706 if (cluster >= 0xfff7 || cluster == 0)
709 sector = ConvertClusterToSector(cluster);
719 /* Make dir current working directory. */
720 int FatSetCWD(char *dir)
733 if (strcmp(cwd.Name, dir) == 0)
736 ret = LoadFileWithName(dir);
740 if (!(fa.Attr & FAT_IS_DIR))
743 strncpy(cwd.Name, fa.Name, sizeof(cwd.Name));
744 cwd.StartSector = ConvertClusterToSector(fa.StartCluster);
745 cwd.CurrSector = cwd.StartSector;
746 cwd.StartCluster = fa.StartCluster;
750 int FatDeleteFile(char *name)
752 uint8_t buf[FAT_HARDSECT];
753 uint16_t *pfat = (uint16_t *)da.Fat;
754 int cluster, next_cluster, filenum, stat=1;
756 if (LoadFileWithName(name) != 0)
759 cluster = fa.StartCluster;
761 /* Free all fat cluster entries for specified file. */
762 while ((cluster <= 0xFFF8) && (cluster != 0x0000))
764 next_cluster = *(pfat+cluster); /* get next cluster */
765 *(pfat+cluster) = 0; /* free current cluster */
766 cluster = next_cluster;
769 /* Remove directory entry for specified file. */
770 readsect(fa.DirSectorNum, 1, buf, sizeof(buf));
771 filenum = fa.DirEntryNum & 0x000F;
773 buf[filenum * 2] = FAT_FILE_DELETED;
774 if (writesect(fa.DirSectorNum, 1, buf, sizeof(buf)) != 0)
777 /* Write updated fat to disk. */
778 if (UpdateFat() != 0)