1#include <fs/fat.h>
2#include <mem/alloc.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6
7// TODO: optimize this so I dont memcpy every iteration :boom:
8
9static bool fat32_walk_cluster(struct fat_fs *fs, uint32_t cluster,
10 fat_walk_callback callback, void *ctx) {
11 uint8_t *cluster_buf = valloc(fs->cluster_size);
12
13 /* on a callback hit, ownership of ret passes to the callback */
14 struct fat_dirent *ret = vnew(ret);
15 if (!cluster_buf || !ret)
16 goto out;
17
18 if (fat_read_cluster(fs, cluster, buffer: cluster_buf)) {
19 for (uint32_t i = 0; i < fs->cluster_size;
20 i += sizeof(struct fat_dirent)) {
21 struct fat_dirent *entry = (struct fat_dirent *) (cluster_buf + i);
22 memcpy(ret, entry, sizeof(struct fat_dirent));
23 if (callback(ret, i / sizeof(struct fat_dirent), ctx)) {
24 vfree(cluster_buf);
25 return true;
26 }
27 }
28 }
29
30out:
31 vfree(ret);
32 vfree(cluster_buf);
33 return false;
34}
35
36static bool fat12_16_walk_cluster(struct fat_fs *fs, uint32_t cluster,
37 fat_walk_callback callback, void *ctx) {
38 struct fat_bpb *bpb = fs->bpb;
39
40 uint32_t bytes_per_sector = fs->disk->sector_size;
41 uint32_t sectors_per_cluster = fs->bpb->sectors_per_cluster;
42 bool is_root = cluster == FAT_DIR_CLUSTER_ROOT;
43
44 uint32_t root_dir_size = bpb->root_entry_count * sizeof(struct fat_dirent);
45
46 uint32_t root_dir_sectors = DIV_ROUND_UP(root_dir_size, bytes_per_sector);
47
48 uint32_t lba = fat_cluster_to_lba(fs, cluster);
49 uint32_t sectors_to_read = is_root ? root_dir_sectors : sectors_per_cluster;
50
51 uint64_t sector_buf_size = sectors_to_read * fs->disk->sector_size;
52
53 uint8_t *sector_buf = valloc(sector_buf_size);
54 if (!sector_buf)
55 return false;
56
57 if (!fs->disk->read_sector(fs->disk, lba, sector_buf, sectors_to_read)) {
58 vfree(sector_buf);
59 return false;
60 }
61
62 struct fat_dirent *ret = vnew(ret);
63 if (!ret) {
64 vfree(sector_buf);
65 return false;
66 }
67
68 uint32_t entries_per_cluster =
69 is_root ? bpb->root_entry_count
70 : (sectors_to_read * bpb->bytes_per_sector) /
71 sizeof(struct fat_dirent);
72
73 for (uint32_t i = 0; i < entries_per_cluster; i++) {
74 struct fat_dirent *entry =
75 (struct fat_dirent *) (sector_buf + i * sizeof(struct fat_dirent));
76
77 if (entry->name[0] == 0x00 || (uint8_t) entry->name[0] == 0xE5)
78 continue;
79
80 memcpy(ret, entry, sizeof(struct fat_dirent));
81 if (callback(ret, i, ctx)) {
82 vfree(sector_buf);
83 return true;
84 }
85 }
86
87 vfree(ret);
88 vfree(sector_buf);
89 return false;
90}
91
92// If the callback returns true, the dirent passed in will be allocated
93bool fat_walk_cluster(struct fat_fs *fs, uint32_t cluster, fat_walk_callback cb,
94 void *ctx) {
95 if (fs->type == FAT_32) {
96 return fat32_walk_cluster(fs, cluster, callback: cb, ctx);
97 } else {
98 return fat12_16_walk_cluster(fs, cluster, callback: cb, ctx);
99 }
100}
101