ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_truncate.c
Go to the documentation of this file.
1
41
42#include <stddef.h>
43#include <stdint.h>
44
45#include "ra8_attributes.h"
46#include "ra8_fs.h"
47#include "ra8_fs_fat_internal.h"
48#include "ra8_fs_meta.h"
49
50/* =============================================================================
51 * Shared helpers
52 * =============================================================================
53 */
54
82static uint64_t internal_trunc_clusters(uint64_t bytes, uint32_t cbytes)
83{
84 return (bytes + cbytes - 1U) / cbytes;
85}
86
115static ra8_err_t
116internal_trunc_walk(const ra8_fs_mount_t* m, uint32_t start, uint32_t idx, uint32_t* out)
117{
118 uint32_t cur = start;
119 for (uint32_t i = 0U; i < idx; i++) {
120 uint32_t next = 0U;
121 const ra8_err_t e = priv_fat_get(m, cur, &next);
122 if (e != k_ra8_ok) {
123 return e;
124 }
125 if (priv_is_eoc(m, next) != 0U) {
127 }
128 cur = next;
129 }
130 *out = cur;
131 return k_ra8_ok;
132}
133
165static ra8_err_t
166internal_trunc_zero_span(const ra8_fs_mount_t* m, uint32_t cluster, uint32_t lo, uint32_t hi)
167{
168 if (lo >= hi) {
169 return k_ra8_ok;
170 }
171 const uint64_t base_lba = priv_cluster_to_lba(m, cluster);
172 uint32_t off = lo;
173 while (off < hi) {
174 const uint32_t sec_idx = off / priv_bps(m);
175 const uint32_t sec_start = sec_idx * priv_bps(m);
176 const uint32_t in_sector = off - sec_start;
177 uint32_t n = priv_bps(m) - in_sector;
178 if (n > (hi - off)) {
179 n = hi - off;
180 }
181 uint8_t* const sec = priv_sec_io();
182 const ra8_err_t re = priv_read_sector(m, base_lba + sec_idx, sec);
183 if (re != k_ra8_ok) {
184 return re;
185 }
186 for (uint32_t i = 0U; i < n; i++) {
187 sec[in_sector + i] = 0U;
188 }
189 const ra8_err_t we = priv_write_sector(m, base_lba + sec_idx, sec);
190 if (we != k_ra8_ok) {
191 return we;
192 }
193 off += n;
194 }
195 return k_ra8_ok;
196}
197
198/* =============================================================================
199 * FAT12/16/32 truncate
200 * =============================================================================
201 */
202
230static ra8_err_t internal_fat_trunc_shrink(ra8_fs_file_t* file, uint32_t new_size, uint32_t cbytes)
231{
232 const ra8_fs_mount_t* m = file->mount;
233 const uint32_t new_nclus = (uint32_t)internal_trunc_clusters(new_size, cbytes);
234 if (new_nclus == 0U) {
235 if (file->first_cluster >= (uint32_t)k_cluster_first_data) {
236 const ra8_err_t e = priv_free_chain(m, file->first_cluster);
237 if (e != k_ra8_ok) {
238 return e;
239 }
240 }
241 file->first_cluster = 0U;
242 file->size_bytes = new_size;
243 return k_ra8_ok;
244 }
245 uint32_t new_tail = 0U;
246 const ra8_err_t we = internal_trunc_walk(m, file->first_cluster, new_nclus - 1U, &new_tail);
247 if (we != k_ra8_ok) {
248 return we;
249 }
250 uint32_t succ = 0U;
251 const ra8_err_t ge = priv_fat_get(m, new_tail, &succ);
252 if (ge != k_ra8_ok) {
253 return ge;
254 }
255 if (priv_is_eoc(m, succ) == 0U) {
256 ra8_err_t e = priv_fat_set(m, new_tail, priv_eoc_write(m));
257 if (e != k_ra8_ok) {
258 return e;
259 }
260 e = priv_free_chain(m, succ);
261 if (e != k_ra8_ok) {
262 return e;
263 }
264 }
265 file->size_bytes = new_size;
266 return k_ra8_ok;
267}
268
299 uint32_t tail,
300 uint32_t have,
301 uint32_t new_nclus,
302 uint32_t cbytes)
303{
304 const ra8_fs_mount_t* m = file->mount;
305 uint32_t cur = tail;
306 if (have == 0U) {
308 if (e != k_ra8_ok) {
309 return e;
310 }
311 file->first_cluster = cur;
312 e = internal_trunc_zero_span(m, cur, 0U, cbytes);
313 if (e != k_ra8_ok) {
314 return e;
315 }
316 have = 1U;
317 }
318 while (have < new_nclus) {
319 uint32_t c = 0U;
321 if (e != k_ra8_ok) {
322 return e;
323 }
324 e = priv_fat_set(m, cur, c);
325 if (e != k_ra8_ok) {
326 return e;
327 }
328 e = internal_trunc_zero_span(m, c, 0U, cbytes);
329 if (e != k_ra8_ok) {
330 return e;
331 }
332 cur = c;
333 have++;
334 }
335 return k_ra8_ok;
336}
337
366static ra8_err_t internal_fat_trunc_grow(ra8_fs_file_t* file, uint32_t new_size, uint32_t cbytes)
367{
368 const ra8_fs_mount_t* m = file->mount;
369 const uint32_t old_size = (uint32_t)file->size_bytes;
370 const uint32_t old_nclus = (uint32_t)internal_trunc_clusters(old_size, cbytes);
371 const uint32_t new_nclus = (uint32_t)internal_trunc_clusters(new_size, cbytes);
372 uint32_t tail = 0U;
373 if (old_nclus > 0U) {
374 const ra8_err_t we = internal_trunc_walk(m, file->first_cluster, old_nclus - 1U, &tail);
375 if (we != k_ra8_ok) {
376 return we;
377 }
378 const uint32_t base = (old_nclus - 1U) * cbytes;
379 const uint32_t cap = old_nclus * cbytes;
380 const uint32_t span_hi = (new_size < cap) ? new_size : cap;
381 const ra8_err_t ze = internal_trunc_zero_span(m, tail, old_size - base, span_hi - base);
382 if (ze != k_ra8_ok) {
383 return ze;
384 }
385 }
386 const ra8_err_t ee = internal_fat_trunc_extend(file, tail, old_nclus, new_nclus, cbytes);
387 if (ee != k_ra8_ok) {
388 return ee;
389 }
390 file->size_bytes = new_size;
391 return k_ra8_ok;
392}
393
419{
420 const ra8_fs_mount_t* m = file->mount;
421 uint8_t* const sec = priv_sec_walk();
422 const ra8_err_t re = priv_read_sector(m, file->dir_entry_lba, sec);
423 if (re != k_ra8_ok) {
424 return re;
425 }
426 priv_entry_set_cluster_size(&sec[file->dir_entry_idx],
427 file->first_cluster,
428 (uint32_t)file->size_bytes);
429 priv_fat_entry_stamp_write(&sec[file->dir_entry_idx]);
430 const ra8_err_t we = priv_write_sector(m, file->dir_entry_lba, sec);
431 if (we != k_ra8_ok) {
432 return we;
433 }
434 file->dirty = 1U;
435 return k_ra8_ok;
436}
437
462static ra8_err_t internal_fat_trunc(ra8_fs_file_t* file, uint32_t new_size)
463{
464 const uint32_t cbytes = priv_cluster_bytes(file->mount);
466 if (new_size < file->size_bytes) {
467 e = internal_fat_trunc_shrink(file, new_size, cbytes);
468 } else if (new_size > file->size_bytes) {
469 e = internal_fat_trunc_grow(file, new_size, cbytes);
470 }
471 if (e != k_ra8_ok) {
472 return e;
473 }
474 file->cur_cluster = file->first_cluster;
475 file->walk_cache_idx = 0U;
476 file->walk_cache_cluster = file->first_cluster;
477 return internal_fat_trunc_commit(file);
478}
479
480/* =============================================================================
481 * exFAT truncate
482 * =============================================================================
483 */
484
514static ra8_err_t
515internal_exfat_free_run(const ra8_fs_mount_t* m, uint32_t first, uint64_t nbytes, uint8_t nofat)
516{
517 uint8_t strm[k_exfat_entry_bytes] = {};
518 priv_wr32(&strm[k_exfat_strm_off_clus], first);
519 priv_wr64(&strm[k_exfat_strm_off_dlen], nbytes);
521 (nofat != 0U) ? (uint8_t)k_exfat_secflag_alloc : (uint8_t)k_exfat_secflag_poss;
522 return priv_exfat_free_clusters(m, strm);
523}
524
553static ra8_err_t
554internal_exfat_shrink_chain_tail(ra8_fs_file_t* file, uint32_t new_nclus, uint32_t cbytes)
555{
556 const ra8_fs_mount_t* m = file->mount;
557 uint32_t new_tail = 0U;
558 const ra8_err_t we = internal_trunc_walk(m, file->first_cluster, new_nclus - 1U, &new_tail);
559 if (we != k_ra8_ok) {
560 return we;
561 }
562 uint32_t succ = 0U;
563 const ra8_err_t ge = priv_fat_get(m, new_tail, &succ);
564 if (ge != k_ra8_ok) {
565 return ge;
566 }
567 ra8_err_t e = priv_fat_set(m, new_tail, priv_eoc_write(m));
568 if (e != k_ra8_ok) {
569 return e;
570 }
571 e = internal_exfat_free_run(m, succ, cbytes, 0U);
572 if (e != k_ra8_ok) {
573 return e;
574 }
575 file->tail_cluster = new_tail;
576 return k_ra8_ok;
577}
578
606static ra8_err_t
607internal_exfat_trunc_shrink(ra8_fs_file_t* file, uint64_t new_size, uint32_t cbytes)
608{
609 const ra8_fs_mount_t* m = file->mount;
610 const uint32_t new_nclus = (uint32_t)internal_trunc_clusters(new_size, cbytes);
611 if (new_nclus < file->alloc_clusters) {
612 if (new_nclus == 0U) {
613 const ra8_err_t e =
614 internal_exfat_free_run(m, file->first_cluster, file->size_bytes, file->no_fat_chain);
615 if (e != k_ra8_ok) {
616 return e;
617 }
618 file->first_cluster = 0U;
619 file->no_fat_chain = 1U;
620 file->tail_cluster = 0U;
621 } else if (file->no_fat_chain != 0U) {
622 const uint32_t first = file->first_cluster + new_nclus;
623 const ra8_err_t e =
625 first,
626 (uint64_t)(file->alloc_clusters - new_nclus) * cbytes,
627 1U);
628 if (e != k_ra8_ok) {
629 return e;
630 }
631 file->tail_cluster = file->first_cluster + new_nclus - 1U;
632 } else {
633 const ra8_err_t e = internal_exfat_shrink_chain_tail(file, new_nclus, cbytes);
634 if (e != k_ra8_ok) {
635 return e;
636 }
637 }
638 file->alloc_clusters = new_nclus;
639 }
640 file->size_bytes = new_size;
641 if (file->valid_bytes > new_size) {
642 file->valid_bytes = new_size;
643 }
644 return k_ra8_ok;
645}
646
676static ra8_err_t internal_exfat_trunc(ra8_fs_file_t* file, uint64_t new_size)
677{
678 const uint32_t cbytes = priv_cluster_bytes(file->mount);
679 if (new_size < file->size_bytes) {
680 const ra8_err_t e = internal_exfat_trunc_shrink(file, new_size, cbytes);
681 if (e != k_ra8_ok) {
682 return e;
683 }
684 } else if (new_size > file->size_bytes) {
685 /* Sized before a single cluster moves: a grow past the volume's whole
686 * cluster count can never succeed, and discovering that AFTER the bitmap
687 * ran dry would leave the volume full for no gain. */
688 const uint64_t want = internal_trunc_clusters(new_size, cbytes);
689 if (want > (uint64_t)file->mount->count_of_clusters) {
690 return k_ra8_err_no_mem;
691 }
692 const ra8_err_t e = priv_exfat_ensure_clusters(file, (uint32_t)want);
693 if (e != k_ra8_ok) {
694 return e;
695 }
696 file->size_bytes = new_size;
697 } else {
698 return k_ra8_ok;
699 }
700 return priv_exfat_flush_set(file);
701}
702
703/* =============================================================================
704 * Dispatch and the public entry point
705 * =============================================================================
706 */
707
741RA8_EXPECTS_LOCK("ra8_fs_lock")
742static ra8_err_t internal_truncate_locked(ra8_fs_file_t* file, uint64_t new_size)
743{
744 if (file == nullptr) {
745 return k_ra8_err_null_ptr;
746 }
747 if (file->in_use == 0U || file->mode == k_ra8_fs_mode_read) {
748 return k_ra8_err_invalid_state;
749 }
751 if (file->mount->type == k_ra8_fs_type_exfat) {
752 e = internal_exfat_trunc(file, new_size);
753 } else if (new_size > (uint64_t)k_ra8_fs_fat_max_file_bytes) {
754 /* The FAT boundary of #676: `DIR_FileSize` cannot record 4 GiB or more,
755 * so the request is refused rather than wrapped (see ra8_fs_meta.h). */
757 } else {
758 e = internal_fat_trunc(file, (uint32_t)new_size);
759 }
760 if (e != k_ra8_ok) {
761 return e;
762 }
763 if (file->offset > file->size_bytes) {
764 file->offset = file->size_bytes;
765 }
766 return k_ra8_ok;
767}
768
769RA8_OWNS_RESOURCE("ra8_fs_lock")
770ra8_err_t ra8_fs_truncate(ra8_fs_file_t* file, uint64_t new_size)
771{
773 const ra8_err_t err = internal_truncate_locked(file, new_size);
775 return err;
776}
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_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ 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
@ 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
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
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
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
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_io(void)
The IO-role sector buffer (leaf data / bitmap sector transfers).
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
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_ensure_clusters(ra8_fs_file_t *file, uint32_t need)
Grow a file's allocation until it owns at least need clusters.
ra8_err_t priv_exfat_flush_set(ra8_fs_file_t *file)
Rewrite a file's entry set from the handle, checksum last.
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_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.
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_fat_entry_stamp_write(uint8_t *entry)
Advance a FAT directory entry's modification time and access date.
static ra8_err_t internal_fat_trunc_extend(ra8_fs_file_t *file, uint32_t tail, uint32_t have, uint32_t new_nclus, uint32_t cbytes)
Append zeroed clusters to a FAT file until it owns new_nclus.
static ra8_err_t internal_fat_trunc_grow(ra8_fs_file_t *file, uint32_t new_size, uint32_t cbytes)
Grow a FAT file to new_size, zero-filling [old_size, new_size).
static uint64_t internal_trunc_clusters(uint64_t bytes, uint32_t cbytes)
Clusters needed to hold bytes at cbytes bytes per cluster.
static ra8_err_t internal_trunc_zero_span(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t lo, uint32_t hi)
Write real zeros over the byte range [lo, hi) inside one cluster.
static ra8_err_t internal_truncate_locked(ra8_fs_file_t *file, uint64_t new_size)
Truncate the file behind file – the guarded body of ra8_fs_truncate.
static ra8_err_t internal_fat_trunc_commit(ra8_fs_file_t *file)
Rewrite a FAT file's directory entry after its length changed.
static ra8_err_t internal_trunc_walk(const ra8_fs_mount_t *m, uint32_t start, uint32_t idx, uint32_t *out)
Walk idx clusters forward along a chain from start.
static ra8_err_t internal_exfat_free_run(const ra8_fs_mount_t *m, uint32_t first, uint64_t nbytes, uint8_t nofat)
Release a cluster run by describing it as a Stream-extension entry.
static ra8_err_t internal_exfat_shrink_chain_tail(ra8_fs_file_t *file, uint32_t new_nclus, uint32_t cbytes)
Trim a FAT-chained exFAT file to new_nclus clusters.
static ra8_err_t internal_fat_trunc(ra8_fs_file_t *file, uint32_t new_size)
Truncate a FAT file to new_size and commit its directory entry.
static ra8_err_t internal_exfat_trunc(ra8_fs_file_t *file, uint64_t new_size)
Truncate an exFAT file to new_size and flush its entry set.
static ra8_err_t internal_fat_trunc_shrink(ra8_fs_file_t *file, uint32_t new_size, uint32_t cbytes)
Free the cluster tail past new_size on a FAT file.
static ra8_err_t internal_exfat_trunc_shrink(ra8_fs_file_t *file, uint64_t new_size, uint32_t cbytes)
Free an exFAT file's cluster tail past new_size.
@ 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_entry_bytes
Directory entry size.
@ k_exfat_secflag_poss
GeneralSecondaryFlags: AllocationPossible.
@ k_exfat_secflag_alloc
AllocationPossible | NoFatChain.
@ k_cluster_first_data
Cluster numbers start at 2.
Volume-level metadata: free space, volume label, and per-entry utime.
ra8_err_t ra8_fs_truncate(ra8_fs_file_t *file, uint64_t new_size)
Set an open file's length to new_size, shrinking or growing it.
@ k_ra8_fs_fat_max_file_bytes
4 GiB - 1: max DIR_FileSize.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_mode_read
Read-only, must exist.
Open-file state.
Cached parse of one mounted FAT volume.