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

exFAT open-for-write: create, truncate, append, and the survey between. 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_openw.c:

Go to the source code of this file.

Functions

static void internal_exfat_seed_handle (ra8_fs_file_t *file, ra8_fs_mount_t *m, const exfat_setpos_t *head, uint32_t count, ra8_fs_mode_t mode)
 Zero every field of a freshly claimed handle and bind it to its set.
static ra8_err_t internal_exfat_survey_alloc (ra8_fs_file_t *file)
 Count a file's clusters and find its tail.
static ra8_err_t internal_exfat_truncate (ra8_fs_file_t *file, const uint8_t *strm)
 Release a file's clusters and rewrite its entry set empty, in place.
static ra8_err_t internal_exfat_writable_set (uint32_t count)
 Reject an entry set this adapter cannot correctly rewrite.
static ra8_err_t internal_exfat_open_found (ra8_fs_mount_t *handle, ra8_fs_mode_t mode, const exfat_setpos_t *head, uint32_t count, const uint8_t *file_e, const uint8_t *strm, ra8_fs_file_t **out_file)
 Open an existing name for writing: truncate it, or park at its end.
static ra8_err_t internal_exfat_open_created (ra8_fs_mount_t *handle, const exfat_dir_t *dir, const uint16_t *name, uint32_t nlen, ra8_fs_mode_t mode, ra8_fs_file_t **out_file)
 Create a name that does not exist and open the empty file it names.
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.

Detailed Description

exFAT open-for-write: create, truncate, append, and the survey between.

ra8_fs_open() used to answer k_ra8_err_not_supported for both writing modes on an exFAT volume, so the one way to put bytes on such a card was a whole-file creator that needed the payload in RAM and one contiguous run on disk (#602). This file is the other half of the fix: it turns a name into an open, writable handle, in the three shapes the API promises.

  • Create. The name does not exist, so a zero-length entry set is laid down before a single byte is written – FirstCluster 0, both lengths 0, NoFatChain clear, which is what exFAT spec sec 7.4.4 says an empty file looks like. The handle is usable immediately, and a caller that opens and closes without writing leaves exactly that empty file, as FAT does.
  • Truncate. The name exists and the mode is write, so the clusters go back to the allocation bitmap and the entry set is rewritten empty – IN PLACE. Keeping the set is what keeps the creation timestamp, and it is why replacing a file no longer costs it its identity.
  • Append. The name exists and the mode is append, so the file's real allocation is surveyed (arithmetic for a contiguous run, a bounded FAT walk for a chained one) and the offset is parked at DataLength.

A path that resolves to a DIRECTORY is refused in both modes: a truncate would hand the clusters holding its contents back to the volume and orphan every file inside it (#604).

References (every shorthand citation in this file):

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

Definition in file ra8_fs_fat_exfat_openw.c.

Function Documentation

◆ internal_exfat_open_created()

ra8_err_t internal_exfat_open_created ( ra8_fs_mount_t * handle,
const exfat_dir_t * dir,
const uint16_t * name,
uint32_t nlen,
ra8_fs_mode_t mode,
ra8_fs_file_t ** out_file )
static

Create a name that does not exist and open the empty file it names.

Claims the file slot BEFORE writing the directory entry, so a full file table is discovered while the directory is still untouched – the other order leaves a real, empty file behind on a call that returned an error.

Parameters
[in,out]handleMounted exFAT volume.
[in]dirDirectory the name is created in.
[in]nameLeaf name as UTF-16 code units (no leading '/').
[in]nlenNumber of units in name.
[in]modeWriting mode to record on the handle.
[out]out_fileReceives the open handle.
Returns
Error code.
Return values
k_ra8_okThe name exists as an empty file and is open.
k_ra8_err_no_memThe file table is full, or the root directory has no run of free slots big enough for the set.
k_ra8_err_*Backend read/write failure.
Precondition
Every pointer is non-NULL; the mount is an exFAT volume.
nlen is at most k_exfat_name_cap.
Postcondition
On success dir holds a zero-length set for that name.
On failure no file slot is left marked in use.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

Definition at line 387 of file ra8_fs_fat_exfat_openw.c.

References ra8_fs_file_t::in_use, internal_exfat_seed_handle(), k_ra8_err_no_mem, k_ra8_ok, ra8_fs_file_t::mount, priv_alloc_file_slot(), and priv_exfat_link().

Referenced by priv_exfat_open_write().

◆ internal_exfat_open_found()

ra8_err_t internal_exfat_open_found ( ra8_fs_mount_t * handle,
ra8_fs_mode_t mode,
const exfat_setpos_t * head,
uint32_t count,
const uint8_t * file_e,
const uint8_t * strm,
ra8_fs_file_t ** out_file )
static

Open an existing name for writing: truncate it, or park at its end.

Refuses a directory before anything else, claims a file slot, seeds it from the Stream entry, and then branches on the mode. On any failure after the slot is claimed the slot is handed back, so a refused open never costs one of the four handles. A ValidDataLength above DataLength is clamped rather than believed: it is not a prefix length if it is longer than the file.

Parameters
[in,out]handleMounted exFAT volume.
[in]modek_ra8_fs_mode_write or k_ra8_fs_mode_append.
[in]headPosition of the set's File entry.
[in]countEntry count of the set.
[in]file_eThe set's 32-byte File entry.
[in]strmThe set's 32-byte Stream-extension entry.
[out]out_fileReceives the open handle.
Returns
Error code.
Return values
k_ra8_okThe handle is open and positioned.
k_ra8_err_invalid_argThe name is a directory.
k_ra8_err_no_memThe file table is full.
k_ra8_err_not_supportedThe set is larger than this adapter rewrites.
k_ra8_err_*Bitmap, FAT, or backend failure.
Precondition
Every pointer is non-NULL; the mount is an exFAT volume.
The caller holds the library lock.
Postcondition
On success *out_file is in use and its offset suits mode.
On failure no file slot is left marked in use.
Note
The mode test is a single condition; both arms are driven by the two writing modes.
Since
0.1.0

Definition at line 300 of file ra8_fs_fat_exfat_openw.c.

References ra8_fs_file_t::cur_cluster, ra8_fs_file_t::first_cluster, ra8_fs_file_t::in_use, internal_exfat_seed_handle(), internal_exfat_survey_alloc(), internal_exfat_truncate(), internal_exfat_writable_set(), k_exfat_attr_directory, k_exfat_attr_read_only, k_exfat_off_file_attr, k_exfat_off_strm_valid, k_exfat_secflag_no_fat, k_exfat_strm_off_clus, k_exfat_strm_off_dlen, k_exfat_strm_off_flags, k_ra8_err_access_denied, k_ra8_err_invalid_arg, k_ra8_err_no_mem, k_ra8_fs_mode_write, k_ra8_ok, ra8_fs_file_t::mount, ra8_fs_file_t::no_fat_chain, ra8_fs_file_t::offset, priv_alloc_file_slot(), priv_rd32(), priv_rd64(), ra8_fs_file_t::size_bytes, ra8_fs_file_t::valid_bytes, and ra8_fs_file_t::walk_cache_cluster.

Referenced by priv_exfat_open_write().

◆ internal_exfat_seed_handle()

void internal_exfat_seed_handle ( ra8_fs_file_t * file,
ra8_fs_mount_t * m,
const exfat_setpos_t * head,
uint32_t count,
ra8_fs_mode_t mode )
static

Zero every field of a freshly claimed handle and bind it to its set.

One place decides what "an open exFAT file that owns nothing" is, so a later field cannot be added to ra8_fs_file_t and left uninitialised on one of the three open paths.

Parameters
[out]fileSlot to initialise.
[in]mOwning mount.
[in]headPosition of the set's File entry.
[in]countEntry count of the set (1 + SecondaryCount).
[in]modeOpen mode to record.
Returns
Nothing.
Precondition
file, m and head are non-NULL.
head came from priv_exfat_find_set or priv_exfat_link.
Postcondition
file is in use, owns no clusters, and is positioned at byte 0.
no_fat_chain is 1: an empty allocation is trivially contiguous, so the first cluster taken can keep the fast path.
Note
The FAT-only dir_entry_* fields are zeroed, not repurposed.
Since
0.1.0

Definition at line 71 of file ra8_fs_fat_exfat_openw.c.

References exfat_setpos_t::cluster, and exfat_setpos_t::index.

Referenced by internal_exfat_open_created(), and internal_exfat_open_found().

◆ internal_exfat_survey_alloc()

ra8_err_t internal_exfat_survey_alloc ( ra8_fs_file_t * file)
static

Count a file's clusters and find its tail.

An append has to know where the allocation ENDS before it can add to it, and the entry set does not record that: a contiguous file implies it from DataLength, and a chained one only reveals it by walking the FAT. The walk is bounded by the volume's cluster count, so a corrupted chain that loops terminates instead of hanging.

A chained file may own MORE clusters than its length needs (another implementation may have pre-allocated), and the walk finds them, so an append reuses that space instead of asking the bitmap for more.

Parameters
[in,out]fileHandle whose stream fields are already populated.
Returns
Error code.
Return values
k_ra8_okalloc_clusters and tail_cluster are set.
k_ra8_err_protocol_errorThe chain points below the first data cluster.
k_ra8_err_*FAT read failure.
Precondition
file is non-NULL with first_cluster, size_bytes and no_fat_chain taken from the on-disk Stream entry.
The mount is an exFAT volume.
Postcondition
On success alloc_clusters == 0 exactly when the file owns nothing.
On success tail_cluster is the last cluster of the allocation.
Note
The owns-nothing test is one compound decision of two conditions – a file with no first cluster, or one with no length. The second is load-bearing: without it the contiguous branch computes a cluster count of 0 and a tail one BELOW the first cluster. Its vectors live with the tests that drive it, cited as libs/ra8_fs/src/ra8_fs_fat_exfat_openw.c@priv_exfat_survey_alloc.
Since
0.1.0

Definition at line 134 of file ra8_fs_fat_exfat_openw.c.

References ra8_fs_mount_t::count_of_clusters, k_cluster_first_data, k_ra8_err_protocol_error, k_ra8_ok, priv_cluster_bytes(), priv_fat_get(), and priv_is_eoc().

Referenced by internal_exfat_open_found().

◆ internal_exfat_truncate()

ra8_err_t internal_exfat_truncate ( ra8_fs_file_t * file,
const uint8_t * strm )
static

Release a file's clusters and rewrite its entry set empty, in place.

What k_ra8_fs_mode_write means on an existing name. The entry set is kept – so the file's creation stamp and its position in the directory survive – and only its allocation and both lengths go.

Parameters
[in,out]fileOpen handle to empty.
[in]strmThe file's Stream entry as read from the volume.
Returns
Error code.
Return values
k_ra8_okThe file is zero-length and owns no clusters.
k_ra8_err_*Bitmap or backend failure.
Precondition
file and strm are non-NULL; the handle is in use.
The caller holds the library lock.
Postcondition
On success the freed clusters read as free in the allocation bitmap.
On success the on-disk entry set reports DataLength 0.
Note
Not thread-safe; callers serialise filesystem operations.
Since
0.1.0

Definition at line 196 of file ra8_fs_fat_exfat_openw.c.

References k_ra8_ok, priv_exfat_flush_set(), and priv_exfat_free_clusters().

Referenced by internal_exfat_open_found().

◆ internal_exfat_writable_set()

ra8_err_t internal_exfat_writable_set ( uint32_t count)
static

Reject an entry set this adapter cannot correctly rewrite.

Two refusals, both honest limits rather than guesses.

     A set SHORTER than ::k_exfat_set_min_entries is not a file: exFAT
     spec sec 7.4 requires a File entry, a Stream entry and at least one
     Name entry, and the flush rewrites the first two by position. A set
     claiming no secondaries would send that rewrite at the entry BEFORE
     the Stream entry -- on a fresh volume, the allocation-bitmap entry
     -- so the shape has to be checked, not assumed.

     A set LONGER than ::k_exfat_set_writable carries a name past this
     adapter's cap, and rewriting only the part that fits would leave a
     SetChecksum computed over the wrong number of entries.

     A `DataLength` above 4 GiB is no longer one of the refusals: the
     whole length model is 64-bit now, so a file past 4 GiB opens,
     appends and truncates like any other -- carrying such files is the
     reason exFAT exists (#676).
Parameters
[in]countEntry count of the set (1 + SecondaryCount).
Returns
Error code.
Return values
k_ra8_okThe set is one this adapter can own.
k_ra8_err_protocol_errorThe set is too short to be a file.
k_ra8_err_not_supportedThe set has more entries than it can rewrite.
Precondition
count came from the File entry's SecondaryCount.
The caller read the set off the volume, not from a guess.
Postcondition
No state is modified on any path.
A success means both the shape and the rewrite fit.
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 253 of file ra8_fs_fat_exfat_openw.c.

References k_exfat_set_min_entries, k_exfat_set_writable, k_ra8_err_not_supported, k_ra8_err_protocol_error, and k_ra8_ok.

Referenced by internal_exfat_open_found().

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