ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_stat.c
Go to the documentation of this file.
1
19
20#include <stddef.h>
21#include <stdint.h>
22
23#include "ra8_attributes.h"
24#include "ra8_fs.h"
25#include "ra8_fs_fat_internal.h"
26
48
73static bool internal_path_is_root(const char* path)
74{
75 const char* p = path;
76 while (*p == '/') {
77 p++;
78 }
79 return *p == '\0';
80}
81
110static bool internal_stat_fat_time_valid(const ra8_fs_datetime_t* v, uint8_t tenth, bool have_tenth)
111{
112 if (v->month == 0U) {
113 return false;
114 }
115 if (v->month > (uint8_t)k_stat_month_max) {
116 return false;
117 }
118 if (v->day == 0U) {
119 return false;
120 }
121 if (v->hour > (uint8_t)k_stat_hour_max) {
122 return false;
123 }
124 if (v->minute > (uint8_t)k_stat_minute_max) {
125 return false;
126 }
127 if (v->second > (uint8_t)k_stat_second_max) {
128 return false;
129 }
130 if (have_tenth) {
131 if (tenth > (uint8_t)k_stat_creation_tenth_max) {
132 return false;
133 }
134 }
135 return true;
136}
137
160static void internal_stat_decode_fat(uint16_t date,
161 uint16_t time,
162 uint8_t tenth,
163 bool have_tenth,
165{
166 ra8_fs_datetime_t v = {};
167 v.year = (uint16_t)((uint32_t)k_fs_time_epoch_year + (date >> k_stat_fat_year_shift));
168 v.month = (uint8_t)((date >> k_stat_fat_month_shift) & (uint16_t)k_stat_fat_month_mask);
169 v.day = (uint8_t)(date & (uint16_t)k_stat_fat_day_mask);
170 v.hour = (uint8_t)((time >> k_stat_fat_hour_shift) & (uint16_t)k_stat_fat_hour_mask);
171 v.minute = (uint8_t)((time >> k_stat_fat_minute_shift) & (uint16_t)k_stat_fat_minute_mask);
172 v.second = (uint8_t)((time & (uint16_t)k_stat_fat_second_mask) * 2U);
173 if (have_tenth) {
174 v.second = (uint8_t)(v.second + ((tenth >= (uint8_t)k_stat_tenths_per_second) ? 1U : 0U));
175 v.centisecond = (uint8_t)(tenth % (uint8_t)k_stat_tenths_per_second);
176 }
177 if (!internal_stat_fat_time_valid(&v, tenth, have_tenth)) {
178 *out = (ra8_fs_timestamp_t){};
179 return;
180 }
181 out->value = v;
182 out->valid = true;
183 out->utc_offset_valid = false;
184}
185
207static void internal_stat_decode_exfat(uint32_t packed,
208 uint8_t tenth,
209 bool have_tenth,
210 uint8_t utc,
212{
213 const uint16_t date = (uint16_t)(packed >> 16U);
214 const uint16_t time = (uint16_t)(packed & (uint32_t)k_stat_packed_time_mask);
215 internal_stat_decode_fat(date, time, tenth, have_tenth, out);
216 if (!out->valid) {
217 return;
218 }
219 if ((utc & (uint8_t)k_fs_utc_valid_bit) == 0U) {
220 return;
221 }
222 int32_t steps = (int32_t)(utc & (uint8_t)k_fs_utc_field_mask);
223 if ((steps & (int32_t)k_stat_utc_sign_bit) != 0) {
224 steps -= (int32_t)k_stat_utc_field_modulus;
225 }
226 const int32_t minutes = steps * (int32_t)k_fs_utc_step_min;
227 if (minutes < (int32_t)k_fs_utc_span_min) {
228 return;
229 }
230 if (minutes > (int32_t)k_fs_utc_span_max) {
231 return;
232 }
233 out->value.utc_offset_min = (int16_t)minutes;
234 out->utc_offset_valid = true;
235}
236
255static void internal_stat_fat_times(const uint8_t* entry, ra8_fs_stat_t* out)
256{
260 true,
261 &out->created);
264 0U,
265 false,
266 &out->modified);
268 0U,
269 0U,
270 false,
271 &out->accessed);
272}
273
292static void internal_stat_exfat_times(const uint8_t* entry, ra8_fs_stat_t* out)
293{
296 true,
298 &out->created);
301 true,
303 &out->modified);
305 0U,
306 false,
308 &out->accessed);
309}
310
335{
336 out->size_bytes = 0U;
337 out->first_cluster = m->root_cluster;
338 out->attr = (uint8_t)k_ra8_fs_attr_directory;
339 out->is_directory = true;
340}
341
368static void internal_entry_to_stat(const uint8_t* entry, ra8_fs_stat_t* out)
369{
370 const uint8_t attr = entry[k_dir_off_attr];
371 out->attr = attr;
372 out->is_directory = (attr & (uint8_t)k_ra8_fs_attr_directory) != 0U;
375 internal_stat_fat_times(entry, out);
376 if (out->is_directory) {
377 out->size_bytes = 0U;
378 }
379}
380
409static ra8_err_t internal_stat_exfat(const ra8_fs_mount_t* m, const char* path, ra8_fs_stat_t* out)
410{
411 exfat_dir_t parent = {};
412 const char* leaf = nullptr;
413 const ra8_err_t pe = priv_exfat_resolve_parent(m, path, &parent, &leaf);
414 if (pe != k_ra8_ok) {
415 return pe;
416 }
418 uint32_t count = 0U;
419 uint8_t file_e[k_exfat_entry_bytes] = {};
420 uint8_t strm[k_exfat_entry_bytes] = {};
421 const ra8_err_t err = priv_exfat_find_set(m,
422 &parent,
423 leaf,
424 pos,
425 (uint32_t)k_exfat_set_max_entries,
426 &count,
427 file_e,
428 strm);
429 if (err != k_ra8_ok) {
430 return err;
431 }
432 out->attr = file_e[k_exfat_off_file_attr];
433 out->is_directory = (out->attr & (uint8_t)k_ra8_fs_attr_directory) != 0U;
436 internal_stat_exfat_times(file_e, out);
437 if (out->is_directory) {
438 out->size_bytes = 0U;
439 }
440 return k_ra8_ok;
441}
442
471RA8_EXPECTS_LOCK("ra8_fs_lock")
472static ra8_err_t
473internal_stat_fat(const ra8_fs_mount_t* handle, const char* path, ra8_fs_stat_t* out)
474{
475 dir_loc_t parent = {};
476 const char* leaf = nullptr;
477 const ra8_err_t rerr = priv_resolve_parent(handle, path, &parent, &leaf);
478 if (rerr != k_ra8_ok) {
479 return rerr;
480 }
481 uint8_t name83[k_max_8_3_name] = {};
482 const uint8_t have83 = priv_path_to_83(leaf, name83);
483 uint64_t lba = 0U;
484 uint32_t off = 0U;
485 uint8_t entry[k_ra8_fs_dir_entry_bytes] = {};
487 if (have83 != 0U) {
488 err = priv_dir_find(handle, &parent, name83, &lba, &off, entry);
489 }
490 if (err == k_ra8_err_not_found) {
491 /* Same long-name fallback as priv_open_locked: a leaf that is not
492 * 8.3-representable still resolves through its VFAT chain. */
493 err = priv_dir_find_long(handle, &parent, leaf, &lba, &off, entry);
494 }
495 if (err != k_ra8_ok) {
496 return err;
497 }
498 internal_entry_to_stat(entry, out);
499 return k_ra8_ok;
500}
501
532RA8_EXPECTS_LOCK("ra8_fs_lock")
533static ra8_err_t
534internal_stat_locked(const ra8_fs_mount_t* handle, const char* path, ra8_fs_stat_t* out)
535{
536 if (handle == nullptr) {
537 return k_ra8_err_null_ptr;
538 }
539 if (path == nullptr) {
540 return k_ra8_err_null_ptr;
541 }
542 if (out == nullptr) {
543 return k_ra8_err_null_ptr;
544 }
545 if (handle->in_use == 0U) {
546 return k_ra8_err_invalid_state;
547 }
548 *out = (ra8_fs_stat_t){};
549 if (internal_path_is_root(path)) {
550 internal_stat_root(handle, out);
551 return k_ra8_ok;
552 }
553 if (handle->type == k_ra8_fs_type_exfat) {
554 return internal_stat_exfat(handle, path, out);
555 }
556 return internal_stat_fat(handle, path, out);
557}
558
559/* =============================================================================
560 * Public entry points -- the lock brackets
561 * =============================================================================
562 */
563
564RA8_OWNS_RESOURCE("ra8_fs_lock")
565ra8_err_t ra8_fs_stat(const ra8_fs_mount_t* handle, const char* path, ra8_fs_stat_t* out)
566{
568 const ra8_err_t err = internal_stat_locked(handle, path, out);
570 return err;
571}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_OWNS_RESOURCE(kind)
RAII-style resource ownership contract.
#define RA8_EXPECTS_LOCK(name)
The function expects the named thread/IRQ lock to be held on entry.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
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.
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
uint16_t priv_rd16(const uint8_t *p)
Decode a little-endian uint16_t from a byte buffer.
Definition ra8_fs_fat.c:42
uint64_t priv_rd64(const uint8_t *p)
Decode a little-endian uint64_t from a byte buffer.
Definition ra8_fs_fat.c:55
ra8_err_t priv_exfat_resolve_parent(const ra8_fs_mount_t *m, const char *path, exfat_dir_t *out_parent, const char **out_leaf)
Resolve every component of path but the last.
ra8_err_t priv_exfat_find_set(const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *path, exfat_setpos_t *pos, uint32_t max_pos, uint32_t *out_count, uint8_t *file_copy, uint8_t *strm_copy)
Implementation of priv_exfat_find_set() – one aligned walk of a directory.
uint32_t priv_entry_first_cluster(const uint8_t *entry)
Read the first cluster from a 32-byte directory entry.
ra8_err_t priv_resolve_parent(const ra8_fs_mount_t *m, const char *path, dir_loc_t *out_parent, const char **out_leaf)
Resolve all-but-the-last path component to a parent directory.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
ra8_err_t priv_dir_find_long(const ra8_fs_mount_t *m, const dir_loc_t *loc, const char *want, uint64_t *out_lba, uint32_t *out_entry_off, uint8_t out_entry[k_ra8_fs_dir_entry_bytes])
Find a directory entry by its VFAT long name (case-insensitive).
void priv_lock_release(void)
Drop the library lock taken by priv_lock_acquire.
void priv_lock_acquire(void)
Take the library lock, if the caller installed one.
uint8_t priv_path_to_83(const char *path, uint8_t *out11)
Convert a "/FILE.TXT"-style path to packed 11-byte 8.3 form.
ra8_err_t priv_dir_find(const ra8_fs_mount_t *m, const dir_loc_t *loc, const uint8_t *name83, uint64_t *out_lba, uint32_t *out_entry_off, uint8_t out_entry[k_ra8_fs_dir_entry_bytes])
Find a directory entry by 8.3 name within a given directory.
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 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 bool internal_path_is_root(const char *path)
Does path name the volume root rather than an entry inside it?
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 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().
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?
fs_stat_field_t
FAT/exFAT packed timestamp field geometry and validation bounds.
@ k_stat_creation_tenth_max
Largest legal FAT creation increment.
@ k_stat_utc_sign_bit
Sign bit in the seven-bit UTC field.
@ k_stat_fat_second_mask
FAT half-second-field mask.
@ k_stat_fat_minute_mask
FAT minute-field mask.
@ k_stat_second_max
Largest civil second.
@ k_stat_hour_max
Largest civil hour.
@ k_stat_fat_hour_mask
FAT hour-field mask.
@ k_stat_fat_hour_shift
FAT hour-field shift.
@ k_stat_packed_time_mask
Low packed time word.
@ k_stat_fat_year_shift
FAT year-field shift.
@ k_stat_minute_max
Largest civil minute.
@ k_stat_fat_minute_shift
FAT minute-field shift.
@ k_stat_fat_month_shift
FAT month-field shift.
@ k_stat_fat_day_mask
FAT day-field mask.
@ k_stat_utc_field_modulus
Two's-complement UTC field modulus.
@ k_stat_month_max
Largest civil month.
@ k_stat_fat_month_mask
FAT month-field mask.
@ k_stat_tenths_per_second
Ten-millisecond units per second.
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 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 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 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.
@ k_fs_utc_span_min
UTC-12:00 expressed in minutes.
@ k_fs_utc_span_max
UTC+14:00 expressed in minutes.
@ k_exfat_off_file_mutc
exFAT spec sec 7.4.14 "LastModifiedUtcOffset".
@ k_exfat_off_file_m10ms
exFAT spec sec 7.4.12 "LastModified10ms".
@ k_exfat_off_file_atime
exFAT spec sec 7.4.10 "LastAccessedTimestamp".
@ k_exfat_off_file_mtime
exFAT spec sec 7.4.9 "LastModifiedTimestamp".
@ k_exfat_off_file_ctime
exFAT spec sec 7.4.8 "CreateTimestamp".
@ k_exfat_off_file_cutc
exFAT spec sec 7.4.13 "CreateUtcOffset".
@ k_exfat_off_file_autc
exFAT spec sec 7.4.15 "LastAccessedUtcOffset".
@ k_exfat_off_file_c10ms
exFAT spec sec 7.4.11 "Create10msIncrement".
@ k_dir_off_lst_acc_date
MS FAT spec sec 6 "DIR_LstAccDate".
@ k_dir_off_crt_time_tenth
MS FAT spec sec 6 "DIR_CrtTimeTenth".
@ k_dir_off_crt_date
MS FAT spec sec 6 "DIR_CrtDate".
@ k_dir_off_crt_time
MS FAT spec sec 6 "DIR_CrtTime".
@ k_dir_off_wrt_date
MS FAT spec sec 6 "DIR_WrtDate".
@ k_dir_off_wrt_time
MS FAT spec sec 6 "DIR_WrtTime".
@ k_fs_utc_valid_bit
exFAT UtcOffset bit 7 = OffsetValid.
@ k_fs_utc_step_min
exFAT UtcOffset granularity, minutes.
@ k_fs_utc_field_mask
exFAT UtcOffset bits 6:0 = the offset.
@ k_fs_time_epoch_year
Year 0 of both on-disk formats.
@ k_exfat_strm_off_dlen
Stream-ext DataLength (low 32 used).
@ k_exfat_strm_off_clus
Stream-ext FirstCluster.
@ k_exfat_entry_bytes
Directory entry size.
@ k_exfat_off_file_attr
File entry: FileAttributes (2 bytes).
@ k_dir_off_attr
MS FAT spec sec 6 "DIR_Attr".
@ k_dir_off_file_size
MS FAT spec sec 6 "DIR_FileSize".
@ k_exfat_set_max_entries
1 File + 1 Stream + 17 Name entries.
@ k_max_8_3_name
8.3 packed length without dot.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_attr_directory
MS FAT spec sec 6 "ATTR_DIRECTORY".
@ k_ra8_fs_dir_entry_bytes
MS FAT spec sec 6 "Directory Entry".
Identifies the directory a lookup/scan should operate in.
Identifies the exFAT directory a lookup, scan or link operates in.
Directory position (cluster + entry index) of one 32-byte entry.
Broken-down civil date and time used at the filesystem boundary.
uint8_t hour
Hour of day, 0..23.
uint8_t minute
Minute of hour, 0..59.
int16_t utc_offset_min
Offset of the civil fields from UTC, in minutes.
uint8_t second
Second of minute, 0..59.
uint8_t month
Month of year, 1..12.
uint8_t centisecond
Hundredths within second, 0..99.
uint16_t year
Full civil year, e.g.
uint8_t day
Day of month, 1..31.
Cached parse of one mounted FAT volume.
uint32_t root_cluster
BPB BPB_RootClus (FAT32 only).
What ra8_fs_stat() read out of a directory entry.
ra8_fs_timestamp_t accessed
Last-accessed timestamp (date-only on FAT).
uint32_t first_cluster
Head of the entry's cluster chain (0 if empty).
uint8_t attr
The entry's own FAT attribute byte.
uint64_t size_bytes
File length in bytes; 0 for a directory.
ra8_fs_timestamp_t modified
Last-modified timestamp.
bool is_directory
true => the ATTR_DIRECTORY bit is set.
ra8_fs_timestamp_t created
Creation timestamp, or invalid for the root.
One decoded on-disk timestamp plus availability facts.
ra8_fs_datetime_t value
Decoded civil date/time.
bool valid
true => the on-disk date is legal.
bool utc_offset_valid
true => utc_offset_min is known.