GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/filesystems.c
Date: 2025-09-14 13:10:38
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