ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mkbookimg.c
Go to the documentation of this file.
1
14
15#include <errno.h>
16#include <fcntl.h>
17#include <limits.h>
18#include <stddef.h>
19#include <stdint.h>
20#include <stdio.h> /* POSIX renameat declaration; no hosted stream is used. */
21#include <string.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <unistd.h>
25
26#include "mkbookimg_internal.h"
27#include "mkbookimg_names.h"
28#include "ra8_attributes.h"
29#include "ra8_fs.h"
30
39
41void priv_mkbookimg_diag(const char* text)
42{
43 size_t offset = 0U;
44 size_t length = strlen(text);
45 while (offset < length) {
46 const ssize_t written = write(STDERR_FILENO, &text[offset], length - offset);
47 if ((written < 0) && (errno == EINTR)) {
48 continue;
49 }
50 if (written <= 0) {
51 return;
52 }
53 offset += (size_t)written;
54 }
55}
56
58void priv_mkbookimg_diag_u64(uint64_t value)
59{
60 char reverse[k_decimal_u64_digits];
61 char forward[k_decimal_u64_digits + 1U];
62 size_t digits = 0U;
63 do {
64 reverse[digits++] = (char)('0' + (value % (uint64_t)k_decimal_base));
65 value /= (uint64_t)k_decimal_base;
66 } while (value != 0U);
67 for (size_t i = 0U; i < digits; ++i) {
68 forward[i] = reverse[digits - i - 1U];
69 }
70 forward[digits] = '\0';
71 priv_mkbookimg_diag(forward);
72}
73
75bool priv_mkbookimg_pread_exact(int fd, uint64_t offset, uint8_t* bytes, size_t length)
76{
77 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
78 return false;
79 }
80 size_t done = 0U;
81 while (done < length) {
82 size_t request = length - done;
83 if (request > (size_t)SSIZE_MAX) {
84 request = (size_t)SSIZE_MAX;
85 }
86 const ssize_t got = pread(fd, &bytes[done], request, (off_t)(offset + done));
87 if ((got < 0) && (errno == EINTR)) {
88 continue;
89 }
90 if (got <= 0) {
91 return false;
92 }
93 done += (size_t)got;
94 }
95 return true;
96}
97
115RA8_INTERNAL static bool
116internal_pwrite_exact(int fd, uint64_t offset, const uint8_t* bytes, size_t length)
117{
118 if (offset > (uint64_t)INT64_MAX || length > ((uint64_t)INT64_MAX - offset)) {
119 return false;
120 }
121 size_t done = 0U;
122 while (done < length) {
123 size_t request = length - done;
124 if (request > (size_t)SSIZE_MAX) {
125 request = (size_t)SSIZE_MAX;
126 }
127 const ssize_t put = pwrite(fd, &bytes[done], request, (off_t)(offset + done));
128 if ((put < 0) && (errno == EINTR)) {
129 continue;
130 }
131 if (put <= 0) {
132 return false;
133 }
134 done += (size_t)put;
135 }
136 return true;
137}
138
158 uint64_t lba,
159 uint32_t count,
160 uint64_t* out_offset,
161 size_t* out_bytes)
162{
163 if (lba > disk->block_count || (uint64_t)count > (disk->block_count - lba)) {
164 return false;
165 }
166 if (lba > (UINT64_MAX / disk->block_size) || count > (SIZE_MAX / disk->block_size)) {
167 return false;
168 }
169 const uint64_t offset = lba * disk->block_size;
170 const size_t bytes = (size_t)count * disk->block_size;
171 if (offset > (uint64_t)INT64_MAX || bytes > ((uint64_t)INT64_MAX - offset)) {
172 return false;
173 }
174 *out_offset = offset;
175 *out_bytes = bytes;
176 return true;
177}
178
198internal_disk_read(void* ctx, uint64_t lba, uint32_t count, uint8_t* buffer)
199{
201 uint64_t offset;
202 size_t bytes;
203 if (disk == nullptr || buffer == nullptr || disk->io_failed ||
204 !internal_block_range(disk, lba, count, &offset, &bytes)) {
206 }
207 if (!priv_mkbookimg_pread_exact(disk->fd, offset, buffer, bytes)) {
208 disk->io_failed = true;
209 return k_ra8_fail;
210 }
211 return k_ra8_ok;
212}
213
233internal_disk_write(void* ctx, uint64_t lba, uint32_t count, const uint8_t* buffer)
234{
236 uint64_t offset;
237 size_t bytes;
238 if (disk == nullptr || buffer == nullptr || disk->io_failed ||
239 !internal_block_range(disk, lba, count, &offset, &bytes)) {
241 }
242 if (!internal_pwrite_exact(disk->fd, offset, buffer, bytes)) {
243 disk->io_failed = true;
244 return k_ra8_fail;
245 }
246 return k_ra8_ok;
247}
248
266internal_disk_capacity(void* ctx, uint64_t* block_count, uint32_t* block_size)
267{
268 const mkbookimg_disk_t* disk = (const mkbookimg_disk_t*)ctx;
269 if (disk == nullptr || block_count == nullptr || block_size == nullptr) {
270 return k_ra8_err_null_ptr;
271 }
272 *block_count = disk->block_count;
273 *block_size = disk->block_size;
274 return k_ra8_ok;
275}
276
293RA8_INTERNAL static bool internal_split_output(const char* path,
294 char parent[k_host_path_cap],
295 char final_name[k_host_name_cap])
296{
297 const size_t length = strlen(path);
298 if (length == 0U || length >= (size_t)k_host_path_cap) {
299 return false;
300 }
301 size_t slash = length;
302 while (slash > 0U && path[slash - 1U] != '/') {
303 --slash;
304 }
305 const size_t leaf_start = slash;
306 const size_t leaf_bytes = length - leaf_start;
307 if (leaf_bytes == 0U || leaf_bytes >= (size_t)k_host_name_cap) {
308 return false;
309 }
310 (void)memcpy(final_name, &path[leaf_start], leaf_bytes);
311 final_name[leaf_bytes] = '\0';
312 if (strcmp(final_name, ".") == 0 || strcmp(final_name, "..") == 0) {
313 return false;
314 }
315 if (slash == 0U) {
316 parent[0] = '.';
317 parent[1] = '\0';
318 } else if (slash == 1U) {
319 parent[0] = '/';
320 parent[1] = '\0';
321 } else {
322 const size_t parent_bytes = slash - 1U;
323 (void)memcpy(parent, path, parent_bytes);
324 parent[parent_bytes] = '\0';
325 }
326 return true;
327}
328
345RA8_INTERNAL static bool
346internal_temp_name(char out[k_host_name_cap], uint64_t process, uint32_t attempt)
347{
348 static const char s_prefix[] = ".mkbookimg.tmp.";
349 char reverse[k_decimal_u64_digits];
350 size_t offset = sizeof(s_prefix) - 1U;
351 (void)memcpy(out, s_prefix, offset);
352 uint64_t values[2] = {process, attempt};
353 for (uint8_t field = 0U; field < 2U; ++field) {
354 size_t digits = 0U;
355 do {
356 reverse[digits++] = (char)('0' + (values[field] % (uint64_t)k_decimal_base));
357 values[field] /= (uint64_t)k_decimal_base;
358 } while (values[field] != 0U);
359 if (offset + digits + 2U > (size_t)k_host_name_cap) {
360 return false;
361 }
362 while (digits > 0U) {
363 out[offset++] = reverse[--digits];
364 }
365 out[offset++] = (field == 0U) ? '.' : '\0';
366 }
367 return true;
368}
369
385RA8_INTERNAL static bool internal_output_begin(const char* final_path, mkbookimg_output_t* output)
386{
387 *output = (mkbookimg_output_t){.directory_fd = -1, .image_fd = -1};
388 char parent[k_host_path_cap];
389 if (!internal_split_output(final_path, parent, output->final_name)) {
390 return false;
391 }
392 output->directory_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
393 if (output->directory_fd < 0) {
394 return false;
395 }
396 for (uint32_t attempt = 0U; attempt < (uint32_t)k_temp_create_attempts; ++attempt) {
397 if (!internal_temp_name(output->temp_name, (uint64_t)getpid(), attempt)) {
398 break;
399 }
400 output->image_fd = openat(output->directory_fd,
401 output->temp_name,
402 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
403 (mode_t)k_output_create_mode);
404 if (output->image_fd >= 0) {
405 output->temp_exists = true;
406 break;
407 }
408 if (errno != EEXIST) {
409 break;
410 }
411 }
412 const uint64_t image_bytes = (uint64_t)k_img_sectors * (uint64_t)k_block_size;
413 if (output->image_fd < 0 || image_bytes > (uint64_t)INT64_MAX ||
414 ftruncate(output->image_fd, (off_t)image_bytes) != 0) {
415 if (output->image_fd >= 0) {
416 (void)close(output->image_fd);
417 output->image_fd = -1;
418 }
419 if (output->temp_exists) {
420 (void)unlinkat(output->directory_fd, output->temp_name, 0);
421 output->temp_exists = false;
422 }
423 (void)close(output->directory_fd);
424 output->directory_fd = -1;
425 return false;
426 }
427 return true;
428}
429
442{
443 if (output->image_fd >= 0) {
444 (void)close(output->image_fd);
445 output->image_fd = -1;
446 }
447 if (output->temp_exists && output->directory_fd >= 0) {
448 (void)unlinkat(output->directory_fd, output->temp_name, 0);
449 output->temp_exists = false;
450 }
451 if (output->directory_fd >= 0) {
452 (void)close(output->directory_fd);
453 output->directory_fd = -1;
454 }
455}
456
473{
474 bool ok = fsync(output->image_fd) == 0;
475 if (close(output->image_fd) != 0) {
476 ok = false;
477 }
478 output->image_fd = -1;
479 if (ok &&
480 renameat(output->directory_fd, output->temp_name, output->directory_fd, output->final_name) ==
481 0) {
482 output->temp_exists = false;
483 if (fsync(output->directory_fd) != 0) {
484 ok = false;
485 }
486 } else {
487 ok = false;
488 }
489 if (output->temp_exists) {
490 (void)unlinkat(output->directory_fd, output->temp_name, 0);
491 output->temp_exists = false;
492 }
493 if (close(output->directory_fd) != 0) {
494 ok = false;
495 }
496 output->directory_fd = -1;
497 return ok;
498}
499
515RA8_INTERNAL static void internal_print_success(const char* out_path, int book_count)
516{
517 priv_mkbookimg_diag("mkbookimg: wrote ");
518 priv_mkbookimg_diag(out_path);
520 priv_mkbookimg_diag_u64((uint64_t)book_count);
521 priv_mkbookimg_diag(" book(s), 64 MiB FAT32)\n");
522}
523
539int main(int argc, char** argv)
540{
541 if (argc < 3) {
542 priv_mkbookimg_diag("usage: mkbookimg <out.img> <book1.rabook> [book2.rabook ...]\n");
543 return 2;
544 }
545 const int book_count = argc - 2;
546 if (book_count > (int)k_max_books) {
547 priv_mkbookimg_diag("mkbookimg: too many books (max 32)\n");
548 return 2;
549 }
550
551 mkbookimg_output_t output;
552 if (!internal_output_begin(argv[1], &output)) {
553 priv_mkbookimg_diag("mkbookimg: cannot create a safe sibling temporary for ");
554 priv_mkbookimg_diag(argv[1]);
556 return 1;
557 }
558 mkbookimg_disk_t disk = {.block_count = k_img_sectors,
559 .block_size = k_block_size,
560 .fd = output.image_fd};
561 const ra8_fs_backend_t backend = {.read_block = internal_disk_read,
562 .write_block = internal_disk_write,
563 .get_capacity = internal_disk_capacity,
564 .ctx = &disk};
565 bool ok = priv_mkbookimg_build_image(&backend, argv, book_count, &disk);
566 if (ok) {
567 ok = internal_output_commit(&output);
568 } else {
569 internal_output_abort(&output);
570 }
571 if (!ok) {
572 priv_mkbookimg_diag("mkbookimg: image generation failed; destination preserved\n");
573 return 1;
574 }
575 internal_print_success(argv[1], book_count);
576 return 0;
577}
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 bool internal_output_commit(mkbookimg_output_t *output)
Durably publish the complete image with one same-directory rename.
Definition mkbookimg.c:472
static ra8_err_t internal_disk_write(void *ctx, uint64_t lba, uint32_t count, const uint8_t *buffer)
Write sectors to the temporary image for ra8_fs.
Definition mkbookimg.c:233
bool priv_mkbookimg_pread_exact(int fd, uint64_t offset, uint8_t *bytes, size_t length)
Read exactly one bounded positioned byte range.
Definition mkbookimg.c:75
static bool internal_split_output(const char *path, char parent[k_host_path_cap], char final_name[k_host_name_cap])
Split an output path into an existing parent directory and safe leaf.
Definition mkbookimg.c:293
int main(int argc, char **argv)
Validate CLI input, construct, verify, and atomically publish one image.
Definition mkbookimg.c:539
static ra8_err_t internal_disk_read(void *ctx, uint64_t lba, uint32_t count, uint8_t *buffer)
Read sectors from the temporary image for ra8_fs.
Definition mkbookimg.c:198
void priv_mkbookimg_diag(const char *text)
Write a complete diagnostic fragment to the standard-error descriptor.
Definition mkbookimg.c:41
static bool internal_block_range(const mkbookimg_disk_t *disk, uint64_t lba, uint32_t count, uint64_t *out_offset, size_t *out_bytes)
Validate and translate a block range to a host byte range.
Definition mkbookimg.c:157
void priv_mkbookimg_diag_u64(uint64_t value)
Emit an unsigned decimal value without a formatting runtime.
Definition mkbookimg.c:58
static bool internal_output_begin(const char *final_path, mkbookimg_output_t *output)
Create and size a private sibling temporary for atomic publication.
Definition mkbookimg.c:385
static ra8_err_t internal_disk_capacity(void *ctx, uint64_t *block_count, uint32_t *block_size)
Report fixed image geometry to ra8_fs.
Definition mkbookimg.c:266
static void internal_output_abort(mkbookimg_output_t *output)
Close and remove an unpublished temporary image.
Definition mkbookimg.c:441
static bool internal_temp_name(char out[k_host_name_cap], uint64_t process, uint32_t attempt)
Build one hidden sibling-temporary leaf from process id and attempt.
Definition mkbookimg.c:346
static bool internal_pwrite_exact(int fd, uint64_t offset, const uint8_t *bytes, size_t length)
Write exactly one bounded positioned byte range.
Definition mkbookimg.c:116
static void internal_print_success(const char *out_path, int book_count)
Print the final success diagnostic line.
Definition mkbookimg.c:515
Module-private seams shared by the two mkbookimg translation units.
@ k_host_path_cap
Hosted path capacity.
@ k_block_size
Bytes per image sector.
@ k_img_sectors
Sectors in the 64 MiB image.
@ k_decimal_base
Decimal conversion radix.
@ k_host_name_cap
Hosted leaf capacity.
@ k_temp_create_attempts
Exclusive-create retry bound.
@ k_output_create_mode
Hosted output creation mode.
@ k_decimal_u64_digits
Maximum uint64_t digits.
@ k_max_books
Maximum input-book count.
bool priv_mkbookimg_build_image(const ra8_fs_backend_t *backend, char **argv, int book_count, const mkbookimg_disk_t *disk)
Format, populate, and unmount the destination image.
Derive an on-card file name from a source book path (long-name capable).
-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).
@ 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).
static const uint8_t s_prefix[k_ra8_net_provision_prefix_bytes]
Exact protocol prefix, including the first field separator.
Host descriptor-backed block device bound into ra8_fs.
bool io_failed
Sticky positioned-I/O failure.
uint64_t block_count
Addressable sectors.
int fd
Open sibling-temporary descriptor.
uint32_t block_size
Bytes in one sector.
Caller-owned atomic-publication state.
Definition mkbookimg.c:32
bool temp_exists
Temp leaf requires unlink.
Definition mkbookimg.c:37
int image_fd
Open temporary image.
Definition mkbookimg.c:36
int directory_fd
Destination-directory handle.
Definition mkbookimg.c:35
char final_name[k_host_name_cap]
Destination leaf.
Definition mkbookimg.c:33
char temp_name[k_host_name_cap]
Owned temporary leaf.
Definition mkbookimg.c:34
Block-device interface that ra8_fs runs on top of.