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

exFAT streaming write: grow the allocation, keep the entry set true. 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_stream.c:

Go to the source code of this file.

Functions

static ra8_err_t internal_exfat_pick_cluster (const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t prefer, uint32_t *out)
 Choose the next cluster to allocate, preferring the tail's successor.
static ra8_err_t internal_exfat_materialize_run (const ra8_fs_mount_t *m, uint32_t first, uint32_t alloc, uint32_t tail, uint32_t next)
 Write a real FAT chain over a contiguous run, ending at next.
static ra8_err_t internal_exfat_link_cluster (ra8_fs_file_t *file, uint32_t next)
 Attach a freshly allocated cluster to the file's allocation.
static ra8_err_t internal_exfat_grow_one (ra8_fs_file_t *file)
 Add one cluster to a file's allocation.
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.
static ra8_err_t internal_exfat_dir_survey (const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t *out_alloc, uint32_t *out_tail, uint8_t *out_nofat)
 Survey a directory's current allocation into run coordinates.
static ra8_err_t internal_exfat_dir_link (const ra8_fs_mount_t *m, uint32_t first, uint32_t alloc, uint32_t tail, uint8_t *nofat, uint32_t next)
 Attach next to a directory's run, converting to a chain if needed.
static ra8_err_t internal_exfat_dir_relen (const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint64_t new_bytes, uint8_t nofat)
 Rewrite a grown directory's own Stream entry to describe its new run.
static ra8_err_t internal_exfat_dir_append (const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t alloc, uint32_t tail, uint8_t *nofat)
 Allocate one cluster, zero it, link it to the run, seal the old tail.
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.
static ra8_err_t internal_exfat_cluster_at (ra8_fs_file_t *file, uint32_t idx, uint32_t *out)
 Resolve the cluster at chain index idx of an already-grown file.
static ra8_err_t internal_exfat_slice_at (ra8_fs_file_t *file, uint64_t pos, uint64_t *out_lba, uint32_t *out_off, uint32_t *out_room)
 Locate byte position pos on the volume, allocating to reach it.
static ra8_err_t internal_exfat_close_gap (ra8_fs_file_t *file)
 Fill [valid_bytes, offset) with real zeros.
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.
static void internal_exfat_patch_stream (const ra8_fs_file_t *file, uint8_t *strm)
 Patch a Stream-extension entry to describe the handle's current state.
static ra8_err_t internal_exfat_gather_set (const ra8_fs_file_t *file, uint8_t *set, exfat_setpos_t *at_file, exfat_setpos_t *at_stream)
 Read a file's entry set back into set, recording where it lives.
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

exFAT streaming write: grow the allocation, keep the entry set true.

The engine behind ra8_fs_open(write|append) on an exFAT volume (#602). Three mechanisms live here and nothing else does:

  1. Growth. A file takes one cluster at a time out of the allocation bitmap. Before scanning it PROBES the successor of its current tail, because a run that stays contiguous keeps NoFatChain set and therefore keeps the read path's O(1) cluster arithmetic.
  2. The NoFatChain transition. exFAT spec sec 7.4.2: while the flag is set the FAT entries for a file's clusters carry nothing an implementation may read, so the moment the next cluster is NOT the successor of the last the flag has to come off – and coming off means first writing a real FAT chain over every cluster already allocated, then linking the new one, then clearing the bit in the entry set. Doing it in any other order leaves a file the format says is contiguous and the FAT says is not.
  3. ValidDataLength vs DataLength. exFAT spec sec 7.4.5 makes the first a PREFIX length: bytes below it were written, bytes between it and DataLength were never initialised and must read as zero. That is why priv_exfat_write_stream closes any gap with real zeros before writing – a hole in the MIDDLE of a file has no encoding, so it must not be created – and why the read path serves zeros past the prefix instead of whatever the previous tenant of those clusters left behind.

References (every shorthand citation in this file):

  • "exFAT spec" = Microsoft Corp., "exFAT file system specification", revision 1.00, March 2021.

NASA Power-of-Ten compliance:

  • Rule 2: the growth walk is bounded by count_of_clusters, the entry-set walk by k_exfat_set_writable, and the byte loops by the byte count.
  • Rule 3: zero malloc; the largest buffer is one entry set on the stack.
  • Rule 7: every bitmap, FAT and backend call is checked.
Since
0.1.0

Definition in file ra8_fs_fat_exfat_stream.c.

Function Documentation

◆ internal_exfat_close_gap()

ra8_err_t internal_exfat_close_gap ( ra8_fs_file_t * file)
static

Fill [valid_bytes, offset) with real zeros.

Only ever has work to do when a handle is positioned past the written prefix – an append to a file some other implementation left with ValidDataLength < DataLength. ValidDataLength is a PREFIX length, so the gap cannot be recorded as unwritten once bytes land beyond it; the only honest answer is to write the zeros a reader is entitled to see.

Parameters
[in,out]fileOpen exFAT handle in a writing mode.
Returns
Error code.
Return values
k_ra8_okThere is no gap left below file->offset.
k_ra8_err_no_memThe volume had no cluster to grow into.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
file is non-NULL and its mount is an exFAT volume.
The caller holds the library lock.
Postcondition
On success file->valid_bytes >= file->offset.
On failure file->valid_bytes still describes real zeros on disk.
Note
Each iteration advances valid_bytes by at least one byte, so the loop is bounded by the gap it was given.
Since
0.1.0

Definition at line 745 of file ra8_fs_fat_exfat_stream.c.

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

Referenced by priv_exfat_write_stream().

◆ internal_exfat_cluster_at()

ra8_err_t internal_exfat_cluster_at ( ra8_fs_file_t * file,
uint32_t idx,
uint32_t * out )
static

Resolve the cluster at chain index idx of an already-grown file.

A contiguous file is arithmetic. A chained one walks the FAT from the forward waypoint the read path already maintains, so a sequential write costs one FAT lookup per cluster rather than one per cluster examined from the head.

Parameters
[in,out]fileFile to position within.
[in]idxChain index (0 = the file's first cluster).
[out]outReceives the cluster number at idx.
Returns
Error code.
Return values
k_ra8_okThe cluster was resolved.
k_ra8_err_invalid_stateThe chain ended before idx – the entry set and the FAT disagree.
k_ra8_err_*FAT read failure.
Precondition
file and out are non-NULL; alloc_clusters > idx.
first_cluster is a real data cluster.
Postcondition
On success the forward waypoint sits at idx.
No volume state is modified.
Note
The waypoint guards are nested rather than joined – see the comment on them – so each is a single condition and MC/DC collapses to branch coverage.
Since
0.1.0

Definition at line 625 of file ra8_fs_fat_exfat_stream.c.

References k_cluster_first_data, k_ra8_err_invalid_state, k_ra8_ok, priv_fat_get(), and priv_is_eoc().

Referenced by internal_exfat_slice_at().

◆ internal_exfat_dir_append()

ra8_err_t internal_exfat_dir_append ( const ra8_fs_mount_t * m,
const exfat_dir_t * dir,
uint32_t alloc,
uint32_t tail,
uint8_t * nofat )
static

Allocate one cluster, zero it, link it to the run, seal the old tail.

The on-disk half of a directory grow, split out so priv_exfat_grow_dir stays inside the function-size gate. Picks a cluster (preferring the tail's successor to stay contiguous), ZEROES it before anything links it – a directory cluster reads as its own contents, so a half-built one must never be reachable – marks it used, links it (converting the run to a FAT chain if contiguity broke, which clears nofat), and retires the old tail cluster's trailing end-of-directory markers so the walk crosses into the fresh cluster (#677).

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory being grown (its cluster is the run head).
[in]allocCluster count of the run before this call.
[in]tailLast cluster of the run before this call.
[in,out]nofat1 when the run is contiguous; cleared if it converts.
Returns
Error code.
Return values
k_ra8_okThe run owns one more zeroed, reachable cluster.
k_ra8_err_no_memThe volume has no free cluster.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
m and dir are non-NULL; [dir->cluster, dir->cluster+alloc) is the run.
tail is the run's last cluster and alloc >= 1.
Postcondition
On success a walk of the run 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 533 of file ra8_fs_fat_exfat_stream.c.

References exfat_dir_t::cluster, internal_exfat_dir_link(), internal_exfat_pick_cluster(), k_ra8_ok, priv_alloc_hint_set(), priv_exfat_bitmap_lba(), priv_exfat_bitmap_mark(), priv_exfat_seal_cluster(), and priv_exfat_zero_cluster().

Referenced by priv_exfat_grow_dir().

◆ internal_exfat_dir_link()

ra8_err_t internal_exfat_dir_link ( const ra8_fs_mount_t * m,
uint32_t first,
uint32_t alloc,
uint32_t tail,
uint8_t * nofat,
uint32_t next )
static

Attach next to a directory's run, converting to a chain if needed.

The directory counterpart of ::priv_exfat_link_cluster, and the home of the NoFatChain-vs-chain decision growth turns on. Three outcomes: the run is contiguous and next continues it (nothing to write – the run stays NoFatChain), the run is contiguous and next does NOT continue it (materialise a real FAT chain over the run, then clear the flag), or the run is already a chain (link the tail on). In every case next is capped with an end-of-chain marker once it is reachable.

Parameters
[in]mMounted exFAT volume.
[in]firstFirst cluster of the directory's run.
[in]allocCluster count of the run (>= 1).
[in]tailLast cluster of the run.
[in,out]nofat1 on entry when the run is contiguous; cleared here when the run is converted to a FAT chain.
[in]nextThe newly allocated, already-zeroed cluster to attach.
Returns
Error code.
Return values
k_ra8_oknext is reachable from first.
k_ra8_err_*FAT write failure.
Precondition
m is non-NULL; [first, first + alloc) is the directory's run.
next is marked allocated in the bitmap already.
Postcondition
On success a walker reaches next from first.
*nofat is 0 whenever the run is no longer one contiguous span.
Note
The stay-contiguous test is one compound decision of two conditions; its vectors live with the tests that drive it, cited as libs/ra8_fs/src/ra8_fs_fat_exfat_stream.c@priv_exfat_dir_link.
Since
0.1.0

Definition at line 406 of file ra8_fs_fat_exfat_stream.c.

References internal_exfat_materialize_run(), k_ra8_ok, priv_eoc_write(), and priv_fat_set().

Referenced by internal_exfat_dir_append().

◆ internal_exfat_dir_relen()

ra8_err_t internal_exfat_dir_relen ( const ra8_fs_mount_t * m,
const exfat_dir_t * dir,
uint64_t new_bytes,
uint8_t nofat )
static

Rewrite a grown directory's own Stream entry to describe its new run.

Reads the directory's File-entry set back from its parent, patches the Stream entry's GeneralSecondaryFlags, ValidDataLength and DataLength – a directory's ValidDataLength equals its DataLength, and both equal the ALLOCATION – recomputes the SetChecksum over the patched bytes, and writes the set back. The set is guaranteed to sit in one cluster (priv_exfat_find_dir_space never splits one), so it is gathered by a straight cursor walk from the recorded head.

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory whose own entry set is rewritten.
[in]new_bytesThe run's new byte length (clusters * cluster bytes).
[in]nofat1 when the run is still contiguous, else 0.
Returns
Error code.
Return values
k_ra8_okThe Stream entry now describes the run.
k_ra8_err_not_supportedThe set is larger than this adapter rewrites.
k_ra8_err_*Backend read/write failure.
Precondition
m is non-NULL; dir->self_cluster holds this directory's File entry.
The set fits within dir->self_cluster.
Postcondition
On success DataLength and ValidDataLength equal new_bytes.
On success the flag byte carries NoFatChain exactly when nofat is 1.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

Definition at line 462 of file ra8_fs_fat_exfat_stream.c.

References k_exfat_entry_bytes, k_exfat_max_set_bytes, k_exfat_off_file_csum, k_exfat_off_file_secnt, k_exfat_off_strm_valid, k_exfat_secflag_alloc, k_exfat_secflag_poss, k_exfat_set_writable, k_exfat_strm_off_dlen, k_exfat_strm_off_flags, k_ra8_err_not_supported, k_ra8_ok, priv_exfat_next_entry(), priv_exfat_set_checksum(), priv_exfat_write_dir_set(), priv_wr16(), priv_wr64(), exfat_dir_t::self_cluster, and exfat_dir_t::self_index.

Referenced by priv_exfat_grow_dir().

◆ internal_exfat_dir_survey()

ra8_err_t internal_exfat_dir_survey ( const ra8_fs_mount_t * m,
const exfat_dir_t * dir,
uint32_t * out_alloc,
uint32_t * out_tail,
uint8_t * out_nofat )
static

Survey a directory's current allocation into run coordinates.

Fills first / alloc / tail / nofat the way an append survey fills a file handle, but from an exfat_dir_t: a contiguous run (contig_end != 0) is pure arithmetic, and a FAT-chained one (the root, or a run some earlier growth already converted) is walked to its tail. A directory always owns at least one cluster, so unlike the file survey there is no owns-nothing case to guard.

Parameters
[in]mMounted exFAT volume.
[in]dirDirectory whose allocation is surveyed.
[out]out_allocReceives the cluster count of the run.
[out]out_tailReceives the last cluster of the run.
[out]out_nofatReceives 1 when the run is contiguous, else 0.
Returns
Error code.
Return values
k_ra8_okThe coordinates are populated.
k_ra8_err_protocol_errorThe FAT chain points below the heap.
k_ra8_err_*FAT read failure.
Precondition
Every pointer is non-NULL; dir->cluster is a heap cluster.
m->type is exFAT.
Postcondition
On success out_tail is the run's last cluster and *out_alloc >= 1.
No volume state is modified.
Note
The chain walk is bounded by the volume's cluster count (P10 Rule 2).
Since
0.1.0

Definition at line 335 of file ra8_fs_fat_exfat_stream.c.

References exfat_dir_t::cluster, exfat_dir_t::contig_end, ra8_fs_mount_t::count_of_clusters, k_cluster_first_data, k_ra8_err_protocol_error, k_ra8_ok, priv_fat_get(), and priv_is_eoc().

Referenced by priv_exfat_grow_dir().

◆ internal_exfat_gather_set()

ra8_err_t internal_exfat_gather_set ( const ra8_fs_file_t * file,
uint8_t * set,
exfat_setpos_t * at_file,
exfat_setpos_t * at_stream )
static

Read a file's entry set back into set, recording where it lives.

Walks file->entry_set_count entries from the recorded head with the ordinary directory cursor, so a set that straddles a cluster boundary is gathered correctly rather than by flat arithmetic that would run off the end of the first cluster.

Parameters
[in]fileOpen exFAT handle carrying the set's coordinates.
[out]setReceives entry_set_count * 32 bytes.
[out]at_fileReceives the File entry's position.
[out]at_streamReceives the Stream entry's position.
Returns
Error code.
Return values
k_ra8_okThe set is in set and both positions are known.
k_ra8_err_*Backend read failure, or the directory ended mid-set.
Precondition
Every pointer is non-NULL and set holds k_exfat_max_set_bytes.
2 <= file->entry_set_count <= k_exfat_set_writable.
Postcondition
On success set mirrors the on-disk entries byte for byte.
No volume state is modified.
Note
Helper of priv_exfat_flush_set (complexity split).
Since
0.1.0

Definition at line 883 of file ra8_fs_fat_exfat_stream.c.

References exfat_cursor_t::cluster, exfat_cursor_t::entry_in_cluster, k_exfat_entry_bytes, k_ra8_ok, and priv_exfat_next_entry().

Referenced by priv_exfat_flush_set().

◆ internal_exfat_grow_one()

ra8_err_t internal_exfat_grow_one ( ra8_fs_file_t * file)
static

Add one cluster to a file's allocation.

Picks a cluster, marks it in the bitmap FIRST and only then links it. That order is deliberate: a failure between the two leaks a cluster, which a later fsck reclaims, whereas the reverse order would leave a file pointing at a cluster the volume still believes is free – and the next allocation would hand it to someone else.

Parameters
[in,out]fileFile to grow by one cluster.
Returns
Error code.
Return values
k_ra8_okThe file owns one more cluster.
k_ra8_err_no_memThe volume is full.
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 is one higher and tail_cluster names the new cluster.
On success the mount's next-free hint points past the new cluster.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

Definition at line 242 of file ra8_fs_fat_exfat_stream.c.

References internal_exfat_link_cluster(), internal_exfat_pick_cluster(), k_ra8_ok, priv_alloc_hint_set(), priv_exfat_bitmap_lba(), and priv_exfat_bitmap_mark().

Referenced by priv_exfat_ensure_clusters().

◆ internal_exfat_link_cluster()

ra8_err_t internal_exfat_link_cluster ( ra8_fs_file_t * file,
uint32_t next )
static

Attach a freshly allocated cluster to the file's allocation.

Three cases, and the middle one is the whole point of the format's NoFatChain bit: the file has no clusters yet (nothing to link), the new cluster continues the contiguous run (still nothing to link – writing FAT entries here would be writing bytes the spec says nobody may read), or contiguity is broken and the run has to become a real chain.

Parameters
[in,out]fileFile being grown; no_fat_chain may be cleared here.
[in]nextThe newly allocated cluster.
Returns
Error code.
Return values
k_ra8_okThe cluster is reachable from the file's head.
k_ra8_err_*FAT write failure.
Precondition
file is non-NULL and its mount is an exFAT volume.
next is marked allocated in the bitmap already.
Postcondition
On success a reader can reach next from first_cluster.
no_fat_chain is 0 whenever the allocation is not one contiguous run.
Note
Not thread-safe; callers serialise filesystem operations.
The stay-contiguous test is one compound decision of two conditions; its vectors live with the tests that drive it, cited as libs/ra8_fs/src/ra8_fs_fat_exfat_stream.c@priv_exfat_link_cluster.
Since
0.1.0

Definition at line 188 of file ra8_fs_fat_exfat_stream.c.

References internal_exfat_materialize_run(), k_ra8_ok, priv_eoc_write(), and priv_fat_set().

Referenced by internal_exfat_grow_one().

◆ internal_exfat_materialize_run()

ra8_err_t internal_exfat_materialize_run ( const ra8_fs_mount_t * m,
uint32_t first,
uint32_t alloc,
uint32_t tail,
uint32_t next )
static

Write a real FAT chain over a contiguous run, ending at next.

The reusable core of the NoFatChain conversion, on explicit run coordinates rather than a file handle, so both the file streaming path (::priv_exfat_link_cluster) and the directory growth path (::priv_exfat_dir_link, #677) drive the identical logic. Links every already-allocated cluster of the run to its successor – the run is contiguous, so a cluster's successor is the next integer – and the tail to next. The caller writes next's end-of-chain marker and clears the flag afterwards.

Parameters
[in]mMounted exFAT volume.
[in]firstFirst cluster of the contiguous run.
[in]allocCluster count of the run (>= 1).
[in]tailLast cluster of the run (first + alloc - 1).
[in]nextThe newly allocated cluster the tail must point at.
Returns
Error code.
Return values
k_ra8_okThe chain now spans the run and reaches next.
k_ra8_err_*FAT write failure; the transition is incomplete.
Precondition
m is non-NULL; the run [first, first + alloc) is contiguous.
tail is first + alloc - 1.
Postcondition
On success FAT[tail] is next and every earlier cluster points at its successor.
The allocation bitmap is untouched.
Note
O(alloc) FAT writes, paid once per run that fragments.
Since
0.1.0

Definition at line 141 of file ra8_fs_fat_exfat_stream.c.

References k_ra8_ok, and priv_fat_set().

Referenced by internal_exfat_dir_link(), and internal_exfat_link_cluster().

◆ internal_exfat_patch_stream()

void internal_exfat_patch_stream ( const ra8_fs_file_t * file,
uint8_t * strm )
static

Patch a Stream-extension entry to describe the handle's current state.

Writes the four fields a stream changes – GeneralSecondaryFlags, FirstCluster, ValidDataLength and DataLength – as the full 64-bit lengths the format defines, so a file past 4 GiB records its real size (#676). NoFatChain is asserted only for a file that actually owns clusters: exFAT spec sec 7.4.4 requires FirstCluster 0 on an empty file, and a flag claiming a contiguous run of nothing is a claim fsck checks.

Parameters
[in]fileFile whose state the entry must describe.
[in,out]strmThe 32-byte Stream-extension entry to patch in place.
Returns
Nothing.
Precondition
file and strm are non-NULL.
strm was read back from the volume as part of this file's set.
Postcondition
DataLength equals file->size_bytes and its high word is 0.
NoFatChain is set only when the allocation is one contiguous run.
Note
The caller recomputes the set's SetChecksum after this.
The two guards are nested rather than joined, because the inner one is meaningless without the outer: there is no contiguity to record about a file that owns nothing.
Since
0.1.0

Definition at line 840 of file ra8_fs_fat_exfat_stream.c.

References k_exfat_off_strm_valid, k_exfat_secflag_alloc, k_exfat_secflag_poss, k_exfat_strm_off_clus, k_exfat_strm_off_dlen, k_exfat_strm_off_flags, priv_wr32(), and priv_wr64().

Referenced by priv_exfat_flush_set().

◆ internal_exfat_pick_cluster()

ra8_err_t internal_exfat_pick_cluster ( const ra8_fs_mount_t * m,
uint64_t bmp_lba,
uint32_t prefer,
uint32_t * out )
static

Choose the next cluster to allocate, preferring the tail's successor.

Probes prefer first and falls back to the hinted bitmap scan. The probe is one sector read and it is what keeps a sequentially written file contiguous on a volume that has room for it.

Parameters
[in]mMounted exFAT volume.
[in]bmp_lbaFirst LBA (volume-relative) of the allocation bitmap.
[in]preferCluster to take if it is free; 0 asks for any.
[out]outReceives the chosen cluster number.
Returns
Error code.
Return values
k_ra8_okA free cluster was chosen.
k_ra8_err_no_memThe volume has no free cluster at all.
k_ra8_err_*Backend read failure.
Precondition
m and out are non-NULL; m->type is exFAT.
bmp_lba came from priv_exfat_bitmap_lba.
Postcondition
On success *out is a cluster whose bitmap bit currently reads 0.
No volume state is modified – the caller marks the bit.
Note
Not thread-safe; callers serialise filesystem operations.
The range guard on prefer is one compound decision of two conditions; its vectors live with the tests that drive it, cited as libs/ra8_fs/src/ra8_fs_fat_exfat_stream.c@priv_exfat_pick_cluster.
Since
0.1.0

Definition at line 88 of file ra8_fs_fat_exfat_stream.c.

References ra8_fs_mount_t::count_of_clusters, k_cluster_first_data, k_ra8_ok, priv_exfat_bitmap_scan(), and priv_exfat_bitmap_test().

Referenced by internal_exfat_dir_append(), and internal_exfat_grow_one().

◆ internal_exfat_slice_at()

ra8_err_t internal_exfat_slice_at ( ra8_fs_file_t * file,
uint64_t pos,
uint64_t * out_lba,
uint32_t * out_off,
uint32_t * out_room )
static

Locate byte position pos on the volume, allocating to reach it.

The single place both the data loop and the gap-filling loop turn a file offset into a sector plus an offset inside it. It grows the allocation first, so a caller never has to ask whether the cluster it is about to write exists.

Parameters
[in,out]fileOpen exFAT handle.
[in]posByte position within the file (64-bit, #676).
[out]out_lbaReceives the volume-relative sector holding pos.
[out]out_offReceives the byte offset of pos inside it.
[out]out_roomReceives the bytes from pos to the sector's end.
Returns
Error code.
Return values
k_ra8_okThe position is backed by an allocated cluster.
k_ra8_err_no_memThe volume had no cluster to grow into.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
Every pointer is non-NULL and the handle's mount is an exFAT volume.
The caller holds the library lock.
Postcondition
On success *out_room >= 1 and *out_off + *out_room is the sector size.
On success file->cur_cluster is the cluster holding pos.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

Definition at line 691 of file ra8_fs_fat_exfat_stream.c.

References internal_exfat_cluster_at(), k_ra8_ok, priv_bps(), priv_cluster_bytes(), priv_cluster_to_lba(), and priv_exfat_ensure_clusters().

Referenced by internal_exfat_close_gap(), and priv_exfat_write_stream().

◆ 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_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_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_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().