ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_exfat_check.c
Go to the documentation of this file.
1
43
44#include <stddef.h>
45#include <stdint.h>
46
47#include "ra8_attributes.h"
48#include "ra8_fs.h"
49#include "ra8_fs_check.h"
51#include "ra8_fs_fat_internal.h"
52
68
84typedef struct {
86 uint32_t top;
87 uint8_t truncated;
89
107static uint32_t internal_exchk_popcount8(uint8_t b)
108{
109 uint32_t n = 0U;
110 for (uint32_t i = 0U; i < (uint32_t)k_exchk_bits_per_byte; i++) {
111 n += ((uint32_t)b >> i) & 1U;
112 }
113 return n;
114}
115
116/* =============================================================================
117 * Marking referenced clusters
118 * =============================================================================
119 */
120
146static void internal_exchk_mark_run(ra8_fs_check_ctx_t* ctx, uint32_t first, uint64_t nclus)
147{
148 if (!priv_check_in_range(ctx, first)) {
150 return;
151 }
152 uint32_t n = (uint32_t)nclus;
153 const uint32_t available = ctx->rep->clusters_total - (first - (uint32_t)k_cluster_first_data);
154 if (nclus > (uint64_t)available) {
155 n = available;
156 ctx->rep->entries_bad++;
158 }
159 for (uint32_t i = 0U; i < n; i++) {
160 const uint32_t c = first + i;
162 return;
163 }
164 }
165}
166
168void ra8_fs_check_test_exfat_mark_run(ra8_fs_check_ctx_t* ctx, uint32_t first, uint64_t nclus)
169{
170 internal_exchk_mark_run(ctx, first, nclus);
171}
172
200{
201 uint32_t c = first;
202 for (uint32_t hop = 0U; hop < ctx->rep->clusters_total; hop++) {
204 return k_ra8_ok;
205 }
206 uint32_t v = 0U;
207 const ra8_err_t err = priv_fat_get(ctx->m, c, &v);
208 if (err != k_ra8_ok) {
209 return err;
210 }
211 if (priv_is_eoc(ctx->m, v) != 0U) {
212 return k_ra8_ok;
213 }
214 if (!priv_check_in_range(ctx, v)) {
216 return k_ra8_ok;
217 }
218 c = v;
219 }
221 return k_ra8_ok;
222}
223
229
258{
259 uint32_t c = dir->cluster;
260 for (uint32_t hop = 0U; hop < ctx->rep->clusters_total; hop++) {
262 return k_ra8_ok;
263 }
264 uint32_t next = 0U;
265 const ra8_err_t e = priv_exfat_step_cluster(ctx->m, c, dir->contig_end, &next);
266 if (e == k_ra8_err_not_found) {
267 return k_ra8_ok; /* the run ended */
268 }
269 if (e != k_ra8_ok) {
270 return e;
271 }
272 c = next;
273 }
275 return k_ra8_ok;
276}
277
280{
281 const exfat_dir_t dir = {.cluster = first};
282 return internal_exchk_mark_dir_alloc(ctx, &dir);
283}
284
307static void internal_exchk_system_run(ra8_fs_check_ctx_t* ctx, const uint8_t* e)
308{
309 const uint32_t first = priv_rd32(&e[k_exfat_strm_off_clus]);
310 const uint64_t bytes = priv_rd64(&e[k_exfat_strm_off_dlen]);
311 const uint32_t cbytes = priv_cluster_bytes(ctx->m);
312 const uint64_t nclus = (bytes / cbytes) + ((bytes % cbytes) != 0U ? 1U : 0U);
313 if (first == 0U) {
314 return;
315 }
316 internal_exchk_mark_run(ctx, first, nclus);
317}
318
319/* =============================================================================
320 * Entry-set verification (SetChecksum + NameHash)
321 * =============================================================================
322 */
323
346static void internal_exchk_extract_name(const uint8_t* set, uint32_t nlen, uint16_t* units)
347{
348 for (uint32_t i = 0U; i < nlen; i++) {
349 const uint32_t ent = 2U + (i / (uint32_t)k_exfat_name_per_entry);
350 const uint32_t col = i % (uint32_t)k_exfat_name_per_entry;
351 const uint32_t off =
352 (ent * (uint32_t)k_exfat_entry_bytes) + (uint32_t)k_exfat_name_off + (col * 2U);
353 units[i] = priv_rd16(&set[off]);
354 }
355}
356
385 const uint8_t* set,
386 uint32_t count,
387 uint64_t lba,
388 uint32_t off)
389{
390 const uint16_t stored_cs = priv_rd16(&set[k_exfat_off_file_csum]);
391 const uint16_t calc_cs = priv_exfat_set_checksum(set, count * (uint32_t)k_exfat_entry_bytes);
392 if (stored_cs != calc_cs) {
393 ctx->rep->entries_bad++;
395 }
396 const uint8_t* strm = &set[k_exfat_entry_bytes];
397 uint32_t nlen = (uint32_t)strm[k_exfat_strm_off_nlen];
398 if (nlen > (uint32_t)k_exfat_name_cap) {
399 nlen = (uint32_t)k_exfat_name_cap;
400 }
401 uint16_t units[k_exfat_name_cap] = {};
402 internal_exchk_extract_name(set, nlen, units);
403 const uint16_t stored_hash = priv_rd16(&strm[k_exfat_off_strm_hash]);
404 const uint16_t calc_hash = priv_exfat_name_hash(units, nlen);
405 if (stored_hash != calc_hash) {
406 ctx->rep->entries_bad++;
408 }
409}
410
411/* =============================================================================
412 * Directory-tree walk
413 * =============================================================================
414 */
415
433static void
435{
436 if (stack->top >= (uint32_t)k_ra8_fs_check_max_dirs) {
437 if (stack->truncated == 0U) {
438 stack->truncated = 1U;
440 }
441 return;
442 }
443 stack->items[stack->top] = *dir;
444 stack->top++;
445}
446
447/* ra8_fs_check_test_exfat_push_overflow(): see header for the test-only contract. */
450 uint32_t cluster,
451 bool already_truncated)
452{
453 exfat_dir_stack_t stack = {
454 .items = nullptr,
455 .top = (uint32_t)k_ra8_fs_check_max_dirs,
456 .truncated = already_truncated ? 1U : 0U,
457 };
458 const exfat_dir_t dir = {.cluster = cluster};
459 internal_exchk_push(ctx, &stack, &dir);
460}
461
492 const uint8_t* set,
493 const uint8_t* file,
494 exfat_dir_stack_t* stack,
495 uint64_t lba,
496 uint32_t off)
497{
498 const uint8_t* strm = &set[k_exfat_entry_bytes];
499 const uint32_t first = priv_rd32(&strm[k_exfat_strm_off_clus]);
500 const uint64_t dlen = priv_rd64(&strm[k_exfat_strm_off_dlen]);
501 const uint8_t is_dir =
502 ((file[k_exfat_off_file_attr] & (uint8_t)k_exfat_attr_directory) != 0U) ? 1U : 0U;
503 if (first == 0U) {
504 if (is_dir == 0U) {
505 ctx->rep->files_visited++;
506 }
507 return k_ra8_ok;
508 }
509 if (!priv_check_in_range(ctx, first)) {
510 ctx->rep->entries_bad++;
512 return k_ra8_ok;
513 }
514 if (is_dir != 0U) {
515 exfat_dir_t sub = {};
516 priv_exfat_dir_from_set(ctx->m, strm, &sub);
517 internal_exchk_push(ctx, stack, &sub);
518 return k_ra8_ok;
519 }
520 ctx->rep->files_visited++;
521 const uint32_t cbytes = priv_cluster_bytes(ctx->m);
522 const uint64_t nclus = (dlen == 0U) ? 1U : ((dlen / cbytes) + ((dlen % cbytes) != 0U ? 1U : 0U));
523 if ((strm[k_exfat_strm_off_flags] & (uint8_t)k_exfat_secflag_no_fat) != 0U) {
524 internal_exchk_mark_run(ctx, first, nclus);
525 return k_ra8_ok;
526 }
527 return internal_exchk_mark_fatchain(ctx, first);
528}
529
561 exfat_cursor_t* cur,
562 const uint8_t* file,
563 exfat_dir_stack_t* stack)
564{
565 const uint32_t count = 1U + (uint32_t)file[k_exfat_off_file_secnt];
566 const uint32_t bic = (cur->entry_in_cluster - 1U) * (uint32_t)k_exfat_entry_bytes;
567 const uint64_t lba = priv_cluster_to_lba(ctx->m, cur->cluster) + (bic / priv_bps(ctx->m));
568 const uint32_t off = bic % priv_bps(ctx->m);
569 if ((count < 2U) || (count > (uint32_t)k_exfat_set_max_entries)) {
570 ctx->rep->entries_bad++;
572 return k_ra8_ok;
573 }
574 uint8_t set[(uint32_t)k_exfat_set_max_entries * (uint32_t)k_exfat_entry_bytes] = {};
575 priv_byte_copy(set, file, (uint32_t)k_exfat_entry_bytes);
576 for (uint32_t k = 1U; k < count; k++) {
577 const ra8_err_t e =
578 priv_exfat_next_entry(ctx->m, cur, &set[(size_t)k * (size_t)k_exfat_entry_bytes]);
579 if (e == k_ra8_err_not_found) {
580 ctx->rep->entries_bad++;
582 return k_ra8_ok;
583 }
584 if (e != k_ra8_ok) {
585 return e;
586 }
587 }
588 internal_exchk_verify_set(ctx, set, count, lba, off);
589 return internal_exchk_set_clusters(ctx, set, file, stack, lba, off);
590}
591
621 exfat_cursor_t* cur,
622 const uint8_t* e,
623 exfat_dir_stack_t* stack,
624 uint32_t dir_cluster,
625 bool* out_stop)
626{
627 const uint32_t count = 1U + (uint32_t)e[k_exfat_off_file_secnt];
628 const uint32_t remaining = (uint32_t)k_exfat_scan_limit - cur->scanned;
629 if (count <= (uint32_t)k_exfat_set_max_entries) {
630 if (count > 1U) {
631 if ((count - 1U) > remaining) {
632 priv_check_fault(ctx, k_ra8_fs_check_fault_scan_truncated, dir_cluster, 0U, 0U);
633 *out_stop = true;
634 return k_ra8_ok;
635 }
636 }
637 }
638 const ra8_err_t se = internal_exchk_set(ctx, cur, e, stack);
639 *out_stop = (se != k_ra8_ok);
640 return se;
641}
642
664 exfat_cursor_t* cur,
665 const exfat_dir_t* dir)
666{
667 uint8_t terminal[k_exfat_entry_bytes] = {};
668 const ra8_err_t probe = priv_exfat_next_entry(ctx->m, cur, terminal);
669 if (probe == k_ra8_err_not_found) {
670 return k_ra8_ok;
671 }
672 if (probe != k_ra8_ok) {
673 return probe;
674 }
675 if (terminal[0] == (uint8_t)k_exfat_entry_eod) {
676 return k_ra8_ok;
677 }
679 return k_ra8_ok;
680}
681
704static ra8_err_t
706{
707 exfat_cursor_t cur = {};
708 priv_exfat_cursor_init(dir, &cur);
709 while (cur.scanned < (uint32_t)k_exfat_scan_limit) {
710 uint8_t e[k_exfat_entry_bytes] = {};
711 const ra8_err_t r = priv_exfat_next_entry(ctx->m, &cur, e);
712 if (r == k_ra8_err_not_found) {
713 return k_ra8_ok; /* run ended without an end-of-directory marker */
714 }
715 if (r != k_ra8_ok) {
716 return r;
717 }
718 if (e[0] == (uint8_t)k_exfat_entry_eod) {
719 return k_ra8_ok;
720 }
721 if ((e[0] & (uint8_t)k_exfat_inuse_bit) == 0U) {
722 continue; /* a deleted remnant */
723 }
724 if ((e[0] == (uint8_t)k_exfat_entry_bitmap) || (e[0] == (uint8_t)k_exfat_entry_upcase)) {
726 continue;
727 }
728 if (e[0] == (uint8_t)k_exfat_entry_file) {
729 bool stop = false;
730 const ra8_err_t fe =
731 internal_exchk_scan_dir_file_entry(ctx, &cur, e, stack, dir->cluster, &stop);
732 if (stop) {
733 return fe;
734 }
735 }
736 }
737 return internal_exchk_scan_dir_terminal(ctx, &cur, dir);
738}
739
758{
759 /* The explicit DFS array (~2 KiB) stays in module-static storage so this
760 * frame remains within budget. Metadata is automatic, letting the overflow
761 * test exercise the real push guard without allocating the whole array. The
762 * walk is iterative and single-threaded under the fs lock. */
763 static exfat_dir_t s_work_items[k_ra8_fs_check_max_dirs];
764 exfat_dir_stack_t worklist = {.items = s_work_items};
765 priv_exfat_dir_root(ctx->m, &worklist.items[0]);
766 worklist.top = 1U;
767 while (worklist.top > 0U) {
768 worklist.top--;
769 const exfat_dir_t dir = worklist.items[worklist.top];
770 ctx->rep->dirs_visited++;
772 if (err != k_ra8_ok) {
773 return err;
774 }
775 err = internal_exchk_scan_dir(ctx, &dir, &worklist);
776 if (err != k_ra8_ok) {
777 return err;
778 }
779 }
780 return k_ra8_ok;
781}
782
783/* =============================================================================
784 * Allocation-bitmap population count and diff
785 * =============================================================================
786 */
787
811static void internal_exchk_diff_byte(ra8_fs_check_ctx_t* ctx, uint8_t b, uint32_t base)
812{
813 for (uint32_t j = 0U; j < (uint32_t)k_exchk_bits_per_byte; j++) {
814 const uint32_t idx = base + j;
815 if (idx >= ctx->rep->clusters_total) {
816 return;
817 }
818 const uint8_t alloc = (uint8_t)(((uint32_t)b >> j) & 1U);
819 const uint8_t refd =
820 (uint8_t)(((uint32_t)ctx->bitmap[idx >> k_exchk_byte_shift] >> (idx & k_exchk_bit_mask)) &
821 1U);
822 if ((alloc != 0U) && (refd == 0U)) {
823 ctx->rep->clusters_lost++;
826 idx + (uint32_t)k_cluster_first_data,
827 0U,
828 0U);
829 }
830 if ((alloc == 0U) && (refd != 0U)) {
831 ctx->rep->bitmap_mismatches++;
834 idx + (uint32_t)k_cluster_first_data,
835 0U,
836 0U);
837 }
838 }
839}
840
866{
867 const uint32_t total = ctx->rep->clusters_total;
868 const uint32_t full_bytes = total >> (uint32_t)k_exchk_byte_shift;
869 const uint32_t rem_bits = total & (uint32_t)k_exchk_bit_mask;
870 const uint32_t nbytes = full_bytes + ((rem_bits != 0U) ? 1U : 0U);
871 uint32_t used = 0U;
872 uint64_t loaded = UINT64_MAX;
873 uint8_t* const sec = priv_sec_io();
874 for (uint32_t bi = 0U; bi < nbytes; bi++) {
875 const uint64_t lba = bmp_lba + (bi / priv_bps(ctx->m));
876 if (lba != loaded) {
877 const ra8_err_t err = priv_read_sector(ctx->m, lba, sec);
878 if (err != k_ra8_ok) {
879 return err;
880 }
881 loaded = lba;
882 }
883 uint8_t b = sec[bi % priv_bps(ctx->m)];
884 if (bi == full_bytes) {
885 b = (uint8_t)(b & (uint8_t)((1U << rem_bits) - 1U));
886 }
887 used += internal_exchk_popcount8(b);
888 if (ctx->bitmap != nullptr) {
889 internal_exchk_diff_byte(ctx, b, bi << (uint32_t)k_exchk_byte_shift);
890 }
891 }
892 ctx->rep->clusters_used = used;
893 ctx->rep->clusters_free = total - used;
894 return k_ra8_ok;
895}
896
897/* `priv_check_exfat()`: see header for the documented contract. */
899{
900 uint32_t bclus = 0U;
901 uint32_t blen = 0U;
902 const ra8_err_t fe = priv_exfat_find_bitmap(ctx->m, &bclus, &blen);
903 if (fe == k_ra8_err_not_found) {
904 ctx->rep->entries_bad++;
906 return k_ra8_ok;
907 }
908 if (fe != k_ra8_ok) {
909 return fe;
910 }
911 const uint64_t bmp_lba = priv_cluster_to_lba(ctx->m, bclus);
912 if (ctx->bitmap != nullptr) {
914 const ra8_err_t te = internal_exchk_tree(ctx);
915 if (te != k_ra8_ok) {
916 return te;
917 }
918 }
919 return internal_exchk_bitmap_pass(ctx, bmp_lba);
920}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_TEST_HELPER
Mark a symbol as externally-linked but only callable from tests.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ 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).
Read-only on-device volume consistency check (fsck) for ra8_fs.
@ k_ra8_fs_check_fault_bad_set_checksum
exFAT entry-set SetChecksum mismatch.
@ k_ra8_fs_check_fault_bad_name_hash
exFAT Stream NameHash mismatch.
@ k_ra8_fs_check_fault_bad_dir_entry
A directory entry's first cluster is invalid.
@ k_ra8_fs_check_fault_bad_chain
A chain runs into free space or off-volume.
@ k_ra8_fs_check_fault_lost_cluster
A cluster is allocated but referenced by none.
@ k_ra8_fs_check_fault_bitmap_ref_unset
exFAT: referenced, but its bitmap bit is 0.
@ k_ra8_fs_check_fault_scan_truncated
The directory worklist hit its static bound.
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
ra8_err_t priv_fat_get(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t *out_value)
Fetch the FAT entry for cluster, returning the next-cluster value.
Definition ra8_fs_fat.c:187
uint16_t priv_rd16(const uint8_t *p)
Decode a little-endian uint16_t from a byte buffer.
Definition ra8_fs_fat.c:42
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
uint8_t priv_is_eoc(const ra8_fs_mount_t *m, uint32_t value)
Test whether value is an end-of-chain marker for this FAT type.
Definition ra8_fs_fat.c:511
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_byte_copy(uint8_t *dst, const uint8_t *src, uint32_t n)
Length-checked byte copy used in place of memcpy().
Definition ra8_fs_fat.c:110
uint64_t priv_rd64(const uint8_t *p)
Decode a little-endian uint64_t from a byte buffer.
Definition ra8_fs_fat.c:55
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
uint8_t * priv_sec_io(void)
The IO-role sector buffer (leaf data / bitmap sector transfers).
void priv_check_zero_bitmap(ra8_fs_check_ctx_t *ctx)
Zero the scratch visited-bitmap over its valid bit range.
bool priv_check_in_range(const ra8_fs_check_ctx_t *ctx, uint32_t cluster)
True when cluster is a real data cluster of the volume under check.
bool priv_check_visit(ra8_fs_check_ctx_t *ctx, uint32_t cluster, ra8_fs_check_fault_kind_t oor_kind)
Visit a cluster during a walk: mark it, and report a range or link fault.
void priv_check_fault(ra8_fs_check_ctx_t *ctx, ra8_fs_check_fault_kind_t kind, uint32_t cluster, uint64_t lba, uint32_t entry_off)
Record one consistency finding into the report.
Cross-TU state and helpers for the volume consistency check (fsck).
@ k_ra8_fs_check_max_dirs
Directory worklist depth cap.
static ra8_err_t internal_exchk_scan_dir(ra8_fs_check_ctx_t *ctx, const exfat_dir_t *dir, exfat_dir_stack_t *stack)
Walk one exFAT directory, verifying and marking every live entry.
void ra8_fs_check_test_exfat_push_overflow(ra8_fs_check_ctx_t *ctx, uint32_t cluster, bool already_truncated)
Exercise the exFAT directory-worklist overflow guard from a host test.
static void internal_exchk_extract_name(const uint8_t *set, uint32_t nlen, uint16_t *units)
Extract a File entry set's name into UTF-16 units for hashing.
static ra8_err_t internal_exchk_bitmap_pass(ra8_fs_check_ctx_t *ctx, uint64_t bmp_lba)
Read the allocation bitmap: count used / free, and diff if referencing.
static void internal_exchk_verify_set(ra8_fs_check_ctx_t *ctx, const uint8_t *set, uint32_t count, uint64_t lba, uint32_t off)
Verify a gathered entry set's SetChecksum and NameHash.
ra8_err_t ra8_fs_check_test_exfat_mark_dir_alloc(ra8_fs_check_ctx_t *ctx, uint32_t first)
Exercise the exFAT FAT-chained directory allocation marker from a host test.
void ra8_fs_check_test_exfat_mark_run(ra8_fs_check_ctx_t *ctx, uint32_t first, uint64_t nclus)
Exercise the exFAT contiguous-run marker from a host unit test.
static ra8_err_t internal_exchk_mark_dir_alloc(ra8_fs_check_ctx_t *ctx, const exfat_dir_t *dir)
Mark a directory's own cluster allocation visited.
static ra8_err_t internal_exchk_mark_fatchain(ra8_fs_check_ctx_t *ctx, uint32_t first)
Mark a FAT-chained file's whole chain visited, detecting cross-links.
static ra8_err_t internal_exchk_scan_dir_file_entry(ra8_fs_check_ctx_t *ctx, exfat_cursor_t *cur, const uint8_t *e, exfat_dir_stack_t *stack, uint32_t dir_cluster, bool *out_stop)
Dispatch one live File entry: truncation-checked, then set-verified.
ra8_fs_exfat_check_const_t
Bit-arithmetic constants for the exFAT allocation-bitmap diff.
@ k_exchk_bits_per_byte
Bits in one allocation-bitmap byte.
@ k_exchk_byte_shift
log2(8): cluster index -> bitmap byte.
@ k_exchk_bit_mask
Cluster index -> bit within its byte.
static uint32_t internal_exchk_popcount8(uint8_t b)
Count the set bits in one byte (portable, no compiler builtin).
static void internal_exchk_push(ra8_fs_check_ctx_t *ctx, exfat_dir_stack_t *stack, const exfat_dir_t *dir)
Push an exFAT subdirectory onto the walk stack, capping the depth.
static void internal_exchk_mark_run(ra8_fs_check_ctx_t *ctx, uint32_t first, uint64_t nclus)
Mark a contiguous cluster run visited, detecting cross-links.
static void internal_exchk_system_run(ra8_fs_check_ctx_t *ctx, const uint8_t *e)
Mark the clusters a bitmap (0x81) or up-case (0x82) system entry owns.
static ra8_err_t internal_exchk_set(ra8_fs_check_ctx_t *ctx, exfat_cursor_t *cur, const uint8_t *file, exfat_dir_stack_t *stack)
Gather and process one File entry set: verify it, then mark or queue it.
static ra8_err_t internal_exchk_tree(ra8_fs_check_ctx_t *ctx)
Walk the whole exFAT directory tree from the root with a worklist.
static void internal_exchk_diff_byte(ra8_fs_check_ctx_t *ctx, uint8_t b, uint32_t base)
Diff one allocation-bitmap byte against the visited bitmap.
ra8_err_t priv_check_exfat(ra8_fs_check_ctx_t *ctx)
Run the exFAT consistency check into the context's report.
static ra8_err_t internal_exchk_set_clusters(ra8_fs_check_ctx_t *ctx, const uint8_t *set, const uint8_t *file, exfat_dir_stack_t *stack, uint64_t lba, uint32_t off)
Validate a verified entry set's first cluster and mark or queue it.
ra8_err_t ra8_fs_check_test_exfat_mark_fatchain(ra8_fs_check_ctx_t *ctx, uint32_t first)
Exercise the exFAT fragmented-chain walker from a host unit test.
static ra8_err_t internal_exchk_scan_dir_terminal(ra8_fs_check_ctx_t *ctx, exfat_cursor_t *cur, const exfat_dir_t *dir)
Probe exactly one entry past the scan ceiling to classify the walk.
void priv_exfat_dir_root(const ra8_fs_mount_t *m, exfat_dir_t *out)
Fill out with the volume root's directory location.
ra8_err_t priv_exfat_step_cluster(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t contig_end, uint32_t *out_next)
Compute the cluster that follows cluster in a directory.
void priv_exfat_cursor_init(const exfat_dir_t *dir, exfat_cursor_t *out)
Aim a fresh cursor at the start of dir.
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_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.
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_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.
@ k_exfat_entry_upcase
Up-case Table directory entry.
@ 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_entry_bitmap
Allocation-bitmap directory entry.
@ k_exfat_off_file_csum
File entry: SetChecksum (2 bytes).
@ k_exfat_secflag_no_fat
GeneralSecondaryFlags: NoFatChain.
@ k_exfat_name_per_entry
UTF-16 units per file-name entry.
@ k_exfat_entry_bytes
Directory entry size.
@ k_exfat_off_strm_hash
Stream entry: NameHash (2 bytes).
@ k_exfat_entry_eod
End-of-directory marker.
@ k_exfat_off_file_attr
File entry: FileAttributes (2 bytes).
@ k_exfat_off_file_secnt
File entry: SecondaryCount.
@ k_exfat_scan_limit
Max dir entries scanned (P10 bound).
@ k_exfat_attr_directory
FileAttributes: directory.
@ 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).
uint32_t cluster
Current directory cluster.
uint32_t entry_in_cluster
Next entry index within the cluster.
Bounded worklist of exFAT directories still to walk (NASA P10 Rule 1).
uint32_t top
Count of pending entries.
uint8_t truncated
1 once an overflow was dropped.
exfat_dir_t * items
Caller-owned pending-directory array.
Identifies the exFAT directory a lookup, scan or link operates in.
uint32_t cluster
First cluster of the directory.
uint32_t contig_end
One past the run's last cluster; 0 => FAT-chained.
The state one ra8_fs_check run threads through its passes.
ra8_fs_check_report_t * rep
The report being filled.
ra8_fs_mount_t * m
The mounted volume under check.
uint8_t * bitmap
Scratch visited-cluster bitmap, or NULL.
uint32_t clusters_used
Clusters that read as allocated.
uint32_t entries_bad
Bad dir entry / SetChecksum / NameHash findings.
uint32_t clusters_total
Data-region cluster count.
uint32_t bitmap_mismatches
exFAT clusters referenced with the bitmap bit clear.
uint32_t clusters_lost
Allocated but referenced by nothing; k_ra8_fs_check_unknown when referenced_scan is false.
uint32_t clusters_free
Clusters that read as free.
uint32_t files_visited
Files walked.
uint32_t dirs_visited
Directories walked.