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: failure - ENOSPC
8
9static inline uint32_t fat12_16_root_dir_lba(struct fat_fs *fs) {
10 return fs->bpb->reserved_sector_count +
11 (fs->bpb->num_fats * fs->bpb->fat_size_16) + fs->volume_base_lba;
12}
13
14static inline uint32_t fat12_16_root_dir_sectors(struct fat_fs *fs) {
15 return ((fs->bpb->root_entry_count * 32) + fs->bpb->bytes_per_sector - 1) /
16 fs->bpb->bytes_per_sector;
17}
18
19static bool fat_find_free_dirent_slot(struct fat_fs *fs, uint32_t dir_cluster,
20 uint8_t *dir_buf, uint32_t *out_cluster,
21 uint32_t *out_offset,
22 uint32_t *out_prev_cluster) {
23
24 if (dir_cluster == FAT_DIR_CLUSTER_ROOT && fs->type != FAT_32) {
25 uint32_t root_lba = fat12_16_root_dir_lba(fs);
26 uint32_t root_secs = fat12_16_root_dir_sectors(fs);
27 for (uint32_t i = 0; i < root_secs; i++) {
28 if (!fs->disk->read_sector(fs->disk, root_lba + i, dir_buf, 1))
29 return false;
30
31 for (uint32_t offset = 0; offset < fs->bpb->bytes_per_sector;
32 offset += sizeof(struct fat_dirent)) {
33 struct fat_dirent *ent =
34 (struct fat_dirent *) (dir_buf + offset);
35 if (ent->name[0] == 0x00 || (uint8_t) ent->name[0] == 0xE5) {
36 *out_cluster = i;
37 *out_offset = offset;
38 return true;
39 }
40 }
41 }
42
43 return false;
44 }
45
46 uint32_t current_cluster = dir_cluster;
47 *out_prev_cluster = 0;
48
49 while (current_cluster != fat_eoc(fs)) {
50 if (!fat_read_cluster(fs, cluster: current_cluster, buffer: dir_buf))
51 return false;
52
53 for (uint32_t offset = 0; offset < fs->cluster_size;
54 offset += sizeof(struct fat_dirent)) {
55 struct fat_dirent *ent = (struct fat_dirent *) (dir_buf + offset);
56 if (ent->name[0] == 0x00 || (uint8_t) ent->name[0] == 0xE5) {
57 *out_cluster = current_cluster;
58 *out_offset = offset;
59 return true;
60 }
61 }
62
63 *out_prev_cluster = current_cluster;
64 current_cluster = fat_read_fat_entry(fs, cluster: current_cluster);
65 }
66
67 return false;
68}
69
70static bool fat_extend_directory(struct fat_fs *fs, uint32_t prev_cluster,
71 uint32_t *new_cluster_out, uint8_t *dir_buf) {
72 if (prev_cluster == FAT_DIR_CLUSTER_ROOT && fs->type != FAT_32) {
73 // can't extend FAT12/16 root directory
74 return false;
75 }
76
77 uint32_t new_cluster = fat_alloc_cluster(fs);
78 if (new_cluster == 0)
79 return false;
80
81 uint8_t *cluster_buf = kmalloc(fs->cluster_size, ALLOC_FLAGS_ZERO);
82 if (!fat_write_cluster(fs, cluster: new_cluster, buffer: cluster_buf)) {
83 kfree(cluster_buf);
84 return false;
85 }
86 kfree(cluster_buf);
87
88 if (prev_cluster)
89 fat_write_fat_entry(fs, cluster: prev_cluster, value: new_cluster);
90 fat_write_fat_entry(fs, cluster: new_cluster, value: fat_eoc(fs));
91
92 if (!fat_read_cluster(fs, cluster: new_cluster, buffer: dir_buf))
93 return false;
94
95 *new_cluster_out = new_cluster;
96 return true;
97}
98
99static void fat_initialize_dirent(struct fat_dirent *ent, const char *filename,
100 uint32_t cluster, enum fat_fileattr attr) {
101 memset(ent, 0, sizeof(struct fat_dirent));
102
103 if (strcmp(str1: filename, str2: ".") == 0) {
104 memcpy(ent->name, ". ", 11);
105 } else if (strcmp(str1: filename, str2: "..") == 0) {
106 memcpy(ent->name, ".. ", 11);
107 } else {
108 fat_format_filename_83(name: filename, out: ent->name);
109 }
110
111 ent->attr = attr;
112
113 ent->low_cluster = cluster & 0xFFFF;
114 ent->high_cluster = cluster >> 16;
115
116 ent->filesize = 0;
117
118 struct fat_date date = fat_get_current_date();
119 struct fat_time time = fat_get_current_time();
120
121 ent->crtdate = date;
122 ent->crttime = time;
123 ent->lastaccess = date;
124 ent->moddate = date;
125 ent->modtime = time;
126}
127
128bool fat_create(struct fat_fs *fs, uint32_t dir_cluster, const char *filename,
129 struct fat_dirent *out_dirent, enum fat_fileattr attr,
130 uint32_t *out_cluster) {
131 if (!dir_cluster)
132 return false;
133
134 if (fat_contains(fs, cluster: dir_cluster, f: filename))
135 return false;
136
137 uint8_t *dir_buf = kmalloc(fs->cluster_size);
138 if (!dir_buf)
139 return false;
140
141 uint32_t slot_cluster = 0, slot_offset = 0, prev_cluster = 0;
142
143 bool found = fat_find_free_dirent_slot(
144 fs, dir_cluster, dir_buf, out_cluster: &slot_cluster, out_offset: &slot_offset, out_prev_cluster: &prev_cluster);
145
146 if (!found && fs->type == FAT_32) {
147 if (!fat_extend_directory(fs, prev_cluster, new_cluster_out: &slot_cluster, dir_buf)) {
148 kfree(dir_buf);
149 return false;
150 }
151 slot_offset = 0;
152 found = true;
153 }
154
155 if (!found) {
156 kfree(dir_buf);
157 return false;
158 }
159
160 uint32_t cluster = 0;
161
162 bool needs_cluster = (attr == FAT_DIR && strcmp(str1: filename, str2: ".") != 0 &&
163 strcmp(str1: filename, str2: "..") != 0);
164
165 if (needs_cluster)
166 cluster = fat_alloc_cluster(fs);
167
168 if (out_cluster)
169 *out_cluster = cluster;
170
171 struct fat_dirent *new_ent = (struct fat_dirent *) (dir_buf + slot_offset);
172 fat_initialize_dirent(ent: new_ent, filename, cluster, attr);
173
174 bool success = false;
175 if (dir_cluster == FAT_DIR_CLUSTER_ROOT && fs->type != FAT_32) {
176 uint32_t root_lba = fat12_16_root_dir_lba(fs);
177 success = fs->disk->write_sector(fs->disk, root_lba + slot_cluster,
178 dir_buf, 1);
179 } else {
180 success = fat_write_cluster(fs, cluster: slot_cluster, buffer: dir_buf);
181 }
182
183 kfree(dir_buf);
184
185 if (success && out_dirent)
186 memcpy(out_dirent, new_ent, sizeof(struct fat_dirent));
187
188 return success;
189}
190
191bool fat_mkdir(struct fat_fs *fs, uint32_t parent_cluster, const char *name,
192 struct fat_dirent *out_dirent) {
193 struct fat_dirent new_dirent;
194 uint32_t new_cluster = 0;
195
196 if (!fat_create(fs, dir_cluster: parent_cluster, filename: name, out_dirent: &new_dirent, attr: FAT_DIR,
197 out_cluster: &new_cluster))
198 return false;
199
200 *out_dirent = new_dirent;
201
202 uint8_t *buf = kmalloc(fs->cluster_size, ALLOC_FLAGS_ZERO);
203 if (!buf)
204 return false;
205
206 struct fat_dirent *dot = (struct fat_dirent *) buf;
207 fat_initialize_dirent(ent: dot, filename: ".", cluster: new_cluster, attr: FAT_DIR);
208
209 struct fat_dirent *dotdot =
210 (struct fat_dirent *) (buf + sizeof(struct fat_dirent));
211
212 fat_initialize_dirent(
213 ent: dotdot, filename: "..", cluster: parent_cluster == fs->root_cluster ? 0 : parent_cluster,
214 attr: FAT_DIR);
215
216 bool ok = fat_write_cluster(fs, cluster: new_cluster, buffer: buf);
217 kfree(buf);
218 return ok;
219}
220