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