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; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 /* Include FAT commands */
28 #define FAT_COMMANDS 1
31 /* Spew FAT tracing */
39 #define DBG(...) printf(__VA_ARGS__)
45 * Basic file system types
48 typedef ao_fat_offset_t offset_t;
49 typedef ao_fat_sector_t sector_t;
50 typedef ao_fat_cluster_t cluster_t;
51 typedef ao_fat_dirent_t dirent_t;
52 typedef ao_fat_cluster_offset_t cluster_offset_t;
55 static uint8_t ao_fat_mutex;
57 /* Partition information, sector numbers */
59 static uint8_t partition_type;
60 static sector_t partition_start, partition_end;
62 #define AO_FAT_BAD_CLUSTER 0xffffff7
63 #define AO_FAT_LAST_CLUSTER 0xfffffff
64 #define AO_FAT_IS_LAST_CLUSTER(c) (((c) & 0xffffff8) == 0xffffff8)
65 #define AO_FAT_IS_LAST_CLUSTER16(c) (((c) & 0xfff8) == 0xfff8)
68 #define SECTOR_SIZE 512
69 #define SECTOR_MASK (SECTOR_SIZE - 1)
70 #define SECTOR_SHIFT 9
72 #define DIRENT_SIZE 32
74 /* File system parameters */
75 static uint8_t sectors_per_cluster;
76 static uint32_t bytes_per_cluster;
77 static sector_t reserved_sector_count;
78 static uint8_t number_fat;
79 static dirent_t root_entries;
80 static sector_t sectors_per_fat;
81 static cluster_t number_cluster;
82 static sector_t fat_start;
83 static sector_t root_start;
84 static sector_t data_start;
85 static cluster_t next_free;
86 static uint8_t filesystem_full;
88 /* FAT32 extra data */
90 static uint8_t fsinfo_dirty;
91 static cluster_t root_cluster;
92 static sector_t fsinfo_sector;
93 static cluster_t free_count;
96 * Deal with LSB FAT data structures
99 get_u32(uint8_t *base)
101 return ((uint32_t) base[0] |
102 ((uint32_t) base[1] << 8) |
103 ((uint32_t) base[2] << 16) |
104 ((uint32_t) base[3] << 24));
108 put_u32(uint8_t *base, uint32_t value)
111 base[1] = value >> 8;
112 base[2] = value >> 16;
113 base[3] = value >> 24;
117 get_u16(uint8_t *base)
119 return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
123 put_u16(uint8_t *base, uint16_t value)
126 base[1] = value >> 8;
130 _ao_fat_cluster_valid(cluster_t cluster)
132 return (2 <= cluster && cluster < number_cluster);
135 /* Start using a sector */
137 _ao_fat_sector_get(sector_t sector)
139 sector += partition_start;
140 if (sector >= partition_end)
142 return ao_bufio_get(sector);
145 /* Finish using a sector, 'w' is 1 if modified */
146 #define _ao_fat_sector_put(b,w) ao_bufio_put(b,w)
148 /* Get the next cluster entry in the chain */
150 _ao_fat_entry_read(cluster_t cluster)
157 if (!_ao_fat_cluster_valid(cluster))
164 sector = cluster >> (SECTOR_SHIFT);
165 offset = cluster & SECTOR_MASK;
166 buf = _ao_fat_sector_get(fat_start + sector);
171 ret = get_u32(buf + offset);
174 ret = get_u16(buf + offset);
175 if (AO_FAT_IS_LAST_CLUSTER16(ret))
178 _ao_fat_sector_put(buf, 0);
182 /* Replace the referenced cluster entry in the chain with
183 * 'new_value'. Return the previous value.
186 _ao_fat_entry_replace(cluster_t cluster, cluster_t new_value)
189 cluster_offset_t offset;
195 if (!_ao_fat_cluster_valid(cluster))
198 /* Convert from cluster index to byte index */
203 sector = cluster >> SECTOR_SHIFT;
204 offset = cluster & SECTOR_MASK;
206 new_value &= 0xfffffff;
207 for (fat = 0; fat < number_fat; fat++) {
208 buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
212 old_value = get_u32(buf + offset);
213 put_u32(buf + offset, new_value | (old_value & 0xf0000000));
215 ret = old_value & 0xfffffff;
217 /* Track the free count if it wasn't marked
218 * invalid when we mounted the file system
220 if (free_count != 0xffffffff) {
221 if (new_value && !ret) {
224 } else if (!new_value && ret) {
232 ret = get_u16(buf + offset);
233 if (AO_FAT_IS_LAST_CLUSTER16(ret))
236 put_u16(buf + offset, new_value);
238 _ao_fat_sector_put(buf, 1);
245 * Walk a cluster chain and mark
246 * all of them as free
249 _ao_fat_free_cluster_chain(cluster_t cluster)
251 while (_ao_fat_cluster_valid(cluster)) {
252 if (cluster < next_free) {
256 cluster = _ao_fat_entry_replace(cluster, 0x00000000);
261 * _ao_fat_cluster_seek
263 * The basic file system operation -- map a file cluster index to a
264 * partition cluster number. Done by computing the cluster number and
265 * then walking that many clusters from the first cluster. Returns
266 * 0xffff if we walk off the end of the file or the cluster chain
270 _ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)
273 cluster = _ao_fat_entry_read(cluster);
274 if (!_ao_fat_cluster_valid(cluster))
282 * _ao_fat_cluster_set_size
284 * Set the number of clusters in the specified chain,
285 * freeing extra ones or alocating new ones as needed
287 * Returns AO_FAT_BAD_CLUSTER on allocation failure
291 _ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
294 cluster_t last_cluster;
295 cluster_t next_cluster;
297 /* Walk the cluster chain to the
298 * spot where it needs to change. That
299 * will either be the end of the chain (in case it needs to grow),
300 * or after the desired number of clusters, in which case it needs to shrink
302 next_cluster = first_cluster;
305 for (have = 0; have < size; have++) {
306 DBG(" %08x", next_cluster);
307 if (!_ao_fat_cluster_valid(next_cluster))
309 last_cluster = next_cluster;
310 next_cluster = _ao_fat_entry_read(next_cluster);
314 /* At this point, last_cluster points to the last valid
315 * cluster in the file, if any. That's the spot in the FAT
316 * that needs to be rewritten, either to truncate the file by
317 * writing an END marker, or to extend the file by writing
318 * more clusters. next_cluster will contain the value of the
319 * FAT at last_cluster.
321 * If this is at the head of the cluster chain, then
322 * last_cluster will be zero and next_cluster will
323 * be the first cluster in the chain.
326 /* The file is large enough, truncate as needed */
327 if (_ao_fat_cluster_valid(next_cluster)) {
328 DBG("truncate between %08x and %08x\n", last_cluster, next_cluster);
331 * Otherwise, rewrite the last cluster
332 * in the chain with a LAST marker
334 (void) _ao_fat_entry_replace(last_cluster,
335 AO_FAT_LAST_CLUSTER);
338 * If the file is getting erased, then
339 * rewrite the directory entry cluster
344 /* Clear the remaining clusters in the chain */
345 _ao_fat_free_cluster_chain(next_cluster);
347 /* The file system is no longer full (if it was) */
350 DBG("unchanged FAT chain\n");
351 /* The chain is already the right length, don't mess with it */
359 return AO_FAT_BAD_CLUSTER;
361 /* Set next_free if it has wrapped or wasn't set before */
362 if (next_free < 2 || number_cluster <= next_free) {
367 /* See if there are enough free clusters in the file system */
370 #define loop_cluster for (free = next_free; need > 0;)
371 #define next_cluster \
372 if (++free == number_cluster) \
374 if (free == next_free) \
378 if (!_ao_fat_entry_read(free))
383 /* Still need some, tell the user that we've failed */
386 return AO_FAT_BAD_CLUSTER;
389 /* Now go allocate those clusters and
390 * thread them onto the chain
394 if (_ao_fat_entry_read(free) == 0) {
395 next_free = free + 1;
396 if (next_free >= number_cluster)
399 DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free);
401 _ao_fat_entry_replace(last_cluster, free);
403 first_cluster = free;
411 DBG("\tlast cluster %08x\n", last_cluster);
412 /* Mark the new end of the chain */
413 _ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
416 DBG("\tfirst cluster %08x\n", first_cluster);
417 return first_cluster;
420 /* Start using a root directory entry */
422 _ao_fat_root_get(dirent_t e)
424 offset_t byte = e * DIRENT_SIZE;
425 sector_t sector = byte >> SECTOR_SHIFT;
426 cluster_offset_t offset = byte & SECTOR_MASK;
430 cluster_t cluster_distance = sector / sectors_per_cluster;
431 sector_t sector_index = sector % sectors_per_cluster;
432 cluster_t cluster = _ao_fat_cluster_seek(root_cluster, cluster_distance);
434 if (_ao_fat_cluster_valid(cluster))
435 sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
439 if (e >= root_entries)
441 sector = root_start + sector;
444 buf = _ao_fat_sector_get(sector);
450 /* Finish using a root directory entry, 'w' is 1 if modified */
452 _ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
454 cluster_offset_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
455 uint8_t *buf = root - offset;
457 _ao_fat_sector_put(buf, write);
461 * _ao_fat_root_extend
463 * On FAT32, make the root directory at least 'ents' entries long
466 _ao_fat_root_extend(dirent_t ents)
469 cluster_t cluster_size;
473 byte_size = (ents + 1) * 0x20;
474 cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster;
475 if (_ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
481 * _ao_fat_setup_partition
483 * Load the boot block and find the first partition
486 _ao_fat_setup_partition(void)
490 uint32_t partition_size;
492 mbr = ao_bufio_get(0);
494 return AO_FAT_FILESYSTEM_MBR_READ_FAILURE;
496 /* Check the signature */
497 if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
498 DBG ("Invalid MBR signature %02x %02x\n",
499 mbr[0x1fe], mbr[0x1ff]);
500 ao_bufio_put(mbr, 0);
501 return AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE;
504 /* Check to see if it's actually a boot block, in which
505 * case it's presumably not a paritioned device
508 if (mbr[0] == 0xeb) {
510 partition_size = get_u16(mbr + 0x13);
511 if (partition_size == 0)
512 partition_size = get_u32(mbr + 0x20);
514 /* Just use the first partition */
515 partition = &mbr[0x1be];
517 partition_type = partition[4];
518 switch (partition_type) {
519 case 4: /* FAT16 up to 32M */
520 case 6: /* FAT16 over 32M */
522 case 0x0b: /* FAT32 up to 2047GB */
523 case 0x0c: /* FAT32 LBA */
526 DBG ("Invalid partition type %02x\n", partition_type);
527 ao_bufio_put(mbr, 0);
528 return AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE;
531 partition_start = get_u32(partition+8);
532 partition_size = get_u32(partition+12);
533 if (partition_size == 0) {
534 DBG ("Zero-sized partition\n");
535 ao_bufio_put(mbr, 0);
536 return AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION;
539 partition_end = partition_start + partition_size;
540 ao_bufio_put(mbr, 0);
541 return AO_FAT_FILESYSTEM_SUCCESS;
545 _ao_fat_setup_fs(void)
547 uint8_t *boot = _ao_fat_sector_get(0);
548 uint32_t data_sectors;
551 return AO_FAT_FILESYSTEM_BOOT_READ_FAILURE;
553 /* Check the signature */
554 if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
555 DBG ("Invalid BOOT signature %02x %02x\n",
556 boot[0x1fe], boot[0x1ff]);
557 _ao_fat_sector_put(boot, 0);
558 return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE;
561 /* Check the sector size */
562 if (get_u16(boot + 0xb) != SECTOR_SIZE) {
563 DBG ("Invalid sector size %d\n",
564 get_u16(boot + 0xb));
565 _ao_fat_sector_put(boot, 0);
566 return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE;
569 sectors_per_cluster = boot[0xd];
570 bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
571 reserved_sector_count = get_u16(boot+0xe);
572 number_fat = boot[0x10];
573 root_entries = get_u16(boot + 0x11);
574 sectors_per_fat = get_u16(boot+0x16);
576 if (sectors_per_fat == 0) {
578 sectors_per_fat = get_u32(boot+0x24);
579 root_cluster = get_u32(boot+0x2c);
580 fsinfo_sector = get_u16(boot + 0x30);
582 _ao_fat_sector_put(boot, 0);
584 free_count = 0xffffffff;
586 if (fat32 && fsinfo_sector) {
587 uint8_t *fsinfo = _ao_fat_sector_get(fsinfo_sector);
590 free_count = get_u32(fsinfo + 0x1e8);
591 next_free = get_u32(fsinfo + 0x1ec);
592 _ao_fat_sector_put(fsinfo, 0);
596 fat_start = reserved_sector_count;;
597 root_start = fat_start + number_fat * sectors_per_fat;
598 data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
600 data_sectors = (partition_end - partition_start) - data_start;
602 number_cluster = data_sectors / sectors_per_cluster;
604 return AO_FAT_FILESYSTEM_SUCCESS;
608 * State for an open file
612 struct ao_fat_dirent *dirent;
614 offset_t cluster_offset;
619 #define AO_FAT_NFILE 8
621 static struct ao_fat_dirent ao_file_dirent[AO_FAT_NFILE];
623 static struct ao_fat_dirent *
624 _ao_fat_file_dirent_alloc(struct ao_fat_dirent *want)
627 struct ao_fat_dirent *free = NULL, *dirent;
629 for (d = 0; d < AO_FAT_NFILE; d++) {
631 dirent = &ao_file_dirent[d];
632 /* See if there's another user of this file already */
633 if (want && dirent->name[0] != 0) {
634 if (dirent->entry == want->entry)
649 static struct ao_file ao_file_table[AO_FAT_NFILE];
652 _ao_fat_fd_alloc(struct ao_fat_dirent *dirent)
656 for (fd = 0; fd < AO_FAT_NFILE; fd++)
657 if (!ao_file_table[fd].busy) {
658 ao_file_table[fd].dirent = _ao_fat_file_dirent_alloc(dirent);
659 ao_file_table[fd].busy = 1;
660 ao_file_table[fd].offset = 0;
661 ao_file_table[fd].cluster_offset = 0;
662 ao_file_table[fd].cluster = ao_file_table[fd].dirent->cluster;
666 return -AO_FAT_EMFILE;
670 _ao_fat_fd_free(int8_t fd)
672 struct ao_file *file = &ao_file_table[fd];
673 struct ao_fat_dirent *dirent = file->dirent;
674 memset(&ao_file_table[fd], '\0', sizeof (struct ao_file));
676 /* Check and see if another ao_file references the same dirent */
677 for (fd = 0; fd < AO_FAT_NFILE; fd++)
678 if (ao_file_table[fd].dirent == dirent)
680 memset(dirent, '\0', sizeof (struct ao_fat_dirent));
683 static struct ao_file *
684 _ao_fat_fd_to_file(int8_t fd)
686 struct ao_file *file;
687 if (fd < 0 || AO_FAT_NFILE <= fd)
690 file = &ao_file_table[fd];
696 static uint8_t ao_filesystem_setup;
697 static uint8_t ao_filesystem_status;
702 if (!ao_filesystem_setup) {
704 ao_filesystem_setup = 1;
707 /* Re-initialize all global state; this will help to allow the
708 * file system to get swapped someday
710 partition_type = partition_start = partition_end = 0;
711 sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
712 number_fat = root_entries = sectors_per_fat = 0;
713 number_cluster = fat_start = root_start = data_start = 0;
714 next_free = filesystem_full = 0;
715 fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
717 /* Reset open file table */
718 memset(&ao_file_table, '\0', sizeof (ao_file_table));
720 ao_filesystem_status = _ao_fat_setup_partition();
721 if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
722 return ao_filesystem_status;
723 ao_filesystem_status = _ao_fat_setup_fs();
724 if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
725 return ao_filesystem_status;
727 return ao_filesystem_status;
733 ao_filesystem_setup = 0;
737 * Basic file operations
741 _ao_fat_current_sector(struct ao_file *file)
743 cluster_t cluster_offset;
744 uint32_t sector_offset;
745 uint16_t sector_index;
748 DBG("current sector offset %d size %d\n",
749 file->offset, file->dirent->size);
751 if (file->offset > (offset_t) file->dirent->size) {
752 printf ("file offset %d larger than size %d\n",
753 file->offset, file->dirent->size);
757 sector_offset = file->offset >> SECTOR_SHIFT;
759 if (!file->cluster || file->offset < file->cluster_offset) {
760 file->cluster = file->dirent->cluster;
761 file->cluster_offset = 0;
762 DBG("\treset to start of file %08x\n", file->cluster);
765 if ((offset_t) (file->cluster_offset + bytes_per_cluster) <= file->offset) {
766 cluster_t cluster_distance;
768 cluster_offset = sector_offset / sectors_per_cluster;
770 cluster_distance = cluster_offset - file->cluster_offset / bytes_per_cluster;
772 DBG("\tseek forward %d clusters\n", cluster_distance);
773 cluster = _ao_fat_cluster_seek(file->cluster, cluster_distance);
775 if (!_ao_fat_cluster_valid(cluster)) {
776 printf ("invalid cluster %08x\n", cluster);
779 file->cluster = cluster;
780 file->cluster_offset = cluster_offset * bytes_per_cluster;
783 sector_index = sector_offset % sectors_per_cluster;
784 DBG("current cluster %08x sector_index %d sector %d\n",
785 file->cluster, sector_index,
786 data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index);
787 return data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index;
791 * _ao_fat_invaldate_cluster_offset
793 * When the file size gets shrunk, invalidate
794 * any file structures referencing clusters beyond that point
798 _ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent)
801 struct ao_file *file;
803 for (fd = 0; fd < AO_FAT_NFILE; fd++) {
804 file = &ao_file_table[fd];
807 if (file->dirent == dirent) {
808 if (file->cluster_offset >= (offset_t) dirent->size) {
809 file->cluster_offset = 0;
810 file->cluster = dirent->cluster;
820 * Set the size of the current file, truncating or extending
821 * the cluster chain as needed
824 _ao_fat_set_size(struct ao_file *file, uint32_t size)
827 cluster_t first_cluster;
828 cluster_t have_clusters, need_clusters;
830 DBG ("Set size %d\n", size);
831 if (size == file->dirent->size) {
832 DBG("\tsize match\n");
833 return AO_FAT_SUCCESS;
836 first_cluster = file->dirent->cluster;
837 have_clusters = (file->dirent->size + bytes_per_cluster - 1) / bytes_per_cluster;
838 need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
840 DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters);
841 if (have_clusters != need_clusters) {
842 if (file->cluster && (offset_t) size > file->cluster_offset) {
843 cluster_t offset_clusters = (file->cluster_offset + bytes_per_cluster) / bytes_per_cluster;
844 cluster_t extra_clusters = need_clusters - offset_clusters;
845 cluster_t next_cluster;
847 DBG ("\tset size relative offset_clusters %d extra_clusters %d\n",
848 offset_clusters, extra_clusters);
850 /* Need one more to account for file->cluster, which we already have */
851 next_cluster = _ao_fat_cluster_set_size(file->cluster, extra_clusters + 1);
852 if (next_cluster == AO_FAT_BAD_CLUSTER)
853 return -AO_FAT_ENOSPC;
855 DBG ("\tset size absolute need_clusters %d\n", need_clusters);
856 first_cluster = _ao_fat_cluster_set_size(first_cluster, need_clusters);
858 if (first_cluster == AO_FAT_BAD_CLUSTER)
859 return -AO_FAT_ENOSPC;
863 DBG ("\tupdate directory size\n");
864 /* Update the directory entry */
865 dent = _ao_fat_root_get(file->dirent->entry);
867 printf ("dent update failed\n");
870 put_u32(dent + 0x1c, size);
871 put_u16(dent + 0x1a, first_cluster);
873 put_u16(dent + 0x14, first_cluster >> 16);
874 _ao_fat_root_put(dent, file->dirent->entry, 1);
876 file->dirent->size = size;
877 file->dirent->cluster = first_cluster;
878 if (have_clusters > need_clusters)
879 _ao_fat_invalidate_cluster_offset(file->dirent);
880 DBG ("set size done\n");
881 return AO_FAT_SUCCESS;
887 * Initialize a root directory entry
890 _ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
893 memset(dent, '\0', 0x20);
894 memmove(dent, name, 11);
901 put_u16(dent + 0x0e, 0);
903 put_u16(dent + 0x10, 0);
905 put_u16(dent + 0x12, 0);
908 put_u16(dent + 0x16, 0);
910 put_u16(dent + 0x18, 0);
913 /* Low cluster bytes */
914 put_u16(dent + 0x1a, 0);
915 /* FAT32 high cluster bytes */
916 put_u16(dent + 0x14, 0);
919 put_u32(dent + 0x1c, 0);
924 _ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry)
926 memcpy(dirent->name, dent + 0x00, 11);
927 dirent->attr = dent[0x0b];
928 dirent->size = get_u32(dent+0x1c);
929 dirent->cluster = get_u16(dent+0x1a);
931 dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
932 dirent->entry = entry;
936 * _ao_fat_flush_fsinfo
938 * Write out any fsinfo changes to disk
942 _ao_fat_flush_fsinfo(void)
955 fsinfo = _ao_fat_sector_get(fsinfo_sector);
957 put_u32(fsinfo + 0x1e8, free_count);
958 put_u32(fsinfo + 0x1ec, next_free);
959 _ao_fat_sector_put(fsinfo, 1);
970 * Flush any pending I/O to storage
976 if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
978 _ao_fat_flush_fsinfo();
985 ao_mutex_get(&ao_fat_mutex);
987 ao_mutex_put(&ao_fat_mutex);
993 * Returns TRUE if the filesystem cannot take
1000 ao_mutex_get(&ao_fat_mutex);
1001 if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
1002 ao_mutex_put(&ao_fat_mutex);
1005 ao_mutex_put(&ao_fat_mutex);
1006 return filesystem_full;
1010 _ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1014 if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1018 dent = _ao_fat_root_get(*entry);
1020 return -AO_FAT_EDIREOF;
1022 if (dent[0] == AO_FAT_DENT_END) {
1023 _ao_fat_root_put(dent, *entry, 0);
1024 return -AO_FAT_EDIREOF;
1026 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
1027 _ao_fat_dirent_init(dirent, dent, *entry);
1028 _ao_fat_root_put(dent, *entry, 0);
1030 return AO_FAT_SUCCESS;
1032 _ao_fat_root_put(dent, *entry, 0);
1038 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1042 ao_mutex_get(&ao_fat_mutex);
1043 status = _ao_fat_readdir(entry, dirent);
1044 ao_mutex_put(&ao_fat_mutex);
1051 * Open an existing file.
1054 _ao_fat_open(char name[11], uint8_t mode)
1057 static struct ao_fat_dirent dirent;
1060 if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1064 status = _ao_fat_readdir(&entry, &dirent);
1066 if (status == -AO_FAT_EDIREOF)
1067 return -AO_FAT_ENOENT;
1070 if (!memcmp(name, dirent.name, 11)) {
1071 if (AO_FAT_IS_DIR(dirent.attr))
1072 return -AO_FAT_EISDIR;
1073 if (!AO_FAT_IS_FILE(dirent.attr))
1074 return -AO_FAT_EPERM;
1075 if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
1076 return -AO_FAT_EACCESS;
1077 return _ao_fat_fd_alloc(&dirent);
1080 return -AO_FAT_ENOENT;
1084 ao_fat_open(char name[11], uint8_t mode)
1088 ao_mutex_get(&ao_fat_mutex);
1089 status = _ao_fat_open(name, mode);
1090 ao_mutex_put(&ao_fat_mutex);
1097 * Close the currently open file
1100 _ao_fat_close(int8_t fd)
1102 struct ao_file *file;
1104 file = _ao_fat_fd_to_file(fd);
1106 return -AO_FAT_EBADF;
1108 _ao_fat_fd_free(fd);
1110 return AO_FAT_SUCCESS;
1114 ao_fat_close(int8_t fd)
1118 ao_mutex_get(&ao_fat_mutex);
1119 status = _ao_fat_close(fd);
1120 ao_mutex_put(&ao_fat_mutex);
1127 * Open and truncate an existing file or
1132 _ao_fat_creat(char name[11])
1138 struct ao_file *file;
1140 if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1143 fd = _ao_fat_open(name, AO_FAT_OPEN_WRITE);
1145 file = &ao_file_table[fd];
1146 status = _ao_fat_set_size(file, 0);
1152 if (fd == -AO_FAT_ENOENT) {
1155 dent = _ao_fat_root_get(entry);
1158 if (_ao_fat_root_extend(entry))
1160 fd = -AO_FAT_ENOSPC;
1163 if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
1164 fd = _ao_fat_fd_alloc(NULL);
1166 _ao_fat_root_put(dent, entry, 0);
1170 file = &ao_file_table[fd];
1171 /* Initialize the dent */
1172 _ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
1174 /* Now initialize the dirent from the dent */
1175 _ao_fat_dirent_init(file->dirent, dent, entry);
1177 /* And write the dent to storage */
1178 _ao_fat_root_put(dent, entry, 1);
1180 status = -AO_FAT_SUCCESS;
1183 _ao_fat_root_put(dent, entry, 0);
1193 ao_fat_creat(char name[11])
1197 ao_mutex_get(&ao_fat_mutex);
1198 status = _ao_fat_creat(name);
1199 ao_mutex_put(&ao_fat_mutex);
1204 * ao_fat_map_current
1206 * Map the sector pointed at by the current file offset
1210 ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, cluster_offset_t *this_time)
1212 cluster_offset_t offset;
1216 offset = file->offset & SECTOR_MASK;
1217 sector = _ao_fat_current_sector(file);
1218 if (sector == 0xffffffff) {
1221 buf = _ao_fat_sector_get(sector);
1224 if (offset + len < SECTOR_SIZE)
1227 *this_time = SECTOR_SIZE - offset;
1235 * Read from the file
1238 ao_fat_read(int8_t fd, void *dst, int len)
1240 uint8_t *dst_b = dst;
1241 cluster_offset_t this_time;
1242 cluster_offset_t offset;
1245 struct ao_file *file;
1247 ao_mutex_get(&ao_fat_mutex);
1248 file = _ao_fat_fd_to_file(fd);
1250 ret = -AO_FAT_EBADF;
1254 if (file->offset + len > (offset_t) file->dirent->size)
1255 len = file->dirent->size - file->offset;
1261 buf = ao_fat_map_current(file, len, &offset, &this_time);
1266 memcpy(dst_b, buf + offset, this_time);
1267 _ao_fat_sector_put(buf, 0);
1272 file->offset = file->offset + this_time;
1275 ao_mutex_put(&ao_fat_mutex);
1282 * Write to the file, extended as necessary
1285 ao_fat_write(int8_t fd, void *src, int len)
1287 uint8_t *src_b = src;
1288 cluster_offset_t this_time;
1289 cluster_offset_t offset;
1292 struct ao_file *file;
1294 ao_mutex_get(&ao_fat_mutex);
1295 file = _ao_fat_fd_to_file(fd);
1297 ret = -AO_FAT_EBADF;
1301 if (file->offset + len > (offset_t) file->dirent->size) {
1302 ret = _ao_fat_set_size(file, file->offset + len);
1308 buf = ao_fat_map_current(file, len, &offset, &this_time);
1313 memcpy(buf + offset, src_b, this_time);
1314 _ao_fat_sector_put(buf, 1);
1319 file->offset = file->offset + this_time;
1322 ao_mutex_put(&ao_fat_mutex);
1329 * Set the position for the next I/O operation
1330 * Note that this doesn't actually change the size
1331 * of the file if the requested position is beyond
1332 * the current file length, that would take a future
1336 ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence)
1338 offset_t new_offset;
1339 struct ao_file *file;
1342 ao_mutex_get(&ao_fat_mutex);
1343 file = _ao_fat_fd_to_file(fd);
1345 ret = -AO_FAT_EBADF;
1349 new_offset = file->offset;
1351 case AO_FAT_SEEK_SET:
1354 case AO_FAT_SEEK_CUR:
1357 case AO_FAT_SEEK_END:
1358 new_offset = file->dirent->size + pos;
1361 ret = file->offset = new_offset;
1363 ao_mutex_put(&ao_fat_mutex);
1370 * Remove a file from the directory, marking
1371 * all clusters as free
1374 ao_fat_unlink(char name[11])
1377 static struct ao_fat_dirent dirent;
1380 ao_mutex_get(&ao_fat_mutex);
1381 if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
1386 while (ao_fat_readdir(&entry, &dirent)) {
1387 if (memcmp(name, dirent.name, 11) == 0) {
1392 if (AO_FAT_IS_DIR(dirent.attr)) {
1393 ret = -AO_FAT_EISDIR;
1396 if (!AO_FAT_IS_FILE(dirent.attr)) {
1397 ret = -AO_FAT_EPERM;
1401 _ao_fat_free_cluster_chain(dirent.cluster);
1402 next = _ao_fat_root_get(dirent.entry + 1);
1403 if (next && next[0] != AO_FAT_DENT_END)
1404 delete = AO_FAT_DENT_EMPTY;
1406 delete = AO_FAT_DENT_END;
1408 _ao_fat_root_put(next, dirent.entry + 1, 0);
1409 ent = _ao_fat_root_get(dirent.entry);
1411 memset(ent, '\0', DIRENT_SIZE);
1413 _ao_fat_root_put(ent, dirent.entry, 1);
1416 ret = AO_FAT_SUCCESS;
1420 ret = -AO_FAT_ENOENT;
1422 ao_mutex_put(&ao_fat_mutex);
1427 ao_fat_rename(char old[11], char new[11])
1436 static const char *filesystem_errors[] = {
1437 [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally",
1438 [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error",
1439 [AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE] = "MBR signature invalid",
1440 [AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE] = "Unsupported paritition type",
1441 [AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION] = "Partition has zero sectors",
1442 [AO_FAT_FILESYSTEM_BOOT_READ_FAILURE] = "Boot block media read error",
1443 [AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE] = "Boot block signature invalid",
1444 [AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE] = "Sector size not 512",
1448 ao_fat_mbr_cmd(void)
1452 ao_mutex_get(&ao_fat_mutex);
1453 status = _ao_fat_setup();
1454 if (status == AO_FAT_FILESYSTEM_SUCCESS) {
1455 printf ("partition type: %02x\n", partition_type);
1456 printf ("partition start: %08x\n", partition_start);
1458 printf ("partition end: %08x\n", partition_end);
1460 printf ("fat32: %d\n", fat32);
1461 printf ("sectors per cluster %d\n", sectors_per_cluster);
1462 printf ("reserved sectors %d\n", reserved_sector_count);
1463 printf ("number of FATs %d\n", number_fat);
1464 printf ("root entries %d\n", root_entries);
1465 printf ("sectors per fat %d\n", sectors_per_fat);
1467 printf ("fat start %d\n", fat_start);
1468 printf ("root start %d\n", root_start);
1469 printf ("data start %d\n", data_start);
1471 printf ("FAT filesystem not available: %s\n", filesystem_errors[status]);
1473 ao_mutex_put(&ao_fat_mutex);
1476 struct ao_fat_attr {
1481 static const struct ao_fat_attr ao_fat_attr[] = {
1482 { .bit = AO_FAT_FILE_READ_ONLY, .label = 'R' },
1483 { .bit = AO_FAT_FILE_HIDDEN, .label = 'H' },
1484 { .bit = AO_FAT_FILE_SYSTEM, .label = 'S' },
1485 { .bit = AO_FAT_FILE_VOLUME_LABEL, .label = 'V' },
1486 { .bit = AO_FAT_FILE_DIRECTORY, .label = 'D' },
1487 { .bit = AO_FAT_FILE_ARCHIVE, .label = 'A' },
1490 #define NUM_FAT_ATTR (sizeof (ao_fat_attr) / sizeof (ao_fat_attr[0]))
1493 ao_fat_list_cmd(void)
1496 static struct ao_fat_dirent dirent;
1500 while ((status = ao_fat_readdir(&entry, &dirent)) == AO_FAT_SUCCESS) {
1501 for (i = 0; i < 8; i++)
1502 putchar(dirent.name[i]);
1505 putchar(dirent.name[i]);
1506 for (i = 0; i < (int) NUM_FAT_ATTR; i++)
1507 putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' ');
1508 printf (" @%08x %d\n", dirent.cluster, dirent.size);
1510 if (status != -AO_FAT_EDIREOF)
1511 printf ("readdir failed: %d\n", status);
1515 ao_fat_parse_name(char name[11])
1522 while (ao_cmd_lex_c != '\n') {
1523 if (ao_cmd_lex_c == '.') {
1528 name[c++] = ao_cmd_lex_c;
1537 ao_fat_dump_cmd(void)
1539 static char name[11];
1542 static char buf[32];
1544 ao_fat_parse_name(name);
1545 if (name[0] == '\0') {
1546 ao_cmd_status = ao_cmd_syntax_error;
1550 fd = ao_fat_open(name, AO_FAT_OPEN_READ);
1552 printf ("Open failed: %d\n", fd);
1555 while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
1556 for (i = 0; i < cnt; i++)
1563 ao_fat_write_cmd(void)
1565 static char name[11];
1570 ao_fat_parse_name(name);
1571 if (name[0] == '\0') {
1572 ao_cmd_status = ao_cmd_syntax_error;
1576 fd = ao_fat_creat(name);
1578 printf ("Open failed: %d\n", fd);
1582 while ((c = getchar()) != 4) {
1583 if (c == '\r') c = '\n';
1585 if (c == '\n') putchar ('\r');
1586 putchar(c); flush();
1588 status = ao_fat_write(fd, &c, 1);
1590 printf ("Write failure %d\n", status);
1600 ao_cmd_put16(a >> 16);
1605 ao_fat_hexdump_cmd(void)
1613 ao_fat_parse_name(name);
1614 if (name[0] == '\0') {
1615 ao_cmd_status = ao_cmd_syntax_error;
1619 fd = ao_fat_open(name, AO_FAT_OPEN_READ);
1621 printf ("Open failed: %d\n", fd);
1625 while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
1627 for (i = 0; i < cnt; i++) {
1629 ao_cmd_put8(buf[i]);
1637 static const struct ao_cmds ao_fat_cmds[] = {
1638 { ao_fat_mbr_cmd, "M\0Show FAT MBR and other info" },
1639 { ao_fat_list_cmd, "F\0List FAT directory" },
1640 { ao_fat_dump_cmd, "D <name>\0Dump FAT file" },
1641 { ao_fat_write_cmd, "W <name>\0Write FAT file (end with ^D)" },
1642 { ao_fat_hexdump_cmd, "H <name>\0HEX dump FAT file" },
1653 ao_cmd_register(&ao_fat_cmds[0]);