ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mkfontimg_host.c
Go to the documentation of this file.
1
15
16#include <errno.h>
17#include <fcntl.h>
18#include <limits.h>
19#include <stddef.h>
20#include <stdint.h>
21#include <stdio.h> /* POSIX renameat declaration; no hosted stream is used. */
22#include <string.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <unistd.h>
26
27#include "mkfontimg_internal.h"
28#include "ra8_attributes.h"
29#include "ra8_err.h"
30#include "ra8_fs.h"
31
32#ifndef O_CLOEXEC
34#define O_CLOEXEC (0)
35#endif
36
37#ifndef O_DIRECTORY
39#define O_DIRECTORY (0)
40#endif
41
42#ifndef O_NOFOLLOW
44#define O_NOFOLLOW (0)
45#endif
46
55
57typedef struct {
58 dev_t device;
59 ino_t inode;
60 uint64_t size;
61 time_t modified_sec;
63 time_t changed_sec;
66
67RA8_PRIV void priv_mkfontimg_diag(const char* text)
68{
69 size_t offset = 0U;
70 size_t length = strlen(text);
71 while (offset < length) {
72 const ssize_t written = write(STDERR_FILENO, &text[offset], length - offset);
73 if ((written < 0) && (errno == EINTR)) {
74 continue;
75 }
76 if (written <= 0) {
77 return;
78 }
79 offset += (size_t)written;
80 }
81}
82
84{
85 char reverse[k_host_decimal_digits];
86 char forward[k_host_decimal_digits + 1U];
87 size_t digits = 0U;
88 do {
89 reverse[digits++] = (char)('0' + (value % (uint64_t)k_host_decimal_base));
90 value /= (uint64_t)k_host_decimal_base;
91 } while (value != 0U);
92 for (size_t i = 0U; i < digits; ++i) {
93 forward[i] = reverse[digits - i - 1U];
94 }
95 forward[digits] = '\0';
96 priv_mkfontimg_diag(forward);
97}
98
116RA8_INTERNAL static bool
117internal_pread_exact(int fd, uint64_t offset, uint8_t* bytes, size_t length)
118{
119 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
120 return false;
121 }
122 size_t done = 0U;
123 while (done < length) {
124 size_t request = length - done;
125 if (request > (size_t)SSIZE_MAX) {
126 request = (size_t)SSIZE_MAX;
127 }
128 const ssize_t got = pread(fd, &bytes[done], request, (off_t)(offset + done));
129 if ((got < 0) && (errno == EINTR)) {
130 continue;
131 }
132 if (got <= 0) {
133 return false;
134 }
135 done += (size_t)got;
136 }
137 return true;
138}
139
157RA8_INTERNAL static bool
158internal_pwrite_exact(int fd, uint64_t offset, const uint8_t* bytes, size_t length)
159{
160 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
161 return false;
162 }
163 size_t done = 0U;
164 while (done < length) {
165 size_t request = length - done;
166 if (request > (size_t)SSIZE_MAX) {
167 request = (size_t)SSIZE_MAX;
168 }
169 const ssize_t put = pwrite(fd, &bytes[done], request, (off_t)(offset + done));
170 if ((put < 0) && (errno == EINTR)) {
171 continue;
172 }
173 if (put <= 0) {
174 return false;
175 }
176 done += (size_t)put;
177 }
178 return true;
179}
180
200 uint64_t lba,
201 uint32_t count,
202 uint64_t* out_offset,
203 size_t* out_bytes)
204{
205 if (lba > disk->block_count || (uint64_t)count > (disk->block_count - lba) ||
206 lba > (UINT64_MAX / disk->block_size) || count > (SIZE_MAX / disk->block_size)) {
207 return false;
208 }
209 const uint64_t offset = lba * disk->block_size;
210 const size_t bytes = (size_t)count * disk->block_size;
211 if (offset > (uint64_t)INT64_MAX || bytes > ((uint64_t)INT64_MAX - offset)) {
212 return false;
213 }
214 *out_offset = offset;
215 *out_bytes = bytes;
216 return true;
217}
218
239internal_disk_read(void* ctx, uint64_t lba, uint32_t count, uint8_t* buffer)
240{
242 uint64_t offset;
243 size_t bytes;
244 if (disk == nullptr || buffer == nullptr || disk->io_failed ||
245 !internal_block_range(disk, lba, count, &offset, &bytes)) {
247 }
248 if (!internal_pread_exact(disk->fd, offset, buffer, bytes)) {
249 disk->io_failed = true;
250 return k_ra8_fail;
251 }
252 return k_ra8_ok;
253}
254
275internal_disk_write(void* ctx, uint64_t lba, uint32_t count, const uint8_t* buffer)
276{
278 uint64_t offset;
279 size_t bytes;
280 if (disk == nullptr || buffer == nullptr || disk->io_failed ||
281 !internal_block_range(disk, lba, count, &offset, &bytes)) {
283 }
284 if (!internal_pwrite_exact(disk->fd, offset, buffer, bytes)) {
285 disk->io_failed = true;
286 return k_ra8_fail;
287 }
288 return k_ra8_ok;
289}
290
308internal_disk_capacity(void* ctx, uint64_t* block_count, uint32_t* block_size)
309{
310 const mkfontimg_disk_t* disk = (const mkfontimg_disk_t*)ctx;
311 if (disk == nullptr || block_count == nullptr || block_size == nullptr) {
312 return k_ra8_err_null_ptr;
313 }
314 *block_count = disk->block_count;
315 *block_size = disk->block_size;
316 return k_ra8_ok;
317}
318
335RA8_INTERNAL static bool internal_split_output(const char* path,
336 char parent[k_mkfontimg_host_path_cap],
337 char leaf[k_mkfontimg_host_name_cap])
338{
339 const size_t length = strlen(path);
340 if (length == 0U || length >= (size_t)k_mkfontimg_host_path_cap) {
341 return false;
342 }
343 size_t slash = length;
344 while (slash > 0U && path[slash - 1U] != '/') {
345 --slash;
346 }
347 const size_t leaf_bytes = length - slash;
348 if (leaf_bytes == 0U || leaf_bytes >= (size_t)k_mkfontimg_host_name_cap) {
349 return false;
350 }
351 (void)memcpy(leaf, &path[slash], leaf_bytes);
352 leaf[leaf_bytes] = '\0';
353 if (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0) {
354 return false;
355 }
356 if (slash == 0U) {
357 parent[0] = '.';
358 parent[1] = '\0';
359 } else if (slash == 1U) {
360 parent[0] = '/';
361 parent[1] = '\0';
362 } else {
363 const size_t parent_bytes = slash - 1U;
364 (void)memcpy(parent, path, parent_bytes);
365 parent[parent_bytes] = '\0';
366 }
367 return true;
368}
369
386RA8_INTERNAL static bool
387internal_temp_name(char out[k_mkfontimg_host_name_cap], uint64_t process, uint32_t attempt)
388{
389 static const char s_prefix[] = ".mkfontimg.tmp.";
390 char reverse[k_host_decimal_digits];
391 size_t offset = sizeof(s_prefix) - 1U;
392 (void)memcpy(out, s_prefix, offset);
393 uint64_t values[2] = {process, attempt};
394 for (uint8_t field = 0U; field < 2U; ++field) {
395 size_t digits = 0U;
396 do {
397 reverse[digits++] = (char)('0' + (values[field] % (uint64_t)k_host_decimal_base));
398 values[field] /= (uint64_t)k_host_decimal_base;
399 } while (values[field] != 0U);
400 if (offset + digits + 2U > (size_t)k_mkfontimg_host_name_cap) {
401 return false;
402 }
403 while (digits > 0U) {
404 out[offset++] = reverse[--digits];
405 }
406 out[offset++] = (field == 0U) ? '.' : '\0';
407 }
408 return true;
409}
410
411RA8_PRIV bool priv_mkfontimg_host_begin(const char* final_path,
412 uint64_t block_count,
413 uint32_t block_size,
414 mkfontimg_host_t* host)
415{
416 *host = (mkfontimg_host_t){.directory_fd = -1, .image_fd = -1};
417 char parent[k_mkfontimg_host_path_cap];
418 if (block_count == 0U || block_size == 0U || block_count > (UINT64_MAX / block_size) ||
419 !internal_split_output(final_path, parent, host->final_name)) {
420 return false;
421 }
422 const uint64_t image_bytes = block_count * block_size;
423 if (image_bytes > (uint64_t)INT64_MAX) {
424 return false;
425 }
426 host->directory_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
427 if (host->directory_fd < 0) {
428 return false;
429 }
430 for (uint32_t attempt = 0U; attempt < (uint32_t)k_host_temp_attempts; ++attempt) {
431 if (!internal_temp_name(host->temp_name, (uint64_t)getpid(), attempt)) {
432 break;
433 }
434 host->image_fd = openat(host->directory_fd,
435 host->temp_name,
436 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
437 (mode_t)k_host_create_mode);
438 if (host->image_fd >= 0 || errno != EEXIST) {
439 break;
440 }
441 }
442 if (host->image_fd < 0) {
444 return false;
445 }
446 host->temp_exists = true;
447 if (ftruncate(host->image_fd, (off_t)image_bytes) != 0) {
449 return false;
450 }
451 host->disk =
452 (mkfontimg_disk_t){.block_count = block_count, .block_size = block_size, .fd = host->image_fd};
453 host->backend = (ra8_fs_backend_t){.read_block = internal_disk_read,
454 .write_block = internal_disk_write,
455 .get_capacity = internal_disk_capacity,
456 .ctx = &host->disk};
457 return true;
458}
459
460RA8_PRIV bool
461priv_mkfontimg_host_seed(mkfontimg_host_t* host, const uint8_t* bytes, uint32_t length)
462{
463 if (host == nullptr || bytes == nullptr || host->image_fd < 0 ||
464 (uint64_t)length > (host->disk.block_count * host->disk.block_size)) {
465 return false;
466 }
467 if (!internal_pwrite_exact(host->image_fd, 0U, bytes, length)) {
468 host->disk.io_failed = true;
469 return false;
470 }
471 return true;
472}
473
487RA8_INTERNAL static host_input_identity_t internal_identity(const struct stat* metadata)
488{
489#ifdef __APPLE__
490 const long modified_nsec = metadata->st_mtimespec.tv_nsec;
491 const long changed_nsec = metadata->st_ctimespec.tv_nsec;
492#else
493 const long modified_nsec = metadata->st_mtim.tv_nsec;
494 const long changed_nsec = metadata->st_ctim.tv_nsec;
495#endif
496 return (host_input_identity_t){.device = metadata->st_dev,
497 .inode = metadata->st_ino,
498 .size = (uint64_t)metadata->st_size,
499 .modified_sec = metadata->st_mtime,
500 .modified_nsec = modified_nsec,
501 .changed_sec = metadata->st_ctime,
502 .changed_nsec = changed_nsec};
503}
504
521 const host_input_identity_t* right)
522{
523 return left->device == right->device && left->inode == right->inode &&
524 left->size == right->size && left->modified_sec == right->modified_sec &&
525 left->modified_nsec == right->modified_nsec && left->changed_sec == right->changed_sec &&
526 left->changed_nsec == right->changed_nsec;
527}
528
547RA8_INTERNAL static bool internal_input_open(const char* path,
548 uint64_t minimum_bytes,
549 uint64_t maximum_bytes,
550 int* out_fd,
551 host_input_identity_t* identity)
552{
553 const int fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
554 if (fd < 0) {
555 return false;
556 }
557 struct stat metadata = {};
558 if (fstat(fd, &metadata) != 0 || !S_ISREG(metadata.st_mode) || metadata.st_size < 0 ||
559 (uint64_t)metadata.st_size < minimum_bytes || (uint64_t)metadata.st_size > maximum_bytes) {
560 (void)close(fd);
561 return false;
562 }
563 *identity = internal_identity(&metadata);
564 *out_fd = fd;
565 return true;
566}
567
584RA8_INTERNAL static bool
585internal_stream(int input_fd, const host_input_identity_t* identity, ra8_fs_file_t* card)
586{
587 uint8_t chunk[k_host_stream_chunk];
588 uint64_t offset = 0U;
589 bool ok = true;
590 while (ok && offset < identity->size) {
591 uint32_t take = (uint32_t)(identity->size - offset);
592 if (take > (uint32_t)sizeof(chunk)) {
593 take = (uint32_t)sizeof(chunk);
594 }
595 ok = internal_pread_exact(input_fd, offset, chunk, take) &&
596 ra8_fs_write(card, chunk, take) == k_ra8_ok;
597 offset += take;
598 }
599 struct stat after = {};
600 if (fstat(input_fd, &after) != 0) {
601 ok = false;
602 } else {
603 const host_input_identity_t observed = internal_identity(&after);
604 if (!internal_identity_equal(identity, &observed)) {
605 ok = false;
606 }
607 }
608 return ok;
609}
610
627RA8_INTERNAL static bool
628internal_compare(int input_fd, const host_input_identity_t* identity, ra8_fs_file_t* card)
629{
630 uint8_t source[k_host_stream_chunk];
631 uint8_t copied[k_host_stream_chunk];
632 uint64_t offset = 0U;
633 bool ok = true;
634 while (ok && offset < identity->size) {
635 uint32_t take = (uint32_t)(identity->size - offset);
636 if (take > (uint32_t)sizeof(source)) {
637 take = (uint32_t)sizeof(source);
638 }
639 uint32_t got = 0U;
640 ok = internal_pread_exact(input_fd, offset, source, take) &&
641 ra8_fs_read(card, copied, take, &got) == k_ra8_ok && got == take &&
642 memcmp(source, copied, take) == 0;
643 offset += take;
644 }
645 struct stat after = {};
646 if (fstat(input_fd, &after) != 0) {
647 ok = false;
648 } else {
649 const host_input_identity_t observed = internal_identity(&after);
650 if (!internal_identity_equal(identity, &observed)) {
651 ok = false;
652 }
653 }
654 return ok;
655}
656
679 const char* input_path,
680 const char* card_name,
681 uint64_t minimum_bytes,
682 uint64_t maximum_bytes,
683 host_input_identity_t* out_identity)
684{
685 int input_fd = -1;
686 if (!internal_input_open(input_path, minimum_bytes, maximum_bytes, &input_fd, out_identity)) {
687 return false;
688 }
689 ra8_fs_file_t* card = nullptr;
690 bool ok = ra8_fs_open(mount, card_name, k_ra8_fs_mode_write, &card) == k_ra8_ok;
691 if (ok) {
692 ok = internal_stream(input_fd, out_identity, card);
693 if (ra8_fs_close(card) != k_ra8_ok) {
694 ok = false;
695 }
696 }
697 if (close(input_fd) != 0) {
698 ok = false;
699 }
700 return ok;
701}
702
725 const char* input_path,
726 const char* card_name,
727 uint64_t minimum_bytes,
728 uint64_t maximum_bytes,
729 const host_input_identity_t* identity)
730{
731 int input_fd = -1;
733 if (!internal_input_open(input_path, minimum_bytes, maximum_bytes, &input_fd, &again) ||
734 !internal_identity_equal(identity, &again)) {
735 if (input_fd >= 0) {
736 (void)close(input_fd);
737 }
738 return false;
739 }
740 uint64_t card_size = 0U;
741 ra8_fs_file_t* card = nullptr;
742 bool ok = ra8_fs_open(mount, card_name, k_ra8_fs_mode_read, &card) == k_ra8_ok &&
743 ra8_fs_size(card, &card_size) == k_ra8_ok && card_size == identity->size;
744 if (ok) {
745 ok = internal_compare(input_fd, identity, card);
746 }
747 if (card != nullptr && ra8_fs_close(card) != k_ra8_ok) {
748 ok = false;
749 }
750 if (close(input_fd) != 0) {
751 ok = false;
752 }
753 return ok;
754}
755
757 ra8_fs_mount_t* mount,
758 const char* input_path,
759 const char* card_name,
760 uint64_t minimum_bytes,
761 uint64_t maximum_bytes,
762 uint64_t* out_bytes)
763{
764 host_input_identity_t identity;
765 if (!internal_mkfontimg_write(mount,
766 input_path,
767 card_name,
768 minimum_bytes,
769 maximum_bytes,
770 &identity)) {
771 return false;
772 }
773 bool ok = internal_mkfontimg_verify(mount,
774 input_path,
775 card_name,
776 minimum_bytes,
777 maximum_bytes,
778 &identity);
779 if (host->disk.io_failed) {
780 ok = false;
781 }
782 if (ok) {
783 *out_bytes = identity.size;
784 }
785 return ok;
786}
787
789{
790 bool ok = !host->disk.io_failed && fsync(host->image_fd) == 0;
791 if (close(host->image_fd) != 0) {
792 ok = false;
793 }
794 host->image_fd = -1;
795 if (ok &&
796 renameat(host->directory_fd, host->temp_name, host->directory_fd, host->final_name) == 0) {
797 host->temp_exists = false;
798 if (fsync(host->directory_fd) != 0) {
799 ok = false;
800 }
801 } else {
802 ok = false;
803 }
804 if (host->temp_exists) {
805 (void)unlinkat(host->directory_fd, host->temp_name, 0);
806 host->temp_exists = false;
807 }
808 if (close(host->directory_fd) != 0) {
809 ok = false;
810 }
811 host->directory_fd = -1;
812 return ok;
813}
814
816{
817 if (host->image_fd >= 0) {
818 (void)close(host->image_fd);
819 host->image_fd = -1;
820 }
821 if (host->temp_exists && host->directory_fd >= 0) {
822 (void)unlinkat(host->directory_fd, host->temp_name, 0);
823 host->temp_exists = false;
824 }
825 if (host->directory_fd >= 0) {
826 (void)close(host->directory_fd);
827 host->directory_fd = -1;
828 }
829}
static ra8_err_t internal_disk_write(void *ctx, uint64_t lba, uint32_t count, const uint8_t *buffer)
Write blocks into the sparse unpublished image.
static bool internal_block_range(const mk_disk_t *disk, uint64_t lba, uint32_t count, uint64_t *out_offset, size_t *out_bytes)
Translate one sector range into bounded hosted byte coordinates.
static ra8_err_t internal_disk_read(void *ctx, uint64_t lba, uint32_t count, uint8_t *buffer)
Read blocks from the sparse unpublished image.
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
static ra8_err_t internal_disk_capacity(void *ctx, uint64_t *block_count, uint32_t *block_size)
Report the fixed card-image geometry.
#define O_NOFOLLOW
Zero fallback paired with explicit no-follow metadata validation.
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
static host_input_identity_t internal_identity(const struct stat *metadata)
Capture all mutation-sensitive fields from one successful fstat.
void priv_mkfontimg_diag(const char *text)
Write one complete best-effort diagnostic fragment to standard error.
static ra8_err_t internal_disk_write(void *ctx, uint64_t lba, uint32_t count, const uint8_t *buffer)
Write sectors into the sparse unpublished image.
bool priv_mkfontimg_host_seed(mkfontimg_host_t *host, const uint8_t *bytes, uint32_t length)
Seed exact bytes at the beginning of the temporary image.
bool priv_mkfontimg_host_begin(const char *final_path, uint64_t block_count, uint32_t block_size, mkfontimg_host_t *host)
Create, size, and bind one private sibling-temporary image.
static bool internal_identity_equal(const host_input_identity_t *left, const host_input_identity_t *right)
Compare two initialized input identities without inspecting padding.
static bool internal_input_open(const char *path, uint64_t minimum_bytes, uint64_t maximum_bytes, int *out_fd, host_input_identity_t *identity)
Open and validate one non-symlink regular input.
static ra8_err_t internal_disk_read(void *ctx, uint64_t lba, uint32_t count, uint8_t *buffer)
Read sectors from the sparse unpublished image.
host_limit_t
Fixed I/O, naming, and formatting bounds.
@ k_host_stream_chunk
Bounded copy chunk bytes.
@ k_host_temp_attempts
Exclusive-create retry bound.
@ k_host_decimal_base
Decimal conversion radix.
@ k_host_decimal_digits
Maximum uint64_t digits.
@ k_host_create_mode
Hosted output creation mode.
bool priv_mkfontimg_host_commit(mkfontimg_host_t *host)
Sync and atomically publish a complete temporary image.
static bool internal_mkfontimg_verify(ra8_fs_mount_t *mount, const char *input_path, const char *card_name, uint64_t minimum_bytes, uint64_t maximum_bytes, const host_input_identity_t *identity)
Reopen the host input and card file and verify byte-for-byte identity.
void priv_mkfontimg_diag_u64(uint64_t value)
Write one unsigned decimal value to standard error without stdio.
void priv_mkfontimg_host_abort(mkfontimg_host_t *host)
Close and unlink an unpublished temporary image.
bool priv_mkfontimg_host_copy(const mkfontimg_host_t *host, ra8_fs_mount_t *mount, const char *input_path, const char *card_name, uint64_t minimum_bytes, uint64_t maximum_bytes, uint64_t *out_bytes)
Stream one stable host input into a card file and verify it by reread.
static ra8_err_t internal_disk_capacity(void *ctx, uint64_t *block_count, uint32_t *block_size)
Report the fixed sparse-image geometry.
static bool internal_pread_exact(int fd, uint64_t offset, uint8_t *bytes, size_t length)
Read an exact positioned byte range with bounded retries.
static bool internal_stream(int input_fd, const host_input_identity_t *identity, ra8_fs_file_t *card)
Stream the first pass from host input to one new card file.
static bool internal_temp_name(char out[k_mkfontimg_host_name_cap], uint64_t process, uint32_t attempt)
Build one collision-resistant hidden temporary leaf.
static bool internal_split_output(const char *path, char parent[k_mkfontimg_host_path_cap], char leaf[k_mkfontimg_host_name_cap])
Split one output path into bounded parent and leaf components.
static bool internal_pwrite_exact(int fd, uint64_t offset, const uint8_t *bytes, size_t length)
Write an exact positioned byte range with bounded retries.
static bool internal_block_range(const mkfontimg_disk_t *disk, uint64_t lba, uint32_t count, uint64_t *out_offset, size_t *out_bytes)
Translate one sector range to a representable byte range.
static bool internal_compare(int input_fd, const host_input_identity_t *identity, ra8_fs_file_t *card)
Compare a second host-input pass with the generated card file.
static bool internal_mkfontimg_write(ra8_fs_mount_t *mount, const char *input_path, const char *card_name, uint64_t minimum_bytes, uint64_t maximum_bytes, host_input_identity_t *out_identity)
Open the host input and stream it into a fresh card file.
Module-private bounded host-storage composition for mkfontimg.
@ k_mkfontimg_host_name_cap
Hosted leaf capacity.
@ k_mkfontimg_host_path_cap
Hosted path capacity.
-proof
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_fail
Generic unspecified failure.
Definition ra8_err.h:133
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ 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
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
int renameat(int old_dir_fd, const char *old_path, int new_dir_fd, const char *new_path)
Rename one directory-relative path to another atomically.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
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).
ra8_err_t ra8_fs_write(ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
Write len bytes; allocate new clusters from FAT free-space scan as needed.
ra8_err_t ra8_fs_size(const ra8_fs_file_t *file, uint64_t *out_bytes)
Report the file's size in bytes (64-bit on exFAT, #676).
ra8_err_t ra8_fs_read(ra8_fs_file_t *file, uint8_t *buf, uint32_t max_len, uint32_t *got_len)
Read up to max_len bytes; advance the cluster chain on cluster crossings.
ra8_err_t ra8_fs_open(ra8_fs_mount_t *handle, const char *path, ra8_fs_mode_t mode, ra8_fs_file_t **out_file)
Open or create a file by path, 8.3 or long.
ra8_err_t ra8_fs_close(ra8_fs_file_t *file)
Close an open file, stamping its final modification time.
@ k_ra8_fs_mode_read
Read-only, must exist.
@ k_ra8_fs_mode_write
Truncate (or create) for writing.
static const uint8_t s_prefix[k_ra8_net_provision_prefix_bytes]
Exact protocol prefix, including the first field separator.
Mutation-sensitive identity for one open regular input.
time_t modified_sec
Modification seconds.
uint64_t size
Captured input length.
ino_t inode
Host inode identity.
time_t changed_sec
Metadata-change seconds.
long changed_nsec
Metadata-change nanoseconds.
dev_t device
Host device identity.
long modified_nsec
Modification nanoseconds.
Descriptor-backed block-device state bound into ra8_fs.
bool io_failed
Sticky positioned-I/O failure.
int fd
Temporary image descriptor.
uint32_t block_size
Bytes per sector.
uint64_t block_count
Addressable sectors.
Caller-owned sibling-temporary and block-backend binding.
ra8_fs_backend_t backend
Portable FS facade.
mkfontimg_disk_t disk
Bound block state.
int image_fd
Image descriptor.
int directory_fd
Parent descriptor.
char final_name[k_mkfontimg_host_name_cap]
Destination leaf.
char temp_name[k_mkfontimg_host_name_cap]
Temporary leaf.
bool temp_exists
Temp cleanup guard.
Block-device interface that ra8_fs runs on top of.
Open-file state.
Cached parse of one mounted FAT volume.