ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_check.c File Reference

Read-only consistency check (fsck): shared scaffolding + FAT12/16/32. More...

#include <stddef.h>
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_fs.h"
#include "ra8_fs_check.h"
#include "ra8_fs_fat_check_internal.h"
#include "ra8_fs_fat_internal.h"
Include dependency graph for ra8_fs_fat_check.c:

Go to the source code of this file.

Data Structures

struct  fat_dir_stack_t
 Bounded worklist of directories still to walk (NASA P10 Rule 1). More...

Enumerations

enum  ra8_fs_check_marker_t : uint32_t {
  k_check_bad_fat12 = 0x0FF7U ,
  k_check_bad_fat16 = 0xFFF7U ,
  k_check_bad_fat32 = 0x0FFFFFF7U
}
 The FAT defective-cluster marker for each FAT width. More...
enum  ra8_fs_check_bit_t : uint32_t {
  k_check_bit_mask = 7U ,
  k_check_byte_shift = 3U
}
 Bit-arithmetic constants for the caller-supplied visited bitmap. More...

Functions

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.
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_mark (ra8_fs_check_ctx_t *ctx, uint32_t cluster)
 Mark cluster visited in the scratch bitmap; report a prior visit.
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_zero_bitmap (ra8_fs_check_ctx_t *ctx)
 Zero the scratch visited-bitmap over its valid bit range.
static uint32_t internal_fat_bad_marker (const ra8_fs_mount_t *m)
 The FAT defective-cluster marker for the mounted variant.
static void internal_fat_classify_one (ra8_fs_check_ctx_t *ctx, uint32_t cluster, uint32_t value)
 Classify one FAT entry value into the report's cluster tallies.
static ra8_err_t internal_fat_classify (ra8_fs_check_ctx_t *ctx)
 Pass 1: read and classify every data cluster's FAT entry.
static ra8_err_t internal_fat_mark_chain (ra8_fs_check_ctx_t *ctx, uint32_t first)
 Mark a file's whole cluster chain, detecting cross-links and breaks.
static void internal_fat_push (ra8_fs_check_ctx_t *ctx, fat_dir_stack_t *stack, uint32_t clus)
 Push a subdirectory onto the walk stack, capping the depth.
void ra8_fs_check_test_fat_push_overflow (ra8_fs_check_ctx_t *ctx, uint32_t cluster, bool already_truncated)
 Exercise the FAT directory-worklist overflow guard from a host test.
static ra8_err_t internal_fat_entry (ra8_fs_check_ctx_t *ctx, fat_dir_stack_t *stack, const uint8_t *ent, uint64_t lba, uint32_t entry_off, uint8_t *out_eod)
 Process one 32-byte FAT directory entry.
static ra8_err_t internal_fat_visit_sector (ra8_fs_check_ctx_t *ctx, fat_dir_stack_t *stack, const uint8_t *buf, uint64_t lba, uint8_t *out_eod)
 Process the 16 directory entries of one loaded sector.
static ra8_err_t internal_fat_scan_fixed_root (ra8_fs_check_ctx_t *ctx, fat_dir_stack_t *stack)
 Walk the FAT12/16 fixed root directory region.
static ra8_err_t internal_fat_scan_cluster_dir (ra8_fs_check_ctx_t *ctx, fat_dir_stack_t *stack, uint32_t first)
 Walk a cluster-chained directory (a FAT32 root or any subdirectory).
ra8_err_t ra8_fs_check_test_fat_scan_cluster_dir (ra8_fs_check_ctx_t *ctx, uint32_t first)
 Exercise the FAT cluster-directory walker from a host unit test.
static ra8_err_t internal_fat_tree (ra8_fs_check_ctx_t *ctx)
 Walk the whole directory tree from the root with an explicit worklist.
static ra8_err_t internal_fat_diff (ra8_fs_check_ctx_t *ctx)
 Pass 3: flag allocated clusters no directory chain reached.
static ra8_err_t internal_fat_fsinfo (ra8_fs_check_ctx_t *ctx)
 Compare a FAT32 volume's FSInfo free count against the classify pass.
ra8_err_t priv_check_fat (ra8_fs_check_ctx_t *ctx)
 Run the FAT12/16/32 consistency check into the context's report.
static bool internal_check_bitmap_ok (uint32_t total, const uint8_t *bitmap, uint32_t bitmap_bytes)
 Decide whether the caller's bitmap enables the reference passes.
static ra8_err_t internal_check_locked (ra8_fs_mount_t *handle, uint8_t *bitmap, uint32_t bitmap_bytes, ra8_fs_check_report_t *report)
 Consistency check – the guarded body of ra8_fs_check().
ra8_err_t ra8_fs_check (ra8_fs_mount_t *handle, uint8_t *bitmap, uint32_t bitmap_bytes, ra8_fs_check_report_t *report)
 Scan a mounted volume for consistency, read-only, and report findings.

Detailed Description

Read-only consistency check (fsck): shared scaffolding + FAT12/16/32.

The FAT half of ra8_fs_check, plus the scaffolding its exFAT half (ra8_fs_fat_exfat_check.c) shares: the finding recorder, the visited-cluster bitmap primitives, and the public entry point that brackets the library lock and dispatches on the volume type.

The FAT check is three passes over a mounted volume, none of which writes to it:

  1. Classify. Read every data cluster's FAT entry and tally free / used / defective, flagging any value that is neither free, a valid in-range next-cluster, an end-of-chain marker, nor the defective marker.
  2. Walk. Follow every directory chain from the root, marking each visited cluster in the caller's bitmap; a second visit is a cross-link, a run into free space or off the volume is a bad chain, and a directory entry naming a cluster the volume does not have is a bad entry.
  3. Diff. A cluster the FAT calls allocated that no walk reached is lost; and on FAT32 the FSInfo free count is compared against the classify pass.

References (every shorthand citation in this file):

  • "MS FAT spec" = Microsoft Corp., "FAT: General Overview of On-Disk Format", v1.03, December 6 2000. FSInfo is sec 5.

NASA Power-of-Ten compliance:

  • Rule 1: the directory tree is walked with an explicit worklist, never recursion.
  • Rule 2: every cluster walk is bounded by count_of_clusters and cut short by the visited bitmap; the worklist depth is capped by k_ra8_fs_check_max_dirs.
  • Rule 3: zero malloc; the visited bitmap is caller-supplied, the worklist a bounded stack.
  • Rule 7: every backend call is checked.
Since
0.1.0

Definition in file ra8_fs_fat_check.c.

Enumeration Type Documentation

◆ ra8_fs_check_bit_t

enum ra8_fs_check_bit_t : uint32_t

Bit-arithmetic constants for the caller-supplied visited bitmap.

The visited bitmap packs one cluster per bit, LSB first, so a cluster index shifts right by k_check_byte_shift to its byte and masks with k_check_bit_mask to its bit – the same layout the exFAT allocation bitmap uses, so the two diff directly.

Since
0.1.0
Enumerator
k_check_bit_mask 

Cluster index -> bit within its byte.

k_check_byte_shift 

log2(8): cluster index -> bitmap byte.

Definition at line 79 of file ra8_fs_fat_check.c.

◆ ra8_fs_check_marker_t

enum ra8_fs_check_marker_t : uint32_t

The FAT defective-cluster marker for each FAT width.

One below the end-of-chain threshold in every FAT variant (MS FAT spec sec 4): a value the formatter or a surface scan writes to fence off a bad cluster. It is counted – informationally – rather than treated as corruption.

Since
0.1.0
Enumerator
k_check_bad_fat12 

FAT12 defective-cluster marker.

k_check_bad_fat16 

FAT16 defective-cluster marker.

k_check_bad_fat32 

FAT32 defective-cluster marker.

Definition at line 62 of file ra8_fs_fat_check.c.

Function Documentation

◆ internal_check_bitmap_ok()

bool internal_check_bitmap_ok ( uint32_t total,
const uint8_t * bitmap,
uint32_t bitmap_bytes )
static

Decide whether the caller's bitmap enables the reference passes.

The bitmap must hold one bit per data cluster – (clusters_total + 7) / 8 bytes – for the walk / lost analysis to run. A NULL or short bitmap leaves the classification pass to run alone, with clusters_lost reported as k_ra8_fs_check_unknown.

Parameters
[in]totalThe volume's data-cluster count.
[in]bitmapThe caller's bitmap, or NULL.
[in]bitmap_bytesThe caller's bitmap size in bytes.
Returns
Whether the reference passes may run.
Return values
truebitmap is non-NULL and large enough.
falseOtherwise.
Precondition
None.
total is the mounted volume's cluster count.
Postcondition
No state modified.
Result depends only on the inputs.
Note
Pure; trivially thread-safe.
Since
0.1.0

Definition at line 815 of file ra8_fs_fat_check.c.

References k_check_bit_mask, and k_check_byte_shift.

Referenced by internal_check_locked().

◆ internal_check_locked()

ra8_err_t internal_check_locked ( ra8_fs_mount_t * handle,
uint8_t * bitmap,
uint32_t bitmap_bytes,
ra8_fs_check_report_t * report )
static

Consistency check – the guarded body of ra8_fs_check().

Validates the arguments, seeds the report, decides whether the caller's bitmap enables the reference passes, and dispatches on the volume type into a local candidate report. The candidate is published only after the entire scan succeeds, so backend failures cannot expose a plausible-looking partial result. The public ra8_fs_check brackets this with the library lock; the full contract is documented there.

Parameters
[in]handleMount handle.
[in]bitmapScratch visited-cluster bitmap, or NULL.
[in]bitmap_bytesSize of bitmap in bytes.
[out]reportReceives the findings.
Returns
Error code.
Return values
k_ra8_okScan completed.
k_ra8_err_null_ptrhandle or report is NULL.
k_ra8_err_invalid_stateMount is not in use.
k_ra8_err_*Backend read failure.
Precondition
The library lock is held (or none is installed).
handle and report are non-NULL.
Postcondition
On k_ra8_ok the report describes the volume.
On error report retains its entry value.
No volume state is modified.
Note
Never call this from outside ra8_fs; it is the unlocked half.
Since
0.1.0

Definition at line 856 of file ra8_fs_fat_check.c.

References internal_check_bitmap_ok(), internal_check_locked(), k_ra8_err_null_ptr, k_ra8_fs_check_unknown, k_ra8_fs_type_exfat, k_ra8_ok, and ra8_fs_check_report_t::type.

Referenced by internal_check_locked(), and ra8_fs_check().

◆ internal_fat_bad_marker()

uint32_t internal_fat_bad_marker ( const ra8_fs_mount_t * m)
static

The FAT defective-cluster marker for the mounted variant.

One below the end-of-chain threshold for the mounted FAT variant (MS FAT spec sec 4).

Parameters
[in]mMounted FAT volume.
Returns
The marker value for m->type.
Return values
k_check_bad_fat12FAT12.
k_check_bad_fat16FAT16.
k_check_bad_fat32FAT32.
Precondition
m is non-NULL and m->type is a FAT variant.
The mount's geometry is populated.
Postcondition
No state modified.
Result depends only on m->type.
Note
Pure; trivially thread-safe.
Since
0.1.0

Definition at line 197 of file ra8_fs_fat_check.c.

References k_check_bad_fat12, k_check_bad_fat16, k_check_bad_fat32, k_ra8_fs_type_fat12, k_ra8_fs_type_fat16, and ra8_fs_mount_t::type.

Referenced by internal_fat_classify_one(), and internal_fat_diff().

◆ internal_fat_classify()

ra8_err_t internal_fat_classify ( ra8_fs_check_ctx_t * ctx)
static

Pass 1: read and classify every data cluster's FAT entry.

A single pass over the FAT, tallying free, used and defective clusters.

Parameters
[in,out]ctxThe scan context.
Returns
Error code.
Return values
k_ra8_okEvery entry classified.
k_ra8_err_*Backend read failure.
Precondition
ctx is non-NULL; ctx->m->type is a FAT variant.
The report's clusters_total is populated.
Postcondition
The free / used / bad tallies describe the whole FAT.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): clusters_total iterations.
Since
0.1.0

Definition at line 270 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::clusters_total, internal_fat_classify_one(), k_cluster_first_data, k_ra8_ok, ra8_fs_check_ctx_t::m, priv_fat_get(), and ra8_fs_check_ctx_t::rep.

Referenced by priv_check_fat().

◆ internal_fat_classify_one()

void internal_fat_classify_one ( ra8_fs_check_ctx_t * ctx,
uint32_t cluster,
uint32_t value )
static

Classify one FAT entry value into the report's cluster tallies.

Free, defective and end-of-chain are each their own tally; a value in [2, 2 + clusters_total) is a valid next-cluster pointer and counts used; anything else – the reserved 1, or a pointer past the last cluster – is a bad FAT value, still counted used but recorded as a fault.

Parameters
[in,out]ctxThe scan context.
[in]clusterThe cluster whose entry value belongs to.
[in]valueThe FAT entry value read for cluster.
Returns
Nothing.
Precondition
ctx is non-NULL; the report's clusters_total is populated.
value came from priv_fat_get for cluster.
Postcondition
Exactly one tally (clusters_free / clusters_bad / clusters_used) is incremented.
A reserved / out-of-range pointer raises a bad-FAT-value fault.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 234 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::clusters_bad, ra8_fs_check_report_t::clusters_free, ra8_fs_check_report_t::clusters_used, internal_fat_bad_marker(), k_cluster_free, k_ra8_fs_check_fault_bad_fat_value, ra8_fs_check_ctx_t::m, priv_check_fault(), priv_check_in_range(), priv_is_eoc(), and ra8_fs_check_ctx_t::rep.

Referenced by internal_fat_classify().

◆ internal_fat_diff()

ra8_err_t internal_fat_diff ( ra8_fs_check_ctx_t * ctx)
static

Pass 3: flag allocated clusters no directory chain reached.

A single pass over the FAT flagging allocated clusters that no chain reached.

Parameters
[in,out]ctxThe scan context.
Returns
Error code.
Return values
k_ra8_okThe diff completed.
k_ra8_err_*Backend read failure.
Precondition
ctx and its bitmap are non-NULL; the tree walk has run.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
clusters_lost counts allocated-but-unvisited clusters.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): clusters_total iterations.
Since
0.1.0

Definition at line 685 of file ra8_fs_fat_check.c.

References ra8_fs_check_ctx_t::bitmap, ra8_fs_check_report_t::clusters_lost, ra8_fs_check_report_t::clusters_total, internal_fat_bad_marker(), k_check_bit_mask, k_check_byte_shift, k_cluster_first_data, k_cluster_free, k_ra8_fs_check_fault_lost_cluster, k_ra8_ok, ra8_fs_check_ctx_t::m, priv_check_fault(), priv_fat_get(), and ra8_fs_check_ctx_t::rep.

Referenced by priv_check_fat().

◆ internal_fat_entry()

ra8_err_t internal_fat_entry ( ra8_fs_check_ctx_t * ctx,
fat_dir_stack_t * stack,
const uint8_t * ent,
uint64_t lba,
uint32_t entry_off,
uint8_t * out_eod )
static

Process one 32-byte FAT directory entry.

Skips the entries that name nothing walkable – end-of-directory, deleted, long-name, "." / "..", and the volume-label – then validates the first cluster of a real file or directory. A file's chain is marked now; a directory is pushed to be walked (and marked) when it is popped.

Parameters
[in,out]ctxThe scan context.
[in,out]stackThe directory worklist.
[in]entThe 32-byte directory entry.
[in]lbaVolume-relative sector the entry was read from.
[in]entry_offByte offset of the entry in that sector.
[out]out_eodSet to 1 when ent is the end-of-directory marker.
Returns
Error code.
Return values
k_ra8_okThe entry was processed (a fault may have been recorded).
k_ra8_err_*Backend read failure while marking a file chain.
Precondition
Every pointer is non-NULL; ent points at 32 valid bytes.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
On a valid file entry its chain is marked; on a directory it is queued.
No volume state is modified.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 420 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::entries_bad, ra8_fs_check_report_t::files_visited, internal_fat_mark_chain(), internal_fat_push(), k_dir_marker_dot, k_dir_marker_free_perm, k_dir_marker_free_used, k_dir_off_attr, k_dir_off_name, k_ra8_fs_attr_directory, k_ra8_fs_attr_lfn, k_ra8_fs_attr_volume_id, k_ra8_fs_check_fault_bad_dir_entry, k_ra8_ok, priv_check_fault(), priv_check_in_range(), priv_entry_first_cluster(), and ra8_fs_check_ctx_t::rep.

Referenced by internal_fat_visit_sector().

◆ internal_fat_fsinfo()

ra8_err_t internal_fat_fsinfo ( ra8_fs_check_ctx_t * ctx)
static

Compare a FAT32 volume's FSInfo free count against the classify pass.

Reads BPB_FSInfo from the boot sector, validates the three FSInfo signatures (MS FAT spec sec 5), and only then compares the stored free count to clusters_free. A volume with no or an untrusted FSInfo sector is not a fault – there is simply nothing to compare.

Parameters
[in,out]ctxThe scan context.
Returns
Error code.
Return values
k_ra8_okCompared, or nothing to compare.
k_ra8_err_*Backend read failure.
Precondition
ctx is non-NULL; the classify pass has run.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
A trustworthy FSInfo count that disagrees raises a fault.
No volume state is modified.
Note
Two sector reads; no loop.
Since
0.1.0

Definition at line 728 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::clusters_free, k_fmt_fsi_lead_sig, k_fmt_fsi_off_free, k_fmt_fsi_off_lead, k_fmt_fsi_off_struct, k_fmt_fsi_off_trail, k_fmt_fsi_struct_sig, k_fmt_fsi_trail_sig, k_fmt_off_f32_fsinfo, k_fs_free_unknown, k_ra8_fs_check_fault_free_count_bad, k_ra8_fs_type_fat32, k_ra8_ok, ra8_fs_check_ctx_t::m, priv_check_fault(), priv_rd16(), priv_rd32(), priv_read_sector(), priv_sec_walk(), ra8_fs_check_ctx_t::rep, ra8_fs_mount_t::reserved_sectors, and ra8_fs_mount_t::type.

Referenced by priv_check_fat().

◆ internal_fat_mark_chain()

ra8_err_t internal_fat_mark_chain ( ra8_fs_check_ctx_t * ctx,
uint32_t first )
static

Mark a file's whole cluster chain, detecting cross-links and breaks.

Follows the FAT from first, marking each cluster. A cluster already marked is a cross-link (or a loop) and stops the walk; a next-cluster that is free or out of range is a broken chain and stops it too. The hop count is bounded by clusters_total. After the last permitted hop, a non-terminal successor is checked once more without reading another FAT entry: it must revisit one of the volume's clusters or name a cluster outside the volume, so the bound cannot look clean.

Parameters
[in,out]ctxThe scan context.
[in]firstThe file's first cluster (>= 2).
Returns
Error code.
Return values
k_ra8_okThe chain was walked (a fault may have been recorded).
k_ra8_err_*Backend read failure.
Precondition
ctx and its bitmap are non-NULL; first is in range.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
Every cluster of the chain up to the first fault reads as visited.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): clusters_total hops, bitmap-terminated.
Since
0.1.0

Definition at line 317 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::clusters_total, k_ra8_fs_check_fault_bad_chain, k_ra8_ok, ra8_fs_check_ctx_t::m, priv_check_fault(), priv_check_in_range(), priv_check_visit(), priv_fat_get(), priv_is_eoc(), and ra8_fs_check_ctx_t::rep.

Referenced by internal_fat_entry().

◆ internal_fat_push()

void internal_fat_push ( ra8_fs_check_ctx_t * ctx,
fat_dir_stack_t * stack,
uint32_t clus )
static

Push a subdirectory onto the walk stack, capping the depth.

Records k_ra8_fs_check_fault_scan_truncated the first time the stack is full and drops the entry, so the tree walk stays bounded.

Parameters
[in,out]ctxThe scan context (for the truncation fault).
[in,out]stackThe directory worklist.
[in]clusThe subdirectory's first cluster.
Returns
Nothing.
Precondition
ctx and stack are non-NULL; clus is in range.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
The subdirectory is queued, or the stack was full and it was dropped.
On the first overflow a truncation fault is recorded.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 364 of file ra8_fs_fat_check.c.

References dir_loc_t::cluster, dir_loc_t::is_root, fat_dir_stack_t::items, k_ra8_fs_check_fault_scan_truncated, k_ra8_fs_check_max_dirs, priv_check_fault(), fat_dir_stack_t::top, and fat_dir_stack_t::truncated.

Referenced by internal_fat_entry(), and ra8_fs_check_test_fat_push_overflow().

◆ internal_fat_scan_cluster_dir()

ra8_err_t internal_fat_scan_cluster_dir ( ra8_fs_check_ctx_t * ctx,
fat_dir_stack_t * stack,
uint32_t first )
static

Walk a cluster-chained directory (a FAT32 root or any subdirectory).

Follows the directory's own cluster chain, marking each cluster (a revisit is a cross-link), and processes every sector of each cluster. The end-of-directory marker or a terminal FAT entry ends the walk. Reaching the hop bound validates the pending successor once more so a full-volume cycle or off-volume tail is reported, never accepted.

Parameters
[in,out]ctxThe scan context.
[in,out]stackThe directory worklist.
[in]firstThe directory's first cluster.
Returns
Error code.
Return values
k_ra8_okThe directory was walked.
k_ra8_err_*Backend read failure.
Precondition
ctx and stack are non-NULL; first is a directory's first cluster.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
Every reachable directory cluster is marked visited.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): clusters_total hops, bitmap-terminated.
Since
0.1.0

Definition at line 577 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::clusters_total, internal_fat_visit_sector(), k_ra8_fs_check_fault_bad_dir_entry, k_ra8_ok, ra8_fs_check_ctx_t::m, priv_check_visit(), priv_cluster_to_lba(), priv_fat_get(), priv_is_eoc(), priv_read_sector(), priv_sec_walk(), ra8_fs_check_ctx_t::rep, and ra8_fs_mount_t::sectors_per_cluster.

Referenced by internal_fat_tree(), and ra8_fs_check_test_fat_scan_cluster_dir().

◆ internal_fat_scan_fixed_root()

ra8_err_t internal_fat_scan_fixed_root ( ra8_fs_check_ctx_t * ctx,
fat_dir_stack_t * stack )
static

Walk the FAT12/16 fixed root directory region.

Reads the FAT12/16 fixed root-directory region sector by sector.

Parameters
[in,out]ctxThe scan context.
[in,out]stackThe directory worklist.
Returns
Error code.
Return values
k_ra8_okThe fixed root was walked.
k_ra8_err_*Backend read failure.
Precondition
ctx and stack are non-NULL; the mount is FAT12/16.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
Every visible root entry was processed.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): the fixed root's sector span.
Since
0.1.0

Definition at line 527 of file ra8_fs_fat_check.c.

References ra8_fs_mount_t::first_root_lba, internal_fat_visit_sector(), k_ra8_ok, ra8_fs_check_ctx_t::m, priv_dir_eps(), priv_read_sector(), priv_sec_walk(), and ra8_fs_mount_t::root_entries.

Referenced by internal_fat_tree().

◆ internal_fat_tree()

ra8_err_t internal_fat_tree ( ra8_fs_check_ctx_t * ctx)
static

Walk the whole directory tree from the root with an explicit worklist.

Pops directories off the worklist and walks each, the volume root first.

Parameters
[in,out]ctxThe scan context.
Returns
Error code.
Return values
k_ra8_okThe tree was walked (faults may have been recorded).
k_ra8_err_*Backend read failure.
Precondition
ctx and its bitmap are non-NULL; ctx->m->type is a FAT variant.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
Every reachable file and directory cluster is marked.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): at most k_ra8_fs_check_max_dirs pops.
Since
0.1.0

Definition at line 638 of file ra8_fs_fat_check.c.

References dir_loc_t::cluster, ra8_fs_check_report_t::dirs_visited, internal_fat_scan_cluster_dir(), internal_fat_scan_fixed_root(), dir_loc_t::is_root, fat_dir_stack_t::items, k_ra8_fs_type_fat32, k_ra8_ok, ra8_fs_check_ctx_t::m, ra8_fs_check_ctx_t::rep, ra8_fs_mount_t::root_cluster, fat_dir_stack_t::top, and ra8_fs_mount_t::type.

Referenced by priv_check_fat().

◆ internal_fat_visit_sector()

ra8_err_t internal_fat_visit_sector ( ra8_fs_check_ctx_t * ctx,
fat_dir_stack_t * stack,
const uint8_t * buf,
uint64_t lba,
uint8_t * out_eod )
static

Process the 16 directory entries of one loaded sector.

Processes the sixteen directory entries packed into one 512-byte sector.

Parameters
[in,out]ctxThe scan context.
[in,out]stackThe directory worklist.
[in]bufThe 512-byte directory sector.
[in]lbaVolume-relative sector number of buf.
[out]out_eodSet to 1 when the end-of-directory marker was hit.
Returns
Error code.
Return values
k_ra8_okThe sector was processed.
k_ra8_err_*Backend read failure while marking a chain.
Precondition
Every pointer is non-NULL; buf holds a directory sector.
No filesystem operation runs concurrently on the mount (single-threaded by contract).
Postcondition
*out_eod is 1 iff the walk should stop.
No volume state is modified.
Note
Bounded loop (NASA Rule 2): one sector's worth of entries.
Since
0.1.0

Definition at line 487 of file ra8_fs_fat_check.c.

References internal_fat_entry(), k_ra8_fs_dir_entry_bytes, k_ra8_ok, ra8_fs_check_ctx_t::m, and priv_dir_eps().

Referenced by internal_fat_scan_cluster_dir(), and internal_fat_scan_fixed_root().

◆ priv_check_fat()

ra8_err_t priv_check_fat ( ra8_fs_check_ctx_t * ctx)

Run the FAT12/16/32 consistency check into the context's report.

The FAT-side dispatch of ra8_fs_check: classifies the FAT, walks the directory tree marking referenced clusters, diffs for lost clusters, and compares the FAT32 FSInfo free count. Defined in ra8_fs_fat_check.c.

Parameters
[in,out]ctxThe scan context (ctx->m->type is a FAT variant).
Returns
Error code.
Return values
k_ra8_okThe scan completed; findings are in the report.
k_ra8_err_*Backend read failure mid-scan.
Precondition
ctx is non-NULL; ctx->m->type is FAT12/16/32.
The mount is in use and its geometry is populated.
Postcondition
On k_ra8_ok the report's counts and faults describe the volume.
No volume state is modified.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 764 of file ra8_fs_fat_check.c.

References ra8_fs_check_ctx_t::bitmap, internal_fat_classify(), internal_fat_diff(), internal_fat_fsinfo(), internal_fat_tree(), k_ra8_ok, and priv_check_zero_bitmap().

◆ priv_check_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.

Increments rep->faults_total and, on the FIRST finding, stamps rep->first_fault with kind and the locator. Later findings raise the total but leave the first fault untouched, so the report always names the earliest thing the scan tripped on – what fsck -n prints first. The per-category counters are the callers' responsibility; this owns only the total and the first-fault stamp.

Parameters
[in,out]ctxThe scan context.
[in]kindThe finding's category.
[in]clusterCluster the finding concerns (0 if n/a).
[in]lbaVolume-relative sector of the entry (0 if n/a).
[in]entry_offByte offset of the entry in that sector (0 if n/a).
Returns
Nothing.
Precondition
ctx and ctx->rep are non-NULL.
kind is not k_ra8_fs_check_fault_none.
Postcondition
rep->faults_total is one higher.
rep->first_fault names the earliest finding.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 109 of file ra8_fs_fat_check.c.

References ra8_fs_check_fault_t::cluster, ra8_fs_check_fault_t::entry_off, ra8_fs_check_report_t::faults_total, ra8_fs_check_report_t::first_fault, ra8_fs_check_fault_t::kind, ra8_fs_check_fault_t::lba, and ra8_fs_check_ctx_t::rep.

Referenced by internal_exchk_diff_byte(), internal_exchk_mark_fatchain(), internal_exchk_mark_run(), internal_exchk_push(), internal_exchk_scan_dir_file_entry(), internal_exchk_scan_dir_terminal(), internal_exchk_set(), internal_exchk_set_clusters(), internal_exchk_verify_set(), internal_fat_classify_one(), internal_fat_diff(), internal_fat_entry(), internal_fat_fsinfo(), internal_fat_mark_chain(), internal_fat_push(), priv_check_exfat(), and priv_check_visit().

◆ priv_check_in_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.

The addressable range is [2, 2 + clusters_total); anything else – the reserved 0 and 1, or a value past the last cluster – is not a cluster this volume has.

Parameters
[in]ctxThe scan context.
[in]clusterThe cluster number to test.
Returns
Whether cluster addresses a data cluster.
Return values
true2 <= cluster < 2 + clusters_total.
falseOut of range.
Precondition
ctx is non-NULL.
The report's clusters_total is populated.
Postcondition
No state modified.
Result depends only on the inputs.
Note
Pure; trivially thread-safe.
Since
0.1.0

Definition at line 125 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::clusters_total, k_cluster_first_data, and ra8_fs_check_ctx_t::rep.

Referenced by internal_exchk_mark_fatchain(), internal_exchk_mark_run(), internal_exchk_set_clusters(), internal_fat_classify_one(), internal_fat_entry(), internal_fat_mark_chain(), and priv_check_visit().

◆ priv_check_mark()

bool priv_check_mark ( ra8_fs_check_ctx_t * ctx,
uint32_t cluster )

Mark cluster visited in the scratch bitmap; report a prior visit.

Test-and-set of the bit for cluster. A bit that was ALREADY set means a second chain reached this cluster – a cross-link, or a loop – so the caller stops the walk it is on. The bit index is cluster - 2, matching the exFAT allocation bitmap's own layout so the two can be diffed directly.

Parameters
[in,out]ctxThe scan context (its bitmap is written).
[in]clusterThe cluster to mark; must be in range.
Returns
Whether the cluster had already been visited.
Return values
trueThe bit was already set (cross-link / loop).
falseThe bit was clear and is now set (first visit).
Precondition
ctx and ctx->bitmap are non-NULL.
priv_check_in_range holds for cluster.
Postcondition
The bit for cluster reads as 1.
No other bit is changed.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 134 of file ra8_fs_fat_check.c.

References ra8_fs_check_ctx_t::bitmap, k_check_bit_mask, k_check_byte_shift, and k_cluster_first_data.

Referenced by priv_check_visit().

◆ priv_check_visit()

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.

The one place the walkers share – FAT chains, exFAT runs, and the directory allocations of both – so the range and cross-link handling lives once. It marks cluster and returns whether the walk must stop: a cluster out of range records oor_kind (bumping entries_bad when that kind is k_ra8_fs_check_fault_bad_dir_entry), and a cluster already visited records a cross-link.

Parameters
[in,out]ctxThe scan context (its bitmap and report are written).
[in]clusterThe cluster to visit.
[in]oor_kindThe fault kind to record when cluster is out of range.
Returns
Whether the caller must stop walking.
Return values
trueA fault was recorded (out of range, or a cross-link).
falsecluster was in range and is now freshly marked.
Precondition
ctx and its bitmap are non-NULL.
oor_kind is a range-style fault kind.
Postcondition
On false the bit for cluster reads as 1.
On true the report carries the finding.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 147 of file ra8_fs_fat_check.c.

References ra8_fs_check_report_t::chains_crosslinked, ra8_fs_check_report_t::entries_bad, k_ra8_fs_check_fault_bad_dir_entry, k_ra8_fs_check_fault_crosslink, priv_check_fault(), priv_check_in_range(), priv_check_mark(), and ra8_fs_check_ctx_t::rep.

Referenced by internal_exchk_mark_dir_alloc(), internal_exchk_mark_fatchain(), internal_exchk_mark_run(), internal_fat_mark_chain(), and internal_fat_scan_cluster_dir().

◆ priv_check_zero_bitmap()

void priv_check_zero_bitmap ( ra8_fs_check_ctx_t * ctx)

Zero the scratch visited-bitmap over its valid bit range.

Clears (bitmap_bits + 7) / 8 bytes so a stale caller buffer cannot make a fresh scan see clusters as already visited. Called once, before the reference passes.

Parameters
[in,out]ctxThe scan context (its bitmap is cleared).
Returns
Nothing.
Precondition
ctx and ctx->bitmap are non-NULL.
ctx->bitmap is at least (bitmap_bits + 7) / 8 bytes.
Postcondition
Every valid bit reads as 0.
No state outside the bitmap is touched.
Note
Not thread-safe; the check holds the library lock.
Since
0.1.0

Definition at line 165 of file ra8_fs_fat_check.c.

References ra8_fs_check_ctx_t::bitmap, ra8_fs_check_ctx_t::bitmap_bits, k_check_bit_mask, and k_check_byte_shift.

Referenced by priv_check_exfat(), and priv_check_fat().

◆ ra8_fs_check()

ra8_err_t ra8_fs_check ( ra8_fs_mount_t * handle,
uint8_t * bitmap,
uint32_t bitmap_bytes,
ra8_fs_check_report_t * report )
nodiscard

Scan a mounted volume for consistency, read-only, and report findings.

Runs the passes described in this header's file comment for handle's filesystem and fills report. Nothing on the volume is written, so it is safe on a volume already suspected to be damaged – that is the whole point.

A return of k_ra8_ok means the scan COMPLETED, not that the volume is clean: a clean volume and a volume with a thousand lost clusters both return k_ra8_ok, and only report->faults_total tells them apart. A non-k_ra8_ok return means the scan could not run to completion – a null argument, an unmounted handle, or a backend read failure – and report retains its entry value. Findings are assembled in a private candidate and published only after the full scan succeeds, so callers cannot mistake partial counts for a completed check.

Pass bitmap of at least (report->clusters_total + 7) / 8 bytes to enable the reference / lost-cluster / cross-link analysis; pass bitmap == NULL (or fewer bytes) to run the classification pass alone (see the file comment). The bitmap is scratch: its contents on entry are ignored and on return undefined.

Parameters
[in]handleMount handle from ra8_fs_mount().
[in]bitmapScratch visited-cluster bitmap, or NULL for the classification-only pass. Not read on entry.
[in]bitmap_bytesSize of bitmap in bytes (0 when bitmap is NULL).
[out]reportReceives the structured findings.
Returns
ra8_err_t Error code.
Return values
k_ra8_okScan completed; inspect report->faults_total.
k_ra8_err_null_ptrhandle or report is NULL.
k_ra8_err_invalid_stateMount is not in use.
k_ra8_err_*Backend read failure while scanning.
Precondition
handle and report are non-NULL.
Mount is in use; no file open on it is mid-write.
Postcondition
On k_ra8_ok report->faults_total == 0 iff the volume is consistent within the scope this checker covers.
On error report retains its entry value.
No volume state is modified, on any return path.
Note
Not thread-safe unless a lock is installed (see ra8_fs_set_lock()).
Warning
Read-only: this never repairs. Do not treat a non-zero faults_total as "then fix it" – image the card off-device first.
See also
ra8_fs_check_report_t
Since
0.1.0

Definition at line 892 of file ra8_fs_fat_check.c.

References internal_check_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_check().

Referenced by ra8_fs_check().

◆ ra8_fs_check_test_fat_push_overflow()

void ra8_fs_check_test_fat_push_overflow ( ra8_fs_check_ctx_t * ctx,
uint32_t cluster,
bool already_truncated )

Exercise the FAT directory-worklist overflow guard from a host test.

Builds an already-full private worklist and attempts one push. This drives the bounded-stack guard without constructing 129 simultaneous directories in a RAM volume. Production code must call ra8_fs_check(), never this test seam.

Parameters
[in,out]ctxSynthetic context whose report receives a fault.
[in]clusterCluster recorded for the dropped directory.
[in]already_truncatedWhether a prior overflow was already recorded.
Returns
Nothing.
Precondition
ctx is non-NULL.
ctx owns a non-NULL, writable report.
Postcondition
A first overflow records one scan-truncated fault.
A repeated overflow records no additional fault.
Note
Test-only and not thread-safe; no filesystem lock is acquired.
Since
0.1.0

Definition at line 380 of file ra8_fs_fat_check.c.

References internal_fat_push(), and k_ra8_fs_check_max_dirs.

◆ ra8_fs_check_test_fat_scan_cluster_dir()

ra8_err_t ra8_fs_check_test_fat_scan_cluster_dir ( ra8_fs_check_ctx_t * ctx,
uint32_t first )

Exercise the FAT cluster-directory walker from a host unit test.

Creates an empty private directory worklist and walks the synthetic FAT chain beginning at first. This seam lets the exact clusters_total boundary be tested without requiring the mounted volume's real root directory to consume every data cluster. Production code must call ra8_fs_check().

Parameters
[in,out]ctxSynthetic scan context whose mount supplies the FAT and directory data.
[in]firstFirst cluster of the synthetic directory chain.
Returns
Error code.
Return values
k_ra8_okThe directory chain was walked; findings are in the report.
k_ra8_err_*Backend read failure.
Precondition
ctx, its report, bitmap and mount are non-NULL.
ctx->rep->clusters_total is non-zero and first is in range.
Postcondition
Every traversed directory cluster is marked, up to the first finding.
A non-terminal successor after the final hop records a cross-link or bad-directory fault.
Note
Test-only and not thread-safe; no filesystem lock is acquired.
MC/DC:
Host vectors independently provide EOC and an in-range cycle after the exact hop bound.
Since
0.1.0

Definition at line 615 of file ra8_fs_fat_check.c.

References internal_fat_scan_cluster_dir().