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