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

Path metadata lookup – what is at this path, without opening it. 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_stat.c:

Go to the source code of this file.

Enumerations

enum  fs_stat_field_t : uint16_t {
  k_stat_fat_year_shift = 9U ,
  k_stat_fat_month_shift = 5U ,
  k_stat_fat_month_mask = 0x0FU ,
  k_stat_fat_day_mask = 0x1FU ,
  k_stat_fat_hour_shift = 11U ,
  k_stat_fat_hour_mask = 0x1FU ,
  k_stat_fat_minute_shift = 5U ,
  k_stat_fat_minute_mask = 0x3FU ,
  k_stat_fat_second_mask = 0x1FU ,
  k_stat_tenths_per_second = 100U ,
  k_stat_month_max = 12U ,
  k_stat_hour_max = 23U ,
  k_stat_minute_max = 59U ,
  k_stat_second_max = 59U ,
  k_stat_creation_tenth_max = 199U ,
  k_stat_packed_time_mask = 0xFFFFU ,
  k_stat_utc_sign_bit = 0x40U ,
  k_stat_utc_field_modulus = 128U
}
 FAT/exFAT packed timestamp field geometry and validation bounds. More...

Functions

static bool internal_path_is_root (const char *path)
 Does path name the volume root rather than an entry inside it?
static bool internal_stat_fat_time_valid (const ra8_fs_datetime_t *v, uint8_t tenth, bool have_tenth)
 Is a decoded FAT civil date/time within every legal calendar bound?
static void internal_stat_decode_fat (uint16_t date, uint16_t time, uint8_t tenth, bool have_tenth, ra8_fs_timestamp_t *out)
 Decode one FAT-format packed date/time pair into a public timestamp.
static void internal_stat_decode_exfat (uint32_t packed, uint8_t tenth, bool have_tenth, uint8_t utc, ra8_fs_timestamp_t *out)
 Decode one exFAT packed stamp, 10-ms increment, and UTC-offset byte.
static void internal_stat_fat_times (const uint8_t *entry, ra8_fs_stat_t *out)
 Decode all three timestamp fields from one FAT directory entry.
static void internal_stat_exfat_times (const uint8_t *entry, ra8_fs_stat_t *out)
 Decode all three timestamp fields from one exFAT File entry.
static void internal_stat_root (const ra8_fs_mount_t *m, ra8_fs_stat_t *out)
 Fill in out for the volume root itself.
static void internal_entry_to_stat (const uint8_t *entry, ra8_fs_stat_t *out)
 Translate a 32-byte FAT directory entry into a ra8_fs_stat_t.
static ra8_err_t internal_stat_exfat (const ra8_fs_mount_t *m, const char *path, ra8_fs_stat_t *out)
 stat a name on an exFAT volume, at any depth.
static ra8_err_t internal_stat_fat (const ra8_fs_mount_t *handle, const char *path, ra8_fs_stat_t *out)
 Resolve one name in a FAT12/16/32 directory and decode its entry.
static ra8_err_t internal_stat_locked (const ra8_fs_mount_t *handle, const char *path, ra8_fs_stat_t *out)
 Look a path up – the guarded body of ra8_fs_stat().
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.

Detailed Description

Path metadata lookup – what is at this path, without opening it.

stat resolves a path exactly as open does and then stops at the directory entry, reading the attribute byte, the size and the first cluster straight out of it. Not opening anything is the whole point: a directory opens perfectly well and reports DIR_FileSize 0, so an open-based stat cannot tell a folder from an empty file – and it spends one of the four file-table slots to fail at it.

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_stat.c.

Enumeration Type Documentation

◆ fs_stat_field_t

enum fs_stat_field_t : uint16_t

FAT/exFAT packed timestamp field geometry and validation bounds.

Enumerator
k_stat_fat_year_shift 

FAT year-field shift.

k_stat_fat_month_shift 

FAT month-field shift.

k_stat_fat_month_mask 

FAT month-field mask.

k_stat_fat_day_mask 

FAT day-field mask.

k_stat_fat_hour_shift 

FAT hour-field shift.

k_stat_fat_hour_mask 

FAT hour-field mask.

k_stat_fat_minute_shift 

FAT minute-field shift.

k_stat_fat_minute_mask 

FAT minute-field mask.

k_stat_fat_second_mask 

FAT half-second-field mask.

k_stat_tenths_per_second 

Ten-millisecond units per second.

k_stat_month_max 

Largest civil month.

k_stat_hour_max 

Largest civil hour.

k_stat_minute_max 

Largest civil minute.

k_stat_second_max 

Largest civil second.

k_stat_creation_tenth_max 

Largest legal FAT creation increment.

k_stat_packed_time_mask 

Low packed time word.

k_stat_utc_sign_bit 

Sign bit in the seven-bit UTC field.

k_stat_utc_field_modulus 

Two's-complement UTC field modulus.

Definition at line 28 of file ra8_fs_fat_stat.c.

Function Documentation

◆ internal_entry_to_stat()

void internal_entry_to_stat ( const uint8_t * entry,
ra8_fs_stat_t * out )
static

Translate a 32-byte FAT directory entry into a ra8_fs_stat_t.

Copies the attribute byte through verbatim – read-only, hidden and system survive, where the old VFS stat invented archive for every entry – and derives is_directory from the ATTR_DIRECTORY bit rather than asserting it is clear. A directory's DIR_FileSize is 0 by specification; it is forced to 0 here anyway so a volume written by some other implementation cannot report a directory with a non-zero length.

Parameters
[in]entry32 bytes of on-disk directory entry.
[out]outReceives the decoded metadata.
Returns
Nothing.
Precondition
entry addresses 32 readable bytes.
out is non-NULL.
Postcondition
out->is_directory matches the entry's ATTR_DIRECTORY bit.
out->size_bytes is 0 whenever out->is_directory is true.
Note
Pure decode; touches no backend.
Since
0.1.0

Definition at line 368 of file ra8_fs_fat_stat.c.

References ra8_fs_stat_t::attr, ra8_fs_stat_t::first_cluster, internal_stat_fat_times(), ra8_fs_stat_t::is_directory, k_dir_off_attr, k_dir_off_file_size, k_ra8_fs_attr_directory, priv_entry_first_cluster(), priv_rd32(), and ra8_fs_stat_t::size_bytes.

Referenced by internal_stat_fat().

◆ internal_path_is_root()

bool internal_path_is_root ( const char * path)
static

Does path name the volume root rather than an entry inside it?

The root has no directory entry of its own, so it cannot be looked up; it is answered from the mount geometry instead. Any run of leading slashes with nothing after it is the root, which covers both "" and "/".

Parameters
[in]pathNUL-terminated path.
Returns
bool true when path names the root directory.
Return values
trueOnly slashes (or nothing at all).
falseAt least one name character follows.
Precondition
path is non-NULL.
path is NUL-terminated.
Postcondition
No state modified.
The result is purely a function of path.
Note
Pure function; bounded by the string's own terminator.
Since
0.1.0

Definition at line 73 of file ra8_fs_fat_stat.c.

Referenced by internal_stat_locked().

◆ internal_stat_decode_exfat()

void internal_stat_decode_exfat ( uint32_t packed,
uint8_t tenth,
bool have_tenth,
uint8_t utc,
ra8_fs_timestamp_t * out )
static

Decode one exFAT packed stamp, 10-ms increment, and UTC-offset byte.

Uses the FAT-compatible packed civil fields, then decodes exFAT's signed 15-minute UTC offset only when its validity bit is set.

Parameters
[in]packedexFAT 32-bit date/time value.
[in]tenthOptional 10-ms increment.
[in]have_tenthtrue for create/modify, false for access.
[in]utcexFAT UtcOffset byte.
[out]outDecoded timestamp.
Returns
Nothing.
Precondition
out is non-NULL.
packed, tenth, and utc are raw fields from one File entry.
Postcondition
Invalid calendar bytes produce an all-zero, invalid result.
A clear/invalid UTC field never masquerades as UTC+00:00.
Note
Pure decode.
Since
0.1.0

Definition at line 207 of file ra8_fs_fat_stat.c.

References internal_stat_decode_fat(), k_fs_utc_field_mask, k_fs_utc_span_max, k_fs_utc_span_min, k_fs_utc_step_min, k_fs_utc_valid_bit, k_stat_packed_time_mask, k_stat_utc_field_modulus, k_stat_utc_sign_bit, ra8_fs_datetime_t::utc_offset_min, ra8_fs_timestamp_t::utc_offset_valid, ra8_fs_timestamp_t::valid, and ra8_fs_timestamp_t::value.

Referenced by internal_stat_exfat_times().

◆ internal_stat_decode_fat()

void internal_stat_decode_fat ( uint16_t date,
uint16_t time,
uint8_t tenth,
bool have_tenth,
ra8_fs_timestamp_t * out )
static

Decode one FAT-format packed date/time pair into a public timestamp.

Reconstructs FAT's even-second field and optional creation centiseconds, then rejects every out-of-range calendar component through internal_stat_fat_time_valid.

Parameters
[in]datePacked FAT date word.
[in]timePacked FAT time word (zero for date-only access time).
[in]tenthFAT 10-ms byte (creation only), or zero.
[in]have_tenthtrue when tenth belongs to the stamp.
[out]outDecoded timestamp; invalid when the fields are illegal.
Returns
Nothing.
Precondition
out is non-NULL.
date and time are raw little-endian values already read as host integers.
Postcondition
out->valid is true exactly when every packed field is legal.
out->utc_offset_valid is false because FAT stores no zone.
Note
Pure decode; FAT carries no UTC-offset field.
Since
0.1.0

Definition at line 160 of file ra8_fs_fat_stat.c.

References ra8_fs_datetime_t::centisecond, ra8_fs_datetime_t::day, ra8_fs_datetime_t::hour, internal_stat_fat_time_valid(), k_fs_time_epoch_year, k_stat_fat_day_mask, k_stat_fat_hour_mask, k_stat_fat_hour_shift, k_stat_fat_minute_mask, k_stat_fat_minute_shift, k_stat_fat_month_mask, k_stat_fat_month_shift, k_stat_fat_second_mask, k_stat_fat_year_shift, k_stat_tenths_per_second, ra8_fs_datetime_t::minute, ra8_fs_datetime_t::month, ra8_fs_datetime_t::second, ra8_fs_timestamp_t::utc_offset_valid, ra8_fs_timestamp_t::valid, ra8_fs_timestamp_t::value, and ra8_fs_datetime_t::year.

Referenced by internal_stat_decode_exfat(), and internal_stat_fat_times().

◆ internal_stat_exfat()

ra8_err_t internal_stat_exfat ( const ra8_fs_mount_t * m,
const char * path,
ra8_fs_stat_t * out )
static

stat a name on an exFAT volume, at any depth.

Reuses priv_exfat_lookup, which resolves the path's intermediate components and hands back the whole Stream entry plus the File entry's attribute byte, so a directory is told from a file by the bit that says so and the lengths come out of the same 32 bytes.

Parameters
[in]mMounted exFAT volume.
[in]pathPath, UTF-8, nested or root-level.
[out]outReceives the metadata of the entry.
Returns
Error code.
Return values
k_ra8_okEntry found; out populated.
k_ra8_err_not_foundNo such name, or a component is missing.
k_ra8_err_invalid_argAn intermediate component names a file.
k_ra8_err_*Backend read failure.
Precondition
m, path and out are non-NULL; m->type is exFAT.
The volume is mounted.
Postcondition
On success out->attr is the entry's own FileAttributes low byte.
No volume state is modified.
Note
The volume root is answered by the caller, before this is reached.
Since
0.1.0

Definition at line 409 of file ra8_fs_fat_stat.c.

References ra8_fs_stat_t::attr, ra8_fs_stat_t::first_cluster, internal_stat_exfat_times(), ra8_fs_stat_t::is_directory, k_exfat_entry_bytes, k_exfat_off_file_attr, k_exfat_set_max_entries, k_exfat_strm_off_clus, k_exfat_strm_off_dlen, k_ra8_fs_attr_directory, k_ra8_ok, priv_exfat_find_set(), priv_exfat_resolve_parent(), priv_rd32(), priv_rd64(), and ra8_fs_stat_t::size_bytes.

◆ internal_stat_exfat_times()

void internal_stat_exfat_times ( const uint8_t * entry,
ra8_fs_stat_t * out )
static

Decode all three timestamp fields from one exFAT File entry.

Maps creation, modification, and access stamps plus their UTC markers into the corresponding public stat timestamps.

Parameters
[in]entryexFAT File entry of at least 32 bytes.
[out]outStat result receiving the three timestamps.
Returns
Nothing.
Precondition
entry is non-NULL and readable for one File entry.
out is non-NULL and writable.
Postcondition
All three timestamp members have been assigned.
No non-timestamp stat member is modified.
Note
Pure decode; an unknown UTC offset never becomes a fabricated zero offset.
Since
0.1.0

Definition at line 292 of file ra8_fs_fat_stat.c.

References ra8_fs_stat_t::accessed, ra8_fs_stat_t::created, internal_stat_decode_exfat(), k_exfat_off_file_atime, k_exfat_off_file_autc, k_exfat_off_file_c10ms, k_exfat_off_file_ctime, k_exfat_off_file_cutc, k_exfat_off_file_m10ms, k_exfat_off_file_mtime, k_exfat_off_file_mutc, ra8_fs_stat_t::modified, and priv_rd32().

Referenced by internal_stat_exfat().

◆ internal_stat_fat()

ra8_err_t internal_stat_fat ( const ra8_fs_mount_t * handle,
const char * path,
ra8_fs_stat_t * out )
static

Resolve one name in a FAT12/16/32 directory and decode its entry.

The leaf is matched by packed 8.3 name and, failing that, by VFAT long name – the same two-step priv_open_locked() uses, so stat and open agree about what exists. Split out of ::priv_stat_locked() so each stays inside the function-size gate.

Parameters
[in]handleMounted FAT volume.
[in]pathNUL-terminated path; never the volume root.
[out]outReceives the metadata of the entry.
Returns
Error code.
Return values
k_ra8_okEntry found; out populated.
k_ra8_err_not_foundNothing at path.
k_ra8_err_invalid_argA component is not a valid 8.3 name.
k_ra8_err_*Backend read failure.
Precondition
The library lock is held (or none is installed).
handle, path and out are non-NULL; the mount is in use.
Postcondition
On success out describes the entry at path.
No volume state is modified and no file slot is consumed.
Note
Never call this from outside ra8_fs; it is the unlocked half.
Since
0.1.0

Definition at line 473 of file ra8_fs_fat_stat.c.

References internal_entry_to_stat(), internal_stat_fat(), k_max_8_3_name, k_ra8_err_not_found, k_ra8_fs_dir_entry_bytes, k_ra8_ok, priv_dir_find(), priv_dir_find_long(), priv_path_to_83(), and priv_resolve_parent().

Referenced by internal_stat_fat(), and internal_stat_locked().

◆ internal_stat_fat_time_valid()

bool internal_stat_fat_time_valid ( const ra8_fs_datetime_t * v,
uint8_t tenth,
bool have_tenth )
static

Is a decoded FAT civil date/time within every legal calendar bound?

Checks month, day, hour, minute, second, and the optional creation-centisecond byte. There is deliberately no upper-bound check on the day: FAT stores it in five bits, so date & k_stat_fat_day_mask cannot exceed 31, which is already the largest civil day. Only day == 0 is representable and illegal, and that is the guard below. Every check here is a single-condition if.

Parameters
[in]vDecoded (not yet validated) civil fields.
[in]tenthFAT 10-ms byte (creation only), or zero.
[in]have_tenthtrue when tenth belongs to the stamp.
Returns
bool Whether every checked field is within its legal civil range.
Return values
trueEvery field is legal.
falseAt least one field is out of range.
Precondition
v is non-NULL.
v was decoded from a raw packed FAT date/time pair.
Postcondition
No state is modified.
The result is purely a function of v, tenth, and have_tenth.
Note
Pure predicate.
Since
0.1.0

Definition at line 110 of file ra8_fs_fat_stat.c.

References ra8_fs_datetime_t::day, ra8_fs_datetime_t::hour, k_stat_creation_tenth_max, k_stat_hour_max, k_stat_minute_max, k_stat_month_max, k_stat_second_max, ra8_fs_datetime_t::minute, ra8_fs_datetime_t::month, and ra8_fs_datetime_t::second.

Referenced by internal_stat_decode_fat().

◆ internal_stat_fat_times()

void internal_stat_fat_times ( const uint8_t * entry,
ra8_fs_stat_t * out )
static

Decode all three timestamp fields from one FAT directory entry.

Maps creation, modification, and date-only access fields into the corresponding public stat timestamps without mutating the entry.

Parameters
[in]entryFAT directory entry of at least 32 bytes.
[out]outStat result receiving the three timestamps.
Returns
Nothing.
Precondition
entry is non-NULL and readable for one directory entry.
out is non-NULL and writable.
Postcondition
All three timestamp members have been assigned.
No non-timestamp stat member is modified.
Note
Pure decode; invalid foreign fields remain representable as invalid.
Since
0.1.0

Definition at line 255 of file ra8_fs_fat_stat.c.

References ra8_fs_stat_t::accessed, ra8_fs_stat_t::created, internal_stat_decode_fat(), k_dir_off_crt_date, k_dir_off_crt_time, k_dir_off_crt_time_tenth, k_dir_off_lst_acc_date, k_dir_off_wrt_date, k_dir_off_wrt_time, ra8_fs_stat_t::modified, and priv_rd16().

Referenced by internal_entry_to_stat().

◆ internal_stat_locked()

ra8_err_t internal_stat_locked ( const ra8_fs_mount_t * handle,
const char * path,
ra8_fs_stat_t * out )
static

Look a path up – the guarded body of ra8_fs_stat().

Validates the arguments, answers the volume root out of the mount geometry (the root has no directory entry to read), and otherwise hands off to the exFAT or the FAT lookup. Nothing is opened on any path, so no file-table slot is consumed and a directory never has to be mistaken for a zero-byte file to be reported at all.

Parameters
[in]handleMount handle.
[in]pathNUL-terminated path.
[out]outReceives the metadata of the entry.
Returns
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.
k_ra8_err_*As documented for ra8_fs_stat().
Precondition
The library lock is held (or none is installed).
handle, path and out are non-NULL.
Postcondition
On success out describes the entry at path.
No volume state is modified and no file slot is consumed.
Note
Never call this from outside ra8_fs; it is the unlocked half.
Since
0.1.0

Definition at line 534 of file ra8_fs_fat_stat.c.

References internal_path_is_root(), internal_stat_fat(), internal_stat_locked(), internal_stat_root(), k_ra8_err_null_ptr, k_ra8_fs_type_exfat, and k_ra8_ok.

Referenced by internal_stat_locked(), and ra8_fs_stat().

◆ internal_stat_root()

void internal_stat_root ( const ra8_fs_mount_t * m,
ra8_fs_stat_t * out )
static

Fill in out for the volume root itself.

The root always exists and is always a directory. Its first_cluster is the FAT32 / exFAT root cluster, and 0 on FAT12/16 where the root is a fixed sector region with no chain – which is the same "no chain" value an empty file carries.

Parameters
[in]mMounted volume supplying the root cluster.
[out]outReceives the metadata of the root.
Returns
Nothing.
Precondition
m and out are non-NULL.
m is in use.
Postcondition
out->is_directory is true and out->size_bytes is 0.
No volume state is modified.
Note
Reads only cached geometry; touches no sector.
Since
0.1.0

Definition at line 334 of file ra8_fs_fat_stat.c.

References ra8_fs_stat_t::attr, ra8_fs_stat_t::first_cluster, ra8_fs_stat_t::is_directory, k_ra8_fs_attr_directory, ra8_fs_mount_t::root_cluster, and ra8_fs_stat_t::size_bytes.

Referenced by internal_stat_locked().

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