GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/filesystems.c
Date: 2025-09-22 22:57:09
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-2025, 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 unsigned ndx, pos;
135 ✗ gate_size_t entry_counter = 0;
136 ✗ gate_result_t ret = GATE_RESULT_FAILED;;
137
138
139 ✗ if (sizeof(local_buffer) > buffer_required_len)
140 {
141 ✗ buffer = gate_mem_alloc(buffer_required_len);
142 ✗ if (buffer == NULL)
143 {
144 ✗ return GATE_RESULT_OUTOFMEMORY;
145 }
146 }
147
148 ✗ if (!fat_entries_count)
149 {
150 ✗ fat_entries_count = &entry_counter;
151 }
152
153 ✗ switch (fat_type)
154 {
155 ✗ case 12:
156 {
157 ✗ for (ndx = 0; (ndx < bootsect->bpb.sectors_per_fat) && (fat_entries_capacity > 0); ndx += 3)
158 {
159 ✗ ret = read_sector(blockstream, bootsect, fat_start + ndx, &buffer[0]);
160 ✗ GATE_BREAK_IF_FAILED(ret);
161 ✗ ret = read_sector(blockstream, bootsect, fat_start + ndx + 1, &buffer[sector_len]);
162 ✗ GATE_BREAK_IF_FAILED(ret);
163 ✗ ret = read_sector(blockstream, bootsect, fat_start + ndx + 2, &buffer[sector_len * 2]);
164 ✗ GATE_BREAK_IF_FAILED(ret);
165
166 ✗ for (pos = 0; (pos < 3 * sector_len) && (fat_entries_capacity > 0); pos += 3)
167 {
168 ✗ *fat_entries = (gate_uint16_t)buffer[pos] | ((gate_uint16_t)buffer[pos + 1] << 8);
169 ✗ ++fat_entries;
170 ✗ ++(*fat_entries_count);
171 ✗ if (0 == --fat_entries_capacity)
172 {
173 ✗ break;
174 }
175
176 ✗ *fat_entries = (((gate_uint16_t)buffer[pos + 1] >> 4) & 0x0f) | ((gate_uint16_t)buffer[pos + 2] << 4);
177 ✗ ++fat_entries;
178 ✗ ++(*fat_entries_count);
179 ✗ --fat_entries_capacity;
180 }
181 }
182 ✗ break;
183 }
184 ✗ case 16:
185 case 32:
186 default:
187 {
188 ✗ ret = GATE_RESULT_NOTSUPPORTED;
189 ✗ break;
190 }
191 }
192
193 ✗ if (buffer && (buffer != &local_buffer[0]))
194 {
195 ✗ gate_mem_dealloc(buffer);
196 }
197 ✗ return ret;
198 }
199
200 ✗ gate_result_t gate_filesystem_read_next_fat_entry(
201 gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect, gate_uint32_t fat_entry, gate_uint32_t* next_fat_entry)
202 {
203 ✗ const unsigned fat_type = get_fat_type(bootsect);
204 ✗ const unsigned fat_start = bootsect->bpb.reserved_sectors;
205 ✗ const unsigned sector_len = bootsect->bpb.bytes_per_sector;
206 unsigned cluster_pos;
207 unsigned fat_offset;
208 unsigned sector_num;
209 unsigned sector_offset;
210 unsigned char local_buffer[512 * 2];
211 ✗ unsigned char* buffer = &local_buffer[0];
212 ✗ const gate_size_t buffer_required_len = (gate_size_t)sector_len * 2u;
213 unsigned next_cluster;
214 ✗ gate_result_t ret = GATE_RESULT_FAILED;
215
216 ✗ if (sizeof(local_buffer) > buffer_required_len)
217 {
218 ✗ buffer = (unsigned char*)gate_mem_alloc(buffer_required_len);
219 ✗ if (buffer == NULL)
220 {
221 ✗ return GATE_RESULT_OUTOFMEMORY;
222 }
223 }
224
225 ✗ switch (fat_type)
226 {
227 ✗ case 12:
228 {
229 ✗ cluster_pos = fat_entry;
230 ✗ fat_offset = (cluster_pos / 2) * 3;
231 ✗ sector_num = fat_offset / sector_len;
232 ✗ sector_offset = fat_offset % sector_len;
233
234 ✗ ret = read_sector(blockstream, bootsect, fat_start + sector_num, (char*)&buffer[0]);
235 ✗ GATE_BREAK_IF_FAILED(ret);
236 ✗ if (sector_offset >= sector_len - 2)
237 {
238 /* fat entry is on the boundary to the next sector */
239 ✗ ret = read_sector(blockstream, bootsect, fat_start + sector_num, (char*)&buffer[sector_len]);
240 ✗ GATE_BREAK_IF_FAILED(ret);
241 }
242
243 ✗ if ((cluster_pos & 1) == 0)
244 {
245 ✗ next_cluster = (gate_uint16_t)buffer[sector_offset] | (((gate_uint16_t)buffer[sector_offset + 1] & 0x0f) << 8);
246 }
247 else
248 {
249 ✗ next_cluster = (((gate_uint16_t)buffer[sector_offset + 1] >> 4) & 0x0f) | ((gate_uint16_t)buffer[sector_offset + 2] << 4);
250 }
251
252 ✗ if (next_cluster >= 0xff8)
253 {
254 ✗ ret = GATE_RESULT_ENDOFSTREAM;
255 ✗ *next_fat_entry = 0;
256 }
257 ✗ else if (next_cluster == 0xff7)
258 {
259 ✗ ret = GATE_RESULT_INVALIDCONTENT;
260 }
261 else
262 {
263 ✗ *next_fat_entry = next_cluster;
264 ✗ ret = GATE_RESULT_OK;
265 }
266 ✗ break;
267 }
268 ✗ case 16:
269 case 32:
270 default:
271 {
272 ✗ ret = GATE_RESULT_NOTSUPPORTED;
273 ✗ break;
274 }
275 }
276
277 ✗ if (buffer && (buffer != &local_buffer[0]))
278 {
279 ✗ gate_mem_dealloc(buffer);
280 }
281 ✗ return ret;
282 }
283
284
285
286 ✗ gate_result_t gate_filesystem_read_fat_cluster(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
287 gate_uint32_t cluster, char* buffer, gate_size_t buffer_capacity, gate_size_t* buffer_filled)
288 {
289 ✗ unsigned fat_type = get_fat_type(bootsect);
290 unsigned total_clusters;
291 unsigned sector_num;
292 unsigned byte_count;
293 gate_int64_t stream_offset;
294 gate_result_t result;
295
296 ✗ switch (fat_type)
297 {
298 ✗ case 12:
299 {
300 ✗ if (cluster < 2)
301 {
302 /* first FAT clusters has cluster-id 2 */
303 ✗ return GATE_RESULT_INVALIDARG;
304 }
305 ✗ cluster -= 2;
306 ✗ total_clusters = get_total_data_clusters(bootsect);
307 ✗ if (cluster >= total_clusters)
308 {
309 ✗ return GATE_RESULT_OUTOFBOUNDS;
310 }
311 ✗ sector_num = get_data_cluster_sector(bootsect, (unsigned)cluster);
312 ✗ stream_offset = (gate_int64_t)sector_num * (gate_int64_t)bootsect->bpb.bytes_per_sector;
313 ✗ byte_count = (unsigned)bootsect->bpb.sectors_per_cluster * (unsigned)bootsect->bpb.bytes_per_sector;
314 ✗ if (buffer_capacity > byte_count)
315 {
316 ✗ buffer_capacity = byte_count;
317 }
318 ✗ result = gate_stream_seek(blockstream, stream_offset, GATE_STREAM_SEEK_BEGIN, NULL);
319 ✗ GATE_RETURN_IF_FAILED(result);
320 ✗ return gate_stream_read_block((gate_stream_t*)blockstream, buffer, buffer_capacity, buffer_filled);
321 }
322 ✗ case 16:
323 case 32:
324 default:
325 ✗ return GATE_RESULT_NOTSUPPORTED;
326 }
327 }
328
329 ✗ static gate_result_t read_fat_dir(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
330 unsigned start_sector, unsigned sectors_count, gate_filesystem_fat_direntry_callback_t callback, void* user_param)
331 {
332 ✗ const unsigned sectorlen = bootsect->bpb.bytes_per_sector;
333 ✗ const unsigned sectorentries = sectorlen / 32;
334
335 ✗ gate_result_t result = GATE_RESULT_OK;
336 unsigned ndx, ent;
337 char buffer[4096];
338 gate_filesystem_fat_direntry_t const* ptr_direntry;
339
340 ✗ for (ndx = 0; ndx != sectors_count; ++ndx)
341 {
342 ✗ result = read_sector(blockstream, bootsect, start_sector + ndx, buffer);
343 ✗ GATE_RETURN_IF_FAILED(result);
344 ✗ ptr_direntry = (gate_filesystem_fat_direntry_t const*)buffer;
345
346 ✗ for (ent = 0; ent != sectorentries; ++ent, ++ptr_direntry)
347 {
348 ✗ switch ((unsigned char)ptr_direntry->filename[0])
349 {
350 ✗ case 0:
351 /* end of directory reached */
352 ✗ return GATE_RESULT_OK;
353 ✗ case 0xe5:
354 /* unused_entry, skip */
355 ✗ continue;
356 ✗ default:
357 ✗ if (ptr_direntry->attributes == 0x0f)
358 {
359 /* long filename, skip */
360 ✗ continue;
361 }
362
363 /* standard entry: */
364 ✗ if (!callback(ptr_direntry, user_param))
365 {
366 /* canceled by callback */
367 ✗ return GATE_RESULT_CANCELED;
368 }
369 ✗ break;
370 }
371 }
372 }
373 ✗ return result;
374 }
375
376 ✗ gate_result_t gate_filesystem_read_fat_directory(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
377 gate_string_t const* subdir, gate_filesystem_fat_direntry_callback_t callback, void* user_param)
378 {
379 ✗ const unsigned root_sector = get_first_root_dir_sector(bootsect);
380 ✗ const unsigned root_sectors_count = get_root_dir_sectors(bootsect);
381
382 ✗ gate_result_t ret = read_fat_dir(blockstream, bootsect, root_sector, root_sectors_count, callback, user_param);
383 ✗ if (ret == GATE_RESULT_CANCELED)
384 {
385 ✗ ret = GATE_RESULT_OK;
386 }
387 ✗ return ret;
388 }
389
390 struct find_fat_file_param
391 {
392 char filename[8];
393 char fileext[3];
394 gate_filesystem_fat_direntry_t matched_entry;
395 };
396
397 ✗ static gate_bool_t find_fat_file_cb(gate_filesystem_fat_direntry_t const* entry, void* user_param)
398 {
399 ✗ struct find_fat_file_param* param = (struct find_fat_file_param*)user_param;
400 ✗ if ((0 == gate_mem_compare(entry->filename, param->filename, 8))
401 ✗ && (0 == gate_mem_compare(entry->extension, param->fileext, 3)))
402 {
403 ✗ gate_mem_copy(&param->matched_entry, entry, sizeof(gate_filesystem_fat_direntry_t));
404 ✗ return false;
405 }
406 ✗ return true;
407 }
408
409
410 ✗ gate_result_t gate_filesystem_find_fat_file(gate_controlstream_t* blockstream, gate_filesystem_fat_bootsect_t const* bootsect,
411 gate_string_t const* path, gate_filesystem_fat_direntry_t* ptr_found_entry)
412 {
413 gate_result_t ret;
414 gate_size_t pos;
415 ✗ gate_size_t len = gate_string_length(path);
416 struct find_fat_file_param param;
417
418 ✗ gate_mem_clear(&param, sizeof(param));
419 ✗ gate_mem_fill(&param.filename[0], ' ', sizeof(param.filename));
420 ✗ gate_mem_fill(&param.fileext[0], ' ', sizeof(param.fileext));
421 ✗ pos = gate_string_char_pos_last(path, '.');
422 ✗ if (pos == GATE_STR_NPOS)
423 {
424 ✗ pos = len > 8 ? 8 : len;
425 ✗ gate_mem_copy(param.filename, gate_string_ptr(path, 0), pos);
426 }
427 else
428 {
429 ✗ gate_mem_copy(param.filename, gate_string_ptr(path, 0), pos > 8 ? 8 : pos);
430 ✗ if (len - pos - 1 >= 3)
431 {
432 ✗ gate_mem_copy(param.fileext, gate_string_ptr(path, pos + 1), 3);
433 }
434 else
435 {
436 ✗ gate_mem_copy(param.fileext, gate_string_ptr(path, pos + 1), len - pos - 1);
437 }
438 }
439
440 ✗ ret = gate_filesystem_read_fat_directory(blockstream, bootsect, NULL, &find_fat_file_cb, &param);
441 ✗ if (GATE_SUCCEEDED(ret))
442 {
443 ✗ if (param.matched_entry.filename[0] != 0)
444 {
445 ✗ gate_mem_copy(ptr_found_entry, &param.matched_entry, sizeof(gate_filesystem_fat_direntry_t));
446 }
447 else
448 {
449 ✗ ret = GATE_RESULT_NOMATCH;
450 }
451 }
452 ✗ return ret;
453 }
454