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

The wall-clock seam, and the packing of a reading into on-disk fields. 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_time.c:

Go to the source code of this file.

Data Structures

struct  fat_stamp_t
 One reading already packed into the two 16-bit FAT words plus tenths. More...
struct  exfat_stamp_t
 One reading already packed into exFAT's 32-bit stamp plus its extras. More...

Functions

static uint32_t internal_clamp_u32 (uint32_t v, uint32_t lo, uint32_t hi)
 Clamp v into the closed range [lo, hi].
static void internal_clamp_datetime (ra8_fs_datetime_t *t)
 Clamp every field of a broken-down time into its on-disk range.
static bool internal_now_or_epoch (ra8_fs_datetime_t *out)
 Read the installed clock, or hand back the FAT epoch.
static uint8_t internal_tenths_of (const ra8_fs_datetime_t *t)
 Count the 10 ms increments the 2-second stamp granularity discards.
static uint8_t internal_utc_byte (int16_t minutes)
 Encode a UTC offset in minutes as an exFAT UtcOffset byte.
static void internal_fat_pack (const ra8_fs_datetime_t *t, fat_stamp_t *out)
 Pack an already-clamped reading into FAT's two words plus the tenths.
static void internal_fat_stamp_now (fat_stamp_t *out)
 Pack the installed clock's reading into FAT's two words plus tenths.
static void internal_exfat_pack (const ra8_fs_datetime_t *t, bool real, exfat_stamp_t *out)
 Pack an already-clamped reading into exFAT's stamp plus side fields.
static void internal_exfat_stamp_now (exfat_stamp_t *out)
 Pack the installed clock's reading into exFAT's stamp plus side fields.
void priv_fat_entry_stamp_create (uint8_t *entry)
 Stamp a fresh FAT directory entry's create, write, and access fields.
void priv_fat_entry_stamp_write (uint8_t *entry)
 Advance a FAT directory entry's modification time and access date.
void priv_fat_entry_stamp_access (uint8_t *entry)
 Advance only a FAT directory entry's last-access date.
void priv_exfat_file_stamp_create (uint8_t *file_entry)
 Stamp a fresh exFAT File entry's create, modify, and access fields.
void priv_exfat_file_stamp_write (uint8_t *file_entry)
 Advance an exFAT File entry's modification (and access) stamps.
void priv_exfat_file_stamp_access (uint8_t *file_entry)
 Advance only an exFAT File entry's last-accessed stamp.
void priv_fat_entry_set_times (uint8_t *entry, const ra8_fs_datetime_t *create, const ra8_fs_datetime_t *modify, const ra8_fs_datetime_t *access)
 Set chosen FAT directory-entry timestamps from caller-supplied readings.
void priv_exfat_file_set_times (uint8_t *file_entry, const ra8_fs_datetime_t *create, const ra8_fs_datetime_t *modify, const ra8_fs_datetime_t *access)
 Set chosen exFAT File-entry timestamps from caller-supplied readings.
ra8_err_t ra8_fs_set_clock (const ra8_fs_clock_t *clock)
 Install (or remove) the calendar source used to stamp files.

Variables

static ra8_fs_clock_t s_clock
 The installed calendar binding; meaningful only when s_clock_bound.
static bool s_clock_bound
 True once a complete binding has been installed.

Detailed Description

The wall-clock seam, and the packing of a reading into on-disk fields.

Every timestamp ra8_fs used to write was zero, and stayed zero forever. That is not merely uninformative, it is not a legal date: FAT packs the month and the day as 1-based fields, so a DIR_WrtDate of 0x0000 claims month 0 of day 0. macOS reads it as uninitialised and shows 31 Dec 1969, Linux clamps both fields to 1 and shows 1980-01-01, Windows shows a blank. No host shows a real time and no two hosts agree.

This file fixes that in two independent layers.

The first has no dependencies at all: with nothing installed, every stamp is 1980-01-01 00:00:00, the first instant either format can express. That alone makes every host agree, and it is what a board with no RTC will write.

The second is the injected clock. ra8_fs cannot reach a calendar itself – ra8_time counts monotonic milliseconds and ra8_rtc_get() lives in ra8_hal, which this library must not depend on if it is to keep building against the host test mock – so the calendar arrives as a function pointer plus a cookie, in the same shape as the block backend and the lock seam. A now() that fails falls back to the epoch for that one stamp: a clock that has not been set yet must not stop a data logger from writing.

Readings are CLAMPED, never rejected. A filesystem that refused to write because a clock came back with month 13 would be trading a cosmetic problem for a data-loss one.

References (every shorthand citation in this file):

  • "MS FAT spec" = Microsoft Corp., "FAT: General Overview of On-Disk Format", v1.03, December 6 2000.
  • "exFAT spec" = Microsoft Corp., "exFAT file system specification", revision 1.00.
Since
0.1.0

Definition in file ra8_fs_fat_time.c.

Function Documentation

◆ internal_clamp_datetime()

void internal_clamp_datetime ( ra8_fs_datetime_t * t)
static

Clamp every field of a broken-down time into its on-disk range.

The whole clamping policy in one place, applied in place: a year before 1980 or after 2107 snaps to the nearest representable year, month/day/hour/minute/second/centisecond snap into their legal spans. A filesystem must not fail a write – or a utime – because a caller (or a clock) handed over a month 13; it records the nearest value the format can express instead. The utc_offset_min field is left untouched; ::priv_utc_byte validates it at pack time.

Parameters
[in,out]tReading to constrain in place.
Returns
Nothing.
Precondition
t is non-NULL.
Every field holds whatever the caller supplied, in range or not.
Postcondition
t->year is in [1980, 2107] and t->month/t->day are at least 1.
Every other field is inside the span the on-disk format can express.
Note
Pure aside from t; trivially thread-safe on distinct buffers.
Since
0.1.0

Definition at line 189 of file ra8_fs_fat_time.c.

References ra8_fs_datetime_t::centisecond, ra8_fs_datetime_t::day, ra8_fs_datetime_t::hour, internal_clamp_u32(), k_fs_time_centi_max, k_fs_time_day_max, k_fs_time_day_min, k_fs_time_epoch_year, k_fs_time_hour_max, k_fs_time_minute_max, k_fs_time_month_max, k_fs_time_month_min, k_fs_time_second_max, k_fs_time_year_max, ra8_fs_datetime_t::minute, ra8_fs_datetime_t::month, ra8_fs_datetime_t::second, and ra8_fs_datetime_t::year.

Referenced by internal_now_or_epoch(), priv_exfat_file_set_times(), and priv_fat_entry_set_times().

◆ internal_clamp_u32()

uint32_t internal_clamp_u32 ( uint32_t v,
uint32_t lo,
uint32_t hi )
static

Clamp v into the closed range [lo, hi].

The whole clamping policy in one place: a clock that returns nonsense degrades the stamp, it does not fail the write.

Parameters
[in]vValue to constrain.
[in]loLowest permitted value.
[in]hiHighest permitted value.
Returns
The constrained value.
Return values
lov was below the range.
hiv was above the range.
vv was already inside the range.
Precondition
lo <= hi.
All three arguments are already widened to 32 bits.
Postcondition
No state modified.
The result is in [lo, hi].
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 153 of file ra8_fs_fat_time.c.

Referenced by internal_clamp_datetime().

◆ internal_exfat_pack()

void internal_exfat_pack ( const ra8_fs_datetime_t * t,
bool real,
exfat_stamp_t * out )
static

Pack an already-clamped reading into exFAT's stamp plus side fields.

exFAT spec sec 7.4.8: the low 16 bits are FAT's time word and the high 16 bits are FAT's date word, which is why the shifts are shared with ::priv_fat_pack. Takes the reading as an argument so the clock-driven stamps and the caller-chosen utime timestamps share one packing; real selects whether the UtcOffset is honoured or recorded as unknown.

Parameters
[in]tReading to pack; already clamped into the on-disk ranges.
[in]realtrue when t is a real instant (its UtcOffset is written); false for the epoch placeholder (UtcOffset recorded unknown).
[out]outReceives the packed stamp, 10 ms increment, and UtcOffset.
Returns
Nothing.
Precondition
t and out are non-NULL; t has been clamped.
t->month and t->day are at least 1.
Postcondition
out->stamp's month and day sub-fields are non-zero.
out->utc is k_fs_utc_unknown or has k_fs_utc_valid_bit set.
Note
Pure aside from out; trivially thread-safe on distinct buffers.
Since
0.1.0

Definition at line 423 of file ra8_fs_fat_time.c.

References ra8_fs_datetime_t::day, ra8_fs_datetime_t::hour, exfat_stamp_t::inc10, internal_tenths_of(), internal_utc_byte(), k_fs_time_epoch_year, k_fs_time_second_div, k_fs_time_shift_hour, k_fs_time_shift_minute, k_fs_utc_unknown, k_fs_xf_shift_date, k_fs_xf_shift_month, k_fs_xf_shift_year, ra8_fs_datetime_t::minute, ra8_fs_datetime_t::month, ra8_fs_datetime_t::second, exfat_stamp_t::stamp, exfat_stamp_t::utc, ra8_fs_datetime_t::utc_offset_min, and ra8_fs_datetime_t::year.

Referenced by internal_exfat_stamp_now(), and priv_exfat_file_set_times().

◆ internal_exfat_stamp_now()

void internal_exfat_stamp_now ( exfat_stamp_t * out)
static

Pack the installed clock's reading into exFAT's stamp plus side fields.

Reads the clock (or the epoch) once and hands the reading to ::priv_exfat_pack, forwarding whether the reading is real so the epoch placeholder never carries a fabricated UtcOffset.

Parameters
[out]outReceives the packed stamp, 10 ms increment, and UtcOffset.
Returns
Nothing.
Precondition
out is non-NULL.
ra8_fs_set_clock is not running concurrently.
Postcondition
out->stamp's month and day sub-fields are non-zero.
out->utc is k_fs_utc_unknown or has k_fs_utc_valid_bit set.
Note
Not thread-safe against ra8_fs_set_clock.
Since
0.1.0

Definition at line 461 of file ra8_fs_fat_time.c.

References internal_exfat_pack(), and internal_now_or_epoch().

Referenced by priv_exfat_file_stamp_access(), priv_exfat_file_stamp_create(), and priv_exfat_file_stamp_write().

◆ internal_fat_pack()

void internal_fat_pack ( const ra8_fs_datetime_t * t,
fat_stamp_t * out )
static

Pack an already-clamped reading into FAT's two words plus the tenths.

MS FAT spec sec 6: the date word is (year - 1980) << 9 | month << 5 | day and the time word is hour << 11 | minute << 5 | second / 2. Takes the reading as an argument rather than reading the clock, so both the clock-driven stamps and the caller-chosen utime timestamps share one packing.

Parameters
[in]tReading to pack; already clamped into the on-disk ranges.
[out]outReceives the packed fields.
Returns
Nothing.
Precondition
t and out are non-NULL; t has been clamped.
t->month and t->day are at least 1.
Postcondition
out->date is a legal FAT date (month and day both non-zero).
out->tenth is at most 199.
Note
Pure aside from out; trivially thread-safe on distinct buffers.
Since
0.1.0

Definition at line 356 of file ra8_fs_fat_time.c.

References fat_stamp_t::date, ra8_fs_datetime_t::day, ra8_fs_datetime_t::hour, internal_tenths_of(), k_fs_date_shift_month, k_fs_date_shift_year, k_fs_time_epoch_year, k_fs_time_second_div, k_fs_time_shift_hour, k_fs_time_shift_minute, ra8_fs_datetime_t::minute, ra8_fs_datetime_t::month, ra8_fs_datetime_t::second, fat_stamp_t::tenth, fat_stamp_t::time, and ra8_fs_datetime_t::year.

Referenced by internal_fat_stamp_now(), and priv_fat_entry_set_times().

◆ internal_fat_stamp_now()

void internal_fat_stamp_now ( fat_stamp_t * out)
static

Pack the installed clock's reading into FAT's two words plus tenths.

Reads the clock (or the epoch) once and hands the reading to ::priv_fat_pack. FAT has no UTC-offset field, so a real reading and the epoch placeholder pack identically.

Parameters
[out]outReceives the packed fields.
Returns
Nothing.
Precondition
out is non-NULL.
ra8_fs_set_clock is not running concurrently.
Postcondition
out->date is a legal FAT date (month and day both non-zero).
out->tenth is at most 199.
Note
Not thread-safe against ra8_fs_set_clock.
Since
0.1.0

Definition at line 389 of file ra8_fs_fat_time.c.

References internal_fat_pack(), and internal_now_or_epoch().

Referenced by priv_fat_entry_stamp_access(), priv_fat_entry_stamp_create(), and priv_fat_entry_stamp_write().

◆ internal_now_or_epoch()

bool internal_now_or_epoch ( ra8_fs_datetime_t * out)
static

Read the installed clock, or hand back the FAT epoch.

Zero-initialises out to the epoch first, so every path – no binding, a now() that fails, a now() that succeeds – leaves a fully populated struct. Then clamps every field into the range the on-disk formats can express.

Parameters
[out]outReceives a reading that is always legal to pack.
Returns
Whether the fields came from a real clock.
Return values
trueAn installed now() answered; the fields describe an instant.
falseNo binding, or now() failed; the fields are the epoch placeholder and the caller must not attach a UTC offset to it.
Precondition
out is non-NULL.
ra8_fs_set_clock is not running concurrently.
Postcondition
out->year is in [1980, 2107] and every other field is in range.
out->month and out->day are at least 1.
Note
Not thread-safe against ra8_fs_set_clock.
MC/DC:
Two single-condition guards, nested rather than joined, because the second cannot be evaluated at all when the first is false (there is no function pointer to call): s_clock_bound, then s_clock.now(...) == k_ra8_ok. Vectors: no binding -> epoch; binding whose now() fails -> epoch; binding whose now() succeeds -> the reading.
Since
0.1.0

Definition at line 237 of file ra8_fs_fat_time.c.

References ra8_fs_datetime_t::day, internal_clamp_datetime(), k_fs_time_day_min, k_fs_time_epoch_year, k_fs_time_month_min, k_ra8_ok, ra8_fs_datetime_t::month, s_clock, s_clock_bound, and ra8_fs_datetime_t::year.

Referenced by internal_exfat_stamp_now(), and internal_fat_stamp_now().

◆ internal_tenths_of()

uint8_t internal_tenths_of ( const ra8_fs_datetime_t * t)
static

Count the 10 ms increments the 2-second stamp granularity discards.

Both formats store seconds divided by two, so an odd second is lost. DIR_CrtTimeTenth (MS FAT spec sec 6) and exFAT's Create10msIncrement (exFAT spec sec 7.4.11) exist to carry it back: the field counts 10 ms units in 0..199, i.e. the odd second contributes a whole 100 and the hundredths contribute the rest.

Parameters
[in]tReading already clamped by ::priv_now_or_epoch.
Returns
The 10 ms increment.
Return values
0..199Odd-second flag folded together with the hundredths.
Precondition
t->second <= 59 and t->centisecond <= 99.
t is non-NULL.
Postcondition
No state modified.
The result is at most 199.
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 284 of file ra8_fs_fat_time.c.

References ra8_fs_datetime_t::centisecond, k_fs_centi_per_second, k_fs_time_second_div, and ra8_fs_datetime_t::second.

Referenced by internal_exfat_pack(), and internal_fat_pack().

◆ internal_utc_byte()

uint8_t internal_utc_byte ( int16_t minutes)
static

Encode a UTC offset in minutes as an exFAT UtcOffset byte.

exFAT spec sec 7.4.13: bit 7 is OffsetValid and bits 6:0 are the offset in 15-minute steps as a 7-bit two's-complement value. An offset that is not a whole 15-minute step, or that falls outside UTC-12:00..UTC+14:00, cannot be expressed – so it is recorded as "not valid" rather than rounded into a lie about which zone the civil time in the stamp belongs to.

Parameters
[in]minutesOffset of the stamped civil time from UTC.
Returns
The encoded byte.
Return values
k_fs_utc_unknownminutes is not representable.
0x80..0xFFOffsetValid set, with the step count in bits 6:0.
Precondition
None – every input is handled.
minutes is the offset of the civil time, not of the observer.
Postcondition
No state modified.
Either the result is k_fs_utc_unknown or bit 7 is set.
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 316 of file ra8_fs_fat_time.c.

References k_fs_utc_field_mask, k_fs_utc_span_max, k_fs_utc_span_min, k_fs_utc_step_min, k_fs_utc_unknown, and k_fs_utc_valid_bit.

Referenced by internal_exfat_pack().

◆ priv_exfat_file_set_times()

void priv_exfat_file_set_times ( uint8_t * file_entry,
const ra8_fs_datetime_t * create,
const ra8_fs_datetime_t * modify,
const ra8_fs_datetime_t * access )

Set chosen exFAT File-entry timestamps from caller-supplied readings.

The exFAT counterpart of priv_fat_entry_set_times, with the two side fields exFAT carries: create and modify each write their 32-bit timestamp, their 10 ms increment and their UtcOffset byte; access writes LastAccessedTimestamp and its UtcOffset (exFAT has no last-accessed 10 ms field). Every non-NULL reading is treated as a real instant, so its UtcOffset is honoured (OffsetValid set when the offset is a whole 15-minute step in UTC-12:00..UTC+14:00, else recorded unknown). Each reading is clamped before packing.

Parameters
[in,out]file_entry32-byte exFAT File (0x85) entry to patch in place.
[in]createCreate stamp, or NULL to leave it unchanged.
[in]modifyModify stamp, or NULL to leave it unchanged.
[in]accessAccess stamp, or NULL to leave it unchanged.
Returns
Nothing.
Precondition
file_entry is non-NULL and addresses 32 writable bytes.
file_entry is a File entry; the caller recomputes SetChecksum after.
Postcondition
Each non-NULL reading's fields hold that (clamped) instant.
No timestamp whose argument was NULL is modified.
Warning
The entry set's SetChecksum covers these bytes – recompute it after patching or the volume fails a host fsck.
Note
Callers serialise filesystem operations.
Since
0.1.0

Definition at line 573 of file ra8_fs_fat_time.c.

References exfat_stamp_t::inc10, internal_clamp_datetime(), internal_exfat_pack(), 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, priv_wr32(), exfat_stamp_t::stamp, and exfat_stamp_t::utc.

Referenced by internal_utime_exfat().

◆ priv_exfat_file_stamp_access()

void priv_exfat_file_stamp_access ( uint8_t * file_entry)

Advance only an exFAT File entry's last-accessed stamp.

The exFAT counterpart of priv_fat_entry_stamp_access, taken by the rename path for the same reason: the name changed, the bytes did not. Writes LastAccessedTimestamp and its UtcOffset byte; exFAT has no last-accessed 10ms field, so there is none to write.

Parameters
[in,out]file_entry32-byte exFAT File (0x85) entry to stamp in place.
Returns
Nothing.
Precondition
file_entry is non-NULL and addresses 32 writable bytes.
file_entry was read back from the volume as part of an entry set.
Postcondition
LastAccessedTimestamp and LastAccessedUtcOffset hold the current stamp.
The create and modify fields are unchanged.
Warning
The entry set's SetChecksum covers these bytes – recompute it after.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 532 of file ra8_fs_fat_time.c.

References internal_exfat_stamp_now(), k_exfat_off_file_atime, k_exfat_off_file_autc, priv_wr32(), exfat_stamp_t::stamp, and exfat_stamp_t::utc.

Referenced by internal_exfat_build_rename_set().

◆ priv_exfat_file_stamp_create()

void priv_exfat_file_stamp_create ( uint8_t * file_entry)

Stamp a fresh exFAT File entry's create, modify, and access fields.

The exFAT counterpart of priv_fat_entry_stamp_create, and it has three more fields to get right than FAT does: the 10ms increments that recover the odd second the 2-second stamp granularity drops, and the UtcOffset bytes that say which zone the civil time belongs to. An offset the format cannot express (not a whole 15-minute step, or outside UTC-12:00..UTC+14:00) is recorded as k_fs_utc_unknown – OffsetValid clear – rather than guessed.

Parameters
[in,out]file_entry32-byte exFAT File (0x85) entry to stamp in place.
Returns
Nothing.
Precondition
file_entry is non-NULL and addresses 32 writable bytes.
file_entry is a File entry; the caller recomputes SetChecksum after.
Postcondition
All three stamps, both 10ms fields, and all three UtcOffset bytes are set.
No byte outside offsets 8..24 is modified.
Warning
The entry set's SetChecksum covers these bytes – recompute it after stamping or the volume fails a host fsck.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 505 of file ra8_fs_fat_time.c.

References exfat_stamp_t::inc10, internal_exfat_stamp_now(), 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, priv_wr32(), exfat_stamp_t::stamp, and exfat_stamp_t::utc.

Referenced by internal_exfat_build_dir_set(), and internal_exfat_build_set().

◆ priv_exfat_file_stamp_write()

void priv_exfat_file_stamp_write ( uint8_t * file_entry)

Advance an exFAT File entry's modification (and access) stamps.

The exFAT counterpart of priv_fat_entry_stamp_write, taken by every flush of a streaming write. It moves LastModifiedTimestamp, its 10 ms increment and its UtcOffset – the three fields that together say WHEN the bytes on the card were put there – and LastAccessedTimestamp with them, because a write is an access. The creation fields are deliberately untouched: a truncate-in-place keeps a file's identity, and its birthday is part of that.

Parameters
[in,out]file_entry32-byte exFAT File (0x85) entry to stamp in place.
Returns
Nothing.
Precondition
file_entry is non-NULL and addresses 32 writable bytes.
file_entry was read back from the volume as part of an entry set.
Postcondition
The modified and accessed stamps name the current instant.
CreateTimestamp, Create10msIncrement and CreateUtcOffset are unchanged.
Warning
The entry set's SetChecksum covers these bytes – recompute it after stamping or the volume fails a host fsck.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 520 of file ra8_fs_fat_time.c.

References exfat_stamp_t::inc10, internal_exfat_stamp_now(), k_exfat_off_file_atime, k_exfat_off_file_autc, k_exfat_off_file_m10ms, k_exfat_off_file_mtime, k_exfat_off_file_mutc, priv_wr32(), exfat_stamp_t::stamp, and exfat_stamp_t::utc.

Referenced by priv_exfat_flush_set().

◆ priv_fat_entry_set_times()

void priv_fat_entry_set_times ( uint8_t * entry,
const ra8_fs_datetime_t * create,
const ra8_fs_datetime_t * modify,
const ra8_fs_datetime_t * access )

Set chosen FAT directory-entry timestamps from caller-supplied readings.

The utime primitive (ra8_fs_utime()): each non-NULL argument overwrites its timestamp fields, each NULL one leaves them exactly as they are. create writes DIR_CrtTimeTenth / DIR_CrtTime / DIR_CrtDate; modify writes DIR_WrtTime / DIR_WrtDate; access writes DIR_LstAccDate (FAT has no last-access time). Unlike the clock-driven stampers this takes explicit readings, so a backup/restore can put an original create/modify time back rather than the moment of the restore. Each reading is clamped into the on-disk range before packing, so an out-of-range field degrades to the nearest legal one and never fails the call.

Parameters
[in,out]entry32-byte directory entry to patch in place.
[in]createCreate stamp to write, or NULL to leave it unchanged.
[in]modifyModify stamp to write, or NULL to leave it unchanged.
[in]accessAccess stamp to write, or NULL to leave it unchanged.
Returns
Nothing.
Precondition
entry is non-NULL and addresses 32 writable bytes.
A non-NULL reading argument points at a readable ra8_fs_datetime_t.
Postcondition
Each non-NULL reading's fields hold that (clamped) instant.
No timestamp whose argument was NULL is modified, nor any non-time byte.
Note
Not thread-safe against ra8_fs_set_clock is irrelevant here – this reads no clock; callers still serialise filesystem operations.
Since
0.1.0

Definition at line 541 of file ra8_fs_fat_time.c.

References fat_stamp_t::date, internal_clamp_datetime(), internal_fat_pack(), 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, priv_wr16(), fat_stamp_t::tenth, and fat_stamp_t::time.

Referenced by internal_utime_fat().

◆ priv_fat_entry_stamp_access()

void priv_fat_entry_stamp_access ( uint8_t * entry)

Advance only a FAT directory entry's last-access date.

What a rename gets. A rename changes the name, not the bytes, so advancing DIR_WrtDate would tell every rsync, backup and OTA "newest image" heuristic that the contents changed – the exact class of false positive #601 exists to remove, only inverted. FAT has no separate metadata-change field, so the honest record of "this entry was touched" is the access date, which is also the only access field FAT has (there is no last-access TIME).

Parameters
[in,out]entry32-byte directory entry to stamp in place.
Returns
Nothing.
Precondition
entry is non-NULL and addresses 32 writable bytes.
entry was read back from the volume.
Postcondition
DIR_LstAccDate holds today's packed date.
Every other byte of entry is unchanged.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 497 of file ra8_fs_fat_time.c.

References fat_stamp_t::date, internal_fat_stamp_now(), k_dir_off_lst_acc_date, and priv_wr16().

Referenced by internal_fat_rename().

◆ priv_fat_entry_stamp_create()

void priv_fat_entry_stamp_create ( uint8_t * entry)

Stamp a fresh FAT directory entry's create, write, and access fields.

Reads the installed clock once (or falls back to the FAT epoch) and writes DIR_CrtTimeTenth, DIR_CrtTime, DIR_CrtDate, DIR_LstAccDate, DIR_WrtTime and DIR_WrtDate from that single reading, so a newly created file's three dates agree with each other instead of straddling a clock tick.

Parameters
[in,out]entry32-byte directory entry to stamp in place.
Returns
Nothing.
Precondition
entry is non-NULL and addresses 32 writable bytes.
The other fields of entry (name, attribute, cluster) are already set or will be set without disturbing offsets 13..25.
Postcondition
Every timestamp field of entry holds a legal calendar value.
No field outside offsets 13..25 is modified.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 474 of file ra8_fs_fat_time.c.

References fat_stamp_t::date, internal_fat_stamp_now(), 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, priv_wr16(), fat_stamp_t::tenth, and fat_stamp_t::time.

Referenced by internal_create_new(), internal_fat_mkdir(), internal_fat_put_vol_id(), and internal_pack_dot_entry().

◆ priv_fat_entry_stamp_write()

void priv_fat_entry_stamp_write ( uint8_t * entry)

Advance a FAT directory entry's modification time and access date.

The write half: DIR_WrtTime / DIR_WrtDate (what every host shows as "date modified" and what every backup and sync tool keys on) plus DIR_LstAccDate, because writing is also accessing. The create fields are left exactly as they are – a file created on a PC keeps the PC's creation date.

Parameters
[in,out]entry32-byte directory entry to stamp in place.
Returns
Nothing.
Precondition
entry is non-NULL and addresses 32 writable bytes.
entry was read back from the volume (or is being assembled for it).
Postcondition
DIR_WrtTime, DIR_WrtDate and DIR_LstAccDate hold the current stamp.
The create fields at offsets 13..17 are unchanged.
Note
Not thread-safe against ra8_fs_set_clock; install the clock first.
Since
0.1.0

Definition at line 487 of file ra8_fs_fat_time.c.

References fat_stamp_t::date, internal_fat_stamp_now(), k_dir_off_lst_acc_date, k_dir_off_wrt_date, k_dir_off_wrt_time, priv_wr16(), and fat_stamp_t::time.

Referenced by internal_close_stamp(), internal_fat_put_vol_id(), internal_fat_trunc_commit(), and internal_truncate_existing().

◆ ra8_fs_set_clock()

ra8_err_t ra8_fs_set_clock ( const ra8_fs_clock_t * clock)
nodiscard

Install (or remove) the calendar source used to stamp files.

With no binding installed – the default – every timestamp ra8_fs writes is 1980-01-01 00:00:00, the first instant both FAT and exFAT can express. That is deliberate and is NOT the same as leaving the fields zero: a FAT date of 0 encodes month 0 and day 0, and both fields are 1-based, so it is not a calendar date at all. macOS (msdosfs) reads a zero date as uninitialised and shows 31 Dec 1969, Linux clamps the fields to 1 and shows 1980-01-01, and Windows shows a blank. No host agrees, and none of them is showing a time. Writing the epoch makes every host agree.

Installing a binding replaces the default for every subsequent stamp. A now that returns anything but k_ra8_ok falls back to the epoch for that stamp only – a clock that has not been set yet must not stop a data logger from writing.

The binding is COPIED, so one built as a compound literal or on the installing function's stack stays valid; the ctx cookie remains the caller's to keep alive.

Parameters
[in]clockBinding to install, or NULL to go back to the epoch default.
Returns
ra8_err_t Error code.
Return values
k_ra8_okBinding installed, or removed when clock was NULL.
k_ra8_err_invalid_argclock is non-NULL but its now is NULL.
Precondition
No filesystem operation is in flight (this is an init-time call).
clock, when non-NULL, has a now that does not itself call into ra8_fs.
Postcondition
On k_ra8_ok every later create/write/rename stamps from clock.
On k_ra8_err_invalid_arg the previously installed binding is unchanged.
Note
Not thread-safe; install before any concurrent filesystem use.
Warning
Timestamps already on disk are not revisited. Installing a clock affects later writes only.
See also
ra8_fs_clock_t
Since
0.1.0

Definition at line 611 of file ra8_fs_fat_time.c.

References k_ra8_err_invalid_arg, k_ra8_ok, ra8_fs_clock_t::now, s_clock, and s_clock_bound.

Variable Documentation

◆ s_clock

ra8_fs_clock_t s_clock
static

The installed calendar binding; meaningful only when s_clock_bound.

A copy of the caller's ra8_fs_clock_t rather than a pointer to it, so a binding built as a compound literal or on the installing function's stack stays valid for the life of the program. The ctx cookie is still the caller's to keep alive.

Note
Written only by ra8_fs_set_clock, which is an init-time call.
Warning
Never modify directly; a half-updated binding would leave the library calling through a function pointer with the wrong cookie.
Since
0.1.0

Definition at line 68 of file ra8_fs_fat_time.c.

Referenced by internal_now_or_epoch(), ra8_fs_set_clock(), and wd_clock_fmt().

◆ s_clock_bound

bool s_clock_bound
static

True once a complete binding has been installed.

Kept separate from s_clock so "no clock" is one predictable branch rather than a NULL test on a function pointer the compiler must reload.

Note
Written only by ra8_fs_set_clock.
Warning
Never modify directly.
Since
0.1.0

Definition at line 82 of file ra8_fs_fat_time.c.

Referenced by internal_now_or_epoch(), and ra8_fs_set_clock().