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