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