2 * Copyright © 2013 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 /* Partition information, sector numbers */
27 static uint8_t partition_type;
28 static uint32_t partition_start, partition_end;
30 #define SECTOR_SIZE 512
31 #define SECTOR_MASK (SECTOR_SIZE - 1)
32 #define SECTOR_SHIFT 9
34 #define DIRENT_SIZE 32
36 /* File system parameters */
37 static uint8_t sectors_per_cluster;
38 static uint32_t bytes_per_cluster;
39 static uint16_t reserved_sector_count;
40 static uint8_t number_fat;
41 static uint16_t root_entries;
42 static uint16_t sectors_per_fat;
43 static uint16_t number_cluster;
44 static uint32_t fat_start;
45 static uint32_t root_start;
46 static uint32_t data_start;
47 static uint16_t first_free_cluster;
50 * Deal with LSB FAT data structures
53 get_u32(uint8_t *base)
55 return ((uint32_t) base[0] |
56 ((uint32_t) base[1] << 8) |
57 ((uint32_t) base[2] << 16) |
58 ((uint32_t) base[3] << 24));
62 put_u32(uint8_t *base, uint32_t value)
66 base[2] = value >> 16;
67 base[3] = value >> 24;
71 get_u16(uint8_t *base)
73 return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
77 put_u16(uint8_t *base, uint16_t value)
84 ao_fat_cluster_valid(uint16_t cluster)
86 return (2 <= cluster && cluster < number_cluster);
89 /* Start using a sector */
91 ao_fat_sector_get(uint32_t sector)
93 sector += partition_start;
94 if (sector >= partition_end)
96 return ao_bufio_get(sector);
99 /* Finish using a sector, 'w' is 1 if modified */
100 #define ao_fat_sector_put(b,w) ao_bufio_put(b,w)
102 /* Start using a root directory entry */
104 ao_fat_root_get(uint16_t e)
106 uint32_t byte = e * DIRENT_SIZE;
107 uint32_t sector = byte >> SECTOR_SHIFT;
108 uint16_t offset = byte & SECTOR_MASK;
111 buf = ao_fat_sector_get(root_start + sector);
117 /* Finish using a root directory entry, 'w' is 1 if modified */
119 ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write)
121 uint16_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
122 uint8_t *buf = root - offset;
124 ao_fat_sector_put(buf, write);
127 /* Get the next cluster entry in the chain */
129 ao_fat_entry_read(uint16_t cluster)
136 if (!ao_fat_cluster_valid(cluster))
140 sector = cluster >> (SECTOR_SHIFT - 1);
141 offset = (cluster << 1) & SECTOR_MASK;
142 buf = ao_fat_sector_get(fat_start + sector);
145 ret = get_u16(buf + offset);
146 ao_fat_sector_put(buf, 0);
150 /* Replace the referenced cluster entry in the chain with
151 * 'new_value'. Return the previous value.
154 ao_fat_entry_replace(uint16_t cluster, uint16_t new_value)
162 if (!ao_fat_cluster_valid(cluster))
166 sector = cluster >> (SECTOR_SHIFT - 1);
167 offset = (cluster << 1) & SECTOR_MASK;
168 buf = ao_fat_sector_get(fat_start + sector);
171 ret = get_u16(buf + offset);
172 put_u16(buf + offset, new_value);
173 ao_fat_sector_put(buf, 1);
176 * Keep the other FATs in sync
178 for (other_fats = 1; other_fats < number_fat; other_fats++) {
179 buf = ao_fat_sector_get(fat_start + other_fats * sectors_per_fat + sector);
181 put_u16(buf + offset, new_value);
182 ao_fat_sector_put(buf, 1);
190 * Walk a cluster chain and mark
191 * all of them as free
194 ao_fat_free_cluster_chain(uint16_t cluster)
196 while (ao_fat_cluster_valid(cluster)) {
197 if (cluster < first_free_cluster)
198 first_free_cluster = cluster;
199 cluster = ao_fat_entry_replace(cluster, 0x0000);
204 * ao_fat_cluster_seek
206 * Walk a cluster chain the specified distance and return what we find
207 * there. If distance is zero, return the provided cluster.
210 ao_fat_cluster_seek(uint16_t cluster, uint16_t distance)
213 cluster = ao_fat_entry_read(cluster);
214 if (!ao_fat_cluster_valid(cluster))
224 * The basic file system operation -- map a file sector number to a
225 * partition sector number. Done by computing the cluster number and
226 * then walking that many clusters from the first cluster, then
227 * adding the sector offset from the start of the cluster. Returns
228 * 0xffffffff if we walk off the end of the file or the cluster chain
232 ao_fat_sector_seek(uint16_t cluster, uint32_t sector)
237 distance = sector / sectors_per_cluster;
238 offset = sector % sectors_per_cluster;
240 cluster = ao_fat_cluster_seek(cluster, distance);
242 if (!ao_fat_cluster_valid(cluster))
245 /* Compute the sector within the partition and return it */
246 return data_start + (uint32_t) (cluster-2) * sectors_per_cluster + offset;
250 * ao_fat_setup_partition
252 * Load the boot block and find the first partition
255 ao_fat_setup_partition(void)
259 uint32_t partition_size;
261 mbr = ao_bufio_get(0);
265 /* Check the signature */
266 if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
267 printf ("Invalid MBR signature %02x %02x\n",
268 mbr[0x1fe], mbr[0x1ff]);
269 ao_bufio_put(mbr, 0);
273 /* Check to see if it's actually a boot block, in which
274 * case it's presumably not a paritioned device
277 if (mbr[0] == 0xeb) {
279 partition_size = get_u16(mbr + 0x13);
280 if (partition_size == 0)
281 partition_size = get_u32(mbr + 0x20);
283 /* Just use the first partition */
284 partition = &mbr[0x1be];
286 partition_type = partition[4];
287 switch (partition_type) {
288 case 4: /* FAT16 up to 32M */
289 case 6: /* FAT16 over 32M */
291 case 0x0b: /* FAT32 up to 2047GB */
292 case 0x0c: /* FAT32 LBA */
295 printf ("Invalid partition type %02x\n", partition_type);
296 ao_bufio_put(mbr, 0);
300 partition_start = get_u32(partition+8);
301 partition_size = get_u32(partition+12);
302 if (partition_size == 0) {
303 printf ("Zero-sized partition\n");
304 ao_bufio_put(mbr, 0);
308 partition_end = partition_start + partition_size;
309 printf ("Partition type %02x start %08x end %08x\n",
310 partition_type, partition_start, partition_end);
311 ao_bufio_put(mbr, 0);
316 ao_fat_setup_fs(void)
318 uint8_t *boot = ao_fat_sector_get(0);
319 uint32_t data_sectors;
324 /* Check the signature */
325 if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
326 printf ("Invalid BOOT signature %02x %02x\n",
327 boot[0x1fe], boot[0x1ff]);
328 ao_fat_sector_put(boot, 0);
332 /* Check the sector size */
333 if (get_u16(boot + 0xb) != SECTOR_SIZE) {
334 printf ("Invalid sector size %d\n",
335 get_u16(boot + 0xb));
336 ao_fat_sector_put(boot, 0);
340 sectors_per_cluster = boot[0xd];
341 bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
342 reserved_sector_count = get_u16(boot+0xe);
343 number_fat = boot[0x10];
344 root_entries = get_u16(boot + 0x11);
345 sectors_per_fat = get_u16(boot+0x16);
347 fat_start = reserved_sector_count;;
348 root_start = fat_start + number_fat * sectors_per_fat;
349 data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
351 data_sectors = (partition_end - partition_start) - data_start;
353 number_cluster = data_sectors / sectors_per_cluster;
355 printf ("sectors per cluster %d\n", sectors_per_cluster);
356 printf ("reserved sectors %d\n", reserved_sector_count);
357 printf ("number of FATs %d\n", number_fat);
358 printf ("root entries %d\n", root_entries);
359 printf ("sectors per fat %d\n", sectors_per_fat);
361 printf ("fat start %d\n", fat_start);
362 printf ("root start %d\n", root_start);
363 printf ("data start %d\n", data_start);
365 ao_fat_sector_put(boot, 0);
373 if (!ao_fat_setup_partition())
375 check_bufio("partition setup");
376 if (!ao_fat_setup_fs())
378 check_bufio("fs setup");
383 * Basic file operations
386 static struct ao_fat_dirent ao_file_dirent;
387 static uint32_t ao_file_offset;
388 static uint8_t ao_file_opened;
391 ao_fat_offset_to_sector(uint32_t offset)
393 if (offset > ao_file_dirent.size)
395 return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> SECTOR_SHIFT);
401 * Set the size of the current file, truncating or extending
402 * the cluster chain as needed
405 ao_fat_set_size(uint32_t size)
407 uint16_t clear_cluster = 0;
409 uint16_t first_cluster;
411 first_cluster = ao_file_dirent.cluster;
412 if (size == ao_file_dirent.size)
413 return AO_FAT_SUCCESS;
415 clear_cluster = ao_file_dirent.cluster;
421 new_num = (size + bytes_per_cluster - 1) / bytes_per_cluster;
422 old_num = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster;
423 if (new_num < old_num) {
424 uint16_t last_cluster;
426 /* Go find the last cluster we want to preserve in the file */
427 last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1);
429 /* Rewrite that cluster entry with 0xffff to mark the end of the chain */
430 clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff);
431 } else if (new_num > old_num) {
434 uint16_t last_cluster;
435 uint16_t highest_allocated = 0;
438 last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1);
442 if (first_free_cluster < 2 || number_cluster <= first_free_cluster)
443 first_free_cluster = 2;
445 /* See if there are enough free clusters in the file system */
446 need = new_num - old_num;
448 #define loop_cluster for (free = first_free_cluster; need > 0;)
449 #define next_cluster \
450 if (++free == number_cluster) \
452 if (free == first_free_cluster) \
456 if (!ao_fat_entry_read(free))
460 /* Still need some, tell the user that we've failed */
462 return -AO_FAT_ENOSPC;
464 /* Now go allocate those clusters */
465 need = new_num - old_num;
467 if (!ao_fat_entry_read(free)) {
468 if (free > highest_allocated)
469 highest_allocated = free;
471 ao_fat_entry_replace(last_cluster, free);
473 first_cluster = free;
479 first_free_cluster = highest_allocated + 1;
480 if (first_free_cluster >= number_cluster)
481 first_free_cluster = 2;
483 /* Mark the new end of the chain */
484 ao_fat_entry_replace(last_cluster, 0xffff);
488 /* Deallocate clusters off the end of the file */
489 if (ao_fat_cluster_valid(clear_cluster))
490 ao_fat_free_cluster_chain(clear_cluster);
492 /* Update the directory entry */
493 dent = ao_fat_root_get(ao_file_dirent.entry);
496 put_u32(dent + 0x1c, size);
497 put_u16(dent + 0x1a, first_cluster);
498 ao_fat_root_put(dent, ao_file_dirent.entry, 1);
499 ao_file_dirent.size = size;
500 ao_file_dirent.cluster = first_cluster;
501 return AO_FAT_SUCCESS;
507 * Initialize a root directory entry
510 ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
512 memset(dent, '\0', 0x20);
513 memmove(dent, name, 11);
520 put_u16(dent + 0x0e, 0);
522 put_u16(dent + 0x10, 0);
524 put_u16(dent + 0x12, 0);
527 put_u16(dent + 0x16, 0);
529 put_u16(dent + 0x18, 0);
532 /* Low cluster bytes */
533 put_u16(dent + 0x1a, 0);
534 /* FAT32 high cluster bytes */
535 put_u16(dent + 0x14, 0);
538 put_u32(dent + 0x1c, 0);
543 ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent)
545 memcpy(dirent->name, dent + 0x00, 11);
546 dirent->attr = dent[0x0b];
547 dirent->size = get_u32(dent+0x1c);
548 dirent->cluster = get_u16(dent+0x1a);
549 dirent->entry = entry;
559 * Open an existing file.
562 ao_fat_open(char name[11], uint8_t mode)
565 struct ao_fat_dirent dirent;
568 return -AO_FAT_EMFILE;
570 while (ao_fat_readdir(&entry, &dirent)) {
571 if (!memcmp(name, dirent.name, 11)) {
572 if (AO_FAT_IS_DIR(dirent.attr))
573 return -AO_FAT_EISDIR;
574 if (!AO_FAT_IS_FILE(dirent.attr))
575 return -AO_FAT_EPERM;
576 if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
577 return -AO_FAT_EACCESS;
578 ao_file_dirent = dirent;
581 return AO_FAT_SUCCESS;
584 return -AO_FAT_ENOENT;
590 * Open and truncate an existing file or
594 ao_fat_creat(char name[11])
600 return -AO_FAT_EMFILE;
602 status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
605 case -AO_FAT_SUCCESS:
606 status = ao_fat_set_size(0);
609 for (entry = 0; entry < root_entries; entry++) {
610 uint8_t *dent = ao_fat_root_get(entry);
613 status = -AO_FAT_EIO;
614 ao_fat_root_put(dent, entry, 0);
618 if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
619 ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
620 ao_fat_dirent_init(dent, entry, &ao_file_dirent);
621 ao_fat_root_put(dent, entry, 1);
623 status = -AO_FAT_SUCCESS;
626 ao_fat_root_put(dent, entry, 0);
629 if (entry == root_entries)
630 status = -AO_FAT_ENOSPC;
638 * Close the currently open file
644 return -AO_FAT_EBADF;
646 memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
650 return AO_FAT_SUCCESS;
659 ao_fat_read(void *dst, int len)
661 uint8_t *dst_b = dst;
669 return -AO_FAT_EBADF;
671 if (ao_file_offset + len > ao_file_dirent.size)
672 len = ao_file_dirent.size - ao_file_offset;
678 offset = ao_file_offset & SECTOR_MASK;
679 if (offset + len < SECTOR_SIZE)
682 this_time = SECTOR_SIZE - offset;
684 sector = ao_fat_offset_to_sector(ao_file_offset);
685 if (sector == 0xffffffff)
687 buf = ao_fat_sector_get(sector);
692 memcpy(dst_b, buf + offset, this_time);
693 ao_fat_sector_put(buf, 0);
698 ao_file_offset += this_time;
706 * Write to the file, extended as necessary
709 ao_fat_write(void *src, int len)
711 uint8_t *src_b = src;
719 return -AO_FAT_EBADF;
721 if (ao_file_offset + len > ao_file_dirent.size) {
722 ret = ao_fat_set_size(ao_file_offset + len);
728 offset = ao_file_offset & SECTOR_MASK;
729 if (offset + len < SECTOR_SIZE)
732 this_time = SECTOR_SIZE - offset;
734 sector = ao_fat_offset_to_sector(ao_file_offset);
735 if (sector == 0xffffffff)
737 buf = ao_fat_sector_get(sector);
742 memcpy(buf + offset, src_b, this_time);
743 ao_fat_sector_put(buf, 1);
748 ao_file_offset += this_time;
756 * Set the position for the next I/O operation
757 * Note that this doesn't actually change the size
758 * of the file if the requested position is beyond
759 * the current file length, that would take a future
763 ao_fat_seek(int32_t pos, uint8_t whence)
766 return -AO_FAT_EBADF;
769 case AO_FAT_SEEK_SET:
770 ao_file_offset = pos;
772 case AO_FAT_SEEK_CUR:
773 ao_file_offset += pos;
775 case AO_FAT_SEEK_END:
776 ao_file_offset = ao_file_dirent.size + pos;
779 return ao_file_offset;
785 * Remove a file from the directory, marking
786 * all clusters as free
789 ao_fat_unlink(char name[11])
792 struct ao_fat_dirent dirent;
794 while (ao_fat_readdir(&entry, &dirent)) {
795 if (memcmp(name, dirent.name, 11) == 0) {
800 if (AO_FAT_IS_DIR(dirent.attr))
801 return -AO_FAT_EISDIR;
802 if (!AO_FAT_IS_FILE(dirent.attr))
803 return -AO_FAT_EPERM;
805 ao_fat_free_cluster_chain(dirent.cluster);
806 next = ao_fat_root_get(dirent.entry + 1);
807 if (next && next[0] != AO_FAT_DENT_END)
808 delete = AO_FAT_DENT_EMPTY;
810 delete = AO_FAT_DENT_END;
812 ao_fat_root_put(next, dirent.entry + 1, 0);
813 ent = ao_fat_root_get(dirent.entry);
815 memset(ent, '\0', DIRENT_SIZE);
817 ao_fat_root_put(ent, dirent.entry, 1);
820 return AO_FAT_SUCCESS;
823 return -AO_FAT_ENOENT;
827 ao_fat_rename(char old[11], char new[11])
833 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
837 if (*entry >= root_entries)
840 dent = ao_fat_root_get(*entry);
842 if (dent[0] == AO_FAT_DENT_END) {
843 ao_fat_root_put(dent, *entry, 0);
846 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
847 ao_fat_dirent_init(dent, *entry, dirent);
848 ao_fat_root_put(dent, *entry, 0);
852 ao_fat_root_put(dent, *entry, 0);
861 struct ao_fat_dirent dirent;
863 while (ao_fat_readdir(&entry, &dirent)) {
864 printf ("%-8.8s.%-3.3s %02x %04x %d\n",
880 static const struct ao_cmds ao_fat_cmds[] = {
881 { ao_fat_test, "F\0Test FAT" },
889 ao_cmd_register(&ao_fat_cmds[0]);