altos: Make sure FAT cluster allocation works for size zero files
[fw/altos] / src / drivers / ao_fat.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
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.
7  *
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.
12  *
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.
16  */
17
18 #ifndef AO_FAT_TEST
19 #include "ao.h"
20 #endif
21
22 #include "ao_fat.h"
23 #include "ao_bufio.h"
24
25 /* Include FAT commands */
26 #ifndef AO_FAT_TEST
27 #define FAT_COMMANDS    1
28 #endif
29
30 /* Spew FAT tracing */
31 #define FAT_TRACE       0
32  
33 #ifdef DBG
34 #undef DBG
35 #endif
36
37 #if FAT_TRACE
38 #define DBG(...) printf(__VA_ARGS__)
39 #else
40 #define DBG(...)
41 #endif
42
43 /*
44  * Basic file system types
45  */
46
47 typedef ao_fat_offset_t         offset_t;
48 typedef ao_fat_sector_t         sector_t;
49 typedef ao_fat_cluster_t        cluster_t;
50 typedef ao_fat_dirent_t         dirent_t;
51 typedef ao_fat_cluster_offset_t cluster_offset_t;
52
53 /* Partition information, sector numbers */
54
55 static uint8_t  partition_type;
56 static sector_t partition_start, partition_end;
57
58 #define AO_FAT_BAD_CLUSTER              0xffffff7
59 #define AO_FAT_LAST_CLUSTER             0xfffffff
60 #define AO_FAT_IS_LAST_CLUSTER(c)               (((c) & 0xffffff8) == 0xffffff8)
61 #define AO_FAT_IS_LAST_CLUSTER16(c)     (((c) & 0xfff8) == 0xfff8)
62
63
64 #define SECTOR_SIZE     512
65 #define SECTOR_MASK     (SECTOR_SIZE - 1)
66 #define SECTOR_SHIFT    9
67
68 #define DIRENT_SIZE     32
69
70 /* File system parameters */
71 static uint8_t          sectors_per_cluster;
72 static uint32_t         bytes_per_cluster;
73 static sector_t         reserved_sector_count;
74 static uint8_t          number_fat;
75 static dirent_t         root_entries;
76 static sector_t         sectors_per_fat;
77 static cluster_t        number_cluster;
78 static sector_t         fat_start;
79 static sector_t         root_start;
80 static sector_t         data_start;
81 static cluster_t        next_free;
82 static uint8_t          filesystem_full;
83
84 /* FAT32 extra data */
85 static uint8_t          fat32;
86 static uint8_t          fsinfo_dirty;
87 static cluster_t        root_cluster;
88 static sector_t         fsinfo_sector;
89 static cluster_t        free_count;
90
91 /*
92  * Deal with LSB FAT data structures
93  */
94 static uint32_t
95 get_u32(uint8_t *base)
96 {
97         return ((uint32_t) base[0] |
98                 ((uint32_t) base[1] << 8) |
99                 ((uint32_t) base[2] << 16) |
100                 ((uint32_t) base[3] << 24));
101 }
102
103 static void
104 put_u32(uint8_t *base, uint32_t value)
105 {
106         base[0] = value;
107         base[1] = value >> 8;
108         base[2] = value >> 16;
109         base[3] = value >> 24;
110 }
111
112 static uint16_t
113 get_u16(uint8_t *base)
114 {
115         return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
116 }
117
118 static void
119 put_u16(uint8_t *base, uint16_t value)
120 {
121         base[0] = value;
122         base[1] = value >> 8;
123 }
124
125 static uint8_t
126 ao_fat_cluster_valid(cluster_t cluster)
127 {
128         return (2 <= cluster && cluster < number_cluster);
129 }
130
131 /* Start using a sector */
132 static uint8_t *
133 ao_fat_sector_get(sector_t sector)
134 {
135         sector += partition_start;
136         if (sector >= partition_end)
137                 return NULL;
138         return ao_bufio_get(sector);
139 }
140
141 /* Finish using a sector, 'w' is 1 if modified */
142 #define ao_fat_sector_put(b,w) ao_bufio_put(b,w)
143
144 /* Get the next cluster entry in the chain */
145 static cluster_t
146 ao_fat_entry_read(cluster_t cluster)
147 {
148         sector_t        sector;
149         cluster_t       offset;
150         uint8_t         *buf;
151         cluster_t       ret;
152
153         if (!ao_fat_cluster_valid(cluster))
154                 return 0xfffffff7;
155
156         if (fat32)
157                 cluster <<= 2;
158         else
159                 cluster <<= 1;
160         sector = cluster >> (SECTOR_SHIFT);
161         offset = cluster & SECTOR_MASK;
162         buf = ao_fat_sector_get(fat_start + sector);
163         if (!buf)
164                 return 0;
165
166         if (fat32) {
167                 ret = get_u32(buf + offset);
168                 ret &= 0xfffffff;
169         } else {
170                 ret = get_u16(buf + offset);
171                 if (AO_FAT_IS_LAST_CLUSTER16(ret))
172                         ret |= 0xfff0000;
173         }
174         ao_fat_sector_put(buf, 0);
175         return ret;
176 }
177
178 /* Replace the referenced cluster entry in the chain with
179  * 'new_value'. Return the previous value.
180  */
181 static cluster_t
182 ao_fat_entry_replace(cluster_t  cluster, cluster_t new_value)
183 {
184         sector_t                sector;
185         cluster_offset_t        offset;
186         uint8_t                 *buf;
187         cluster_t               ret;
188         cluster_t               old_value;
189         uint8_t                 fat;
190
191         if (!ao_fat_cluster_valid(cluster))
192                 return 0xfffffff7;
193
194         /* Convert from cluster index to byte index */
195         if (fat32)
196                 cluster <<= 2;
197         else
198                 cluster <<= 1;
199         sector = cluster >> SECTOR_SHIFT;
200         offset = cluster & SECTOR_MASK;
201
202         new_value &= 0xfffffff;
203         for (fat = 0; fat < number_fat; fat++) {
204                 buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
205                 if (!buf)
206                         return 0;
207                 if (fat32) {
208                         old_value = get_u32(buf + offset);
209                         put_u32(buf + offset, new_value | (old_value & 0xf0000000));
210                         if (fat == 0) {
211                                 ret = old_value & 0xfffffff;
212
213                                 /* Track the free count if it wasn't marked
214                                  * invalid when we mounted the file system
215                                  */
216                                 if (free_count != 0xffffffff) {
217                                         if (new_value && !ret) {
218                                                 --free_count;
219                                                 fsinfo_dirty = 1;
220                                         } else if (!new_value && ret) {
221                                                 ++free_count;
222                                                 fsinfo_dirty = 1;
223                                         }
224                                 }
225                         }
226                 } else {
227                         if (fat == 0) {
228                                 ret = get_u16(buf + offset);
229                                 if (AO_FAT_IS_LAST_CLUSTER16(ret))
230                                         ret |= 0xfff0000;
231                         }
232                         put_u16(buf + offset, new_value);
233                 }
234                 ao_fat_sector_put(buf, 1);
235         }
236         return ret;
237         
238 }
239
240 /*
241  * Walk a cluster chain and mark
242  * all of them as free
243  */
244 static void
245 ao_fat_free_cluster_chain(cluster_t cluster)
246 {
247         while (ao_fat_cluster_valid(cluster)) {
248                 if (cluster < next_free) {
249                         next_free = cluster;
250                         fsinfo_dirty = 1;
251                 }
252                 cluster = ao_fat_entry_replace(cluster, 0x00000000);
253         }
254 }
255
256 /*
257  * ao_fat_cluster_seek
258  * 
259  * The basic file system operation -- map a file cluster index to a
260  * partition cluster number. Done by computing the cluster number and
261  * then walking that many clusters from the first cluster. Returns
262  * 0xffff if we walk off the end of the file or the cluster chain
263  * is damaged somehow
264  */
265 static cluster_t
266 ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)
267 {
268         while (distance) {
269                 cluster = ao_fat_entry_read(cluster);
270                 if (!ao_fat_cluster_valid(cluster))
271                         break;
272                 distance--;
273         }
274         return cluster;
275 }
276
277 /*
278  * ao_fat_cluster_set_size
279  *
280  * Set the number of clusters in the specified chain,
281  * freeing extra ones or alocating new ones as needed
282  *
283  * Returns AO_FAT_BAD_CLUSTER on allocation failure
284  */
285
286 static cluster_t
287 ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
288 {
289         cluster_t       have;
290         cluster_t       last_cluster;
291         cluster_t       next_cluster;
292
293         /* Walk the cluster chain to the
294          * spot where it needs to change. That
295          * will either be the end of the chain (in case it needs to grow),
296          * or after the desired number of clusters, in which case it needs to shrink
297          */
298         next_cluster = first_cluster;
299         last_cluster = 0;
300         DBG("\tclusters:");
301         for (have = 0; have < size; have++) {
302                 DBG(" %08x", next_cluster);
303                 if (!ao_fat_cluster_valid(next_cluster))
304                         break;
305                 last_cluster = next_cluster;
306                 next_cluster = ao_fat_entry_read(next_cluster);
307         }
308         DBG("\n");
309
310         /* At this point, last_cluster points to the last valid
311          * cluster in the file, if any. That's the spot in the FAT
312          * that needs to be rewritten, either to truncate the file by
313          * writing an END marker, or to extend the file by writing
314          * more clusters. next_cluster will contain the value of the
315          * FAT at last_cluster.
316          *
317          * If this is at the head of the cluster chain, then
318          * last_cluster will be zero and next_cluster will
319          * be the first cluster in the chain.
320          */
321         if (have == size) {
322                 /* The file is large enough, truncate as needed */
323                 if (ao_fat_cluster_valid(next_cluster)) {
324                         DBG("truncate between %08x and %08x\n", last_cluster, next_cluster);
325                         if (last_cluster)
326                                 /*
327                                  * Otherwise, rewrite the last cluster
328                                  * in the chain with a LAST marker
329                                  */
330                                 (void) ao_fat_entry_replace(last_cluster,
331                                                             AO_FAT_LAST_CLUSTER);
332                         else
333                                 /*
334                                  * If the file is getting erased, then
335                                  * rewrite the directory entry cluster
336                                  * value
337                                  */
338                                 first_cluster = 0;
339
340                         /* Clear the remaining clusters in the chain */
341                         ao_fat_free_cluster_chain(next_cluster);
342
343                         /* The file system is no longer full (if it was) */
344                         filesystem_full = 0;
345                 } else {
346                         DBG("unchanged FAT chain\n");
347                         /* The chain is already the right length, don't mess with it */
348                         ;
349                 }
350         } else {
351                 cluster_t       need;
352                 cluster_t       free;
353
354                 if (filesystem_full)
355                         return AO_FAT_BAD_CLUSTER;
356
357                 /* Set next_free if it has wrapped or wasn't set before */
358                 if (next_free < 2 || number_cluster <= next_free) {
359                         next_free = 2;
360                         fsinfo_dirty = 1;
361                 }
362
363                 /* See if there are enough free clusters in the file system */
364                 need = size - have;
365
366 #define loop_cluster    for (free = next_free; need > 0;)
367 #define next_cluster                            \
368                 if (++free == number_cluster)   \
369                         free = 2;               \
370                 if (free == next_free)          \
371                         break;                  \
372
373                 loop_cluster {
374                         if (!ao_fat_entry_read(free))
375                                 need--;
376                         next_cluster;
377                 }
378
379                 /* Still need some, tell the user that we've failed */
380                 if (need) {
381                         filesystem_full = 1;
382                         return AO_FAT_BAD_CLUSTER;
383                 }
384
385                 /* Now go allocate those clusters and
386                  * thread them onto the chain
387                  */
388                 need = size - have;
389                 loop_cluster {
390                         if (ao_fat_entry_read(free) == 0) {
391                                 next_free = free + 1;
392                                 if (next_free >= number_cluster)
393                                         next_free = 2;
394                                 fsinfo_dirty = 1;
395                                 DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free);
396                                 if (last_cluster)
397                                         ao_fat_entry_replace(last_cluster, free);
398                                 else
399                                         first_cluster = free;
400                                 last_cluster = free;
401                                 need--;
402                         }
403                         next_cluster;
404                 }
405 #undef loop_cluster
406 #undef next_cluster
407                 DBG("\tlast cluster %08x\n", last_cluster);
408                 /* Mark the new end of the chain */
409                 ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
410         }
411
412         DBG("\tfirst cluster %08x\n", first_cluster);
413         return first_cluster;
414 }
415
416 /* Start using a root directory entry */
417 static uint8_t *
418 ao_fat_root_get(dirent_t e)
419 {
420         offset_t                byte = e * DIRENT_SIZE;
421         sector_t                sector = byte >> SECTOR_SHIFT;
422         cluster_offset_t        offset = byte & SECTOR_MASK;
423         uint8_t                 *buf;
424
425         if (fat32) {
426                 cluster_t       cluster_distance = sector / sectors_per_cluster;
427                 sector_t        sector_index = sector % sectors_per_cluster;
428                 cluster_t       cluster = ao_fat_cluster_seek(root_cluster, cluster_distance);
429
430                 if (ao_fat_cluster_valid(cluster))
431                         sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
432                 else
433                         return NULL;
434         } else {
435                 if (e >= root_entries)
436                         return NULL;
437                 sector = root_start + sector;
438         }
439
440         buf = ao_fat_sector_get(sector);
441         if (!buf)
442                 return NULL;
443         return buf + offset;
444 }
445
446 /* Finish using a root directory entry, 'w' is 1 if modified */
447 static void
448 ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
449 {
450         cluster_offset_t        offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
451         uint8_t                 *buf = root - offset;
452
453         ao_fat_sector_put(buf, write);
454 }
455
456 /*
457  * ao_fat_root_extend
458  *
459  * On FAT32, make the root directory at least 'ents' entries long
460  */
461 static int8_t
462 ao_fat_root_extend(dirent_t ents)
463 {
464         offset_t        byte_size;
465         cluster_t       cluster_size;
466         if (!fat32)
467                 return 0;
468         
469         byte_size = (ents + 1) * 0x20;
470         cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster;
471         if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
472                 return 1;
473         return 0;
474 }
475                 
476 /*
477  * ao_fat_setup_partition
478  * 
479  * Load the boot block and find the first partition
480  */
481 static uint8_t
482 ao_fat_setup_partition(void)
483 {
484         uint8_t *mbr;
485         uint8_t *partition;
486         uint32_t partition_size;
487
488         mbr = ao_bufio_get(0);
489         if (!mbr)
490                 return AO_FAT_FILESYSTEM_MBR_READ_FAILURE;
491
492         /* Check the signature */
493         if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
494                 DBG ("Invalid MBR signature %02x %02x\n",
495                         mbr[0x1fe], mbr[0x1ff]);
496                 ao_bufio_put(mbr, 0);
497                 return AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE;
498         }
499
500         /* Check to see if it's actually a boot block, in which
501          * case it's presumably not a paritioned device
502          */
503
504         if (mbr[0] == 0xeb) {
505                 partition_start = 0;
506                 partition_size = get_u16(mbr + 0x13);
507                 if (partition_size == 0)
508                         partition_size = get_u32(mbr + 0x20);
509         } else {
510                 /* Just use the first partition */
511                 partition = &mbr[0x1be];
512         
513                 partition_type = partition[4];
514                 switch (partition_type) {
515                 case 4:         /* FAT16 up to 32M */
516                 case 6:         /* FAT16 over 32M */
517                         break;
518                 case 0x0b:      /* FAT32 up to 2047GB */
519                 case 0x0c:      /* FAT32 LBA */
520                         break;
521                 default:
522                         DBG ("Invalid partition type %02x\n", partition_type);
523                         ao_bufio_put(mbr, 0);
524                         return AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE;
525                 }
526
527                 partition_start = get_u32(partition+8);
528                 partition_size = get_u32(partition+12);
529                 if (partition_size == 0) {
530                         DBG ("Zero-sized partition\n");
531                         ao_bufio_put(mbr, 0);
532                         return AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION;
533                 }
534         }
535         partition_end = partition_start + partition_size;
536         ao_bufio_put(mbr, 0);
537         return AO_FAT_FILESYSTEM_SUCCESS;
538 }
539         
540 static uint8_t
541 ao_fat_setup_fs(void)
542 {
543         uint8_t         *boot = ao_fat_sector_get(0);
544         uint32_t        data_sectors;
545
546         if (!boot)
547                 return AO_FAT_FILESYSTEM_BOOT_READ_FAILURE;
548
549         /* Check the signature */
550         if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
551                 DBG ("Invalid BOOT signature %02x %02x\n",
552                         boot[0x1fe], boot[0x1ff]);
553                 ao_fat_sector_put(boot, 0);
554                 return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE;
555         }
556
557         /* Check the sector size */
558         if (get_u16(boot + 0xb) != SECTOR_SIZE) {
559                 DBG ("Invalid sector size %d\n",
560                         get_u16(boot + 0xb));
561                 ao_fat_sector_put(boot, 0);
562                 return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE;
563         }
564
565         sectors_per_cluster = boot[0xd];
566         bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
567         reserved_sector_count = get_u16(boot+0xe);
568         number_fat = boot[0x10];
569         root_entries = get_u16(boot + 0x11);
570         sectors_per_fat = get_u16(boot+0x16);
571         fat32 = 0;
572         if (sectors_per_fat == 0) {
573                 fat32 = 1;
574                 sectors_per_fat = get_u32(boot+0x24);
575                 root_cluster = get_u32(boot+0x2c);
576                 fsinfo_sector = get_u16(boot + 0x30);
577         }
578         ao_fat_sector_put(boot, 0);
579
580         free_count = 0xffffffff;
581         next_free = 0;
582         if (fat32 && fsinfo_sector) {
583                 uint8_t *fsinfo = ao_fat_sector_get(fsinfo_sector);
584
585                 if (fsinfo) {
586                         free_count = get_u32(fsinfo + 0x1e8);
587                         next_free = get_u32(fsinfo + 0x1ec);
588                         ao_fat_sector_put(fsinfo, 0);
589                 }
590         }
591
592         fat_start = reserved_sector_count;;
593         root_start = fat_start + number_fat * sectors_per_fat;
594         data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
595
596         data_sectors = (partition_end - partition_start) - data_start;
597
598         number_cluster = data_sectors / sectors_per_cluster;
599
600         return AO_FAT_FILESYSTEM_SUCCESS;
601 }
602
603 /*
604  * State for the current opened file
605  */
606 static struct ao_fat_dirent     ao_file_dirent;
607 static uint32_t                 ao_file_offset;
608 static uint32_t                 ao_file_cluster_offset;
609 static cluster_t                ao_file_cluster;
610 static uint8_t                  ao_file_opened;
611 static uint8_t                  ao_filesystem_setup;
612 static uint8_t                  ao_filesystem_status;
613
614 static uint8_t
615 ao_fat_setup(void)
616 {
617         if (!ao_filesystem_setup) {
618
619                 ao_filesystem_setup = 1;
620                 ao_bufio_setup();
621         
622                 /* Re-initialize all global state; this will help to allow the
623                  * file system to get swapped someday
624                  */
625                 partition_type = partition_start = partition_end = 0;
626                 sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
627                 number_fat = root_entries = sectors_per_fat = 0;
628                 number_cluster = fat_start = root_start = data_start = 0;
629                 next_free = filesystem_full = 0;
630                 fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
631                 memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent));
632
633                 ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0;
634                 ao_filesystem_status = ao_fat_setup_partition();
635                 if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
636                         return ao_filesystem_status;
637                 ao_filesystem_status = ao_fat_setup_fs();
638                 if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
639                         return ao_filesystem_status;
640         }
641         return ao_filesystem_status;
642 }
643
644 void
645 ao_fat_unmount(void)
646 {
647         ao_filesystem_setup = 0;
648 }
649
650 /*
651  * Basic file operations
652  */
653
654 static uint32_t
655 ao_fat_current_sector(void)
656 {
657         cluster_t       cluster_offset;
658         uint32_t        sector_offset;
659         uint16_t        sector_index;
660         cluster_t       cluster;
661
662         DBG("current sector offset %d size %d\n",
663             ao_file_offset, ao_file_dirent.size);
664
665         if (ao_file_offset > ao_file_dirent.size)
666                 return 0xffffffff;
667
668         sector_offset = ao_file_offset >> SECTOR_SHIFT;
669
670         if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) {
671                 ao_file_cluster = ao_file_dirent.cluster;
672                 ao_file_cluster_offset = 0;
673                 DBG("\treset to start of file %08x\n", ao_file_cluster);
674         }
675
676         if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) {
677                 cluster_t       cluster_distance;
678
679                 cluster_offset = sector_offset / sectors_per_cluster;
680
681                 cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster;
682
683                 DBG("\tseek forward %d clusters\n", cluster_distance);
684                 cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance);
685
686                 if (!ao_fat_cluster_valid(cluster))
687                         return 0xffffffff;
688                 ao_file_cluster = cluster;
689                 ao_file_cluster_offset = cluster_offset * bytes_per_cluster;
690         }
691
692         sector_index = sector_offset % sectors_per_cluster;
693         DBG("current cluster %08x sector_index %d sector %d\n",
694             ao_file_cluster, sector_index,
695             data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index);
696         return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index;
697 }
698
699 static void
700 ao_fat_set_offset(uint32_t offset)
701 {
702         DBG("Set offset %d\n", offset);
703         ao_file_offset = offset;
704 }
705
706 /*
707  * ao_fat_set_size
708  *
709  * Set the size of the current file, truncating or extending
710  * the cluster chain as needed
711  */
712 static int8_t
713 ao_fat_set_size(uint32_t size)
714 {
715         uint8_t         *dent;
716         cluster_t       first_cluster;
717         cluster_t       have_clusters, need_clusters;
718
719         DBG ("Set size %d\n", size);
720         if (size == ao_file_dirent.size) {
721                 DBG("\tsize match\n");
722                 return AO_FAT_SUCCESS;
723         }
724
725         first_cluster = ao_file_dirent.cluster;
726         have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster;
727         need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
728
729         DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters);
730         if (have_clusters != need_clusters) {
731                 if (ao_file_cluster && size >= ao_file_cluster_offset) {
732                         cluster_t       offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster;
733                         cluster_t       extra_clusters = need_clusters - offset_clusters;
734                         cluster_t       next_cluster;
735
736                         DBG ("\tset size relative offset_clusters %d extra_clusters %d\n",
737                              offset_clusters, extra_clusters);
738                         next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters);
739                         if (next_cluster == AO_FAT_BAD_CLUSTER)
740                                 return -AO_FAT_ENOSPC;
741                 } else {
742                         DBG ("\tset size absolute need_clusters %d\n", need_clusters);
743                         first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters);
744
745                         if (first_cluster == AO_FAT_BAD_CLUSTER)
746                                 return -AO_FAT_ENOSPC;
747                 }
748         }
749
750         DBG ("\tupdate directory size\n");
751         /* Update the directory entry */
752         dent = ao_fat_root_get(ao_file_dirent.entry);
753         if (!dent)
754                 return -AO_FAT_EIO;
755         put_u32(dent + 0x1c, size);
756         put_u16(dent + 0x1a, first_cluster);
757         if (fat32)
758                 put_u16(dent + 0x14, first_cluster >> 16);
759         ao_fat_root_put(dent, ao_file_dirent.entry, 1);
760
761         ao_file_dirent.size = size;
762         ao_file_dirent.cluster = first_cluster;
763         DBG ("set size done\n");
764         return AO_FAT_SUCCESS;
765 }
766
767 /*
768  * ao_fat_root_init
769  *
770  * Initialize a root directory entry
771  */
772 static void
773 ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
774 {
775         memset(dent, '\0', 0x20);
776         memmove(dent, name, 11);
777
778         dent[0x0b] = 0x00;
779         dent[0x0c] = 0x00;
780         dent[0x0d] = 0x00;
781
782         /* XXX fix time */
783         put_u16(dent + 0x0e, 0);
784         /* XXX fix date */
785         put_u16(dent + 0x10, 0);
786         /* XXX fix date */
787         put_u16(dent + 0x12, 0);
788
789         /* XXX fix time */
790         put_u16(dent + 0x16, 0);
791         /* XXX fix date */
792         put_u16(dent + 0x18, 0);
793
794         /* cluster number */
795         /* Low cluster bytes */
796         put_u16(dent + 0x1a, 0);
797         /* FAT32 high cluster bytes */
798         put_u16(dent + 0x14, 0);
799
800         /* size */
801         put_u32(dent + 0x1c, 0);
802 }
803
804
805 static void
806 ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent)
807 {
808         memcpy(dirent->name, dent + 0x00, 11);
809         dirent->attr = dent[0x0b];
810         dirent->size = get_u32(dent+0x1c);
811         dirent->cluster = get_u16(dent+0x1a);
812         if (fat32)
813                 dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
814         dirent->entry = entry;
815 }
816
817 /*
818  * ao_fat_flush_fsinfo
819  *
820  * Write out any fsinfo changes to disk
821  */
822
823 static void
824 ao_fat_flush_fsinfo(void)
825 {
826         uint8_t *fsinfo;
827
828         if (!fat32)
829                 return;
830
831         if (!fsinfo_dirty)
832                 return;
833         fsinfo_dirty = 0;
834         if (!fsinfo_sector)
835                 return;
836
837         fsinfo = ao_fat_sector_get(fsinfo_sector);
838         if (fsinfo) {
839                 put_u32(fsinfo + 0x1e8, free_count);
840                 put_u32(fsinfo + 0x1ec, next_free);
841                 ao_fat_sector_put(fsinfo, 1);
842         }
843 }
844
845 /*
846  * Public API
847  */
848
849 /*
850  * ao_fat_sync
851  *
852  * Flush any pending I/O to storage
853  */
854
855 void
856 ao_fat_sync(void)
857 {
858         if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
859                 return;
860         ao_fat_flush_fsinfo();
861         ao_bufio_flush();
862 }
863
864 /*
865  * ao_fat_full
866  *
867  * Returns TRUE if the filesystem cannot take
868  * more data
869  */
870
871 int8_t
872 ao_fat_full(void)
873 {
874         if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
875                 return 1;
876         return filesystem_full;
877 }
878
879 /*
880  * ao_fat_open
881  *
882  * Open an existing file.
883  */
884 int8_t
885 ao_fat_open(char name[11], uint8_t mode)
886 {
887         uint16_t                entry = 0;
888         struct ao_fat_dirent    dirent;
889
890         if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
891                 return -AO_FAT_EIO;
892
893         if (ao_file_opened)
894                 return -AO_FAT_EMFILE;
895         
896         while (ao_fat_readdir(&entry, &dirent)) {
897                 if (!memcmp(name, dirent.name, 11)) {
898                         if (AO_FAT_IS_DIR(dirent.attr))
899                                 return -AO_FAT_EISDIR;
900                         if (!AO_FAT_IS_FILE(dirent.attr))
901                                 return -AO_FAT_EPERM;
902                         if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
903                                 return -AO_FAT_EACCESS;
904                         ao_file_dirent = dirent;
905                         ao_fat_set_offset(0);
906                         ao_file_opened = 1;
907                         return AO_FAT_SUCCESS;
908                 }
909         }
910         return -AO_FAT_ENOENT;
911 }
912
913 /*
914  * ao_fat_creat
915  *
916  * Open and truncate an existing file or
917  * create a new file
918  */
919 int8_t
920 ao_fat_creat(char name[11])
921 {
922         uint16_t        entry;
923         int8_t          status;
924         uint8_t         *dent;
925
926         if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
927                 return -AO_FAT_EIO;
928
929         if (ao_file_opened)
930                 return -AO_FAT_EMFILE;
931
932         status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
933
934         switch (status) {
935         case -AO_FAT_SUCCESS:
936                 status = ao_fat_set_size(0);
937                 break;
938         case -AO_FAT_ENOENT:
939                 entry = 0;
940                 for (;;) {
941                         dent = ao_fat_root_get(entry);
942                         if (!dent) {
943                                 
944                                 if (ao_fat_root_extend(entry))
945                                         continue;
946                                 status = -AO_FAT_ENOSPC;
947                                 break;
948                         }
949                                 
950                         if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
951                                 ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
952                                 ao_fat_dirent_init(dent, entry,  &ao_file_dirent);
953                                 ao_fat_root_put(dent, entry, 1);
954                                 ao_file_opened = 1;
955                                 ao_fat_set_offset(0);
956                                 status = -AO_FAT_SUCCESS;
957                                 break;
958                         } else {
959                                 ao_fat_root_put(dent, entry, 0);
960                         }
961                         entry++;
962                 }
963         }
964         return status;
965 }
966
967 /*
968  * ao_fat_close
969  *
970  * Close the currently open file
971  */
972 int8_t
973 ao_fat_close(void)
974 {
975         if (!ao_file_opened)
976                 return -AO_FAT_EBADF;
977
978         memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
979         ao_file_offset = 0;
980         ao_file_cluster = 0;
981         ao_file_opened = 0;
982
983         ao_fat_sync();
984         return AO_FAT_SUCCESS;
985 }
986
987 /*
988  * ao_fat_map_current
989  *
990  * Map the sector pointed at by the current file offset
991  */
992
993 static void *
994 ao_fat_map_current(int len, cluster_offset_t *offsetp, cluster_offset_t *this_time)
995 {
996         cluster_offset_t        offset;
997         sector_t                sector;
998         void                    *buf;
999
1000         offset = ao_file_offset & SECTOR_MASK;
1001         sector = ao_fat_current_sector();
1002         if (sector == 0xffffffff)
1003                 return NULL;
1004         buf = ao_fat_sector_get(sector);
1005         if (offset + len < SECTOR_SIZE)
1006                 *this_time = len;
1007         else
1008                 *this_time = SECTOR_SIZE - offset;
1009         *offsetp = offset;
1010         return buf;
1011 }
1012
1013 /*
1014  * ao_fat_read
1015  *
1016  * Read from the file
1017  */
1018 int
1019 ao_fat_read(void *dst, int len)
1020 {
1021         uint8_t                 *dst_b = dst;
1022         cluster_offset_t        this_time;
1023         cluster_offset_t        offset;
1024         uint8_t                 *buf;
1025         int                     ret = 0;
1026
1027         if (!ao_file_opened)
1028                 return -AO_FAT_EBADF;
1029
1030         if (ao_file_offset + len > ao_file_dirent.size)
1031                 len = ao_file_dirent.size - ao_file_offset;
1032
1033         if (len < 0)
1034                 len = 0;
1035
1036         while (len) {
1037                 buf = ao_fat_map_current(len, &offset, &this_time);
1038                 if (!buf) {
1039                         ret = -AO_FAT_EIO;
1040                         break;
1041                 }
1042                 memcpy(dst_b, buf + offset, this_time);
1043                 ao_fat_sector_put(buf, 0);
1044
1045                 ret += this_time;
1046                 len -= this_time;
1047                 dst_b += this_time;
1048                 ao_fat_set_offset(ao_file_offset + this_time);
1049         }
1050         return ret;
1051 }
1052
1053 /*
1054  * ao_fat_write
1055  *
1056  * Write to the file, extended as necessary
1057  */
1058 int
1059 ao_fat_write(void *src, int len)
1060 {
1061         uint8_t         *src_b = src;
1062         uint16_t        this_time;
1063         uint16_t        offset;
1064         uint8_t         *buf;
1065         int             ret = 0;
1066
1067         if (!ao_file_opened)
1068                 return -AO_FAT_EBADF;
1069
1070         if (ao_file_offset + len > ao_file_dirent.size) {
1071                 ret = ao_fat_set_size(ao_file_offset + len);
1072                 if (ret < 0)
1073                         return ret;
1074         }
1075
1076         while (len) {
1077                 buf = ao_fat_map_current(len, &offset, &this_time);
1078                 if (!buf) {
1079                         ret = -AO_FAT_EIO;
1080                         break;
1081                 }
1082                 memcpy(buf + offset, src_b, this_time);
1083                 ao_fat_sector_put(buf, 1);
1084
1085                 ret += this_time;
1086                 len -= this_time;
1087                 src_b += this_time;
1088                 ao_fat_set_offset(ao_file_offset + this_time);
1089         }
1090         return ret;
1091 }
1092
1093 /*
1094  * ao_fat_seek
1095  *
1096  * Set the position for the next I/O operation
1097  * Note that this doesn't actually change the size
1098  * of the file if the requested position is beyond
1099  * the current file length, that would take a future
1100  * write
1101  */
1102 int32_t
1103 ao_fat_seek(int32_t pos, uint8_t whence)
1104 {
1105         uint32_t        new_offset = ao_file_offset;
1106
1107         if (!ao_file_opened)
1108                 return -AO_FAT_EBADF;
1109
1110         switch (whence) {
1111         case AO_FAT_SEEK_SET:
1112                 new_offset = pos;
1113                 break;
1114         case AO_FAT_SEEK_CUR:
1115                 new_offset += pos;
1116                 break;
1117         case AO_FAT_SEEK_END:
1118                 new_offset = ao_file_dirent.size + pos;
1119                 break;
1120         }
1121         ao_fat_set_offset(new_offset);
1122         return ao_file_offset;
1123 }
1124
1125 /*
1126  * ao_fat_unlink
1127  *
1128  * Remove a file from the directory, marking
1129  * all clusters as free
1130  */
1131 int8_t
1132 ao_fat_unlink(char name[11])
1133 {
1134         uint16_t                entry = 0;
1135         struct ao_fat_dirent    dirent;
1136
1137         if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1138                 return -AO_FAT_EIO;
1139
1140         while (ao_fat_readdir(&entry, &dirent)) {
1141                 if (memcmp(name, dirent.name, 11) == 0) {
1142                         uint8_t *next;
1143                         uint8_t *ent;
1144                         uint8_t delete;
1145
1146                         if (AO_FAT_IS_DIR(dirent.attr))
1147                                 return -AO_FAT_EISDIR;
1148                         if (!AO_FAT_IS_FILE(dirent.attr))
1149                                 return -AO_FAT_EPERM;
1150
1151                         ao_fat_free_cluster_chain(dirent.cluster);
1152                         next = ao_fat_root_get(dirent.entry + 1);
1153                         if (next && next[0] != AO_FAT_DENT_END)
1154                                 delete = AO_FAT_DENT_EMPTY;
1155                         else
1156                                 delete = AO_FAT_DENT_END;
1157                         if (next)
1158                                 ao_fat_root_put(next, dirent.entry + 1, 0);
1159                         ent = ao_fat_root_get(dirent.entry);
1160                         if (ent) {
1161                                 memset(ent, '\0', DIRENT_SIZE);
1162                                 *ent = delete;
1163                                 ao_fat_root_put(ent, dirent.entry, 1);
1164                         }
1165                         ao_bufio_flush();
1166                         return AO_FAT_SUCCESS;
1167                 }
1168         }
1169         return -AO_FAT_ENOENT;
1170 }
1171
1172 int8_t
1173 ao_fat_rename(char old[11], char new[11])
1174 {
1175         return -AO_FAT_EIO;
1176 }
1177
1178 int8_t
1179 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1180 {
1181         uint8_t *dent;
1182
1183         if (ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1184                 return -AO_FAT_EIO;
1185
1186         for (;;) {
1187                 dent = ao_fat_root_get(*entry);
1188                 if (!dent)
1189                         return -AO_FAT_EDIREOF;
1190
1191                 if (dent[0] == AO_FAT_DENT_END) {
1192                         ao_fat_root_put(dent, *entry, 0);
1193                         return -AO_FAT_EDIREOF;
1194                 }
1195                 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
1196                         ao_fat_dirent_init(dent, *entry, dirent);
1197                         ao_fat_root_put(dent, *entry, 0);
1198                         (*entry)++;
1199                         return AO_FAT_SUCCESS;
1200                 }
1201                 ao_fat_root_put(dent, *entry, 0);
1202                 (*entry)++;
1203         }
1204 }
1205
1206 #if FAT_COMMANDS
1207
1208 static const char *filesystem_errors[] = {
1209         [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally",
1210         [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error",
1211         [AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE] = "MBR signature invalid",
1212         [AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE] = "Unsupported paritition type",
1213         [AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION] = "Partition has zero sectors",
1214         [AO_FAT_FILESYSTEM_BOOT_READ_FAILURE] = "Boot block media read error",
1215         [AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE] = "Boot block signature invalid",
1216         [AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE] = "Sector size not 512",
1217 };
1218         
1219 static void
1220 ao_fat_mbr_cmd(void)
1221 {
1222         uint8_t status;
1223
1224         status = ao_fat_setup();
1225         if (status == AO_FAT_FILESYSTEM_SUCCESS) {
1226                 printf ("partition type: %02x\n", partition_type);
1227                 printf ("partition start: %08x\n", partition_start);
1228
1229                 printf ("partition end:   %08x\n", partition_end);
1230
1231                 printf ("fat32: %d\n", fat32);
1232                 printf ("sectors per cluster %d\n", sectors_per_cluster);
1233                 printf ("reserved sectors %d\n", reserved_sector_count);
1234                 printf ("number of FATs %d\n", number_fat);
1235                 printf ("root entries %d\n", root_entries);
1236                 printf ("sectors per fat %d\n", sectors_per_fat);
1237
1238                 printf ("fat  start %d\n", fat_start);
1239                 printf ("root start %d\n", root_start);
1240                 printf ("data start %d\n", data_start);
1241         } else {
1242                 printf ("FAT filesystem not available: %s\n", filesystem_errors[status]);
1243         }
1244 }
1245
1246 struct ao_fat_attr {
1247         uint8_t bit;
1248         char    label;
1249 };
1250
1251 static const struct ao_fat_attr ao_fat_attr[] = {
1252         { .bit = AO_FAT_FILE_READ_ONLY, .label = 'R' },
1253         { .bit = AO_FAT_FILE_HIDDEN, .label = 'H' },
1254         { .bit = AO_FAT_FILE_SYSTEM, .label = 'S' },
1255         { .bit = AO_FAT_FILE_VOLUME_LABEL, .label = 'V' },
1256         { .bit = AO_FAT_FILE_DIRECTORY, .label = 'D' },
1257         { .bit = AO_FAT_FILE_ARCHIVE, .label = 'A' },
1258 };
1259
1260 #define NUM_FAT_ATTR    (sizeof (ao_fat_attr) / sizeof (ao_fat_attr[0]))
1261
1262 static void
1263 ao_fat_list_cmd(void)
1264 {
1265         uint16_t                entry = 0;
1266         struct ao_fat_dirent    dirent;
1267         int                     i;
1268         int8_t                  status;
1269
1270         while ((status = ao_fat_readdir(&entry, &dirent)) == AO_FAT_SUCCESS) {
1271                 for (i = 0; i < 8; i++)
1272                         putchar(dirent.name[i]);
1273                 putchar('.');
1274                 for (; i < 11; i++)
1275                         putchar(dirent.name[i]);
1276                 for (i = 0; i < NUM_FAT_ATTR; i++)
1277                         putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' ');
1278                 printf (" @%08x %d\n", dirent.cluster, dirent.size);
1279         }
1280         if (status != -AO_FAT_EDIREOF)
1281                 printf ("readdir failed: %d\n", status);
1282 }
1283
1284 static uint8_t
1285 ao_fat_parse_name(char name[11])
1286 {
1287         uint8_t c;
1288
1289         name[0] = '\0';
1290         ao_cmd_white();
1291         c = 0;
1292         while (ao_cmd_lex_c != '\n') {
1293                 if (ao_cmd_lex_c == '.') {
1294                         for (; c < 8; c++)
1295                                 name[c] = ' ';
1296                 } else {
1297                         if (c < 11)
1298                                 name[c++] = ao_cmd_lex_c;
1299                 }
1300                 ao_cmd_lex();
1301         }
1302         while (c < 11)
1303                 name[c++] = ' ';
1304 }
1305
1306 static void
1307 ao_fat_show_cmd(void)
1308 {
1309         char            name[11];
1310         int8_t          status;
1311         int             cnt, i;
1312         char            buf[64];
1313
1314         ao_fat_parse_name(name);
1315         if (name[0] == '\0') {
1316                 ao_cmd_status = ao_cmd_syntax_error;
1317                 return;
1318         }
1319                 
1320         status = ao_fat_open(name, AO_FAT_OPEN_READ);
1321         if (status) {
1322                 printf ("Open failed: %d\n", status);
1323                 return;
1324         }
1325         while ((cnt = ao_fat_read(buf, sizeof(buf))) > 0) {
1326                 for (i = 0; i < cnt; i++)
1327                         putchar(buf[i]);
1328         }
1329         ao_fat_close();
1330 }
1331
1332 static void
1333 ao_fat_putchar(char c)
1334 {
1335 }
1336
1337 static void
1338 ao_fat_write_cmd(void)
1339 {
1340         char            name[11];
1341         int8_t          status;
1342         int             cnt, i;
1343         char            buf[64];
1344         char            c;
1345
1346         ao_fat_parse_name(name);
1347         if (name[0] == '\0') {
1348                 ao_cmd_status = ao_cmd_syntax_error;
1349                 return;
1350         }
1351                 
1352         status = ao_fat_creat(name);
1353         if (status) {
1354                 printf ("Open failed: %d\n", status);
1355                 return;
1356         }
1357         flush();
1358         while ((c = getchar()) != 4) {
1359                 if (c == '\r') c = '\n';
1360                 if (ao_echo()) {
1361                         if (c == '\n') putchar ('\r');
1362                         putchar(c); flush();
1363                 }
1364                 if (ao_fat_write(&c, 1) != 1) {
1365                         printf ("Write failure\n");
1366                         break;
1367                 }
1368         }
1369         ao_fat_close();
1370 }
1371
1372 static const struct ao_cmds ao_fat_cmds[] = {
1373         { ao_fat_mbr_cmd,       "M\0Show FAT MBR and other info" },
1374         { ao_fat_list_cmd,      "F\0List FAT directory" },
1375         { ao_fat_show_cmd,      "S <name>\0Show FAT file" },
1376         { ao_fat_write_cmd,     "W <name>\0Write FAT file (end with ^D)" },
1377         { 0, NULL },
1378 };
1379
1380 #endif
1381
1382 void
1383 ao_fat_init(void)
1384 {
1385         ao_bufio_init();
1386         ao_cmd_register(&ao_fat_cmds[0]);
1387 }