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