ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_exfat_stream.c
Go to the documentation of this file.
1
44
45#include <stddef.h>
46#include <stdint.h>
47
48#include "ra8_attributes.h"
49#include "ra8_fs.h"
50#include "ra8_fs_fat_internal.h"
51
52/* =============================================================================
53 * Growth: one cluster at a time, contiguous while it can be
54 * =============================================================================
55 */
56
89 uint64_t bmp_lba,
90 uint32_t prefer,
91 uint32_t* out)
92{
93 const uint32_t past_end = (uint32_t)k_cluster_first_data + m->count_of_clusters;
94 if ((prefer >= (uint32_t)k_cluster_first_data) && (prefer < past_end)) {
95 uint8_t is_free = 0U;
96 const ra8_err_t pe = priv_exfat_bitmap_test(m, bmp_lba, prefer, &is_free);
97 if (pe != k_ra8_ok) {
98 return pe;
99 }
100 if (is_free != 0U) {
101 *out = prefer;
102 return k_ra8_ok;
103 }
104 }
105 return priv_exfat_bitmap_scan(m, bmp_lba, 1U, out);
106}
107
142 uint32_t first,
143 uint32_t alloc,
144 uint32_t tail,
145 uint32_t next)
146{
147 for (uint32_t i = 0U; (i + 1U) < alloc; i++) {
148 const uint32_t here = first + i;
149 const ra8_err_t e = priv_fat_set(m, here, here + 1U);
150 if (e != k_ra8_ok) {
151 return e;
152 }
153 }
154 return priv_fat_set(m, tail, next);
155}
156
189{
190 if (file->alloc_clusters == 0U) {
191 return k_ra8_ok;
192 }
193 if ((file->no_fat_chain != 0U) && (next == (file->tail_cluster + 1U))) {
194 return k_ra8_ok;
195 }
196 if (file->no_fat_chain != 0U) {
197 const ra8_err_t e = internal_exfat_materialize_run(file->mount,
198 file->first_cluster,
199 file->alloc_clusters,
200 file->tail_cluster,
201 next);
202 if (e != k_ra8_ok) {
203 return e;
204 }
205 file->no_fat_chain = 0U;
206 } else {
207 const ra8_err_t e = priv_fat_set(file->mount, file->tail_cluster, next);
208 if (e != k_ra8_ok) {
209 return e;
210 }
211 }
212 return priv_fat_set(file->mount, next, priv_eoc_write(file->mount));
213}
214
243{
244 const ra8_fs_mount_t* m = file->mount;
245 uint64_t bmp_lba = 0U;
246 ra8_err_t e = priv_exfat_bitmap_lba(m, &bmp_lba);
247 if (e != k_ra8_ok) {
248 return e;
249 }
250 const uint32_t prefer = (file->alloc_clusters > 0U) ? (file->tail_cluster + 1U) : 0U;
251 uint32_t next = 0U;
252 e = internal_exfat_pick_cluster(m, bmp_lba, prefer, &next);
253 if (e != k_ra8_ok) {
254 return e;
255 }
256 e = priv_exfat_bitmap_mark(m, bmp_lba, next, 1U);
257 if (e != k_ra8_ok) {
258 return e;
259 }
260 priv_alloc_hint_set(m, next + 1U);
261 e = internal_exfat_link_cluster(file, next);
262 if (e != k_ra8_ok) {
263 return e;
264 }
265 if (file->alloc_clusters == 0U) {
266 file->first_cluster = next;
267 file->cur_cluster = next;
268 file->walk_cache_idx = 0U;
269 file->walk_cache_cluster = next;
270 }
271 file->tail_cluster = next;
272 file->alloc_clusters++;
273 return k_ra8_ok;
274}
275
276/* `priv_exfat_ensure_clusters()`: see header for the documented contract. */
278{
279 while (file->alloc_clusters < need) {
280 const ra8_err_t e = internal_exfat_grow_one(file);
281 if (e != k_ra8_ok) {
282 return e;
283 }
284 }
285 return k_ra8_ok;
286}
287
288/* =============================================================================
289 * Directory growth: the same allocation, one directory-specific rewrite (#677)
290 * =============================================================================
291 *
292 * A directory's on-disk allocation is structurally a file's: a run of clusters
293 * that carries `NoFatChain` while it is contiguous and becomes a real FAT chain
294 * the moment it is not. So directory growth reuses the machinery above --
295 * ::priv_exfat_pick_cluster to prefer the tail's successor, and
296 * ::priv_exfat_materialize_run for the conversion -- and adds only the two
297 * things a directory needs that a mid-write file does not: the new cluster is
298 * ZEROED before it is linked (a directory cluster reads as its own contents, so
299 * a half-built one must never be reachable), and the directory's OWN Stream
300 * entry in its parent is rewritten so `DataLength` and the `NoFatChain` flag
301 * keep describing the run after the mount that grew it is gone.
302 */
303
336 const exfat_dir_t* dir,
337 uint32_t* out_alloc,
338 uint32_t* out_tail,
339 uint8_t* out_nofat)
340{
341 if (dir->contig_end != 0U) {
342 *out_nofat = 1U;
343 *out_alloc = dir->contig_end - dir->cluster;
344 *out_tail = dir->contig_end - 1U;
345 return k_ra8_ok;
346 }
347 *out_nofat = 0U;
348 uint32_t clus = dir->cluster;
349 uint32_t held = 1U;
350 for (uint32_t guard = 0U; guard < m->count_of_clusters; guard++) {
351 uint32_t next = 0U;
352 const ra8_err_t e = priv_fat_get(m, clus, &next);
353 if (e != k_ra8_ok) {
354 return e;
355 }
356 if (priv_is_eoc(m, next) != 0U) {
357 break;
358 }
359 if (next < (uint32_t)k_cluster_first_data) {
361 }
362 clus = next;
363 held++;
364 }
365 *out_alloc = held;
366 *out_tail = clus;
367 return k_ra8_ok;
368}
369
407 uint32_t first,
408 uint32_t alloc,
409 uint32_t tail,
410 uint8_t* nofat,
411 uint32_t next)
412{
413 if ((*nofat != 0U) && (next == (tail + 1U))) {
414 return k_ra8_ok; /* the run stays contiguous; NoFatChain is still true */
415 }
416 if (*nofat != 0U) {
417 const ra8_err_t e = internal_exfat_materialize_run(m, first, alloc, tail, next);
418 if (e != k_ra8_ok) {
419 return e;
420 }
421 *nofat = 0U;
422 } else {
423 const ra8_err_t e = priv_fat_set(m, tail, next);
424 if (e != k_ra8_ok) {
425 return e;
426 }
427 }
428 return priv_fat_set(m, next, priv_eoc_write(m));
429}
430
463 const exfat_dir_t* dir,
464 uint64_t new_bytes,
465 uint8_t nofat)
466{
467 uint8_t set[k_exfat_max_set_bytes] = {};
468 exfat_cursor_t cur = {.cluster = dir->self_cluster,
469 .entry_in_cluster = dir->self_index,
470 .scanned = 0U,
471 .contig_end = 0U};
472 ra8_err_t e = priv_exfat_next_entry(m, &cur, &set[0]);
473 if (e != k_ra8_ok) {
474 return e;
475 }
476 const uint32_t count = 1U + (uint32_t)set[k_exfat_off_file_secnt];
477 if (count > (uint32_t)k_exfat_set_writable) {
479 }
480 for (uint32_t k = 1U; k < count; k++) {
481 e = priv_exfat_next_entry(m, &cur, &set[(size_t)k * (size_t)k_exfat_entry_bytes]);
482 if (e != k_ra8_ok) {
483 return e;
484 }
485 }
486 uint8_t* strm = &set[k_exfat_entry_bytes];
488 (nofat != 0U) ? (uint8_t)k_exfat_secflag_alloc : (uint8_t)k_exfat_secflag_poss;
489 priv_wr64(&strm[k_exfat_off_strm_valid], new_bytes);
490 priv_wr64(&strm[k_exfat_strm_off_dlen], new_bytes);
492 priv_exfat_set_checksum(set, count * (uint32_t)k_exfat_entry_bytes));
494 dir->self_cluster,
495 dir->self_index,
496 set,
497 count * (uint32_t)k_exfat_entry_bytes);
498}
499
534 const exfat_dir_t* dir,
535 uint32_t alloc,
536 uint32_t tail,
537 uint8_t* nofat)
538{
539 uint64_t bmp_lba = 0U;
540 ra8_err_t e = priv_exfat_bitmap_lba(m, &bmp_lba);
541 if (e != k_ra8_ok) {
542 return e;
543 }
544 uint32_t next = 0U;
545 e = internal_exfat_pick_cluster(m, bmp_lba, tail + 1U, &next);
546 if (e != k_ra8_ok) {
547 return e;
548 }
549 e = priv_exfat_zero_cluster(m, next);
550 if (e != k_ra8_ok) {
551 return e;
552 }
553 e = priv_exfat_bitmap_mark(m, bmp_lba, next, 1U);
554 if (e != k_ra8_ok) {
555 return e;
556 }
557 priv_alloc_hint_set(m, next + 1U);
558 e = internal_exfat_dir_link(m, dir->cluster, alloc, tail, nofat, next);
559 if (e != k_ra8_ok) {
560 return e;
561 }
562 return priv_exfat_seal_cluster(m, tail);
563}
564
565/* `priv_exfat_grow_dir()`: see header for the documented contract. */
567{
568 uint32_t alloc = 0U;
569 uint32_t tail = 0U;
570 uint8_t nofat = 0U;
571 ra8_err_t e = internal_exfat_dir_survey(m, dir, &alloc, &tail, &nofat);
572 if (e != k_ra8_ok) {
573 return e;
574 }
575 e = internal_exfat_dir_append(m, dir, alloc, tail, &nofat);
576 if (e != k_ra8_ok) {
577 return e;
578 }
579 const uint32_t new_alloc = alloc + 1U;
580 const uint32_t cbytes = priv_cluster_bytes(m);
581 /* The volume root has no entry set to patch -- its extent is the boot-sector
582 * FAT chain, which ::priv_exfat_dir_link just extended. A subdirectory carries
583 * the location of its own set and must update it, or the next mount reads the
584 * old length and cannot see the cluster this call added (#677). */
585 if (dir->self_cluster >= (uint32_t)k_cluster_first_data) {
586 e = internal_exfat_dir_relen(m, dir, (uint64_t)new_alloc * cbytes, nofat);
587 if (e != k_ra8_ok) {
588 return e;
589 }
590 }
591 dir->contig_end = (nofat != 0U) ? (dir->cluster + new_alloc) : 0U;
592 return k_ra8_ok;
593}
594
625static ra8_err_t internal_exfat_cluster_at(ra8_fs_file_t* file, uint32_t idx, uint32_t* out)
626{
627 if (file->no_fat_chain != 0U) {
628 *out = file->first_cluster + idx;
629 return k_ra8_ok;
630 }
631 /* Nested rather than joined, because only the inner guard has both answers
632 * available: by the time a chained file is walked its waypoint is always a
633 * real cluster, so a joined decision would carry a condition no input can
634 * make false -- a permanent MC/DC hole. The inner guard varies with every
635 * backward seek. */
636 uint32_t from = file->first_cluster;
637 uint32_t hops = idx;
638 if (file->walk_cache_cluster >= (uint32_t)k_cluster_first_data) {
639 if (idx >= file->walk_cache_idx) {
640 from = file->walk_cache_cluster;
641 hops = idx - file->walk_cache_idx;
642 }
643 }
644 uint32_t cur = from;
645 for (uint32_t i = 0U; i < hops; i++) {
646 uint32_t next = 0U;
647 const ra8_err_t e = priv_fat_get(file->mount, cur, &next);
648 if (e != k_ra8_ok) {
649 return e;
650 }
651 if (priv_is_eoc(file->mount, next) != 0U) {
653 }
654 cur = next;
655 }
656 file->walk_cache_idx = idx;
657 file->walk_cache_cluster = cur;
658 *out = cur;
659 return k_ra8_ok;
660}
661
692 uint64_t pos,
693 uint64_t* out_lba,
694 uint32_t* out_off,
695 uint32_t* out_room)
696{
697 const ra8_fs_mount_t* m = file->mount;
698 const uint32_t cbytes = priv_cluster_bytes(m);
699 const uint32_t idx = (uint32_t)(pos / cbytes);
700 ra8_err_t e = priv_exfat_ensure_clusters(file, idx + 1U);
701 if (e != k_ra8_ok) {
702 return e;
703 }
704 uint32_t cur = 0U;
705 e = internal_exfat_cluster_at(file, idx, &cur);
706 if (e != k_ra8_ok) {
707 return e;
708 }
709 file->cur_cluster = cur;
710 const uint32_t in_cluster = (uint32_t)(pos % cbytes);
711 *out_lba = priv_cluster_to_lba(m, cur) + (in_cluster / priv_bps(m));
712 *out_off = in_cluster % priv_bps(m);
713 *out_room = priv_bps(m) - *out_off;
714 return k_ra8_ok;
715}
716
746{
747 while (file->valid_bytes < file->offset) {
748 uint64_t lba = 0U;
749 uint32_t off = 0U;
750 uint32_t room = 0U;
751 ra8_err_t e = internal_exfat_slice_at(file, file->valid_bytes, &lba, &off, &room);
752 if (e != k_ra8_ok) {
753 return e;
754 }
755 const uint64_t gap = file->offset - file->valid_bytes;
756 uint32_t n = room;
757 if (gap < (uint64_t)room) {
758 n = (uint32_t)gap;
759 }
760 e = priv_write_into_sector(file->mount, lba, off, k_zero_sector, n);
761 if (e != k_ra8_ok) {
762 return e;
763 }
764 file->valid_bytes += n;
765 }
766 return k_ra8_ok;
767}
768
769/* `priv_exfat_write_stream()`: see header for the documented contract. */
770ra8_err_t priv_exfat_write_stream(ra8_fs_file_t* file, const uint8_t* buf, uint32_t len)
771{
773 if (e != k_ra8_ok) {
774 return e;
775 }
776 uint32_t consumed = 0U;
777 while (consumed < len) {
778 uint64_t lba = 0U;
779 uint32_t off = 0U;
780 uint32_t room = 0U;
781 e = internal_exfat_slice_at(file, file->offset, &lba, &off, &room);
782 if (e != k_ra8_ok) {
783 return e;
784 }
785 uint32_t put = len - consumed;
786 if (put > room) {
787 put = room;
788 }
789 e = priv_write_into_sector(file->mount, lba, off, &buf[consumed], put);
790 if (e != k_ra8_ok) {
791 return e;
792 }
793 consumed += put;
794 file->offset += put;
795 if (file->offset > file->valid_bytes) {
796 file->valid_bytes = file->offset;
797 }
798 if (file->offset > file->size_bytes) {
799 file->size_bytes = file->offset;
800 }
801 }
802 return k_ra8_ok;
803}
804
805/* =============================================================================
806 * Flush: the entry set says exactly what the volume holds
807 * =============================================================================
808 */
809
840static void internal_exfat_patch_stream(const ra8_fs_file_t* file, uint8_t* strm)
841{
842 uint8_t flags = (uint8_t)k_exfat_secflag_poss;
843 uint32_t first = 0U;
844 if (file->alloc_clusters > 0U) {
845 first = file->first_cluster;
846 if (file->no_fat_chain != 0U) {
847 flags = (uint8_t)k_exfat_secflag_alloc;
848 }
849 }
850 strm[k_exfat_strm_off_flags] = flags;
851 priv_wr32(&strm[k_exfat_strm_off_clus], first);
852 priv_wr64(&strm[k_exfat_off_strm_valid], file->valid_bytes);
853 priv_wr64(&strm[k_exfat_strm_off_dlen], file->size_bytes);
854}
855
884 uint8_t* set,
885 exfat_setpos_t* at_file,
886 exfat_setpos_t* at_stream)
887{
888 exfat_cursor_t cur = {.cluster = file->entry_set_cluster,
889 .entry_in_cluster = file->entry_set_index,
890 .scanned = 0U};
891 for (uint32_t k = 0U; k < file->entry_set_count; k++) {
892 const exfat_setpos_t here = {.cluster = cur.cluster, .index = cur.entry_in_cluster};
893 const ra8_err_t e =
894 priv_exfat_next_entry(file->mount, &cur, &set[(size_t)k * (size_t)k_exfat_entry_bytes]);
895 if (e != k_ra8_ok) {
896 return e;
897 }
898 if (k == 0U) {
899 *at_file = here;
900 }
901 if (k == 1U) {
902 *at_stream = here;
903 }
904 }
905 return k_ra8_ok;
906}
907
908/* `priv_exfat_flush_set()`: see header for the documented contract. */
910{
911 const ra8_fs_mount_t* m = file->mount;
912 uint8_t set[k_exfat_max_set_bytes] = {};
913 exfat_setpos_t at_file = {};
914 exfat_setpos_t at_stream = {};
915 ra8_err_t e = internal_exfat_gather_set(file, set, &at_file, &at_stream);
916 if (e != k_ra8_ok) {
917 return e;
918 }
919 /* Stamp and patch BEFORE the checksum: it covers every byte both touch, so
920 * the other order writes a set a host `fsck` rejects (#601). */
922 /* A write also sets the archive attribute (#681): the convention a backup tool
923 * clears and the OS re-sets on modification. The low byte of the 16-bit
924 * FileAttributes field carries it; the other bits (read-only can never be set
925 * on a handle that reached a write) are preserved. */
927 (uint8_t)(set[k_exfat_off_file_attr] | (uint8_t)k_exfat_attr_archive);
930 priv_exfat_set_checksum(set, file->entry_set_count * (uint32_t)k_exfat_entry_bytes));
931 e =
932 priv_exfat_write_dir_set(m, at_file.cluster, at_file.index, set, (uint32_t)k_exfat_entry_bytes);
933 if (e != k_ra8_ok) {
934 return e;
935 }
937 at_stream.cluster,
938 at_stream.index,
940 (uint32_t)k_exfat_entry_bytes);
941}
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_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ 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 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
void priv_wr64(uint8_t *p, uint64_t v)
Encode a uint64_t into a byte buffer, little-endian.
Definition ra8_fs_fat.c:77
ra8_err_t priv_fat_set(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t value)
Write value into the FAT entry for cluster across every FAT copy.
Definition ra8_fs_fat.c:486
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
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
uint32_t priv_eoc_write(const ra8_fs_mount_t *m)
End-of-chain value to write for this FAT type.
Definition ra8_fs_fat.c:526
void priv_wr32(uint8_t *p, uint32_t v)
Encode a little-endian uint32_t into a byte buffer.
Definition ra8_fs_fat.c:68
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
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_set(const ra8_fs_mount_t *m, uint32_t cluster)
Move the next-free hint forward to cluster.
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.
const uint8_t k_zero_sector[k_ra8_fs_sector_max]
One whole sector of zero bytes, in read-only storage.
ra8_err_t priv_exfat_seal_cluster(const ra8_fs_mount_t *m, uint32_t clus)
Replace every end-of-directory marker in a cluster with an unused one.
ra8_err_t priv_exfat_zero_cluster(const ra8_fs_mount_t *m, uint32_t clus)
Zero every sector of one cluster.
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.
static ra8_err_t internal_exfat_slice_at(ra8_fs_file_t *file, uint64_t pos, uint64_t *out_lba, uint32_t *out_off, uint32_t *out_room)
Locate byte position pos on the volume, allocating to reach it.
static ra8_err_t internal_exfat_close_gap(ra8_fs_file_t *file)
Fill [valid_bytes, offset) with real zeros.
static ra8_err_t internal_exfat_cluster_at(ra8_fs_file_t *file, uint32_t idx, uint32_t *out)
Resolve the cluster at chain index idx of an already-grown file.
static void internal_exfat_patch_stream(const ra8_fs_file_t *file, uint8_t *strm)
Patch a Stream-extension entry to describe the handle's current state.
static ra8_err_t internal_exfat_link_cluster(ra8_fs_file_t *file, uint32_t next)
Attach a freshly allocated cluster to the file's allocation.
static ra8_err_t internal_exfat_dir_relen(const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint64_t new_bytes, uint8_t nofat)
Rewrite a grown directory's own Stream entry to describe its new run.
ra8_err_t priv_exfat_grow_dir(const ra8_fs_mount_t *m, exfat_dir_t *dir)
Append one zeroed cluster to a directory, keeping its entry set true.
static ra8_err_t internal_exfat_dir_link(const ra8_fs_mount_t *m, uint32_t first, uint32_t alloc, uint32_t tail, uint8_t *nofat, uint32_t next)
Attach next to a directory's run, converting to a chain if needed.
static ra8_err_t internal_exfat_dir_survey(const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t *out_alloc, uint32_t *out_tail, uint8_t *out_nofat)
Survey a directory's current allocation into run coordinates.
static ra8_err_t internal_exfat_gather_set(const ra8_fs_file_t *file, uint8_t *set, exfat_setpos_t *at_file, exfat_setpos_t *at_stream)
Read a file's entry set back into set, recording where it lives.
ra8_err_t priv_exfat_write_stream(ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
Stream bytes into an exFAT file, growing its allocation as needed.
ra8_err_t priv_exfat_ensure_clusters(ra8_fs_file_t *file, uint32_t need)
Grow a file's allocation until it owns at least need clusters.
static ra8_err_t internal_exfat_materialize_run(const ra8_fs_mount_t *m, uint32_t first, uint32_t alloc, uint32_t tail, uint32_t next)
Write a real FAT chain over a contiguous run, ending at next.
static ra8_err_t internal_exfat_dir_append(const ra8_fs_mount_t *m, const exfat_dir_t *dir, uint32_t alloc, uint32_t tail, uint8_t *nofat)
Allocate one cluster, zero it, link it to the run, seal the old tail.
static ra8_err_t internal_exfat_grow_one(ra8_fs_file_t *file)
Add one cluster to a file's allocation.
static ra8_err_t internal_exfat_pick_cluster(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t prefer, uint32_t *out)
Choose the next cluster to allocate, preferring the tail's successor.
ra8_err_t priv_exfat_flush_set(ra8_fs_file_t *file)
Rewrite a file's entry set from the handle, checksum last.
ra8_err_t priv_exfat_bitmap_mark(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint32_t count)
Mark a contiguous cluster run as allocated in the bitmap.
ra8_err_t priv_exfat_bitmap_test(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t clus, uint8_t *out_free)
Read one allocation-bitmap bit: is this cluster free?
ra8_err_t priv_exfat_bitmap_scan(const ra8_fs_mount_t *m, uint64_t bmp_lba, uint32_t need, uint32_t *out_clus)
Find a contiguous free run, starting from the mount's next-free hint.
uint16_t priv_exfat_set_checksum(const uint8_t *set, uint32_t bytes)
Compute the SetChecksum over a built directory entry set.
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_write_into_sector(const ra8_fs_mount_t *m, uint64_t lba, uint32_t off_in_sector, const uint8_t *src, uint32_t put)
Implementation of priv_write_into_sector() – one read-modify-write.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
void priv_exfat_file_stamp_write(uint8_t *file_entry)
Advance an exFAT File entry's modification (and access) stamps.
@ k_exfat_strm_off_dlen
Stream-ext DataLength (low 32 used).
@ 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_entry_bytes
Directory entry size.
@ k_exfat_off_strm_valid
Stream entry: ValidDataLength (8 B).
@ k_exfat_secflag_poss
GeneralSecondaryFlags: AllocationPossible.
@ k_exfat_secflag_alloc
AllocationPossible | NoFatChain.
@ k_exfat_off_file_attr
File entry: FileAttributes (2 bytes).
@ k_exfat_off_file_secnt
File entry: SecondaryCount.
@ k_exfat_max_set_bytes
(2 + ceil(64/15)) * 32 = 7 entries.
@ k_exfat_attr_archive
FileAttributes: archive.
@ k_exfat_set_writable
1 File + 1 Stream + 5 Name (64 chars).
@ k_cluster_first_data
Cluster numbers start at 2.
Linear cursor over a directory's 32-byte entries.
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 self_cluster
Parent cluster holding this dir's File entry; 0 => root.
uint32_t cluster
First cluster of the directory.
uint32_t self_index
File-entry index within self_cluster.
uint32_t contig_end
One past the run's last cluster; 0 => FAT-chained.
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.
Open-file state.
Cached parse of one mounted FAT volume.
uint32_t count_of_clusters
Per MS spec: data_sectors / SPC.