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

Cross-TU prototypes of the VFAT long-name WRITE seam. More...

Include dependency graph for ra8_fs_fat_lfn_write_internal.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ra8_fs_name_kind_t priv_name_classify (const char *leaf, uint16_t *out_units, uint32_t *out_nunits, uint8_t *out83, uint8_t *out_ntres)
 Decide whether a leaf needs one entry, one entry plus case flags, or a chain.
void priv_lfn_alias_basis (const uint16_t *leaf, uint32_t n, uint32_t tail, uint8_t *out11)
 Derive the LONGNA~N.TXT 8.3 alias a long name is filed under.
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.
ra8_err_t priv_dir_find_free_run (const ra8_fs_mount_t *m, const dir_loc_t *loc, uint32_t need, dir_slot_t *out)
 Locate need consecutive free entry slots in a given directory.
ra8_err_t priv_dir_reserve (const ra8_fs_mount_t *m, const dir_loc_t *loc, const char *leaf, dir_insert_t *out)
 Decide how a name will be stored and set aside the slots for it.
ra8_err_t priv_dir_commit (const ra8_fs_mount_t *m, const dir_insert_t *plan, const uint8_t *tmpl, uint64_t *out_lba, uint32_t *out_off)
 Write a reserved run: the long-name chain, then the 8.3 entry.
ra8_err_t priv_dir_lookup_any (const ra8_fs_mount_t *m, const dir_loc_t *loc, const char *leaf, uint64_t *out_lba, uint32_t *out_off, uint8_t out_entry[k_ra8_fs_dir_entry_bytes])
 Resolve one leaf name by 8.3 first and by long name second.
ra8_err_t priv_dir_erase_chain (const ra8_fs_mount_t *m, const dir_loc_t *loc, uint64_t lba, uint32_t off, const uint8_t *name83)
 Delete a directory entry together with its long-name chain.

Detailed Description

Cross-TU prototypes of the VFAT long-name WRITE seam.

The adapter's general cross-TU prototypes live in the two alphabetical halves ra8_fs_fat_protos_a_internal.h and ra8_fs_fat_protos_b_internal.h. The helpers declared here are kept apart from those because they are one feature rather than one letter range: everything open, mkdir, rename, unlink and rmdir need in order to FILE a name that is not 8.3-representable, find it again, and take it away without leaving the chain behind. Splitting them out also keeps the two halves inside the 1000-line source cap, which they were already close to.

The seam spans three translation units, so the declarations cannot live in any one of them:

Aggregated by the ra8_fs_fat_internal.h umbrella, like the other two.

Since
0.1.0

Definition in file ra8_fs_fat_lfn_write_internal.h.

Function Documentation

◆ priv_dir_commit()

ra8_err_t priv_dir_commit ( const ra8_fs_mount_t * m,
const dir_insert_t * plan,
const uint8_t * tmpl,
uint64_t * out_lba,
uint32_t * out_off )

Write a reserved run: the long-name chain, then the 8.3 entry.

Fills the slots priv_dir_reserve() set aside. The chain is written back to front – the physically first slot carries the HIGHEST order number with the 0x40 "last logical group" flag – because that is the order a scanner reassembles them in, and every slot carries the checksum of the 8.3 name that closes the run. The 8.3 entry itself is tmpl with its name and DIR_NTRes fields replaced, so the caller decides the attribute, the first cluster and the size, and rename can carry an existing entry across unchanged.

Parameters
[in]mMounted FAT12/16/32 volume.
[in]planReservation produced by priv_dir_reserve().
[in]tmpl32-byte entry template; name and NTRes are overwritten.
[out]out_lbaSector the 8.3 entry landed in.
[out]out_offByte offset of the 8.3 entry within that sector.
Returns
Error code.
Return values
k_ra8_okThe entry (and any chain) is on disk.
k_ra8_err_no_memThe reserved run ran past the end of the directory.
k_ra8_err_*Backend read/write error.
Precondition
All pointers are non-NULL; tmpl addresses 32 readable bytes.
plan came from a priv_dir_reserve() that returned k_ra8_ok, and the directory has not been modified since.
Postcondition
On success a scan of the directory finds the new name.
On a mid-run failure the directory may hold a partial chain, which a scanner skips because it is not closed by a matching 8.3 entry.
Note
A run that fits inside one sector – which is every name of 15 groups or fewer that does not straddle a boundary, so nearly all of them – reaches the backend as a SINGLE write. The chain and the entry that closes it then appear together or not at all, which is the best any FAT directory update can do: the format has no journal, and a run spanning two sectors is two writes with a window between them.
Not thread-safe; callers serialise directory access.
Since
0.1.0

Definition at line 374 of file ra8_fs_fat_lfn_write.c.

References dir_walk_t::cur_lba, dir_slot_t::ent, internal_slot_advance(), k_dir_name_field_len, k_dir_off_name, k_dir_off_ntres, k_ra8_fs_dir_entry_bytes, k_ra8_ok, dir_insert_t::lfn_entries, dir_insert_t::name83, dir_insert_t::ntres, dir_insert_t::nunits, priv_byte_copy(), priv_lfn_fill_slot(), priv_read_sector(), priv_sec_walk(), priv_sfn_checksum(), priv_write_sector(), dir_insert_t::start, dir_insert_t::units, and dir_slot_t::w.

Referenced by internal_create_new(), internal_fat_mkdir(), and internal_fat_rename().

◆ priv_dir_erase_chain()

ra8_err_t priv_dir_erase_chain ( const ra8_fs_mount_t * m,
const dir_loc_t * loc,
uint64_t lba,
uint32_t off,
const uint8_t * name83 )

Delete a directory entry together with its long-name chain.

Marking only the 8.3 entry 0xE5 – which is all unlink used to do – leaves the attr-0x0F slots in front of it on disk, pointing at a checksum nothing answers to any more. This driver's own reader skips them, but fsck.fat and chkdsk report them as orphaned long-name entries, so the chain is taken away with the entry it belonged to. Only slots that are contiguous with the entry AND carry the checksum of name83 are touched, so an unrelated chain left in front of it by an earlier deletion is not swept up as well.

Parameters
[in]mMounted FAT12/16/32 volume.
[in]locDirectory holding the entry.
[in]lbaSector of the 8.3 entry.
[in]offByte offset of the 8.3 entry within that sector.
[in]name83The entry's packed 11-byte name (its checksum source).
Returns
Error code.
Return values
k_ra8_okEntry and chain marked deleted.
k_ra8_err_not_foundThe walk did not reach (lba, off).
k_ra8_err_*Backend read/write error.
Precondition
All pointers are non-NULL; (lba, off) name an entry in loc.
name83 is the name field of the entry at (lba, off).
Postcondition
On success no long-name slot in loc references that entry.
The entry's cluster chain is NOT touched; freeing it is the caller's job.
Note
Not thread-safe; callers serialise directory access.
Since
0.1.0

Definition at line 586 of file ra8_fs_fat_lfn_write.c.

References internal_dir_collect_chain(), internal_dir_erase_positions(), k_lfn_erase_max, k_ra8_ok, and priv_sfn_checksum().

Referenced by internal_fat_rename(), internal_fat_rmdir(), and internal_unlink_locked().

◆ priv_dir_find_free_run()

ra8_err_t priv_dir_find_free_run ( const ra8_fs_mount_t * m,
const dir_loc_t * loc,
uint32_t need,
dir_slot_t * out )

Locate need consecutive free entry slots in a given directory.

Walks loc and returns a cursor on the first slot of the first run of need slots whose name field is 0x00 (never used) or 0xE5 (deleted). A long name occupies its chain and its 8.3 entry in one unbroken run, which is why the free slots have to be found together rather than one at a time; need == 1 is the ordinary short-name case and behaves like a first-free-slot search.

Parameters
[in]mMount providing geometry and backend.
[in]locDirectory to search (root or a subdirectory).
[in]needConsecutive slots required; at least 1.
[out]outReceives a cursor addressing the run's first slot.
Returns
Error code.
Return values
k_ra8_okRun found; out is positioned on it.
k_ra8_err_no_memThe directory holds no run that long.
k_ra8_err_*Backend error.
Precondition
All pointers are non-NULL and need is at least 1.
m is mounted with valid geometry.
Postcondition
On success every slot of the run is free.
No on-disk state is modified.
Note
Thread-safety inherited from the backend.
Since
0.1.0

Definition at line 168 of file ra8_fs_fat_lfn_write.c.

References dir_walk_t::cur_lba, dir_slot_t::ent, internal_slot_is_free(), k_ra8_err_no_mem, k_ra8_fs_dir_entry_bytes, k_ra8_ok, priv_dir_eps(), priv_dir_walk_init_loc(), priv_dir_walk_next_sector(), priv_read_sector(), priv_sec_walk(), and dir_slot_t::w.

Referenced by priv_dir_reserve().

◆ priv_dir_lookup_any()

ra8_err_t priv_dir_lookup_any ( const ra8_fs_mount_t * m,
const dir_loc_t * loc,
const char * leaf,
uint64_t * out_lba,
uint32_t * out_off,
uint8_t out_entry[k_ra8_fs_dir_entry_bytes] )

Resolve one leaf name by 8.3 first and by long name second.

The two-step every verb has to use, in one place so they cannot drift apart: pack the leaf to 8.3 and look that up, and if it is not representable – or the lookup misses because the file is filed under a generated ~N alias – reassemble the directory's VFAT chains and match the long name instead.

Parameters
[in]mMounted FAT12/16/32 volume.
[in]locDirectory to search.
[in]leafLeaf component (no slashes).
[out]out_lbaSector containing the matched 8.3 entry.
[out]out_offByte offset within that sector.
[out]out_entry32 bytes of the matched entry.
Returns
Error code.
Return values
k_ra8_okFound; out parameters populated.
k_ra8_err_not_foundNeither lookup matched.
k_ra8_err_*Backend error.
Precondition
All pointers are non-NULL.
loc names a directory that exists on m.
Postcondition
On success the out parameters identify one on-disk 8.3 entry.
No on-disk state is modified.
Note
Not thread-safe; callers serialise directory access.
Since
0.1.0

Definition at line 605 of file ra8_fs_fat_lfn_write.c.

References k_max_8_3_name, k_ra8_err_not_found, k_ra8_fs_dir_entry_bytes, priv_dir_find(), priv_dir_find_long(), and priv_path_to_83().

Referenced by internal_enter_subdir(), internal_fat_mkdir(), internal_fat_rename(), internal_rmdir_locate(), internal_unlink_locate(), and priv_open_locked().

◆ priv_dir_reserve()

ra8_err_t priv_dir_reserve ( const ra8_fs_mount_t * m,
const dir_loc_t * loc,
const char * leaf,
dir_insert_t * out )

Decide how a name will be stored and set aside the slots for it.

Classifies leaf, generates a unique ~N alias when it needs a long-name chain, and reserves a run of free slots long enough for the chain plus its 8.3 entry – growing the directory by a cluster when no run is long enough, which a FAT12/16 fixed root cannot do. Nothing is written: this is the half that is allowed to fail, so a caller that must allocate something first (mkdir needs a cluster for the new directory) can fail before it does.

Parameters
[in]mMounted FAT12/16/32 volume.
[in]locDirectory the entry is going into.
[in]leafLeaf component (no slashes); must outlive the commit.
[out]outReceives the reservation.
Returns
Error code.
Return values
k_ra8_okSlots reserved; out is ready to commit.
k_ra8_err_invalid_argleaf is empty, over-long, or illegal.
k_ra8_err_no_memNo run long enough and the directory cannot grow.
k_ra8_err_*Backend or FAT error.
Precondition
All pointers are non-NULL; m is a mounted FAT volume.
loc names a directory that exists on m.
Postcondition
On success no other entry in loc carries out->name83.
On failure no directory entry has been written.
Note
Not thread-safe; callers serialise directory access.
Since
0.1.0

Definition at line 283 of file ra8_fs_fat_lfn_write.c.

References internal_alias_unique(), internal_dir_grow(), k_lfn_chars_per_ent, k_lfnw_grow_max, k_name_kind_invalid, k_name_kind_long, k_ra8_err_invalid_arg, k_ra8_err_no_mem, k_ra8_ok, dir_insert_t::lfn_entries, dir_insert_t::name83, dir_insert_t::ntres, dir_insert_t::nunits, priv_dir_find_free_run(), priv_name_classify(), dir_insert_t::start, and dir_insert_t::units.

Referenced by internal_create_new(), internal_fat_mkdir(), and internal_fat_rename().

◆ priv_lfn_alias_basis()

void priv_lfn_alias_basis ( const uint16_t * leaf,
uint32_t n,
uint32_t tail,
uint8_t * out11 )

Derive the LONGNA~N.TXT 8.3 alias a long name is filed under.

The Microsoft FAT specification's basis-name generation: spaces and all but the last interior dot are dropped, the remainder is upper-cased with anything an 8.3 field cannot hold replaced by _, and the base is truncated to leave room for ~ plus the decimal tail. A leaf that contributes no base characters at all ("...") yields _, because an 8.3 name may not have an empty base.

Every unit above ASCII maps to _, which is what VFAT does with a character its OEM code page cannot express. Working from CODE UNITS rather than UTF-8 bytes is what makes that one underscore per character instead of one per byte.

Parameters
[in]leafLong name being filed, as UTF-16 code units.
[in]nNumber of units in leaf.
[in]tailSequence number, 1..k_lfn_alias_tail_max.
[out]out11Receives the packed, space-padded 11-byte alias.
Returns
Nothing.
Precondition
leaf and out11 are non-NULL; out11 holds k_max_8_3_name bytes.
tail is at least 1 and at most k_lfn_alias_tail_max.
Postcondition
Every byte of out11 is a legal 8.3 character or a space pad.
out11 always contains a ~ followed by tail in decimal.
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 769 of file ra8_fs_fat_name.c.

References internal_alias_collect(), internal_alias_digits(), internal_alias_ext_dot(), k_alias_radix, k_filename_base_len, k_filename_ext_len, and k_max_8_3_name.

Referenced by internal_alias_unique().

◆ 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_name_classify()

ra8_fs_name_kind_t priv_name_classify ( const char * leaf,
uint16_t * out_units,
uint32_t * out_nunits,
uint8_t * out83,
uint8_t * out_ntres )

Decide whether a leaf needs one entry, one entry plus case flags, or a chain.

Decodes leaf from UTF-8 into the code units the on-disk chain carries, which is where a malformed encoding is refused – an over-long form, a raw surrogate, a truncated sequence – and then rejects anything a long name may not hold at all (an empty leaf, one longer than k_lfn_write_max CODE UNITS, a control character, or one of the FAT specification's illegal characters). Characters above ASCII are legal: the slots are UTF-16 and the reader returns them as such. What is left is an 8.3 name if it has a 1..8 character base, a 0 or 1..3 character extension, at most one dot, and no character an 8.3 field cannot hold – a space and a second dot are both enough to disqualify it however short it is. An 8.3 name whose base or extension is all one case round-trips through DIR_NTRes; one that MIXES cases in either half does not, and is reported as needing a chain.

Parameters
[in]leafLeaf component (no slashes), NUL-terminated UTF-8.
[out]out_unitsReceives leaf as UTF-16LE code units.
[out]out_nunitsReceives how many units that is.
[out]out83Receives the packed 11-byte name on a short verdict.
[out]out_ntresReceives the DIR_NTRes case flags (0 unless short).
Returns
The on-disk shape required.
Return values
k_name_kind_shortout83 and out_ntres are ready to write.
k_name_kind_longA chain plus a generated alias is required.
k_name_kind_invalidEmpty, over-long, illegal, or not valid UTF-8.
Precondition
Every pointer is non-NULL.
out_units addresses k_lfn_write_max writable units and out83 addresses k_max_8_3_name writable bytes.
Postcondition
out_ntres and out_nunits are written on every outcome.
leaf is not modified.
Note
Pure function; trivially thread-safe.
Since
0.1.0

Definition at line 571 of file ra8_fs_fat_name.c.

References internal_name_case_kind(), internal_name_is_83(), internal_unit_is_lfn_legal(), k_lfn_write_max, k_name_kind_invalid, k_name_kind_long, k_ra8_ok, priv_path_to_83(), and priv_utf8_to_utf16().

Referenced by priv_dir_reserve().

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