GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/filesystems.c
Date: 2025-12-12 23:40: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 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 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