+/* Get the next cluster entry in the chain */
+static uint16_t
+ao_fat_entry_raw_read(uint16_t cluster, uint8_t fat)
+{
+ uint32_t sector;
+ uint16_t offset;
+ uint8_t *buf;
+ uint16_t ret;
+
+// cluster -= 2;
+ sector = cluster >> (SECTOR_SHIFT - 1);
+ offset = (cluster << 1) & SECTOR_MASK;
+ buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
+ if (!buf)
+ return 0;
+ ret = get_u16(buf + offset);
+ ao_fat_sector_put(buf, 0);
+ return ret;
+}
+
+void
+dump_fat(void)
+{
+ int e;
+
+ printf ("\n **** FAT ****\n\n");
+ for (e = 0; e < number_cluster; e++) {
+ if ((e & 0xf) == 0x0)
+ printf ("%04x: ", e);
+ printf (" %04x", ao_fat_entry_raw_read(e, 0));
+ if ((e & 0xf) == 0xf)
+ putchar ('\n');
+ }
+}
+
+void
+fat_list(void)
+{
+ uint16_t entry = 0;
+ struct ao_fat_dirent dirent;
+
+ printf (" **** Root directory ****\n");
+ while (ao_fat_readdir(&entry, &dirent)) {
+ printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n",
+ entry,
+ dirent.name,
+ dirent.name + 8,
+ dirent.attr,
+ dirent.cluster,
+ dirent.size);
+ }
+
+ printf (" **** End of root directory ****\n");
+}
+
+void
+fatal(char *msg, ...)
+{
+ dump_fat();
+ fat_list();
+
+ va_list l;
+ va_start(l, msg);
+ vfprintf(stderr, msg, l);
+ va_end(l);
+
+ abort();
+}
+
+void
+check_fat(void)
+{
+ int e;
+ int f;
+
+ for (e = 0; e < number_cluster; e++) {
+ uint16_t v = ao_fat_entry_raw_read(e, 0);
+ for (f = 1; f < number_fat; f++) {
+ if (ao_fat_entry_raw_read(e, f) != v)
+ fatal ("fats differ at %d\n", e);
+ }
+ }
+}
+
+uint16_t
+check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used)
+{
+ uint16_t clusters = 0;
+ uint16_t cluster;
+
+ if (!first_cluster)
+ return 0;
+
+ for (cluster = first_cluster;
+ (cluster & 0xfff8) != 0xfff8;
+ cluster = ao_fat_entry_raw_read(cluster, 0))
+ {
+ if (!ao_fat_cluster_valid(cluster))
+ fatal("file %d: invalid cluster %04x\n", dent, cluster);
+ if (used[cluster])
+ fatal("file %d: duplicate cluster %04x\n", dent, cluster);
+ used[cluster] = 1;
+ clusters++;
+ }
+ return clusters;
+}
+
+void
+check_fs(void)
+{
+ uint16_t r;
+ uint16_t cluster, chain;
+ uint8_t *used;
+
+ check_fat();
+
+ used = calloc(1, number_cluster);
+
+ for (r = 0; r < root_entries; r++) {
+ uint8_t *dent = ao_fat_root_get(r);
+ uint16_t clusters;
+ uint32_t size;
+ uint16_t first_cluster;
+ uint8_t name[11];
+
+ if (!dent)
+ fatal("cannot map dent %d\n", r);
+ memcpy(name, dent+0, 11);
+ first_cluster = get_u16(dent + 0x1a);
+ size = get_u32(dent + 0x1c);
+ ao_fat_root_put(dent, r, 0);
+
+ if (name[0] == AO_FAT_DENT_END) {
+ break;
+ }
+
+ clusters = check_file(r, first_cluster, used);
+ if (size > clusters * bytes_per_cluster)
+ fatal("file %d: size %u beyond clusters %d (%u)\n",
+ r, size, clusters, clusters * bytes_per_cluster);
+ if (size <= (clusters - 1) * bytes_per_cluster)
+ fatal("file %d: size %u too small clusters %d (%u)\n",
+ r, size, clusters, clusters * bytes_per_cluster);
+ }
+ for (; r < root_entries; r++) {
+ uint8_t *dent = ao_fat_root_get(r);
+ if (!dent)
+ fatal("cannot map dent %d\n", r);
+ if (dent[0] != AO_FAT_DENT_END)
+ fatal("found non-zero dent past end %d\n", r);
+ ao_fat_root_put(dent, r, 0);
+ }
+
+ for (cluster = 0; cluster < 2; cluster++) {
+ chain = ao_fat_entry_raw_read(cluster, 0);
+
+ if ((chain & 0xfff8) != 0xfff8)
+ fatal("cluster %d: not marked busy\n", cluster);
+ }
+ for (; cluster < number_cluster; cluster++) {
+ chain = ao_fat_entry_raw_read(cluster, 0);
+
+ if (chain != 0) {
+ if (used[cluster] == 0)
+ fatal("cluster %d: marked busy, but not in any file\n", cluster);
+ } else {
+ if (used[cluster] != 0)
+ fatal("cluster %d: marked free, but foudn in file\n", cluster);
+ }
+ }
+}
+
+#define NUM_FILES 512
+#define LINES_FILE 1000
+
+uint32_t sizes[NUM_FILES];
+
+unsigned char md5[NUM_FILES][MD5_DIGEST_LENGTH];
+