23#error "port/posix is host-only and must never be compiled or linked into target firmware."
40#include <sys/statvfs.h>
45#if defined(__linux__) || defined(__APPLE__)
46#include <sys/syscall.h>
65#ifndef AT_SYMLINK_NOFOLLOW
67#define AT_SYMLINK_NOFOLLOW (0)
70#ifndef RENAME_NOREPLACE
72#define RENAME_NOREPLACE (1U << 0U)
76typedef enum : uint8_t {
88 for (uint16_t i = 0U; i < length; ++i) {
103 }
else if (S_ISLNK(meta.st_mode)) {
105 status = priv_fs_posix_root_alias_verify(parent_fd, component, out_alias);
109 }
else if (!S_ISDIR(meta.st_mode)) {
144 uint16_t length = 0U;
145 while ((*cursor)[length] !=
'\0') {
146 if ((*cursor)[length] ==
'/') {
158 *out_length = length;
171 int next_owned = next;
183 int current = dup(state->
root_fd);
187 const char* cursor = &path[1];
188 for (uint16_t component = 0U; component < (uint16_t)
k_fw_fs_path_cap; ++component) {
190 uint16_t length = 0U;
195 if (cursor[length] ==
'\0') {
196 (void)
memcpy(out_leaf, name, (
size_t)length + 1U);
197 *out_parent_fd = current;
204 cursor = &cursor[(uint16_t)(length + 1U)];
228 if (path[1] ==
'\0') {
229 if (fstat(state->
root_fd, out) != 0) {
246 const int saved_errno = errno;
248 if (stat_result != 0) {
249 if (saved_errno == ENOENT) {
265 struct stat native = {};
272 out->
size_bytes = exists ? (uint64_t)native.st_size : 0U;
296 if (path[1] ==
'\0') {
322 void* directory_state,
323 uint32_t state_bytes)
334 void* directory_state,
359 struct stat meta = {};
365 out->
size_bytes = S_ISDIR(meta.st_mode) ? 0U : (uint64_t)meta.st_size;
390 const int saved_errno = errno;
401 struct stat meta = {};
410 if (S_ISLNK(meta.st_mode)) {
413 if (!S_ISREG(meta.st_mode)) {
422 const int result = unlinkat(parent_fd, leaf, 0);
423 const int saved_errno = errno;
434 struct stat meta = {};
443 if (S_ISLNK(meta.st_mode)) {
446 if (!S_ISDIR(meta.st_mode)) {
455 const int result = unlinkat(parent_fd, leaf, AT_REMOVEDIR);
456 const int saved_errno = errno;
467#if defined(__linux__) && defined(SYS_renameat2)
468 if (syscall(SYS_renameat2, old_fd, old_leaf, new_fd, new_leaf,
RENAME_NOREPLACE) == 0) {
471 if (errno == ENOSYS) {
475#elif defined(__APPLE__)
476 if (renameatx_np(old_fd, old_leaf, new_fd, new_leaf, RENAME_EXCL) == 0) {
479 if (errno == ENOSYS) {
513 const char* old_leaf,
515 const char* new_leaf,
518 struct stat old_parent = {};
519 if (fstat(old_fd, &old_parent) != 0) {
522 struct stat new_parent = {};
523 if (fstat(new_fd, &new_parent) != 0) {
526 if (old_parent.st_dev != new_parent.st_dev) {
532 if (
renameat(old_fd, old_leaf, new_fd, new_leaf) != 0) {
539 const char* old_path,
540 const char* new_path)
542 struct stat source = {};
543 bool source_exists =
false;
548 if (!source_exists) {
551 if (S_ISLNK(source.st_mode)) {
554 struct stat destination = {};
555 bool destination_exists =
false;
559 return destination_stat;
561 if (destination_exists) {
562 if (S_ISLNK(destination.st_mode)) {
606 struct statvfs space = {};
607 if (fstatvfs(state->
root_fd, &space) != 0) {
610 out->
total_bytes = (uint64_t)space.f_blocks * (uint64_t)space.f_frsize;
611 out->
free_bytes = (uint64_t)space.f_bavail * (uint64_t)space.f_frsize;
644 void* transaction_state,
645 uint32_t state_bytes,
646 const char* destination,
670 if (destination_stat.
exists) {
675 (void)
memset(txn, 0,
sizeof(*txn));
686 void* transaction_state,
689 uint32_t* out_written)
716 void* transaction_state,
748 const ra8_err_t checked = validator(validator_ctx, &staged);
766 if (fsync(parent_fd) != 0) {
770 return (result ==
k_ra8_ok) ? closed : result;
787 *out_published =
true;
#define O_DIRECTORY
No-op directory-open fallback for hosts lacking the flag.
Architecture-neutral filesystem namespace, stream, and transaction ports.
ra8_err_t fw_fs_close(fw_fs_file_t *file)
Close and consume an open handle.
Backend-author interface for binding concrete filesystem ports.
ra8_err_t fw_fs_bind(fw_fs_t *out, const fw_fs_namespace_iface_t *namespace_iface, const fw_fs_stream_iface_t *stream_iface, const fw_fs_transaction_iface_t *transaction_iface, void *ctx, const fw_fs_caps_t *caps)
Bind segregated vtables and one context into a complete facade.
#define RENAME_NOREPLACE
Host flag value for atomic no-replace rename probing.
static ra8_err_t internal_intermediate_check(int parent_fd, const char *component, posix_root_alias_t *out_alias)
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.
static ra8_err_t internal_directory_open(fw_fs_posix_state_t *state, const char *path, int *out_fd)
static ra8_err_t internal_txn_commit(void *ctx, void *transaction_state, bool *out_published)
ra8_err_t priv_fs_posix_dir_open(void *ctx, const char *path, void *directory_state, uint32_t state_bytes)
Open a confined raw-directory cursor in caller storage.
static ra8_err_t internal_component_copy(const char *start, uint16_t length, char *out)
static ra8_err_t internal_parent_open_step(int *current, const char *name)
static ra8_err_t internal_txn_begin(void *ctx, void *transaction_state, uint32_t state_bytes, const char *destination, fw_fs_transaction_policy_t policy)
static ra8_err_t internal_stage_open(fw_fs_posix_state_t *state, posix_transaction_state_t *txn)
static ra8_err_t internal_rename(void *ctx, const char *old_path, const char *new_path, bool replace)
static fw_fs_node_type_t internal_node_type(mode_t mode)
static ra8_err_t internal_rename_opened(int old_fd, const char *old_leaf, int new_fd, const char *new_leaf, bool replace)
Validate opened parents and perform the selected rename operation.
ra8_err_t priv_fs_posix_dir_next(void *ctx, void *directory_state, fw_fs_dirent_value_t *out, bool *out_entry)
Copy the next visible raw-directory entry.
posix_directory_budget_t
Number of dot records plus the completion look-ahead record.
@ k_posix_directory_budget_overhead
Dot entries plus EOF look-ahead.
ra8_err_t priv_fs_posix_dir_close(void *ctx, void *directory_state)
Close one owned raw-directory descriptor.
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.
static ra8_err_t internal_space(void *ctx, fw_fs_space_t *out)
static ra8_err_t internal_txn_validate(void *ctx, void *transaction_state, fw_fs_validate_fn_t validator, void *validator_ctx)
ra8_err_t priv_fs_posix_parent_open(fw_fs_posix_state_t *state, const char *path, int *out_parent_fd, char *out_leaf)
Resolve a canonical path's parent without following any symlink.
static ra8_err_t internal_rename_validate_endpoints(fw_fs_posix_state_t *state, const char *old_path, const char *new_path)
static ra8_err_t internal_next_component(const char **cursor, char *out_name, uint16_t *out_length)
#define O_NOFOLLOW
Zero fallback paired with explicit no-follow metadata validation.
static ra8_err_t internal_parent_sync(fw_fs_posix_state_t *state, const char *path)
static ra8_err_t internal_mkdir(void *ctx, const char *path)
static ra8_err_t internal_native_stat(fw_fs_posix_state_t *state, const char *path, struct stat *out, bool *out_exists)
static ra8_err_t internal_txn_abort(void *ctx, void *transaction_state)
static ra8_err_t internal_txn_seek(void *ctx, void *transaction_state, uint64_t offset)
static ra8_err_t internal_rmdir(void *ctx, const char *path)
static ra8_err_t internal_txn_write(void *ctx, void *transaction_state, const uint8_t *src, uint32_t len, uint32_t *out_written)
#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.
static ra8_err_t internal_rename_noreplace(int old_fd, const char *old_leaf, int new_fd, const char *new_leaf)
static ra8_err_t internal_unlink(void *ctx, const char *path)
static ra8_err_t internal_stat(void *ctx, const char *path, fw_fs_stat_t *out)
Root-confined hosted POSIX adapter for fw_if_fs.
ra8_err_t priv_fs_posix_stage_path(const char *destination, uint32_t id, char *out)
Build an 8.3-compatible sibling transaction path.
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_root_alias_open(int root_fd, posix_root_alias_t alias, int *out_fd)
Open a classified root alias through its canonical components.
ra8_err_t priv_fs_posix_directory_next(int fd, posix_directory_reader_t *reader, posix_directory_record_t *out, bool *out_end)
Read and validate the next raw hosted directory record.
ra8_err_t priv_fs_posix_copy_path(char *out, const char *path)
Copy one bounded portable path.
ra8_err_t priv_fs_posix_errno(int value)
Map one captured errno value into ra8_err_t.
ra8_err_t priv_fs_posix_listdir(void *ctx, const char *path, uint32_t max_entries, fw_fs_list_fn_t callback, void *callback_ctx, uint32_t *out_count, bool *out_complete)
Enumerate a POSIX directory through bounded raw records.
fw_fs_timestamp_t priv_fs_posix_timestamp(time_t seconds, long nanoseconds)
Convert a POSIX UTC instant into the portable civil representation.
File-local contracts for the confined POSIX filesystem adapter.
Shared errno/descriptor helpers for the POSIX filesystem port.
posix_root_alias_t
Classification of a verified filesystem-root directory alias.
@ k_posix_root_alias_none
Component is not an approved root alias.
ra8_err_t priv_fs_posix_seek(void *ctx, void *file_state, uint64_t offset)
Seek a POSIX descriptor to an unsigned absolute offset.
ra8_err_t priv_fs_posix_close(void *ctx, void *file_state)
Close and consume one caller-owned POSIX file state.
ra8_err_t priv_fs_posix_sync(void *ctx, void *file_state)
Flush file contents and metadata through POSIX fsync.
ra8_err_t priv_fs_posix_write(void *ctx, void *file_state, const uint8_t *src, uint32_t len, uint32_t *out_written)
Complete POSIX short writes while reporting any accepted prefix.
ra8_err_t priv_fs_posix_size(void *ctx, void *file_state, uint64_t *out_size)
Report a POSIX descriptor's current file length.
@ k_posix_directory_mode
Owner-only created-directory mode.
@ k_posix_stage_attempts
Collision-search attempt cap.
@ k_posix_component_cap
Component buffer including NUL.
const fw_fs_stream_iface_t * priv_fs_posix_stream_iface(void)
Borrow the immutable POSIX byte-stream operation table.
ra8_err_t priv_fs_posix_open(void *ctx, const char *path, fw_fs_open_mode_t mode, void *file_state, uint32_t state_bytes)
Open one confined regular file into caller workspace.
static ra8_err_t internal_txn_commit(void *ctx, void *transaction_state, bool *out_published)
static ra8_err_t internal_txn_begin(void *ctx, void *transaction_state, uint32_t state_bytes, const char *destination, fw_fs_transaction_policy_t policy)
static const fw_fs_namespace_iface_t s_namespace_iface
Immutable firmware namespace vtable.
static ra8_err_t internal_rename(void *ctx, const char *old_path, const char *new_path, bool replace)
static ra8_err_t internal_space(void *ctx, fw_fs_space_t *out)
static ra8_err_t internal_txn_validate(void *ctx, void *transaction_state, fw_fs_validate_fn_t validator, void *validator_ctx)
static ra8_err_t internal_mkdir(void *ctx, const char *path)
static const fw_fs_transaction_iface_t s_transaction_iface
Immutable firmware transaction vtable.
static ra8_err_t internal_txn_abort(void *ctx, void *transaction_state)
static ra8_err_t internal_txn_seek(void *ctx, void *transaction_state, uint64_t offset)
static ra8_err_t internal_rmdir(void *ctx, const char *path)
static ra8_err_t internal_txn_write(void *ctx, void *transaction_state, const uint8_t *src, uint32_t len, uint32_t *out_written)
static ra8_err_t internal_unlink(void *ctx, const char *path)
static ra8_err_t internal_stat(void *ctx, const char *path, fw_fs_stat_t *out)
fw_fs_node_type_t
Filesystem node kind reported by fw_fs_stat.
@ k_fw_fs_node_none
No node exists at the path.
@ k_fw_fs_node_directory
Directory.
@ k_fw_fs_node_file
Regular byte stream.
@ k_fw_fs_node_other
Backend-specific non-file node.
@ k_fw_fs_node_symlink
Symbolic link, if observable.
struct fw_fs_transaction_iface fw_fs_transaction_iface_t
ra8_err_t(* fw_fs_validate_fn_t)(void *ctx, fw_fs_file_t *staged)
Validate staged bytes through a read-only generic file handle.
@ k_fw_fs_path_cap
Largest portable path including its NUL.
struct fw_fs_namespace_iface fw_fs_namespace_iface_t
@ k_fw_fs_open_create_new
Create only when leaf is absent.
@ k_fw_fs_open_read
Existing file, read-only.
fw_fs_transaction_policy_t
Destination policy for a staged transaction.
@ k_fw_fs_txn_create_new
Commit only when destination is absent.
@ k_fw_fs_txn_replace_atomic
Atomically replace an existing file.
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_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
@ k_ra8_err_invalid_arg
Invalid function argument.
@ k_ra8_err_exists
Item already exists – cannot create again.
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
@ k_ra8_err_access_denied
Operation refused because the target is protected against it.
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
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.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Static properties and workspace requirements of one bound port.
Stable caller-owned value returned by fw_fs_dir_next.
uint64_t size_bytes
File length; zero for dirs.
uint16_t name_bytes
Bytes excluding the NUL.
fw_fs_node_type_t type
Entry kind.
char name[k_fw_fs_path_cap]
Copied NUL-terminated leaf.
Caller-owned open file; fields are private to the facade.
Caller-owned POSIX adapter state.
bool atomic_noreplace
Runtime-probed rename guarantee.
uint32_t transaction_id
Per-binding stage-name counter.
int root_fd
Open descriptor, or -1 while inactive.
Portable volume usage snapshot.
uint64_t free_bytes
Bytes available to new data.
uint64_t total_bytes
Addressable data bytes.
uint64_t used_bytes
Allocated bytes.
Result of a portable metadata query.
fw_fs_timestamp_t created
Creation/birth time, when supported.
uint64_t size_bytes
File length; zero for a directory.
fw_fs_timestamp_t accessed
Last access time, when supported.
bool exists
False means a clean lookup miss.
fw_fs_node_type_t type
Kind of node at the path.
fw_fs_timestamp_t modified
Content modification time, when supported.
One complete composition-root filesystem binding.
Validated view over one raw host directory record.
uint16_t name_bytes
Name length excluding the terminator.
const char * name
NUL-terminated name inside the read buffer.
POSIX state placed in caller directory workspace.
posix_directory_reader_t reader
Bounded raw-record buffer and cursor.
int fd
Owned directory descriptor.
int fd
Owned descriptor, or -1 while closed.
POSIX state placed in caller transaction workspace.
bool writer_open
True while the stage descriptor is owned.
fw_fs_transaction_policy_t policy
Requested destination collision policy.
char stage[k_fw_fs_path_cap]
Private sibling stage path.
char destination[k_fw_fs_path_cap]
Final publication path.
bool stage_exists
True while the stage leaf needs cleanup.
posix_file_state_t file_state
Open descriptor state for the stage.