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.
26 * Basic file system types
29 typedef ao_fat_offset_t offset_t;
30 typedef ao_fat_sector_t sector_t;
31 typedef ao_fat_cluster_t cluster_t;
32 typedef ao_fat_dirent_t dirent_t;
33 typedef ao_fat_cluster_offset_t cluster_offset_t;
35 /* Partition information, sector numbers */
37 static uint8_t partition_type;
38 static sector_t partition_start, partition_end;
40 #define AO_FAT_BAD_CLUSTER 0xffffff7
41 #define AO_FAT_LAST_CLUSTER 0xfffffff
42 #define AO_FAT_IS_LAST_CLUSTER(c) (((c) & 0xffffff8) == 0xffffff8)
43 #define AO_FAT_IS_LAST_CLUSTER16(c) (((c) & 0xfff8) == 0xfff8)
46 #define SECTOR_SIZE 512
47 #define SECTOR_MASK (SECTOR_SIZE - 1)
48 #define SECTOR_SHIFT 9
50 #define DIRENT_SIZE 32
52 /* File system parameters */
53 static uint8_t sectors_per_cluster;
54 static uint32_t bytes_per_cluster;
55 static sector_t reserved_sector_count;
56 static uint8_t number_fat;
57 static dirent_t root_entries;
58 static sector_t sectors_per_fat;
59 static cluster_t number_cluster;
60 static sector_t fat_start;
61 static sector_t root_start;
62 static sector_t data_start;
63 static cluster_t next_free;
64 static uint8_t filesystem_full;
66 /* FAT32 extra data */
68 static uint8_t fsinfo_dirty;
69 static cluster_t root_cluster;
70 static sector_t fsinfo_sector;
71 static cluster_t free_count;
74 * Deal with LSB FAT data structures
77 get_u32(uint8_t *base)
79 return ((uint32_t) base[0] |
80 ((uint32_t) base[1] << 8) |
81 ((uint32_t) base[2] << 16) |
82 ((uint32_t) base[3] << 24));
86 put_u32(uint8_t *base, uint32_t value)
90 base[2] = value >> 16;
91 base[3] = value >> 24;
95 get_u16(uint8_t *base)
97 return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
101 put_u16(uint8_t *base, uint16_t value)
104 base[1] = value >> 8;
108 ao_fat_cluster_valid(cluster_t cluster)
110 return (2 <= cluster && cluster < number_cluster);
113 /* Start using a sector */
115 ao_fat_sector_get(sector_t sector)
117 sector += partition_start;
118 if (sector >= partition_end)
120 return ao_bufio_get(sector);
123 /* Finish using a sector, 'w' is 1 if modified */
124 #define ao_fat_sector_put(b,w) ao_bufio_put(b,w)
126 /* Get the next cluster entry in the chain */
128 ao_fat_entry_read(cluster_t cluster)
135 if (!ao_fat_cluster_valid(cluster))
142 sector = cluster >> (SECTOR_SHIFT);
143 offset = cluster & SECTOR_MASK;
144 buf = ao_fat_sector_get(fat_start + sector);
149 ret = get_u32(buf + offset);
152 ret = get_u16(buf + offset);
153 if (AO_FAT_IS_LAST_CLUSTER16(ret))
156 ao_fat_sector_put(buf, 0);
160 /* Replace the referenced cluster entry in the chain with
161 * 'new_value'. Return the previous value.
164 ao_fat_entry_replace(cluster_t cluster, cluster_t new_value)
167 cluster_offset_t offset;
173 if (!ao_fat_cluster_valid(cluster))
176 /* Convert from cluster index to byte index */
181 sector = cluster >> SECTOR_SHIFT;
182 offset = cluster & SECTOR_MASK;
184 new_value &= 0xfffffff;
185 for (fat = 0; fat < number_fat; fat++) {
186 buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
190 old_value = get_u32(buf + offset);
191 put_u32(buf + offset, new_value | (old_value & 0xf0000000));
193 ret = old_value & 0xfffffff;
195 /* Track the free count if it wasn't marked
196 * invalid when we mounted the file system
198 if (free_count != 0xffffffff) {
199 if (new_value && !ret) {
202 } else if (!new_value && ret) {
210 ret = get_u16(buf + offset);
211 if (AO_FAT_IS_LAST_CLUSTER16(ret))
214 put_u16(buf + offset, new_value);
216 ao_fat_sector_put(buf, 1);
223 * Walk a cluster chain and mark
224 * all of them as free
227 ao_fat_free_cluster_chain(cluster_t cluster)
229 while (ao_fat_cluster_valid(cluster)) {
230 if (cluster < next_free) {
234 cluster = ao_fat_entry_replace(cluster, 0x00000000);
239 * ao_fat_cluster_seek
241 * The basic file system operation -- map a file cluster index to a
242 * partition cluster number. Done by computing the cluster number and
243 * then walking that many clusters from the first cluster. Returns
244 * 0xffff if we walk off the end of the file or the cluster chain
248 ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)
251 cluster = ao_fat_entry_read(cluster);
252 if (!ao_fat_cluster_valid(cluster))
260 * ao_fat_cluster_set_size
262 * Set the number of clusters in the specified chain,
263 * freeing extra ones or alocating new ones as needed
265 * Returns AO_FAT_BAD_CLUSTER on allocation failure
269 ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
271 cluster_t clear_cluster = 0;
274 clear_cluster = first_cluster;
278 cluster_t last_cluster = 0;
279 cluster_t next_cluster;
281 /* Walk the cluster chain to the
282 * spot where it needs to change. That
283 * will either be the end of the chain (in case it needs to grow),
284 * or after the desired number of clusters, in which case it needs to shrink
286 next_cluster = first_cluster;
287 for (have = 0; have < size; have++) {
288 last_cluster = next_cluster;
289 next_cluster = ao_fat_entry_read(last_cluster);
290 if (!ao_fat_cluster_valid(next_cluster))
295 /* The file is large enough, truncate as needed */
296 if (ao_fat_cluster_valid(next_cluster)) {
297 /* Rewrite that cluster entry with 0xffff to mark the end of the chain */
298 clear_cluster = ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
301 /* The chain is already the right length, don't mess with it */
309 return AO_FAT_BAD_CLUSTER;
311 if (next_free < 2 || number_cluster <= next_free) {
316 /* See if there are enough free clusters in the file system */
319 #define loop_cluster for (free = next_free; need > 0;)
320 #define next_cluster \
321 if (++free == number_cluster) \
323 if (free == next_free) \
327 if (!ao_fat_entry_read(free))
331 /* Still need some, tell the user that we've failed */
334 return AO_FAT_BAD_CLUSTER;
337 /* Now go allocate those clusters and
338 * thread them onto the chain
342 if (!ao_fat_entry_read(free)) {
343 next_free = free + 1;
344 if (next_free >= number_cluster)
348 ao_fat_entry_replace(last_cluster, free);
350 first_cluster = free;
358 /* Mark the new end of the chain */
359 ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
363 /* Deallocate clusters off the end of the file */
364 if (ao_fat_cluster_valid(clear_cluster))
365 ao_fat_free_cluster_chain(clear_cluster);
366 return first_cluster;
369 /* Start using a root directory entry */
371 ao_fat_root_get(dirent_t e)
373 offset_t byte = e * DIRENT_SIZE;
374 sector_t sector = byte >> SECTOR_SHIFT;
375 cluster_offset_t offset = byte & SECTOR_MASK;
379 cluster_t cluster_distance = sector / sectors_per_cluster;
380 sector_t sector_index = sector % sectors_per_cluster;
381 cluster_t cluster = ao_fat_cluster_seek(root_cluster, cluster_distance);
383 if (ao_fat_cluster_valid(cluster))
384 sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
388 if (e >= root_entries)
390 sector = root_start + sector;
393 buf = ao_fat_sector_get(sector);
399 /* Finish using a root directory entry, 'w' is 1 if modified */
401 ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
403 cluster_offset_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
404 uint8_t *buf = root - offset;
406 ao_fat_sector_put(buf, write);
415 ao_fat_root_extend(dirent_t ents)
418 cluster_t cluster_size;
422 byte_size = ents * 0x20;
423 cluster_size = byte_size / bytes_per_cluster;
424 if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
430 * ao_fat_setup_partition
432 * Load the boot block and find the first partition
435 ao_fat_setup_partition(void)
439 uint32_t partition_size;
441 mbr = ao_bufio_get(0);
445 /* Check the signature */
446 if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
447 printf ("Invalid MBR signature %02x %02x\n",
448 mbr[0x1fe], mbr[0x1ff]);
449 ao_bufio_put(mbr, 0);
453 /* Check to see if it's actually a boot block, in which
454 * case it's presumably not a paritioned device
457 if (mbr[0] == 0xeb) {
459 partition_size = get_u16(mbr + 0x13);
460 if (partition_size == 0)
461 partition_size = get_u32(mbr + 0x20);
463 /* Just use the first partition */
464 partition = &mbr[0x1be];
466 partition_type = partition[4];
467 switch (partition_type) {
468 case 4: /* FAT16 up to 32M */
469 case 6: /* FAT16 over 32M */
471 case 0x0b: /* FAT32 up to 2047GB */
472 case 0x0c: /* FAT32 LBA */
475 printf ("Invalid partition type %02x\n", partition_type);
476 ao_bufio_put(mbr, 0);
480 partition_start = get_u32(partition+8);
481 partition_size = get_u32(partition+12);
482 if (partition_size == 0) {
483 printf ("Zero-sized partition\n");
484 ao_bufio_put(mbr, 0);
488 partition_end = partition_start + partition_size;
489 printf ("Partition type %02x start %08x end %08x\n",
490 partition_type, partition_start, partition_end);
491 ao_bufio_put(mbr, 0);
496 ao_fat_setup_fs(void)
498 uint8_t *boot = ao_fat_sector_get(0);
499 uint32_t data_sectors;
504 /* Check the signature */
505 if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
506 printf ("Invalid BOOT signature %02x %02x\n",
507 boot[0x1fe], boot[0x1ff]);
508 ao_fat_sector_put(boot, 0);
512 /* Check the sector size */
513 if (get_u16(boot + 0xb) != SECTOR_SIZE) {
514 printf ("Invalid sector size %d\n",
515 get_u16(boot + 0xb));
516 ao_fat_sector_put(boot, 0);
520 sectors_per_cluster = boot[0xd];
521 bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
522 reserved_sector_count = get_u16(boot+0xe);
523 number_fat = boot[0x10];
524 root_entries = get_u16(boot + 0x11);
525 sectors_per_fat = get_u16(boot+0x16);
527 if (sectors_per_fat == 0) {
529 sectors_per_fat = get_u32(boot+0x24);
530 root_cluster = get_u32(boot+0x2c);
531 fsinfo_sector = get_u16(boot + 0x30);
533 ao_fat_sector_put(boot, 0);
535 free_count = 0xffffffff;
537 if (fat32 && fsinfo_sector) {
538 uint8_t *fsinfo = ao_fat_sector_get(fsinfo_sector);
541 free_count = get_u32(fsinfo + 0x1e8);
542 next_free = get_u32(fsinfo + 0x1ec);
543 ao_fat_sector_put(fsinfo, 0);
547 fat_start = reserved_sector_count;;
548 root_start = fat_start + number_fat * sectors_per_fat;
549 data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
551 data_sectors = (partition_end - partition_start) - data_start;
553 number_cluster = data_sectors / sectors_per_cluster;
555 printf ("fat32: %d\n", fat32);
556 printf ("sectors per cluster %d\n", sectors_per_cluster);
557 printf ("reserved sectors %d\n", reserved_sector_count);
558 printf ("number of FATs %d\n", number_fat);
559 printf ("root entries %d\n", root_entries);
560 printf ("sectors per fat %d\n", sectors_per_fat);
562 printf ("fat start %d\n", fat_start);
563 printf ("root start %d\n", root_start);
564 printf ("data start %d\n", data_start);
570 * State for the current opened file
572 static struct ao_fat_dirent ao_file_dirent;
573 static uint32_t ao_file_offset;
574 static uint32_t ao_file_cluster_offset;
575 static cluster_t ao_file_cluster;
576 static uint8_t ao_file_opened;
577 static uint8_t ao_filesystem_available;
578 static uint8_t ao_filesystem_setup;
583 if (!ao_filesystem_setup) {
585 ao_filesystem_setup = 1;
588 /* Re-initialize all global state; this will help to allow the
589 * file system to get swapped someday
591 partition_type = partition_start = partition_end = 0;
592 sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
593 number_fat = root_entries = sectors_per_fat = 0;
594 number_cluster = fat_start = root_start = data_start = 0;
595 next_free = filesystem_full = 0;
596 fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
597 memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent));
599 ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0;
600 if (!ao_fat_setup_partition())
602 if (!ao_fat_setup_fs())
604 ao_filesystem_available = 1;
606 return ao_filesystem_available;
610 * Basic file operations
614 ao_fat_current_sector(void)
616 cluster_t cluster_offset;
617 uint32_t sector_offset;
618 uint16_t sector_index;
621 if (ao_file_offset > ao_file_dirent.size)
624 sector_offset = ao_file_offset >> SECTOR_SHIFT;
626 if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) {
627 ao_file_cluster = ao_file_dirent.cluster;
628 ao_file_cluster_offset = 0;
631 if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) {
632 cluster_t cluster_distance;
634 cluster_offset = sector_offset / sectors_per_cluster;
636 cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster;
638 cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance);
640 if (!ao_fat_cluster_valid(cluster))
642 ao_file_cluster = cluster;
643 ao_file_cluster_offset = cluster_offset * bytes_per_cluster;
646 sector_index = sector_offset % sectors_per_cluster;
647 return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index;
651 ao_fat_set_offset(uint32_t offset)
653 ao_file_offset = offset;
659 * Set the size of the current file, truncating or extending
660 * the cluster chain as needed
663 ao_fat_set_size(uint32_t size)
666 cluster_t first_cluster;
667 cluster_t have_clusters, need_clusters;
669 if (size == ao_file_dirent.size)
670 return AO_FAT_SUCCESS;
672 first_cluster = ao_file_dirent.cluster;
673 have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster;
674 need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
676 if (have_clusters != need_clusters) {
677 if (ao_file_cluster && size >= ao_file_cluster_offset) {
678 cluster_t offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster;
679 cluster_t extra_clusters = need_clusters - offset_clusters;
680 cluster_t next_cluster;
682 next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters);
683 if (next_cluster == AO_FAT_BAD_CLUSTER)
684 return -AO_FAT_ENOSPC;
686 first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters);
688 if (first_cluster == AO_FAT_BAD_CLUSTER)
689 return -AO_FAT_ENOSPC;
693 /* Update the directory entry */
694 dent = ao_fat_root_get(ao_file_dirent.entry);
697 put_u32(dent + 0x1c, size);
698 put_u16(dent + 0x1a, first_cluster);
700 put_u16(dent + 0x14, first_cluster >> 16);
701 ao_fat_root_put(dent, ao_file_dirent.entry, 1);
703 ao_file_dirent.size = size;
704 ao_file_dirent.cluster = first_cluster;
705 return AO_FAT_SUCCESS;
711 * Initialize a root directory entry
714 ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
716 memset(dent, '\0', 0x20);
717 memmove(dent, name, 11);
724 put_u16(dent + 0x0e, 0);
726 put_u16(dent + 0x10, 0);
728 put_u16(dent + 0x12, 0);
731 put_u16(dent + 0x16, 0);
733 put_u16(dent + 0x18, 0);
736 /* Low cluster bytes */
737 put_u16(dent + 0x1a, 0);
738 /* FAT32 high cluster bytes */
739 put_u16(dent + 0x14, 0);
742 put_u32(dent + 0x1c, 0);
747 ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent)
749 memcpy(dirent->name, dent + 0x00, 11);
750 dirent->attr = dent[0x0b];
751 dirent->size = get_u32(dent+0x1c);
752 dirent->cluster = get_u16(dent+0x1a);
754 dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
755 dirent->entry = entry;
759 * ao_fat_flush_fsinfo
761 * Write out any fsinfo changes to disk
765 ao_fat_flush_fsinfo(void)
778 fsinfo = ao_fat_sector_get(fsinfo_sector);
780 put_u32(fsinfo + 0x1e8, free_count);
781 put_u32(fsinfo + 0x1ec, next_free);
782 ao_fat_sector_put(fsinfo, 1);
793 * Flush any pending I/O to storage
801 ao_fat_flush_fsinfo();
808 * Returns TRUE if the filesystem cannot take
817 return filesystem_full;
823 * Open an existing file.
826 ao_fat_open(char name[11], uint8_t mode)
829 struct ao_fat_dirent dirent;
835 return -AO_FAT_EMFILE;
837 while (ao_fat_readdir(&entry, &dirent)) {
838 if (!memcmp(name, dirent.name, 11)) {
839 if (AO_FAT_IS_DIR(dirent.attr))
840 return -AO_FAT_EISDIR;
841 if (!AO_FAT_IS_FILE(dirent.attr))
842 return -AO_FAT_EPERM;
843 if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
844 return -AO_FAT_EACCESS;
845 ao_file_dirent = dirent;
846 ao_fat_set_offset(0);
848 return AO_FAT_SUCCESS;
851 return -AO_FAT_ENOENT;
857 * Open and truncate an existing file or
861 ao_fat_creat(char name[11])
871 return -AO_FAT_EMFILE;
873 status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
876 case -AO_FAT_SUCCESS:
877 status = ao_fat_set_size(0);
882 dent = ao_fat_root_get(entry);
885 if (ao_fat_root_extend(entry))
887 status = -AO_FAT_ENOSPC;
891 if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
892 ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
893 ao_fat_dirent_init(dent, entry, &ao_file_dirent);
894 ao_fat_root_put(dent, entry, 1);
896 ao_fat_set_offset(0);
897 status = -AO_FAT_SUCCESS;
900 ao_fat_root_put(dent, entry, 0);
911 * Close the currently open file
917 return -AO_FAT_EBADF;
919 memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
925 return AO_FAT_SUCCESS;
934 ao_fat_read(void *dst, int len)
936 uint8_t *dst_b = dst;
944 return -AO_FAT_EBADF;
946 if (ao_file_offset + len > ao_file_dirent.size)
947 len = ao_file_dirent.size - ao_file_offset;
953 offset = ao_file_offset & SECTOR_MASK;
954 if (offset + len < SECTOR_SIZE)
957 this_time = SECTOR_SIZE - offset;
959 sector = ao_fat_current_sector();
960 if (sector == 0xffffffff)
962 buf = ao_fat_sector_get(sector);
967 memcpy(dst_b, buf + offset, this_time);
968 ao_fat_sector_put(buf, 0);
973 ao_fat_set_offset(ao_file_offset + this_time);
981 * Write to the file, extended as necessary
984 ao_fat_write(void *src, int len)
986 uint8_t *src_b = src;
994 return -AO_FAT_EBADF;
996 if (ao_file_offset + len > ao_file_dirent.size) {
997 ret = ao_fat_set_size(ao_file_offset + len);
1003 offset = ao_file_offset & SECTOR_MASK;
1004 if (offset + len < SECTOR_SIZE)
1007 this_time = SECTOR_SIZE - offset;
1009 sector = ao_fat_current_sector();
1010 if (sector == 0xffffffff)
1012 buf = ao_fat_sector_get(sector);
1017 memcpy(buf + offset, src_b, this_time);
1018 ao_fat_sector_put(buf, 1);
1023 ao_fat_set_offset(ao_file_offset + this_time);
1031 * Set the position for the next I/O operation
1032 * Note that this doesn't actually change the size
1033 * of the file if the requested position is beyond
1034 * the current file length, that would take a future
1038 ao_fat_seek(int32_t pos, uint8_t whence)
1040 uint32_t new_offset = ao_file_offset;
1042 if (!ao_file_opened)
1043 return -AO_FAT_EBADF;
1046 case AO_FAT_SEEK_SET:
1049 case AO_FAT_SEEK_CUR:
1052 case AO_FAT_SEEK_END:
1053 new_offset = ao_file_dirent.size + pos;
1056 ao_fat_set_offset(new_offset);
1057 return ao_file_offset;
1063 * Remove a file from the directory, marking
1064 * all clusters as free
1067 ao_fat_unlink(char name[11])
1070 struct ao_fat_dirent dirent;
1072 if (!ao_fat_setup())
1074 while (ao_fat_readdir(&entry, &dirent)) {
1075 if (memcmp(name, dirent.name, 11) == 0) {
1080 if (AO_FAT_IS_DIR(dirent.attr))
1081 return -AO_FAT_EISDIR;
1082 if (!AO_FAT_IS_FILE(dirent.attr))
1083 return -AO_FAT_EPERM;
1085 ao_fat_free_cluster_chain(dirent.cluster);
1086 next = ao_fat_root_get(dirent.entry + 1);
1087 if (next && next[0] != AO_FAT_DENT_END)
1088 delete = AO_FAT_DENT_EMPTY;
1090 delete = AO_FAT_DENT_END;
1092 ao_fat_root_put(next, dirent.entry + 1, 0);
1093 ent = ao_fat_root_get(dirent.entry);
1095 memset(ent, '\0', DIRENT_SIZE);
1097 ao_fat_root_put(ent, dirent.entry, 1);
1100 return AO_FAT_SUCCESS;
1103 return -AO_FAT_ENOENT;
1107 ao_fat_rename(char old[11], char new[11])
1113 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1117 if (!ao_fat_setup())
1120 dent = ao_fat_root_get(*entry);
1124 if (dent[0] == AO_FAT_DENT_END) {
1125 ao_fat_root_put(dent, *entry, 0);
1128 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
1129 ao_fat_dirent_init(dent, *entry, dirent);
1130 ao_fat_root_put(dent, *entry, 0);
1134 ao_fat_root_put(dent, *entry, 0);
1143 struct ao_fat_dirent dirent;
1145 while (ao_fat_readdir(&entry, &dirent)) {
1146 printf ("%-8.8s.%-3.3s %02x %04x %d\n",
1155 static const struct ao_cmds ao_fat_cmds[] = {
1156 { ao_fat_list, "F\0List FAT" },
1164 ao_cmd_register(&ao_fat_cmds[0]);