From 0838b6c8797b84cf8df8f92ee20fb6ae79e434d7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 1 Apr 2013 02:02:14 -0700 Subject: [PATCH] altos: Make sure FAT cluster allocation works for size zero files There were some rounding errors mis-computing the number of clusters needed, and the logic to figure out how to re-connect a chain was broken. Signed-off-by: Keith Packard --- src/drivers/ao_fat.c | 195 +++++++++++++++++++++++++------------------ 1 file changed, 112 insertions(+), 83 deletions(-) diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 6aae1410..3c72c397 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -286,101 +286,130 @@ ao_fat_cluster_seek(cluster_t cluster, cluster_t distance) static cluster_t ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) { - cluster_t clear_cluster = 0; + cluster_t have; + cluster_t last_cluster; + cluster_t next_cluster; + + /* Walk the cluster chain to the + * spot where it needs to change. That + * will either be the end of the chain (in case it needs to grow), + * or after the desired number of clusters, in which case it needs to shrink + */ + next_cluster = first_cluster; + last_cluster = 0; + DBG("\tclusters:"); + for (have = 0; have < size; have++) { + DBG(" %08x", next_cluster); + if (!ao_fat_cluster_valid(next_cluster)) + break; + last_cluster = next_cluster; + next_cluster = ao_fat_entry_read(next_cluster); + } + DBG("\n"); + + /* At this point, last_cluster points to the last valid + * cluster in the file, if any. That's the spot in the FAT + * that needs to be rewritten, either to truncate the file by + * writing an END marker, or to extend the file by writing + * more clusters. next_cluster will contain the value of the + * FAT at last_cluster. + * + * If this is at the head of the cluster chain, then + * last_cluster will be zero and next_cluster will + * be the first cluster in the chain. + */ + if (have == size) { + /* The file is large enough, truncate as needed */ + if (ao_fat_cluster_valid(next_cluster)) { + DBG("truncate between %08x and %08x\n", last_cluster, next_cluster); + if (last_cluster) + /* + * Otherwise, rewrite the last cluster + * in the chain with a LAST marker + */ + (void) ao_fat_entry_replace(last_cluster, + AO_FAT_LAST_CLUSTER); + else + /* + * If the file is getting erased, then + * rewrite the directory entry cluster + * value + */ + first_cluster = 0; - if (size == 0) { - clear_cluster = first_cluster; - first_cluster = 0; - } else { - cluster_t have; - cluster_t last_cluster = 0; - cluster_t next_cluster; - - /* Walk the cluster chain to the - * spot where it needs to change. That - * will either be the end of the chain (in case it needs to grow), - * or after the desired number of clusters, in which case it needs to shrink - */ - next_cluster = first_cluster; - for (have = 0; have < size; have++) { - last_cluster = next_cluster; - next_cluster = ao_fat_entry_read(last_cluster); - if (!ao_fat_cluster_valid(next_cluster)) - break; - } + /* Clear the remaining clusters in the chain */ + ao_fat_free_cluster_chain(next_cluster); - if (have == size) { - /* The file is large enough, truncate as needed */ - if (ao_fat_cluster_valid(next_cluster)) { - /* Rewrite that cluster entry with 0xffff to mark the end of the chain */ - clear_cluster = ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); - filesystem_full = 0; - } else { - /* The chain is already the right length, don't mess with it */ - ; - } + /* The file system is no longer full (if it was) */ + filesystem_full = 0; } else { - cluster_t need; - cluster_t free; + DBG("unchanged FAT chain\n"); + /* The chain is already the right length, don't mess with it */ + ; + } + } else { + cluster_t need; + cluster_t free; - if (filesystem_full) - return AO_FAT_BAD_CLUSTER; + if (filesystem_full) + return AO_FAT_BAD_CLUSTER; - if (next_free < 2 || number_cluster <= next_free) { - next_free = 2; - fsinfo_dirty = 1; - } + /* Set next_free if it has wrapped or wasn't set before */ + if (next_free < 2 || number_cluster <= next_free) { + next_free = 2; + fsinfo_dirty = 1; + } - /* See if there are enough free clusters in the file system */ - need = size - have; + /* See if there are enough free clusters in the file system */ + need = size - have; #define loop_cluster for (free = next_free; need > 0;) -#define next_cluster \ - if (++free == number_cluster) \ - free = 2; \ - if (free == next_free) \ - break; \ - - loop_cluster { - if (!ao_fat_entry_read(free)) - need--; - next_cluster; - } - /* Still need some, tell the user that we've failed */ - if (need) { - filesystem_full = 1; - return AO_FAT_BAD_CLUSTER; - } +#define next_cluster \ + if (++free == number_cluster) \ + free = 2; \ + if (free == next_free) \ + break; \ + + loop_cluster { + if (!ao_fat_entry_read(free)) + need--; + next_cluster; + } - /* Now go allocate those clusters and - * thread them onto the chain - */ - need = size - have; - loop_cluster { - if (!ao_fat_entry_read(free)) { - next_free = free + 1; - if (next_free >= number_cluster) - next_free = 2; - fsinfo_dirty = 1; - if (last_cluster) - ao_fat_entry_replace(last_cluster, free); - else - first_cluster = free; - last_cluster = free; - need--; - } - next_cluster; + /* Still need some, tell the user that we've failed */ + if (need) { + filesystem_full = 1; + return AO_FAT_BAD_CLUSTER; + } + + /* Now go allocate those clusters and + * thread them onto the chain + */ + need = size - have; + loop_cluster { + if (ao_fat_entry_read(free) == 0) { + next_free = free + 1; + if (next_free >= number_cluster) + next_free = 2; + fsinfo_dirty = 1; + DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free); + if (last_cluster) + ao_fat_entry_replace(last_cluster, free); + else + first_cluster = free; + last_cluster = free; + need--; } + next_cluster; + } #undef loop_cluster #undef next_cluster - /* Mark the new end of the chain */ - ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); - } + DBG("\tlast cluster %08x\n", last_cluster); + /* Mark the new end of the chain */ + ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); } - /* Deallocate clusters off the end of the file */ - if (ao_fat_cluster_valid(clear_cluster)) - ao_fat_free_cluster_chain(clear_cluster); + DBG("\tfirst cluster %08x\n", first_cluster); return first_cluster; } @@ -437,8 +466,8 @@ ao_fat_root_extend(dirent_t ents) if (!fat32) return 0; - byte_size = ents * 0x20; - cluster_size = byte_size / bytes_per_cluster; + byte_size = (ents + 1) * 0x20; + cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster; if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER) return 1; return 0; -- 2.30.2