1 /*
2 * Copyright (c) 2015 Steve White
3 * Copyright (c) 2022 Travis Geiselbrecht
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9
10 #include <lk/err.h>
11 #include <lib/bio.h>
12 #include <lib/fs.h>
13 #include <lk/trace.h>
14 #include <lk/debug.h>
15 #include <lk/cpp.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <endian.h>
19
20 #include "fat_priv.h"
21 #include "fat_fs.h"
22 #include "file.h"
23 #include "dir.h"
24
25 #define LOCAL_TRACE FAT_GLOBAL_TRACE(0)
26
fat_dump(fat_fs * fat)27 __NO_INLINE static void fat_dump(fat_fs *fat) {
28 const auto info = fat->info();
29 printf("bytes_per_sector %u\n", info.bytes_per_sector);
30 printf("sectors_per_cluster %u\n", info.sectors_per_cluster);
31 printf("bytes_per_cluster %u\n", info.bytes_per_cluster);
32 printf("reserved_sectors %u\n", info.reserved_sectors);
33 printf("fat_bits %u\n", info.fat_bits);
34 printf("fat_count %u\n", info.fat_count);
35 printf("sectors_per_fat %u\n", info.sectors_per_fat);
36 printf("total_sectors %u\n", info.total_sectors);
37 printf("active_fat %u\n", info.active_fat);
38 printf("data_start_sector %u\n", info.data_start_sector);
39 printf("total_clusters %u\n", info.total_clusters);
40 printf("root_cluster %u\n", info.root_cluster);
41 printf("root_entries %u\n", info.root_entries);
42 printf("root_start_sector %u\n", info.root_start_sector);
43 printf("root_dir_sectors %u\n", info.root_dir_sectors);
44 }
45
46 fat_fs::fat_fs() = default;
47 fat_fs::~fat_fs() = default;
48
mount(bdev_t * dev,fscookie ** cookie)49 status_t fat_fs::mount(bdev_t *dev, fscookie **cookie) {
50 status_t result = NO_ERROR;
51
52 if (!dev)
53 return ERR_NOT_VALID;
54
55 uint8_t *bs = (uint8_t *)malloc(512);
56 if (!bs) {
57 return ERR_NO_MEMORY;
58 }
59
60 // free the block on the way out of the function
61 auto ac = lk::make_auto_call([&]() { free(bs); });
62
63 ssize_t err = bio_read(dev, bs, 0, 512);
64 if (err < 0) {
65 return err;
66 }
67
68 if (((bs[0x1fe] != 0x55) || (bs[0x1ff] != 0xaa)) && (bs[0x15] == 0xf8)) {
69 printf("missing boot signature\n");
70 return ERR_NOT_VALID;
71 }
72
73 // allocate a structure, all fields implicity zeroed
74 auto *fat = new fat_fs;
75 if (!fat) {
76 return ERR_NO_MEMORY;
77 }
78 fat->dev_ = dev;
79
80 // if we early terminate, free the fat structure
81 auto ac2 = lk::make_auto_call([&]() { delete(fat); });
82
83 auto *info = &fat->info_;
84
85 info->bytes_per_sector = fat_read16(bs,0xb);
86 if ((info->bytes_per_sector != 0x200) && (info->bytes_per_sector != 0x400) && (info->bytes_per_sector != 0x800)) {
87 printf("unsupported sector size (%x)\n", info->bytes_per_sector);
88 return ERR_NOT_VALID;
89 }
90
91 info->sectors_per_cluster = bs[0xd];
92 switch (info->sectors_per_cluster) {
93 case 1:
94 case 2:
95 case 4:
96 case 8:
97 case 0x10:
98 case 0x20:
99 case 0x40:
100 case 0x80:
101 break;
102 default:
103 printf("unsupported sectors/cluster (%x)\n", info->sectors_per_cluster);
104 return ERR_NOT_VALID;
105 }
106
107 info->reserved_sectors = fat_read16(bs, 0xe);
108 info->fat_count = bs[0x10];
109
110 if ((info->fat_count == 0) || (info->fat_count > 8)) {
111 printf("unreasonable FAT count (%x)\n", info->fat_count);
112 return ERR_NOT_VALID;
113 }
114
115 if (bs[0x15] != 0xf8) {
116 printf("unsupported media descriptor byte (%x)\n", bs[0x15]);
117 return ERR_NOT_VALID;
118 }
119
120 // determine the FAT type according to logic in the FAT specification
121
122 // read the number of root entries
123 info->root_entries = fat_read16(bs, 0x11); // number of root entries, shall be 0 for fat32
124 if (info->root_entries % (info->bytes_per_sector / 0x20)) {
125 printf("illegal number of root entries (%x)\n", info->root_entries);
126 return ERR_NOT_VALID;
127 }
128 info->root_dir_sectors = ((info->root_entries * 32) + (info->bytes_per_sector - 1)) / info->bytes_per_sector;
129
130 // sectors per fat
131 info->sectors_per_fat = fat_read16(bs, 0x16); // read FAT size 16 bit
132 if (info->sectors_per_fat == 0) {
133 info->sectors_per_fat = fat_read32(bs,0x24); // read FAT size 32 bit
134 if (info->sectors_per_fat == 0) {
135 printf("invalid sectors per fat 0\n");
136 return ERR_NOT_VALID;
137 }
138 }
139
140 // total sectors
141 info->total_sectors = fat_read16(bs,0x13); // total sectors 16
142 if (info->total_sectors == 0) {
143 info->total_sectors = fat_read32(bs,0x20); // total sectors 32
144 }
145 if (info->total_sectors == 0) {
146 // TODO: test that total sectors <= bio device size
147 printf("invalid total sector count 0\n");
148 return ERR_NOT_VALID;
149 }
150
151 // first data sector is just after the root dir (in fat12/16) which is just after the FAT(s), which is just
152 // after the reserved sectors
153 info->data_start_sector = info->reserved_sectors + (info->fat_count * info->sectors_per_fat) + info->root_dir_sectors;
154 auto data_sectors = info->total_sectors - info->data_start_sector;
155 LTRACEF("data sectors %u\n", data_sectors);
156
157 info->total_clusters = data_sectors / info->sectors_per_cluster;
158 LTRACEF("total clusters %u\n", info->total_clusters);
159
160 // table according to FAT spec
161 if (info->total_clusters < 4085) {
162 info->fat_bits = 12;
163 } else if (info->total_clusters < 65525) {
164 info->fat_bits = 16;
165 } else {
166 info->fat_bits = 32;
167 }
168
169 if (info->fat_bits == 32) {
170 // FAT32 root entries should be zero because it's cluster based
171 if (info->root_entries != 0) {
172 printf("nonzero root entries (%u) in a FAT32 volume. invalid\n", info->root_entries);
173 return ERR_NOT_VALID;
174 }
175
176 // In FAT32, root directory acts much like a file and occupies a cluster chain starting generally
177 // at cluster 2.
178 info->root_cluster = fat_read32(bs, 0x2c);
179 if (info->root_cluster >= info->total_clusters) {
180 printf("root cluster too large (%x > %x)\n", info->root_cluster, info->total_clusters);
181 return ERR_NOT_VALID;
182 }
183 if (info->root_cluster < 2) {
184 printf("root cluster too small (<2) %u\n", info->root_cluster);
185 return ERR_NOT_VALID;
186 }
187
188 // read the active fat
189 info->active_fat = (bs[0x28] & 0x80) ? 0 : (bs[0x28] & 0xf);
190
191 // TODO: read in fsinfo structure
192 } else {
193 // On a FAT 12 or FAT 16 volumes the root directory is at a fixed position immediately after the File Allocation Tables
194 info->root_start_sector = info->reserved_sectors + info->fat_count * info->sectors_per_fat;
195 }
196
197 info->bytes_per_cluster = info->sectors_per_cluster * info->bytes_per_sector;
198 fat->bcache_ = bcache_create(fat->dev(), info->bytes_per_sector, 16);
199
200 // we're okay, cancel our cleanup of the fat structure
201 ac2.cancel();
202
203 #if LOCAL_TRACE
204 printf("Mounted FAT volume, some information:\n");
205 fat_dump(fat);
206 #endif
207
208 *cookie = (fscookie *)fat;
209
210 return result;
211 }
212
unmount(fscookie * cookie)213 status_t fat_fs::unmount(fscookie *cookie) {
214 auto *fat = (fat_fs *)cookie;
215
216 {
217 AutoLock guard(fat->lock);
218
219 // TODO: handle unmounting when files/dirs are active
220 DEBUG_ASSERT(list_is_empty(&fat->file_list_));
221
222 bcache_destroy(fat->bcache());
223 }
224
225 delete fat;
226
227 return NO_ERROR;
228 }
229
add_to_file_list(fat_file * file)230 void fat_fs::add_to_file_list(fat_file *file) {
231 DEBUG_ASSERT(lock.is_held());
232 DEBUG_ASSERT(!list_in_list(&file->node_));
233
234 LTRACEF("file %p, location %u:%u\n", file, file->dir_loc().starting_dir_cluster, file->dir_loc().dir_offset);
235
236 list_add_head(&file_list_, &file->node_);
237 }
238
lookup_file(const dir_entry_location & loc)239 fat_file *fat_fs::lookup_file(const dir_entry_location &loc) {
240 DEBUG_ASSERT(lock.is_held());
241
242 fat_file *f;
243 list_for_every_entry(&file_list_, f, fat_file, node_) {
244 if (loc == f->dir_loc()) {
245 return f;
246 }
247 }
248
249 return nullptr;
250 }
251
252 static const struct fs_api fat_api = {
253 .format = nullptr,
254 .fs_stat = nullptr,
255
256 .mount = fat_fs::mount,
257 .unmount = fat_fs::unmount,
258 .open = fat_file::open_file,
259 .create = nullptr,
260 .remove = nullptr,
261 .truncate = nullptr,
262 .stat = fat_file::stat_file,
263 .read = fat_file::read_file,
264 .write = nullptr,
265 .close = fat_file::close_file,
266
267 .mkdir = nullptr,
268 .opendir = fat_dir::opendir,
269 .readdir = fat_dir::readdir,
270 .closedir = fat_dir::closedir,
271
272 .file_ioctl = nullptr,
273 };
274
275 STATIC_FS_IMPL(fat, &fat_api);
276