+/*
+ * _ao_fat_invaldate_cluster_offset
+ *
+ * When the file size gets shrunk, invalidate
+ * any file structures referencing clusters beyond that point
+ */
+
+static void
+_ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent)
+{
+ int8_t fd;
+ struct ao_file *file;
+
+ for (fd = 0; fd < AO_FAT_NFILE; fd++) {
+ file = &ao_file_table[fd];
+ if (!file->busy)
+ continue;
+ if (file->dirent == dirent) {
+ if (file->cluster_offset >= dirent->size) {
+ file->cluster_offset = 0;
+ file->cluster = dirent->cluster;
+ }
+ }
+ }
+}
+
+
+/*
+ * _ao_fat_set_size
+ *
+ * Set the size of the current file, truncating or extending
+ * the cluster chain as needed
+ */
+static int8_t
+_ao_fat_set_size(struct ao_file *file, uint32_t size)
+{
+ uint8_t *dent;
+ cluster_t first_cluster;
+ cluster_t have_clusters, need_clusters;
+
+ DBG ("Set size %d\n", size);
+ if (size == file->dirent->size) {
+ DBG("\tsize match\n");
+ return AO_FAT_SUCCESS;
+ }
+
+ first_cluster = file->dirent->cluster;
+ have_clusters = (file->dirent->size + bytes_per_cluster - 1) / bytes_per_cluster;
+ need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
+
+ DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters);
+ if (have_clusters != need_clusters) {
+ if (file->cluster && size > file->cluster_offset) {
+ cluster_t offset_clusters = (file->cluster_offset + bytes_per_cluster) / bytes_per_cluster;
+ cluster_t extra_clusters = need_clusters - offset_clusters;
+ cluster_t next_cluster;
+
+ DBG ("\tset size relative offset_clusters %d extra_clusters %d\n",
+ offset_clusters, extra_clusters);
+
+ /* Need one more to account for file->cluster, which we already have */
+ next_cluster = _ao_fat_cluster_set_size(file->cluster, extra_clusters + 1);
+ if (next_cluster == AO_FAT_BAD_CLUSTER)
+ return -AO_FAT_ENOSPC;
+ } else {
+ DBG ("\tset size absolute need_clusters %d\n", need_clusters);
+ first_cluster = _ao_fat_cluster_set_size(first_cluster, need_clusters);
+
+ if (first_cluster == AO_FAT_BAD_CLUSTER)
+ return -AO_FAT_ENOSPC;
+ }
+ }
+
+ DBG ("\tupdate directory size\n");
+ /* Update the directory entry */
+ dent = _ao_fat_root_get(file->dirent->entry);
+ if (!dent) {
+ printf ("dent update failed\n");
+ return -AO_FAT_EIO;
+ }
+ put_u32(dent + 0x1c, size);
+ put_u16(dent + 0x1a, first_cluster);
+ if (fat32)
+ put_u16(dent + 0x14, first_cluster >> 16);
+ _ao_fat_root_put(dent, file->dirent->entry, 1);
+
+ file->dirent->size = size;
+ file->dirent->cluster = first_cluster;
+ if (have_clusters > need_clusters)
+ _ao_fat_invalidate_cluster_offset(file->dirent);
+ DBG ("set size done\n");
+ return AO_FAT_SUCCESS;
+}
+
+/*
+ * _ao_fat_root_init
+ *
+ * Initialize a root directory entry
+ */
+static void
+_ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
+{
+ memset(dent, '\0', 0x20);
+ memmove(dent, name, 11);
+
+ dent[0x0b] = 0x00;
+ dent[0x0c] = 0x00;
+ dent[0x0d] = 0x00;
+
+ /* XXX fix time */
+ put_u16(dent + 0x0e, 0);
+ /* XXX fix date */
+ put_u16(dent + 0x10, 0);
+ /* XXX fix date */
+ put_u16(dent + 0x12, 0);
+
+ /* XXX fix time */
+ put_u16(dent + 0x16, 0);
+ /* XXX fix date */
+ put_u16(dent + 0x18, 0);
+
+ /* cluster number */
+ /* Low cluster bytes */
+ put_u16(dent + 0x1a, 0);
+ /* FAT32 high cluster bytes */
+ put_u16(dent + 0x14, 0);
+
+ /* size */
+ put_u32(dent + 0x1c, 0);
+}
+
+
+static void
+_ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry)
+{
+ memcpy(dirent->name, dent + 0x00, 11);
+ dirent->attr = dent[0x0b];
+ dirent->size = get_u32(dent+0x1c);
+ dirent->cluster = get_u16(dent+0x1a);
+ if (fat32)
+ dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
+ dirent->entry = entry;
+}
+
+/*
+ * _ao_fat_flush_fsinfo
+ *
+ * Write out any fsinfo changes to disk
+ */
+
+static void
+_ao_fat_flush_fsinfo(void)
+{
+ uint8_t *fsinfo;
+
+ if (!fat32)
+ return;
+
+ if (!fsinfo_dirty)
+ return;
+ fsinfo_dirty = 0;
+ if (!fsinfo_sector)
+ return;
+
+ fsinfo = _ao_fat_sector_get(fsinfo_sector);
+ if (fsinfo) {
+ put_u32(fsinfo + 0x1e8, free_count);
+ put_u32(fsinfo + 0x1ec, next_free);
+ _ao_fat_sector_put(fsinfo, 1);
+ }
+}
+
+/*
+ * Public API
+ */
+
+/*
+ * ao_fat_sync
+ *
+ * Flush any pending I/O to storage
+ */
+
+static void
+_ao_fat_sync(void)
+{
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+ return;
+ _ao_fat_flush_fsinfo();
+ ao_bufio_flush();
+}
+
+void
+ao_fat_sync(void)
+{
+ ao_mutex_get(&ao_fat_mutex);
+ _ao_fat_sync();
+ ao_mutex_put(&ao_fat_mutex);
+}
+
+/*
+ * ao_fat_full
+ *
+ * Returns TRUE if the filesystem cannot take
+ * more data
+ */
+
+int8_t
+ao_fat_full(void)
+{
+ ao_mutex_get(&ao_fat_mutex);
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
+ ao_mutex_put(&ao_fat_mutex);
+ return 1;
+ }
+ ao_mutex_put(&ao_fat_mutex);
+ return filesystem_full;
+}
+
+static int8_t
+_ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
+{
+ uint8_t *dent;
+
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+ return -AO_FAT_EIO;
+
+ for (;;) {
+ dent = _ao_fat_root_get(*entry);
+ if (!dent)
+ return -AO_FAT_EDIREOF;
+
+ if (dent[0] == AO_FAT_DENT_END) {
+ _ao_fat_root_put(dent, *entry, 0);
+ return -AO_FAT_EDIREOF;
+ }
+ if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
+ _ao_fat_dirent_init(dirent, dent, *entry);
+ _ao_fat_root_put(dent, *entry, 0);
+ (*entry)++;
+ return AO_FAT_SUCCESS;
+ }
+ _ao_fat_root_put(dent, *entry, 0);
+ (*entry)++;
+ }
+}
+
+int8_t
+ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
+{
+ int8_t status;
+
+ ao_mutex_get(&ao_fat_mutex);
+ status = _ao_fat_readdir(entry, dirent);
+ ao_mutex_put(&ao_fat_mutex);
+ return status;
+}
+
+/*
+ * ao_fat_open
+ *
+ * Open an existing file.
+ */
+static int8_t
+_ao_fat_open(char name[11], uint8_t mode)