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

exFAT allocation-bitmap primitives and directory entry-set construction. More...

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

Go to the source code of this file.

Functions

static uint16_t internal_exfat_csum_add (uint16_t cs, uint8_t b)
 One step of exFAT's rotate-right-then-add 16-bit checksum.
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.
uint16_t priv_exfat_set_checksum (const uint8_t *set, uint32_t bytes)
 Compute the SetChecksum over a built directory entry set.
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.
static ra8_err_t internal_exfat_bitmap_window (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t from, uint32_t need, uint32_t *out_clus)
 Scan a window of the allocation bitmap for a contiguous free run.
ra8_err_t priv_exfat_bitmap_scan (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t need, uint32_t *out_clus)
 Implementation of priv_exfat_bitmap_scan() – hinted pass, then a full rescan.
ra8_err_t priv_exfat_bmp_switch (const ra8_fs_mount_t *m, uint64_t lba, uint64_t *loaded, uint8_t *sec)
 Flush the cached bitmap sector and load lba if it changed.
ra8_err_t priv_exfat_bitmap_mark (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
 Implementation of priv_exfat_bitmap_mark() – one read-modify-write per sector.
ra8_err_t priv_exfat_bitmap_test (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint8_t *out_free)
 Implementation of priv_exfat_bitmap_test() – one sector read, one bit.
static ra8_err_t internal_exfat_read_entry (const ra8_fs_mount_t *m, uint32_t cluster, uint32_t idx, uint8_t *out)
 Read one 32-byte directory entry by index within a cluster.
static uint8_t internal_exfat_slot_free (uint8_t type_byte)
 True if a directory entry slot is free (end-of-dir or deleted).
static ra8_err_t internal_exfat_space_in_cluster (const ra8_fs_mount_t *m, uint32_t cluster, uint32_t need, uint32_t *out_idx)
 Scan one directory cluster for need consecutive free entries.
static ra8_err_t internal_exfat_scan_dir_space (const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t need, uint32_t *out_clus, uint32_t *out_idx)
 Scan a directory's existing clusters for need consecutive free slots.
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.
static uint32_t internal_exfat_build_set (uint8_t *set, const uint16_t *name, uint32_t nlen)
 Build a zero-length File + Stream + Name entry set into set.
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_link (const ra8_fs_mount_t *m, const exfat_dir_t *dir, const uint16_t *name, uint32_t nlen, exfat_setpos_t *out_head, uint32_t *out_count)
 Implementation of priv_exfat_link() – one directory-slot scan, one set write.

Detailed Description

exFAT allocation-bitmap primitives and directory entry-set construction.

The two things every exFAT write needs before it can put a byte anywhere: somewhere to put it (the allocation bitmap – locate, probe, scan, mark) and something to name it (the File + Stream + Name directory entry set – build, checksum, place). The streaming engine that drives them lives in ra8_fs_fat_exfat_stream.c.

Since
0.1.0

Definition in file ra8_fs_fat_exfat_write.c.

Function Documentation

◆ internal_exfat_bitmap_window()

ra8_err_t internal_exfat_bitmap_window ( const ra8_fs_mount_t * m,
uint64_t bmp_lba,
uint32_t from,
uint32_t need,
uint32_t * out_clus )
static

Scan a window of the allocation bitmap for a contiguous free run.

Walks cluster indices [from, m->count_of_clusters) looking for need consecutive clear bits, loading each bitmap sector once. Assumes a contiguous bitmap (true for a freshly formatted volume).

Parameters
[in]mMounted exFAT volume.
[in]bmp_lbaFirst LBA (volume-relative) of the bitmap.
[in]fromCluster INDEX (0-based) to begin the walk at.
[in]needNumber of contiguous free clusters required.
[out]out_clusFirst cluster of the found run.
Returns
Error code.
Return values
k_ra8_okA run of need free clusters was found.
k_ra8_err_no_memNo such run in this window.
k_ra8_err_*Backend read failure.
Precondition
m and out_clus are non-NULL; need >= 1.
from is a cluster index, not a cluster number.
Postcondition
On success *out_clus is the run's first cluster NUMBER.
No volume state modified.
Note
O(count_of_clusters - from); bounded by the volume size.
Since
0.1.0

Definition at line 131 of file ra8_fs_fat_exfat_write.c.

References ra8_fs_mount_t::count_of_clusters, k_cluster_first_data, k_exfat_bit_mask, k_exfat_bit_shift, k_ra8_err_no_mem, k_ra8_ok, priv_bps(), priv_read_sector(), and priv_sec_io().

Referenced by priv_exfat_bitmap_scan().

◆ internal_exfat_build_set()

uint32_t internal_exfat_build_set ( uint8_t * set,
const uint16_t * name,
uint32_t nlen )
static

Build a zero-length File + Stream + Name entry set into set.

Fills the typed entries, the name hash, the creation stamps and the trailing SetChecksum for a file that owns NO clusters yet: FirstCluster 0, DataLength 0, ValidDataLength 0 and GeneralSecondaryFlags = AllocationPossible with NoFatChain CLEAR. That is what exFAT spec sec 7.4.4 requires of an empty file, and it is the honest starting point for a stream: the first priv_exfat_flush_set after a byte lands rewrites the three fields and sets NoFatChain if the run is still contiguous.

Parameters
[out]setBuffer (>= k_exfat_max_set_bytes).
[in]nameFile name as UTF-16 code units.
[in]nlenName length in UTF-16 UNITS, which is what NameLength counts and what a Name entry holds fifteen of.
Returns
The total byte length of the built set.
Return values
>0Number of bytes written into set.
Precondition
set and name are non-NULL; set is large enough.
nlen is the unit count priv_utf8_to_utf16() produced, and >= 1.
Postcondition
set holds a complete entry set with a valid SetChecksum.
No volume state modified.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 476 of file ra8_fs_fat_exfat_write.c.

References k_exfat_attr_archive, k_exfat_entry_bytes, k_exfat_entry_file, k_exfat_entry_name, k_exfat_entry_stream, k_exfat_name_off, k_exfat_name_per_entry, k_exfat_off_file_attr, k_exfat_off_file_csum, k_exfat_off_file_secnt, k_exfat_off_strm_hash, k_exfat_secflag_poss, k_exfat_strm_off_flags, k_exfat_strm_off_nlen, priv_exfat_file_stamp_create(), priv_exfat_name_hash(), priv_exfat_set_checksum(), and priv_wr16().

Referenced by priv_exfat_link().

◆ internal_exfat_csum_add()

uint16_t internal_exfat_csum_add ( uint16_t cs,
uint8_t b )
static

One step of exFAT's rotate-right-then-add 16-bit checksum.

Used for both the directory SetChecksum and the name hash.

Parameters
[in]csRunning checksum.
[in]bNext byte.
Returns
Updated checksum.
Return values
0..0xFFFFThe folded value.
Precondition
None.
cs is the prior running value.
Postcondition
No state modified.
Result depends only on inputs.
Note
Pure function.
Since
0.1.0

Definition at line 42 of file ra8_fs_fat_exfat_write.c.

References k_exfat_csum_hi_bit.

Referenced by priv_exfat_name_hash(), and priv_exfat_set_checksum().

◆ internal_exfat_read_entry()

ra8_err_t internal_exfat_read_entry ( const ra8_fs_mount_t * m,
uint32_t cluster,
uint32_t idx,
uint8_t * out )
static

Read one 32-byte directory entry by index within a cluster.

Reads the containing sector and copies out the addressed entry.

Parameters
[in]mMounted exFAT volume.
[in]clusterDirectory cluster.
[in]idxEntry index within the cluster.
[out]outReceives the entry.
Returns
Error code.
Return values
k_ra8_okEntry read.
k_ra8_err_*Backend read failure.
Precondition
All pointers are non-NULL.
idx is within the cluster's entry capacity.
Postcondition
out holds the entry on success.
No state modified.
Note
Reads the containing sector.
Since
0.1.0

Definition at line 270 of file ra8_fs_fat_exfat_write.c.

References k_exfat_entry_bytes, k_ra8_ok, priv_bps(), priv_byte_copy(), priv_cluster_to_lba(), priv_read_sector(), and priv_sec_io().

Referenced by internal_exfat_space_in_cluster().

◆ internal_exfat_scan_dir_space()

ra8_err_t internal_exfat_scan_dir_space ( const ra8_fs_mount_t * m,
const exfat_dir_t * dir,
uint32_t need,
uint32_t * out_clus,
uint32_t * out_idx )
static

Scan a directory's existing clusters for need consecutive free slots.

The inner walk of priv_exfat_find_dir_space, split out so the grow loop that wraps it stays small. Walks dir cluster by cluster and reports k_ra8_err_no_mem when its CURRENT run has no room – it never grows the directory itself; that is the caller's job.

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory to search at its current size.
[in]needNumber of consecutive free entries required.
[out]out_clusReceives the cluster holding the run.
[out]out_idxReceives the run's first entry index in that cluster.
Returns
Error code.
Return values
k_ra8_okA run was found at the current size.
k_ra8_err_no_memThe directory's current run has no run of need.
k_ra8_err_*Backend read failure.
Precondition
Every pointer argument is non-NULL; need >= 1.
m->type is exFAT.
Postcondition
On success the run location is returned.
No volume state is modified.
Note
The walk is bounded by k_exfat_scan_limit clusters (P10 Rule 2).
Since
0.1.0

Definition at line 390 of file ra8_fs_fat_exfat_write.c.

References exfat_dir_t::cluster, exfat_dir_t::contig_end, internal_exfat_space_in_cluster(), k_exfat_scan_limit, k_ra8_err_no_mem, k_ra8_err_not_found, k_ra8_ok, and priv_exfat_step_cluster().

Referenced by priv_exfat_find_dir_space().

◆ internal_exfat_slot_free()

uint8_t internal_exfat_slot_free ( uint8_t type_byte)
static

True if a directory entry slot is free (end-of-dir or deleted).

A slot is reusable when it is the end-of-directory marker or has bit 7 clear.

Parameters
[in]type_byteEntry type byte (entry[0]).
Returns
1 if the slot is available for reuse, else 0.
Return values
1Slot is free.
0Slot is in use.
Precondition
None.
type_byte is entry[0].
Postcondition
No state modified.
Result depends only on type_byte.
Note
exFAT marks an in-use entry with bit 7 set.
Since
0.1.0

Definition at line 300 of file ra8_fs_fat_exfat_write.c.

References k_exfat_entry_eod, and k_exfat_inuse_bit.

Referenced by internal_exfat_space_in_cluster().

◆ internal_exfat_space_in_cluster()

ra8_err_t internal_exfat_space_in_cluster ( const ra8_fs_mount_t * m,
uint32_t cluster,
uint32_t need,
uint32_t * out_idx )
static

Scan one directory cluster for need consecutive free entries.

Inner half of priv_exfat_find_dir_space, split out so the outer walk stays inside the nesting and statement budgets.

Parameters
[in]mMounted exFAT volume.
[in]clusterDirectory cluster to scan.
[in]needNumber of consecutive free entries required.
[out]out_idxEntry index of the run start within cluster.
Returns
Error code.
Return values
k_ra8_okA run of need free slots starts at *out_idx.
k_ra8_err_no_memNo such run in this cluster.
k_ra8_err_*Backend read failure.
Precondition
m and out_idx are non-NULL; need >= 1.
cluster belongs to the directory being searched.
Postcondition
On success *out_idx addresses the first free slot of the run.
No volume state modified.
Note
A set is never split across two clusters.
Since
0.1.0

Definition at line 333 of file ra8_fs_fat_exfat_write.c.

References internal_exfat_read_entry(), internal_exfat_slot_free(), k_exfat_entry_bytes, k_ra8_err_no_mem, k_ra8_ok, and priv_cluster_bytes().

Referenced by internal_exfat_scan_dir_space().

◆ priv_exfat_bitmap_mark()

ra8_err_t priv_exfat_bitmap_mark ( const ra8_fs_mount_t * m,
uint64_t bmp_lba,
uint32_t clus,
uint32_t count )

Implementation of priv_exfat_bitmap_mark() – one read-modify-write per sector.

Mark a contiguous cluster run as allocated in the bitmap.

Definition at line 213 of file ra8_fs_fat_exfat_write.c.

References k_cluster_first_data, k_exfat_bit_mask, k_exfat_bit_shift, k_ra8_ok, priv_bps(), priv_exfat_bmp_switch(), priv_sec_io(), and priv_write_sector().

Referenced by internal_exfat_dir_alloc(), internal_exfat_dir_append(), and internal_exfat_grow_one().

◆ priv_exfat_bitmap_scan()

ra8_err_t priv_exfat_bitmap_scan ( const ra8_fs_mount_t * m,
uint64_t bmp_lba,
uint32_t need,
uint32_t * out_clus )

Implementation of priv_exfat_bitmap_scan() – hinted pass, then a full rescan.

Find a contiguous free run, starting from the mount's next-free hint.

Definition at line 170 of file ra8_fs_fat_exfat_write.c.

References ra8_fs_mount_t::count_of_clusters, internal_exfat_bitmap_window(), k_cluster_first_data, k_ra8_err_no_mem, and priv_alloc_hint_get().

Referenced by internal_exfat_dir_alloc(), and internal_exfat_pick_cluster().

◆ priv_exfat_bitmap_test()

ra8_err_t priv_exfat_bitmap_test ( const ra8_fs_mount_t * m,
uint64_t bmp_lba,
uint32_t clus,
uint8_t * out_free )

Implementation of priv_exfat_bitmap_test() – one sector read, one bit.

Read one allocation-bitmap bit: is this cluster free?

Definition at line 233 of file ra8_fs_fat_exfat_write.c.

References k_cluster_first_data, k_exfat_bit_mask, k_exfat_bit_shift, k_ra8_ok, priv_bps(), priv_read_sector(), and priv_sec_io().

Referenced by internal_exfat_pick_cluster().

◆ priv_exfat_bmp_switch()

ra8_err_t priv_exfat_bmp_switch ( const ra8_fs_mount_t * m,
uint64_t lba,
uint64_t * loaded,
uint8_t * sec )

Flush the cached bitmap sector and load lba if it changed.

Writes the dirty cached sector before reading the newly requested one.

Parameters
[in]mMounted exFAT volume.
[in]lbaBitmap sector wanted next.
[in,out]loadedCurrently-cached LBA (UINT32_MAX if none).
[in,out]secCached sector buffer.
Returns
Error code.
Return values
k_ra8_oksec now holds lba.
k_ra8_err_*Backend read/write failure.
Precondition
All pointers are non-NULL.
sec matches loaded on entry.
Postcondition
sec holds lba; the previous sector was written if dirty.
loaded == lba.
Note
Keeps the caller's loop nesting shallow.
Since
0.1.0

Definition at line 192 of file ra8_fs_fat_exfat_write.c.

References k_ra8_ok, priv_read_sector(), and priv_write_sector().

Referenced by priv_exfat_bitmap_clear(), and priv_exfat_bitmap_mark().

◆ priv_exfat_find_bitmap()

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.

Streams the root directory for the 0x81 entry and returns its data run.

Parameters
[in]mMounted exFAT volume.
[out]out_clusFirst cluster of the allocation bitmap.
[out]out_lenBitmap length in bytes.
Returns
Error code.
Return values
k_ra8_okBitmap located.
k_ra8_err_not_foundNo allocation-bitmap entry.
k_ra8_err_*Backend read failure.
Precondition
All pointers are non-NULL; m->type is exFAT.
m->root_cluster is valid.
Postcondition
On success the bitmap location is returned.
No volume state modified.
Note
Reads only the directory chain.
Since
0.1.0

Definition at line 81 of file ra8_fs_fat_exfat_write.c.

References k_exfat_entry_bitmap, k_exfat_entry_bytes, k_exfat_entry_eod, k_exfat_scan_limit, k_exfat_strm_off_clus, k_exfat_strm_off_dlen, k_ra8_err_not_found, k_ra8_ok, priv_exfat_cursor_init(), priv_exfat_dir_root(), priv_exfat_next_entry(), priv_rd32(), and exfat_cursor_t::scanned.

Referenced by internal_exfat_dir_alloc(), internal_space_exfat_free(), priv_check_exfat(), and priv_exfat_bitmap_lba().

◆ priv_exfat_find_dir_space()

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.

Walks dir cluster by cluster, scanning each for a run of reusable slots – end-of-directory markers and retired entries both qualify. A set is never split across two clusters, so the run must fit inside one. When no existing cluster has room, the directory is GROWN one zeroed cluster at a time (priv_exfat_grow_dir) and rescanned, so k_ra8_err_no_mem now means the VOLUME is full, not merely that the directory was (#677). One grow always yields at least k_exfat_set_writable free slots – a fresh cluster holds far more than any set needs – so the retry is bounded by k_exfat_dir_grow_max.

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory to search (its own set location grows it).
[in]needNumber of consecutive free entries required.
[out]out_clusReceives the cluster holding the run.
[out]out_idxReceives the run's first entry index in that cluster.
Returns
Error code.
Return values
k_ra8_okA run was found (after growing, if it had to).
k_ra8_err_no_memThe volume has no free cluster to grow into.
k_ra8_err_*Bitmap or backend read/write failure.
Precondition
Every pointer argument is non-NULL; need >= 1.
m->type is exFAT.
Postcondition
On k_ra8_ok the run location is returned; the directory may be one or more clusters larger than on entry.
On any error no directory slot is reserved.
Note
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 420 of file ra8_fs_fat_exfat_write.c.

References internal_exfat_scan_dir_space(), k_exfat_dir_grow_max, k_ra8_err_no_mem, k_ra8_ok, and priv_exfat_grow_dir().

Referenced by internal_exfat_place_rename(), priv_exfat_link(), and priv_exfat_mkdir().

◆ priv_exfat_link()

ra8_err_t priv_exfat_link ( const ra8_fs_mount_t * m,
const exfat_dir_t * dir,
const uint16_t * name,
uint32_t nlen,
exfat_setpos_t * out_head,
uint32_t * out_count )

Implementation of priv_exfat_link() – one directory-slot scan, one set write.

Lay down a fresh File/Stream/Name entry set and report where it went.

Definition at line 544 of file ra8_fs_fat_exfat_write.c.

References exfat_setpos_t::cluster, exfat_setpos_t::index, internal_exfat_build_set(), k_exfat_max_set_bytes, k_exfat_name_per_entry, k_ra8_ok, priv_exfat_find_dir_space(), and priv_exfat_write_dir_set().

Referenced by internal_exfat_open_created().

◆ priv_exfat_name_hash()

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.

Hashes the up-cased UTF-16LE name (low then high byte per unit), up-casing through priv_exfat_upcase_unit – the volume's own table, which is what the specification defines the hash over.

Up-casing with an ASCII-only rule, which is what this did, stored a hash no compliant reader recomputes for any name outside ASCII: the host could see the file listed and then fail to find it, because the hash is the index it probes with (#606).

Parameters
[in]nameFile name as UTF-16 code units.
[in]nlenName length in UTF-16 units.
Returns
16-bit name hash.
Return values
0..0xFFFFThe hash value.
Precondition
name is non-NULL and holds at least nlen units.
The volume's up-case table is the canonical one, or the name is ASCII.
Postcondition
No state modified.
Inputs unmodified.
Note
Pure function.
Since
0.1.0

Definition at line 49 of file ra8_fs_fat_exfat_write.c.

References internal_exfat_csum_add(), k_utf_byte_mask, k_utf_byte_shift, and priv_exfat_upcase_unit().

Referenced by internal_exchk_verify_set(), internal_exfat_build_dir_set(), internal_exfat_build_rename_set(), and internal_exfat_build_set().

◆ priv_exfat_set_checksum()

uint16_t priv_exfat_set_checksum ( const uint8_t * set,
uint32_t bytes )

Compute the SetChecksum over a built directory entry set.

Folds every byte except the File entry's checksum field (bytes 2-3).

Parameters
[in]setContiguous entry-set bytes (File + Stream + Name entries).
[in]bytesTotal byte count of the set.
Returns
16-bit SetChecksum.
Return values
0..0xFFFFThe checksum.
Precondition
set is non-NULL and at least bytes long.
bytes is a multiple of the entry size.
Postcondition
No state modified.
set is unmodified.
Note
Pure function.
Since
0.1.0

Definition at line 65 of file ra8_fs_fat_exfat_write.c.

References internal_exfat_csum_add(), and k_exfat_off_file_csum.

Referenced by internal_exchk_verify_set(), internal_exfat_build_dir_set(), internal_exfat_build_rename_set(), internal_exfat_build_set(), internal_exfat_dir_relen(), internal_setattr_exfat(), internal_utime_exfat(), and priv_exfat_flush_set().

◆ priv_exfat_write_dir_set()

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.

Read-modify-writes each entry slot so neighbouring entries are preserved.

Parameters
[in]mMounted exFAT volume.
[in]clusterDirectory cluster holding the run.
[in]idxEntry index of the run start.
[in]setBuilt entry-set bytes.
[in]bytesTotal size of the set.
Returns
Error code.
Return values
k_ra8_okEntries written.
k_ra8_err_*Backend read/write failure.
Precondition
m and set are non-NULL; the run fits in the cluster.
bytes is a multiple of the entry size.
Postcondition
The directory holds the new entry set.
Read-modify-write preserves neighbouring entries.
Note
Writes one entry (sector RMW) at a time.
Since
0.1.0

Definition at line 517 of file ra8_fs_fat_exfat_write.c.

References k_exfat_entry_bytes, k_ra8_ok, priv_bps(), priv_byte_copy(), priv_cluster_to_lba(), priv_read_sector(), priv_sec_io(), and priv_write_sector().

Referenced by internal_exfat_dir_relen(), internal_exfat_place_rename(), internal_exfat_put_entry(), internal_setattr_exfat(), internal_utime_exfat(), priv_exfat_flush_set(), priv_exfat_link(), priv_exfat_mkdir(), and priv_exfat_set_label().