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