ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_exfat_dir.c
Go to the documentation of this file.
1
37
38#include <stddef.h>
39#include <stdint.h>
40
41#include "ra8_attributes.h"
42#include "ra8_fs.h"
43#include "ra8_fs_fat_internal.h"
44
45/* =============================================================================
46 * Path resolution
47 * =============================================================================
48 */
49
82 const exfat_dir_t* cur,
83 const char* comp,
84 uint32_t len,
85 exfat_dir_t* out)
86{
87 /* Two bounds, in two domains, and both are needed. The BYTE bound sizes the
88 * buffer, because @p comp is UTF-8 here and a 64-unit name is up to three
89 * times that many bytes. The UNIT bound below is the one the FORMAT imposes,
90 * so it is checked after conversion -- 65 ASCII characters and 65 accented
91 * ones are equally one over, and only the unit count says so (#606). */
92 if (len >= (uint32_t)k_exfat_name_u8_cap) {
94 }
95 char namebuf[k_exfat_name_u8_cap] = {};
96 for (uint32_t i = 0U; i < len; i++) {
97 namebuf[i] = comp[i];
98 }
99 namebuf[len] = '\0';
100 /* Validate the component converts within the exFAT unit cap and is well-formed
101 * UTF-8 before searching -- the byte bound above cannot see a name short in
102 * bytes yet over the 64-UNIT cap (#606). */
103 uint16_t units[k_exfat_name_cap] = {};
104 uint32_t nunits = 0U;
105 const ra8_err_t ce = priv_exfat_name_to_units(m, namebuf, units, &nunits);
106 if (ce == k_ra8_err_no_mem) {
107 return k_ra8_err_invalid_arg; /* over the name cap: an argument error */
108 }
109 if (ce != k_ra8_ok) {
110 return ce;
111 }
112 /* ::priv_exfat_find_set rather than ::priv_exfat_find, because a directory
113 * that fills up has to REWRITE its own File-entry set to grow (#677), and only
114 * find_set reports where that set lives. `pos[0]` is the File entry; carrying
115 * it in the descendant is what lets ::priv_exfat_grow_dir patch the right
116 * metadata without a second walk of the parent. */
118 uint32_t count = 0U;
119 uint8_t file_e[k_exfat_entry_bytes] = {};
120 uint8_t strm[k_exfat_entry_bytes] = {};
121 const ra8_err_t e = priv_exfat_find_set(m,
122 cur,
123 namebuf,
124 pos,
125 (uint32_t)k_exfat_set_max_entries,
126 &count,
127 file_e,
128 strm);
129 if (e != k_ra8_ok) {
130 return e;
131 }
132 if ((file_e[k_exfat_off_file_attr] & (uint8_t)k_exfat_attr_directory) == 0U) {
133 return k_ra8_err_invalid_arg; /* a file cannot be an intermediate component */
134 }
135 if (priv_rd32(&strm[k_exfat_strm_off_clus]) < (uint32_t)k_cluster_first_data) {
136 return k_ra8_err_protocol_error; /* a directory always owns a cluster */
137 }
138 priv_exfat_dir_from_set(m, strm, out);
139 out->self_cluster = pos[0].cluster;
140 out->self_index = pos[0].index;
141 return k_ra8_ok;
142}
143
144/* `priv_exfat_resolve_parent()`: see header for the documented contract. */
146 const char* path,
147 exfat_dir_t* out_parent,
148 const char** out_leaf)
149{
150 const char* p = path;
151 while (*p == '/') {
152 p++;
153 }
154 exfat_dir_t cur = {};
155 priv_exfat_dir_root(m, &cur);
156 for (uint32_t depth = 0U; depth < (uint32_t)k_exfat_path_depth; depth++) {
157 const char* end = p;
158 uint32_t nlen = 0U;
159 while (*end != '\0') {
160 if (*end == '/') {
161 break;
162 }
163 end++;
164 nlen++;
165 }
166 if (*end == '\0') {
167 *out_parent = cur;
168 *out_leaf = p;
169 return k_ra8_ok;
170 }
171 exfat_dir_t next = {};
172 const ra8_err_t e = internal_exfat_enter(m, &cur, p, nlen, &next);
173 if (e != k_ra8_ok) {
174 return e;
175 }
176 cur = next;
177 p = end;
178 while (*p == '/') {
179 p++;
180 }
181 }
182 return k_ra8_err_invalid_arg; /* deeper than k_exfat_path_depth */
183}
184
185/* `priv_exfat_resolve_dir()`: see header for the documented contract. */
187{
188 const char* p = path;
189 while (*p == '/') {
190 p++;
191 }
192 if (*p == '\0') {
193 priv_exfat_dir_root(m, out);
194 return k_ra8_ok;
195 }
196 exfat_dir_t parent = {};
197 const char* leaf = nullptr;
198 const ra8_err_t e = priv_exfat_resolve_parent(m, path, &parent, &leaf);
199 if (e != k_ra8_ok) {
200 return e;
201 }
202 const uint32_t len = priv_strlen(leaf);
203 if (len == 0U) {
204 *out = parent; /* trailing slash: the parent IS the directory */
205 return k_ra8_ok;
206 }
207 return internal_exfat_enter(m, &parent, leaf, len, out);
208}
209
210/* `priv_exfat_lookup()`: see header for the documented contract. */
212priv_exfat_lookup(const ra8_fs_mount_t* m, const char* path, uint8_t* out_strm, uint8_t* out_attr)
213{
214 exfat_dir_t parent = {};
215 const char* leaf = nullptr;
216 const ra8_err_t e = priv_exfat_resolve_parent(m, path, &parent, &leaf);
217 if (e != k_ra8_ok) {
218 return e;
219 }
220 if (priv_strlen(leaf) == 0U) {
221 return k_ra8_err_invalid_arg; /* the volume root is not an entry set */
222 }
223 return priv_exfat_find(m, &parent, leaf, out_strm, out_attr);
224}
225
226/* =============================================================================
227 * mkdir
228 * =============================================================================
229 */
230
231/* `priv_exfat_zero_cluster()`: see header for the documented contract. */
233{
234 const uint64_t base = priv_cluster_to_lba(m, clus);
235 for (uint32_t s = 0U; s < m->sectors_per_cluster; s++) {
236 const ra8_err_t e = priv_write_sector(m, base + s, k_zero_sector);
237 if (e != k_ra8_ok) {
238 return e;
239 }
240 }
241 return k_ra8_ok;
242}
243
244/* `priv_exfat_seal_cluster()`: see header for the documented contract. */
246{
247 const uint8_t unused =
248 (uint8_t)((uint8_t)k_exfat_entry_file & (uint8_t)~(uint8_t)k_exfat_inuse_bit);
249 const uint64_t base = priv_cluster_to_lba(m, clus);
250 for (uint32_t s = 0U; s < m->sectors_per_cluster; s++) {
251 uint8_t* const sec = priv_sec_io();
252 ra8_err_t e = priv_read_sector(m, base + s, sec);
253 if (e != k_ra8_ok) {
254 return e;
255 }
256 uint8_t dirty = 0U;
257 for (uint32_t off = 0U; off < priv_bps(m); off += (uint32_t)k_exfat_entry_bytes) {
258 if (sec[off] == (uint8_t)k_exfat_entry_eod) {
259 sec[off] = unused;
260 dirty = 1U;
261 }
262 }
263 if (dirty != 0U) {
264 e = priv_write_sector(m, base + s, sec);
265 if (e != k_ra8_ok) {
266 return e;
267 }
268 }
269 }
270 return k_ra8_ok;
271}
272
307 uint8_t* set,
308 const uint16_t* name,
309 uint32_t nlen,
310 uint32_t first)
311{
312 const uint32_t name_entries =
313 (nlen + (uint32_t)k_exfat_name_per_entry - 1U) / (uint32_t)k_exfat_name_per_entry;
314 const uint32_t sec_count = 1U + name_entries;
315 const uint32_t total = (1U + sec_count) * (uint32_t)k_exfat_entry_bytes;
316 for (uint32_t i = 0U; i < total; i++) {
317 set[i] = 0U;
318 }
319 set[0] = (uint8_t)k_exfat_entry_file;
320 set[k_exfat_off_file_secnt] = (uint8_t)sec_count;
322 /* Before the SetChecksum below, which covers these bytes (#601). */
324 const uint32_t cbytes = priv_cluster_bytes(m);
325 uint8_t* strm = &set[k_exfat_entry_bytes];
326 strm[0] = (uint8_t)k_exfat_entry_stream;
328 strm[k_exfat_strm_off_nlen] = (uint8_t)nlen;
330 priv_wr32(&strm[k_exfat_off_strm_valid], cbytes);
331 priv_wr32(&strm[k_exfat_strm_off_clus], first);
332 priv_wr32(&strm[k_exfat_strm_off_dlen], cbytes);
333 for (uint32_t n = 0U; n < name_entries; n++) {
334 uint8_t* ne = &set[(size_t)(2U + n) * (size_t)k_exfat_entry_bytes];
335 ne[0] = (uint8_t)k_exfat_entry_name;
336 for (uint32_t c = 0U; c < (uint32_t)k_exfat_name_per_entry; c++) {
337 const uint32_t pos = (n * (uint32_t)k_exfat_name_per_entry) + c;
338 if (pos < nlen) {
339 /* A whole UTF-16 unit, not the low byte of one -- the same correction
340 * the file-side builder needed (#606). */
341 priv_wr16(&ne[k_exfat_name_off + (c * 2U)], name[pos]);
342 }
343 }
344 }
346 return total;
347}
348
376static ra8_err_t
377internal_exfat_dir_alloc(const ra8_fs_mount_t* m, uint32_t* out_clus, uint64_t* out_bmp)
378{
379 uint32_t bclus = 0U;
380 uint32_t blen = 0U;
381 ra8_err_t e = priv_exfat_find_bitmap(m, &bclus, &blen);
382 if (e != k_ra8_ok) {
383 return e;
384 }
385 *out_bmp = priv_cluster_to_lba(m, bclus);
386 e = priv_exfat_bitmap_scan(m, *out_bmp, 1U, out_clus);
387 if (e != k_ra8_ok) {
388 return e;
389 }
390 e = priv_exfat_zero_cluster(m, *out_clus);
391 if (e != k_ra8_ok) {
392 return e;
393 }
394 e = priv_exfat_bitmap_mark(m, *out_bmp, *out_clus, 1U);
395 if (e != k_ra8_ok) {
396 return e;
397 }
398 /* Only now, with the bit actually set, is it true that the next free cluster
399 * starts past this one (#607). */
400 priv_alloc_hint_set(m, *out_clus + 1U);
401 return k_ra8_ok;
402}
403
432 const exfat_dir_t* parent,
433 const char* leaf,
434 uint32_t nlen)
435{
436 if (nlen == 0U) {
437 return k_ra8_err_invalid_arg; /* "/" or a trailing slash: the root itself */
438 }
439 if (nlen > (uint32_t)k_exfat_name_cap) {
441 }
442 uint8_t strm[k_exfat_entry_bytes] = {};
443 uint8_t attr = 0U;
444 /* mkdir REFUSES an existing name rather than replacing it, unlike
445 * ra8_fs_write_file(), which is a create-or-replace verb (#603). A directory
446 * and a file are both an entry set under that name, so both refuse here.
447 *
448 * The third verdict is the one that is easy to drop: a BACKEND FAILURE is not
449 * "the name is absent". Testing only for k_ra8_ok would turn an unreadable
450 * directory into a green light to create, and the volume would then hold two
451 * entry sets for one name -- the #603 duplicate, reintroduced through the
452 * error path instead of the happy one. */
453 const ra8_err_t fe = priv_exfat_find(m, parent, leaf, strm, &attr);
454 if (fe == k_ra8_ok) {
455 return k_ra8_err_exists;
456 }
457 if (fe != k_ra8_err_not_found) {
458 return fe;
459 }
460 return k_ra8_ok;
461}
462
463/* `priv_exfat_mkdir()`: see header for the documented contract. */
464ra8_err_t priv_exfat_mkdir(const ra8_fs_mount_t* m, const char* path)
465{
466 exfat_dir_t parent = {};
467 const char* leaf = nullptr;
468 ra8_err_t e = priv_exfat_resolve_parent(m, path, &parent, &leaf);
469 if (e != k_ra8_ok) {
470 return e;
471 }
472 /* The leaf becomes code units here, once, and every on-disk length below
473 * counts those: the slot run, `NameLength`, and the name hash. Counting the
474 * caller's BYTES instead is what made a directory whose name held one
475 * multi-byte character unopenable by that name (#606). */
476 uint16_t units[k_exfat_name_cap] = {};
477 uint32_t nlen = 0U;
478 const ra8_err_t ne = priv_exfat_name_to_units(m, leaf, units, &nlen);
479 if (ne == k_ra8_err_no_mem) {
480 return k_ra8_err_invalid_arg; /* over the name cap: an argument, not a full disk */
481 }
482 if (ne != k_ra8_ok) {
483 return ne;
484 }
485 e = internal_exfat_mkdir_check(m, &parent, leaf, nlen);
486 if (e != k_ra8_ok) {
487 return e;
488 }
489 /* Reserve the parent's slot run BEFORE allocating, so a full parent directory
490 * costs no bitmap churn and needs no rollback. */
491 const uint32_t need =
492 2U + ((nlen + (uint32_t)k_exfat_name_per_entry - 1U) / (uint32_t)k_exfat_name_per_entry);
493 uint32_t dclus = 0U;
494 uint32_t didx = 0U;
495 e = priv_exfat_find_dir_space(m, &parent, need, &dclus, &didx);
496 if (e != k_ra8_ok) {
497 return e;
498 }
499 uint32_t newclus = 0U;
500 uint64_t bmp_lba = 0U;
501 e = internal_exfat_dir_alloc(m, &newclus, &bmp_lba);
502 if (e != k_ra8_ok) {
503 return e;
504 }
505 uint8_t set[k_exfat_max_set_bytes] = {};
506 const uint32_t bytes = internal_exfat_build_dir_set(m, set, units, nlen, newclus);
507 e = priv_exfat_write_dir_set(m, dclus, didx, set, bytes);
508 if (e != k_ra8_ok) {
509 (void)priv_exfat_bitmap_clear(m, bmp_lba, newclus, 1U); /* never leak the cluster */
510 return e;
511 }
512 return k_ra8_ok;
513}
514
515/* =============================================================================
516 * rmdir
517 * =============================================================================
518 */
519
554static ra8_err_t
555internal_exfat_dir_is_empty(const ra8_fs_mount_t* m, const exfat_dir_t* dir, uint8_t* out_empty)
556{
557 exfat_cursor_t cur = {};
558 priv_exfat_cursor_init(dir, &cur);
559 while (cur.scanned < (uint32_t)k_exfat_scan_limit) {
560 uint8_t e[k_exfat_entry_bytes] = {};
561 const ra8_err_t r = priv_exfat_next_entry(m, &cur, e);
562 if (r == k_ra8_err_not_found) {
563 *out_empty = 1U; /* the run ended without an occupant and without a 0x00 */
564 return k_ra8_ok;
565 }
566 if (r != k_ra8_ok) {
567 return r;
568 }
569 if (e[0] == (uint8_t)k_exfat_entry_eod) {
570 *out_empty = 1U;
571 return k_ra8_ok;
572 }
573 if ((e[0] & (uint8_t)k_exfat_inuse_bit) == 0U) {
574 continue; /* a deleted remnant is not an occupant */
575 }
576 *out_empty = 0U;
577 return k_ra8_ok;
578 }
579 *out_empty = 1U;
580 return k_ra8_ok;
581}
582
616 const char* path,
617 exfat_setpos_t* pos,
618 uint32_t* out_count,
619 uint8_t* strm,
620 exfat_dir_t* out_dir)
621{
622 exfat_dir_t parent = {};
623 const char* leaf = nullptr;
624 ra8_err_t e = priv_exfat_resolve_parent(m, path, &parent, &leaf);
625 if (e != k_ra8_ok) {
626 return e;
627 }
628 if (priv_strlen(leaf) == 0U) {
629 return k_ra8_err_invalid_arg; /* "/" or a trailing slash: the root itself */
630 }
631 uint8_t file_e[k_exfat_entry_bytes] = {};
633 &parent,
634 leaf,
635 pos,
636 (uint32_t)k_exfat_set_max_entries,
637 out_count,
638 file_e,
639 strm);
640 if (e != k_ra8_ok) {
641 return e;
642 }
643 if ((file_e[k_exfat_off_file_attr] & (uint8_t)k_exfat_attr_directory) == 0U) {
644 return k_ra8_err_invalid_arg; /* a file: ra8_fs_unlink() is the verb for it */
645 }
646 if (priv_rd32(&strm[k_exfat_strm_off_clus]) < (uint32_t)k_cluster_first_data) {
647 return k_ra8_err_protocol_error; /* a directory always owns a cluster */
648 }
649 priv_exfat_dir_from_set(m, strm, out_dir);
650 return k_ra8_ok;
651}
652
653/* `priv_exfat_rmdir()`: see header for the documented contract. */
654ra8_err_t priv_exfat_rmdir(const ra8_fs_mount_t* m, const char* path)
655{
657 uint32_t count = 0U;
658 uint8_t strm[k_exfat_entry_bytes] = {};
659 exfat_dir_t victim = {};
660 ra8_err_t e = internal_exfat_rmdir_locate(m, path, pos, &count, strm, &victim);
661 if (e != k_ra8_ok) {
662 return e;
663 }
664 uint8_t empty = 0U;
665 e = internal_exfat_dir_is_empty(m, &victim, &empty);
666 if (e != k_ra8_ok) {
667 return e;
668 }
669 if (empty == 0U) {
670 return k_ra8_err_not_empty;
671 }
672 /* Entries first, clusters second -- the same order ::priv_exfat_unlink uses.
673 * A fault between the two leaves clusters marked used with nothing pointing
674 * at them, which a host `fsck` reports and repairs; the reverse order would
675 * leave a live entry set pointing at free space, which it cannot. */
676 e = priv_exfat_drop_set(m, pos, count);
677 if (e != k_ra8_ok) {
678 return e;
679 }
680 return priv_exfat_free_clusters(m, strm);
681}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_not_empty
Container still holds members – the operation requires it empty.
Definition ra8_err.h:258
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_protocol_error
Protocol-level error (e.g.
Definition ra8_err.h:429
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
uint64_t priv_cluster_to_lba(const ra8_fs_mount_t *m, uint32_t cluster)
Convert a cluster number into its first data-region LBA.
Definition ra8_fs_fat.c:541
ra8_err_t priv_read_sector(const ra8_fs_mount_t *m, uint64_t lba, uint8_t *buf)
Read a single sector into the module scratch buffer.
Definition ra8_fs_fat.c:134
uint32_t priv_bps(const ra8_fs_mount_t *m)
One mounted volume's sector size in bytes.
Definition ra8_fs_fat.c:84
void priv_wr32(uint8_t *p, uint32_t v)
Encode a little-endian uint32_t into a byte buffer.
Definition ra8_fs_fat.c:68
uint32_t priv_cluster_bytes(const ra8_fs_mount_t *m)
One mounted volume's cluster size in bytes.
Definition ra8_fs_fat.c:90
ra8_err_t priv_write_sector(const ra8_fs_mount_t *m, uint64_t lba, const uint8_t *buf)
Write a single sector from a caller-provided buffer.
Definition ra8_fs_fat.c:140
void priv_wr16(uint8_t *p, uint16_t v)
Encode a little-endian uint16_t into a byte buffer.
Definition ra8_fs_fat.c:61
void priv_alloc_hint_set(const ra8_fs_mount_t *m, uint32_t cluster)
Move the next-free hint forward to cluster.
uint8_t * priv_sec_io(void)
The IO-role sector buffer (leaf data / bitmap sector transfers).
const uint8_t k_zero_sector[k_ra8_fs_sector_max]
One whole sector of zero bytes, in read-only storage.
ra8_err_t priv_exfat_mkdir(const ra8_fs_mount_t *m, const char *path)
Create one exFAT directory at path.
ra8_err_t priv_exfat_seal_cluster(const ra8_fs_mount_t *m, uint32_t clus)
Replace every end-of-directory marker in a cluster with an unused one.
ra8_err_t priv_exfat_rmdir(const ra8_fs_mount_t *m, const char *path)
Remove one EMPTY exFAT directory at path.
static ra8_err_t internal_exfat_mkdir_check(const ra8_fs_mount_t *m, const exfat_dir_t *parent, const char *leaf, uint32_t nlen)
Decide whether leaf may be created in parent.
static ra8_err_t internal_exfat_dir_alloc(const ra8_fs_mount_t *m, uint32_t *out_clus, uint64_t *out_bmp)
Allocate and initialise one cluster as an empty exFAT directory.
ra8_err_t priv_exfat_zero_cluster(const ra8_fs_mount_t *m, uint32_t clus)
Zero every sector of one cluster.
static ra8_err_t internal_exfat_dir_is_empty(const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint8_t *out_empty)
Decide whether an exFAT directory holds any live entry.
static ra8_err_t internal_exfat_enter(const ra8_fs_mount_t *m, const exfat_dir_t *cur, const char *comp, uint32_t len, exfat_dir_t *out)
Descend into the named directory component within cur.
ra8_err_t priv_exfat_resolve_dir(const ra8_fs_mount_t *m, const char *path, exfat_dir_t *out)
Resolve a whole path to the directory it names.
ra8_err_t priv_exfat_lookup(const ra8_fs_mount_t *m, const char *path, uint8_t *out_strm, uint8_t *out_attr)
Resolve a path and return the leaf entry set's location fields.
ra8_err_t priv_exfat_resolve_parent(const ra8_fs_mount_t *m, const char *path, exfat_dir_t *out_parent, const char **out_leaf)
Resolve every component of path but the last.
static uint32_t internal_exfat_build_dir_set(const ra8_fs_mount_t *m, uint8_t *set, const uint16_t *name, uint32_t nlen, uint32_t first)
Build the File + Stream + Name entry set a DIRECTORY needs.
static ra8_err_t internal_exfat_rmdir_locate(const ra8_fs_mount_t *m, const char *path, exfat_setpos_t *pos, uint32_t *out_count, uint8_t *strm, exfat_dir_t *out_dir)
Resolve a path to a removable exFAT directory's entry set.
ra8_err_t priv_exfat_drop_set(const ra8_fs_mount_t *m, const exfat_setpos_t *pos, uint32_t count)
Retire an entry set by clearing the in-use bit on every entry.
ra8_err_t priv_exfat_find_dir_space(const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t need, uint32_t *out_clus, uint32_t *out_idx)
Find need consecutive free entry slots inside one directory cluster.
void priv_exfat_dir_root(const ra8_fs_mount_t *m, exfat_dir_t *out)
Fill out with the volume root's directory location.
void priv_exfat_cursor_init(const exfat_dir_t *dir, exfat_cursor_t *out)
Aim a fresh cursor at the start of dir.
ra8_err_t priv_exfat_bitmap_clear(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
Clear a contiguous cluster run in the allocation bitmap.
void priv_exfat_dir_from_set(const ra8_fs_mount_t *m, const uint8_t *strm, exfat_dir_t *out)
Build a directory location from a Stream entry's allocation fields.
ra8_err_t priv_exfat_free_clusters(const ra8_fs_mount_t *m, const uint8_t *strm)
Implementation of priv_exfat_free_clusters() – run or chain, bitmap only.
ra8_err_t priv_exfat_find_set(const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *path, exfat_setpos_t *pos, uint32_t max_pos, uint32_t *out_count, uint8_t *file_copy, uint8_t *strm_copy)
Implementation of priv_exfat_find_set() – one aligned walk of a directory.
ra8_err_t priv_exfat_name_to_units(const ra8_fs_mount_t *m, const char *path, uint16_t *out, uint32_t *out_units)
Convert a caller's exFAT name to code units, refusing what will not fold.
ra8_err_t priv_exfat_next_entry(const ra8_fs_mount_t *m, exfat_cursor_t *cur, uint8_t *out)
Fetch the next 32-byte directory entry, following the cluster chain.
uint32_t priv_strlen(const char *s)
Length of a NUL-terminated string.
ra8_err_t priv_exfat_find(const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *path, uint8_t *out_strm, uint8_t *out_attr)
Find a name in ONE exFAT directory.
ra8_err_t priv_exfat_bitmap_mark(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
Mark a contiguous cluster run as allocated in the bitmap.
ra8_err_t priv_exfat_bitmap_scan(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t need, uint32_t *out_clus)
Find a contiguous free run, starting from the mount's next-free hint.
uint16_t priv_exfat_set_checksum(const uint8_t *set, uint32_t bytes)
Compute the SetChecksum over a built directory entry set.
uint16_t priv_exfat_name_hash(const uint16_t *name, uint32_t nlen)
Compute the exFAT NameHash for a name in UTF-16 code units.
ra8_err_t priv_exfat_write_dir_set(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t idx, const uint8_t *set, uint32_t bytes)
Write a pre-built entry set into consecutive directory entries.
ra8_err_t priv_exfat_find_bitmap(const ra8_fs_mount_t *m, uint32_t *out_clus, uint32_t *out_len)
Locate the allocation-bitmap entry in the exFAT root directory.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
void priv_exfat_file_stamp_create(uint8_t *file_entry)
Stamp a fresh exFAT File entry's create, modify, and access fields.
@ k_exfat_name_off
File-name entry character offset.
@ k_exfat_strm_off_dlen
Stream-ext DataLength (low 32 used).
@ k_exfat_strm_off_nlen
Stream-ext NameLength (UTF-16 units).
@ k_exfat_strm_off_clus
Stream-ext FirstCluster.
@ k_exfat_strm_off_flags
Stream-ext GeneralSecondaryFlags.
@ k_exfat_path_depth
Max nested components per exFAT path.
@ k_exfat_off_file_csum
File entry: SetChecksum (2 bytes).
@ k_exfat_name_per_entry
UTF-16 units per file-name entry.
@ k_exfat_entry_bytes
Directory entry size.
@ k_exfat_off_strm_valid
Stream entry: ValidDataLength (8 B).
@ k_exfat_off_strm_hash
Stream entry: NameHash (2 bytes).
@ k_exfat_entry_eod
End-of-directory marker.
@ k_exfat_secflag_alloc
AllocationPossible | NoFatChain.
@ k_exfat_entry_stream
Stream-extension entry.
@ k_exfat_off_file_attr
File entry: FileAttributes (2 bytes).
@ k_exfat_off_file_secnt
File entry: SecondaryCount.
@ k_exfat_name_u8_cap
That name in UTF-8: 3 * 64, plus a NUL.
@ k_exfat_max_set_bytes
(2 + ceil(64/15)) * 32 = 7 entries.
@ k_exfat_scan_limit
Max dir entries scanned (P10 bound).
@ k_exfat_attr_directory
FileAttributes: directory.
@ k_exfat_entry_name
File-name entry.
@ k_exfat_name_cap
Longest name we store, in UTF-16 units.
@ k_exfat_inuse_bit
Directory entry type bit 7 = in use.
@ k_exfat_entry_file
File directory entry.
@ k_exfat_set_max_entries
1 File + 1 Stream + 17 Name entries.
@ k_cluster_first_data
Cluster numbers start at 2.
Linear cursor over a directory's 32-byte entries.
uint32_t scanned
Total entries read (P10 bound).
Identifies the exFAT directory a lookup, scan or link operates in.
uint32_t self_cluster
Parent cluster holding this dir's File entry; 0 => root.
uint32_t self_index
File-entry index within self_cluster.
Directory position (cluster + entry index) of one 32-byte entry.
uint32_t index
Entry index within that cluster (0-based).
uint32_t cluster
Directory cluster holding the entry.
Cached parse of one mounted FAT volume.
uint32_t sectors_per_cluster
BPB BPB_SecPerClus.