ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
exfat_mkimage.c
Go to the documentation of this file.
1
20
21#include <errno.h>
22#include <fcntl.h>
23#include <limits.h>
24#include <stddef.h>
25#include <stdint.h>
26#include <stdio.h> /* POSIX renameat declaration; no hosted stream is used. */
27#include <string.h>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <unistd.h>
31
32#include "ra8_attributes.h"
33#include "ra8_err.h"
34#include "ra8_fs.h"
35
36#ifndef O_CLOEXEC
38#define O_CLOEXEC (0)
39#endif
40
41#ifndef O_DIRECTORY
43#define O_DIRECTORY (0)
44#endif
45
46#ifndef O_NOFOLLOW
48#define O_NOFOLLOW (0)
49#endif
50
64
66typedef struct {
67 uint64_t block_count;
68 uint32_t block_size;
69 int fd;
70 bool io_failed;
71} mk_disk_t;
72
81
83typedef struct {
84 const char* path;
85 const char* meaning;
86 bool is_dir;
88
90static const mk_entry_t s_entries[] = {
91 {"/\xD0\x9F\xD0\xB0\xD0\xBF\xD0\xBA\xD0\xB0", "Cyrillic directory (Papka)", true},
92 {"/\xE4\xBD\xA0\xE5\xA5\xBD", "CJK directory (ni hao)", true},
93 {"/Caf\xC3\xA9.txt", "Latin-1 accent (Cafe.txt)", false},
94 {"/\xE4\xBD\xA0\xE5\xA5\xBD.txt", "CJK filename", false},
95 {"/\xF0\x9F\x98\x80.txt", "astral-plane emoji, a surrogate pair on disk", false},
96 {"/\xD0\x9F\xD0\xB0\xD0\xBF\xD0\xBA\xD0\xB0/n\xC3\xB6te.txt",
97 "non-ASCII file inside a non-ASCII directory",
98 false},
99 {"/README.txt", "plain ASCII control", false},
100};
101
114RA8_INTERNAL static void internal_log(int fd, const char* text)
115{
116 size_t offset = 0U;
117 size_t length = strlen(text);
118 while (offset < length) {
119 const ssize_t written = write(fd, &text[offset], length - offset);
120 if ((written < 0) && (errno == EINTR)) {
121 continue;
122 }
123 if (written <= 0) {
124 return;
125 }
126 offset += (size_t)written;
127 }
128}
129
142RA8_INTERNAL static void internal_log_u64(int fd, uint64_t value)
143{
144 char reverse[k_mk_decimal_digits];
145 char forward[k_mk_decimal_digits + 1U];
146 size_t digits = 0U;
147 do {
148 reverse[digits++] = (char)('0' + (value % (uint64_t)k_mk_decimal_base));
149 value /= (uint64_t)k_mk_decimal_base;
150 } while (value != 0U);
151 for (size_t i = 0U; i < digits; ++i) {
152 forward[i] = reverse[digits - i - 1U];
153 }
154 forward[digits] = '\0';
155 internal_log(fd, forward);
156}
157
175RA8_INTERNAL static bool
176internal_pread_exact(int fd, uint64_t offset, uint8_t* bytes, size_t length)
177{
178 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
179 return false;
180 }
181 size_t done = 0U;
182 while (done < length) {
183 size_t request = length - done;
184 if (request > (size_t)SSIZE_MAX) {
185 request = (size_t)SSIZE_MAX;
186 }
187 const ssize_t got = pread(fd, &bytes[done], request, (off_t)(offset + done));
188 if ((got < 0) && (errno == EINTR)) {
189 continue;
190 }
191 if (got <= 0) {
192 return false;
193 }
194 done += (size_t)got;
195 }
196 return true;
197}
198
216RA8_INTERNAL static bool
217internal_pwrite_exact(int fd, uint64_t offset, const uint8_t* bytes, size_t length)
218{
219 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
220 return false;
221 }
222 size_t done = 0U;
223 while (done < length) {
224 size_t request = length - done;
225 if (request > (size_t)SSIZE_MAX) {
226 request = (size_t)SSIZE_MAX;
227 }
228 const ssize_t put = pwrite(fd, &bytes[done], request, (off_t)(offset + done));
229 if ((put < 0) && (errno == EINTR)) {
230 continue;
231 }
232 if (put <= 0) {
233 return false;
234 }
235 done += (size_t)put;
236 }
237 return true;
238}
239
259 uint64_t lba,
260 uint32_t count,
261 uint64_t* out_offset,
262 size_t* out_bytes)
263{
264 if (lba > disk->block_count || (uint64_t)count > (disk->block_count - lba) ||
265 lba > (UINT64_MAX / disk->block_size) || count > (SIZE_MAX / disk->block_size)) {
266 return false;
267 }
268 const uint64_t offset = lba * disk->block_size;
269 const size_t bytes = (size_t)count * disk->block_size;
270 if (offset > (uint64_t)INT64_MAX || bytes > ((uint64_t)INT64_MAX - offset)) {
271 return false;
272 }
273 *out_offset = offset;
274 *out_bytes = bytes;
275 return true;
276}
277
298internal_disk_read(void* ctx, uint64_t lba, uint32_t count, uint8_t* buffer)
299{
300 mk_disk_t* disk = (mk_disk_t*)ctx;
301 uint64_t offset;
302 size_t bytes;
303 if (disk == nullptr || buffer == nullptr || disk->io_failed ||
304 !internal_block_range(disk, lba, count, &offset, &bytes)) {
306 }
307 if (!internal_pread_exact(disk->fd, offset, buffer, bytes)) {
308 disk->io_failed = true;
309 return k_ra8_fail;
310 }
311 return k_ra8_ok;
312}
313
334internal_disk_write(void* ctx, uint64_t lba, uint32_t count, const uint8_t* buffer)
335{
336 mk_disk_t* disk = (mk_disk_t*)ctx;
337 uint64_t offset;
338 size_t bytes;
339 if (disk == nullptr || buffer == nullptr || disk->io_failed ||
340 !internal_block_range(disk, lba, count, &offset, &bytes)) {
342 }
343 if (!internal_pwrite_exact(disk->fd, offset, buffer, bytes)) {
344 disk->io_failed = true;
345 return k_ra8_fail;
346 }
347 return k_ra8_ok;
348}
349
367internal_disk_capacity(void* ctx, uint64_t* block_count, uint32_t* block_size)
368{
369 const mk_disk_t* disk = (const mk_disk_t*)ctx;
370 if (disk == nullptr || block_count == nullptr || block_size == nullptr) {
371 return k_ra8_err_null_ptr;
372 }
373 *block_count = disk->block_count;
374 *block_size = disk->block_size;
375 return k_ra8_ok;
376}
377
394RA8_INTERNAL static bool
395internal_split_output(const char* path, char parent[k_mk_path_cap], char leaf[k_mk_name_cap])
396{
397 const size_t length = strlen(path);
398 if (length == 0U || length >= (size_t)k_mk_path_cap) {
399 return false;
400 }
401 size_t slash = length;
402 while (slash > 0U && path[slash - 1U] != '/') {
403 --slash;
404 }
405 const size_t leaf_bytes = length - slash;
406 if (leaf_bytes == 0U || leaf_bytes >= (size_t)k_mk_name_cap) {
407 return false;
408 }
409 (void)memcpy(leaf, &path[slash], leaf_bytes);
410 leaf[leaf_bytes] = '\0';
411 if (strcmp(leaf, ".") == 0 || strcmp(leaf, "..") == 0) {
412 return false;
413 }
414 if (slash == 0U) {
415 parent[0] = '.';
416 parent[1] = '\0';
417 } else if (slash == 1U) {
418 parent[0] = '/';
419 parent[1] = '\0';
420 } else {
421 const size_t parent_bytes = slash - 1U;
422 (void)memcpy(parent, path, parent_bytes);
423 parent[parent_bytes] = '\0';
424 }
425 return true;
426}
427
444RA8_INTERNAL static bool
445internal_temp_name(char out[k_mk_name_cap], uint64_t process, uint32_t attempt)
446{
447 static const char s_prefix[] = ".exfat_mkimage.tmp.";
448 char reverse[k_mk_decimal_digits];
449 size_t offset = sizeof(s_prefix) - 1U;
450 (void)memcpy(out, s_prefix, offset);
451 uint64_t values[2] = {process, attempt};
452 for (uint8_t field = 0U; field < 2U; ++field) {
453 size_t digits = 0U;
454 do {
455 reverse[digits++] = (char)('0' + (values[field] % (uint64_t)k_mk_decimal_base));
456 values[field] /= (uint64_t)k_mk_decimal_base;
457 } while (values[field] != 0U);
458 if (offset + digits + 2U > (size_t)k_mk_name_cap) {
459 return false;
460 }
461 while (digits > 0U) {
462 out[offset++] = reverse[--digits];
463 }
464 out[offset++] = (field == 0U) ? '.' : '\0';
465 }
466 return true;
467}
468
484RA8_INTERNAL static bool internal_output_begin(const char* path, mk_output_t* output)
485{
486 *output = (mk_output_t){.directory_fd = -1, .image_fd = -1};
487 char parent[k_mk_path_cap];
488 if (!internal_split_output(path, parent, output->final_name)) {
489 return false;
490 }
491 output->directory_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
492 if (output->directory_fd < 0) {
493 return false;
494 }
495 for (uint32_t attempt = 0U; attempt < (uint32_t)k_mk_temp_attempts; ++attempt) {
496 if (!internal_temp_name(output->temp_name, (uint64_t)getpid(), attempt)) {
497 break;
498 }
499 output->image_fd = openat(output->directory_fd,
500 output->temp_name,
501 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
502 (mode_t)k_mk_create_mode);
503 if (output->image_fd >= 0 || errno != EEXIST) {
504 break;
505 }
506 }
507 if (output->image_fd < 0) {
508 (void)close(output->directory_fd);
509 output->directory_fd = -1;
510 return false;
511 }
512 output->temp_exists = true;
513 const uint64_t bytes = (uint64_t)k_mk_block_count * (uint64_t)k_mk_block_size;
514 if (bytes > (uint64_t)INT64_MAX || ftruncate(output->image_fd, (off_t)bytes) != 0) {
515 (void)close(output->image_fd);
516 output->image_fd = -1;
517 (void)unlinkat(output->directory_fd, output->temp_name, 0);
518 output->temp_exists = false;
519 (void)close(output->directory_fd);
520 output->directory_fd = -1;
521 return false;
522 }
523 return true;
524}
525
538{
539 if (output->image_fd >= 0) {
540 (void)close(output->image_fd);
541 output->image_fd = -1;
542 }
543 if (output->temp_exists && output->directory_fd >= 0) {
544 (void)unlinkat(output->directory_fd, output->temp_name, 0);
545 output->temp_exists = false;
546 }
547 if (output->directory_fd >= 0) {
548 (void)close(output->directory_fd);
549 output->directory_fd = -1;
550 }
551}
552
568{
569 bool ok = fsync(output->image_fd) == 0;
570 if (close(output->image_fd) != 0) {
571 ok = false;
572 }
573 output->image_fd = -1;
574 if (ok &&
575 renameat(output->directory_fd, output->temp_name, output->directory_fd, output->final_name) ==
576 0) {
577 output->temp_exists = false;
578 if (fsync(output->directory_fd) != 0) {
579 ok = false;
580 }
581 } else {
582 ok = false;
583 }
584 if (output->temp_exists) {
585 (void)unlinkat(output->directory_fd, output->temp_name, 0);
586 output->temp_exists = false;
587 }
588 if (close(output->directory_fd) != 0) {
589 ok = false;
590 }
591 output->directory_fd = -1;
592 return ok;
593}
594
610{
611 uint8_t payload[k_mk_payload];
612 for (uint32_t i = 0U; i < (uint32_t)k_mk_payload; ++i) {
613 payload[i] = (uint8_t)('A' + (i % (uint32_t)k_mk_alphabet));
614 }
615 for (size_t i = 0U; i < (sizeof(s_entries) / sizeof(s_entries[0])); ++i) {
616 const mk_entry_t* entry = &s_entries[i];
617 const ra8_err_t error =
618 entry->is_dir ? ra8_fs_mkdir(mount, entry->path)
619 : ra8_fs_write_file(mount, entry->path, payload, (uint32_t)k_mk_payload);
620 if (error != k_ra8_ok) {
621 internal_log(STDERR_FILENO, "exfat_mkimage: cannot create ");
622 internal_log(STDERR_FILENO, entry->meaning);
623 internal_log(STDERR_FILENO, " (error ");
624 internal_log_u64(STDERR_FILENO, (uint64_t)error);
625 internal_log(STDERR_FILENO, ")\n");
626 return error;
627 }
628 internal_log(STDOUT_FILENO, " created ");
629 internal_log(STDOUT_FILENO, entry->path);
630 internal_log(STDOUT_FILENO, " ");
631 internal_log(STDOUT_FILENO, entry->meaning);
632 internal_log(STDOUT_FILENO, "\n");
633 }
634 return k_ra8_ok;
635}
636
652int main(int argc, char** argv)
653{
654 if (argc > 2) {
655 internal_log(STDERR_FILENO, "usage: exfat_mkimage [output.img]\n");
656 return 2;
657 }
658 const char* out_path = (argc == 2) ? argv[1] : "exfat_showcase.img";
659 mk_output_t output;
660 if (!internal_output_begin(out_path, &output)) {
661 internal_log(STDERR_FILENO, "exfat_mkimage: cannot create safe sibling temporary\n");
662 return 1;
663 }
664 mk_disk_t disk = {.block_count = k_mk_block_count,
665 .block_size = k_mk_block_size,
666 .fd = output.image_fd};
667 const ra8_fs_backend_t backend = {.read_block = internal_disk_read,
668 .write_block = internal_disk_write,
669 .get_capacity = internal_disk_capacity,
670 .ctx = &disk};
671 const ra8_fs_format_opts_t options = {.type = k_ra8_fs_type_exfat, .label = "RA8IMAGE"};
672 ra8_fs_mount_t* mount = nullptr;
673 ra8_err_t error = ra8_fs_format(&backend, &options);
674 if (error == k_ra8_ok) {
675 error = ra8_fs_mount(&backend, &mount);
676 }
677 if (error == k_ra8_ok) {
678 error = internal_populate(mount);
679 }
680 if (mount != nullptr && ra8_fs_unmount(mount) != k_ra8_ok) {
681 error = k_ra8_fail;
682 }
683 bool ok = error == k_ra8_ok && !disk.io_failed;
684 if (ok) {
685 ok = internal_output_commit(&output);
686 } else {
687 internal_output_abort(&output);
688 }
689 if (!ok) {
690 internal_log(STDERR_FILENO, "exfat_mkimage: generation failed; destination preserved\n");
691 return 1;
692 }
693 internal_log(STDOUT_FILENO, "wrote ");
694 internal_log(STDOUT_FILENO, out_path);
695 internal_log(STDOUT_FILENO, " (");
696 internal_log_u64(STDOUT_FILENO, (uint64_t)k_mk_block_count * (uint64_t)k_mk_block_size);
697 internal_log(STDOUT_FILENO, " bytes) -- the card exactly as ra8_fs left it\n");
698 return 0;
699}
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_temp_name(char out[k_mk_name_cap], uint64_t process, uint32_t attempt)
Build one hidden temporary leaf from process id and retry index.
static const mk_entry_t s_entries[]
Deterministic UTF-8 showcase contents.
int main(int argc, char **argv)
Format, populate, verify closure, and atomically publish one 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 void internal_output_abort(mk_output_t *output)
Close and remove an unpublished temporary.
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.
static void internal_log_u64(int fd, uint64_t value)
Log one unsigned decimal value without a formatting stream.
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
static bool internal_output_commit(mk_output_t *output)
Sync and atomically publish one complete sibling temporary.
static void internal_log(int fd, const char *text)
Write one complete diagnostic fragment to a raw descriptor.
static ra8_err_t internal_disk_capacity(void *ctx, uint64_t *block_count, uint32_t *block_size)
Report the fixed card-image geometry.
static bool internal_split_output(const char *path, char parent[k_mk_path_cap], char leaf[k_mk_name_cap])
Split a destination into bounded parent and leaf components.
static ra8_err_t internal_populate(ra8_fs_mount_t *mount)
Populate every deterministic UTF-8 showcase entry.
static bool internal_pread_exact(int fd, uint64_t offset, uint8_t *bytes, size_t length)
Read an exact positioned range from a regular descriptor.
static bool internal_pwrite_exact(int fd, uint64_t offset, const uint8_t *bytes, size_t length)
Write an exact positioned range to a regular descriptor.
static bool internal_output_begin(const char *path, mk_output_t *output)
Create and exact-size one private sibling temporary.
mk_limit_t
Image geometry and bounded hosted-storage constants.
@ k_mk_alphabet
ASCII payload cycle length.
@ k_mk_decimal_base
Decimal conversion radix.
@ k_mk_decimal_digits
Maximum uint64_t digits.
@ k_mk_temp_attempts
Exclusive-create retry bound.
@ k_mk_payload
Bytes in each showcase file.
@ k_mk_block_count
Blocks in the 64 MiB image.
@ k_mk_path_cap
Hosted path capacity.
@ k_mk_create_mode
Hosted output creation mode.
@ k_mk_block_size
Bytes per image block.
@ k_mk_name_cap
Hosted leaf capacity.
#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.
-proof
Annotation-attribute framework macros for ra8-firmware.
#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.
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_format(const ra8_fs_backend_t *backend, const ra8_fs_format_opts_t *opts)
Format a block device as a fresh, empty FAT12/FAT16/FAT32/exFAT volume.
ra8_err_t ra8_fs_mount(const ra8_fs_backend_t *backend, ra8_fs_mount_t **out_handle)
Mount a FAT volume from a block-device backend, auto-selecting the first partition.
ra8_err_t ra8_fs_unmount(ra8_fs_mount_t *handle)
Unmount a previously mounted volume and release its slot.
ra8_err_t ra8_fs_mkdir(const ra8_fs_mount_t *handle, const char *path)
Create a directory at path.
ra8_err_t ra8_fs_write_file(ra8_fs_mount_t *handle, const char *path, const uint8_t *data, uint32_t len)
Create a whole file in one call (provisioning helper).
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
static const uint8_t s_prefix[k_ra8_net_provision_prefix_bytes]
Exact protocol prefix, including the first field separator.
Descriptor-backed card state bound into ra8_fs.
int fd
Open temporary image descriptor.
bool io_failed
Sticky positioned-I/O failure.
uint32_t block_size
Bytes per logical block.
uint64_t block_count
Addressable logical blocks.
One showcase volume entry.
bool is_dir
True for a directory.
const char * meaning
Human-readable description.
const char * path
Absolute UTF-8 volume path.
Caller-owned atomic-publication state.
int directory_fd
Parent directory handle.
bool temp_exists
Temp unlink guard.
int image_fd
Temporary image handle.
char final_name[k_mk_name_cap]
Destination leaf.
char temp_name[k_mk_name_cap]
Hidden temporary leaf.
Block-device interface that ra8_fs runs on top of.
Tunables for ra8_fs_format() (the on-disk geometry of a fresh volume).
Cached parse of one mounted FAT volume.