altos: Add FAT32 support. And lots more testing.
[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
578 static uint8_t
579 ao_fat_setup(void)
580 {
581         ao_bufio_setup();
582         
583         partition_type = partition_start = partition_end = 0;
584         sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
585         number_fat = root_entries = sectors_per_fat = 0;
586         number_cluster = fat_start = root_start = data_start = 0;
587         next_free = filesystem_full = 0;
588         fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
589         memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent));
590         ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0;
591         if (!ao_fat_setup_partition())
592                 return 0;
593         if (!ao_fat_setup_fs())
594                 return 0;
595         return 1;
596 }
597
598 /*
599  * Basic file operations
600  */
601
602 static uint32_t
603 ao_fat_current_sector(void)
604 {
605         cluster_t       cluster_offset;
606         uint32_t        sector_offset;
607         uint16_t        sector_index;
608         cluster_t       cluster;
609
610         if (ao_file_offset > ao_file_dirent.size)
611                 return 0xffffffff;
612
613         sector_offset = ao_file_offset >> SECTOR_SHIFT;
614
615         if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) {
616                 ao_file_cluster = ao_file_dirent.cluster;
617                 ao_file_cluster_offset = 0;
618         }
619
620         if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) {
621                 cluster_t       cluster_distance;
622
623                 cluster_offset = sector_offset / sectors_per_cluster;
624
625                 cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster;
626
627                 cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance);
628
629                 if (!ao_fat_cluster_valid(cluster))
630                         return 0xffffffff;
631                 ao_file_cluster = cluster;
632                 ao_file_cluster_offset = cluster_offset * bytes_per_cluster;
633         }
634
635         sector_index = sector_offset % sectors_per_cluster;
636         return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index;
637 }
638
639 static void
640 ao_fat_set_offset(uint32_t offset)
641 {
642         ao_file_offset = offset;
643 }
644
645 /*
646  * ao_fat_set_size
647  *
648  * Set the size of the current file, truncating or extending
649  * the cluster chain as needed
650  */
651 static int8_t
652 ao_fat_set_size(uint32_t size)
653 {
654         uint8_t         *dent;
655         cluster_t       first_cluster;
656         cluster_t       have_clusters, need_clusters;
657
658         if (size == ao_file_dirent.size)
659                 return AO_FAT_SUCCESS;
660
661         first_cluster = ao_file_dirent.cluster;
662         have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster;
663         need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
664
665         if (have_clusters != need_clusters) {
666                 if (ao_file_cluster && size >= ao_file_cluster_offset) {
667                         cluster_t       offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster;
668                         cluster_t       extra_clusters = need_clusters - offset_clusters;
669                         cluster_t       next_cluster;
670
671                         next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters);
672                         if (next_cluster == AO_FAT_BAD_CLUSTER)
673                                 return -AO_FAT_ENOSPC;
674                 } else {
675                         first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters);
676
677                         if (first_cluster == AO_FAT_BAD_CLUSTER)
678                                 return -AO_FAT_ENOSPC;
679                 }
680         }
681
682         /* Update the directory entry */
683         dent = ao_fat_root_get(ao_file_dirent.entry);
684         if (!dent)
685                 return -AO_FAT_EIO;
686         put_u32(dent + 0x1c, size);
687         put_u16(dent + 0x1a, first_cluster);
688         if (fat32)
689                 put_u16(dent + 0x14, first_cluster >> 16);
690         ao_fat_root_put(dent, ao_file_dirent.entry, 1);
691
692         ao_file_dirent.size = size;
693         ao_file_dirent.cluster = first_cluster;
694         return AO_FAT_SUCCESS;
695 }
696
697 /*
698  * ao_fat_root_init
699  *
700  * Initialize a root directory entry
701  */
702 void
703 ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
704 {
705         memset(dent, '\0', 0x20);
706         memmove(dent, name, 11);
707
708         dent[0x0b] = 0x00;
709         dent[0x0c] = 0x00;
710         dent[0x0d] = 0x00;
711
712         /* XXX fix time */
713         put_u16(dent + 0x0e, 0);
714         /* XXX fix date */
715         put_u16(dent + 0x10, 0);
716         /* XXX fix date */
717         put_u16(dent + 0x12, 0);
718
719         /* XXX fix time */
720         put_u16(dent + 0x16, 0);
721         /* XXX fix date */
722         put_u16(dent + 0x18, 0);
723
724         /* cluster number */
725         /* Low cluster bytes */
726         put_u16(dent + 0x1a, 0);
727         /* FAT32 high cluster bytes */
728         put_u16(dent + 0x14, 0);
729
730         /* size */
731         put_u32(dent + 0x1c, 0);
732 }
733
734
735 static void
736 ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent)
737 {
738         memcpy(dirent->name, dent + 0x00, 11);
739         dirent->attr = dent[0x0b];
740         dirent->size = get_u32(dent+0x1c);
741         dirent->cluster = get_u16(dent+0x1a);
742         if (fat32)
743                 dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
744         dirent->entry = entry;
745 }
746
747 /*
748  * ao_fat_flush_fsinfo
749  *
750  * Write out any fsinfo changes to disk
751  */
752
753 void
754 ao_fat_flush_fsinfo(void)
755 {
756         uint8_t *fsinfo;
757
758         if (!fat32)
759                 return;
760
761         if (!fsinfo_dirty)
762                 return;
763         fsinfo_dirty = 0;
764         if (!fsinfo_sector)
765                 return;
766
767         fsinfo = ao_fat_sector_get(fsinfo_sector);
768         if (fsinfo) {
769                 put_u32(fsinfo + 0x1e8, free_count);
770                 put_u32(fsinfo + 0x1ec, next_free);
771                 ao_fat_sector_put(fsinfo, 1);
772         }
773 }
774
775 /*
776  * Public API
777  */
778
779 /*
780  * ao_fat_open
781  *
782  * Open an existing file.
783  */
784 int8_t
785 ao_fat_open(char name[11], uint8_t mode)
786 {
787         uint16_t                entry = 0;
788         struct ao_fat_dirent    dirent;
789
790         if (ao_file_opened)
791                 return -AO_FAT_EMFILE;
792         
793         while (ao_fat_readdir(&entry, &dirent)) {
794                 if (!memcmp(name, dirent.name, 11)) {
795                         if (AO_FAT_IS_DIR(dirent.attr))
796                                 return -AO_FAT_EISDIR;
797                         if (!AO_FAT_IS_FILE(dirent.attr))
798                                 return -AO_FAT_EPERM;
799                         if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
800                                 return -AO_FAT_EACCESS;
801                         ao_file_dirent = dirent;
802                         ao_fat_set_offset(0);
803                         ao_file_opened = 1;
804                         return AO_FAT_SUCCESS;
805                 }
806         }
807         return -AO_FAT_ENOENT;
808 }
809
810 /*
811  * ao_fat_creat
812  *
813  * Open and truncate an existing file or
814  * create a new file
815  */
816 int8_t
817 ao_fat_creat(char name[11])
818 {
819         uint16_t        entry;
820         int8_t          status;
821         uint8_t         *dent;
822
823         if (ao_file_opened)
824                 return -AO_FAT_EMFILE;
825
826         status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
827
828         switch (status) {
829         case -AO_FAT_SUCCESS:
830                 status = ao_fat_set_size(0);
831                 break;
832         case -AO_FAT_ENOENT:
833                 entry = 0;
834                 for (;;) {
835                         dent = ao_fat_root_get(entry);
836                         if (!dent) {
837                                 
838                                 if (ao_fat_root_extend(entry))
839                                         continue;
840                                 status = -AO_FAT_ENOSPC;
841                                 break;
842                         }
843                                 
844                         if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
845                                 ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
846                                 ao_fat_dirent_init(dent, entry,  &ao_file_dirent);
847                                 ao_fat_root_put(dent, entry, 1);
848                                 ao_file_opened = 1;
849                                 ao_fat_set_offset(0);
850                                 status = -AO_FAT_SUCCESS;
851                                 break;
852                         } else {
853                                 ao_fat_root_put(dent, entry, 0);
854                         }
855                         entry++;
856                 }
857         }
858         return status;
859 }
860
861 /*
862  * ao_fat_close
863  *
864  * Close the currently open file
865  */
866 int8_t
867 ao_fat_close(void)
868 {
869         if (!ao_file_opened)
870                 return -AO_FAT_EBADF;
871
872         memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
873         ao_file_offset = 0;
874         ao_file_cluster = 0;
875         ao_file_opened = 0;
876
877         ao_fat_flush_fsinfo();
878         ao_bufio_flush();
879         return AO_FAT_SUCCESS;
880 }
881
882 /*
883  * ao_fat_read
884  *
885  * Read from the file
886  */
887 int
888 ao_fat_read(void *dst, int len)
889 {
890         uint8_t         *dst_b = dst;
891         uint32_t        sector;
892         uint16_t        this_time;
893         uint16_t        offset;
894         uint8_t         *buf;
895         int             ret = 0;
896
897         if (!ao_file_opened)
898                 return -AO_FAT_EBADF;
899
900         if (ao_file_offset + len > ao_file_dirent.size)
901                 len = ao_file_dirent.size - ao_file_offset;
902
903         if (len < 0)
904                 len = 0;
905
906         while (len) {
907                 offset = ao_file_offset & SECTOR_MASK;
908                 if (offset + len < SECTOR_SIZE)
909                         this_time = len;
910                 else
911                         this_time = SECTOR_SIZE - offset;
912
913                 sector = ao_fat_current_sector();
914                 if (sector == 0xffffffff)
915                         break;
916                 buf = ao_fat_sector_get(sector);
917                 if (!buf) {
918                         ret = -AO_FAT_EIO;
919                         break;
920                 }
921                 memcpy(dst_b, buf + offset, this_time);
922                 ao_fat_sector_put(buf, 0);
923
924                 ret += this_time;
925                 len -= this_time;
926                 dst_b += this_time;
927                 ao_fat_set_offset(ao_file_offset + this_time);
928         }
929         return ret;
930 }
931
932 /*
933  * ao_fat_write
934  *
935  * Write to the file, extended as necessary
936  */
937 int
938 ao_fat_write(void *src, int len)
939 {
940         uint8_t         *src_b = src;
941         uint32_t        sector;
942         uint16_t        this_time;
943         uint16_t        offset;
944         uint8_t         *buf;
945         int             ret = 0;
946
947         if (!ao_file_opened)
948                 return -AO_FAT_EBADF;
949
950         if (ao_file_offset + len > ao_file_dirent.size) {
951                 ret = ao_fat_set_size(ao_file_offset + len);
952                 if (ret < 0)
953                         return ret;
954         }
955
956         while (len) {
957                 offset = ao_file_offset & SECTOR_MASK;
958                 if (offset + len < SECTOR_SIZE)
959                         this_time = len;
960                 else
961                         this_time = SECTOR_SIZE - offset;
962
963                 sector = ao_fat_current_sector();
964                 if (sector == 0xffffffff)
965                         break;
966                 buf = ao_fat_sector_get(sector);
967                 if (!buf) {
968                         ret = -AO_FAT_EIO;
969                         break;
970                 }
971                 memcpy(buf + offset, src_b, this_time);
972                 ao_fat_sector_put(buf, 1);
973
974                 ret += this_time;
975                 len -= this_time;
976                 src_b += this_time;
977                 ao_fat_set_offset(ao_file_offset + this_time);
978         }
979         return ret;
980 }
981
982 /*
983  * ao_fat_seek
984  *
985  * Set the position for the next I/O operation
986  * Note that this doesn't actually change the size
987  * of the file if the requested position is beyond
988  * the current file length, that would take a future
989  * write
990  */
991 int32_t
992 ao_fat_seek(int32_t pos, uint8_t whence)
993 {
994         uint32_t        new_offset = ao_file_offset;
995
996         if (!ao_file_opened)
997                 return -AO_FAT_EBADF;
998
999         switch (whence) {
1000         case AO_FAT_SEEK_SET:
1001                 new_offset = pos;
1002                 break;
1003         case AO_FAT_SEEK_CUR:
1004                 new_offset += pos;
1005                 break;
1006         case AO_FAT_SEEK_END:
1007                 new_offset = ao_file_dirent.size + pos;
1008                 break;
1009         }
1010         ao_fat_set_offset(new_offset);
1011         return ao_file_offset;
1012 }
1013
1014 /*
1015  * ao_fat_unlink
1016  *
1017  * Remove a file from the directory, marking
1018  * all clusters as free
1019  */
1020 int8_t
1021 ao_fat_unlink(char name[11])
1022 {
1023         uint16_t                entry = 0;
1024         struct ao_fat_dirent    dirent;
1025
1026         while (ao_fat_readdir(&entry, &dirent)) {
1027                 if (memcmp(name, dirent.name, 11) == 0) {
1028                         uint8_t *next;
1029                         uint8_t *ent;
1030                         uint8_t delete;
1031
1032                         if (AO_FAT_IS_DIR(dirent.attr))
1033                                 return -AO_FAT_EISDIR;
1034                         if (!AO_FAT_IS_FILE(dirent.attr))
1035                                 return -AO_FAT_EPERM;
1036
1037                         ao_fat_free_cluster_chain(dirent.cluster);
1038                         next = ao_fat_root_get(dirent.entry + 1);
1039                         if (next && next[0] != AO_FAT_DENT_END)
1040                                 delete = AO_FAT_DENT_EMPTY;
1041                         else
1042                                 delete = AO_FAT_DENT_END;
1043                         if (next)
1044                                 ao_fat_root_put(next, dirent.entry + 1, 0);
1045                         ent = ao_fat_root_get(dirent.entry);
1046                         if (ent) {
1047                                 memset(ent, '\0', DIRENT_SIZE);
1048                                 *ent = delete;
1049                                 ao_fat_root_put(ent, dirent.entry, 1);
1050                         }
1051                         ao_bufio_flush();
1052                         return AO_FAT_SUCCESS;
1053                 }
1054         }
1055         return -AO_FAT_ENOENT;
1056 }
1057
1058 int8_t
1059 ao_fat_rename(char old[11], char new[11])
1060 {
1061         return -AO_FAT_EIO;
1062 }
1063
1064 int8_t
1065 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1066 {
1067         uint8_t *dent;
1068
1069         for (;;) {
1070                 dent = ao_fat_root_get(*entry);
1071                 if (!dent)
1072                         return 0;
1073
1074                 if (dent[0] == AO_FAT_DENT_END) {
1075                         ao_fat_root_put(dent, *entry, 0);
1076                         return 0;
1077                 }
1078                 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
1079                         ao_fat_dirent_init(dent, *entry, dirent);
1080                         ao_fat_root_put(dent, *entry, 0);
1081                         (*entry)++;
1082                         return 1;
1083                 }
1084                 ao_fat_root_put(dent, *entry, 0);
1085                 (*entry)++;
1086         }
1087 }
1088
1089 static void
1090 ao_fat_list(void)
1091 {
1092         uint16_t                entry = 0;
1093         struct ao_fat_dirent    dirent;
1094
1095         while (ao_fat_readdir(&entry, &dirent)) {
1096                 printf ("%-8.8s.%-3.3s %02x %04x %d\n",
1097                         dirent.name,
1098                         dirent.name + 8,
1099                         dirent.attr,
1100                         dirent.cluster,
1101                         dirent.size);
1102         }
1103 }
1104
1105 static void
1106 ao_fat_test(void)
1107 {
1108         ao_fat_setup();
1109         ao_fat_list();
1110 }
1111
1112 static const struct ao_cmds ao_fat_cmds[] = {
1113         { ao_fat_test,  "F\0Test FAT" },
1114         { 0, NULL },
1115 };
1116
1117 void
1118 ao_fat_init(void)
1119 {
1120         ao_bufio_init();
1121         ao_cmd_register(&ao_fat_cmds[0]);
1122 }