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