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

FAT subdirectory creation and removal (mkdir / rmdir). 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_dirmk.c:

Go to the source code of this file.

Enumerations

enum  dir_scan_t : uint8_t {
  k_dir_scan_more = 0U ,
  k_dir_scan_empty = 1U ,
  k_dir_scan_used = 2U
}
 Verdict of scanning one directory sector for occupancy. More...

Functions

static void internal_pack_dot_entry (uint8_t *ent, uint32_t dots, uint32_t cluster)
 Pack a "." or ".." dot entry into a 32-byte directory slot.
static ra8_err_t internal_dir_cluster_init (const ra8_fs_mount_t *m, uint32_t new_cluster, uint32_t parent_cluster)
 Initialise a freshly allocated directory cluster ("." + ".." + zeros).
static ra8_err_t internal_fat_mkdir (const ra8_fs_mount_t *handle, const char *path)
 Implementation of internal_fat_mkdir() – create one FAT directory.
static ra8_err_t internal_mkdir_locked (const ra8_fs_mount_t *handle, const char *path)
 Create a directory – the guarded body of ra8_fs_mkdir().
static dir_scan_t internal_rmdir_scan_sector (const ra8_fs_mount_t *m, const uint8_t *buf)
 Classify one already-loaded directory sector as empty / occupied / more.
static ra8_err_t internal_dir_is_empty (const ra8_fs_mount_t *m, uint32_t cluster, uint8_t *out_empty)
 Decide whether a subdirectory holds any entry other than "." / "..".
static ra8_err_t internal_rmdir_locate (const ra8_fs_mount_t *handle, const char *path, dir_target_t *out, uint32_t *out_cluster)
 Resolve a path to a removable subdirectory's entry and first cluster.
static ra8_err_t internal_fat_rmdir (const ra8_fs_mount_t *handle, const char *path)
 Implementation of internal_fat_rmdir() – remove one empty FAT directory.
static ra8_err_t internal_rmdir_locked (const ra8_fs_mount_t *handle, const char *path)
 Remove an empty directory – the guarded body of ra8_fs_rmdir().
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

FAT subdirectory creation and removal (mkdir / rmdir).

The two verbs that add and take away a directory, kept together because they are inverses over the same on-disk shape: mkdir allocates a cluster, stamps . and .. into it and links an ATTR_DIRECTORY entry, and rmdir undoes exactly that – after proving the directory holds nothing but those two dot entries.

The public entry points at the bottom dispatch: exFAT has neither dot entries nor a parent back-link, so its half lives in ra8_fs_fat_exfat_dir.c (#605) rather than being bent into the FAT shape here.

Split out of ra8_fs_fat_dir.c for the 1000-line file-size cap.

Since
0.1.0

Definition in file ra8_fs_fat_dirmk.c.

Enumeration Type Documentation

◆ dir_scan_t

enum dir_scan_t : uint8_t

Verdict of scanning one directory sector for occupancy.

Three-valued because "this sector held nothing" and "the directory ends here" are different answers: the first means keep walking, the second means stop and report empty.

Invariant
Exactly one value is returned per scanned sector.
See also
priv_rmdir_scan_sector()
Since
0.1.0
Enumerator
k_dir_scan_more 

Only skippable slots here; keep walking.

k_dir_scan_empty 

End-of-directory marker reached.

k_dir_scan_used 

A real entry was found; not empty.

Definition at line 252 of file ra8_fs_fat_dirmk.c.

Function Documentation

◆ internal_dir_cluster_init()

ra8_err_t internal_dir_cluster_init ( const ra8_fs_mount_t * m,
uint32_t new_cluster,
uint32_t parent_cluster )
static

Initialise a freshly allocated directory cluster ("." + ".." + zeros).

Writes the self ("."), parent ("..") links into the first sector and zeroes every remaining sector of the cluster so the directory has a clean end-of-directory marker for subsequent entries.

Parameters
[in]mMount providing geometry and backend.
[in]new_clusterThe directory's own first cluster.
[in]parent_clusterParent's first cluster (0 when the parent is root).
Returns
Error code.
Return values
k_ra8_okCluster initialised on disk.
k_ra8_err_*Backend write error.
Precondition
m is non-NULL; new_cluster >= k_cluster_first_data.
m->sectors_per_cluster >= 1.
Postcondition
On success the cluster holds "." and ".." then zeros.
On failure the cluster may be partially written.
Note
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 99 of file ra8_fs_fat_dirmk.c.

References internal_pack_dot_entry(), k_ra8_fs_dir_entry_bytes, k_ra8_ok, k_zero_sector, priv_bps(), priv_byte_fill(), priv_cluster_to_lba(), priv_sec_walk(), priv_write_sector(), and ra8_fs_mount_t::sectors_per_cluster.

Referenced by internal_fat_mkdir().

◆ internal_dir_is_empty()

ra8_err_t internal_dir_is_empty ( const ra8_fs_mount_t * m,
uint32_t cluster,
uint8_t * out_empty )
static

Decide whether a subdirectory holds any entry other than "." / "..".

Walks the directory's cluster chain with the shared dir_walk_t iterator, classifying each sector via priv_rmdir_scan_sector() and stopping at the first definite answer.

Parameters
[in]mMount providing geometry and backend.
[in]clusterThe subdirectory's first cluster.
[out]out_emptyReceives 1 when the directory is removable, else 0.
Returns
Error code.
Return values
k_ra8_okVerdict written to *out_empty.
k_ra8_err_protocol_errorCluster-chain cycle detected (corrupt FAT).
k_ra8_err_*Backend read error.
Precondition
m and out_empty are non-NULL; cluster >= k_cluster_first_data.
The entry for cluster carries k_ra8_fs_attr_directory.
Postcondition
On success *out_empty is 0 or 1.
No on-disk state is modified.
Note
The walk is bounded: priv_dir_walk_next_sector fails a chain that revisits a cluster, so the loop cannot run away on a corrupt FAT (NASA Power of 10 Rule 2).
Since
0.1.0

Definition at line 338 of file ra8_fs_fat_dirmk.c.

References dir_walk_t::cur_lba, internal_rmdir_scan_sector(), k_dir_scan_empty, k_dir_scan_used, k_ra8_ok, priv_dir_walk_init_loc(), priv_dir_walk_next_sector(), priv_read_sector(), and priv_sec_walk().

Referenced by internal_fat_rmdir().

◆ internal_fat_mkdir()

ra8_err_t internal_fat_mkdir ( const ra8_fs_mount_t * handle,
const char * path )
static

Implementation of internal_fat_mkdir() – create one FAT directory.

Resolves the parent, rejects an existing name, finds a free parent slot, allocates and initialises a directory cluster ("." / ".."), then writes the parent's directory entry. On any post-allocation failure the new cluster is freed so the volume is not leaked.

Parameters
[in]handleMounted FAT12/16/32 volume.
[in]pathNUL-terminated directory path to create.
Returns
Error code.
Return values
k_ra8_okDirectory created.
k_ra8_err_invalid_argLeaf is not an 8.3 name.
k_ra8_err_existsThe name already exists in the parent.
k_ra8_err_no_memParent directory full or volume full.
k_ra8_err_not_foundAn intermediate path component is missing.
k_ra8_err_*Backend / FAT error.
Precondition
handle and path are non-NULL; mount is a FAT volume.
The parent path exists.
Postcondition
On success the new directory has "." and ".." and an empty body.
On failure no cluster is leaked (a partial allocation is freed).
Note
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 148 of file ra8_fs_fat_dirmk.c.

References dir_loc_t::cluster, internal_dir_cluster_init(), dir_loc_t::is_root, k_dir_off_attr, k_ra8_err_exists, k_ra8_fs_attr_directory, k_ra8_fs_dir_entry_bytes, k_ra8_ok, priv_alloc_eoc_cluster(), priv_dir_commit(), priv_dir_lookup_any(), priv_dir_reserve(), priv_entry_set_cluster_size(), priv_fat_entry_stamp_create(), priv_free_chain(), and priv_resolve_parent().

Referenced by internal_mkdir_locked().

◆ internal_fat_rmdir()

ra8_err_t internal_fat_rmdir ( const ra8_fs_mount_t * handle,
const char * path )
static

Implementation of internal_fat_rmdir() – remove one empty FAT directory.

Locates the directory, proves it holds nothing but its own "." and ".." links, frees its cluster chain, then 0xE5-marks its entry in the parent. The emptiness proof runs before anything is freed, so a refused removal changes nothing on disk.

Parameters
[in]handleMounted FAT12/16/32 volume.
[in]pathNUL-terminated directory path to remove.
Returns
Error code.
Return values
k_ra8_okDirectory removed.
k_ra8_err_invalid_argRoot, not an 8.3 name, or not a directory.
k_ra8_err_not_foundNo such entry.
k_ra8_err_not_emptyThe directory still holds entries.
k_ra8_err_protocol_errorCorrupt chain or a directory with no cluster.
k_ra8_err_*Backend / FAT error.
Precondition
handle and path are non-NULL; handle is a mounted FAT volume.
No open file handle refers to an entry inside path.
Postcondition
On success the name no longer resolves and its cluster chain is free.
On any error other than a mid-free backend fault the volume is unchanged.
Note
An open handle to a file inside the directory cannot be orphaned: that file's entry is still present, so the emptiness check refuses first.
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 459 of file ra8_fs_fat_dirmk.c.

References dir_target_t::entry, internal_dir_is_empty(), internal_rmdir_locate(), k_dir_off_name, k_ra8_err_not_empty, k_ra8_ok, dir_target_t::lba, dir_target_t::off, dir_target_t::parent, priv_dir_erase_chain(), and priv_free_chain().

Referenced by internal_rmdir_locked().

◆ internal_mkdir_locked()

ra8_err_t internal_mkdir_locked ( const ra8_fs_mount_t * handle,
const char * path )
static

Create a directory – the guarded body of ra8_fs_mkdir().

Validates arguments and the mount, then dispatches to the FAT or the exFAT directory creator (#605).

Parameters
[in]handleMount handle.
[in]pathNUL-terminated directory path to create.
Returns
Error code.
Return values
k_ra8_okDirectory created.
k_ra8_err_null_ptrhandle or path was NULL.
k_ra8_err_invalid_stateMount not in use.
k_ra8_err_*See internal_fat_mkdir / priv_exfat_mkdir.
Precondition
The library lock is held (or none is installed).
handle and path are non-NULL.
Mount is in use.
Postcondition
On success a new empty directory exists at path.
On failure the volume is unchanged (a partial alloc is rolled back).
Note
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 223 of file ra8_fs_fat_dirmk.c.

References internal_fat_mkdir(), internal_mkdir_locked(), k_ra8_err_null_ptr, and k_ra8_fs_type_exfat.

Referenced by internal_mkdir_locked(), and ra8_fs_mkdir().

◆ internal_pack_dot_entry()

void internal_pack_dot_entry ( uint8_t * ent,
uint32_t dots,
uint32_t cluster )
static

Pack a "." or ".." dot entry into a 32-byte directory slot.

Writes the FAT self/parent link: a space-padded name of dots dots, the directory attribute, and cluster as the first cluster (size 0). A parent that is the volume root is recorded as cluster 0 per the FAT specification.

Parameters
[out]ent32-byte slot to populate (zeroed by this function).
[in]dots1 for ".", 2 for "..".
[in]clusterSelf cluster ("."), or parent cluster ("..", 0 if root).
Returns
Nothing.
Precondition
ent addresses 32 writable bytes.
dots is 1 or 2.
Postcondition
ent holds a directory dot entry pointing at cluster.
Bytes after the name/attr/cluster fields are zero.
Note
Trivially thread-safe; not reentrant against the same buffer.
Since
0.1.0

Definition at line 54 of file ra8_fs_fat_dirmk.c.

References k_dir_name_field_len, k_dir_off_attr, k_ra8_fs_attr_directory, k_ra8_fs_dir_entry_bytes, priv_entry_set_cluster_size(), and priv_fat_entry_stamp_create().

Referenced by internal_dir_cluster_init().

◆ internal_rmdir_locate()

ra8_err_t internal_rmdir_locate ( const ra8_fs_mount_t * handle,
const char * path,
dir_target_t * out,
uint32_t * out_cluster )
static

Resolve a path to a removable subdirectory's entry and first cluster.

Resolves the parent, refuses the volume root, packs the leaf to 8.3, looks it up, and requires the matched entry to carry k_ra8_fs_attr_directory with a real first cluster. Split out of internal_fat_rmdir so both stay inside the function-size gate.

Parameters
[in]handleMounted FAT12/16/32 volume.
[in]pathDirectory path to remove.
[out]outReceives the parent, the entry position and the entry.
[out]out_clusterThe directory's own first cluster.
Returns
Error code.
Return values
k_ra8_okLocated; outputs populated.
k_ra8_err_invalid_argpath is the root, is unstorable, or names a file rather than a directory.
k_ra8_err_not_foundNo such entry.
k_ra8_err_protocol_errorThe entry claims no data cluster.
k_ra8_err_*Backend error.
Precondition
All pointers are non-NULL; handle is a mounted FAT volume.
handle->type is not exFAT (the caller has dispatched already).
Postcondition
On success the outputs address a directory entry on disk.
No on-disk state is modified.
Note
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 399 of file ra8_fs_fat_dirmk.c.

References dir_target_t::entry, k_cluster_first_data, k_dir_off_attr, k_ra8_err_invalid_arg, k_ra8_err_protocol_error, k_ra8_fs_attr_directory, k_ra8_ok, dir_target_t::lba, dir_target_t::off, dir_target_t::parent, priv_dir_lookup_any(), priv_entry_first_cluster(), and priv_resolve_parent().

Referenced by internal_fat_rmdir().

◆ internal_rmdir_locked()

ra8_err_t internal_rmdir_locked ( const ra8_fs_mount_t * handle,
const char * path )
static

Remove an empty directory – the guarded body of ra8_fs_rmdir().

Validates arguments and the mount, then dispatches to the FAT or the exFAT directory remover (#605). exFAT rmdir landed with exFAT mkdir, in one change: before it, a volume mounted here never contained a directory this driver had made, so the verb had no reachable subject and could only have been exercised against a hand-crafted fixture.

Parameters
[in]handleMount handle.
[in]pathNUL-terminated directory path to remove.
Returns
Error code.
Return values
k_ra8_okDirectory removed.
k_ra8_err_null_ptrhandle or path was NULL.
k_ra8_err_invalid_stateMount not in use.
k_ra8_err_*See internal_fat_rmdir / priv_exfat_rmdir.
Precondition
The library lock is held (or none is installed).
handle and path are non-NULL.
Mount is in use.
Postcondition
On success path no longer resolves.
On failure the volume is unchanged.
Note
Not thread-safe; callers serialise.
Since
0.1.0

Definition at line 513 of file ra8_fs_fat_dirmk.c.

References internal_fat_rmdir(), internal_rmdir_locked(), k_ra8_err_null_ptr, and k_ra8_fs_type_exfat.

Referenced by internal_rmdir_locked(), and ra8_fs_rmdir().

◆ internal_rmdir_scan_sector()

dir_scan_t internal_rmdir_scan_sector ( const ra8_fs_mount_t * m,
const uint8_t * buf )
static

Classify one already-loaded directory sector as empty / occupied / more.

A slot does not count as an occupant when it is deleted (0xE5), an attr-0x0F long-name slot, or one of the synthetic "." / ".." dot entries. Long-name slots are skipped rather than counted because ra8_fs_unlink() clears only the 8.3 entry, so a removed file leaves its whole LFN chain behind; counting those remnants would make a genuinely empty directory permanently un-removable. A live LFN chain is always followed by its own 8.3 entry in the same directory, so nothing real is missed by skipping it.

Parameters
[in]mMounted volume (supplies the entries-per-sector bound).
[in]bufWhole-sector buffer holding directory entries.
Returns
The sector's verdict.
Return values
k_dir_scan_usedA non-skippable entry was seen.
k_dir_scan_emptyThe end-of-directory marker was seen first.
k_dir_scan_moreSector exhausted with only skippable slots.
Precondition
buf is non-NULL and holds a sector loaded from disk.
buf addresses at least one whole directory sector.
Postcondition
No state is modified; buf is unchanged.
The scan visits at most one sector's worth of slots.
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 288 of file ra8_fs_fat_dirmk.c.

References k_dir_marker_dot, k_dir_marker_free_perm, k_dir_marker_free_used, k_dir_off_attr, k_dir_off_name, k_dir_scan_empty, k_dir_scan_more, k_dir_scan_used, k_ra8_fs_attr_lfn, k_ra8_fs_dir_entry_bytes, and priv_dir_eps().

Referenced by internal_dir_is_empty().

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