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