|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Writing VFAT long names: reserve the slots, build the chain, take it away again. 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_lfnw_local_t : uint32_t { k_lfnw_grow_max = 3U } |
| Bounds used only by this translation unit's directory manipulation. More... | |
Functions | |
| static ra8_err_t | internal_alias_unique (const ra8_fs_mount_t *m, const dir_loc_t *loc, const uint16_t *leaf, uint32_t n, uint8_t *out11) |
| Pick the lowest ~N alias the directory does not already hold. | |
| static uint8_t | internal_slot_is_free (const uint8_t *ent) |
| Is this 32-byte slot available for a new entry? | |
| 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. | |
| static ra8_err_t | internal_dir_grow (const ra8_fs_mount_t *m, const dir_loc_t *loc) |
| Append one zeroed cluster to a directory's chain. | |
| 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. | |
| static ra8_err_t | internal_slot_advance (const ra8_fs_mount_t *m, dir_slot_t *cur, uint8_t *buf, uint64_t *lba_io) |
| Step a slot cursor forward one entry, flushing across sector edges. | |
| 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. | |
| static void | internal_run_push (dir_pos_t *run, uint32_t *len_io, uint64_t lba, uint32_t off) |
| Record one long-name slot address, keeping the newest on overflow. | |
| static ra8_err_t | internal_dir_collect_chain (const ra8_fs_mount_t *m, const dir_loc_t *loc, uint64_t tlba, uint32_t toff, uint8_t csum, dir_pos_t *run, uint32_t *out_len) |
| Collect the long-name slots that belong to one 8.3 entry. | |
| static ra8_err_t | internal_dir_erase_positions (const ra8_fs_mount_t *m, const dir_pos_t *pos, uint32_t count) |
| Mark every listed slot deleted, one sector read-modify-write at a time. | |
| 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. | |
| 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. | |
Writing VFAT long names: reserve the slots, build the chain, take it away again.
ra8_fs_fat_lfn.c has always been able to READ a long name – reassemble a run of attr-0x0F entries, check the checksum against the 8.3 entry behind them, and match it. This file is the other direction, and with it the filesystem can finally store the name the caller asked for instead of refusing it: ra8_fs_open(), ra8_fs_mkdir() and ra8_fs_rename() used to answer k_ra8_err_invalid_arg for anything that was not 8.3-representable.
Three pieces, in the order a create runs them:
The fourth piece runs in the other direction. priv_dir_erase_chain() is what stops unlink and rename leaving ORPHANS: clearing only the 8.3 entry left the whole attr-0x0F run on disk with a checksum that now matched nothing, which in-tree readers skip but fsck.fat and chkdsk both report. It walks forward to the doomed entry keeping the run of long-name slots that immediately precede it AND carry its checksum, then 0xE5s all of them together with it.
Definition in file ra8_fs_fat_lfn_write.c.
| enum ra8_fs_lfnw_local_t : uint32_t |
Bounds used only by this translation unit's directory manipulation.
k_lfnw_grow_max is the loop bound that keeps the "no run this long -- grow and look again" retry finite (NASA Power of 10 Rule 2). Two grows always suffice: the longest run this code ever asks for is k_lfn_erase_max + 1 = 21 slots, and the smallest cluster this driver mounts is one 512-byte sector = 16 slots, so a run can need at most one fresh cluster beyond a completely full one. The third attempt is slack, not a requirement.
| Enumerator | |
|---|---|
| k_lfnw_grow_max | Cluster-growth retries before reporting no_mem. |
Definition at line 63 of file ra8_fs_fat_lfn_write.c.
|
static |
Pick the lowest ~N alias the directory does not already hold.
Generates the basis name for N = 1, 2, 3 ... and looks each one up by its packed 8.3 form, stopping at the first miss. Looking the alias up (rather than only the long name) is the point: two different long names routinely produce the same basis, and an alias collision would give one file two 8.3 entries with the same name.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | loc | Directory the entry is going into. |
| [in] | leaf | Long name being filed, as UTF-16 code units. |
| [in] | n | Number of units in leaf. |
| [out] | out11 | Receives the packed 11-byte alias. |
| k_ra8_ok | An unused alias was found; out11 holds it. |
| k_ra8_err_no_mem | Every alias in the supported range is taken. |
| k_ra8_err_* | Backend read error while probing. |
out11 addresses k_max_8_3_name bytes. leaf is the unit array priv_name_classify() filled, and it reported k_name_kind_long for it. loc carries the name in out11. Definition at line 103 of file ra8_fs_fat_lfn_write.c.
References k_lfn_alias_tail_max, k_ra8_err_no_mem, k_ra8_err_not_found, k_ra8_fs_dir_entry_bytes, k_ra8_ok, priv_dir_find(), and priv_lfn_alias_basis().
Referenced by priv_dir_reserve().
|
static |
Collect the long-name slots that belong to one 8.3 entry.
Walks the directory to the entry at (tlba, toff), keeping the run of attr-0x0F slots that immediately precede it AND carry csum. Any live entry, any deleted slot, or a long-name slot with a different checksum resets the run – so a stale chain left in front of this file by some earlier deletion is not swept up with it.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | loc | Directory holding the entry. |
| [in] | tlba | Sector of the target 8.3 entry. |
| [in] | toff | Byte offset of the target within that sector. |
| [in] | csum | Checksum of the target's packed 8.3 name. |
| [out] | run | Receives up to k_lfn_erase_max slot addresses. |
| [out] | out_len | Receives how many of run were filled. |
| k_ra8_ok | The target was reached; run/. |
| k_ra8_err_not_found | The walk ended without reaching the target. |
| k_ra8_err_* | Backend read error. |
run holds k_lfn_erase_max entries. tlba, toff) came from a lookup in this same directory. run holds only slots that precede the target. Definition at line 492 of file ra8_fs_fat_lfn_write.c.
References dir_walk_t::cur_lba, internal_run_push(), k_dir_marker_free_used, k_dir_off_attr, k_dir_off_name, k_lfn_off_checksum, k_ra8_err_not_found, k_ra8_fs_attr_lfn, 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(), and priv_sec_walk().
Referenced by priv_dir_erase_chain().
|
static |
Mark every listed slot deleted, one sector read-modify-write at a time.
The addresses arrive in increasing order, so a single sector buffer is enough: it is written back only when the next address moves to a different sector, which for the usual chain means one read and one write for the whole deletion.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | pos | Slot addresses, in increasing sector order. |
| [in] | count | How many; at least 1. |
| k_ra8_ok | Every listed slot now reads 0xE5. |
| k_ra8_err_* | Backend read/write error. |
m and pos are non-NULL; count is at least 1. Definition at line 560 of file ra8_fs_fat_lfn_write.c.
References k_dir_marker_free_used, k_dir_off_name, k_ra8_ok, dir_pos_t::lba, dir_pos_t::off, priv_read_sector(), priv_sec_walk(), and priv_write_sector().
Referenced by priv_dir_erase_chain().
|
static |
Append one zeroed cluster to a directory's chain.
Walks to the chain's last cluster with the shared directory iterator, allocates a fresh cluster, zeroes every sector of it so it reads as end-of-directory, and only then links it in – a directory is never visible in a half-initialised state. A FAT12/16 volume root has no chain to extend and is refused.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | loc | Directory to extend. |
| k_ra8_ok | The directory is one cluster longer. |
| k_ra8_err_no_mem | A FAT12/16 fixed root, or the volume is full. |
| k_ra8_err_protocol_error | The existing chain revisits a cluster. |
| k_ra8_err_* | Backend or FAT error. |
m and loc are non-NULL; m is a mounted FAT volume. loc names a directory that exists on m. Definition at line 235 of file ra8_fs_fat_lfn_write.c.
References dir_walk_t::cluster, dir_loc_t::is_root, k_ra8_err_no_mem, k_ra8_fs_type_fat32, k_ra8_ok, k_zero_sector, priv_alloc_eoc_cluster(), priv_cluster_to_lba(), priv_dir_walk_init_loc(), priv_dir_walk_next_sector(), priv_fat_set(), priv_free_chain(), priv_write_sector(), ra8_fs_mount_t::sectors_per_cluster, and ra8_fs_mount_t::type.
Referenced by priv_dir_reserve().
|
static |
Record one long-name slot address, keeping the newest on overflow.
A legal chain is at most k_lfn_erase_max entries, so the shift only runs on a corrupt directory. Keeping the entries CLOSEST to the 8.3 entry is the safe choice there: those are the ones the checksum test has most recently confirmed belong to it.
| [in,out] | run | Array of at least k_lfn_erase_max positions. |
| [in,out] | len_io | Current length; incremented until the cap is reached. |
| [in] | lba | Sector of the slot to record. |
| [in] | off | Byte offset of the slot within that sector. |
run and len_io are non-NULL; *len_io is at most the cap. off is a valid entry offset inside one sector. Definition at line 446 of file ra8_fs_fat_lfn_write.c.
References k_lfn_erase_max.
Referenced by internal_dir_collect_chain().
|
static |
Step a slot cursor forward one entry, flushing across sector edges.
The write buffer holds one sector; crossing into the next one means committing what is in it first, because the run being written may already have modified this sector's earlier slots.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in,out] | cur | Cursor to advance. |
| [in,out] | buf | Sector buffer; written out and reloaded on a crossing. |
| [in,out] | lba_io | Holds the buffer's LBA; updated on a crossing. |
| k_ra8_ok | The cursor addresses the next slot. |
| k_ra8_err_no_mem | The directory ended mid-run (corrupt reservation). |
| k_ra8_err_* | Backend read/write error. |
buf holds the sector at *lba_io. cur is positioned inside the reserved run. buf holds the sector cur now points into. Definition at line 350 of file ra8_fs_fat_lfn_write.c.
References dir_walk_t::cur_lba, dir_slot_t::ent, k_ra8_err_no_mem, k_ra8_ok, priv_dir_eps(), priv_dir_walk_next_sector(), priv_read_sector(), priv_write_sector(), and dir_slot_t::w.
Referenced by priv_dir_commit().
|
static |
Is this 32-byte slot available for a new entry?
Both free markers count: 0xE5 is a slot whose entry was deleted, and 0x00 is the end-of-directory marker, past which every slot in the allocated directory space is unused. Writing into a run that starts at 0x00 is safe because the slot after the run keeps its own 0x00, which is what still terminates the directory.
| [in] | ent | 32-byte directory slot. |
| 1U | The slot may be overwritten. |
| 0U | The slot holds a live entry. |
ent addresses 32 readable bytes. ent was loaded from a directory sector. Definition at line 158 of file ra8_fs_fat_lfn_write.c.
References k_dir_marker_free_perm, k_dir_marker_free_used, and k_dir_off_name.
Referenced by priv_dir_find_free_run().
| 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.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | plan | Reservation produced by priv_dir_reserve(). |
| [in] | tmpl | 32-byte entry template; name and NTRes are overwritten. |
| [out] | out_lba | Sector the 8.3 entry landed in. |
| [out] | out_off | Byte offset of the 8.3 entry within that sector. |
| k_ra8_ok | The entry (and any chain) is on disk. |
| k_ra8_err_no_mem | The reserved run ran past the end of the directory. |
| k_ra8_err_* | Backend read/write error. |
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. 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().
| 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.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | loc | Directory holding the entry. |
| [in] | lba | Sector of the 8.3 entry. |
| [in] | off | Byte offset of the 8.3 entry within that sector. |
| [in] | name83 | The entry's packed 11-byte name (its checksum source). |
| k_ra8_ok | Entry and chain marked deleted. |
| k_ra8_err_not_found | The walk did not reach (lba, off). |
| k_ra8_err_* | Backend read/write error. |
lba, off) name an entry in loc. name83 is the name field of the entry at (lba, off). loc references that entry. 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().
| 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.
| [in] | m | Mount providing geometry and backend. |
| [in] | loc | Directory to search (root or a subdirectory). |
| [in] | need | Consecutive slots required; at least 1. |
| [out] | out | Receives a cursor addressing the run's first slot. |
| k_ra8_ok | Run found; out is positioned on it. |
| k_ra8_err_no_mem | The directory holds no run that long. |
| k_ra8_err_* | Backend error. |
need is at least 1. m is mounted with valid geometry. 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().
| 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.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | loc | Directory to search. |
| [in] | leaf | Leaf component (no slashes). |
| [out] | out_lba | Sector containing the matched 8.3 entry. |
| [out] | out_off | Byte offset within that sector. |
| [out] | out_entry | 32 bytes of the matched entry. |
| k_ra8_ok | Found; out parameters populated. |
| k_ra8_err_not_found | Neither lookup matched. |
| k_ra8_err_* | Backend error. |
loc names a directory that exists on m. 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().
| 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.
| [in] | m | Mounted FAT12/16/32 volume. |
| [in] | loc | Directory the entry is going into. |
| [in] | leaf | Leaf component (no slashes); must outlive the commit. |
| [out] | out | Receives the reservation. |
| k_ra8_ok | Slots reserved; out is ready to commit. |
| k_ra8_err_invalid_arg | leaf is empty, over-long, or illegal. |
| k_ra8_err_no_mem | No run long enough and the directory cannot grow. |
| k_ra8_err_* | Backend or FAT error. |
m is a mounted FAT volume. loc names a directory that exists on m. loc carries out->name83. 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().