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

Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write). More...

#include <stdint.h>
#include "ra8_err.h"
#include "ra8_fs_seams.h"
#include "ra8_fs_types.h"
Include dependency graph for ra8_fs.h:

Go to the source code of this file.

Functions

ra8_err_t ra8_fs_format (const ra8_fs_backend_t *backend, const ra8_fs_format_opts_t *opts)
 Format a block device as a fresh, empty FAT12/FAT16/FAT32/exFAT volume.
ra8_err_t ra8_fs_probe (const ra8_fs_backend_t *backend, ra8_fs_type_t *out_type)
 Identify a FAT/exFAT volume without claiming a mount slot.
ra8_err_t ra8_fs_mount (const ra8_fs_backend_t *backend, ra8_fs_mount_t **out_handle)
 Mount a FAT volume from a block-device backend, auto-selecting the first partition.
ra8_err_t ra8_fs_mount_partition (const ra8_fs_backend_t *backend, uint8_t index, ra8_fs_mount_t **out_handle)
 Mount a FAT/exFAT volume from a specific partition of a block device.
ra8_err_t ra8_fs_unmount (ra8_fs_mount_t *handle)
 Unmount a previously mounted volume and release its slot.
ra8_err_t ra8_fs_open (ra8_fs_mount_t *handle, const char *path, ra8_fs_mode_t mode, ra8_fs_file_t **out_file)
 Open or create a file by path, 8.3 or long.
ra8_err_t ra8_fs_close (ra8_fs_file_t *file)
 Close an open file, stamping its final modification time.
ra8_err_t ra8_fs_read (ra8_fs_file_t *file, uint8_t *buf, uint32_t max_len, uint32_t *got_len)
 Read up to max_len bytes; advance the cluster chain on cluster crossings.
ra8_err_t ra8_fs_write (ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
 Write len bytes; allocate new clusters from FAT free-space scan as needed.
ra8_err_t ra8_fs_write_file (ra8_fs_mount_t *handle, const char *path, const uint8_t *data, uint32_t len)
 Create a whole file in one call (provisioning helper).
ra8_err_t ra8_fs_seek (ra8_fs_file_t *file, uint64_t offset_bytes)
 Move the file offset to offset_bytes (clamped to size).
ra8_err_t ra8_fs_tell (const ra8_fs_file_t *file, uint64_t *out_offset)
 Report the current offset (64-bit; see ra8_fs_seek).
ra8_err_t ra8_fs_size (const ra8_fs_file_t *file, uint64_t *out_bytes)
 Report the file's size in bytes (64-bit on exFAT, #676).
ra8_err_t ra8_fs_stat (const ra8_fs_mount_t *handle, const char *path, ra8_fs_stat_t *out)
 Report what a path names – file or directory – without opening it.
ra8_err_t ra8_fs_listdir (const ra8_fs_mount_t *handle, const char *path, ra8_fs_listdir_cb_t cb, void *ctx)
 Enumerate directory entries; invoke cb once per visible entry.
ra8_err_t ra8_fs_dir_open (ra8_fs_mount_t *handle, const char *path, ra8_fs_dir_t *directory)
 Open a caller-owned directory cursor without holding the filesystem lock.
ra8_err_t ra8_fs_dir_next (ra8_fs_dir_t *directory, ra8_fs_dirent_t *out, bool *out_entry)
 Copy the next visible directory entry.
ra8_err_t ra8_fs_dir_close (ra8_fs_dir_t *directory)
 Consume one open directory cursor.
ra8_err_t ra8_fs_unlink (const ra8_fs_mount_t *handle, const char *path)
 Delete a file: mark its dir entry deleted and free its clusters.
ra8_err_t ra8_fs_rename (const ra8_fs_mount_t *handle, const char *old_path, const char *new_path)
 Rename a file within its own directory.
ra8_err_t ra8_fs_mkdir (const ra8_fs_mount_t *handle, const char *path)
 Create a directory at path.
ra8_err_t ra8_fs_rmdir (const ra8_fs_mount_t *handle, const char *path)
 Remove an empty directory at path.

Detailed Description

Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).

ra8_fs is a self-contained, pure-C FAT filesystem implementation that sits on top of an arbitrary block-device backend (ra8_fs_backend_t), with no RTOS and no vendor-SDK dependencies. It is the platform's only filesystem: the vendored FileX it once coexisted with was retired by #611.

Two backends are intended for production use:

  1. ra8_sdhi – on-board SD/MMC card (sweep 1).
  2. ra8_usb_pmsc storage backend – mass-storage gadget loopback (sweep 2).

Any object that supplies read_block(), write_block(), and get_capacity() works – including the in-memory mock used in tests/storage/src/test_ra8_fs.c.

What this implements

  • BPB (BIOS Parameter Block) parse + FAT type auto-detection per Microsoft "FAT: General Overview of On-Disk Format" v1.03 (the formal count_of_clusters rule).
  • FAT12 / FAT16 / FAT32 cluster-chain walking (read AND write).
  • 8.3 short filename directory parsing in the FAT12/16 fixed root directory and in any FAT32 cluster-chain root.
  • VFAT long file names, read AND write: a name that is not 8.3-representable is stored as a chain of attr-0x0F entries behind a generated LONGNA~1.TXT alias, and unlink / rmdir / rename take the chain away with the entry it belongs to. A name that differs from an 8.3 one only in case costs no chain at all – it travels in the two DIR_NTRes flags, so data.log reads back as data.log.
  • Linear file read across cluster boundaries.
  • File create + write, growing the chain by allocating from the FAT free-cluster scan when the current cluster fills. The scan starts at a per-mount next-free hint and reads the FAT through a one-sector cache, so appending to a file costs a bounded number of block reads per cluster instead of rescanning the whole FAT.
  • exFAT streaming write: open / create / append / truncate through the same ra8_fs_open() seam, growing out of the allocation bitmap one cluster at a time. The entry set keeps NoFatChain while the run stays contiguous and materialises a real FAT chain the moment it cannot, so a fragmented volume is written rather than refused, and ValidDataLength is tracked apart from DataLength so bytes past the written prefix read as zero the way the format requires.
  • FAT32 FSInfo: validated at mount (both signatures plus the trailing signature), used to seed the free count and the next-free hint, and written back when a file is closed or the volume is unmounted.
  • Create / modify / access timestamps on FAT and exFAT. With no clock installed every stamp is the legal FAT epoch (1980-01-01 00:00:00); install one with ra8_fs_set_clock() to record real time.
  • Seek / tell / size.
  • Listdir via callback.
  • Unlink (mark dir entry 0xE5, free chain) and rmdir of an empty directory. Both refuse the wrong kind of entry: unlink will not take a directory and rmdir will not take a file.
  • Directories on exFAT too, not only on FAT: mkdir, rmdir, nested path resolution and per-directory listing. An exFAT directory is the same File + Stream + Name entry set a file gets, with the Directory attribute and a zeroed cluster behind it, and no "." / ".." entries – exFAT has none.
  • GROWING a directory. A full directory takes another cluster out of the allocation bitmap, so its ceiling is free space, not one cluster: FAT subdirectories and the FAT32 root grow their chains, and an exFAT directory grows as its files do – contiguous, then a FAT chain (#677).

Names are UTF-8, on both formats

Every name crossing this API is UTF-8; both on-disk formats store UTF-16LE, converted in one place so the three paths that did their own cannot disagree again (#606).

  • All of Unicode is storable: a 4-byte UTF-8 character becomes a surrogate PAIR on disk and returns as the same 4 bytes.
  • Malformed UTF-8 is REFUSED, never patched: an over-long encoding, a directly-encoded surrogate or a truncated sequence is invalid_arg.
  • Lookup is case-insensitive across the BMP, through the canonical up-case table this library writes at format time.
  • exFAT's stored NameHash uses that same table, which is what the format defines it over. A volume carrying a DIFFERENT table refuses non-ASCII names with k_ra8_err_not_supported instead; ASCII is unaffected.
  • Length limits are in UTF-16 UNITS: 247 on FAT, 64 on exFAT; a UTF-8 argument may be three times as many bytes. An 8.3 short name stays ASCII, and a long name's alias maps what it cannot represent to _, as VFAT.

What this deliberately skips

  • exFAT names past 64 UTF-16 units (the format allows 255), and locale-sensitive folding (Turkish dotless i, full case folding).
  • Extended / logical MBR partition chains: the four MBR primary entries and the GPT entry array are addressable by index through ra8_fs_mount_partition() (with ra8_fs_mount() auto-selecting the first, and a superfloppy BPB at LBA 0 still supported transparently), but an extended partition's logical chain is not walked.

Limits (compile-time):

  • 4 concurrent open file handles (k_ra8_fs_max_files).
  • 2 concurrent mount points (k_ra8_fs_max_mounts).
  • File size: 64-bit on exFAT (#676) – a file past 4 GiB is exactly what exFAT exists for. FAT12/16/32 files stay capped at 4 GiB - 1 (DIR_FileSize is 32-bit; the format's own ceiling), enforced with k_ra8_err_invalid_size at the FAT boundary.
  • Sector size: 512 / 1024 / 2048 / 4096 bytes, taken from the backend's reported block size and cross-checked against the BPB / VBR (#683).
  • Media size: 64-bit LBAs end to end – backend interface, partition base, GPT entries – so volumes past 2 TiB are addressable (#683).

The 4Kn-sector and beyond-2-TiB paths are SIMULATION-VERIFIED ONLY (host tests over fake backends); no such medium has been on the bench. 512-byte sub-2-TiB media remain the hardware-proven configuration.

Concurrency

The library owns three pieces of shared mutable state – the file-handle table, the mount table and one static scratch sector – so every public entry point is serialised against every other one, not merely against itself. With no lock installed (the default) that serialisation is the caller's job and the library behaves exactly as it always has: bare metal pays nothing, not even a branch worth measuring.

An RTOS-world caller installs a lock instead of duplicating that discipline at every call site – see ra8_fs_lock_t and ra8_fs_set_lock(). The lock is taken at the public boundary and released on every return path, including error returns; no internal helper takes it, so there is no lock-ordering graph and no recursion. It serialises the library: it does not make one open ra8_fs_file_t safe to drive from two threads at once.

Definition in file ra8_fs.h.

Function Documentation

◆ ra8_fs_close()

ra8_err_t ra8_fs_close ( ra8_fs_file_t * file)
nodiscard

Close an open file, stamping its final modification time.

File contents are never buffered – ra8_fs_write() has already put every byte on the volume – so what closing does is metadata. A handle that was written through gets DIR_WrtTime / DIR_WrtDate set to the moment of the close (the time a host shows as "modified" and a sync tool compares), and the volume's FAT32 FSInfo free count is committed. A handle opened read-only, or write-opened and never written, touches nothing.

Parameters
[in,out]fileHandle from ra8_fs_open().
Return values
k_ra8_okClosed.
k_ra8_err_null_ptrfile is NULL.
k_ra8_err_*The timestamp or FSInfo write failed. The handle is released anyway – a close that could be refused would leak the slot.
Postcondition
The file slot is free for reuse whatever the metadata write did.
Since
0.1.0

Definition at line 578 of file ra8_fs_fat_file.c.

References priv_close_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_close().

Referenced by demo_read_test_file(), demo_read_verify(), eoh_provision_or_halt(), etoc_provision_one(), fileops_expect_absent(), fileops_read_back(), fs_fmt_create_and_verify(), fs_fmt_rename_and_verify(), imp_read_cache(), internal_close(), internal_compute_source_key(), internal_demo_read_once(), internal_file_exists(), internal_mkfontimg_verify(), internal_mkfontimg_write(), internal_native_close(), internal_pc_font_or_halt(), internal_pc_read_cache(), internal_ra8_io_roundtrip_read_verify(), internal_ra8_io_roundtrip_write_subdir(), internal_ra8_vfs_compress_load_blob(), internal_ra8_vfs_compress_store_blob(), internal_read_font(), internal_read_stamp(), internal_read_whole_file(), internal_stream_book(), internal_stream_open(), internal_swap_vfs_read_verify(), internal_swap_vfs_write(), internal_verify_book(), internal_verify_book_reopen(), ra8_fs_close(), ra8_io_roundtrip_read_verify(), ra8_io_roundtrip_subdir_file(), rabook_import_compile_adapter(), sd_demo_read_payload(), sd_demo_write_payload(), sdhi_demo_read_and_verify(), sdhi_demo_write_payload(), and sh_sd_book_close().

◆ ra8_fs_dir_close()

ra8_err_t ra8_fs_dir_close ( ra8_fs_dir_t * directory)
nodiscard

Consume one open directory cursor.

Parameters
[in,out]directoryOpen caller-owned cursor.
Returns
Lifecycle status.
Postcondition
Success clears all cursor state; no backend resource remains owned.
Since
0.1.0

Definition at line 786 of file ra8_fs_fat_dir.c.

References ra8_fs_dir_t::is_open, k_ra8_err_invalid_state, k_ra8_err_null_ptr, and k_ra8_ok.

Referenced by internal_native_dir_close(), and internal_native_listdir().

◆ ra8_fs_dir_next()

ra8_err_t ra8_fs_dir_next ( ra8_fs_dir_t * directory,
ra8_fs_dirent_t * out,
bool * out_entry )
nodiscard

Copy the next visible directory entry.

Parameters
[in,out]directoryOpen caller-owned cursor.
[out]outStable entry value owned by the caller.
[out]out_entryTrue when out is populated; false at clean EOF.
Returns
Media, corruption, or lifecycle status.
Precondition
Required pointers are non-NULL and no forbidden directory mutation occurred.
Postcondition
No filesystem lock remains held; out survives later cursor calls.
Since
0.1.0

Definition at line 748 of file ra8_fs_fat_dir.c.

References internal_fat_dir_next(), k_ra8_err_invalid_state, k_ra8_err_null_ptr, k_ra8_fs_type_exfat, k_ra8_ok, priv_exfat_dir_next(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_dir_next().

Referenced by internal_native_dir_next(), internal_native_listdir(), and ra8_fs_dir_next().

◆ ra8_fs_dir_open()

ra8_err_t ra8_fs_dir_open ( ra8_fs_mount_t * handle,
const char * path,
ra8_fs_dir_t * directory )
nodiscard

Open a caller-owned directory cursor without holding the filesystem lock.

Path resolution is locked only for this call. Each later next reacquires and releases the lock, so client metadata and stream operations are valid between entries. Two cursor objects advance independently. The enumerated directory and its ancestors must not be mutated until close; mutation elsewhere is permitted.

Parameters
[in,out]handleMounted filesystem.
[in]pathDirectory path.
[out]directoryIdle caller-owned cursor.
Returns
Path, mount, or lifecycle status.
Return values
k_ra8_okCursor opened.
k_ra8_err_busydirectory is already open.
Precondition
Required pointers are non-NULL and handle is mounted.
Postcondition
Success leaves no filesystem lock held.
Since
0.1.0

Definition at line 720 of file ra8_fs_fat_dir.c.

References internal_dir_open_locked(), k_ra8_err_null_ptr, k_ra8_ok, memset(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_dir_open().

Referenced by internal_native_dir_open(), internal_native_listdir(), and ra8_fs_dir_open().

◆ ra8_fs_format()

ra8_err_t ra8_fs_format ( const ra8_fs_backend_t * backend,
const ra8_fs_format_opts_t * opts )
nodiscard

Format a block device as a fresh, empty FAT12/FAT16/FAT32/exFAT volume.

Lays down a complete volume so the very next ra8_fs_mount() on the same backend detects exactly opts->type. The FAT variants are written as a superfloppy (no MBR) at LBA 0; exFAT is written into an MBR partition (see below). For the FAT variants this is a classic BPB layout:

  • a full BPB (jump prologue, OEM name, BytsPerSec, SecPerClus, RsvdSecCnt, NumFATs, RootEntCnt or RootClus, TotSec, media descriptor, FATSz, volume label, filesystem-type string, and the 0x55 0xAA boot signature);
  • the reserved FAT[0] media descriptor and FAT[1] end-of-chain marker in every FAT copy (plus the root-cluster EOC and an FSInfo sector for FAT32);
  • a zeroed root directory (FAT12/16 fixed root, or the FAT32 root cluster).

The cluster size is chosen (when opts->sectors_per_cluster == 0) so the resulting count_of_clusters lands in the band the requested type requires per Microsoft "FAT: General Overview of On-Disk Format" sec 3.5 (FAT12 < 4085, 4085 <= FAT16 < 65525, FAT32 >= 65525). If the backend's capacity cannot satisfy opts->type – too small for FAT16/FAT32, or too large for FAT12/FAT16 even at the maximum cluster size – the call fails without writing anything.

exFAT (k_ra8_fs_type_exfat) is written the way a PC writes it, so a card formatted here mounts on a desktop: a DOS/MBR partition table at LBA 0 with one type-0x07 partition aligned at 1 MiB, and the volume itself inside that partition rather than at LBA 0. ra8_fs_mount() follows the partition table back (ra8_fs_mount_t::partition_base_lba records where it landed), and also mounts a card partitioned by a desktop. Because the partition cannot start at sector 0, the device must be big enough for the 1 MiB alignment gap ON TOP OF the 32 MiB minimum volume – a 32 MiB card is no longer formattable.

Inside the partition exFAT lays down its own on-disk structures instead of a FAT BPB: the 12-sector boot region (Main + Backup) with the VBR checksum sector, the single FAT, the allocation-bitmap cluster(s), the canonical compressed up-case table (which may span several clusters), and a root-directory cluster carrying the volume-label, allocation-bitmap, and up-case-table system directory entries. The image is fsck.exfat-clean and round-trips through ra8_fs_mount() plus the whole exFAT file API: ra8_fs_open() in every mode, ra8_fs_read(), ra8_fs_write(), ra8_fs_write_file(), ra8_fs_rename(), and ra8_fs_unlink().

Parameters
[in]backendBlock-device implementation (read/write/get_capacity all non-NULL). Its write_block is driven during the format.
[in]optsFormat options. type selects the variant; label is an optional 0..11 char volume label; sectors_per_cluster pins the cluster size (0 = auto-select).
Returns
ra8_err_t
Return values
k_ra8_okVolume formatted; ready to mount.
k_ra8_err_null_ptrbackend or opts is NULL.
k_ra8_err_invalid_argBackend has NULL callbacks, the reported block size is not a power of two in 512..4096, or sectors_per_cluster is non-zero and not a power of two in 1..128.
k_ra8_err_not_supportedopts->type is unknown / not a writable filesystem, or the device is too small to hold a partitioned exFAT volume (the 1 MiB alignment gap plus the 32 MiB minimum).
k_ra8_err_invalid_sizeCapacity cannot satisfy opts->type.
k_ra8_err_*Backend read/write failure (volume may be partially written on a mid-format I/O error).
Precondition
backend->write_block and backend->get_capacity are non-NULL.
No volume from this backend is currently mounted (the caller has unmounted it first; formatting under a live mount corrupts cached geometry).
Postcondition
On success the backend's first sectors hold a valid opts->type BPB, FAT, and an empty root directory.
On k_ra8_err_invalid_size / argument errors, the backend is untouched.
Note
Not thread-safe unless a lock is installed (see ra8_fs_set_lock()).
Warning
Destroys all data on the device.
Example:
opts.label = "SCRATCH";
if (ra8_fs_format(&backend, &opts) == k_ra8_ok) {
ra8_fs_mount_t* mnt = nullptr;
(void)ra8_fs_mount(&backend, &mnt); // mnt->type == k_ra8_fs_type_fat16
}
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_t ra8_fs_format(const ra8_fs_backend_t *backend, const ra8_fs_format_opts_t *opts)
Format a block device as a fresh, empty FAT12/FAT16/FAT32/exFAT volume.
ra8_err_t ra8_fs_mount(const ra8_fs_backend_t *backend, ra8_fs_mount_t **out_handle)
Mount a FAT volume from a block-device backend, auto-selecting the first partition.
@ k_ra8_fs_type_fat16
4085 <= count_of_clusters < 65525.
Tunables for ra8_fs_format() (the on-disk geometry of a fresh volume).
ra8_fs_type_t type
FAT variant to lay down (12/16/32).
const char * label
0..11 char volume label, or NULL.
Cached parse of one mounted FAT volume.
See also
ra8_fs_mount() Detects the type this routine wrote.
Since
0.1.0

Definition at line 744 of file ra8_fs_fat_mount.c.

References internal_format_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_format().

Referenced by demo_fs_format_mount(), demo_fs_format_or_panic(), eoh_mount_or_halt(), etoc_mount_or_halt(), fs_fmt_run_exfat(), fs_fmt_run_one_type(), internal_demo_mount(), internal_demo_mount(), internal_demo_probe_fat(), internal_format_mount(), internal_pc_mount_or_halt(), internal_ra8_io_roundtrip_format_mount(), internal_swap_run_one(), main(), ra8_fs_format(), ra8_io_roundtrip_mount(), and sdhi_demo_mount_via_io().

◆ ra8_fs_listdir()

ra8_err_t ra8_fs_listdir ( const ra8_fs_mount_t * handle,
const char * path,
ra8_fs_listdir_cb_t cb,
void * ctx )
nodiscard

Enumerate directory entries; invoke cb once per visible entry.

Every supported filesystem enumerates any directory by path ("/" for the root, "/books" for a subdirectory) – exFAT included since #605. FAT's synthetic "." and ".." entries are not reported; exFAT has none to report. A subdirectory appears in its parent's listing like any other entry, with the directory bit set in the attr argument.

Parameters
[in]handleMount handle.
[in]pathDirectory path ("/" or a nested path).
[in]cbCallback (must be non-NULL).
[in]ctxCookie forwarded to the callback.
Return values
k_ra8_okEnumeration complete.
k_ra8_err_null_ptrhandle/cb NULL.
k_ra8_err_not_foundA path component does not exist.
k_ra8_err_invalid_argA path component names a file.
Since
0.1.0

Definition at line 711 of file ra8_fs_fat_dir.c.

References internal_listdir_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_listdir().

Referenced by demo_file_ops(), fileops_step_listdir(), fs_fmt_assert_empty_root(), ra8_fs_listdir(), and sh_sd_scan().

◆ ra8_fs_mkdir()

ra8_err_t ra8_fs_mkdir ( const ra8_fs_mount_t * handle,
const char * path )
nodiscard

Create a directory at path.

Resolves all-but-the-last path component to an existing parent directory, then creates the final component as a new, empty subdirectory. Nested paths are supported ("/books/scifi"), provided each intermediate component already exists. A partial allocation is rolled back on failure, so the volume is never leaked.

On FAT the new directory is stamped with its "." and ".." links, and a leaf that is not 8.3-representable is stored as a long name, exactly as ra8_fs_open() stores one. On exFAT the new directory is one zeroed cluster – entry type 0x00 is end-of-directory, so that IS an empty directory – and there are no dot entries at all, because exFAT has none.

An existing name is REFUSED with k_ra8_err_exists, whether it is a file or a directory. mkdir is not a create-or-replace verb; ra8_fs_write_file() is the one that replaces.

Parameters
[in]handleMount handle.
[in]pathNUL-terminated directory path to create.
Returns
ra8_err_t Error code.
Return values
k_ra8_okDirectory created.
k_ra8_err_null_ptrhandle or path was NULL.
k_ra8_err_invalid_stateMount is not in use.
k_ra8_err_invalid_argThe leaf is empty, longer than 247 characters, or holds a character no FAT name may carry.
k_ra8_err_existsThe name already exists in the parent.
k_ra8_err_not_foundAn intermediate component does not exist.
k_ra8_err_no_memParent directory or volume is full.
Precondition
handle and path are non-NULL; the parent path exists.
Mount is in use.
Postcondition
On success an empty directory exists at path.
On failure the volume is unchanged.
Note
Not thread-safe unless a lock is installed (see ra8_fs_set_lock()).
Since
0.1.0

Definition at line 536 of file ra8_fs_fat_dirmk.c.

References internal_mkdir_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_mkdir().

Referenced by internal_native_mkdir(), internal_populate(), and ra8_fs_mkdir().

◆ ra8_fs_mount()

ra8_err_t ra8_fs_mount ( const ra8_fs_backend_t * backend,
ra8_fs_mount_t ** out_handle )
nodiscard

Mount a FAT volume from a block-device backend, auto-selecting the first partition.

Reads sector 0 and, if it is not itself a boot sector, follows the partition table to the first volume: MBR partition 0, or – on a GPT disk (protective MBR type 0xEE) – the first Microsoft Basic Data entry. It then validates the BPB signature and bytes-per-sector, computes the cluster count, dispatches to FAT12 / FAT16 / FAT32, and caches the layout in *out_handle. Exactly equivalent to ra8_fs_mount_partition() with k_ra8_fs_partition_auto; use that sibling to reach any other partition.

Parameters
[in]backendBlock-device implementation. Must remain alive for the lifetime of the mount.
[out]out_handlePopulated mount handle on success.
Returns
ra8_err_t
Return values
k_ra8_okVolume mounted successfully.
k_ra8_err_null_ptrbackend or out_handle is NULL.
k_ra8_err_invalid_argBackend has NULL function pointers.
k_ra8_err_no_memNo free mount slot.
k_ra8_err_validation_failedBPB signature invalid / unsupported.
Precondition
backend->read_block is non-NULL.
out_handle is non-NULL.
Postcondition
On success, out_handle->in_use == 1 and type != unknown.
On failure, *out_handle is left untouched.
Note
Not thread-safe unless a lock is installed (see ra8_fs_set_lock()).
See also
ra8_fs_mount_partition() Mount a partition chosen by index.
Since
0.1.0

Definition at line 762 of file ra8_fs_fat_mount.c.

References internal_mount_locked(), k_ra8_fs_partition_auto, priv_lock_acquire(), priv_lock_release(), and ra8_fs_mount().

Referenced by demo_fs_format_mount(), demo_fs_format_or_panic(), eoh_mount_or_halt(), etoc_mount_or_halt(), fileops_mount_volume(), fs_fmt_run_exfat(), fs_fmt_run_one_type(), imp_mount_or_halt(), internal_demo_mount(), internal_demo_mount(), internal_format_mount(), internal_mount(), internal_mount_image(), internal_mount_sd(), internal_native_mount(), internal_pc_mount_or_halt(), internal_ra8_io_roundtrip_format_mount(), internal_swap_run_one(), main(), ra8_fs_mount(), ra8_io_roundtrip_mount(), sd_demo_mount_or_halt(), sdhi_demo_mount_via_io(), selftest_mount_volume(), selftest_mount_volume(), selftest_mount_volume(), selftest_mount_volume(), selftest_mount_volume(), and sh_sd_mount().

◆ ra8_fs_mount_partition()

ra8_err_t ra8_fs_mount_partition ( const ra8_fs_backend_t * backend,
uint8_t index,
ra8_fs_mount_t ** out_handle )
nodiscard

Mount a FAT/exFAT volume from a specific partition of a block device.

Generalises ra8_fs_mount() with an explicit partition selector so a multi-partition card or disk exposes every volume, not just the first. Sector 0 decides the table type: a GPT disk (protective MBR type 0xEE) selects entry index of the GPT partition-entry array; any other 0xAA55 MBR selects primary entry index (0-3). The chosen partition's first LBA becomes out_handle->partition_base_lba and every subsequent access is partition-relative. Passing k_ra8_fs_partition_auto reproduces ra8_fs_mount() exactly, including transparent superfloppy (LBA 0) mounts; an explicit index requires a partition table, so a superfloppy with no table returns k_ra8_err_not_found.

Parameters
[in]backendBlock-device implementation. Must remain alive for the lifetime of the mount.
[in]indexZero-based partition index, or k_ra8_fs_partition_auto.
[out]out_handlePopulated mount handle on success.
Returns
ra8_err_t
Return values
k_ra8_okVolume mounted successfully.
k_ra8_err_null_ptrbackend or out_handle is NULL.
k_ra8_err_invalid_argBackend has NULL function pointers.
k_ra8_err_no_memNo free mount slot.
k_ra8_err_out_of_rangeindex is past the partition table.
k_ra8_err_not_foundThe selected entry is empty, or there is no partition table to index.
k_ra8_err_not_supportedGPT entry-array geometry this backend cannot address (non-128-byte entries, or a first LBA above 32 bits).
k_ra8_err_validation_failedBPB signature invalid / unsupported.
Precondition
backend->read_block is non-NULL.
out_handle is non-NULL.
Postcondition
On success, out_handle->in_use == 1 and type != unknown.
On failure, *out_handle is left untouched.
Note
Not thread-safe unless a lock is installed (see ra8_fs_set_lock()).
See also
ra8_fs_mount() Auto-select the first partition.
Since
0.1.0

Definition at line 773 of file ra8_fs_fat_mount.c.

References internal_mount_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_mount_partition().

Referenced by ra8_fs_mount_partition().

◆ ra8_fs_open()

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

Open or create a file by path, 8.3 or long.

A path that resolves to a DIRECTORY is rejected in every mode. In write mode that rejection is load-bearing: the truncate step would free the cluster chain holding the directory's contents and leave every file inside it unreachable. In read mode it prevents handing back the zero-byte handle a directory's DIR_FileSize of 0 would otherwise describe.

All three modes work on every filesystem this adapter mounts, exFAT included (#602). An exFAT write grows the file one cluster at a time out of the allocation bitmap, so the size a caller can create is bounded by the volume's free space rather than by RAM, and a fragmented volume is written the way the format intends: the entry set keeps NoFatChain while the run stays contiguous and drops it – materialising a real FAT chain over the clusters already allocated – the first time the next cluster is not the successor of the last.

Parameters
[in]handleMount handle.
[in]pathPath from the volume root, e.g. "HELLO.TXT" or "/Reading List/Chapter One.txt". Any component may be a long name of up to 247 characters.
[in]modek_ra8_fs_mode_read, _write, or _append.
[out]out_filePopulated file handle on success.
Return values
k_ra8_okFile opened.
k_ra8_err_null_ptrAny pointer arg is NULL.
k_ra8_err_invalid_argpath names a directory (any mode), or a new name is empty, holds a character no name may carry, or is longer than the volume allows – 247 characters on FAT, 64 on exFAT.
k_ra8_err_not_foundRead mode and path doesn't exist.
k_ra8_err_access_deniedA writing mode (_write / _append) on a file whose read-only attribute is set; a read open of the same file succeeds.
k_ra8_err_no_memNo free file slot, no directory slot, or the volume has no free cluster.
k_ra8_err_no_dataWrite mode failed to allocate cluster.
k_ra8_err_not_supportedAn exFAT entry set larger than this adapter rewrites (a name over 64 chars written by another implementation).
Precondition
handle->in_use == 1.
Postcondition
On success, out_file->in_use == 1 and offset is at file start (read/write) or end (append).
On k_ra8_err_invalid_arg the volume is unchanged.
Note
Write mode TRUNCATES in place on both filesystems: the name keeps its directory entry (and its creation stamp) and only the contents go.
The read-only attribute is honored HERE, at open time (like POSIX/DOS): a handle already opened for writing keeps writing even if the file is marked read-only afterwards via ra8_fs_set_attr(). A content write also sets the archive attribute.
See also
ra8_fs_rmdir() The verb for removing a directory.
ra8_fs_set_attr() Sets / clears the read-only attribute this honors.
Since
0.1.0

Definition at line 569 of file ra8_fs_fat_file.c.

References priv_lock_acquire(), priv_lock_release(), priv_open_locked(), and ra8_fs_open().

Referenced by demo_read_test_file(), demo_read_verify(), eoh_provision_or_halt(), etoc_provision_one(), fileops_expect_absent(), fileops_read_back(), fs_fmt_create_and_verify(), fs_fmt_rename_and_verify(), imp_read_cache(), internal_compute_source_key(), internal_file_exists(), internal_mkfontimg_verify(), internal_mkfontimg_write(), internal_native_open(), internal_open_or_provision(), internal_pc_font_or_halt(), internal_pc_read_cache(), internal_read_stamp(), internal_read_whole_file(), internal_stream_book(), internal_stream_open(), internal_verify_book_reopen(), ra8_fs_open(), sd_demo_read_payload(), sd_demo_write_payload(), and sh_sd_book_open().

◆ ra8_fs_probe()

ra8_err_t ra8_fs_probe ( const ra8_fs_backend_t * backend,
ra8_fs_type_t * out_type )
nodiscard

Identify a FAT/exFAT volume without claiming a mount slot.

Runs the same superfloppy/MBR/GPT discovery and BPB/VBR validation as ra8_fs_mount(), but parses into temporary caller-invisible state and returns only the detected type. This is the authoritative probe used by the pluggable format registry: partitioned exFAT is not mistaken for an unknown format merely because LBA 0 is an MBR.

Parameters
[in]backendReadable block backend.
[out]out_typeDetected FAT12/16/32 or exFAT type.
Return values
k_ra8_okA supported volume was validated.
k_ra8_err_null_ptrEither argument was NULL.
k_ra8_err_invalid_argRequired backend operations/capacity are invalid.
k_ra8_err_validation_failedNo supported volume is present.
k_ra8_err_*Backend or partition-parse failure.
Precondition
No concurrent filesystem call unless a lock is installed.
Postcondition
No mount/file slot is consumed and the medium is not modified.
On failure out_type is untouched.
Since
0.1.0

Definition at line 753 of file ra8_fs_fat_mount.c.

References internal_probe_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_probe().

Referenced by internal_exfat_probe(), internal_fat_probe(), and ra8_fs_probe().

◆ ra8_fs_read()

ra8_err_t ra8_fs_read ( ra8_fs_file_t * file,
uint8_t * buf,
uint32_t max_len,
uint32_t * got_len )
nodiscard

◆ ra8_fs_rename()

ra8_err_t ra8_fs_rename ( const ra8_fs_mount_t * handle,
const char * old_path,
const char * new_path )
nodiscard

Rename a file within its own directory.

FAT12/16/32: re-files the entry under the new name – writing the new entry (with its long-name chain, if the new name needs one) before taking the old one away, so a failure between the two leaves the file reachable under both names rather than under neither. The cluster, the size and the attributes are carried across unchanged. Legacy note: this used to rewrite the 11-byte packed 8.3 name in the existing directory entry. exFAT: patches the Stream entry's NameLength and NameHash, rebuilds the Name entry, and recomputes the SetChecksum – supported when both names fit one Name entry (<= 15 characters), which keeps the entry-set length unchanged. Data clusters never move.

The LAST-ACCESS stamp advances; the modification stamp deliberately does not. A rename changes the name, not the bytes, so moving the modification time would tell every rsync, backup and "pick the newest image" heuristic that the contents changed. Neither format has a metadata-change field, so the access stamp is the honest record that the entry was touched.

Parameters
[in]handleMount handle.
[in]old_pathExisting path.
[in]new_pathReplacement path, in the same directory (must not already exist).
Return values
k_ra8_okFile renamed.
k_ra8_err_null_ptrAny pointer arg is NULL.
k_ra8_err_not_foundold_path does not exist.
k_ra8_err_access_deniedold_path's read-only attribute is set.
k_ra8_err_existsnew_path already resolves.
k_ra8_err_not_supportedexFAT name longer than 15 characters, or the two paths are in different directories.
k_ra8_err_invalid_argThe new leaf holds a character no FAT name may carry, or is longer than 247 characters.
k_ra8_err_no_memThe directory has no room for the new entry.
Precondition
The file is not open.
Postcondition
On success new_path resolves to the same data.
Since
0.1.0

Definition at line 808 of file ra8_fs_fat_dir.c.

References internal_rename_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_rename().

Referenced by fileops_suite_mutate(), fs_fmt_rename_and_verify(), internal_atomic_replace(), internal_native_rename(), and ra8_fs_rename().

◆ ra8_fs_rmdir()

ra8_err_t ra8_fs_rmdir ( const ra8_fs_mount_t * handle,
const char * path )
nodiscard

Remove an empty directory at path.

The symmetric partner of ra8_fs_mkdir(): resolves all-but-the-last path component to an existing parent, requires the final component to be an existing directory, requires that directory to be empty, then frees its clusters and takes its entry away. Nested paths are supported ("/books/scifi").

The emptiness proof runs before anything is freed, so a refusal costs the volume nothing. Only remnants are discounted – FAT's deleted (0xE5) slots and orphaned long-name entries, exFAT's entries with the in-use bit clear – so a directory another implementation left remnants in is still removable, while anything live makes it k_ra8_err_not_empty. On FAT the directory's own "." and ".." links do not count either; exFAT has none to discount.

The volume root is not removable, and neither is a file: use ra8_fs_unlink() for those.

Parameters
[in]handleMount handle.
[in]pathNUL-terminated directory path to remove.
Returns
ra8_err_t Error code.
Return values
k_ra8_okDirectory removed.
k_ra8_err_null_ptrhandle or path was NULL.
k_ra8_err_invalid_stateMount is not in use.
k_ra8_err_invalid_argpath is the root, or names a file rather than a directory.
k_ra8_err_not_foundNo such entry, or a component is missing.
k_ra8_err_not_emptyThe directory still holds entries.
k_ra8_err_protocol_errorCorrupt chain, or a directory entry that claims no data cluster.
Precondition
handle and path are non-NULL; the mount is in use.
No open file handle refers to an entry inside path.
Postcondition
On success path no longer resolves and its cluster is free.
On any refusal (wrong type, non-empty, root) the volume is unchanged.
Note
An open handle to a file inside path cannot be orphaned by this call: that file's directory entry still exists, so the emptiness check refuses the removal first.
Not thread-safe; callers serialise.
Example:
ra8_err_t e = ra8_fs_rmdir(mnt, "/logs");
// unlink the contents first, then retry
}
@ k_ra8_err_not_empty
Container still holds members – the operation requires it empty.
Definition ra8_err.h:258
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
ra8_err_t ra8_fs_rmdir(const ra8_fs_mount_t *handle, const char *path)
Remove an empty directory at path.
See also
ra8_fs_mkdir() Creates the directory this removes.
ra8_fs_unlink() Removes a file instead.
Since
0.1.0

Definition at line 545 of file ra8_fs_fat_dirmk.c.

References internal_rmdir_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_rmdir().

Referenced by internal_native_rmdir(), and ra8_fs_rmdir().

◆ ra8_fs_seek()

ra8_err_t ra8_fs_seek ( ra8_fs_file_t * file,
uint64_t offset_bytes )
nodiscard

Move the file offset to offset_bytes (clamped to size).

64-bit so any position in a >4 GiB exFAT file is reachable (#676).

Return values
k_ra8_okSeek committed.
k_ra8_err_null_ptrfile is NULL.
Since
0.1.0

Definition at line 786 of file ra8_fs_fat_fileio.c.

References internal_seek_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_seek().

Referenced by internal_native_seek(), internal_seek(), internal_source_read(), ra8_fs_seek(), sh_sd_book_read(), and sh_sd_comic_read().

◆ ra8_fs_size()

ra8_err_t ra8_fs_size ( const ra8_fs_file_t * file,
uint64_t * out_bytes )
nodiscard

◆ ra8_fs_stat()

ra8_err_t ra8_fs_stat ( const ra8_fs_mount_t * handle,
const char * path,
ra8_fs_stat_t * out )
nodiscard

Report what a path names – file or directory – without opening it.

Resolves path the same way ra8_fs_open() does (parent walk, 8.3 lookup, VFAT long-name fallback on FAT; root-directory entry-set scan on exFAT) but stops at the directory entry and reads the answer straight out of it. Nothing is opened, so no file-table slot is consumed and a directory is reported as a directory rather than as a zero-byte file – which is what opening one would have made it look like, DIR_FileSize being 0 by definition.

Creation, modification, and access timestamps are decoded from the same entry. FAT access time is date-only and FAT carries no UTC offset; exFAT reports its 10-ms increments and offset-valid bits. A malformed third-party stamp is returned with timestamp.valid == false rather than guessed.

A path naming the volume root ("", "/") is answered from the mount geometry: it always exists and is always a directory. Because the root has no directory entry of its own, its three timestamp results are invalid.

Parameters
[in]handleMount handle.
[in]pathNUL-terminated path. Nested paths resolve on every supported filesystem, FAT12/16/32 and exFAT alike.
[out]outReceives the entry's metadata on success.
Returns
ra8_err_t Error code.
Return values
k_ra8_okEntry found; out populated.
k_ra8_err_null_ptrAny pointer argument was NULL.
k_ra8_err_invalid_stateMount is not in use.
k_ra8_err_not_foundNothing at path (or an intermediate component is missing).
k_ra8_err_invalid_argA path component is longer than 247 characters.
k_ra8_err_*Backend read failure.
Precondition
handle, path and out are non-NULL.
Mount is in use.
Postcondition
On k_ra8_ok, out->is_directory matches the entry's ATTR_DIRECTORY bit and out->size_bytes is 0 whenever it is set.
A non-root entry reports decoded create/modify/access metadata when its on-disk fields are legal; the root reports all three invalid.
No volume state is modified and no file slot is consumed, on any path.
Note
Not thread-safe unless a lock is installed (see ra8_fs_set_lock()).
Example:
ra8_fs_stat_t st = {};
const ra8_err_t e = ra8_fs_stat(mnt, "/README.TXT", &st);
// e == k_ra8_err_not_found means "no such name", not "I/O failed".
ra8_err_t ra8_fs_stat(const ra8_fs_mount_t *handle, const char *path, ra8_fs_stat_t *out)
Report what a path names – file or directory – without opening it.
What ra8_fs_stat() read out of a directory entry.
See also
ra8_fs_listdir() Enumerate a directory this reported.
ra8_fs_open() Same resolution, but takes a handle.
Since
0.1.0

Definition at line 565 of file ra8_fs_fat_stat.c.

References internal_stat_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_stat().

Referenced by demo_file_ops(), internal_native_stat(), and ra8_fs_stat().

◆ ra8_fs_tell()

ra8_err_t ra8_fs_tell ( const ra8_fs_file_t * file,
uint64_t * out_offset )
nodiscard

Report the current offset (64-bit; see ra8_fs_seek).

Since
0.1.0

Definition at line 795 of file ra8_fs_fat_fileio.c.

References internal_tell_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_tell().

Referenced by internal_native_tell(), internal_tell(), and ra8_fs_tell().

◆ ra8_fs_unlink()

ra8_err_t ra8_fs_unlink ( const ra8_fs_mount_t * handle,
const char * path )
nodiscard

Delete a file: mark its dir entry deleted and free its clusters.

FAT12/16/32: 0xE5-marks the entry – together with the VFAT long-name chain in front of it, if it has one – and frees the FAT chain. exFAT: clears the in-use bit on the whole entry set and frees the clusters in the allocation bitmap (the bitmap is authoritative).

A DIRECTORY is refused on both filesystems. Deleting one this way would free the chain that holds its children while their own entries still claim their clusters, leaving them allocated and unreachable – lost clusters that only a reformat recovers. Use ra8_fs_rmdir().

Parameters
[in]handleMount handle.
[in]pathNUL-terminated path to the file.
Return values
k_ra8_okFile unlinked.
k_ra8_err_null_ptrhandle/path NULL.
k_ra8_err_invalid_argpath names a directory.
k_ra8_err_access_deniedpath's read-only attribute is set.
k_ra8_err_not_foundFile doesn't exist.
Precondition
handle and path are non-NULL; the mount is in use.
No open file handle refers to path.
Postcondition
On success the name no longer resolves and its clusters are free.
On k_ra8_err_invalid_arg the volume is unchanged.
Note
Not thread-safe; callers serialise.
See also
ra8_fs_rmdir() Removes a directory instead.
Since
0.1.0

Definition at line 799 of file ra8_fs_fat_dir.c.

References internal_unlink_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_unlink().

Referenced by demo_file_ops(), fileops_suite_create(), fileops_suite_mutate(), fs_fmt_write_cycle(), internal_atomic_replace(), internal_compile_and_cache(), internal_native_unlink(), internal_write_stamp(), and ra8_fs_unlink().

◆ ra8_fs_unmount()

ra8_err_t ra8_fs_unmount ( ra8_fs_mount_t * handle)
nodiscard

Unmount a previously mounted volume and release its slot.

On a FAT32 volume carrying a valid FSInfo sector this is also where the free-cluster count and next-free hint are written back, so a card the firmware wrote reports the right free space on a desktop and fsck.fat stops calling the summary wrong. The slot is released even when that write fails – an unmount that could be refused would be worse than a stale free count.

Parameters
[in,out]handleMount handle returned by ra8_fs_mount().
Return values
k_ra8_okSlot released.
k_ra8_err_null_ptrhandle is NULL.
k_ra8_err_invalid_stateSlot was not in use.
k_ra8_err_*FSInfo writeback failed; the slot is still released.
Precondition
handle was returned by ra8_fs_mount.
Postcondition
handle->in_use == 0.
Any pending FSInfo update has been attempted exactly once.
Since
0.1.0

Definition at line 782 of file ra8_fs_fat_mount.c.

References internal_unmount_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_unmount().

Referenced by demo_thread_entry(), fileops_run_ladder(), fs_fmt_run_exfat(), fs_fmt_run_one_type(), internal_finish_image(), internal_native_unmount(), internal_run_font(), internal_swap_run_one(), main(), priv_mkbookimg_build_image(), ra8_fs_unmount(), ra8_sdfont_load(), selftest_host_pass(), and selftest_host_pass().

◆ ra8_fs_write()

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

Write len bytes; allocate new clusters from FAT free-space scan as needed.

New clusters come from a scan that starts at the mount's next-free hint rather than at cluster 2, reading the FAT through a one-sector cache, so appending stays linear in the bytes written instead of quadratic. The directory entry's modification time advances with every call, and again when the handle is closed.

Return values
k_ra8_okWrote all bytes; size + dir entry updated.
k_ra8_err_null_ptrfile or buf NULL.
k_ra8_err_invalid_statefile not opened for writing.
k_ra8_err_invalid_sizeFAT volume and the write would push the file past 4 GiB - 1 (DIR_FileSize is 32-bit); exFAT files have no such cap (#676).
k_ra8_err_no_memVolume out of free clusters.
Postcondition
On success the entry's DIR_WrtTime / DIR_WrtDate name this write.
Since
0.1.0

Definition at line 767 of file ra8_fs_fat_fileio.c.

References internal_write_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_write().

Referenced by internal_native_write(), internal_ra8_io_roundtrip_write_subdir(), internal_ra8_vfs_compress_store_blob(), internal_stream(), internal_stream_book(), internal_swap_vfs_write(), internal_write(), ra8_fs_write(), ra8_io_roundtrip_subdir_file(), and sdhi_demo_write_payload().

◆ ra8_fs_write_file()

ra8_err_t ra8_fs_write_file ( ra8_fs_mount_t * handle,
const char * path,
const uint8_t * data,
uint32_t len )
nodiscard

Create a whole file in one call (provisioning helper).

Convenience wrapper, and nothing more: on EVERY filesystem it opens path in write mode, writes data, and closes. It carried a second, exFAT-only implementation until exFAT learned to stream (#602) – a whole-file creator that needed one contiguous run and the entire payload in RAM at once. Both limits are gone with it, and the one remaining path means the two filesystems can no longer disagree about what this call does.

An existing path is REPLACED on both filesystems: write mode truncates it, so its old contents are discarded and its clusters returned to the volume's free space. Calling this twice with the same name leaves exactly one file, of the second call's contents, with no space lost to the first.

Parameters
[in]handleMounted volume.
[in]pathFile path, UTF-8; nested paths resolve on both filesystems, provided every intermediate directory exists.
[in]dataFile contents.
[in]lenByte count; 0 leaves an empty file.
Return values
k_ra8_okFile created and written.
k_ra8_err_null_ptrAny pointer argument was NULL.
k_ra8_err_invalid_argEmpty/oversized name, or path names an existing directory.
k_ra8_err_access_deniedpath exists and its read-only attribute is set (it would be replaced – refused).
k_ra8_err_no_memOut of free clusters or directory slots.
k_ra8_err_*Backend error.
Precondition
No handle is open on path.
Postcondition
On success path resolves to exactly one entry of len bytes.
On success a replaced predecessor's clusters read as free.
Note
Not atomic: the truncate lands before the new content does, so a failure mid-write leaves path short, not stale.
Since
0.1.0

Definition at line 777 of file ra8_fs_fat_fileio.c.

References internal_write_file_locked(), priv_lock_acquire(), priv_lock_release(), and ra8_fs_write_file().

Referenced by demo_file_ops(), demo_write_test_file(), eoh_provision_or_halt(), etoc_provision_one(), fileops_step_write(), fs_fmt_create_and_verify(), internal_demo_run(), internal_dispatch_and_cache(), internal_open_or_provision(), internal_pc_font_or_halt(), internal_pc_persist_and_verify_or_halt(), internal_populate(), internal_write_stamp(), ra8_fs_write_file(), ra8_io_roundtrip_root_file(), rabook_compile_from_epub(), and sd_demo_write_payload().