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

The exFAT streaming-write mechanism: entry-set positions and its helpers. More...

#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_fs.h"
#include "ra8_fs_fat_types_internal.h"
Include dependency graph for ra8_fs_fat_exfat_stream_internal.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ra8_err_t priv_exfat_bitmap_test (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint8_t *out_free)
 Read one allocation-bitmap bit: is this cluster free?
ra8_err_t priv_exfat_bitmap_scan (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t need, uint32_t *out_clus)
 Find a contiguous free run, starting from the mount's next-free hint.
ra8_err_t priv_exfat_bitmap_mark (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
 Mark a contiguous cluster run as allocated in the bitmap.
ra8_err_t priv_exfat_grow_dir (const ra8_fs_mount_t *m, exfat_dir_t *dir)
 Append one zeroed cluster to a directory, keeping its entry set true.
ra8_err_t priv_exfat_find_set (const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *path, exfat_setpos_t *pos, uint32_t max_pos, uint32_t *out_count, uint8_t *file_copy, uint8_t *strm_copy)
 Locate a file's full directory-entry set with per-entry positions.
ra8_err_t priv_exfat_free_clusters (const ra8_fs_mount_t *m, const uint8_t *strm)
 Free the cluster run / chain referenced by a Stream entry.
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)
 Lay down a fresh File/Stream/Name entry set and report where it went.
ra8_err_t priv_exfat_open_write (ra8_fs_mount_t *handle, const char *path, ra8_fs_mode_t mode, ra8_fs_file_t **out_file)
 Open an exFAT file for writing: create, truncate, or position to append.
ra8_err_t priv_exfat_write_stream (ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
 Stream bytes into an exFAT file, growing its allocation as needed.
ra8_err_t priv_exfat_ensure_clusters (ra8_fs_file_t *file, uint32_t need)
 Grow a file's allocation until it owns at least need clusters.
ra8_err_t priv_exfat_flush_set (ra8_fs_file_t *file)
 Rewrite a file's entry set from the handle, checksum last.

Detailed Description

The exFAT streaming-write mechanism: entry-set positions and its helpers.

exFAT streaming write (#602) is one self-contained mechanism with its own vocabulary, so it gets its own themed sub-header rather than being scattered alphabetically across ra8_fs_fat_protos_a_internal.h and its _b twin – the same reasoning that gave the timestamp and allocator mechanisms theirs. Everything the grow/flush engine needs is declared here, whichever translation unit defines it:

It is pulled in by the ra8_fs_fat_internal.h umbrella and by nothing outside this module.

References (every shorthand citation in this file):

  • "exFAT spec" = Microsoft Corp., "exFAT file system specification", revision 1.00, March 2021. Section numbers track that document.
Since
0.1.0

Definition in file ra8_fs_fat_exfat_stream_internal.h.

Function Documentation

◆ 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 )

Mark a contiguous cluster run as allocated in the bitmap.

Sets one bit per cluster, batching read-modify-write per bitmap sector. Per exFAT spec sec 7.1 the bitmap alone is authoritative for allocation state, so this – not a FAT edit – is what makes a cluster owned.

Parameters
[in]mMounted exFAT volume.
[in]bmp_lbaFirst LBA (volume-relative) of the bitmap.
[in]clusFirst cluster of the run.
[in]countNumber of clusters to mark used.
Returns
Error code.
Return values
k_ra8_okAll bits set + written.
k_ra8_err_*Backend read/write failure.
Precondition
m is non-NULL; the run is within the bitmap.
count >= 1.
Postcondition
The count bits for the run read as 1.
Only the affected bitmap sectors are rewritten.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

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 )

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

Walks the bitmap from the hint and, only if that finds nothing, repeats from cluster index 0 – a full rescan rather than a wrap-around of the remainder, because a free run may STRADDLE the hint and a window that stopped there would report a full disk the volume does not have.

Parameters
[in]mMounted exFAT volume.
[in]bmp_lbaFirst LBA (volume-relative) of the bitmap.
[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 anywhere (volume full / too fragmented).
k_ra8_err_*Backend read failure.
Precondition
m and out_clus are non-NULL; need >= 1.
The bitmap region is contiguous on disk.
Postcondition
On success *out_clus is the run's first cluster number.
No volume state is modified.
Note
Worst case is two passes over the bitmap; typical case is one short one.
Since
0.1.0

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 )

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

The probe the contiguity-preserving allocator runs before anything else. Keeping a run contiguous is worth one sector read, because a file that stays contiguous keeps NoFatChain and therefore keeps the read path's O(1) cluster arithmetic.

Parameters
[in]mMounted exFAT volume.
[in]bmp_lbaFirst LBA (volume-relative) of the bitmap.
[in]clusCluster number to test.
[out]out_freeReceives 1 when the cluster is free, else 0.
Returns
Error code.
Return values
k_ra8_okThe bit was read; *out_free is 0 or 1.
k_ra8_err_*Backend read failure.
Precondition
m and out_free are non-NULL; m->type is exFAT.
clus is inside [2, 2 + count_of_clusters).
Postcondition
*out_free is 0 or 1 on success.
No volume state is modified.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

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_ensure_clusters()

ra8_err_t priv_exfat_ensure_clusters ( ra8_fs_file_t * file,
uint32_t need )

Grow a file's allocation until it owns at least need clusters.

Takes clusters one at a time from the allocation bitmap, preferring the tail's successor so a run that can stay contiguous keeps its NoFatChain fast path, and materialising a real FAT chain over the run the moment it cannot. The streaming write path drives it to reach the cluster an offset lands in; the truncate path (#680) drives it to pre-size a file, which is why it lives in the header rather than staying private to the stream.

Parameters
[in,out]fileFile to grow; alloc_clusters, tail_cluster, first_cluster and no_fat_chain are updated in place.
[in]needClusters the file must own on return (>= 1 to have effect).
Returns
Error code.
Return values
k_ra8_okThe file owns need clusters or more.
k_ra8_err_no_memThe volume ran out before reaching need.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
file is non-NULL, in use, and its mount is an exFAT volume.
The caller holds the library lock.
Postcondition
On success alloc_clusters >= need and tail_cluster names the last.
On failure the clusters already taken stay allocated to file.
Note
Bounded: each iteration raises alloc_clusters, which cannot pass the volume's cluster count without the bitmap scan failing first.
Since
0.1.0

Definition at line 277 of file ra8_fs_fat_exfat_stream.c.

References internal_exfat_grow_one(), and k_ra8_ok.

Referenced by internal_exfat_slice_at(), and internal_exfat_trunc().

◆ priv_exfat_find_set()

ra8_err_t priv_exfat_find_set ( const ra8_fs_mount_t * m,
const exfat_dir_t * dir,
const char * path,
exfat_setpos_t * pos,
uint32_t max_pos,
uint32_t * out_count,
uint8_t * file_copy,
uint8_t * strm_copy )

Locate a file's full directory-entry set with per-entry positions.

Walks the root directory like priv_exfat_find but records the (cluster, index) of every entry in the matched set – the File entry plus all its secondaries – so callers can rewrite them in place. Non-matching sets are skipped entry-by-entry, which keeps the cursor aligned.

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory to search.
[in]pathTarget leaf name (ASCII); leading '/' stripped.
[out]posReceives the positions (File entry first).
[in]max_posCapacity of pos.
[out]out_countReceives the entry count (1 + SecondaryCount).
[out]file_copyReceives the 32-byte File entry.
[out]strm_copyReceives the 32-byte Stream-extension entry.
Returns
Error code.
Return values
k_ra8_okSet found; outputs populated.
k_ra8_err_not_foundNo matching set in dir.
k_ra8_err_no_memThe set has more entries than max_pos.
k_ra8_err_protocol_errorNo EOD marker appeared before the scan bound.
k_ra8_err_*Backend read failure.
Precondition
All pointers are non-NULL; m->type is exFAT.
dir locates an existing directory on this volume.
Postcondition
On success pos holds *out_count valid positions.
On failure the outputs are unspecified.
Note
One directory is searched – the one dir names (#605).
Since
0.1.0

Locate a file's full directory-entry set with per-entry positions.

Definition at line 194 of file ra8_fs_fat_exfat_mutate.c.

References exfat_cursor_t::cluster, exfat_cursor_t::entry_in_cluster, internal_exfat_try_set(), k_exfat_entry_bytes, k_exfat_entry_eod, k_exfat_entry_file, k_exfat_name_cap, k_exfat_scan_limit, k_ra8_err_not_found, k_ra8_err_protocol_error, k_ra8_ok, priv_exfat_cursor_init(), priv_exfat_needle_units(), priv_exfat_next_entry(), and exfat_cursor_t::scanned.

Referenced by internal_exfat_enter(), internal_exfat_rmdir_locate(), internal_setattr_exfat(), internal_stat_exfat(), internal_utime_exfat(), priv_exfat_open_write(), priv_exfat_rename(), and priv_exfat_unlink_at().

◆ priv_exfat_flush_set()

ra8_err_t priv_exfat_flush_set ( ra8_fs_file_t * file)

Rewrite a file's entry set from the handle, checksum last.

Reads the set back through a cursor from the recorded head, patches the File entry's modification stamps and the Stream entry's flags, FirstCluster, ValidDataLength and DataLength, accumulates the SetChecksum over the PATCHED bytes, and only then writes the two entries down. Order matters: the checksum covers every byte the patch touched, so computing it before the patch produces a set a host fsck rejects.

Parameters
[in,out]fileOpen exFAT handle whose entry set is to be committed.
Returns
Error code.
Return values
k_ra8_okThe on-disk set describes the handle exactly.
k_ra8_err_*Backend read/write failure.
Precondition
file is non-NULL, in use, and its mount is an exFAT volume.
file->entry_set_cluster / _index / _count came from a successful priv_exfat_find_set or priv_exfat_link.
Postcondition
On success the Stream entry's DataLength equals file->size_bytes.
On success the File entry's SetChecksum covers the patched bytes.
Note
Called after every write and again at close, so a writer cut off mid-stream still leaves a set describing what is really on the card.
Since
0.1.0

Definition at line 909 of file ra8_fs_fat_exfat_stream.c.

References exfat_setpos_t::cluster, exfat_setpos_t::index, internal_exfat_gather_set(), internal_exfat_patch_stream(), k_exfat_attr_archive, k_exfat_entry_bytes, k_exfat_max_set_bytes, k_exfat_off_file_attr, k_exfat_off_file_csum, k_ra8_ok, priv_exfat_file_stamp_write(), priv_exfat_set_checksum(), priv_exfat_write_dir_set(), and priv_wr16().

Referenced by internal_close_stamp(), internal_exfat_trunc(), internal_exfat_truncate(), and internal_write_locked().

◆ priv_exfat_free_clusters()

ra8_err_t priv_exfat_free_clusters ( const ra8_fs_mount_t * m,
const uint8_t * strm )

Free the cluster run / chain referenced by a Stream entry.

Contiguous (NoFatChain) files free ceil(DataLength / cluster bytes) bitmap bits from the first cluster; FAT-chained files walk the FAT and free each visited cluster. Per exFAT spec sec 7.1 the bitmap alone is authoritative, so the FAT entries themselves are left untouched.

Parameters
[in]mMounted exFAT volume.
[in]strmThe file's 32-byte Stream-extension entry.
Returns
Error code.
Return values
k_ra8_okClusters freed (or the file had none).
k_ra8_err_protocol_errorFAT chain exhausted the volume bound without EOC.
k_ra8_err_*Bitmap or backend failure.
Precondition
m and strm are non-NULL.
The caller owns the file: either its entries are already deleted, or it is truncating through an open handle.
Postcondition
The file's clusters read as free in the bitmap.
FAT contents are unchanged.
Note
Chain walk is bounded by the volume's cluster count.
Since
0.1.0

Free the cluster run / chain referenced by a Stream entry.

Definition at line 279 of file ra8_fs_fat_exfat_mutate.c.

References ra8_fs_mount_t::count_of_clusters, k_cluster_first_data, k_exfat_secflag_no_fat, k_exfat_strm_off_clus, k_exfat_strm_off_dlen, k_exfat_strm_off_flags, k_ra8_err_protocol_error, k_ra8_ok, priv_cluster_bytes(), priv_exfat_bitmap_clear(), priv_exfat_bitmap_lba(), priv_fat_get(), priv_is_eoc(), priv_rd32(), and priv_rd64().

Referenced by internal_exfat_free_run(), internal_exfat_truncate(), priv_exfat_rmdir(), and priv_exfat_unlink_at().

◆ priv_exfat_grow_dir()

ra8_err_t priv_exfat_grow_dir ( const ra8_fs_mount_t * m,
exfat_dir_t * dir )

Append one zeroed cluster to a directory, keeping its entry set true.

The directory counterpart of the file grow path, reusing its contiguous/NoFatChain->FAT-chain machinery (#677). Surveys the directory's run, prefers the tail's successor to stay contiguous, ZEROES the new cluster before linking it (a directory cluster reads as its own contents, so a half-built one must never be reachable), links it – converting the run to a real FAT chain if contiguity is broken – and rewrites the directory's own Stream entry so a later mount sees the larger run. The volume ROOT carries no such entry set (self_cluster 0) and only has its FAT chain extended.

Parameters
[in]mMounted exFAT volume.
[in,out]dirDirectory to grow; its contig_end is updated to reflect the new run (0 once it has become a FAT chain).
Returns
Error code.
Return values
k_ra8_okThe directory owns one more zeroed cluster.
k_ra8_err_no_memThe volume has no free cluster.
k_ra8_err_not_supportedThe directory's own set is larger than this adapter can rewrite.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
m and dir are non-NULL; m->type is exFAT.
dir->cluster is a heap cluster and dir->self_cluster locates its set (or is 0 for the root).
Postcondition
On success a walk of dir reaches the new cluster and it reads empty.
On failure any cluster taken is at worst leaked, never linked-but-dirty.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

Definition at line 566 of file ra8_fs_fat_exfat_stream.c.

References exfat_dir_t::cluster, exfat_dir_t::contig_end, internal_exfat_dir_append(), internal_exfat_dir_relen(), internal_exfat_dir_survey(), k_cluster_first_data, k_ra8_ok, priv_cluster_bytes(), and exfat_dir_t::self_cluster.

Referenced by priv_exfat_find_dir_space().

◆ 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 )

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

Finds a run of free directory slots, builds the set (typed entries, name hash, creation stamps, SetChecksum) and writes it. The caller gets the head position and the entry count back so a streaming writer can keep rewriting the same set as the file grows, instead of searching the directory again on every flush.

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory the set is linked into.
[in]nameLeaf name as UTF-16 code units (no leading '/').
[in]nlenName length in UTF-16 UNITS (1..k_exfat_name_cap), which is what NameLength counts (#606).
[out]out_headReceives the File entry's (cluster, index).
[out]out_countReceives the entry count (1 + SecondaryCount).
Returns
Error code.
Return values
k_ra8_okEntry set written; outputs populated.
k_ra8_err_no_memNo run of free directory slots big enough.
k_ra8_err_*Backend read/write failure.
Precondition
m, dir, name, out_head and out_count are non-NULL.
nlen is the unit count priv_exfat_name_to_units() produced, >= 1.
Postcondition
On success dir holds a zero-length entry set for that name.
On success *out_head addresses that set's File entry.
Note
The set is kept within one directory cluster.
Since
0.1.0

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_open_write()

ra8_err_t priv_exfat_open_write ( ra8_fs_mount_t * handle,
const char * path,
ra8_fs_mode_t mode,
ra8_fs_file_t ** out_file )

Open an exFAT file for writing: create, truncate, or position to append.

The exFAT half of ra8_fs_open for the two writing modes. It locates the name's entry set, refuses a directory (a truncate would free the clusters holding its contents), and then either truncates the file in place – keeping its entry, and therefore its creation stamp – or seeks to DataLength for an append. A name that does not exist is created as a zero-length entry set, so the handle is usable before a single byte has been written.

Parameters
[in,out]handleMounted exFAT volume.
[in]pathFlat root-level name (ASCII); leading '/' stripped.
[in]modek_ra8_fs_mode_write or k_ra8_fs_mode_append.
[out]out_fileReceives the open handle.
Returns
Error code.
Return values
k_ra8_okFile open and positioned for mode.
k_ra8_err_invalid_argEmpty or over-long name, or path is a directory.
k_ra8_err_no_memFile table full, or no free directory slot.
k_ra8_err_not_supportedThe existing entry set is larger than this adapter can rewrite.
k_ra8_err_*Backend read/write failure.
Precondition
handle, path and out_file are non-NULL; the mount is in use.
mode is a writing mode; read opens never reach here.
Postcondition
On success *out_file is in use with its entry-set coordinates set.
On failure no file slot is left marked in use.
Note
Not thread-safe; the public entry point holds the library lock.
Since
0.1.0

Definition at line 412 of file ra8_fs_fat_exfat_openw.c.

References internal_exfat_open_created(), internal_exfat_open_found(), k_exfat_entry_bytes, k_exfat_name_cap, k_exfat_set_max_entries, k_ra8_err_invalid_arg, k_ra8_err_no_mem, k_ra8_err_not_found, k_ra8_ok, priv_exfat_find_set(), priv_exfat_name_to_units(), and priv_exfat_resolve_parent().

Referenced by priv_exfat_open().

◆ priv_exfat_write_stream()

ra8_err_t priv_exfat_write_stream ( ra8_fs_file_t * file,
const uint8_t * buf,
uint32_t len )

Stream bytes into an exFAT file, growing its allocation as needed.

The engine behind ra8_fs_write on an exFAT mount. It closes any gap between ValidDataLength and the write offset with real zeros first (the format expresses "not yet written" only as a PREFIX length, so a hole in the middle cannot be recorded and must not be left), then writes sector slice by sector slice, taking one more cluster from the allocation bitmap whenever the offset crosses into a cluster the file does not own yet.

Parameters
[in,out]fileOpen exFAT handle in a writing mode.
[in]bufSource bytes.
[in]lenNumber of bytes to write (> 0).
Returns
Error code.
Return values
k_ra8_okAll len bytes are on the volume.
k_ra8_err_no_memThe volume has no free cluster left.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
file and buf are non-NULL; the handle is in use and writable.
file->mount->type is exFAT.
Postcondition
On success file->offset advanced by len and valid_bytes covers it.
On failure some bytes may already be on the volume; the entry set is not updated here, so the file's recorded length never overstates it.
Note
Does NOT touch the directory – priv_exfat_flush_set does that.
Since
0.1.0

Definition at line 770 of file ra8_fs_fat_exfat_stream.c.

References internal_exfat_close_gap(), internal_exfat_slice_at(), k_ra8_ok, and priv_write_into_sector().