ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_dir.c
Go to the documentation of this file.
1
16
17#include <stddef.h>
18#include <stdint.h>
19#include <string.h>
20
21#include "ra8_attributes.h"
22#include "ra8_fs.h"
23#include "ra8_fs_fat_internal.h"
24
33
34static_assert(sizeof(ra8_fs_dir_private_t) <= (size_t)k_ra8_fs_dir_state_bytes,
35 "ra8_fs directory cursor state exceeds public storage");
36static_assert(alignof(ra8_fs_dir_private_t) <= alignof(uint64_t),
37 "ra8_fs directory cursor state alignment exceeds public storage");
38
39/* =============================================================================
40 * Public API: listdir / unlink
41 * =============================================================================
42 */
43
72 const uint8_t* buf,
73 lfn_state_t* lfn,
75 void* ctx)
76{
77 for (uint32_t e = 0; e < priv_dir_eps(m); e++) {
78 const uint8_t* ent = &buf[(size_t)e * (size_t)k_ra8_fs_dir_entry_bytes];
80 return 1U;
81 }
83 priv_lfn_reset(lfn); /* a deleted slot breaks any in-progress chain */
84 continue;
85 }
87 priv_lfn_add(lfn, ent);
88 continue;
89 }
90 if (ent[k_dir_off_name] == k_dir_marker_dot) {
91 priv_lfn_reset(lfn); /* synthetic "." / ".." -- not reported to the caller */
92 continue;
93 }
94 /* +1 for the NUL: a maximal 8.3 name "12345678.123" is 12 chars, and
95 * priv_83_to_str writes the terminator at index 12 -- without the slack
96 * that store overflows the buffer and corrupts the adjacent `ent` pointer,
97 * mismatching the LFN checksum so the long name is dropped (matches the
98 * `+ 1U` sizing already used in ra8_fs_fat_file.c). */
99 char short_name[(uint32_t)k_ra8_fs_short_name_len + 1U] = {};
100 /* The NTRes case flags come along: an entry this driver wrote for
101 * `data.log` stores `DATA LOG` with both flags set, and reporting it as
102 * `DATA.LOG` would lose the caller's own name for no reason (#600). */
103 priv_83_to_str(&ent[k_dir_off_name], ent[k_dir_off_ntres], short_name);
104 /* Report the VFAT long name when the preceding chain matches; else the 8.3.
105 * The chain is UTF-16 on disk and UTF-8 at this boundary, and a chain that
106 * does not convert -- an unpaired surrogate, which nothing conforming
107 * writes -- falls back to the alias rather than being reported under a name
108 * that would not re-open it (#606). */
109 char lname[k_lfn_utf8_cap] = {};
110 uint32_t lnunits = 0U;
111 const uint16_t* lunits = priv_lfn_units_for(lfn, &ent[k_dir_off_name], &lnunits);
112 uint8_t have_long = 0U;
113 if (lunits != nullptr) {
114 have_long = (priv_utf16_to_utf8(lunits, lnunits, lname, (uint32_t)k_lfn_utf8_cap) == k_ra8_ok)
115 ? 1U
116 : 0U;
117 }
118 const uint32_t size = priv_rd32(&ent[k_dir_off_file_size]);
119 cb((have_long != 0U) ? lname : short_name, ent[k_dir_off_attr], size, ctx);
120 priv_lfn_reset(lfn);
121 }
122 return 0U;
123}
124
158RA8_EXPECTS_LOCK("ra8_fs_lock")
160 const char* path,
162 void* ctx)
163{
164 if (handle == nullptr || cb == nullptr || path == nullptr) {
165 return k_ra8_err_null_ptr;
166 }
167 if (handle->in_use == 0U) {
168 return k_ra8_err_invalid_state;
169 }
170 if (handle->type == k_ra8_fs_type_exfat) {
171 /* Any exFAT directory, not just the root (#605). A path that names nothing
172 * reports not_found and one that names a FILE reports invalid_arg, which is
173 * what the FAT side has always answered -- the two filesystems no longer
174 * disagree about what "list this path" means. */
175 exfat_dir_t dir = {};
176 const ra8_err_t xerr = priv_exfat_resolve_dir(handle, path, &dir);
177 if (xerr != k_ra8_ok) {
178 return xerr;
179 }
180 return priv_exfat_listdir(handle, &dir, cb, ctx);
181 }
182 dir_loc_t loc = {};
183 const ra8_err_t rerr = priv_resolve_dir(handle, path, &loc);
184 if (rerr != k_ra8_ok) {
185 return rerr;
186 }
187 dir_walk_t w = {};
188 priv_dir_walk_init_loc(handle, &loc, &w);
189 lfn_state_t lfn = {}; /* persists across sectors -- LFN chains can straddle them */
190 priv_lfn_reset(&lfn);
191 uint8_t eod = 0;
192 uint8_t* const buf = priv_sec_walk();
193 while (eod == 0U) {
194 ra8_err_t err = priv_read_sector(handle, w.cur_lba, buf);
195 if (err != k_ra8_ok) {
196 return err;
197 }
198 if (internal_listdir_visit_sector(handle, buf, &lfn, cb, ctx) != 0U) {
199 return k_ra8_ok;
200 }
201 err = priv_dir_walk_next_sector(handle, &w, &eod);
202 if (err != k_ra8_ok) {
203 return err;
204 }
205 }
206 return k_ra8_ok;
207}
208
209RA8_EXPECTS_LOCK("ra8_fs_lock")
226{
227 uint8_t end = 0U;
228 const ra8_err_t step = priv_dir_walk_next_sector(state->mount, &state->fat_walk, &end);
229 if (end != 0U) {
230 state->finished = true;
231 }
232 return step;
233}
234
235RA8_EXPECTS_LOCK("ra8_fs_lock")
256 const uint8_t* sector,
257 ra8_fs_dirent_t* out,
258 bool* out_entry)
259{
260 while (state->fat_walk.entry_idx < priv_bps(state->mount)) {
261 const uint8_t* entry = &sector[state->fat_walk.entry_idx];
262 state->fat_walk.entry_idx += (uint32_t)k_ra8_fs_dir_entry_bytes;
263 if (entry[k_dir_off_name] == k_dir_marker_free_perm) {
264 state->finished = true;
265 return;
266 }
268 priv_lfn_reset(&state->fat_lfn);
269 continue;
270 }
271 if (entry[k_dir_off_attr] == k_ra8_fs_attr_lfn) {
272 priv_lfn_add(&state->fat_lfn, entry);
273 continue;
274 }
275 if (entry[k_dir_off_name] == k_dir_marker_dot) {
276 priv_lfn_reset(&state->fat_lfn);
277 continue;
278 }
279 char short_name[(uint32_t)k_ra8_fs_short_name_len + 1U] = {};
280 char long_name[k_lfn_utf8_cap] = {};
281 priv_83_to_str(&entry[k_dir_off_name], entry[k_dir_off_ntres], short_name);
282 uint32_t units = 0U;
283 const uint16_t* name_units =
284 priv_lfn_units_for(&state->fat_lfn, &entry[k_dir_off_name], &units);
285 const char* name = short_name;
286 if (name_units != nullptr) {
287 if (priv_utf16_to_utf8(name_units, units, long_name, (uint32_t)sizeof(long_name)) ==
288 k_ra8_ok) {
289 name = long_name;
290 }
291 }
292 (void)memcpy(out->name, name, strlen(name) + 1U);
293 out->attr = entry[k_dir_off_attr];
294 out->size_bytes = (uint64_t)priv_rd32(&entry[k_dir_off_file_size]);
295 priv_lfn_reset(&state->fat_lfn);
296 *out_entry = true;
297 return;
298 }
299}
300
301RA8_EXPECTS_LOCK("ra8_fs_lock")
321{
322 *out_entry = false;
323 while (!state->finished) {
324 if (state->fat_walk.entry_idx >= priv_bps(state->mount)) {
325 const ra8_err_t step = internal_fat_dir_advance_sector(state);
326 if (step != k_ra8_ok) {
327 return step;
328 }
329 if (state->finished) {
330 return k_ra8_ok;
331 }
332 }
333 uint8_t* const sector = priv_sec_walk();
334 const ra8_err_t err = priv_read_sector(state->mount, state->fat_walk.cur_lba, sector);
335 if (err != k_ra8_ok) {
336 return err;
337 }
338 internal_fat_dir_scan_sector(state, sector, out, out_entry);
339 if (*out_entry || state->finished) {
340 return k_ra8_ok;
341 }
342 }
343 return k_ra8_ok;
344}
345
346RA8_EXPECTS_LOCK("ra8_fs_lock")
367{
368 if (handle->in_use == 0U) {
369 return k_ra8_err_invalid_state;
370 }
371 *state = (ra8_fs_dir_private_t){.mount = handle};
372 ra8_err_t err = k_ra8_ok;
373 if (handle->type == k_ra8_fs_type_exfat) {
374 exfat_dir_t dir = {};
375 err = priv_exfat_resolve_dir(handle, path, &dir);
376 if (err == k_ra8_ok) {
377 priv_exfat_cursor_init(&dir, &state->exfat_cursor);
378 }
379 } else {
380 dir_loc_t location = {};
381 err = priv_resolve_dir(handle, path, &location);
382 if (err == k_ra8_ok) {
383 priv_dir_walk_init_loc(handle, &location, &state->fat_walk);
384 priv_lfn_reset(&state->fat_lfn);
385 }
386 }
387 return err;
388}
389
422 const char* path,
423 dir_target_t* out,
424 uint32_t* out_cluster)
425{
426 const char* leaf = nullptr;
427 const ra8_err_t rerr = priv_resolve_parent(handle, path, &out->parent, &leaf);
428 if (rerr != k_ra8_ok) {
429 return rerr;
430 }
431 const ra8_err_t err =
432 priv_dir_lookup_any(handle, &out->parent, leaf, &out->lba, &out->off, out->entry);
433 if (err != k_ra8_ok) {
434 return err;
435 }
436 /* The lookup matches on the name alone, so a directory matches like any
437 * other entry. Freeing its chain orphans every file inside it -- their
438 * clusters stay allocated in the FAT with nothing referencing them, which
439 * fsck.fat reports as lost clusters (#604). */
440 if ((out->entry[k_dir_off_attr] & (uint8_t)k_ra8_fs_attr_directory) != 0U) {
441 return k_ra8_err_invalid_arg; /* a directory: ra8_fs_rmdir() is the verb */
442 }
443 /* Honor the read-only attribute (#681): a file a host marked read-only is not
444 * deletable through the ordinary path, matching DOS/Windows `del`. Refused
445 * before priv_free_chain() touches the FAT, so a denied unlink changes nothing. */
446 if ((out->entry[k_dir_off_attr] & (uint8_t)k_ra8_fs_attr_read_only) != 0U) {
448 }
449 *out_cluster = priv_entry_first_cluster(out->entry);
450 return k_ra8_ok;
451}
452
484RA8_EXPECTS_LOCK("ra8_fs_lock")
485static ra8_err_t internal_unlink_locked(const ra8_fs_mount_t* handle, const char* path)
486{
487 if (handle == nullptr || path == nullptr) {
488 return k_ra8_err_null_ptr;
489 }
490 if (handle->in_use == 0U) {
491 return k_ra8_err_invalid_state;
492 }
493 if (handle->type == k_ra8_fs_type_exfat) {
494 return priv_exfat_unlink(handle, path);
495 }
496 dir_target_t t = {};
497 uint32_t cluster = 0;
498 const ra8_err_t lerr = internal_unlink_locate(handle, path, &t, &cluster);
499 if (lerr != k_ra8_ok) {
500 return lerr;
501 }
502 if (cluster >= (uint32_t)k_cluster_first_data) {
503 const ra8_err_t ferr = priv_free_chain(handle, cluster);
504 if (ferr != k_ra8_ok) {
505 return ferr;
506 }
507 }
508 /* The whole chain, not only the 8.3 entry: clearing one slot left the file's
509 * long-name entries on disk with a checksum nothing matched, which fsck.fat
510 * and chkdsk both report as orphans (#600). */
511 return priv_dir_erase_chain(handle, &t.parent, t.lba, t.off, &t.entry[k_dir_off_name]);
512}
513
546 const char* old_path,
547 const char* new_path,
548 dir_loc_t* out_parent,
549 const char** out_old,
550 const char** out_new)
551{
552 dir_loc_t op = {};
553 const char* ol = nullptr;
554 ra8_err_t e1 = priv_resolve_parent(handle, old_path, &op, &ol);
555 if (e1 != k_ra8_ok) {
556 return e1;
557 }
558 dir_loc_t np = {};
559 const char* nl = nullptr;
560 ra8_err_t e2 = priv_resolve_parent(handle, new_path, &np, &nl);
561 if (e2 != k_ra8_ok) {
562 return e2;
563 }
564 if (op.is_root != np.is_root) {
566 }
567 if (op.is_root == 0U) {
568 if (op.cluster != np.cluster) {
570 }
571 }
572 *out_parent = op;
573 *out_old = ol;
574 *out_new = nl;
575 return k_ra8_ok;
576}
577
618static ra8_err_t
619internal_fat_rename(const ra8_fs_mount_t* handle, const char* old_path, const char* new_path)
620{
621 dir_target_t t = {};
622 const char* ol = nullptr;
623 const char* nl = nullptr;
624 ra8_err_t perr = internal_rename_prepare(handle, old_path, new_path, &t.parent, &ol, &nl);
625 if (perr != k_ra8_ok) {
626 return perr;
627 }
628 uint64_t dup_lba = 0U;
629 uint32_t dup_off = 0U;
630 uint8_t dup[k_ra8_fs_dir_entry_bytes] = {};
631 if (priv_dir_lookup_any(handle, &t.parent, nl, &dup_lba, &dup_off, dup) == k_ra8_ok) {
632 return k_ra8_err_exists;
633 }
634 ra8_err_t err = priv_dir_lookup_any(handle, &t.parent, ol, &t.lba, &t.off, t.entry);
635 if (err != k_ra8_ok) {
636 return err;
637 }
638 /* Honor the read-only attribute (#681): a read-only file is refused before any
639 * new entry is filed or the old chain is erased, so the directory is untouched. */
640 if ((t.entry[k_dir_off_attr] & (uint8_t)k_ra8_fs_attr_read_only) != 0U) {
642 }
643 dir_insert_t plan = {};
644 err = priv_dir_reserve(handle, &t.parent, nl, &plan);
645 if (err != k_ra8_ok) {
646 return err;
647 }
648 /* Access date only, deliberately. A rename changes the name, not the bytes,
649 * so advancing DIR_WrtDate would tell every rsync, backup and "newest image"
650 * OTA heuristic that the contents changed -- the same class of false signal
651 * #601 exists to remove, only inverted. FAT has no metadata-change field, so
652 * the access date is the honest record that the entry was touched. It goes on
653 * the template BEFORE the commit, because since #600 a rename writes a new
654 * entry rather than editing the old one in place. */
656 uint64_t new_lba = 0U;
657 uint32_t new_off = 0U;
658 err = priv_dir_commit(handle, &plan, t.entry, &new_lba, &new_off);
659 if (err != k_ra8_ok) {
660 return err;
661 }
662 return priv_dir_erase_chain(handle, &t.parent, t.lba, t.off, &t.entry[k_dir_off_name]);
663}
664
682RA8_EXPECTS_LOCK("ra8_fs_lock")
683static ra8_err_t
684internal_rename_locked(const ra8_fs_mount_t* handle, const char* old_path, const char* new_path)
685{
686 if (handle == nullptr) {
687 return k_ra8_err_null_ptr;
688 }
689 if (old_path == nullptr) {
690 return k_ra8_err_null_ptr;
691 }
692 if (new_path == nullptr) {
693 return k_ra8_err_null_ptr;
694 }
695 if (handle->in_use == 0U) {
696 return k_ra8_err_invalid_state;
697 }
698 if (handle->type == k_ra8_fs_type_exfat) {
699 return priv_exfat_rename(handle, old_path, new_path);
700 }
701 return internal_fat_rename(handle, old_path, new_path);
702}
703
704/* =============================================================================
705 * Public entry points -- the lock brackets
706 * =============================================================================
707 */
708
709RA8_OWNS_RESOURCE("ra8_fs_lock")
711ra8_fs_listdir(const ra8_fs_mount_t* handle, const char* path, ra8_fs_listdir_cb_t cb, void* ctx)
712{
714 const ra8_err_t err = internal_listdir_locked(handle, path, cb, ctx);
716 return err;
717}
718
719RA8_OWNS_RESOURCE("ra8_fs_lock")
720ra8_err_t ra8_fs_dir_open(ra8_fs_mount_t* handle, const char* path, ra8_fs_dir_t* directory)
721{
722 if (handle == nullptr) {
723 return k_ra8_err_null_ptr;
724 }
725 if (path == nullptr) {
726 return k_ra8_err_null_ptr;
727 }
728 if (directory == nullptr) {
729 return k_ra8_err_null_ptr;
730 }
731 if (directory->is_open) {
732 return k_ra8_err_busy;
733 }
734 void* state_memory = directory->state;
735 ra8_fs_dir_private_t* const state = state_memory;
737 const ra8_err_t err = internal_dir_open_locked(handle, path, state);
739 if (err == k_ra8_ok) {
740 directory->is_open = true;
741 } else {
742 (void)memset(directory->state, 0, sizeof(directory->state));
743 }
744 return err;
745}
746
747RA8_OWNS_RESOURCE("ra8_fs_lock")
748ra8_err_t ra8_fs_dir_next(ra8_fs_dir_t* directory, ra8_fs_dirent_t* out, bool* out_entry)
749{
750 if (directory == nullptr) {
751 return k_ra8_err_null_ptr;
752 }
753 if (out == nullptr) {
754 return k_ra8_err_null_ptr;
755 }
756 if (out_entry == nullptr) {
757 return k_ra8_err_null_ptr;
758 }
759 if (!directory->is_open) {
760 return k_ra8_err_invalid_state;
761 }
762 *out = (ra8_fs_dirent_t){};
763 *out_entry = false;
764 void* state_memory = directory->state;
765 ra8_fs_dir_private_t* const state = state_memory;
767 ra8_err_t err = (state->mount->in_use != 0U) ? k_ra8_ok : k_ra8_err_invalid_state;
768 if (err == k_ra8_ok) {
769 if (!state->finished) {
770 if (state->mount->type == k_ra8_fs_type_exfat) {
771 err = priv_exfat_dir_next(state->mount, &state->exfat_cursor, out, out_entry);
772 } else {
773 err = internal_fat_dir_next(state, out, out_entry);
774 }
775 }
776 }
777 if (err == k_ra8_ok) {
778 if (!*out_entry) {
779 state->finished = true;
780 }
781 }
783 return err;
784}
785
787{
788 if (directory == nullptr) {
789 return k_ra8_err_null_ptr;
790 }
791 if (!directory->is_open) {
793 }
794 *directory = (ra8_fs_dir_t){};
795 return k_ra8_ok;
796}
797
798RA8_OWNS_RESOURCE("ra8_fs_lock")
799ra8_err_t ra8_fs_unlink(const ra8_fs_mount_t* handle, const char* path)
800{
802 const ra8_err_t err = internal_unlink_locked(handle, path);
804 return err;
805}
806
807RA8_OWNS_RESOURCE("ra8_fs_lock")
808ra8_err_t ra8_fs_rename(const ra8_fs_mount_t* handle, const char* old_path, const char* new_path)
809{
811 const ra8_err_t err = internal_rename_locked(handle, old_path, new_path);
813 return err;
814}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_OWNS_RESOURCE(kind)
RAII-style resource ownership contract.
#define RA8_EXPECTS_LOCK(name)
The function expects the named thread/IRQ lock to be held on entry.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ 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_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_access_denied
Operation refused because the target is protected against it.
Definition ra8_err.h:276
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
ra8_err_t ra8_fs_listdir(const ra8_fs_mount_t *handle, const char *path, ra8_fs_listdir_cb_t cb, void *ctx)
Enumerate directory entries; invoke cb once per visible entry.
ra8_err_t ra8_fs_dir_next(ra8_fs_dir_t *directory, ra8_fs_dirent_t *out, bool *out_entry)
Copy the next visible directory entry.
ra8_err_t ra8_fs_rename(const ra8_fs_mount_t *handle, const char *old_path, const char *new_path)
Rename a file within its own directory.
ra8_err_t ra8_fs_unlink(const ra8_fs_mount_t *handle, const char *path)
Delete a file: mark its dir entry deleted and free its clusters.
ra8_err_t ra8_fs_dir_open(ra8_fs_mount_t *handle, const char *path, ra8_fs_dir_t *directory)
Open a caller-owned directory cursor without holding the filesystem lock.
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
uint32_t priv_dir_eps(const ra8_fs_mount_t *m)
Directory entries per sector on one mounted volume.
Definition ra8_fs_fat.c:96
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
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
static ra8_err_t internal_rename_prepare(const ra8_fs_mount_t *handle, const char *old_path, const char *new_path, dir_loc_t *out_parent, const char **out_old, const char **out_new)
Resolve a rename's old/new paths to a shared parent and two leaves.
static void internal_fat_dir_scan_sector(ra8_fs_dir_private_t *state, const uint8_t *sector, ra8_fs_dirent_t *out, bool *out_entry)
Scan one already-read sector's remaining entries for one visible entry.
static ra8_err_t internal_fat_dir_advance_sector(ra8_fs_dir_private_t *state)
Advance an independent FAT cursor to its next directory sector.
static ra8_err_t internal_unlink_locate(const ra8_fs_mount_t *handle, const char *path, dir_target_t *out, uint32_t *out_cluster)
Resolve a path to a deletable FILE's entry and first cluster.
static ra8_err_t internal_fat_dir_next(ra8_fs_dir_private_t *state, ra8_fs_dirent_t *out, bool *out_entry)
Copy one visible FAT entry from an independent cursor.
static ra8_err_t internal_dir_open_locked(ra8_fs_mount_t *handle, const char *path, ra8_fs_dir_private_t *state)
Resolve and initialize one private filesystem cursor.
static ra8_err_t internal_rename_locked(const ra8_fs_mount_t *handle, const char *old_path, const char *new_path)
Rename – the guarded body of ra8_fs_rename().
static uint8_t internal_listdir_visit_sector(const ra8_fs_mount_t *m, const uint8_t *buf, lfn_state_t *lfn, ra8_fs_listdir_cb_t cb, void *ctx)
Visit every visible entry in one already-loaded directory sector.
static ra8_err_t internal_listdir_locked(const ra8_fs_mount_t *handle, const char *path, ra8_fs_listdir_cb_t cb, void *ctx)
Enumerate a directory – the guarded body of ra8_fs_listdir().
static ra8_err_t internal_unlink_locked(const ra8_fs_mount_t *handle, const char *path)
Delete a file – the guarded body of ra8_fs_unlink().
static ra8_err_t internal_fat_rename(const ra8_fs_mount_t *handle, const char *old_path, const char *new_path)
Rename by writing the new entry first, then taking the old one away.
ra8_err_t ra8_fs_dir_close(ra8_fs_dir_t *directory)
Consume one open directory cursor.
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.
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_listdir(const ra8_fs_mount_t *m, const exfat_dir_t *dir, ra8_fs_listdir_cb_t cb, void *ctx)
Enumerate ONE directory of an exFAT volume.
ra8_err_t priv_exfat_dir_next(const ra8_fs_mount_t *m, exfat_cursor_t *cur, ra8_fs_dirent_t *out, bool *out_entry)
Copy the next visible exFAT directory entry from an existing cursor.
ra8_err_t priv_resolve_dir(const ra8_fs_mount_t *m, const char *path, dir_loc_t *out)
Resolve a whole path to the directory it names.
uint32_t priv_entry_first_cluster(const uint8_t *entry)
Read the first cluster from a 32-byte directory entry.
ra8_err_t priv_resolve_parent(const ra8_fs_mount_t *m, const char *path, dir_loc_t *out_parent, const char **out_leaf)
Resolve all-but-the-last path component to a parent directory.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
ra8_err_t priv_free_chain(const ra8_fs_mount_t *m, uint32_t start)
Free an entire cluster chain starting at start.
void priv_lfn_add(lfn_state_t *s, const uint8_t *ent)
Fold one LFN directory entry's 13 UTF-16LE code units into the state.
const uint16_t * priv_lfn_units_for(const lfn_state_t *s, const uint8_t *name83, uint32_t *out_units)
Code units of the chain that precedes name83, or NULL if none.
void priv_lfn_reset(lfn_state_t *s)
Reset the LFN reassembly state so a fresh chain can start.
ra8_err_t priv_dir_reserve(const ra8_fs_mount_t *m, const dir_loc_t *loc, const char *leaf, dir_insert_t *out)
Decide how a name will be stored and set aside the slots for it.
ra8_err_t priv_dir_erase_chain(const ra8_fs_mount_t *m, const dir_loc_t *loc, uint64_t lba, uint32_t off, const uint8_t *name83)
Delete a directory entry together with its long-name chain.
ra8_err_t priv_dir_lookup_any(const ra8_fs_mount_t *m, const dir_loc_t *loc, const char *leaf, uint64_t *out_lba, uint32_t *out_off, uint8_t out_entry[k_ra8_fs_dir_entry_bytes])
Resolve one leaf name by 8.3 first and by long name second.
ra8_err_t priv_dir_commit(const ra8_fs_mount_t *m, const dir_insert_t *plan, const uint8_t *tmpl, uint64_t *out_lba, uint32_t *out_off)
Write a reserved run: the long-name chain, then the 8.3 entry.
void priv_lock_release(void)
Drop the library lock taken by priv_lock_acquire.
void priv_lock_acquire(void)
Take the library lock, if the caller installed one.
void priv_dir_walk_init_loc(const ra8_fs_mount_t *m, const dir_loc_t *loc, dir_walk_t *w)
Initialise a directory walker for an arbitrary directory location.
ra8_err_t priv_dir_walk_next_sector(const ra8_fs_mount_t *m, dir_walk_t *w, uint8_t *out_eod)
Advance the walker to the next sector.
void priv_83_to_str(const uint8_t *in11, uint8_t ntres, char *out13)
Unpack on-disk 11-byte 8.3 name into NUL-terminated "NAME.EXT".
void priv_fat_entry_stamp_access(uint8_t *entry)
Advance only a FAT directory entry's last-access date.
@ k_dir_off_attr
MS FAT spec sec 6 "DIR_Attr".
@ k_dir_off_ntres
MS FAT spec sec 6 "DIR_NTRes".
@ k_dir_off_file_size
MS FAT spec sec 6 "DIR_FileSize".
@ k_dir_off_name
MS FAT spec sec 6 "DIR_Name" (11 bytes).
@ k_dir_marker_free_used
Slot was used, deleted.
@ k_dir_marker_free_perm
End-of-directory.
@ k_cluster_first_data
Cluster numbers start at 2.
@ k_lfn_utf8_cap
A 247-unit name in UTF-8: 3 * 247, + NUL.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_attr_lfn
Long-file-name marker (we skip).
@ k_ra8_fs_attr_directory
MS FAT spec sec 6 "ATTR_DIRECTORY".
@ k_ra8_fs_attr_read_only
MS FAT spec sec 6 "ATTR_READ_ONLY".
@ k_ra8_fs_short_name_len
8.3 short name "AAAAAAAA.EXT" + NUL.
void(* ra8_fs_listdir_cb_t)(const char *name, uint8_t attr, uint64_t size, void *ctx)
Callback invoked once per directory entry by ra8_fs_listdir().
@ k_ra8_fs_dir_entry_bytes
MS FAT spec sec 6 "Directory Entry".
@ k_ra8_fs_dir_state_bytes
Opaque caller-owned cursor state.
ra8_err_t priv_utf16_to_utf8(const uint16_t *in, uint32_t units, char *out, uint32_t cap)
Convert UTF-16LE code units into a NUL-terminated UTF-8 name.
Definition ra8_fs_utf.c:499
Everything decided about a new directory entry before anything is written.
Identifies the directory a lookup/scan should operate in.
uint8_t is_root
1 => the volume root; 0 => the subdirectory at cluster.
uint32_t cluster
First cluster of the subdirectory (ignored when root).
One resolved directory entry: where it is, and what is in it.
uint32_t off
Byte offset within that sector.
uint8_t entry[k_ra8_fs_dir_entry_bytes]
The entry as read from disk.
dir_loc_t parent
Directory the entry lives in.
uint64_t lba
Sector holding the entry.
Internal cursor used by the directory iterator.
uint64_t cur_lba
Currently loaded LBA.
Linear cursor over a directory's 32-byte entries.
In-progress reassembly of one LFN chain across the directory scan.
Private state stored inside one public opaque directory cursor.
bool finished
Clean end-of-directory was observed.
dir_walk_t fat_walk
FAT sector and cluster position.
ra8_fs_mount_t * mount
Mounted volume retained by open.
exfat_cursor_t exfat_cursor
exFAT entry-set position.
lfn_state_t fat_lfn
FAT long-name state across sectors.
Caller-owned opaque directory cursor.
bool is_open
Facade lifecycle guard.
Stable directory-entry value copied by ra8_fs_dir_next.
Cached parse of one mounted FAT volume.