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

VFAT long-filename (LFN) entry layout: reading a chain, and filling one slot. 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_lfn.c:

Go to the source code of this file.

Enumerations

enum  ra8_fs_lfn_char_off_t : uint8_t {
  k_lfn_char_off_0 = 1U ,
  k_lfn_char_off_1 = 3U ,
  k_lfn_char_off_2 = 5U ,
  k_lfn_char_off_3 = 7U ,
  k_lfn_char_off_4 = 9U ,
  k_lfn_char_off_5 = 14U ,
  k_lfn_char_off_6 = 16U ,
  k_lfn_char_off_7 = 18U ,
  k_lfn_char_off_8 = 20U ,
  k_lfn_char_off_9 = 22U ,
  k_lfn_char_off_10 = 24U ,
  k_lfn_char_off_11 = 28U ,
  k_lfn_char_off_12 = 30U
}
 Byte offsets of the 13 UTF-16 name characters in a 32-byte LFN entry. More...
enum  ra8_fs_lfn_scan_t : uint8_t {
  k_lfn_scan_continue = 0U ,
  k_lfn_scan_found = 1U ,
  k_lfn_scan_eod = 2U
}
 Outcome of scanning one directory sector for a long-name match. More...

Functions

uint8_t priv_sfn_checksum (const uint8_t *name83)
 Compute the 8.3 short-name checksum carried in each LFN directory entry.
void priv_lfn_fill_slot (uint8_t *ent, const uint16_t *name, uint32_t nlen, uint32_t order, uint8_t is_last, uint8_t csum)
 Fill one 32-byte slot with the order -th group of a long name.
void priv_lfn_reset (lfn_state_t *s)
 Reset the LFN reassembly state so a fresh chain can start.
void priv_lfn_add (lfn_state_t *s, const uint8_t *ent)
 Fold one LFN directory entry's 13 UTF-16LE code units into the state.
const uint16_t * priv_lfn_units_for (const lfn_state_t *s, const uint8_t *name83, uint32_t *out_units)
 Code units of the chain that precedes name83, or NULL if none.
static ra8_fs_lfn_scan_t internal_dir_find_long_sector (const ra8_fs_mount_t *m, const uint16_t *needle, uint32_t nneedle, const uint8_t *buf, uint64_t cur_lba, lfn_state_t *lfn, uint64_t *out_lba, uint32_t *out_entry_off, uint8_t out_entry[k_ra8_fs_dir_entry_bytes])
 Scan one directory sector for a long-name match, updating the chain.
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).
ra8_err_t priv_free_chain (const ra8_fs_mount_t *m, uint32_t start)
 Free an entire cluster chain starting at start.

Variables

static const uint8_t s_lfn_char_off [k_lfn_chars_per_ent]
 Byte offset of each of an LFN entry's thirteen name characters.

Detailed Description

VFAT long-filename (LFN) entry layout: reading a chain, and filling one slot.

Reassembles a chain of attr-0x0F entries into a long file name and matches it during directory scans. It also owns the two things the WRITE side needs from the same layout – the 8.3 checksum that binds a chain to its entry, and priv_lfn_fill_slot(), which lays one slot out from the same character-offset table the reassembler indexes. Keeping both directions against one table is the point: a writer with its own copy of those thirteen offsets would be a second chance to disagree about where character 7 lives. The verbs that decide WHEN to write a chain live in ra8_fs_fat_lfn_write.c.

Since
0.1.0

Definition in file ra8_fs_fat_lfn.c.

Enumeration Type Documentation

◆ ra8_fs_lfn_char_off_t

enum ra8_fs_lfn_char_off_t : uint8_t

Byte offsets of the 13 UTF-16 name characters in a 32-byte LFN entry.

MS FAT spec sec 7 "Long Directory Entries": LDIR_Name1 holds five chars at offsets 1,3,5,7,9; LDIR_Name2 holds six at 14,16,18,20,22,24; LDIR_Name3 holds two at 28,30. Each char is two bytes (low byte first).

Enumerator
k_lfn_char_off_0 

LDIR_Name1 char 0.

k_lfn_char_off_1 

LDIR_Name1 char 1.

k_lfn_char_off_2 

LDIR_Name1 char 2.

k_lfn_char_off_3 

LDIR_Name1 char 3.

k_lfn_char_off_4 

LDIR_Name1 char 4.

k_lfn_char_off_5 

LDIR_Name2 char 0.

k_lfn_char_off_6 

LDIR_Name2 char 1.

k_lfn_char_off_7 

LDIR_Name2 char 2.

k_lfn_char_off_8 

LDIR_Name2 char 3.

k_lfn_char_off_9 

LDIR_Name2 char 4.

k_lfn_char_off_10 

LDIR_Name2 char 5.

k_lfn_char_off_11 

LDIR_Name3 char 0.

k_lfn_char_off_12 

LDIR_Name3 char 1.

Definition at line 47 of file ra8_fs_fat_lfn.c.

◆ ra8_fs_lfn_scan_t

enum ra8_fs_lfn_scan_t : uint8_t

Outcome of scanning one directory sector for a long-name match.

Lets priv_dir_find_long_sector report "keep walking", "found", or "end of directory reached" without unwinding the caller's loop state, keeping each function under the cognitive-complexity gate.

Enumerator
k_lfn_scan_continue 

No match in this sector; advance to the next.

k_lfn_scan_found 

Long name matched; out parameters populated.

k_lfn_scan_eod 

End-of-directory marker hit; stop the walk.

Definition at line 195 of file ra8_fs_fat_lfn.c.

Function Documentation

◆ internal_dir_find_long_sector()

ra8_fs_lfn_scan_t internal_dir_find_long_sector ( const ra8_fs_mount_t * m,
const uint16_t * needle,
uint32_t nneedle,
const uint8_t * buf,
uint64_t cur_lba,
lfn_state_t * lfn,
uint64_t * out_lba,
uint32_t * out_entry_off,
uint8_t out_entry[k_ra8_fs_dir_entry_bytes] )
static

Scan one directory sector for a long-name match, updating the chain.

Folds any LFN sub-entries into lfn and, on the trailing 8.3 entry, compares its reassembled long name against needle. The per-sector body of priv_dir_find_long, extracted so both the scan and the walk stay under the function-size / complexity gates.

Parameters
[in]mMounted volume (entries-per-sector bound).
[in]needleRequested name as UTF-16 code units.
[in]nneedleNumber of units in needle.
[in]bufOne whole directory sector.
[in]cur_lbaLBA of buf (recorded into out_lba on hit).
[in,out]lfnReassembly state carried across sectors.
[out]out_lbaSector of the matched 8.3 entry (on found).
[out]out_entry_offByte offset within the sector (on found).
[out]out_entry32 bytes of the matched 8.3 entry (on found).
Returns
Scan outcome.
Return values
k_lfn_scan_foundMatch; out parameters populated.
k_lfn_scan_eodFree-permanent marker hit; directory ended.
k_lfn_scan_continueNo match in this sector.
Precondition
All pointers are non-NULL; buf holds one full sector.
lfn was initialised by priv_lfn_reset() before the first sector.
Postcondition
On found, the out parameters identify the on-disk 8.3 entry.
lfn reflects any LFN entries accumulated from this sector.
Note
Not thread-safe; the caller serialises directory access.
Since
0.1.0

Definition at line 234 of file ra8_fs_fat_lfn.c.

References k_dir_marker_free_perm, k_dir_marker_free_used, k_dir_off_attr, k_dir_off_name, k_lfn_scan_continue, k_lfn_scan_eod, k_lfn_scan_found, k_ra8_fs_attr_lfn, k_ra8_fs_dir_entry_bytes, priv_byte_copy(), priv_dir_eps(), priv_lfn_add(), priv_lfn_reset(), priv_lfn_units_for(), and priv_utf16_ieq().

Referenced by priv_dir_find_long().

◆ priv_dir_find_long()

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

Walks the directory described by loc sector by sector, calling priv_dir_find_long_sector() on each one. LFN chains are carried across sector boundaries via an lfn_state_t accumulator. A leading '/' in want is stripped before matching. Returns on the first name that matches want via priv_name_ieq(), or reports not-found when the end-of-directory marker is reached without a hit. Used as the fallback by ra8_fs_open() when the 8.3 short-name lookup misses, so that files with names longer than 8.3 (e.g. ".epub" four-char extensions) are accessible by their real long name.

Parameters
[in]mMount providing geometry and backend.
[in]locDirectory to search (root or a subdirectory).
[in]wantRequested name (a leading '/' is ignored).
[out]out_lbaSector containing the matched 8.3 entry.
[out]out_entry_offByte offset within the sector.
[out]out_entry32 bytes of the matched 8.3 directory entry.
Returns
Error code.
Return values
k_ra8_okLong name matched; out parameters populated.
k_ra8_err_not_foundNo entry's long name equals want.
k_ra8_err_*Backend read error propagated from priv_read_sector().
Precondition
All pointer parameters are non-NULL.
want is a NUL-terminated ASCII string.
Postcondition
On k_ra8_ok, out parameters identify the on-disk 8.3 entry for want.
On failure, the out parameters are left in an unspecified state.
Note
Not thread-safe; the caller serialises directory access.
Since
0.1.0

Definition at line 275 of file ra8_fs_fat_lfn.c.

References dir_walk_t::cur_lba, internal_dir_find_long_sector(), k_lfn_scan_eod, k_lfn_scan_found, k_lfn_write_max, k_ra8_err_no_mem, k_ra8_err_not_found, k_ra8_fs_dir_entry_bytes, k_ra8_ok, priv_dir_walk_init_loc(), priv_dir_walk_next_sector(), priv_lfn_reset(), priv_read_sector(), priv_sec_walk(), and priv_utf8_to_utf16().

Referenced by internal_setattr_fat(), internal_stat_fat(), internal_utime_fat(), and priv_dir_lookup_any().

◆ priv_free_chain()

ra8_err_t priv_free_chain ( const ra8_fs_mount_t * m,
uint32_t start )

Free an entire cluster chain starting at start.

Walks the chain via priv_fat_get, marking each cluster free. A guard counter bounds the loop against on-disk loops.

Parameters
[in]mMount providing FAT access.
[in]startFirst cluster of the chain.
Returns
Error code.
Return values
k_ra8_okAll clusters freed.
k_ra8_err_protocol_errorLoop detected in chain.
k_ra8_err_*Backend error.
Precondition
m is non-NULL with a valid backend.
start is a valid cluster number or sentinel.
Postcondition
On success, every cluster in the chain has FAT entry = 0.
On failure, FAT may be partially updated.
Note
Thread-safety inherited from the backend.
Since
0.1.0

Definition at line 331 of file ra8_fs_fat_lfn.c.

References ra8_fs_mount_t::count_of_clusters, k_cluster_first_data, k_cluster_free, k_ra8_err_protocol_error, k_ra8_ok, priv_alloc_hint_lower(), priv_fat_get(), priv_fat_set(), priv_free_count_gave(), and priv_is_eoc().

Referenced by internal_dir_grow(), internal_fat_mkdir(), internal_fat_rmdir(), internal_fat_trunc_shrink(), internal_truncate_existing(), and internal_unlink_locked().

◆ priv_lfn_add()

void priv_lfn_add ( lfn_state_t * s,
const uint8_t * ent )

Fold one LFN directory entry's 13 UTF-16LE code units into the state.

Reads the sequence number from ent (low 5 bits of LDIR_Ord) to locate the unit group within the assembled name, then copies each of the 13 units at their VFAT byte offsets (LDIR_Name1/2/3) VERBATIM. A zero unit or the padding value (0xFFFF) terminates the group early. The stored checksum is updated from LDIR_Chksum. Out-of-range sequence numbers are silently ignored to tolerate a corrupt chain.

Units above 0x7F used to become ?, which made the reported name one the caller could not hand back to ra8_fs_open() – the file was listed and then unopenable, and two names differing only in an accent collided (#606).

Parameters
[in,out]sReassembly state being accumulated.
[in]ent32-byte raw LFN directory entry (attribute byte == 0x0F).
Returns
Nothing.
Precondition
s is non-NULL and was initialised by priv_lfn_reset().
ent is non-NULL and points to exactly 32 valid bytes.
Postcondition
If the sequence number is in range, s->units and s->checksum reflect the units from this entry.
If the sequence number is out of range, s is unchanged.
Note
Not thread-safe; the caller serialises directory access.
Since
0.1.0

Definition at line 141 of file ra8_fs_fat_lfn.c.

References lfn_state_t::checksum, lfn_state_t::have, k_lfn_chars_per_ent, k_lfn_max_entries, k_lfn_off_checksum, k_lfn_off_seq, k_lfn_seq_order_mask, k_lfn_unicode_pad, k_lfn_write_max, s_lfn_char_off, and lfn_state_t::units.

Referenced by internal_dir_find_long_sector(), internal_fat_dir_scan_sector(), and internal_listdir_visit_sector().

◆ priv_lfn_fill_slot()

void priv_lfn_fill_slot ( uint8_t * ent,
const uint16_t * name,
uint32_t nlen,
uint32_t order,
uint8_t is_last,
uint8_t csum )

Fill one 32-byte slot with the order -th group of a long name.

Writes the thirteen UTF-16LE code units of group order into the three character runs of an attr-0x0F entry, exactly as priv_lfn_add() reads them back. The group that ends the name carries the NUL terminator immediately after its last character and 0xFFFF in every slot past it – the padding a scanner uses to know the name stopped short of the group boundary. A name whose length is an exact multiple of thirteen therefore carries no terminator at all, which is what the specification requires and what the reassembler already handles by running out of groups.

Parameters
[out]ent32-byte slot to fill; zeroed by this function first.
[in]nameThe long name, as UTF-16LE code units.
[in]nlenIts length in code units.
[in]order1-based group index (1 = the first thirteen characters).
[in]is_lastNon-zero for the LAST logical group, which is the physically FIRST slot and carries k_lfn_seq_last.
[in]csumpriv_sfn_checksum() of the 8.3 entry closing the chain.
Returns
Nothing.
Precondition
ent and name are non-NULL; ent addresses 32 writable bytes.
order is at least 1 and (order - 1) * 13 is at most nlen.
Postcondition
ent[k_dir_off_attr] is k_ra8_fs_attr_lfn and LDIR_FstClusLO is 0.
No byte of ent outside the 32-byte slot is written.
Note
Not thread-safe against the same ent; trivially so otherwise.
Since
0.1.0

Definition at line 98 of file ra8_fs_fat_lfn.c.

References k_dir_off_attr, k_lfn_chars_per_ent, k_lfn_off_checksum, k_lfn_off_clus_lo, k_lfn_off_seq, k_lfn_off_type, k_lfn_seq_last, k_lfn_unicode_pad, k_ra8_fs_attr_lfn, k_ra8_fs_dir_entry_bytes, priv_wr16(), and s_lfn_char_off.

Referenced by priv_dir_commit().

◆ priv_lfn_reset()

void priv_lfn_reset ( lfn_state_t * s)

Reset the LFN reassembly state so a fresh chain can start.

Clears the accumulated unit array, then resets the stored checksum and the "have" flag to zero. Called at the start of a directory walk and whenever a deleted or consumed 8.3 entry breaks an in-progress chain.

Parameters
[in,out]sReassembly state to reset.
Returns
Nothing.
Precondition
s is non-NULL.
s was previously initialised (e.g. via zero-init or a prior reset).
Postcondition
s->units is all zero.
s->have and s->checksum are both zero.
Note
Not thread-safe; the caller serialises directory access.
Since
0.1.0

Definition at line 131 of file ra8_fs_fat_lfn.c.

References lfn_state_t::checksum, lfn_state_t::have, k_lfn_write_max, and lfn_state_t::units.

Referenced by internal_dir_find_long_sector(), internal_fat_dir_scan_sector(), internal_listdir_visit_sector(), and priv_dir_find_long().

◆ priv_lfn_units_for()

const uint16_t * priv_lfn_units_for ( const lfn_state_t * s,
const uint8_t * name83,
uint32_t * out_units )

Code units of the chain that precedes name83, or NULL if none.

Returns the reassembled name only when a chain was accumulated and its checksum matches name83, so a stray chain never aliases an entry. The units are returned rather than text because that is the domain a lookup compares in and the domain the up-case table folds; only the listing path converts, and only at the API boundary.

The length is the run of non-zero units, exactly as the NUL used to end the string: a group that never arrived leaves zeros, and the name stops there instead of running into another chain's characters.

Parameters
[in]sReassembly state carried across the directory walk.
[in]name83The 8.3 entry the chain is claimed to belong to.
[out]out_unitsReceives the unit count (0 when there is no name).
Returns
Pointer to the units, or nullptr.
Return values
s->unitsThe chain is present and binds to name83.
nullptrNo chain, an empty one, or a checksum mismatch.
Precondition
s and out_units are non-NULL; name83 addresses 11 bytes.
s was initialised by priv_lfn_reset() before the walk.
Postcondition
*out_units is written on both outcomes.
s is not modified.
Note
Not thread-safe; the caller serialises directory access.
Since
0.1.0

Definition at line 171 of file ra8_fs_fat_lfn.c.

References lfn_state_t::checksum, lfn_state_t::have, k_lfn_write_max, priv_sfn_checksum(), and lfn_state_t::units.

Referenced by internal_dir_find_long_sector(), internal_fat_dir_scan_sector(), and internal_listdir_visit_sector().

◆ priv_sfn_checksum()

uint8_t priv_sfn_checksum ( const uint8_t * name83)

Compute the 8.3 short-name checksum carried in each LFN directory entry.

Implements the rotate-right-add algorithm from the Microsoft FAT spec section 7 ("Long File Name Directory Entries"). Each of the eleven bytes of the 8.3 name field is folded into a running sum: the previous sum is rotated right by one bit (preserving the low bit) and the next byte is added. The result binds a set of LFN slots to their trailing 8.3 entry so that a chain cannot alias a different short name.

Parameters
[in]name83Pointer to the 11-byte 8.3 name field (DIR_Name in the FAT spec, space-padded, not NUL-terminated).
Returns
Computed 8-bit checksum.
Return values
0..255The folded checksum; all values are possible.
Precondition
name83 is non-NULL and points to at least 11 valid bytes.
The 11-byte field is the raw DIR_Name from a valid FAT directory entry.
Postcondition
Return value matches the Checksum field in every associated LFN entry.
No bytes of name83 are modified (read-only access).
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 87 of file ra8_fs_fat_lfn.c.

References k_dir_name_field_len, and k_sfn_csum_high_bit.

Referenced by priv_dir_commit(), priv_dir_erase_chain(), and priv_lfn_units_for().

Variable Documentation

◆ s_lfn_char_off

const uint8_t s_lfn_char_off[k_lfn_chars_per_ent]
static
Initial value:
= {(uint8_t)k_lfn_char_off_0,
(uint8_t)k_lfn_char_off_1,
(uint8_t)k_lfn_char_off_2,
(uint8_t)k_lfn_char_off_3,
(uint8_t)k_lfn_char_off_4,
(uint8_t)k_lfn_char_off_5,
(uint8_t)k_lfn_char_off_6,
(uint8_t)k_lfn_char_off_7,
(uint8_t)k_lfn_char_off_8,
(uint8_t)k_lfn_char_off_9,
@ k_lfn_char_off_12
LDIR_Name3 char 1.
@ k_lfn_char_off_8
LDIR_Name2 char 3.
@ k_lfn_char_off_0
LDIR_Name1 char 0.
@ k_lfn_char_off_9
LDIR_Name2 char 4.
@ k_lfn_char_off_6
LDIR_Name2 char 1.
@ k_lfn_char_off_3
LDIR_Name1 char 3.
@ k_lfn_char_off_4
LDIR_Name1 char 4.
@ k_lfn_char_off_7
LDIR_Name2 char 2.
@ k_lfn_char_off_5
LDIR_Name2 char 0.
@ k_lfn_char_off_11
LDIR_Name3 char 0.
@ k_lfn_char_off_2
LDIR_Name1 char 2.
@ k_lfn_char_off_1
LDIR_Name1 char 1.
@ k_lfn_char_off_10
LDIR_Name2 char 5.

Byte offset of each of an LFN entry's thirteen name characters.

Built from ra8_fs_lfn_char_off_t so the reassembler and the writer index the same layout from one table – a second table would be a second chance to disagree about where character 7 lives.

Note
Read-only; shared by priv_lfn_add() and priv_lfn_fill_slot().
Since
0.1.0

Definition at line 72 of file ra8_fs_fat_lfn.c.

Referenced by priv_lfn_add(), and priv_lfn_fill_slot().