ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
fw_if_fs_posix_bind.c
Go to the documentation of this file.
1
21
22#ifndef RA8_OFF_TARGET
23#error "port/posix is host-only and must never be compiled or linked into target firmware."
24#endif
25
26#ifndef _GNU_SOURCE
28#define _GNU_SOURCE
29#endif
30
31#include <errno.h>
32#include <fcntl.h>
33#include <stdint.h> // ra8-keep-include: `uint8_t` used directly
34#include <stdio.h>
35#include <string.h>
36#include <unistd.h> // ra8-keep-include: `syscall` used directly
37
38#include "fw_if_fs_posix.h" // ra8-keep-include: `fw_fs_posix_state_t` and public interface
39#include "ra8_attributes.h" // ra8-keep-include: `RA8_INTERNAL` used directly
40
41#if defined(__linux__) || defined(__APPLE__)
42#include <sys/syscall.h>
43#endif
44
45#include "fw_if_fs.h" // ra8-keep-include: `fw_fs_t` backend interface used directly
47#include "ra8_err.h" // ra8-keep-include: `ra8_err_t` used directly
48
49#ifndef O_CLOEXEC
51#define O_CLOEXEC (0)
52#endif
53
54#ifndef O_NOFOLLOW
56#define O_NOFOLLOW (0)
57#endif
58
59#ifndef RENAME_NOREPLACE
61#define RENAME_NOREPLACE (1U << 0U)
62#endif
63
79{
80#if defined(__linux__) && defined(SYS_renameat2)
81 errno = 0;
82 const long result = syscall(SYS_renameat2, -1, "x", -1, "y", RENAME_NOREPLACE);
83 if (result != -1L) {
84 return false;
85 }
86 return errno == EBADF;
87#elif defined(__APPLE__)
88 errno = 0;
89 const int result = renameatx_np(-1, "x", -1, "y", RENAME_EXCL);
90 if (result != -1) {
91 return false;
92 }
93 return errno == EBADF;
94#else
95 return false;
96#endif
97}
98
119internal_root_base_open(const char* path, const char** out_cursor, int* out_fd)
120{
122 *out_cursor = path;
123 *out_fd = -1;
124 if (path[0] != '\0') {
125 const char* anchor = (path[0] == '/') ? "/" : ".";
126 const int opened = open(anchor, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
127 if (opened < 0) {
128 status = priv_fs_posix_errno(errno);
129 } else {
130 *out_fd = opened;
131 status = k_ra8_ok;
132 }
133 }
134 return status;
135}
136
154RA8_INTERNAL static ra8_err_t internal_root_skip_slashes(const char** cursor, uint16_t* consumed)
155{
156 ra8_err_t status = k_ra8_ok;
158 while (**cursor == '/') {
159 if (*consumed >= (uint16_t)(k_fw_fs_path_cap - 1U)) {
160 status = k_ra8_err_invalid_size;
161 break;
162 }
163 *cursor = &(*cursor)[1];
164 ++(*consumed);
165 }
166 return status;
167}
168
188internal_root_name_copy(const char** cursor, uint16_t* consumed, char* out_component)
189{
190 uint16_t length = 0U;
191 ra8_err_t status = k_ra8_ok;
193 for (uint16_t index = 0U; index < (uint16_t)k_posix_component_cap; ++index) {
194 const char value = **cursor;
195 bool stop = false;
196 if (value == '/') {
197 stop = true;
198 }
199 if (!stop) {
200 if (value == '\0') {
201 stop = true;
202 }
203 }
204 if (!stop) {
205 if (length >= (uint16_t)(k_posix_component_cap - 1U)) {
206 status = k_ra8_err_invalid_size;
207 stop = true;
208 }
209 }
210 if (!stop) {
211 if (*consumed >= (uint16_t)(k_fw_fs_path_cap - 1U)) {
212 status = k_ra8_err_invalid_size;
213 stop = true;
214 }
215 }
216 if (stop) {
217 break;
218 }
219 out_component[length] = value;
220 ++length;
221 *cursor = &(*cursor)[1];
222 ++(*consumed);
223 }
224 out_component[length] = '\0';
225 return status;
226}
227
247 uint16_t* consumed,
248 char* out_component,
249 bool* out_end)
250{
251 *out_end = false;
252 out_component[0] = '\0';
253 ra8_err_t status = internal_root_skip_slashes(cursor, consumed);
254 if (status == k_ra8_ok) {
255 if (**cursor == '\0') {
256 *out_end = true;
257 } else {
258 status = internal_root_name_copy(cursor, consumed, out_component);
259 }
260 }
261 return status;
262}
263
282RA8_INTERNAL static ra8_err_t internal_root_open_step(int* current, const char* component)
283{
284 int next = -1;
285 ra8_err_t status = priv_fs_posix_component_open(*current, component, &next);
286 if (status == k_ra8_ok) {
287 const ra8_err_t closed = priv_fs_posix_close_fd(current);
288 if (closed != k_ra8_ok) {
289 status = priv_fs_posix_close_fd_preserve(&next, closed);
290 } else {
291 *current = next;
292 next = -1;
293 }
294 }
295 if (next >= 0) {
296 status = priv_fs_posix_close_fd_preserve(&next, status);
297 }
298 return status;
299}
300
317RA8_INTERNAL static ra8_err_t internal_root_walk_step(int* current, const char* component)
318{
319 ra8_err_t status = k_ra8_ok;
320 if (strcmp(component, ".") != 0) {
321 status = internal_root_open_step(current, component);
322 }
323 return status;
324}
325
345internal_root_walk(const char** cursor, uint16_t* consumed, int* current, bool* out_complete)
346{
347 ra8_err_t status = k_ra8_ok;
348 *out_complete = false;
350 for (uint16_t component = 0U; component < (uint16_t)k_fw_fs_path_cap; ++component) {
351 char name[k_posix_component_cap];
352 bool end = false;
353 bool stop = false;
354 status = internal_root_component_scan(cursor, consumed, name, &end);
355 if (status != k_ra8_ok) {
356 stop = true;
357 }
358 if (!stop) {
359 if (end) {
360 *out_complete = true;
361 stop = true;
362 }
363 }
364 if (!stop) {
365 status = internal_root_walk_step(current, name);
366 if (status != k_ra8_ok) {
367 stop = true;
368 }
369 }
370 if (stop) {
371 break;
372 }
373 }
374 return status;
375}
376
398RA8_INTERNAL static ra8_err_t internal_root_open(const char* path, int* out_fd)
399{
400 const char* cursor = path;
401 uint16_t consumed = 0U;
402 int current = -1;
403 bool complete = false;
404 ra8_err_t status = internal_root_base_open(path, &cursor, &current);
405 *out_fd = -1;
406 if (status == k_ra8_ok) {
407 status = internal_root_walk(&cursor, &consumed, &current, &complete);
408 }
409 if (status == k_ra8_ok) {
410 if (!complete) {
411 status = k_ra8_err_invalid_size;
412 } else {
413 *out_fd = current;
414 current = -1;
415 }
416 }
417 if (current >= 0) {
418 const ra8_err_t closed = priv_fs_posix_close_fd(&current);
419 if (status == k_ra8_ok) {
420 status = closed;
421 }
422 }
423 return status;
424}
425
439{
440 state->root_fd = -1;
441 state->transaction_id = 0U;
442 state->removable_media = false;
443 state->atomic_noreplace = false;
444 state->initialized = false;
445}
446
461{
462 *out = (fw_fs_caps_t){
463 .max_file_bytes = (uint64_t)INT64_MAX,
464 .flags = (uint32_t)k_fw_fs_cap_namespace | (uint32_t)k_fw_fs_cap_stream |
471 .file_workspace_bytes = sizeof(posix_file_state_t),
472 .directory_workspace_bytes = sizeof(posix_directory_state_t),
473 .transaction_workspace_bytes = sizeof(posix_transaction_state_t),
474 .path_max_bytes = (uint16_t)k_fw_fs_path_cap,
475 .name_max_bytes = (uint16_t)(k_posix_component_cap - 1U),
476 .max_open_files = (uint16_t)k_posix_max_open_files,
477 .max_open_directories = (uint16_t)k_posix_max_open_files,
478 .file_workspace_align = (uint8_t)_Alignof(posix_file_state_t),
479 .directory_workspace_align = (uint8_t)_Alignof(posix_directory_state_t),
480 .transaction_workspace_align = (uint8_t)_Alignof(posix_transaction_state_t),
481 };
482#ifdef __APPLE__
483 out->flags |= (uint32_t)k_fw_fs_cap_created_time;
484#endif
485 if (state->atomic_noreplace) {
486 out->flags |= (uint32_t)k_fw_fs_cap_atomic_noreplace;
487 }
488 if (state->removable_media) {
489 out->flags |= (uint32_t)k_fw_fs_cap_removable_media;
490 }
491}
492
494{
495 if (out == nullptr) {
496 return k_ra8_err_null_ptr;
497 }
498 if (state == nullptr) {
499 return k_ra8_err_null_ptr;
500 }
501 if (cfg == nullptr) {
502 return k_ra8_err_null_ptr;
503 }
504 if (cfg->root_path == nullptr) {
505 return k_ra8_err_null_ptr;
506 }
507 if (state->initialized) {
508 return k_ra8_err_exists;
509 }
511 int root = -1;
512 const ra8_err_t root_status = internal_root_open(cfg->root_path, &root);
513 if (root_status != k_ra8_ok) {
514 return root_status;
515 }
516 state->root_fd = root;
517 state->transaction_id = 0U;
518 state->removable_media = cfg->removable_media;
520 state->initialized = true;
521 fw_fs_caps_t caps = {};
522 internal_caps(state, &caps);
523 const ra8_err_t bound = priv_fs_posix_bind_interfaces(out, state, &caps);
524 if (bound != k_ra8_ok) {
525 (void)fw_fs_posix_deinit(state);
526 }
527 return bound;
528}
529
531{
532 if (state == nullptr) {
533 return k_ra8_err_null_ptr;
534 }
535 if (!state->initialized) {
537 }
538 const ra8_err_t status = priv_fs_posix_close_fd(&state->root_fd);
540 return status;
541}
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
Architecture-neutral filesystem namespace, stream, and transaction ports.
#define RENAME_NOREPLACE
Host flag value for atomic no-replace rename probing.
ra8_err_t priv_fs_posix_component_open(int parent_fd, const char *component, int *out_fd)
Open one validated directory component without following its pathname.
ra8_err_t priv_fs_posix_bind_interfaces(fw_fs_t *out, fw_fs_posix_state_t *state, const fw_fs_caps_t *caps)
Bind the immutable POSIX operation tables to initialized state.
#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.
Root-confined hosted POSIX adapter for fw_if_fs.
static ra8_err_t internal_root_skip_slashes(const char **cursor, uint16_t *consumed)
Skip repeated path separators under the complete-path bound.
static ra8_err_t internal_root_base_open(const char *path, const char **out_cursor, int *out_fd)
Open the descriptor anchor for an absolute or relative root path.
ra8_err_t fw_fs_posix_deinit(fw_fs_posix_state_t *state)
Close the root descriptor; no bound operation is valid afterward.
static ra8_err_t internal_root_walk_step(int *current, const char *component)
Apply one scanned component to the current root descriptor.
static ra8_err_t internal_root_name_copy(const char **cursor, uint16_t *consumed, char *out_component)
Copy one bounded root component and advance its path cursor.
static ra8_err_t internal_root_component_scan(const char **cursor, uint16_t *consumed, char *out_component, bool *out_end)
Scan one bounded component while normalizing repeated slashes.
static ra8_err_t internal_root_open_step(int *current, const char *component)
Descend into one root component and retire the previous descriptor.
static ra8_err_t internal_root_open(const char *path, int *out_fd)
Open one caller-selected confinement root component by component.
static ra8_err_t internal_root_walk(const char **cursor, uint16_t *consumed, int *current, bool *out_complete)
Walk every remaining root component beneath an owned anchor.
static bool internal_atomic_noreplace_available(void)
Probe whether the host provides an atomic no-replace rename.
static void internal_caps(const fw_fs_posix_state_t *state, fw_fs_caps_t *out)
Assemble capabilities for one initialized POSIX adapter.
ra8_err_t fw_fs_posix_init(fw_fs_t *out, fw_fs_posix_state_t *state, const fw_fs_posix_cfg_t *cfg)
Open/configure a root-confined POSIX binding.
static void internal_state_reset(fw_fs_posix_state_t *state)
Restore one inactive POSIX adapter state to its public sentinel.
ra8_err_t priv_fs_posix_close_fd(int *fd)
Close exactly once and invalidate the caller's descriptor.
ra8_err_t priv_fs_posix_close_fd_preserve(int *fd, ra8_err_t primary)
Close one owned descriptor while preserving a primary status.
ra8_err_t priv_fs_posix_errno(int value)
Map one captured errno value into ra8_err_t.
Shared errno/descriptor helpers for the POSIX filesystem port.
@ k_posix_max_open_files
Truthful hosted descriptor capacity.
@ k_posix_component_cap
Component buffer including NUL.
@ k_fw_fs_path_cap
Largest portable path including its NUL.
@ k_fw_fs_cap_durable_directory_sync
Namespace changes can be made durable.
@ k_fw_fs_cap_durable_file_sync
Successful file sync reaches durable media.
@ k_fw_fs_cap_symlinks
Symbolic links may be represented by the backend.
@ k_fw_fs_cap_stream
Regular-file stream operations are available.
@ k_fw_fs_cap_namespace
Metadata and namespace operations are available.
@ k_fw_fs_cap_space_query
Volume capacity and available bytes can be queried.
@ k_fw_fs_cap_rejects_symlink_walk
Path traversal refuses symbolic-link components.
@ k_fw_fs_cap_removable_media
The backing volume may disappear at runtime.
@ k_fw_fs_cap_same_volume_rename
Rename is supported within one backend volume.
@ k_fw_fs_cap_modified_time
Modification timestamps may be reported as valid.
@ k_fw_fs_cap_create_exclusive
Open can atomically require that its leaf be absent.
@ k_fw_fs_cap_file_sync
An explicit file-sync operation is available.
@ k_fw_fs_cap_accessed_time
Access timestamps may be reported as valid.
@ k_fw_fs_cap_atomic_noreplace
Rename can atomically reject an existing destination.
@ k_fw_fs_cap_created_time
Creation timestamps may be reported as valid.
@ k_fw_fs_cap_transactions
Staged publication operations are available.
@ k_fw_fs_cap_atomic_replace
Rename can atomically replace an existing destination.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_LOOP_BOUND(ceiling)
NASA Power-of-10 Rule 2: bind ONE loop to a compile-time ceiling.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ 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_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ 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_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
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
Static properties and workspace requirements of one bound port.
uint32_t flags
OR of fw_fs_capability_t.
POSIX composition-root settings.
const char * root_path
Existing host directory used as /.
bool removable_media
Truthful property of the selected root.
Caller-owned POSIX adapter state.
bool atomic_noreplace
Runtime-probed rename guarantee.
bool removable_media
Capability input.
bool initialized
Lifecycle guard.
uint32_t transaction_id
Per-binding stage-name counter.
int root_fd
Open descriptor, or -1 while inactive.
One complete composition-root filesystem binding.
POSIX state placed in caller directory workspace.
POSIX state placed in caller file workspace.
POSIX state placed in caller transaction workspace.