ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_dirmk.c
Go to the documentation of this file.
1
22
23#include <stddef.h>
24#include <stdint.h>
25
26#include "ra8_attributes.h"
27#include "ra8_fs.h"
28#include "ra8_fs_fat_internal.h"
29
54static void internal_pack_dot_entry(uint8_t* ent, uint32_t dots, uint32_t cluster)
55{
56 for (uint32_t i = 0; i < (uint32_t)k_ra8_fs_dir_entry_bytes; i++) {
57 ent[i] = 0;
58 }
59 for (uint32_t i = 0; i < (uint32_t)k_dir_name_field_len; i++) {
60 ent[i] = ' ';
61 }
62 for (uint32_t i = 0; i < dots; i++) {
63 ent[i] = '.';
64 }
66 priv_entry_set_cluster_size(ent, cluster, 0U);
67 /* "." and ".." are ordinary directory entries to every host, and every mkfs
68 * stamps them. Leaving them at zero would put an illegal date inside a
69 * directory this firmware had just created (#601). */
71}
72
98static ra8_err_t
99internal_dir_cluster_init(const ra8_fs_mount_t* m, uint32_t new_cluster, uint32_t parent_cluster)
100{
101 uint8_t* const buf = priv_sec_walk();
102 priv_byte_fill(buf, 0U, priv_bps(m));
103 internal_pack_dot_entry(&buf[0], 1U, new_cluster);
104 internal_pack_dot_entry(&buf[k_ra8_fs_dir_entry_bytes], 2U, parent_cluster);
105 const uint64_t base = priv_cluster_to_lba(m, new_cluster);
106 ra8_err_t err = priv_write_sector(m, base, buf);
107 if (err != k_ra8_ok) {
108 return err;
109 }
110 for (uint32_t s = 1; s < m->sectors_per_cluster; s++) {
111 err = priv_write_sector(m, base + s, k_zero_sector);
112 if (err != k_ra8_ok) {
113 return err;
114 }
115 }
116 return k_ra8_ok;
117}
118
148static ra8_err_t internal_fat_mkdir(const ra8_fs_mount_t* handle, const char* path)
149{
150 dir_loc_t parent = {};
151 const char* leaf = nullptr;
152 const ra8_err_t rerr = priv_resolve_parent(handle, path, &parent, &leaf);
153 if (rerr != k_ra8_ok) {
154 return rerr;
155 }
156 uint64_t lba = 0;
157 uint32_t off = 0;
158 uint8_t entry[k_ra8_fs_dir_entry_bytes] = {};
159 /* By long name as well as by 8.3: a second `mkdir("/Reading List")` has to
160 * be refused, and the alias probe behind `priv_dir_reserve()` would happily
161 * file a second copy as `READIN~2` if it were not. */
162 if (priv_dir_lookup_any(handle, &parent, leaf, &lba, &off, entry) == k_ra8_ok) {
163 return k_ra8_err_exists;
164 }
165 dir_insert_t plan = {};
166 ra8_err_t err = priv_dir_reserve(handle, &parent, leaf, &plan);
167 if (err != k_ra8_ok) {
168 return err;
169 }
170 uint32_t new_cluster = 0;
171 err = priv_alloc_eoc_cluster(handle, &new_cluster);
172 if (err != k_ra8_ok) {
173 return err;
174 }
175 const uint32_t parent_cluster = (parent.is_root != 0U) ? 0U : parent.cluster;
176 err = internal_dir_cluster_init(handle, new_cluster, parent_cluster);
177 if (err != k_ra8_ok) {
178 (void)priv_free_chain(handle, new_cluster);
179 return err;
180 }
181 uint8_t tmpl[k_ra8_fs_dir_entry_bytes] = {};
183 priv_entry_set_cluster_size(tmpl, new_cluster, 0U);
184 /* Same reasoning as the file case: a zero FAT date is illegal, and the
185 * directory's own "." and ".." were stamped when the cluster was built, so
186 * the entry in the parent must agree with them (#601). */
188 err = priv_dir_commit(handle, &plan, tmpl, &lba, &off);
189 if (err != k_ra8_ok) {
190 (void)priv_free_chain(handle, new_cluster);
191 return err;
192 }
193 return k_ra8_ok;
194}
195
222RA8_EXPECTS_LOCK("ra8_fs_lock")
223static ra8_err_t internal_mkdir_locked(const ra8_fs_mount_t* handle, const char* path)
224{
225 if (handle == nullptr) {
226 return k_ra8_err_null_ptr;
227 }
228 if (path == nullptr) {
229 return k_ra8_err_null_ptr;
230 }
231 if (handle->in_use == 0U) {
232 return k_ra8_err_invalid_state;
233 }
234 if (handle->type == k_ra8_fs_type_exfat) {
235 return priv_exfat_mkdir(handle, path);
236 }
237 return internal_fat_mkdir(handle, path);
238}
239
252typedef enum : uint8_t {
256} dir_scan_t;
257
288static dir_scan_t internal_rmdir_scan_sector(const ra8_fs_mount_t* m, const uint8_t* buf)
289{
290 for (uint32_t e = 0; e < priv_dir_eps(m); e++) {
291 const uint8_t* ent = &buf[(size_t)e * (size_t)k_ra8_fs_dir_entry_bytes];
293 return k_dir_scan_empty;
294 }
296 continue;
297 }
298 if (ent[k_dir_off_attr] == k_ra8_fs_attr_lfn) {
299 continue;
300 }
301 if (ent[k_dir_off_name] == k_dir_marker_dot) {
302 continue;
303 }
304 return k_dir_scan_used;
305 }
306 return k_dir_scan_more;
307}
308
337static ra8_err_t
338internal_dir_is_empty(const ra8_fs_mount_t* m, uint32_t cluster, uint8_t* out_empty)
339{
340 const dir_loc_t loc = {.is_root = 0U, .cluster = cluster};
341 dir_walk_t w = {};
342 priv_dir_walk_init_loc(m, &loc, &w);
343 uint8_t eod = 0;
344 uint8_t* const buf = priv_sec_walk();
345 while (eod == 0U) {
346 ra8_err_t err = priv_read_sector(m, w.cur_lba, buf);
347 if (err != k_ra8_ok) {
348 return err;
349 }
350 const dir_scan_t verdict = internal_rmdir_scan_sector(m, buf);
351 if (verdict == k_dir_scan_used) {
352 *out_empty = 0U;
353 return k_ra8_ok;
354 }
355 if (verdict == k_dir_scan_empty) {
356 *out_empty = 1U;
357 return k_ra8_ok;
358 }
359 err = priv_dir_walk_next_sector(m, &w, &eod);
360 if (err != k_ra8_ok) {
361 return err;
362 }
363 }
364 *out_empty = 1U; /* chain exhausted with no occupant and no 0x00 marker */
365 return k_ra8_ok;
366}
367
400 const char* path,
401 dir_target_t* out,
402 uint32_t* out_cluster)
403{
404 const char* leaf = nullptr;
405 const ra8_err_t rerr = priv_resolve_parent(handle, path, &out->parent, &leaf);
406 if (rerr != k_ra8_ok) {
407 return rerr;
408 }
409 if (leaf[0] == '\0') {
410 return k_ra8_err_invalid_arg; /* "/" or a trailing slash: the root itself */
411 }
412 const ra8_err_t err =
413 priv_dir_lookup_any(handle, &out->parent, leaf, &out->lba, &out->off, out->entry);
414 if (err != k_ra8_ok) {
415 return err;
416 }
417 if ((out->entry[k_dir_off_attr] & (uint8_t)k_ra8_fs_attr_directory) == 0U) {
418 return k_ra8_err_invalid_arg; /* a file: ra8_fs_unlink() is the verb for it */
419 }
420 *out_cluster = priv_entry_first_cluster(out->entry);
421 if (*out_cluster < (uint32_t)k_cluster_first_data) {
422 return k_ra8_err_protocol_error; /* a directory always owns a cluster */
423 }
424 return k_ra8_ok;
425}
426
459static ra8_err_t internal_fat_rmdir(const ra8_fs_mount_t* handle, const char* path)
460{
461 dir_target_t t = {};
462 uint32_t cluster = 0;
463 ra8_err_t err = internal_rmdir_locate(handle, path, &t, &cluster);
464 if (err != k_ra8_ok) {
465 return err;
466 }
467 uint8_t empty = 0;
468 err = internal_dir_is_empty(handle, cluster, &empty);
469 if (err != k_ra8_ok) {
470 return err;
471 }
472 if (empty == 0U) {
473 return k_ra8_err_not_empty;
474 }
475 err = priv_free_chain(handle, cluster);
476 if (err != k_ra8_ok) {
477 return err;
478 }
479 return priv_dir_erase_chain(handle, &t.parent, t.lba, t.off, &t.entry[k_dir_off_name]);
480}
481
512RA8_EXPECTS_LOCK("ra8_fs_lock")
513static ra8_err_t internal_rmdir_locked(const ra8_fs_mount_t* handle, const char* path)
514{
515 if (handle == nullptr) {
516 return k_ra8_err_null_ptr;
517 }
518 if (path == nullptr) {
519 return k_ra8_err_null_ptr;
520 }
521 if (handle->in_use == 0U) {
522 return k_ra8_err_invalid_state;
523 }
524 if (handle->type == k_ra8_fs_type_exfat) {
525 return priv_exfat_rmdir(handle, path);
526 }
527 return internal_fat_rmdir(handle, path);
528}
529
530/* =============================================================================
531 * Public entry points -- the lock brackets
532 * =============================================================================
533 */
534
535RA8_OWNS_RESOURCE("ra8_fs_lock")
536ra8_err_t ra8_fs_mkdir(const ra8_fs_mount_t* handle, const char* path)
537{
539 const ra8_err_t err = internal_mkdir_locked(handle, path);
541 return err;
542}
543
544RA8_OWNS_RESOURCE("ra8_fs_lock")
545ra8_err_t ra8_fs_rmdir(const ra8_fs_mount_t* handle, const char* path)
546{
548 const ra8_err_t err = internal_rmdir_locked(handle, path);
550 return err;
551}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_OWNS_RESOURCE(kind)
RAII-style resource ownership contract.
#define RA8_EXPECTS_LOCK(name)
The function expects the named thread/IRQ lock to be held on entry.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_not_empty
Container still holds members – the operation requires it empty.
Definition ra8_err.h:258
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_protocol_error
Protocol-level error (e.g.
Definition ra8_err.h:429
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
ra8_err_t ra8_fs_mkdir(const ra8_fs_mount_t *handle, const char *path)
Create a directory at path.
ra8_err_t ra8_fs_rmdir(const ra8_fs_mount_t *handle, const char *path)
Remove an empty directory at path.
void priv_byte_fill(uint8_t *dst, uint8_t value, uint32_t n)
Fill n bytes of dst with value.
Definition ra8_fs_fat.c:102
uint64_t priv_cluster_to_lba(const ra8_fs_mount_t *m, uint32_t cluster)
Convert a cluster number into its first data-region LBA.
Definition ra8_fs_fat.c:541
uint32_t priv_dir_eps(const ra8_fs_mount_t *m)
Directory entries per sector on one mounted volume.
Definition ra8_fs_fat.c:96
ra8_err_t priv_read_sector(const ra8_fs_mount_t *m, uint64_t lba, uint8_t *buf)
Read a single sector into the module scratch buffer.
Definition ra8_fs_fat.c:134
uint32_t priv_bps(const ra8_fs_mount_t *m)
One mounted volume's sector size in bytes.
Definition ra8_fs_fat.c:84
ra8_err_t priv_write_sector(const ra8_fs_mount_t *m, uint64_t lba, const uint8_t *buf)
Write a single sector from a caller-provided buffer.
Definition ra8_fs_fat.c:140
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
const uint8_t k_zero_sector[k_ra8_fs_sector_max]
One whole sector of zero bytes, in read-only storage.
static ra8_err_t internal_dir_cluster_init(const ra8_fs_mount_t *m, uint32_t new_cluster, uint32_t parent_cluster)
Initialise a freshly allocated directory cluster ("." + ".." + zeros).
static ra8_err_t internal_rmdir_locate(const ra8_fs_mount_t *handle, const char *path, dir_target_t *out, uint32_t *out_cluster)
Resolve a path to a removable subdirectory's entry and first cluster.
static ra8_err_t internal_fat_rmdir(const ra8_fs_mount_t *handle, const char *path)
Implementation of internal_fat_rmdir() – remove one empty FAT directory.
static ra8_err_t internal_dir_is_empty(const ra8_fs_mount_t *m, uint32_t cluster, uint8_t *out_empty)
Decide whether a subdirectory holds any entry other than "." / "..".
static ra8_err_t internal_fat_mkdir(const ra8_fs_mount_t *handle, const char *path)
Implementation of internal_fat_mkdir() – create one FAT directory.
static dir_scan_t internal_rmdir_scan_sector(const ra8_fs_mount_t *m, const uint8_t *buf)
Classify one already-loaded directory sector as empty / occupied / more.
static ra8_err_t internal_mkdir_locked(const ra8_fs_mount_t *handle, const char *path)
Create a directory – the guarded body of ra8_fs_mkdir().
dir_scan_t
Verdict of scanning one directory sector for occupancy.
@ k_dir_scan_used
A real entry was found; not empty.
@ k_dir_scan_more
Only skippable slots here; keep walking.
@ k_dir_scan_empty
End-of-directory marker reached.
static void internal_pack_dot_entry(uint8_t *ent, uint32_t dots, uint32_t cluster)
Pack a "." or ".." dot entry into a 32-byte directory slot.
static ra8_err_t internal_rmdir_locked(const ra8_fs_mount_t *handle, const char *path)
Remove an empty directory – the guarded body of ra8_fs_rmdir().
uint32_t priv_entry_first_cluster(const uint8_t *entry)
Read the first cluster from a 32-byte directory entry.
void priv_entry_set_cluster_size(uint8_t *entry, uint32_t cluster, uint32_t size)
Patch first-cluster + size back into a 32-byte directory entry.
ra8_err_t priv_resolve_parent(const ra8_fs_mount_t *m, const char *path, dir_loc_t *out_parent, const char **out_leaf)
Resolve all-but-the-last path component to a parent directory.
ra8_err_t priv_alloc_eoc_cluster(const ra8_fs_mount_t *m, uint32_t *out_c)
Allocate a fresh cluster, mark it EOC, and return its number.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
ra8_err_t priv_free_chain(const ra8_fs_mount_t *m, uint32_t start)
Free an entire cluster chain starting at start.
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_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.
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.
void priv_lock_release(void)
Drop the library lock taken by priv_lock_acquire.
void priv_lock_acquire(void)
Take the library lock, if the caller installed one.
void priv_dir_walk_init_loc(const ra8_fs_mount_t *m, const dir_loc_t *loc, dir_walk_t *w)
Initialise a directory walker for an arbitrary directory location.
ra8_err_t priv_dir_walk_next_sector(const ra8_fs_mount_t *m, dir_walk_t *w, uint8_t *out_eod)
Advance the walker to the next sector.
void priv_fat_entry_stamp_create(uint8_t *entry)
Stamp a fresh FAT directory entry's create, write, and access fields.
@ k_dir_off_attr
MS FAT spec sec 6 "DIR_Attr".
@ k_dir_name_field_len
8 + 3 raw chars (no dot).
@ k_dir_off_name
MS FAT spec sec 6 "DIR_Name" (11 bytes).
@ k_dir_marker_free_used
Slot was used, deleted.
@ k_dir_marker_free_perm
End-of-directory.
@ k_cluster_first_data
Cluster numbers start at 2.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_attr_lfn
Long-file-name marker (we skip).
@ k_ra8_fs_attr_directory
MS FAT spec sec 6 "ATTR_DIRECTORY".
@ k_ra8_fs_dir_entry_bytes
MS FAT spec sec 6 "Directory Entry".
Everything decided about a new directory entry before anything is written.
Identifies the directory a lookup/scan should operate in.
uint8_t is_root
1 => the volume root; 0 => the subdirectory at cluster.
uint32_t cluster
First cluster of the subdirectory (ignored when root).
One resolved directory entry: where it is, and what is in it.
uint32_t off
Byte offset within that sector.
uint8_t entry[k_ra8_fs_dir_entry_bytes]
The entry as read from disk.
dir_loc_t parent
Directory the entry lives in.
uint64_t lba
Sector holding the entry.
Internal cursor used by the directory iterator.
uint64_t cur_lba
Currently loaded LBA.
Cached parse of one mounted FAT volume.
uint32_t sectors_per_cluster
BPB BPB_SecPerClus.