1#include <fs/fat.h>
2#include <mem/alloc.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6
7bool fat_read_file(struct fat_fs *fs, struct fat_dirent *ent, uint32_t offset,
8 uint32_t size, uint8_t *out_buf) {
9 if (!ent || !out_buf || offset >= ent->filesize)
10 return false;
11
12 uint32_t cluster_size = fs->cluster_size;
13 uint32_t file_size = ent->filesize;
14
15 // Do not read past end of file
16 if (offset + size > file_size)
17 size = file_size - offset;
18
19 uint32_t cluster = fat_get_dir_cluster(d: ent);
20 if (cluster == 0)
21 return false;
22
23 uint8_t *temp_buf = kmalloc(cluster_size);
24 uint32_t read = 0;
25
26 // reach the starting offset
27 uint32_t skip_clusters = offset / cluster_size;
28 uint32_t skip_offset = offset % cluster_size;
29
30 while (skip_clusters--) {
31 if (fat_is_eoc(fs, cluster))
32 goto oops;
33
34 cluster = fat_read_fat_entry(fs, cluster);
35 }
36
37 while (read < size && !fat_is_eoc(fs, cluster)) {
38 if (!fat_read_cluster(fs, cluster, buffer: temp_buf))
39 goto oops;
40
41 uint32_t to_copy = cluster_size - skip_offset;
42 if (to_copy > (size - read))
43 to_copy = size - read;
44
45 memcpy(out_buf + read, temp_buf + skip_offset, to_copy);
46 read += to_copy;
47
48 skip_offset = 0;
49 if (read < size)
50 cluster = fat_read_fat_entry(fs, cluster);
51 }
52
53 kfree(temp_buf);
54 return true;
55oops:
56 kfree(temp_buf);
57 return false;
58}
59
60bool fat_write_file(struct fat_fs *fs, struct fat_dirent *ent, uint32_t offset,
61 const uint8_t *data, uint32_t size) {
62 if (!ent || !data)
63 return false;
64
65 uint32_t cluster_size = fs->cluster_size;
66 uint32_t cluster = fat_get_dir_cluster(d: ent);
67 if (cluster == 0) {
68 cluster = fat_alloc_cluster(fs);
69 if (cluster == 0)
70 return false;
71
72 ent->high_cluster = cluster >> 16;
73 ent->low_cluster = cluster & 0xFFFF;
74
75 fat_write_fat_entry(fs, cluster, value: fat_eoc(fs));
76 }
77
78 uint32_t end_offset = offset + size;
79 uint32_t needed_clusters = (end_offset + cluster_size - 1) / cluster_size;
80
81 // extend chain to needed length
82 uint32_t chain_len = 1;
83 uint32_t current = cluster;
84 while (!fat_is_eoc(fs, cluster: current)) {
85 current = fat_read_fat_entry(fs, cluster: current);
86 chain_len++;
87 }
88
89 current = cluster;
90 if (chain_len < needed_clusters) {
91 for (uint32_t i = chain_len; i < needed_clusters; i++) {
92 uint32_t new_cluster = fat_alloc_cluster(fs);
93 if (!new_cluster)
94 return false;
95
96 fat_write_fat_entry(fs, cluster: current, value: new_cluster);
97 current = new_cluster;
98 }
99 fat_write_fat_entry(fs, cluster: current, value: fat_eoc(fs));
100 }
101
102 // Reset to start and walk to offset
103 current = cluster;
104 uint32_t skip_clusters = offset / cluster_size;
105 uint32_t skip_offset = offset % cluster_size;
106 for (uint32_t i = 0; i < skip_clusters; i++) {
107 current = fat_read_fat_entry(fs, cluster: current);
108 }
109
110 uint8_t *temp_buf = kmalloc(cluster_size);
111 uint32_t written = 0;
112
113 while (written < size && !fat_is_eoc(fs, cluster: current)) {
114 if (skip_offset || (size - written) < cluster_size) {
115 // Partial write — read-modify-write
116 if (!fat_read_cluster(fs, cluster: current, buffer: temp_buf))
117 goto oops;
118 }
119
120 uint32_t to_write = cluster_size - skip_offset;
121 if (to_write > (size - written))
122 to_write = size - written;
123
124 memcpy(temp_buf + skip_offset, data + written, to_write);
125
126 if (!fat_write_cluster(fs, cluster: current, buffer: temp_buf))
127 goto oops;
128
129 written += to_write;
130 skip_offset = 0;
131
132 if (written < size)
133 current = fat_read_fat_entry(fs, cluster: current);
134 }
135
136 if (end_offset > ent->filesize)
137 ent->filesize = end_offset;
138
139 kfree(temp_buf);
140 return true;
141oops:
142 kfree(temp_buf);
143 return false;
144}
145