ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_exfat_mutate.c
Go to the documentation of this file.
1
15
16#include <stddef.h>
17#include <stdint.h>
18#include <string.h>
19
20#include "ra8_attributes.h"
21#include "ra8_fs.h"
22#include "ra8_fs_fat_internal.h"
23
24/* ---- exFAT mutation helpers (unlink / rename / listdir) ------------------ */
25
26/* `priv_exfat_bitmap_clear()`: see header for the documented contract. */
28priv_exfat_bitmap_clear(const ra8_fs_mount_t* m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
29{
30 uint64_t loaded = UINT64_MAX;
31 uint8_t* const sec = priv_sec_io();
32 for (uint32_t k = 0U; k < count; k++) {
33 const uint32_t idx = (clus - k_cluster_first_data) + k;
34 const uint64_t lba = bmp_lba + ((idx >> k_exfat_bit_shift) / priv_bps(m));
35 const uint32_t byte = (idx >> k_exfat_bit_shift) % priv_bps(m);
36 const uint32_t bit = idx & k_exfat_bit_mask;
37 ra8_err_t e = priv_exfat_bmp_switch(m, lba, &loaded, sec);
38 if (e != k_ra8_ok) {
39 return e;
40 }
41 sec[byte] = (uint8_t)(sec[byte] & (uint8_t)~(uint8_t)(1U << bit));
42 /* Pull the scan hint back to the space just released, or the next create
43 * would step over it and only find it after a full rescan (#607). */
45 }
46 return priv_write_sector(m, loaded, sec);
47}
48
79 exfat_cursor_t* cur,
80 const uint16_t* name,
81 uint32_t nlen,
82 uint32_t sc,
83 exfat_setpos_t* pos,
84 uint8_t* strm_copy,
85 uint8_t* out_match)
86{
87 uint8_t matched = 1U;
88 for (uint32_t k = 0U; k < sc; k++) {
89 uint8_t se[k_exfat_entry_bytes] = {};
90 const ra8_err_t r = priv_exfat_next_entry(m, cur, se);
91 if (r != k_ra8_ok) {
92 return r;
93 }
94 /* AFTER the read: ::priv_exfat_next_entry may cross a cluster boundary before
95 * reading, so the entry lives at the cursor's CURRENT cluster, one slot back
96 * (it has already advanced past what it read). Capturing before the call
97 * records (old cluster, one-past-end) for the first entry of a new cluster --
98 * harmless while directories were one cluster, a mis-retired slot once they
99 * grow (#677). */
100 const exfat_setpos_t sp = {.cluster = cur->cluster, .index = cur->entry_in_cluster - 1U};
101 pos[1U + k] = sp;
102 if (k == 0U) {
103 if (se[0] != (uint8_t)k_exfat_entry_stream) {
104 matched = 0U;
105 continue;
106 }
107 if ((uint32_t)se[k_exfat_strm_off_nlen] != nlen) {
108 matched = 0U;
109 continue;
110 }
111 priv_byte_copy(strm_copy, se, (uint32_t)k_exfat_entry_bytes);
112 continue;
113 }
114 if (matched == 0U) {
115 continue;
116 }
117 if (se[0] != (uint8_t)k_exfat_entry_name) {
118 matched = 0U;
119 continue;
120 }
121 const uint32_t cpos = (k - 1U) * (uint32_t)k_exfat_name_per_entry;
122 if (priv_exfat_name_chunk_eq(se, name, cpos, nlen) == 0U) {
123 matched = 0U;
124 }
125 }
126 *out_match = matched;
127 return k_ra8_ok;
128}
129
163 exfat_cursor_t* cur,
165 const uint8_t* e,
166 const uint16_t* need,
167 uint32_t nlen,
168 exfat_setpos_t* pos,
169 uint32_t max_pos,
170 uint32_t* out_count,
171 uint8_t* file_copy,
172 uint8_t* strm_copy)
173{
174 const uint32_t sc = (uint32_t)e[k_exfat_off_file_secnt];
175 const uint32_t total = 1U + sc;
176 if (total > max_pos) {
177 return k_ra8_err_no_mem;
178 }
179 pos[0] = at;
180 priv_byte_copy(file_copy, e, (uint32_t)k_exfat_entry_bytes);
181 uint8_t matched = 0U;
182 const ra8_err_t r = internal_exfat_take_set(m, cur, need, nlen, sc, pos, strm_copy, &matched);
183 if (r != k_ra8_ok) {
184 return r;
185 }
186 if (matched == 1U) {
187 *out_count = total;
188 return k_ra8_ok;
189 }
190 return k_ra8_err_not_found;
191}
192
195 const exfat_dir_t* dir,
196 const char* path,
197 exfat_setpos_t* pos,
198 uint32_t max_pos,
199 uint32_t* out_count,
200 uint8_t* file_copy,
201 uint8_t* strm_copy)
202{
203 /* Strip leading slashes so a "/name" path matches (#93), as FAT does. */
204 while (*path == '/') {
205 path++;
206 }
207 uint16_t need[k_exfat_name_cap] = {};
208 uint32_t nlen = 0U;
209 const ra8_err_t ne = priv_exfat_needle_units(m, path, need, &nlen, k_ra8_err_not_found);
210 if (ne != k_ra8_ok) {
211 return ne;
212 }
213 exfat_cursor_t cur = {};
214 priv_exfat_cursor_init(dir, &cur);
215 while (cur.scanned < (uint32_t)k_exfat_scan_limit) {
216 uint8_t e[k_exfat_entry_bytes] = {};
217 ra8_err_t r = priv_exfat_next_entry(m, &cur, e);
218 if (r != k_ra8_ok) {
219 return r;
220 }
221 /* AFTER the read (see ::priv_exfat_take_set): the File entry lives at the
222 * cursor's current cluster, one slot back, so a set whose File entry is the
223 * first of a grown cluster is recorded where it truly is (#677). */
224 const exfat_setpos_t at = {.cluster = cur.cluster, .index = cur.entry_in_cluster - 1U};
225 if (e[0] == (uint8_t)k_exfat_entry_eod) {
226 return k_ra8_err_not_found;
227 }
228 if (e[0] != (uint8_t)k_exfat_entry_file) {
229 continue;
230 }
232 &cur,
233 at,
234 e,
235 need,
236 nlen,
237 pos,
238 max_pos,
239 out_count,
240 file_copy,
241 strm_copy);
242 if (r != k_ra8_err_not_found) {
243 return r;
244 }
245 }
247}
248
268static ra8_err_t
269internal_exfat_put_entry(const ra8_fs_mount_t* m, const exfat_setpos_t* where, const uint8_t* entry)
270{
272 where->cluster,
273 where->index,
274 entry,
275 (uint32_t)k_exfat_entry_bytes);
276}
277
280{
281 const uint32_t first = priv_rd32(&strm[k_exfat_strm_off_clus]);
282 /* The full 64-bit DataLength: reading only the low word here under-counted a
283 * >4 GiB file's clusters and leaked the rest on unlink/truncate (#676). */
284 const uint64_t size = priv_rd64(&strm[k_exfat_strm_off_dlen]);
285 if (first < k_cluster_first_data) {
286 return k_ra8_ok;
287 }
288 if (size == 0U) {
289 return k_ra8_ok;
290 }
291 uint64_t bmp_lba = 0U;
292 ra8_err_t e = priv_exfat_bitmap_lba(m, &bmp_lba);
293 if (e != k_ra8_ok) {
294 return e;
295 }
296 const uint32_t cluster_bytes = priv_cluster_bytes(m);
297 const uint8_t nofat =
298 ((strm[k_exfat_strm_off_flags] & (uint8_t)k_exfat_secflag_no_fat) != 0U) ? 1U : 0U;
299 if (nofat == 1U) {
300 const uint32_t count = (uint32_t)((size + cluster_bytes - 1U) / cluster_bytes);
301 return priv_exfat_bitmap_clear(m, bmp_lba, first, count);
302 }
303 uint32_t clus = first;
304 for (uint32_t guard = 0U; guard < m->count_of_clusters; guard++) {
305 e = priv_exfat_bitmap_clear(m, bmp_lba, clus, 1U);
306 if (e != k_ra8_ok) {
307 return e;
308 }
309 uint32_t next = 0U;
310 e = priv_fat_get(m, clus, &next);
311 if (e != k_ra8_ok) {
312 return e;
313 }
314 if (priv_is_eoc(m, next) != 0U) {
315 return k_ra8_ok;
316 }
317 clus = next;
318 }
320}
321
322/* `priv_exfat_drop_set()`: see header for the documented contract. */
323ra8_err_t priv_exfat_drop_set(const ra8_fs_mount_t* m, const exfat_setpos_t* pos, uint32_t count)
324{
325 for (uint32_t k = 0U; k < count; k++) {
326 /* A single-entry read at a recorded position: contig_end is irrelevant
327 * because this cursor never advances past the entry it was aimed at. */
328 exfat_cursor_t one = {.cluster = pos[k].cluster,
329 .entry_in_cluster = pos[k].index,
330 .scanned = 0U,
331 .contig_end = 0U};
332 uint8_t entry[k_exfat_entry_bytes] = {};
333 ra8_err_t e = priv_exfat_next_entry(m, &one, entry);
334 if (e != k_ra8_ok) {
335 return e;
336 }
337 entry[0] = (uint8_t)(entry[0] & (uint8_t)~(uint8_t)k_exfat_inuse_bit);
338 e = internal_exfat_put_entry(m, &pos[k], entry);
339 if (e != k_ra8_ok) {
340 return e;
341 }
342 }
343 return k_ra8_ok;
344}
345
346/* `priv_exfat_unlink_at()`: see header for the documented contract. */
347ra8_err_t priv_exfat_unlink_at(const ra8_fs_mount_t* m, const exfat_dir_t* dir, const char* name)
348{
350 uint32_t count = 0U;
351 uint8_t file_e[k_exfat_entry_bytes] = {};
352 uint8_t strm_e[k_exfat_entry_bytes] = {};
353 const ra8_err_t e = priv_exfat_find_set(m,
354 dir,
355 name,
356 pos,
357 (uint32_t)k_exfat_set_max_entries,
358 &count,
359 file_e,
360 strm_e);
361 if (e != k_ra8_ok) {
362 return e;
363 }
364 /* A directory's entry set looks like a file's, so without this the chain
365 * holding every child is handed to priv_exfat_free_clusters and the children
366 * become unreachable allocated clusters -- silent, unrecoverable loss (#604). */
367 if ((file_e[k_exfat_off_file_attr] & (uint8_t)k_exfat_attr_directory) != 0U) {
369 }
370 /* Honor the read-only attribute (#681): a read-only file is refused before the
371 * entry set is dropped or its clusters freed, so a denied unlink is a no-op. */
372 if ((file_e[k_exfat_off_file_attr] & (uint8_t)k_exfat_attr_read_only) != 0U) {
374 }
375 const ra8_err_t de = priv_exfat_drop_set(m, pos, count);
376 if (de != k_ra8_ok) {
377 return de;
378 }
379 return priv_exfat_free_clusters(m, strm_e);
380}
381
382/* `priv_exfat_unlink()`: see header for the documented contract. */
383ra8_err_t priv_exfat_unlink(const ra8_fs_mount_t* m, const char* path)
384{
385 exfat_dir_t parent = {};
386 const char* leaf = nullptr;
387 const ra8_err_t e = priv_exfat_resolve_parent(m, path, &parent, &leaf);
388 if (e != k_ra8_ok) {
389 return e;
390 }
391 if (priv_strlen(leaf) == 0U) {
392 return k_ra8_err_invalid_arg; /* "/" names the root, which is not a file */
393 }
394 return priv_exfat_unlink_at(m, &parent, leaf);
395}
396
428static uint32_t internal_exfat_build_rename_set(uint8_t* set,
429 const uint8_t* file_e,
430 const uint8_t* strm_e,
431 const uint16_t* new_name,
432 uint32_t new_len)
433{
434 const uint32_t name_entries =
435 (new_len + (uint32_t)k_exfat_name_per_entry - 1U) / (uint32_t)k_exfat_name_per_entry;
436 const uint32_t sec_count = 1U + name_entries;
437 const uint32_t total = (1U + sec_count) * (uint32_t)k_exfat_entry_bytes;
438 for (uint32_t i = 0U; i < total; i++) {
439 set[i] = 0U;
440 }
441 priv_byte_copy(&set[0], file_e, (uint32_t)k_exfat_entry_bytes);
443 set[k_exfat_off_file_secnt] = (uint8_t)sec_count;
444 uint8_t* strm = &set[k_exfat_entry_bytes];
445 strm[k_exfat_strm_off_nlen] = (uint8_t)new_len;
446 priv_wr16(&strm[k_exfat_off_strm_hash], priv_exfat_name_hash(new_name, new_len));
447 for (uint32_t n = 0U; n < name_entries; n++) {
448 uint8_t* ne = &set[(size_t)(2U + n) * (size_t)k_exfat_entry_bytes];
449 ne[0] = (uint8_t)k_exfat_entry_name;
450 for (uint32_t c = 0U; c < (uint32_t)k_exfat_name_per_entry; c++) {
451 const uint32_t p = (n * (uint32_t)k_exfat_name_per_entry) + c;
452 if (p < new_len) {
453 priv_wr16(&ne[k_exfat_name_off + (c * 2U)], new_name[p]);
454 }
455 }
456 }
457 /* Access stamp only, and before the checksum that covers it. Same reasoning
458 * as the FAT rename: the name moved, the bytes did not, so LastModified must
459 * not move or every backup tool concludes the file changed (#601). */
462 return total;
463}
464
495 const char* old_path,
496 const char* new_path,
497 exfat_dir_t* out_parent,
498 const char** out_old,
499 const char** out_new)
500{
501 exfat_dir_t op = {};
502 const char* ol = nullptr;
503 const ra8_err_t e1 = priv_exfat_resolve_parent(m, old_path, &op, &ol);
504 if (e1 != k_ra8_ok) {
505 return e1;
506 }
507 exfat_dir_t np = {};
508 const char* nl = nullptr;
509 const ra8_err_t e2 = priv_exfat_resolve_parent(m, new_path, &np, &nl);
510 if (e2 != k_ra8_ok) {
511 return e2;
512 }
513 if (op.cluster != np.cluster) {
515 }
516 if (priv_strlen(ol) == 0U) {
518 }
519 if (priv_strlen(nl) == 0U) {
521 }
522 *out_parent = op;
523 *out_old = ol;
524 *out_new = nl;
525 return k_ra8_ok;
526}
527
562 const exfat_dir_t* parent,
563 const exfat_setpos_t* pos,
564 uint32_t old_count,
565 const uint8_t* set,
566 uint32_t bytes)
567{
568 const uint32_t new_count = bytes / (uint32_t)k_exfat_entry_bytes;
569 if (new_count == old_count) {
570 for (uint32_t k = 0U; k < new_count; k++) {
571 const ra8_err_t e =
572 internal_exfat_put_entry(m, &pos[k], &set[(size_t)k * (size_t)k_exfat_entry_bytes]);
573 if (e != k_ra8_ok) {
574 return e;
575 }
576 }
577 return k_ra8_ok;
578 }
579 uint32_t nclus = 0U;
580 uint32_t nidx = 0U;
581 const ra8_err_t se = priv_exfat_find_dir_space(m, parent, new_count, &nclus, &nidx);
582 if (se != k_ra8_ok) {
583 return se;
584 }
585 const ra8_err_t we = priv_exfat_write_dir_set(m, nclus, nidx, set, bytes);
586 if (we != k_ra8_ok) {
587 return we;
588 }
589 return priv_exfat_drop_set(m, pos, old_count);
590}
591
592/* `priv_exfat_rename()`: see header for the documented contract. */
593ra8_err_t priv_exfat_rename(const ra8_fs_mount_t* m, const char* old_path, const char* new_path)
594{
595 exfat_dir_t parent = {};
596 const char* old_name = nullptr;
597 const char* new_name = nullptr;
598 const ra8_err_t pe =
599 internal_exfat_rename_prepare(m, old_path, new_path, &parent, &old_name, &new_name);
600 if (pe != k_ra8_ok) {
601 return pe;
602 }
603 /* The new leaf in UTF-16 up front: the built Name entries and the NameHash
604 * both count CODE UNITS, and a UTF-8 argument of the same unit count can be
605 * three times as many bytes (#606). A name over the cap is an argument fault,
606 * not a full disk. The old leaf stays UTF-8: ::priv_exfat_find_set converts it
607 * itself, and the entry count it reports is all the placement below needs. */
608 uint16_t new_units[k_exfat_name_cap] = {};
609 uint32_t new_len = 0U;
610 const ra8_err_t nue =
611 priv_exfat_needle_units(m, new_name, new_units, &new_len, k_ra8_err_invalid_arg);
612 if (nue != k_ra8_ok) {
613 return nue;
614 }
615 uint8_t e_strm[k_exfat_entry_bytes] = {};
616 uint8_t e_attr = 0U;
617 if (priv_exfat_find(m, &parent, new_name, e_strm, &e_attr) == k_ra8_ok) {
618 return k_ra8_err_exists;
619 }
621 uint32_t count = 0U;
622 uint8_t file_e[k_exfat_entry_bytes] = {};
623 uint8_t strm_e[k_exfat_entry_bytes] = {};
624 const ra8_err_t fe = priv_exfat_find_set(m,
625 &parent,
626 old_name,
627 pos,
628 (uint32_t)k_exfat_set_max_entries,
629 &count,
630 file_e,
631 strm_e);
632 if (fe != k_ra8_ok) {
633 return fe;
634 }
635 /* Honor the read-only attribute (#681). file_e is the File entry read back by
636 * priv_exfat_find_set above, so its FileAttributes low byte carries the
637 * read-only bit; a read-only file is refused before the rename set is rebuilt
638 * or placed. */
639 if ((file_e[k_exfat_off_file_attr] & (uint8_t)k_exfat_attr_read_only) != 0U) {
641 }
642 uint8_t set[k_exfat_max_set_bytes] = {};
643 const uint32_t bytes = internal_exfat_build_rename_set(set, file_e, strm_e, new_units, new_len);
644 return internal_exfat_place_rename(m, &parent, pos, count, set, bytes);
645}
646
686 exfat_cursor_t* cur,
687 uint32_t sc,
688 uint32_t nlen,
689 char* name,
690 uint32_t cap)
691{
692 uint16_t units[k_exfat_name_cap] = {};
693 uint32_t got = 0U;
694 name[0] = '\0';
695 for (uint32_t k = 1U; k < sc; k++) {
696 uint8_t ne[k_exfat_entry_bytes] = {};
697 const ra8_err_t r = priv_exfat_next_entry(m, cur, ne);
698 if (r != k_ra8_ok) {
699 return r;
700 }
701 if (ne[0] != (uint8_t)k_exfat_entry_name) {
702 continue;
703 }
704 for (uint32_t c = 0U; c < (uint32_t)k_exfat_name_per_entry; c++) {
705 const uint32_t p = ((k - 1U) * (uint32_t)k_exfat_name_per_entry) + c;
706 if (p >= nlen) {
707 break;
708 }
709 if (got < (uint32_t)k_exfat_name_cap) {
710 units[got] = priv_rd16(&ne[k_exfat_name_off + (c * 2U)]);
711 got++;
712 }
713 }
714 }
715 if (priv_utf16_to_utf8(units, got, name, cap) != k_ra8_ok) {
716 name[0] = '\0';
717 }
718 return k_ra8_ok;
719}
720
747 exfat_cursor_t* cur,
748 const uint8_t* e,
749 ra8_fs_dirent_t* out,
750 bool* out_entry)
751{
752 const uint32_t sc = (uint32_t)e[k_exfat_off_file_secnt];
753 const uint8_t attr = e[k_exfat_off_file_attr];
754 uint8_t strm[k_exfat_entry_bytes] = {};
755 ra8_err_t r = priv_exfat_next_entry(m, cur, strm);
756 if (r != k_ra8_ok) {
757 return r;
758 }
759 if (strm[0] != (uint8_t)k_exfat_entry_stream) {
760 return k_ra8_ok;
761 }
762 /* A directory's exFAT DataLength is its ALLOCATION, not a byte count, so
763 * reporting it as a size would tell every caller that an empty folder held
764 * a cluster's worth of bytes. FAT reports 0 for a directory and so does
765 * ra8_fs_stat(); this is the third place that has to agree (#605). */
766 uint64_t size = priv_rd64(&strm[k_exfat_strm_off_dlen]);
767 if ((attr & (uint8_t)k_exfat_attr_directory) != 0U) {
768 size = 0U;
769 }
770 const uint32_t nlen = (uint32_t)strm[k_exfat_strm_off_nlen];
771 char name[k_exfat_name_u8_cap] = {};
772 r = internal_exfat_gather_name(m, cur, sc, nlen, name, (uint32_t)k_exfat_name_u8_cap);
773 if (r != k_ra8_ok) {
774 return r;
775 }
776 if (name[0] == '\0') {
777 return k_ra8_ok; /* units no UTF-8 string encodes: report nothing, not a lie */
778 }
779 (void)memcpy(out->name, name, strlen(name) + 1U);
780 out->attr = attr;
781 out->size_bytes = size;
782 *out_entry = true;
783 return k_ra8_ok;
784}
785
787 exfat_cursor_t* cur,
788 ra8_fs_dirent_t* out,
789 bool* out_entry)
790{
791 *out_entry = false;
792 while (cur->scanned < (uint32_t)k_exfat_scan_limit) {
793 uint8_t entry[k_exfat_entry_bytes] = {};
794 ra8_err_t err = priv_exfat_next_entry(m, cur, entry);
795 if (err == k_ra8_err_not_found) {
796 return k_ra8_ok;
797 }
798 if (err != k_ra8_ok) {
799 return err;
800 }
801 if (entry[0] == (uint8_t)k_exfat_entry_eod) {
802 return k_ra8_ok;
803 }
804 if (entry[0] != (uint8_t)k_exfat_entry_file) {
805 continue;
806 }
807 err = internal_exfat_list_emit(m, cur, entry, out, out_entry);
808 if (err != k_ra8_ok) {
809 return err;
810 }
811 if (*out_entry) {
812 return k_ra8_ok;
813 }
814 }
816}
817
818/* `priv_exfat_listdir()`: see header for the documented contract. */
820 const exfat_dir_t* dir,
822 void* ctx)
823{
824 exfat_cursor_t cur = {};
825 priv_exfat_cursor_init(dir, &cur);
826 for (;;) {
827 ra8_fs_dirent_t entry = {};
828 bool present = false;
829 const ra8_err_t err = priv_exfat_dir_next(m, &cur, &entry, &present);
830 if ((err != k_ra8_ok) || !present) {
831 return err;
832 }
833 cb(entry.name, entry.attr, entry.size_bytes, ctx);
834 }
835}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ 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_access_denied
Operation refused because the target is protected against it.
Definition ra8_err.h:276
@ k_ra8_err_protocol_error
Protocol-level error (e.g.
Definition ra8_err.h:429
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
ra8_err_t priv_fat_get(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t *out_value)
Fetch the FAT entry for cluster, returning the next-cluster value.
Definition ra8_fs_fat.c:187
uint16_t priv_rd16(const uint8_t *p)
Decode a little-endian uint16_t from a byte buffer.
Definition ra8_fs_fat.c:42
uint8_t priv_is_eoc(const ra8_fs_mount_t *m, uint32_t value)
Test whether value is an end-of-chain marker for this FAT type.
Definition ra8_fs_fat.c:511
uint32_t priv_bps(const ra8_fs_mount_t *m)
One mounted volume's sector size in bytes.
Definition ra8_fs_fat.c:84
void priv_byte_copy(uint8_t *dst, const uint8_t *src, uint32_t n)
Length-checked byte copy used in place of memcpy().
Definition ra8_fs_fat.c:110
uint64_t priv_rd64(const uint8_t *p)
Decode a little-endian uint64_t from a byte buffer.
Definition ra8_fs_fat.c:55
uint32_t priv_cluster_bytes(const ra8_fs_mount_t *m)
One mounted volume's cluster size in bytes.
Definition ra8_fs_fat.c:90
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
void priv_wr16(uint8_t *p, uint16_t v)
Encode a little-endian uint16_t into a byte buffer.
Definition ra8_fs_fat.c:61
void priv_alloc_hint_lower(const ra8_fs_mount_t *m, uint32_t cluster)
Pull the next-free hint back to cluster if it is further on.
ra8_err_t priv_exfat_bitmap_lba(const ra8_fs_mount_t *m, uint64_t *out_lba)
Resolve the exFAT allocation bitmap's first LBA, once per mount.
uint8_t * priv_sec_io(void)
The IO-role sector buffer (leaf data / bitmap sector transfers).
ra8_err_t priv_exfat_resolve_parent(const ra8_fs_mount_t *m, const char *path, exfat_dir_t *out_parent, const char **out_leaf)
Resolve every component of path but the last.
ra8_err_t priv_exfat_find_dir_space(const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t need, uint32_t *out_clus, uint32_t *out_idx)
Find need consecutive free entry slots inside one directory cluster.
void priv_exfat_cursor_init(const exfat_dir_t *dir, exfat_cursor_t *out)
Aim a fresh cursor at the start of dir.
ra8_err_t priv_exfat_listdir(const ra8_fs_mount_t *m, const exfat_dir_t *dir, ra8_fs_listdir_cb_t cb, void *ctx)
Enumerate ONE directory of an exFAT volume.
ra8_err_t priv_exfat_unlink(const ra8_fs_mount_t *m, const char *path)
Delete a file on an exFAT volume, at any depth.
static uint32_t internal_exfat_build_rename_set(uint8_t *set, const uint8_t *file_e, const uint8_t *strm_e, const uint16_t *new_name, uint32_t new_len)
Assemble a file's directory set under a new name, keeping its data.
static ra8_err_t internal_exfat_put_entry(const ra8_fs_mount_t *m, const exfat_setpos_t *where, const uint8_t *entry)
Rewrite one 32-byte directory entry at a recorded position.
static ra8_err_t internal_exfat_rename_prepare(const ra8_fs_mount_t *m, const char *old_path, const char *new_path, exfat_dir_t *out_parent, const char **out_old, const char **out_new)
Resolve a rename's two paths to one shared parent plus two leaves.
static ra8_err_t internal_exfat_try_set(const ra8_fs_mount_t *m, exfat_cursor_t *cur, exfat_setpos_t at, const uint8_t *e, const uint16_t *need, uint32_t nlen, exfat_setpos_t *pos, uint32_t max_pos, uint32_t *out_count, uint8_t *file_copy, uint8_t *strm_copy)
Test one File entry set against a needle, recording its positions.
ra8_err_t priv_exfat_free_clusters(const ra8_fs_mount_t *m, const uint8_t *strm)
Implementation of priv_exfat_free_clusters() – run or chain, bitmap only.
ra8_err_t priv_exfat_dir_next(const ra8_fs_mount_t *m, exfat_cursor_t *cur, ra8_fs_dirent_t *out, bool *out_entry)
Copy the next visible exFAT directory entry from an existing cursor.
static ra8_err_t internal_exfat_list_emit(const ra8_fs_mount_t *m, exfat_cursor_t *cur, const uint8_t *e, ra8_fs_dirent_t *out, bool *out_entry)
Copy one File entry set into a stable directory value.
ra8_err_t priv_exfat_drop_set(const ra8_fs_mount_t *m, const exfat_setpos_t *pos, uint32_t count)
Retire an entry set by clearing the in-use bit on every entry.
static ra8_err_t internal_exfat_gather_name(const ra8_fs_mount_t *m, exfat_cursor_t *cur, uint32_t sc, uint32_t nlen, char *name, uint32_t cap)
Consume a set's Name entries and assemble the name as UTF-8.
ra8_err_t priv_exfat_find_set(const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *path, exfat_setpos_t *pos, uint32_t max_pos, uint32_t *out_count, uint8_t *file_copy, uint8_t *strm_copy)
Implementation of priv_exfat_find_set() – one aligned walk of a directory.
ra8_err_t priv_exfat_unlink_at(const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *name)
Delete a FILE named name from dir.
static ra8_err_t internal_exfat_place_rename(const ra8_fs_mount_t *m, const exfat_dir_t *parent, const exfat_setpos_t *pos, uint32_t old_count, const uint8_t *set, uint32_t bytes)
Write a rebuilt rename set, in place when it fits, else by relocation.
ra8_err_t priv_exfat_rename(const ra8_fs_mount_t *m, const char *old_path, const char *new_path)
Rename a root-level file on an exFAT volume, at any storable length.
static ra8_err_t internal_exfat_take_set(const ra8_fs_mount_t *m, exfat_cursor_t *cur, const uint16_t *name, uint32_t nlen, uint32_t sc, exfat_setpos_t *pos, uint8_t *strm_copy, uint8_t *out_match)
Consume a set's secondary entries, recording positions + matching.
ra8_err_t priv_exfat_bitmap_clear(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
Clear a contiguous cluster run in the allocation bitmap.
ra8_err_t priv_exfat_next_entry(const ra8_fs_mount_t *m, exfat_cursor_t *cur, uint8_t *out)
Fetch the next 32-byte directory entry, following the cluster chain.
uint32_t priv_strlen(const char *s)
Length of a NUL-terminated string.
uint8_t priv_exfat_name_chunk_eq(const uint8_t *entry, const uint16_t *name, uint32_t pos, uint32_t nlen)
Compare one file-name entry's 15 UTF-16 units against a needle.
ra8_err_t priv_exfat_needle_units(const ra8_fs_mount_t *m, const char *name, uint16_t *out, uint32_t *out_units, ra8_err_t too_long)
Convert a leaf name to code units, mapping over-long to too_long.
ra8_err_t priv_exfat_find(const ra8_fs_mount_t *m, const exfat_dir_t *dir, const char *path, uint8_t *out_strm, uint8_t *out_attr)
Find a name in ONE exFAT directory.
uint16_t priv_exfat_set_checksum(const uint8_t *set, uint32_t bytes)
Compute the SetChecksum over a built directory entry set.
uint16_t priv_exfat_name_hash(const uint16_t *name, uint32_t nlen)
Compute the exFAT NameHash for a name in UTF-16 code units.
ra8_err_t priv_exfat_write_dir_set(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t idx, const uint8_t *set, uint32_t bytes)
Write a pre-built entry set into consecutive directory entries.
ra8_err_t priv_exfat_bmp_switch(const ra8_fs_mount_t *m, uint64_t lba, uint64_t *loaded, uint8_t *sec)
Flush the cached bitmap sector and load lba if it changed.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
void priv_exfat_file_stamp_access(uint8_t *file_entry)
Advance only an exFAT File entry's last-accessed stamp.
@ k_exfat_name_off
File-name entry character offset.
@ k_exfat_strm_off_dlen
Stream-ext DataLength (low 32 used).
@ k_exfat_strm_off_nlen
Stream-ext NameLength (UTF-16 units).
@ k_exfat_strm_off_clus
Stream-ext FirstCluster.
@ k_exfat_strm_off_flags
Stream-ext GeneralSecondaryFlags.
@ k_exfat_off_file_csum
File entry: SetChecksum (2 bytes).
@ k_exfat_secflag_no_fat
GeneralSecondaryFlags: NoFatChain.
@ k_exfat_name_per_entry
UTF-16 units per file-name entry.
@ k_exfat_entry_bytes
Directory entry size.
@ k_exfat_off_strm_hash
Stream entry: NameHash (2 bytes).
@ k_exfat_entry_eod
End-of-directory marker.
@ k_exfat_bit_shift
log2(8): cluster index -> byte.
@ k_exfat_entry_stream
Stream-extension entry.
@ k_exfat_off_file_attr
File entry: FileAttributes (2 bytes).
@ k_exfat_off_file_secnt
File entry: SecondaryCount.
@ k_exfat_name_u8_cap
That name in UTF-8: 3 * 64, plus a NUL.
@ k_exfat_max_set_bytes
(2 + ceil(64/15)) * 32 = 7 entries.
@ k_exfat_scan_limit
Max dir entries scanned (P10 bound).
@ k_exfat_attr_read_only
FileAttributes: read-only.
@ k_exfat_attr_directory
FileAttributes: directory.
@ k_exfat_bit_mask
Bit index within a bitmap byte.
@ k_exfat_entry_name
File-name entry.
@ k_exfat_name_cap
Longest name we store, in UTF-16 units.
@ k_exfat_inuse_bit
Directory entry type bit 7 = in use.
@ k_exfat_entry_file
File directory entry.
@ k_exfat_set_max_entries
1 File + 1 Stream + 17 Name entries.
@ k_cluster_first_data
Cluster numbers start at 2.
void(* ra8_fs_listdir_cb_t)(const char *name, uint8_t attr, uint64_t size, void *ctx)
Callback invoked once per directory entry by ra8_fs_listdir().
ra8_err_t priv_utf16_to_utf8(const uint16_t *in, uint32_t units, char *out, uint32_t cap)
Convert UTF-16LE code units into a NUL-terminated UTF-8 name.
Definition ra8_fs_utf.c:499
Linear cursor over a directory's 32-byte entries.
uint32_t scanned
Total entries read (P10 bound).
uint32_t cluster
Current directory cluster.
uint32_t entry_in_cluster
Next entry index within the cluster.
Identifies the exFAT directory a lookup, scan or link operates in.
uint32_t cluster
First cluster of the directory.
Directory position (cluster + entry index) of one 32-byte entry.
uint32_t index
Entry index within that cluster (0-based).
uint32_t cluster
Directory cluster holding the entry.
Stable directory-entry value copied by ra8_fs_dir_next.
char name[k_ra8_fs_dir_name_cap]
NUL-terminated visible leaf.
uint8_t attr
On-disk FAT attributes.
uint64_t size_bytes
File bytes; zero for dirs.
Cached parse of one mounted FAT volume.
uint32_t count_of_clusters
Per MS spec: data_sectors / SPC.