GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/filesystems.c
Date: 2026-10-19 17:06:30
Exec Total Coverage
Lines: 0 195 0.0%
Functions: 0 17 0.0%
Branches: 0 97 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2026, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/filesystems.h"
30 #include "gate/results.h"
31
32 ✗ gate_result_t gate_filesystem_read_bootsector(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t* bootsect)
33 {
34 gate_result_t ret;
35 char buffer[512];
36 ✗ gate_size_t received = 0;
37
38 do
39 {
40 ✗ ret = gate_stream_seek(blockstream, 0, GATE_STREAM_SEEK_BEGIN, NULL);
41 ✗ GATE_BREAK_IF_FAILED(ret);
42
43 ✗ ret = gate_stream_read(blockstream, buffer, 512, &received);
44 ✗ GATE_BREAK_IF_FAILED(ret);
45
46 ✗ gate_mem_copy(bootsect, &buffer[0], 512);
47 } while (0);
48
49 ✗ return ret;
50 }
51
52 ✗ static unsigned get_total_clusters(gate_filesystem_fat_bootsect_t const* bootsect)
53 {
54 ✗ return (unsigned)bootsect->bpb.total_sectors / (unsigned)bootsect->bpb.sectors_per_cluster;
55 }
56
57 ✗ static unsigned get_first_root_dir_sector(gate_filesystem_fat_bootsect_t const* bootsect)
58 {
59 ✗ return (unsigned)bootsect->bpb.reserved_sectors + (unsigned)bootsect->bpb.sectors_per_fat * (unsigned)bootsect->bpb.number_of_fats;
60 }
61
62 ✗ static unsigned get_root_dir_sectors(gate_filesystem_fat_bootsect_t const* bootsect)
63 {
64 ✗ return (bootsect->bpb.root_entries * 32 + (unsigned)bootsect->bpb.bytes_per_sector - 1)
65 ✗ / (unsigned)bootsect->bpb.bytes_per_sector;
66 }
67
68 ✗ static unsigned get_total_data_sectors(gate_filesystem_fat_bootsect_t const* bootsect)
69 {
70 ✗ unsigned const first_root_dir_sector = get_first_root_dir_sector(bootsect);
71 ✗ unsigned const root_dir_sectors = get_root_dir_sectors(bootsect);
72 ✗ return (unsigned)bootsect->bpb.total_sectors - first_root_dir_sector - root_dir_sectors;
73 }
74
75 ✗ static unsigned get_total_data_clusters(gate_filesystem_fat_bootsect_t const* bootsect)
76 {
77 ✗ return get_total_data_sectors(bootsect) / (unsigned)bootsect->bpb.sectors_per_cluster;
78 }
79
80 ✗ static unsigned get_root_dir_sectors_count(gate_filesystem_fat_bootsect_t const* bootsect)
81 {
82 ✗ unsigned const sector_size = bootsect->bpb.bytes_per_sector;
83 ✗ return (bootsect->bpb.root_entries * 32 + sector_size - 1) / sector_size;
84 }
85
86 ✗ static unsigned get_data_cluster_sector(gate_filesystem_fat_bootsect_t const* bootsect, unsigned cluster_num)
87 {
88 /* notice: our cluster_num starts with "0" (FAT clusters start with "2") */
89 ✗ unsigned const root_dir_sectors = get_root_dir_sectors_count(bootsect);
90 ✗ unsigned const first_data_cluster_sector = get_first_root_dir_sector(bootsect) + root_dir_sectors;
91 ✗ return first_data_cluster_sector + cluster_num * (unsigned)bootsect->bpb.sectors_per_cluster;
92 }
93
94 ✗ static unsigned get_fat_type(gate_filesystem_fat_bootsect_t const* bootsect)
95 {
96 ✗ gate_size_t total_clusters = bootsect->bpb.total_sectors / bootsect->bpb.sectors_per_cluster;
97
98 ✗ if (total_clusters < 4085)
99 {
100 ✗ return 12;
101 }
102 ✗ else if (total_clusters < 65525)
103 {
104 ✗ return 16;
105 }
106 else
107 {
108 ✗ return 32;
109 }
110 }
111
112 ✗ static gate_result_t read_sector(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect, unsigned sector, char* out_buffer)
113 {
114 ✗ const gate_size_t sector_length = bootsect->bpb.bytes_per_sector;
115 ✗ const gate_int64_t pos = (gate_int64_t)sector * (gate_int64_t)sector_length;
116 gate_size_t length_returned;
117 ✗ gate_result_t result = gate_stream_seek(blockstream, pos, GATE_STREAM_SEEK_BEGIN, NULL);
118 ✗ if (GATE_SUCCEEDED(result))
119 {
120 ✗ result = gate_stream_read_block((gate_stream_t*)blockstream, out_buffer, sector_length, &length_returned);
121 }
122 ✗ return result;
123 }
124
125 ✗ gate_result_t gate_filesystem_read_fat_entries(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
126 gate_uint16_t* fat_entries, gate_size_t fat_entries_capacity, gate_size_t* fat_entries_count)
127 {
128 ✗ const unsigned fat_type = get_fat_type(bootsect);
129 ✗ const unsigned fat_start = bootsect->bpb.reserved_sectors;
130 ✗ const unsigned sector_len = bootsect->bpb.bytes_per_sector;
131 char local_buffer[512 * 3];
132 ✗ char* buffer = &local_buffer[0];
133 ✗ const gate_size_t buffer_required_len = (gate_size_t)sector_len * 3u;
134 ✗ gate_size_t entry_counter = 0;
135 ✗ gate_result_t ret = GATE_RESULT_FAILED;;
136
137
138 ✗ if (sizeof(local_buffer) > buffer_required_len)
139 {
140 ✗ buffer = gate_mem_alloc(buffer_required_len);
141 ✗ if (buffer == NULL)
142 {
143 ✗ return GATE_RESULT_OUTOFMEMORY;
144 }
145 }
146
147 ✗ if (!fat_entries_count)
148 {
149 ✗ fat_entries_count = &entry_counter;
150 }
151
152 ✗ switch (fat_type)
153 {
154 ✗ case 12:
155 {
156 unsigned ndx;
157 ✗ for (ndx = 0; (ndx < bootsect->bpb.sectors_per_fat) && (fat_entries_capacity > 0); ndx += 3)
158 {
159 unsigned pos;
160 ✗ ret = read_sector(blockstream, bootsect, fat_start + ndx, &buffer[0]);
161 ✗ GATE_BREAK_IF_FAILED(ret);
162 ✗ ret = read_sector(blockstream, bootsect, fat_start + ndx + 1, &buffer[sector_len]);
163 ✗ GATE_BREAK_IF_FAILED(ret);
164 ✗ ret = read_sector(blockstream, bootsect, fat_start + ndx + 2, &buffer[sector_len * 2]);
165 ✗ GATE_BREAK_IF_FAILED(ret);
166
167 ✗ for (pos = 0; (pos < 3 * sector_len) && (fat_entries_capacity > 0); pos += 3)
168 {
169 ✗ *fat_entries = (gate_uint16_t)buffer[pos] | ((gate_uint16_t)buffer[pos + 1] << 8);
170 ✗ ++fat_entries;
171 ✗ ++(*fat_entries_count);
172 ✗ if (0 == --fat_entries_capacity)
173 {
174 ✗ break;
175 }
176
177 ✗ *fat_entries = (((gate_uint16_t)buffer[pos + 1] >> 4) & 0x0f) | ((gate_uint16_t)buffer[pos + 2] << 4);
178 ✗ ++fat_entries;
179 ✗ ++(*fat_entries_count);
180 ✗ --fat_entries_capacity;
181 }
182 }
183 ✗ break;
184 }
185 ✗ case 16:
186 case 32:
187 default:
188 {
189 ✗ ret = GATE_RESULT_NOTSUPPORTED;
190 ✗ break;
191 }
192 }
193
194 ✗ if (buffer && (buffer != &local_buffer[0]))
195 {
196 ✗ gate_mem_dealloc(buffer);
197 }
198 ✗ return ret;
199 }
200
201 ✗ gate_result_t gate_filesystem_read_next_fat_entry(
202 gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect, gate_uint32_t fat_entry, gate_uint32_t* next_fat_entry)
203 {
204 ✗ const unsigned fat_type = get_fat_type(bootsect);
205 ✗ const unsigned fat_start = bootsect->bpb.reserved_sectors;
206 ✗ const unsigned sector_len = bootsect->bpb.bytes_per_sector;
207 unsigned char local_buffer[512 * 2];
208 ✗ unsigned char* buffer = &local_buffer[0];
209 ✗ const gate_size_t buffer_required_len = (gate_size_t)sector_len * 2u;
210 ✗ gate_result_t ret = GATE_RESULT_FAILED;
211
212 ✗ if (sizeof(local_buffer) > buffer_required_len)
213 {
214 ✗ buffer = (unsigned char*)gate_mem_alloc(buffer_required_len);
215 ✗ if (buffer == NULL)
216 {
217 ✗ return GATE_RESULT_OUTOFMEMORY;
218 }
219 }
220
221 ✗ switch (fat_type)
222 {
223 ✗ case 12:
224 {
225 unsigned next_cluster;
226 ✗ unsigned cluster_pos = fat_entry;
227 ✗ unsigned fat_offset = (cluster_pos / 2) * 3;
228 ✗ unsigned sector_num = fat_offset / sector_len;
229 ✗ unsigned sector_offset = fat_offset % sector_len;
230
231 ✗ ret = read_sector(blockstream, bootsect, fat_start + sector_num, (char*)&buffer[0]);
232 ✗ GATE_BREAK_IF_FAILED(ret);
233 ✗ if (sector_offset >= sector_len - 2)
234 {
235 /* fat entry is on the boundary to the next sector */
236 ✗ ret = read_sector(blockstream, bootsect, fat_start + sector_num, (char*)&buffer[sector_len]);
237 ✗ GATE_BREAK_IF_FAILED(ret);
238 }
239
240 ✗ if ((cluster_pos & 1) == 0)
241 {
242 ✗ next_cluster = (gate_uint16_t)buffer[sector_offset] | (((gate_uint16_t)buffer[sector_offset + 1] & 0x0f) << 8);
243 }
244 else
245 {
246 ✗ next_cluster = (((gate_uint16_t)buffer[sector_offset + 1] >> 4) & 0x0f) | ((gate_uint16_t)buffer[sector_offset + 2] << 4);
247 }
248
249 ✗ if (next_cluster >= 0xff8)
250 {
251 ✗ ret = GATE_RESULT_ENDOFSTREAM;
252 ✗ *next_fat_entry = 0;
253 }
254 ✗ else if (next_cluster == 0xff7)
255 {
256 ✗ ret = GATE_RESULT_INVALIDCONTENT;
257 }
258 else
259 {
260 ✗ *next_fat_entry = next_cluster;
261 ✗ ret = GATE_RESULT_OK;
262 }
263 ✗ break;
264 }
265 ✗ case 16:
266 case 32:
267 default:
268 {
269 ✗ ret = GATE_RESULT_NOTSUPPORTED;
270 ✗ break;
271 }
272 }
273
274 ✗ if (buffer && (buffer != &local_buffer[0]))
275 {
276 ✗ gate_mem_dealloc(buffer);
277 }
278 ✗ return ret;
279 }
280
281
282
283 ✗ gate_result_t gate_filesystem_read_fat_cluster(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
284 gate_uint32_t cluster, char* buffer, gate_size_t buffer_capacity, gate_size_t* buffer_filled)
285 {
286 ✗ unsigned fat_type = get_fat_type(bootsect);
287
288 ✗ switch (fat_type)
289 {
290 ✗ case 12:
291 {
292 unsigned total_clusters;
293 unsigned sector_num;
294 unsigned byte_count;
295 gate_int64_t stream_offset;
296 gate_result_t result;
297
298 ✗ if (cluster < 2)
299 {
300 /* first FAT clusters has cluster-id 2 */
301 ✗ return GATE_RESULT_INVALIDARG;
302 }
303 ✗ cluster -= 2;
304 ✗ total_clusters = get_total_data_clusters(bootsect);
305 ✗ if (cluster >= total_clusters)
306 {
307 ✗ return GATE_RESULT_OUTOFBOUNDS;
308 }
309 ✗ sector_num = get_data_cluster_sector(bootsect, (unsigned)cluster);
310 ✗ stream_offset = (gate_int64_t)sector_num * (gate_int64_t)bootsect->bpb.bytes_per_sector;
311 ✗ byte_count = (unsigned)bootsect->bpb.sectors_per_cluster * (unsigned)bootsect->bpb.bytes_per_sector;
312 ✗ if (buffer_capacity > byte_count)
313 {
314 ✗ buffer_capacity = byte_count;
315 }
316 ✗ result = gate_stream_seek(blockstream, stream_offset, GATE_STREAM_SEEK_BEGIN, NULL);
317 ✗ GATE_RETURN_IF_FAILED(result);
318 ✗ return gate_stream_read_block((gate_stream_t*)blockstream, buffer, buffer_capacity, buffer_filled);
319 }
320 ✗ case 16:
321 case 32:
322 default:
323 ✗ return GATE_RESULT_NOTSUPPORTED;
324 }
325 }
326
327 ✗ static gate_result_t read_fat_dir(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
328 unsigned start_sector, unsigned sectors_count, gate_filesystem_fat_direntry_callback_t callback, void* user_param)
329 {
330 ✗ const unsigned sectorlen = bootsect->bpb.bytes_per_sector;
331 ✗ const unsigned sectorentries = sectorlen / 32;
332
333 ✗ gate_result_t result = GATE_RESULT_OK;
334 unsigned ndx;
335
336 ✗ for (ndx = 0; ndx != sectors_count; ++ndx)
337 {
338 gate_filesystem_fat_direntry_t const* ptr_direntry;
339 unsigned ent;
340 char buffer[4096];
341 ✗ result = read_sector(blockstream, bootsect, start_sector + ndx, buffer);
342 ✗ GATE_RETURN_IF_FAILED(result);
343 ✗ ptr_direntry = (gate_filesystem_fat_direntry_t const*)buffer;
344
345 ✗ for (ent = 0; ent != sectorentries; ++ent, ++ptr_direntry)
346 {
347 ✗ switch ((unsigned char)ptr_direntry->filename[0])
348 {
349 ✗ case 0:
350 /* end of directory reached */
351 ✗ return GATE_RESULT_OK;
352 ✗ case 0xe5:
353 /* unused_entry, skip */
354 ✗ continue;
355 ✗ default:
356 ✗ if (ptr_direntry->attributes == 0x0f)
357 {
358 /* long filename, skip */
359 ✗ continue;
360 }
361
362 /* standard entry: */
363 ✗ if (!callback(ptr_direntry, user_param))
364 {
365 /* canceled by callback */
366 ✗ return GATE_RESULT_CANCELED;
367 }
368 ✗ break;
369 }
370 }
371 }
372 ✗ return result;
373 }
374
375 ✗ gate_result_t gate_filesystem_read_fat_directory(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
376 gate_string_t const* subdir, gate_filesystem_fat_direntry_callback_t callback, void* user_param)
377 {
378 ✗ const unsigned root_sector = get_first_root_dir_sector(bootsect);
379 ✗ const unsigned root_sectors_count = get_root_dir_sectors(bootsect);
380
381 ✗ gate_result_t ret = read_fat_dir(blockstream, bootsect, root_sector, root_sectors_count, callback, user_param);
382 ✗ if (ret == GATE_RESULT_CANCELED)
383 {
384 ✗ ret = GATE_RESULT_OK;
385 }
386 ✗ return ret;
387 }
388
389 struct find_fat_file_param
390 {
391 char filename[8];
392 char fileext[3];
393 gate_filesystem_fat_direntry_t matched_entry;
394 };
395
396 ✗ static gate_bool_t GATE_CALL find_fat_file_cb(gate_filesystem_fat_direntry_t const* entry, void* user_param)
397 {
398 ✗ struct find_fat_file_param* param = (struct find_fat_file_param*)user_param;
399 ✗ if ((0 == gate_mem_compare(entry->filename, param->filename, 8))
400 ✗ && (0 == gate_mem_compare(entry->extension, param->fileext, 3)))
401 {
402 ✗ gate_mem_copy(&param->matched_entry, entry, sizeof(gate_filesystem_fat_direntry_t));
403 ✗ return false;
404 }
405 ✗ return true;
406 }
407
408
409 ✗ gate_result_t gate_filesystem_find_fat_file(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
410 gate_string_t const* path, gate_filesystem_fat_direntry_t* ptr_found_entry)
411 {
412 gate_result_t ret;
413 gate_size_t pos;
414 ✗ gate_size_t len = gate_string_length(path);
415 struct find_fat_file_param param;
416
417 ✗ gate_mem_clear(&param, sizeof(param));
418 ✗ gate_mem_fill(&param.filename[0], ' ', sizeof(param.filename));
419 ✗ gate_mem_fill(&param.fileext[0], ' ', sizeof(param.fileext));
420 ✗ pos = gate_string_char_pos_last(path, '.');
421 ✗ if (pos == GATE_STR_NPOS)
422 {
423 ✗ pos = len > 8 ? 8 : len;
424 ✗ gate_mem_copy(param.filename, gate_string_ptr(path, 0), pos);
425 }
426 else
427 {
428 ✗ gate_mem_copy(param.filename, gate_string_ptr(path, 0), pos > 8 ? 8 : pos);
429 ✗ if (len - pos - 1 >= 3)
430 {
431 ✗ gate_mem_copy(param.fileext, gate_string_ptr(path, pos + 1), 3);
432 }
433 else
434 {
435 ✗ gate_mem_copy(param.fileext, gate_string_ptr(path, pos + 1), len - pos - 1);
436 }
437 }
438
439 ✗ ret = gate_filesystem_read_fat_directory(blockstream, bootsect, NULL, &find_fat_file_cb, &param);
440 ✗ if (GATE_SUCCEEDED(ret))
441 {
442 ✗ if (param.matched_entry.filename[0] != 0)
443 {
444 ✗ gate_mem_copy(ptr_found_entry, &param.matched_entry, sizeof(gate_filesystem_fat_direntry_t));
445 }
446 else
447 {
448 ✗ ret = GATE_RESULT_NOMATCH;
449 }
450 }
451 ✗ return ret;
452 }
453