ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fmt_host_fd.c
Go to the documentation of this file.
1
10
11#define _POSIX_C_SOURCE (200809L)
12
13#include <errno.h>
14#include <fcntl.h>
15#include <limits.h>
16#include <stddef.h>
17#include <stdint.h>
18#include <string.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <unistd.h>
22
23#include "ra8_attributes.h"
25
47extern int renameat(int old_dir_fd, const char* old_path, int new_dir_fd, const char* new_path);
48
50typedef enum : uint32_t {
55
77static ra8_err_t internal_pread(void* ctx, uint64_t offset, uint8_t* bytes, size_t len, size_t* got)
78{
80 if ((source == nullptr) || (got == nullptr) || ((bytes == nullptr) && (len != 0U))) {
81 return k_ra8_err_null_ptr;
82 }
83 *got = 0U;
84 if ((offset >= source->source.size) || (len == 0U)) {
85 return k_ra8_ok;
86 }
87 uint64_t remain = source->source.size - offset;
88 if ((uint64_t)len > remain) {
89 len = (size_t)remain;
90 }
91 while (*got < len) {
92 const ssize_t rc = pread(source->fd, &bytes[*got], len - *got, (off_t)(offset + *got));
93 if (rc > 0) {
94 *got += (size_t)rc;
95 } else if (rc == 0) {
96 break;
97 } else if (errno != EINTR) {
98 return k_ra8_fail;
99 }
100 }
101 return k_ra8_ok;
102}
103
122static ra8_err_t internal_fd_write(void* ctx, const uint8_t* bytes, size_t len)
123{
125 if ((sink == nullptr) || ((bytes == nullptr) && (len != 0U))) {
126 return k_ra8_err_null_ptr;
127 }
128 size_t done = 0U;
129 while (done < len) {
130 const ssize_t rc = write(sink->fd, &bytes[done], len - done);
131 if (rc > 0) {
132 done += (size_t)rc;
133 } else if ((rc < 0) && (errno == EINTR)) {
134 continue;
135 } else {
136 return k_ra8_fail;
137 }
138 }
139 return k_ra8_ok;
140}
141
160static ra8_err_t internal_copy(char* out, size_t cap, const char* text, size_t len)
161{
162 if ((len + 1U) > cap) {
164 }
165 (void)memcpy(out, text, len);
166 out[len] = '\0';
167 return k_ra8_ok;
168}
169
188static ra8_err_t internal_split_path(const char* path,
189 char parent[k_ra8_fmt_host_path_cap],
190 char leaf[k_ra8_fmt_host_name_cap])
191{
192 const size_t len = (path == nullptr) ? 0U : strlen(path);
193 if ((len == 0U) || (len >= (size_t)k_ra8_fmt_host_path_cap)) {
195 }
196 const char* slash = strrchr(path, '/');
197 const char* name = (slash == nullptr) ? path : &slash[1];
198 const size_t name_len = len - (size_t)(name - path);
199 if ((name_len == 0U) || ((name_len == 1U) && (name[0] == '.')) ||
200 ((name_len == 2U) && (name[0] == '.') && (name[1] == '.'))) {
202 }
203 ra8_err_t rc = internal_copy(leaf, k_ra8_fmt_host_name_cap, name, name_len);
204 if (rc != k_ra8_ok) {
205 return rc;
206 }
207 if (slash == nullptr) {
208 return internal_copy(parent, k_ra8_fmt_host_path_cap, ".", 1U);
209 }
210 const size_t parent_len = (slash == path) ? 1U : (size_t)(slash - path);
211 return internal_copy(parent, k_ra8_fmt_host_path_cap, path, parent_len);
212}
213
232static ra8_err_t internal_name_u64(char* name, size_t cap, size_t* len, uint64_t value)
233{
234 char reverse[20U];
235 size_t count = 0U;
236 do {
237 reverse[count++] = (char)('0' + (char)(value % k_host_decimal_radix));
238 value /= k_host_decimal_radix;
239 } while (value != 0U);
240 if ((*len + count + 1U) > cap) {
242 }
243 while (count != 0U) {
244 name[(*len)++] = reverse[--count];
245 }
246 name[*len] = '\0';
247 return k_ra8_ok;
248}
249
267{
268 static const char suffix[] = ".ra8tmp.";
269 size_t len = 0U;
270 state->stage_name[len++] = '.';
271 const size_t leaf_len = strlen(state->final_name);
272 if ((1U + leaf_len + sizeof(suffix)) > sizeof(state->stage_name)) {
274 }
275 (void)memcpy(&state->stage_name[len], state->final_name, leaf_len);
276 len += leaf_len;
277 (void)memcpy(&state->stage_name[len], suffix, sizeof(suffix) - 1U);
278 len += sizeof(suffix) - 1U;
279 ra8_err_t rc =
280 internal_name_u64(state->stage_name, sizeof(state->stage_name), &len, (uint64_t)getpid());
281 if ((rc == k_ra8_ok) && ((len + 2U) <= sizeof(state->stage_name))) {
282 state->stage_name[len++] = '.';
283 state->stage_name[len] = '\0';
284 rc = internal_name_u64(state->stage_name, sizeof(state->stage_name), &len, attempt);
285 }
286 return rc;
287}
288
306static ra8_err_t internal_validate_destination(int parent_fd, const char* leaf)
307{
308 struct stat status = {};
309 if (fstatat(parent_fd, leaf, &status, AT_SYMLINK_NOFOLLOW) == 0) {
310 return S_ISREG(status.st_mode) ? k_ra8_ok : k_ra8_err_access_denied;
311 }
312 return (errno == ENOENT) ? k_ra8_ok : k_ra8_fail;
313}
314
334static ra8_err_t internal_transaction_append(void* ctx, const uint8_t* bytes, size_t len)
335{
337 if ((state == nullptr) || !state->active || ((bytes == nullptr) && (len != 0U))) {
339 }
340 ra8_fmt_host_fd_sink_t sink = {.fd = state->stage_fd};
341 const ra8_err_t rc = internal_fd_write(&sink, bytes, len);
342 if (rc == k_ra8_ok) {
343 state->position += len;
344 }
345 return rc;
346}
347
360static void internal_transaction_abort(void* ctx)
361{
363 if (state == nullptr) {
364 return;
365 }
366 if (state->stage_fd >= 0) {
367 (void)close(state->stage_fd);
368 state->stage_fd = -1;
369 }
370 if (state->stage_exists && (state->parent_fd >= 0)) {
371 (void)unlinkat(state->parent_fd, state->stage_name, 0);
372 }
373 if (state->parent_fd >= 0) {
374 (void)close(state->parent_fd);
375 state->parent_fd = -1;
376 }
377 state->stage_exists = false;
378 state->active = false;
379}
380
399{
401 if ((state == nullptr) || !state->active || (state->stage_fd < 0)) {
403 }
404 const int stage_sync_rc = fsync(state->stage_fd);
405 const int stage_close_rc = close(state->stage_fd);
406 state->stage_fd = -1;
407 if ((stage_sync_rc != 0) || (stage_close_rc != 0)) {
409 return k_ra8_fail;
410 }
411 if (renameat(state->parent_fd, state->stage_name, state->parent_fd, state->final_name) != 0) {
413 return k_ra8_fail;
414 }
415 state->stage_exists = false;
416 const int sync_rc = fsync(state->parent_fd);
417 (void)close(state->parent_fd);
418 state->parent_fd = -1;
419 state->active = false;
420 return (sync_rc == 0) ? k_ra8_ok : k_ra8_fail;
421}
422
436static void internal_snapshot(const struct stat* status, ra8_fmt_host_snapshot_t* out)
437{
438#ifdef __APPLE__
439 const struct timespec modified = status->st_mtimespec;
440 const struct timespec changed = status->st_ctimespec;
441#else
442 const struct timespec modified = status->st_mtim;
443 const struct timespec changed = status->st_ctim;
444#endif
446 .device = (uint64_t)status->st_dev,
447 .inode = (uint64_t)status->st_ino,
448 .size = (uint64_t)status->st_size,
449 .mtime_sec = (int64_t)modified.tv_sec,
450 .mtime_nsec = (int64_t)modified.tv_nsec,
451 .ctime_sec = (int64_t)changed.tv_sec,
452 .ctime_nsec = (int64_t)changed.tv_nsec,
453 };
454}
455
473 const ra8_fmt_host_snapshot_t* second)
474{
475 return (first->device == second->device) && (first->inode == second->inode) &&
476 (first->size == second->size) && (first->mtime_sec == second->mtime_sec) &&
477 (first->mtime_nsec == second->mtime_nsec) && (first->ctime_sec == second->ctime_sec) &&
478 (first->ctime_nsec == second->ctime_nsec);
479}
480
498static ra8_err_t internal_source_validate(void* ctx, uint64_t expected_size)
499{
500 const ra8_fmt_host_source_t* source = (const ra8_fmt_host_source_t*)ctx;
501 if ((source == nullptr) || (source->source.size != expected_size)) {
503 }
504 return priv_fmt_host_source_unchanged(source);
505}
506
512
514 uint64_t max_size,
516{
517 if ((path == nullptr) || (out == nullptr)) {
518 return k_ra8_err_null_ptr;
519 }
520 *out = (ra8_fmt_host_source_t){.fd = -1};
521 const int fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
522 if (fd < 0) {
523 return (errno == ELOOP) ? k_ra8_err_access_denied : k_ra8_fail;
524 }
525 struct stat status = {};
526 if ((fstat(fd, &status) != 0) || !S_ISREG(status.st_mode) || (status.st_size <= 0) ||
527 ((uint64_t)status.st_size > max_size)) {
528 (void)close(fd);
530 }
531 out->fd = fd;
534 out->source.ctx = out;
535 out->source.size = (uint64_t)status.st_size;
536 internal_snapshot(&status, &out->snapshot);
537 return k_ra8_ok;
538}
539
541 const ra8_fmt_host_source_t* second)
542{
543 if ((first == nullptr) || (second == nullptr) || (first == second) || (first->fd < 0) ||
544 (second->fd < 0) || (first->fd == second->fd)) {
545 return false;
546 }
547 return internal_snapshot_same(&first->snapshot, &second->snapshot);
548}
549
551{
552 if ((source == nullptr) || (source->fd < 0)) {
554 }
555 struct stat status = {};
556 if (fstat(source->fd, &status) != 0) {
557 return k_ra8_fail;
558 }
559 if (!S_ISREG(status.st_mode)) {
561 }
563 internal_snapshot(&status, &current);
564 return internal_snapshot_same(&source->snapshot, &current) ? k_ra8_ok
566}
567
569{
570 if ((source != nullptr) && (source->fd >= 0)) {
571 (void)close(source->fd);
572 source->fd = -1;
573 }
574}
575
580
581RA8_PRIV void priv_fmt_host_log_byte(void* ctx, uint8_t byte)
582{
583 (void)internal_fd_write(ctx, &byte, 1U);
584}
585
589{
590 if ((path == nullptr) || (state == nullptr) || (out == nullptr)) {
591 return k_ra8_err_null_ptr;
592 }
593 *state = (ra8_fmt_host_transaction_t){.parent_fd = -1, .stage_fd = -1};
594 *out = (ra8_fmt_transaction_t){};
595 char parent[k_ra8_fmt_host_path_cap];
596 ra8_err_t rc = internal_split_path(path, parent, state->final_name);
597 if (rc != k_ra8_ok) {
598 return rc;
599 }
600 state->parent_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
601 if (state->parent_fd < 0) {
602 return k_ra8_fail;
603 }
605 for (uint32_t attempt = 0U; (attempt < k_host_create_attempts) && (rc == k_ra8_ok); ++attempt) {
606 rc = internal_stage_name(state, attempt);
607 if (rc == k_ra8_ok) {
608 state->stage_fd = openat(state->parent_fd,
609 state->stage_name,
610 O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
611 (mode_t)k_host_mode_private);
612 if (state->stage_fd >= 0) {
613 state->stage_exists = true;
614 state->active = true;
615 out->ops = &s_transaction_ops;
616 out->ctx = state;
617 return k_ra8_ok;
618 }
619 rc = (errno == EEXIST) ? k_ra8_ok : k_ra8_fail;
620 }
621 }
623 return (rc == k_ra8_ok) ? k_ra8_err_exists : rc;
624}
static RA8_INTERNAL ssize_t internal_pread(int fd, void *buf, size_t count, off_t offset)
Call the default positioned input primitive.
Definition emu_host_io.c:83
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
#define O_NOFOLLOW
Zero fallback paired with explicit no-follow metadata validation.
#define AT_SYMLINK_NOFOLLOW
Zero fallback paired with explicit target-type rejection.
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
-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).
static ra8_err_t internal_source_validate(const ra8_camera_source_t *source)
Validate a source handle and its mandatory vtable rows.
Definition ra8_camera.c:115
@ k_ra8_fail
Generic unspecified failure.
Definition ra8_err.h:133
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ 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_access_denied
Operation refused because the target is protected against it.
Definition ra8_err.h:276
@ 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
ra8_err_t priv_fmt_host_transaction_begin(const char *path, ra8_fmt_host_transaction_t *state, ra8_fmt_transaction_t *out)
Begin a sibling-temp durable replacement transaction.
static ra8_err_t internal_fd_write(void *ctx, const uint8_t *bytes, size_t len)
Append all requested bytes to one descriptor.
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.
static void internal_transaction_abort(void *ctx)
Discard transaction-owned staging resources.
static ra8_err_t internal_pread(void *ctx, uint64_t offset, uint8_t *bytes, size_t len, size_t *got)
Perform positioned reads with exact offset handling and legal short EOF.
static ra8_err_t internal_copy(char *out, size_t cap, const char *text, size_t len)
Copy a bounded string slice and append NUL.
bool priv_fmt_host_sources_same(const ra8_fmt_host_source_t *first, const ra8_fmt_host_source_t *second)
Confirm two opens captured the same unchanged regular-file object.
static ra8_err_t internal_transaction_commit(void *ctx)
Sync, install, and directory-sync one staged artifact.
ra8_err_t priv_fmt_host_source_open(const char *path, uint64_t max_size, ra8_fmt_host_source_t *out)
Open a bounded, regular, non-symlink input object.
void priv_fmt_host_log_byte(void *ctx, uint8_t byte)
Adapt a logging byte to an injected raw-fd sink.
static const ra8_fmt_transaction_ops_t s_transaction_ops
static ra8_err_t internal_stage_name(ra8_fmt_host_transaction_t *state, uint32_t attempt)
Build a bounded sibling staging name.
ra8_fmt_sink_t priv_fmt_host_fd_sink(ra8_fmt_host_fd_sink_t *state)
Obtain the exact-write portable sink for a raw descriptor.
static void internal_snapshot(const struct stat *status, ra8_fmt_host_snapshot_t *out)
Capture portable-width identity and mutation fields from host metadata.
static ra8_err_t internal_name_u64(char *name, size_t cap, size_t *len, uint64_t value)
Append one unsigned decimal to a bounded stage name.
static ra8_err_t internal_transaction_append(void *ctx, const uint8_t *bytes, size_t len)
Append transaction bytes exactly once.
static ra8_err_t internal_validate_destination(int parent_fd, const char *leaf)
Reject existing non-regular or symlink destinations.
static ra8_err_t internal_split_path(const char *path, char parent[k_ra8_fmt_host_path_cap], char leaf[k_ra8_fmt_host_name_cap])
Split a destination into a parent path and safe leaf name.
static ra8_err_t internal_source_validate(void *ctx, uint64_t expected_size)
Portable source-validation callback over host snapshot evidence.
host_const_t
Host adapter bounds.
@ k_host_create_attempts
Collision-bounded stage creation.
@ k_host_mode_private
Staging-file permissions.
@ k_host_decimal_radix
Decimal name digit radix.
void priv_fmt_host_source_close(ra8_fmt_host_source_t *source)
Close an open host source; safe after failed open.
static bool internal_snapshot_same(const ra8_fmt_host_snapshot_t *first, const ra8_fmt_host_snapshot_t *second)
Compare complete captured regular-file evidence.
ra8_err_t priv_fmt_host_source_unchanged(const ra8_fmt_host_source_t *source)
Revalidate one open descriptor against its captured snapshot.
Raw file-descriptor adapters for the portable format-tool contracts.
@ k_ra8_fmt_host_path_cap
Parent-path storage including NUL.
@ k_ra8_fmt_host_name_cap
One leaf name including NUL.
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.
char * strrchr(const char *s, int c)
Locate last occurrence of character in string.
Append sink backed by a caller-owned descriptor.
int fd
Borrowed writable descriptor.
Captured regular-file identity and mutation evidence.
int64_t ctime_nsec
Metadata-change timestamp nanoseconds.
int64_t ctime_sec
Metadata-change timestamp seconds.
uint64_t device
Filesystem device identifier.
int64_t mtime_sec
Modification timestamp seconds.
int64_t mtime_nsec
Modification timestamp nanoseconds.
uint64_t size
Captured regular-file extent.
uint64_t inode
File object identifier.
Open raw-fd source and its portable view.
ra8_fmt_source_t source
Portable positioned-read view.
int fd
Owned descriptor, or -1 when closed.
ra8_fmt_host_snapshot_t snapshot
Immutable-open evidence.
Caller-owned state for one sibling-file transaction.
char stage_name[k_ra8_fmt_host_name_cap]
Staging leaf name.
char final_name[k_ra8_fmt_host_name_cap]
Destination leaf name.
int parent_fd
Owned parent directory.
bool stage_exists
Stage still needs unlink.
int stage_fd
Owned staging descriptor.
bool active
Transaction is usable.
uint64_t position
Bytes appended so far.
Injected append-only sink.
uint64_t size
Exact object byte length.
void * ctx
Backend-owned context.
ra8_fmt_source_validate_fn validate
Optional stability callback.
jof_pread_fn read_at
Positioned-read callback.
Durable artifact-transaction operations.
One caller-owned artifact transaction.
void * ctx
Backend-owned state.
const ra8_fmt_transaction_ops_t * ops
Transaction implementation.