1 /*
2  * Copyright (c) 2015 Steve White
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include <lk/err.h>
10 #include <lib/bio.h>
11 #include <lib/fs.h>
12 #include <lk/trace.h>
13 #include <lk/debug.h>
14 #include <malloc.h>
15 #include <string.h>
16 #include <endian.h>
17 
18 #include "fat32_priv.h"
19 #include "fat_fs.h"
20 
fat32_dump(fat_fs_t * fat)21 static void fat32_dump(fat_fs_t *fat) {
22     printf("bytes_per_sector=%i\n", fat->bytes_per_sector);
23     printf("sectors_per_cluster=%i\n", fat->sectors_per_cluster);
24     printf("bytes_per_cluster=%i\n", fat->bytes_per_cluster);
25     printf("reserved_sectors=%i\n", fat->reserved_sectors);
26     printf("fat_bits=%i\n", fat->fat_bits);
27     printf("fat_count=%i\n", fat->fat_count);
28     printf("sectors_per_fat=%i\n", fat->sectors_per_fat);
29     printf("total_sectors=%i\n", fat->total_sectors);
30     printf("active_fat=%i\n", fat->active_fat);
31     printf("data_start=%i\n", fat->data_start);
32     printf("total_clusters=%i\n", fat->total_clusters);
33     printf("root_cluster=%i\n", fat->root_cluster);
34     printf("root_entries=%i\n", fat->root_entries);
35     printf("root_start=%i\n", fat->root_start);
36 }
37 
fat32_mount(bdev_t * dev,fscookie ** cookie)38 status_t fat32_mount(bdev_t *dev, fscookie **cookie) {
39     status_t result = NO_ERROR;
40 
41     if (!dev)
42         return ERR_NOT_VALID;
43 
44     uint8_t *bs = malloc(512);
45     int err = bio_read(dev, bs, 1024, 512);
46     if (err < 0) {
47         result = ERR_GENERIC;
48         goto end;
49     }
50 
51     if (((bs[0x1fe] != 0x55) || (bs[0x1ff] != 0xaa)) && (bs[0x15] == 0xf8)) {
52         printf("missing boot signature\n");
53         result = ERR_NOT_VALID;
54         goto end;
55     }
56 
57     fat_fs_t *fat = malloc(sizeof(fat_fs_t));
58     fat->lba_start = 1024;
59     fat->dev = dev;
60 
61     fat->bytes_per_sector = fat_read16(bs,0xb);
62     if ((fat->bytes_per_sector != 0x200) && (fat->bytes_per_sector != 0x400) && (fat->bytes_per_sector != 0x800)) {
63         printf("unsupported sector size (%x)\n", fat->bytes_per_sector);
64         result = ERR_NOT_VALID;
65         goto end;
66     }
67 
68     fat->sectors_per_cluster = bs[0xd];
69     switch (fat->sectors_per_cluster) {
70         case 1:
71         case 2:
72         case 4:
73         case 8:
74         case 0x10:
75         case 0x20:
76         case 0x40:
77         case 0x80:
78             break;
79         default:
80             printf("unsupported sectors/cluster (%x)\n", fat->sectors_per_cluster);
81             result = ERR_NOT_VALID;
82             goto end;
83     }
84 
85     fat->reserved_sectors = fat_read16(bs, 0xe);
86     fat->fat_count = bs[0x10];
87 
88     if ((fat->fat_count == 0) || (fat->fat_count > 8)) {
89         printf("unreasonable FAT count (%x)\n", fat->fat_count);
90         result = ERR_NOT_VALID;
91         goto end;
92     }
93 
94     if (bs[0x15] != 0xf8) {
95         printf("unsupported media descriptor byte (%x)\n", bs[0x15]);
96         result = ERR_NOT_VALID;
97         goto end;
98     }
99 
100     fat->sectors_per_fat = fat_read16(bs, 0x16);
101     if (fat->sectors_per_fat == 0) {
102         fat->fat_bits = 32;
103         fat->sectors_per_fat = fat_read32(bs,0x24);
104         fat->total_sectors = fat_read32(bs,0x20);
105         fat->active_fat = (bs[0x28] & 0x80) ? 0 : (bs[0x28] & 0xf);
106         fat->data_start = fat->reserved_sectors + (fat->fat_count * fat->sectors_per_fat);
107         fat->total_clusters = (fat->total_sectors - fat->data_start) / fat->sectors_per_cluster;
108 
109         // In FAT32, root directory appears in data area on given cluster and can be a cluster chain.
110         fat->root_cluster = fat_read32(bs,0x2c);
111         fat->root_start = 0;
112         if (fat->root_cluster >= fat->total_clusters) {
113             printf("root cluster too large (%x > %x)\n", fat->root_cluster, fat->total_clusters);
114             result = ERR_NOT_VALID;
115             goto end;
116         }
117         fat->root_entries = 0;
118     } else {
119         if (fat->fat_count != 2) {
120             printf("illegal FAT count (%x)\n", fat->fat_count);
121             result = ERR_NOT_VALID;
122             goto end;
123         }
124 
125         // On a FAT 12 or FAT 16 volumes the root directory is at a fixed position immediately after the File Allocation Tables
126         fat->root_cluster = 0;
127         fat->root_entries = fat_read16(bs,0x11);
128         if (fat->root_entries % (fat->bytes_per_sector / 0x20)) {
129             printf("illegal number of root entries (%x)\n", fat->root_entries);
130             result = ERR_NOT_VALID;
131             goto end;
132         }
133 
134         fat->total_sectors = fat_read16(bs,0x13);
135         if (fat->total_sectors == 0) {
136             fat->total_sectors = fat_read32(bs,0x20);
137         }
138 
139         fat->root_start = fat->reserved_sectors + fat->fat_count * fat->sectors_per_fat;
140         fat->data_start = fat->root_start + fat->root_entries * 0x20 / fat->bytes_per_sector;
141         fat->total_clusters = (fat->total_sectors - fat->data_start) / fat->sectors_per_cluster;
142 
143         if (fat->total_clusters < 0xff2) {
144             printf("small FAT12, not supported\n");
145             result = ERR_NOT_VALID;
146             goto end;
147         }
148         fat->fat_bits = 16;
149     }
150 
151     fat->bytes_per_cluster = fat->sectors_per_cluster * fat->bytes_per_sector;
152     fat->cache = bcache_create(fat->dev, fat->bytes_per_sector, 4);
153 
154     *cookie = (fscookie *)fat;
155 end:
156     free(bs);
157     return result;
158 }
159 
fat32_unmount(fscookie * cookie)160 status_t fat32_unmount(fscookie *cookie) {
161     fat_fs_t *fat = (fat_fs_t *)cookie;
162     bcache_destroy(fat->cache);
163     free(fat);
164     return NO_ERROR;
165 }
166 
167 static const struct fs_api fat32_api = {
168     .mount = fat32_mount,
169     .unmount = fat32_unmount,
170     .open = fat32_open_file,
171     .stat = fat32_stat_file,
172     .read = fat32_read_file,
173     .close = fat32_close_file,
174 };
175 
176 STATIC_FS_IMPL(fat32, &fat32_api);
177