2 * Copyright © 2013 Keith Packard <keithp@keithp.com>
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.
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.
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.
25 /* Partition information, sector numbers */
27 static uint8_t partition_type;
28 static uint32_t partition_start, partition_end;
30 #define SECTOR_SIZE 512
31 #define SECTOR_MASK (SECTOR_SIZE - 1)
32 #define SECTOR_SHIFT 9
34 #define DIRENT_SIZE 32
36 /* File system parameters */
37 static uint8_t sectors_per_cluster;
38 static uint32_t bytes_per_cluster;
39 static uint16_t reserved_sector_count;
40 static uint8_t number_fat;
41 static uint16_t root_entries;
42 static uint16_t sectors_per_fat;
43 static uint16_t number_cluster;
44 static uint32_t fat_start;
45 static uint32_t root_start;
46 static uint32_t data_start;
47 static uint16_t first_free_cluster;
50 * Deal with LSB FAT data structures
53 get_u32(uint8_t *base)
55 return ((uint32_t) base[0] |
56 ((uint32_t) base[1] << 8) |
57 ((uint32_t) base[2] << 16) |
58 ((uint32_t) base[3] << 24));
62 put_u32(uint8_t *base, uint32_t value)
66 base[2] = value >> 16;
67 base[3] = value >> 24;
71 get_u16(uint8_t *base)
73 return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
77 put_u16(uint8_t *base, uint16_t value)
84 ao_fat_cluster_valid(uint16_t cluster)
86 return (2 <= cluster && cluster < number_cluster);
89 /* Start using a sector */
91 ao_fat_sector_get(uint32_t sector)
93 sector += partition_start;
94 if (sector >= partition_end)
96 return ao_bufio_get(sector);
99 /* Finish using a sector, 'w' is 1 if modified */
100 #define ao_fat_sector_put(b,w) ao_bufio_put(b,w)
102 /* Start using a root directory entry */
104 ao_fat_root_get(uint16_t e)
106 uint32_t byte = e * DIRENT_SIZE;
107 uint32_t sector = byte >> SECTOR_SHIFT;
108 uint16_t offset = byte & SECTOR_MASK;
111 buf = ao_fat_sector_get(root_start + sector);
117 /* Finish using a root directory entry, 'w' is 1 if modified */
119 ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write)
121 uint16_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
122 uint8_t *buf = root - offset;
124 ao_fat_sector_put(buf, write);
127 /* Get the next cluster entry in the chain */
129 ao_fat_entry_read(uint16_t cluster)
136 if (!ao_fat_cluster_valid(cluster))
139 sector = cluster >> (SECTOR_SHIFT - 1);
140 offset = (cluster << 1) & SECTOR_MASK;
141 buf = ao_fat_sector_get(fat_start + sector);
144 ret = get_u16(buf + offset);
145 ao_fat_sector_put(buf, 0);
149 /* Replace the referenced cluster entry in the chain with
150 * 'new_value'. Return the previous value.
153 ao_fat_entry_replace(uint16_t cluster, uint16_t new_value)
161 if (!ao_fat_cluster_valid(cluster))
164 sector = cluster >> (SECTOR_SHIFT - 1);
165 offset = (cluster << 1) & SECTOR_MASK;
166 buf = ao_fat_sector_get(fat_start + sector);
169 ret = get_u16(buf + offset);
170 put_u16(buf + offset, new_value);
171 ao_fat_sector_put(buf, 1);
174 * Keep the other FATs in sync
176 for (other_fats = 1; other_fats < number_fat; other_fats++) {
177 buf = ao_fat_sector_get(fat_start + other_fats * sectors_per_fat + sector);
179 put_u16(buf + offset, new_value);
180 ao_fat_sector_put(buf, 1);
188 * Walk a cluster chain and mark
189 * all of them as free
192 ao_fat_free_cluster_chain(uint16_t cluster)
194 while (ao_fat_cluster_valid(cluster)) {
195 if (cluster < first_free_cluster)
196 first_free_cluster = cluster;
197 cluster = ao_fat_entry_replace(cluster, 0x0000);
202 * ao_fat_cluster_seek
204 * The basic file system operation -- map a file cluster index to a
205 * partition cluster number. Done by computing the cluster number and
206 * then walking that many clusters from the first cluster. Returns
207 * 0xffff if we walk off the end of the file or the cluster chain
211 ao_fat_cluster_seek(uint16_t cluster, uint16_t distance)
214 cluster = ao_fat_entry_read(cluster);
215 if (!ao_fat_cluster_valid(cluster))
223 * ao_fat_setup_partition
225 * Load the boot block and find the first partition
228 ao_fat_setup_partition(void)
232 uint32_t partition_size;
234 mbr = ao_bufio_get(0);
238 /* Check the signature */
239 if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
240 printf ("Invalid MBR signature %02x %02x\n",
241 mbr[0x1fe], mbr[0x1ff]);
242 ao_bufio_put(mbr, 0);
246 /* Check to see if it's actually a boot block, in which
247 * case it's presumably not a paritioned device
250 if (mbr[0] == 0xeb) {
252 partition_size = get_u16(mbr + 0x13);
253 if (partition_size == 0)
254 partition_size = get_u32(mbr + 0x20);
256 /* Just use the first partition */
257 partition = &mbr[0x1be];
259 partition_type = partition[4];
260 switch (partition_type) {
261 case 4: /* FAT16 up to 32M */
262 case 6: /* FAT16 over 32M */
264 case 0x0b: /* FAT32 up to 2047GB */
265 case 0x0c: /* FAT32 LBA */
268 printf ("Invalid partition type %02x\n", partition_type);
269 ao_bufio_put(mbr, 0);
273 partition_start = get_u32(partition+8);
274 partition_size = get_u32(partition+12);
275 if (partition_size == 0) {
276 printf ("Zero-sized partition\n");
277 ao_bufio_put(mbr, 0);
281 partition_end = partition_start + partition_size;
282 printf ("Partition type %02x start %08x end %08x\n",
283 partition_type, partition_start, partition_end);
284 ao_bufio_put(mbr, 0);
289 ao_fat_setup_fs(void)
291 uint8_t *boot = ao_fat_sector_get(0);
292 uint32_t data_sectors;
297 /* Check the signature */
298 if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
299 printf ("Invalid BOOT signature %02x %02x\n",
300 boot[0x1fe], boot[0x1ff]);
301 ao_fat_sector_put(boot, 0);
305 /* Check the sector size */
306 if (get_u16(boot + 0xb) != SECTOR_SIZE) {
307 printf ("Invalid sector size %d\n",
308 get_u16(boot + 0xb));
309 ao_fat_sector_put(boot, 0);
313 sectors_per_cluster = boot[0xd];
314 bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
315 reserved_sector_count = get_u16(boot+0xe);
316 number_fat = boot[0x10];
317 root_entries = get_u16(boot + 0x11);
318 sectors_per_fat = get_u16(boot+0x16);
320 fat_start = reserved_sector_count;;
321 root_start = fat_start + number_fat * sectors_per_fat;
322 data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
324 data_sectors = (partition_end - partition_start) - data_start;
326 number_cluster = data_sectors / sectors_per_cluster;
328 printf ("sectors per cluster %d\n", sectors_per_cluster);
329 printf ("reserved sectors %d\n", reserved_sector_count);
330 printf ("number of FATs %d\n", number_fat);
331 printf ("root entries %d\n", root_entries);
332 printf ("sectors per fat %d\n", sectors_per_fat);
334 printf ("fat start %d\n", fat_start);
335 printf ("root start %d\n", root_start);
336 printf ("data start %d\n", data_start);
338 ao_fat_sector_put(boot, 0);
346 if (!ao_fat_setup_partition())
348 check_bufio("partition setup");
349 if (!ao_fat_setup_fs())
351 check_bufio("fs setup");
356 * Basic file operations
359 static struct ao_fat_dirent ao_file_dirent;
360 static uint32_t ao_file_offset;
361 static uint32_t ao_file_cluster_offset;
362 static uint16_t ao_file_cluster;
363 static uint8_t ao_file_opened;
366 ao_fat_current_sector(void)
368 uint16_t cluster_offset;
369 uint32_t sector_offset;
370 uint16_t sector_index;
373 if (ao_file_offset > ao_file_dirent.size)
376 sector_offset = ao_file_offset >> SECTOR_SHIFT;
378 if (!ao_file_cluster || ao_file_offset < ao_file_cluster_offset) {
379 ao_file_cluster = ao_file_dirent.cluster;
380 ao_file_cluster_offset = 0;
383 if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) {
384 uint16_t cluster_distance;
386 cluster_offset = sector_offset / sectors_per_cluster;
388 cluster_distance = cluster_offset - ao_file_cluster_offset / bytes_per_cluster;
390 cluster = ao_fat_cluster_seek(ao_file_cluster, cluster_distance);
392 if (!ao_fat_cluster_valid(cluster))
394 ao_file_cluster = cluster;
395 ao_file_cluster_offset = cluster_offset * bytes_per_cluster;
398 sector_index = sector_offset % sectors_per_cluster;
399 return data_start + (uint32_t) (ao_file_cluster-2) * sectors_per_cluster + sector_index;
403 ao_fat_set_offset(uint32_t offset)
405 ao_file_offset = offset;
411 * Set the size of the current file, truncating or extending
412 * the cluster chain as needed
415 ao_fat_set_size(uint32_t size)
417 uint16_t clear_cluster = 0;
419 uint16_t first_cluster;
421 first_cluster = ao_file_dirent.cluster;
422 if (size == ao_file_dirent.size)
423 return AO_FAT_SUCCESS;
425 clear_cluster = ao_file_dirent.cluster;
431 new_num = (size + bytes_per_cluster - 1) / bytes_per_cluster;
432 old_num = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster;
433 if (new_num < old_num) {
434 uint16_t last_cluster;
436 /* Go find the last cluster we want to preserve in the file */
437 last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1);
439 /* Rewrite that cluster entry with 0xffff to mark the end of the chain */
440 clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff);
441 } else if (new_num > old_num) {
444 uint16_t last_cluster;
445 uint16_t highest_allocated = 0;
448 last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1);
452 if (first_free_cluster < 2 || number_cluster <= first_free_cluster)
453 first_free_cluster = 2;
455 /* See if there are enough free clusters in the file system */
456 need = new_num - old_num;
458 #define loop_cluster for (free = first_free_cluster; need > 0;)
459 #define next_cluster \
460 if (++free == number_cluster) \
462 if (free == first_free_cluster) \
466 if (!ao_fat_entry_read(free))
470 /* Still need some, tell the user that we've failed */
472 return -AO_FAT_ENOSPC;
474 /* Now go allocate those clusters */
475 need = new_num - old_num;
477 if (!ao_fat_entry_read(free)) {
478 if (free > highest_allocated)
479 highest_allocated = free;
481 ao_fat_entry_replace(last_cluster, free);
483 first_cluster = free;
489 first_free_cluster = highest_allocated + 1;
490 if (first_free_cluster >= number_cluster)
491 first_free_cluster = 2;
493 /* Mark the new end of the chain */
494 ao_fat_entry_replace(last_cluster, 0xffff);
498 /* Deallocate clusters off the end of the file */
499 if (ao_fat_cluster_valid(clear_cluster))
500 ao_fat_free_cluster_chain(clear_cluster);
502 /* Update the directory entry */
503 dent = ao_fat_root_get(ao_file_dirent.entry);
506 put_u32(dent + 0x1c, size);
507 put_u16(dent + 0x1a, first_cluster);
508 ao_fat_root_put(dent, ao_file_dirent.entry, 1);
509 ao_file_dirent.size = size;
510 ao_file_dirent.cluster = first_cluster;
511 return AO_FAT_SUCCESS;
517 * Initialize a root directory entry
520 ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
522 memset(dent, '\0', 0x20);
523 memmove(dent, name, 11);
530 put_u16(dent + 0x0e, 0);
532 put_u16(dent + 0x10, 0);
534 put_u16(dent + 0x12, 0);
537 put_u16(dent + 0x16, 0);
539 put_u16(dent + 0x18, 0);
542 /* Low cluster bytes */
543 put_u16(dent + 0x1a, 0);
544 /* FAT32 high cluster bytes */
545 put_u16(dent + 0x14, 0);
548 put_u32(dent + 0x1c, 0);
553 ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent)
555 memcpy(dirent->name, dent + 0x00, 11);
556 dirent->attr = dent[0x0b];
557 dirent->size = get_u32(dent+0x1c);
558 dirent->cluster = get_u16(dent+0x1a);
559 dirent->entry = entry;
569 * Open an existing file.
572 ao_fat_open(char name[11], uint8_t mode)
575 struct ao_fat_dirent dirent;
578 return -AO_FAT_EMFILE;
580 while (ao_fat_readdir(&entry, &dirent)) {
581 if (!memcmp(name, dirent.name, 11)) {
582 if (AO_FAT_IS_DIR(dirent.attr))
583 return -AO_FAT_EISDIR;
584 if (!AO_FAT_IS_FILE(dirent.attr))
585 return -AO_FAT_EPERM;
586 if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
587 return -AO_FAT_EACCESS;
588 ao_file_dirent = dirent;
589 ao_fat_set_offset(0);
591 return AO_FAT_SUCCESS;
594 return -AO_FAT_ENOENT;
600 * Open and truncate an existing file or
604 ao_fat_creat(char name[11])
610 return -AO_FAT_EMFILE;
612 status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
615 case -AO_FAT_SUCCESS:
616 status = ao_fat_set_size(0);
619 for (entry = 0; entry < root_entries; entry++) {
620 uint8_t *dent = ao_fat_root_get(entry);
623 status = -AO_FAT_EIO;
624 ao_fat_root_put(dent, entry, 0);
628 if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
629 ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
630 ao_fat_dirent_init(dent, entry, &ao_file_dirent);
631 ao_fat_root_put(dent, entry, 1);
633 ao_fat_set_offset(0);
634 status = -AO_FAT_SUCCESS;
637 ao_fat_root_put(dent, entry, 0);
640 if (entry == root_entries)
641 status = -AO_FAT_ENOSPC;
649 * Close the currently open file
655 return -AO_FAT_EBADF;
657 memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
662 return AO_FAT_SUCCESS;
671 ao_fat_read(void *dst, int len)
673 uint8_t *dst_b = dst;
681 return -AO_FAT_EBADF;
683 if (ao_file_offset + len > ao_file_dirent.size)
684 len = ao_file_dirent.size - ao_file_offset;
690 offset = ao_file_offset & SECTOR_MASK;
691 if (offset + len < SECTOR_SIZE)
694 this_time = SECTOR_SIZE - offset;
696 sector = ao_fat_current_sector();
697 if (sector == 0xffffffff)
699 buf = ao_fat_sector_get(sector);
704 memcpy(dst_b, buf + offset, this_time);
705 ao_fat_sector_put(buf, 0);
710 ao_fat_set_offset(ao_file_offset + this_time);
718 * Write to the file, extended as necessary
721 ao_fat_write(void *src, int len)
723 uint8_t *src_b = src;
731 return -AO_FAT_EBADF;
733 if (ao_file_offset + len > ao_file_dirent.size) {
734 ret = ao_fat_set_size(ao_file_offset + len);
740 offset = ao_file_offset & SECTOR_MASK;
741 if (offset + len < SECTOR_SIZE)
744 this_time = SECTOR_SIZE - offset;
746 sector = ao_fat_current_sector();
747 if (sector == 0xffffffff)
749 buf = ao_fat_sector_get(sector);
754 memcpy(buf + offset, src_b, this_time);
755 ao_fat_sector_put(buf, 1);
760 ao_fat_set_offset(ao_file_offset + this_time);
768 * Set the position for the next I/O operation
769 * Note that this doesn't actually change the size
770 * of the file if the requested position is beyond
771 * the current file length, that would take a future
775 ao_fat_seek(int32_t pos, uint8_t whence)
777 uint32_t new_offset = ao_file_offset;
780 return -AO_FAT_EBADF;
783 case AO_FAT_SEEK_SET:
786 case AO_FAT_SEEK_CUR:
789 case AO_FAT_SEEK_END:
790 new_offset = ao_file_dirent.size + pos;
793 ao_fat_set_offset(new_offset);
794 return ao_file_offset;
800 * Remove a file from the directory, marking
801 * all clusters as free
804 ao_fat_unlink(char name[11])
807 struct ao_fat_dirent dirent;
809 while (ao_fat_readdir(&entry, &dirent)) {
810 if (memcmp(name, dirent.name, 11) == 0) {
815 if (AO_FAT_IS_DIR(dirent.attr))
816 return -AO_FAT_EISDIR;
817 if (!AO_FAT_IS_FILE(dirent.attr))
818 return -AO_FAT_EPERM;
820 ao_fat_free_cluster_chain(dirent.cluster);
821 next = ao_fat_root_get(dirent.entry + 1);
822 if (next && next[0] != AO_FAT_DENT_END)
823 delete = AO_FAT_DENT_EMPTY;
825 delete = AO_FAT_DENT_END;
827 ao_fat_root_put(next, dirent.entry + 1, 0);
828 ent = ao_fat_root_get(dirent.entry);
830 memset(ent, '\0', DIRENT_SIZE);
832 ao_fat_root_put(ent, dirent.entry, 1);
835 return AO_FAT_SUCCESS;
838 return -AO_FAT_ENOENT;
842 ao_fat_rename(char old[11], char new[11])
848 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
852 if (*entry >= root_entries)
855 dent = ao_fat_root_get(*entry);
857 if (dent[0] == AO_FAT_DENT_END) {
858 ao_fat_root_put(dent, *entry, 0);
861 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
862 ao_fat_dirent_init(dent, *entry, dirent);
863 ao_fat_root_put(dent, *entry, 0);
867 ao_fat_root_put(dent, *entry, 0);
876 struct ao_fat_dirent dirent;
878 while (ao_fat_readdir(&entry, &dirent)) {
879 printf ("%-8.8s.%-3.3s %02x %04x %d\n",
895 static const struct ao_cmds ao_fat_cmds[] = {
896 { ao_fat_test, "F\0Test FAT" },
904 ao_cmd_register(&ao_fat_cmds[0]);