|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
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"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. | |
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.
Definition in file ra8_fs_fat_lfn.c.
| 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).
Definition at line 47 of file ra8_fs_fat_lfn.c.
| 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.
|
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.
| [in] | m | Mounted volume (entries-per-sector bound). |
| [in] | needle | Requested name as UTF-16 code units. |
| [in] | nneedle | Number of units in needle. |
| [in] | buf | One whole directory sector. |
| [in] | cur_lba | LBA of buf (recorded into out_lba on hit). |
| [in,out] | lfn | Reassembly state carried across sectors. |
| [out] | out_lba | Sector of the matched 8.3 entry (on found). |
| [out] | out_entry_off | Byte offset within the sector (on found). |
| [out] | out_entry | 32 bytes of the matched 8.3 entry (on found). |
| k_lfn_scan_found | Match; out parameters populated. |
| k_lfn_scan_eod | Free-permanent marker hit; directory ended. |
| k_lfn_scan_continue | No match in this sector. |
buf holds one full sector. lfn was initialised by priv_lfn_reset() before the first sector. lfn reflects any LFN entries accumulated from this sector.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().
| 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.
| [in] | m | Mount providing geometry and backend. |
| [in] | loc | Directory to search (root or a subdirectory). |
| [in] | want | Requested name (a leading '/' is ignored). |
| [out] | out_lba | Sector containing the matched 8.3 entry. |
| [out] | out_entry_off | Byte offset within the sector. |
| [out] | out_entry | 32 bytes of the matched 8.3 directory entry. |
| k_ra8_ok | Long name matched; out parameters populated. |
| k_ra8_err_not_found | No entry's long name equals want. |
| k_ra8_err_* | Backend read error propagated from priv_read_sector(). |
want is a NUL-terminated ASCII string. want. 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().
| 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.
| [in] | m | Mount providing FAT access. |
| [in] | start | First cluster of the chain. |
| k_ra8_ok | All clusters freed. |
| k_ra8_err_protocol_error | Loop detected in chain. |
| k_ra8_err_* | Backend error. |
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().
| 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).
| [in,out] | s | Reassembly state being accumulated. |
| [in] | ent | 32-byte raw LFN directory entry (attribute byte == 0x0F). |
s is non-NULL and was initialised by priv_lfn_reset(). ent is non-NULL and points to exactly 32 valid bytes. s->units and s->checksum reflect the units from this entry. s is unchanged.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().
| 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.
| [out] | ent | 32-byte slot to fill; zeroed by this function first. |
| [in] | name | The long name, as UTF-16LE code units. |
| [in] | nlen | Its length in code units. |
| [in] | order | 1-based group index (1 = the first thirteen characters). |
| [in] | is_last | Non-zero for the LAST logical group, which is the physically FIRST slot and carries k_lfn_seq_last. |
| [in] | csum | priv_sfn_checksum() of the 8.3 entry closing the chain. |
ent and name are non-NULL; ent addresses 32 writable bytes. order is at least 1 and (order - 1) * 13 is at most nlen. ent outside the 32-byte slot is written.ent; trivially so otherwise.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().
| 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.
| [in,out] | s | Reassembly state to reset. |
s is non-NULL. s was previously initialised (e.g. via zero-init or a prior reset). s->units is all zero. s->have and s->checksum are both zero.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().
| 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.
| [in] | s | Reassembly state carried across the directory walk. |
| [in] | name83 | The 8.3 entry the chain is claimed to belong to. |
| [out] | out_units | Receives the unit count (0 when there is no name). |
| s->units | The chain is present and binds to name83. |
| nullptr | No chain, an empty one, or a checksum mismatch. |
s and out_units are non-NULL; name83 addresses 11 bytes. s was initialised by priv_lfn_reset() before the walk. s is not modified.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().
| 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.
| [in] | name83 | Pointer to the 11-byte 8.3 name field (DIR_Name in the FAT spec, space-padded, not NUL-terminated). |
| 0..255 | The folded checksum; all values are possible. |
name83 is non-NULL and points to at least 11 valid bytes. name83 are modified (read-only access).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().
|
static |
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.
Definition at line 72 of file ra8_fs_fat_lfn.c.
Referenced by priv_lfn_add(), and priv_lfn_fill_slot().