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 = kmalloc(fs->cluster_size);
12
13 struct fat_dirent *ret = kmalloc(sizeof(struct fat_dirent));
14 if (!cluster_buf || !ret)
15 return false;
16
17 if (fat_read_cluster(fs, cluster, buffer: cluster_buf)) {
18 for (uint32_t i = 0; i < fs->cluster_size;
19 i += sizeof(struct fat_dirent)) {
20 struct fat_dirent *entry = (struct fat_dirent *) (cluster_buf + i);
21 memcpy(ret, entry, sizeof(struct fat_dirent));
22 if (callback(ret, i / sizeof(struct fat_dirent), ctx)) {
23 kfree(cluster_buf);
24 return true;
25 }
26 }
27 }
28 kfree(ret);
29 kfree(cluster_buf);
30 return false;
31}
32
33static bool fat12_16_walk_cluster(struct fat_fs *fs, uint32_t cluster,
34 fat_walk_callback callback, void *ctx) {
35 struct fat_bpb *bpb = fs->bpb;
36
37 uint32_t bytes_per_sector = fs->disk->sector_size;
38 uint32_t sectors_per_cluster = fs->bpb->sectors_per_cluster;
39 bool is_root = cluster == FAT_DIR_CLUSTER_ROOT;
40
41 uint32_t root_dir_size = bpb->root_entry_count * sizeof(struct fat_dirent);
42
43 uint32_t root_dir_sectors =
44 (root_dir_size + bytes_per_sector - 1) / bytes_per_sector;
45
46 uint32_t lba = fat_cluster_to_lba(fs, cluster);
47 uint32_t sectors_to_read = is_root ? root_dir_sectors : sectors_per_cluster;
48
49 uint64_t sector_buf_size = sectors_to_read * fs->disk->sector_size;
50
51 uint8_t *sector_buf = kmalloc(sector_buf_size);
52 if (!sector_buf)
53 return false;
54
55 if (!fs->disk->read_sector(fs->disk, lba, sector_buf, sectors_to_read)) {
56 kfree(sector_buf);
57 return false;
58 }
59
60 struct fat_dirent *ret = kmalloc(sizeof(struct fat_dirent));
61 if (!ret) {
62 kfree(sector_buf);
63 return false;
64 }
65
66 uint32_t entries_per_cluster =
67 is_root ? bpb->root_entry_count
68 : (sectors_to_read * bpb->bytes_per_sector) /
69 sizeof(struct fat_dirent);
70
71 for (uint32_t i = 0; i < entries_per_cluster; i++) {
72 struct fat_dirent *entry =
73 (struct fat_dirent *) (sector_buf + i * sizeof(struct fat_dirent));
74
75 if (entry->name[0] == 0x00 || (uint8_t) entry->name[0] == 0xE5)
76 continue;
77
78 memcpy(ret, entry, sizeof(struct fat_dirent));
79 if (callback(ret, i, ctx)) {
80 kfree(sector_buf);
81 return true;
82 }
83 }
84
85 kfree(ret);
86 kfree(sector_buf);
87 return false;
88}
89
90// If the callback returns true, the dirent passed in will be allocated
91bool fat_walk_cluster(struct fat_fs *fs, uint32_t cluster, fat_walk_callback cb,
92 void *ctx) {
93 if (fs->type == FAT_32) {
94 return fat32_walk_cluster(fs, cluster, callback: cb, ctx);
95 } else {
96 return fat12_16_walk_cluster(fs, cluster, callback: cb, ctx);
97 }
98}
99