ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
fw_if_fs_posix.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 "fw_if_fs_posix.h" // ra8-keep-include: `fw_fs_posix_state_t` and public interface
32
33#include <errno.h>
34#include <fcntl.h>
35#include <stddef.h> // ra8-keep-include: `size_t` used directly
36#include <stdint.h> // ra8-keep-include: `uint8_t` used directly
37#include <stdio.h>
38#include <string.h>
39#include <sys/stat.h> // ra8-keep-include: `stat` and `fstat` used directly
40#include <sys/statvfs.h>
41#include <unistd.h>
42
43#include "ra8_attributes.h" // ra8-keep-include: `RA8_INTERNAL` and `RA8_PRIV` used directly
44
45#if defined(__linux__) || defined(__APPLE__)
46#include <sys/syscall.h>
47#endif
48
49#include "fw_if_fs.h" // ra8-keep-include: `fw_fs_t` backend interface used directly
50#include "fw_if_fs_backend.h"
51#include "fw_if_fs_posix_contracts_internal.h" // ra8-keep-include: `internal_native_stat` unit-private forward declarations
52#include "fw_if_fs_posix_internal.h" // ra8-keep-include: `priv_fs_posix_errno` cross-unit contracts
53#include "ra8_err.h" // ra8-keep-include: `ra8_err_t` used directly
54
55#ifndef O_CLOEXEC
57#define O_CLOEXEC (0)
58#endif
59
60#ifndef O_NOFOLLOW
62#define O_NOFOLLOW (0)
63#endif
64
65#ifndef AT_SYMLINK_NOFOLLOW
67#define AT_SYMLINK_NOFOLLOW (0)
68#endif
69
70#ifndef RENAME_NOREPLACE
72#define RENAME_NOREPLACE (1U << 0U)
73#endif
74
79
80RA8_INTERNAL static ra8_err_t internal_component_copy(const char* start, uint16_t length, char* out)
81{
82 if (length == 0U) {
84 }
85 if (length >= (uint16_t)k_posix_component_cap) {
87 }
88 for (uint16_t i = 0U; i < length; ++i) {
89 out[i] = start[i];
90 }
91 out[length] = '\0';
92 return k_ra8_ok;
93}
94
96internal_intermediate_check(int parent_fd, const char* component, posix_root_alias_t* out_alias)
97{
98 *out_alias = k_posix_root_alias_none;
99 struct stat meta;
100 ra8_err_t status = k_ra8_ok;
101 if (fstatat(parent_fd, component, &meta, AT_SYMLINK_NOFOLLOW) != 0) {
102 status = priv_fs_posix_errno(errno);
103 } else if (S_ISLNK(meta.st_mode)) {
104#ifdef __APPLE__
105 status = priv_fs_posix_root_alias_verify(parent_fd, component, out_alias);
106#else
108#endif
109 } else if (!S_ISDIR(meta.st_mode)) {
110 status = k_ra8_err_not_found;
111 } else {
112 status = k_ra8_ok;
113 }
114 return status;
115}
116
117RA8_PRIV ra8_err_t priv_fs_posix_component_open(int parent_fd, const char* component, int* out_fd)
118{
119 *out_fd = -1;
121 ra8_err_t status = internal_intermediate_check(parent_fd, component, &alias);
122 if (status == k_ra8_ok) {
123#ifdef __APPLE__
124 if (alias != k_posix_root_alias_none) {
125 status = priv_fs_posix_root_alias_open(parent_fd, alias, out_fd);
126 } else
127#endif
128 {
129 const int opened =
130 openat(parent_fd, component, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
131 if (opened < 0) {
132 status = priv_fs_posix_errno(errno);
133 } else {
134 *out_fd = opened;
135 }
136 }
137 }
138 return status;
139}
140
142internal_next_component(const char** cursor, char* out_name, uint16_t* out_length)
143{
144 uint16_t length = 0U;
145 while ((*cursor)[length] != '\0') {
146 if ((*cursor)[length] == '/') {
147 break;
148 }
149 ++length;
150 if (length >= (uint16_t)k_posix_component_cap) {
152 }
153 }
154 const ra8_err_t copied = internal_component_copy(*cursor, length, out_name);
155 if (copied != k_ra8_ok) {
156 return copied;
157 }
158 *out_length = length;
159 return k_ra8_ok;
160}
161
162RA8_INTERNAL static ra8_err_t internal_parent_open_step(int* current, const char* name)
163{
164 int next = -1;
165 const ra8_err_t opened = priv_fs_posix_component_open(*current, name, &next);
166 if (opened != k_ra8_ok) {
167 return priv_fs_posix_close_fd_preserve(current, opened);
168 }
169 const ra8_err_t closed = priv_fs_posix_close_fd(current);
170 if (closed != k_ra8_ok) {
171 int next_owned = next;
172 return priv_fs_posix_close_fd_preserve(&next_owned, closed);
173 }
174 *current = next;
175 return k_ra8_ok;
176}
177
179 const char* path,
180 int* out_parent_fd,
181 char* out_leaf)
182{
183 int current = dup(state->root_fd);
184 if (current < 0) {
185 return priv_fs_posix_errno(errno);
186 }
187 const char* cursor = &path[1];
188 for (uint16_t component = 0U; component < (uint16_t)k_fw_fs_path_cap; ++component) {
189 char name[k_posix_component_cap];
190 uint16_t length = 0U;
191 const ra8_err_t scanned = internal_next_component(&cursor, name, &length);
192 if (scanned != k_ra8_ok) {
193 return priv_fs_posix_close_fd_preserve(&current, scanned);
194 }
195 if (cursor[length] == '\0') {
196 (void)memcpy(out_leaf, name, (size_t)length + 1U);
197 *out_parent_fd = current;
198 return k_ra8_ok;
199 }
200 const ra8_err_t stepped = internal_parent_open_step(&current, name);
201 if (stepped != k_ra8_ok) {
202 return stepped;
203 }
204 cursor = &cursor[(uint16_t)(length + 1U)];
205 }
207}
208
210{
211 if (S_ISREG(mode)) {
212 return k_fw_fs_node_file;
213 }
214 if (S_ISDIR(mode)) {
216 }
217 if (S_ISLNK(mode)) {
219 }
220 return k_fw_fs_node_other;
221}
222
224 const char* path,
225 struct stat* out,
226 bool* out_exists)
227{
228 if (path[1] == '\0') {
229 if (fstat(state->root_fd, out) != 0) {
230 return priv_fs_posix_errno(errno);
231 }
232 *out_exists = true;
233 return k_ra8_ok;
234 }
235 int parent_fd = -1;
236 char leaf[k_posix_component_cap];
237 const ra8_err_t parent = priv_fs_posix_parent_open(state, path, &parent_fd, leaf);
238 if (parent != k_ra8_ok) {
239 if (parent == k_ra8_err_not_found) {
240 *out_exists = false;
241 return k_ra8_ok;
242 }
243 return parent;
244 }
245 const int stat_result = fstatat(parent_fd, leaf, out, AT_SYMLINK_NOFOLLOW);
246 const int saved_errno = errno;
247 const ra8_err_t closed = priv_fs_posix_close_fd(&parent_fd);
248 if (stat_result != 0) {
249 if (saved_errno == ENOENT) {
250 *out_exists = false;
251 return k_ra8_ok;
252 }
253 return priv_fs_posix_errno(saved_errno);
254 }
255 if (closed != k_ra8_ok) {
256 return closed;
257 }
258 *out_exists = true;
259 return k_ra8_ok;
260}
261
262RA8_INTERNAL static ra8_err_t internal_stat(void* ctx, const char* path, fw_fs_stat_t* out)
263{
265 struct stat native = {};
266 bool exists = false;
267 const ra8_err_t result = internal_native_stat(state, path, &native, &exists);
268 if (result != k_ra8_ok) {
269 return result;
270 }
271 out->exists = exists;
272 out->size_bytes = exists ? (uint64_t)native.st_size : 0U;
273 out->type = exists ? internal_node_type(native.st_mode) : k_fw_fs_node_none;
274 if (exists) {
275#ifdef __APPLE__
276 out->created =
277 priv_fs_posix_timestamp(native.st_birthtimespec.tv_sec, native.st_birthtimespec.tv_nsec);
278 out->modified =
279 priv_fs_posix_timestamp(native.st_mtimespec.tv_sec, native.st_mtimespec.tv_nsec);
280 out->accessed =
281 priv_fs_posix_timestamp(native.st_atimespec.tv_sec, native.st_atimespec.tv_nsec);
282#else
283 out->modified = priv_fs_posix_timestamp(native.st_mtim.tv_sec, native.st_mtim.tv_nsec);
284 out->accessed = priv_fs_posix_timestamp(native.st_atim.tv_sec, native.st_atim.tv_nsec);
285#endif
286 }
287 if (out->type == k_fw_fs_node_directory) {
288 out->size_bytes = 0U;
289 }
290 return k_ra8_ok;
291}
292
294internal_directory_open(fw_fs_posix_state_t* state, const char* path, int* out_fd)
295{
296 if (path[1] == '\0') {
297 *out_fd = dup(state->root_fd);
298 return (*out_fd < 0) ? priv_fs_posix_errno(errno) : k_ra8_ok;
299 }
300 int parent_fd = -1;
301 char leaf[k_posix_component_cap];
302 const ra8_err_t parent = priv_fs_posix_parent_open(state, path, &parent_fd, leaf);
303 if (parent != k_ra8_ok) {
304 return parent;
305 }
306 int opened = -1;
307 const ra8_err_t open_status = priv_fs_posix_component_open(parent_fd, leaf, &opened);
308 const ra8_err_t closed = priv_fs_posix_close_fd(&parent_fd);
309 if (open_status != k_ra8_ok) {
310 return open_status;
311 }
312 if (closed != k_ra8_ok) {
313 int owned = opened;
314 return priv_fs_posix_close_fd_preserve(&owned, closed);
315 }
316 *out_fd = opened;
317 return k_ra8_ok;
318}
319
321 const char* path,
322 void* directory_state,
323 uint32_t state_bytes)
324{
325 if (state_bytes < (uint32_t)sizeof(posix_directory_state_t)) {
326 return k_ra8_err_no_mem;
327 }
328 posix_directory_state_t* directory = (posix_directory_state_t*)directory_state;
329 *directory = (posix_directory_state_t){.fd = -1};
330 return internal_directory_open((fw_fs_posix_state_t*)ctx, path, &directory->fd);
331}
332
334 void* directory_state,
336 bool* out_entry)
337{
338 (void)ctx;
339 posix_directory_state_t* directory = (posix_directory_state_t*)directory_state;
340 for (uint16_t skipped = 0U; skipped < (uint16_t)k_posix_directory_budget_overhead; ++skipped) {
341 posix_directory_record_t record = {};
342 bool at_end = false;
343 ra8_err_t err =
344 priv_fs_posix_directory_next(directory->fd, &directory->reader, &record, &at_end);
345 if (err != k_ra8_ok) {
346 *out_entry = false;
347 return err;
348 }
349 if (at_end) {
350 *out_entry = false;
351 return err;
352 }
353 if (strcmp(record.name, ".") == 0) {
354 continue;
355 }
356 if (strcmp(record.name, "..") == 0) {
357 continue;
358 }
359 struct stat meta = {};
360 if (fstatat(directory->fd, record.name, &meta, AT_SYMLINK_NOFOLLOW) != 0) {
361 return priv_fs_posix_errno(errno);
362 }
363 (void)memcpy(out->name, record.name, (size_t)record.name_bytes + 1U);
364 out->name_bytes = record.name_bytes;
365 out->size_bytes = S_ISDIR(meta.st_mode) ? 0U : (uint64_t)meta.st_size;
366 out->type = internal_node_type(meta.st_mode);
367 *out_entry = true;
368 return k_ra8_ok;
369 }
371}
372
373RA8_PRIV ra8_err_t priv_fs_posix_dir_close(void* ctx, void* directory_state)
374{
375 (void)ctx;
376 posix_directory_state_t* directory = (posix_directory_state_t*)directory_state;
377 return priv_fs_posix_close_fd(&directory->fd);
378}
379
380RA8_INTERNAL static ra8_err_t internal_mkdir(void* ctx, const char* path)
381{
383 int parent_fd = -1;
384 char leaf[k_posix_component_cap];
385 const ra8_err_t parent = priv_fs_posix_parent_open(state, path, &parent_fd, leaf);
386 if (parent != k_ra8_ok) {
387 return parent;
388 }
389 const int result = mkdirat(parent_fd, leaf, (mode_t)k_posix_directory_mode);
390 const int saved_errno = errno;
391 const ra8_err_t closed = priv_fs_posix_close_fd(&parent_fd);
392 if (result != 0) {
393 return priv_fs_posix_errno(saved_errno);
394 }
395 return closed;
396}
397
398RA8_INTERNAL static ra8_err_t internal_unlink(void* ctx, const char* path)
399{
401 struct stat meta = {};
402 bool exists = false;
403 const ra8_err_t stated = internal_native_stat(state, path, &meta, &exists);
404 if (stated != k_ra8_ok) {
405 return stated;
406 }
407 if (!exists) {
408 return k_ra8_err_not_found;
409 }
410 if (S_ISLNK(meta.st_mode)) {
412 }
413 if (!S_ISREG(meta.st_mode)) {
415 }
416 int parent_fd = -1;
417 char leaf[k_posix_component_cap];
418 const ra8_err_t parent = priv_fs_posix_parent_open(state, path, &parent_fd, leaf);
419 if (parent != k_ra8_ok) {
420 return parent;
421 }
422 const int result = unlinkat(parent_fd, leaf, 0);
423 const int saved_errno = errno;
424 const ra8_err_t closed = priv_fs_posix_close_fd(&parent_fd);
425 if (result != 0) {
426 return priv_fs_posix_errno(saved_errno);
427 }
428 return closed;
429}
430
431RA8_INTERNAL static ra8_err_t internal_rmdir(void* ctx, const char* path)
432{
434 struct stat meta = {};
435 bool exists = false;
436 const ra8_err_t stated = internal_native_stat(state, path, &meta, &exists);
437 if (stated != k_ra8_ok) {
438 return stated;
439 }
440 if (!exists) {
441 return k_ra8_err_not_found;
442 }
443 if (S_ISLNK(meta.st_mode)) {
445 }
446 if (!S_ISDIR(meta.st_mode)) {
448 }
449 int parent_fd = -1;
450 char leaf[k_posix_component_cap];
451 const ra8_err_t parent = priv_fs_posix_parent_open(state, path, &parent_fd, leaf);
452 if (parent != k_ra8_ok) {
453 return parent;
454 }
455 const int result = unlinkat(parent_fd, leaf, AT_REMOVEDIR);
456 const int saved_errno = errno;
457 const ra8_err_t closed = priv_fs_posix_close_fd(&parent_fd);
458 if (result != 0) {
459 return priv_fs_posix_errno(saved_errno);
460 }
461 return closed;
462}
463
465internal_rename_noreplace(int old_fd, const char* old_leaf, int new_fd, const char* new_leaf)
466{
467#if defined(__linux__) && defined(SYS_renameat2)
468 if (syscall(SYS_renameat2, old_fd, old_leaf, new_fd, new_leaf, RENAME_NOREPLACE) == 0) {
469 return k_ra8_ok;
470 }
471 if (errno == ENOSYS) {
473 }
474 return priv_fs_posix_errno(errno);
475#elif defined(__APPLE__)
476 if (renameatx_np(old_fd, old_leaf, new_fd, new_leaf, RENAME_EXCL) == 0) {
477 return k_ra8_ok;
478 }
479 if (errno == ENOSYS) {
481 }
482 return priv_fs_posix_errno(errno);
483#else
484 (void)old_fd;
485 (void)old_leaf;
486 (void)new_fd;
487 (void)new_leaf;
489#endif
490}
491
513 const char* old_leaf,
514 int new_fd,
515 const char* new_leaf,
516 bool replace)
517{
518 struct stat old_parent = {};
519 if (fstat(old_fd, &old_parent) != 0) {
520 return priv_fs_posix_errno(errno);
521 }
522 struct stat new_parent = {};
523 if (fstat(new_fd, &new_parent) != 0) {
524 return priv_fs_posix_errno(errno);
525 }
526 if (old_parent.st_dev != new_parent.st_dev) {
528 }
529 if (!replace) {
530 return internal_rename_noreplace(old_fd, old_leaf, new_fd, new_leaf);
531 }
532 if (renameat(old_fd, old_leaf, new_fd, new_leaf) != 0) {
533 return priv_fs_posix_errno(errno);
534 }
535 return k_ra8_ok;
536}
537
539 const char* old_path,
540 const char* new_path)
541{
542 struct stat source = {};
543 bool source_exists = false;
544 const ra8_err_t stated = internal_native_stat(state, old_path, &source, &source_exists);
545 if (stated != k_ra8_ok) {
546 return stated;
547 }
548 if (!source_exists) {
549 return k_ra8_err_not_found;
550 }
551 if (S_ISLNK(source.st_mode)) {
553 }
554 struct stat destination = {};
555 bool destination_exists = false;
556 const ra8_err_t destination_stat =
557 internal_native_stat(state, new_path, &destination, &destination_exists);
558 if (destination_stat != k_ra8_ok) {
559 return destination_stat;
560 }
561 if (destination_exists) {
562 if (S_ISLNK(destination.st_mode)) {
564 }
565 }
566 return k_ra8_ok;
567}
568
570internal_rename(void* ctx, const char* old_path, const char* new_path, bool replace)
571{
573 const ra8_err_t validated = internal_rename_validate_endpoints(state, old_path, new_path);
574 if (validated != k_ra8_ok) {
575 return validated;
576 }
577 int old_fd = -1;
578 int new_fd = -1;
579 char old_leaf[k_posix_component_cap];
580 char new_leaf[k_posix_component_cap];
581 ra8_err_t result = priv_fs_posix_parent_open(state, old_path, &old_fd, old_leaf);
582 if (result == k_ra8_ok) {
583 result = priv_fs_posix_parent_open(state, new_path, &new_fd, new_leaf);
584 }
585 if (result == k_ra8_ok) {
586 result = internal_rename_opened(old_fd, old_leaf, new_fd, new_leaf, replace);
587 }
588 if (old_fd >= 0) {
589 const ra8_err_t closed = priv_fs_posix_close_fd(&old_fd);
590 if (result == k_ra8_ok) {
591 result = closed;
592 }
593 }
594 if (new_fd >= 0) {
595 const ra8_err_t closed = priv_fs_posix_close_fd(&new_fd);
596 if (result == k_ra8_ok) {
597 result = closed;
598 }
599 }
600 return result;
601}
602
604{
606 struct statvfs space = {};
607 if (fstatvfs(state->root_fd, &space) != 0) {
608 return priv_fs_posix_errno(errno);
609 }
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;
612 out->used_bytes = out->total_bytes - ((uint64_t)space.f_bfree * (uint64_t)space.f_frsize);
613 return k_ra8_ok;
614}
615
618{
619 for (uint16_t attempt = 0U; attempt < (uint16_t)k_posix_stage_attempts; ++attempt) {
620 ++state->transaction_id;
621 const ra8_err_t named =
623 if (named != k_ra8_ok) {
624 return named;
625 }
626 const ra8_err_t opened = priv_fs_posix_open(state,
627 txn->stage,
629 &txn->file_state,
630 sizeof(txn->file_state));
631 if (opened == k_ra8_ok) {
632 txn->writer_open = true;
633 txn->stage_exists = true;
634 return k_ra8_ok;
635 }
636 if (opened != k_ra8_err_exists) {
637 return opened;
638 }
639 }
640 return k_ra8_err_no_mem;
641}
642
644 void* transaction_state,
645 uint32_t state_bytes,
646 const char* destination,
648{
649 if (state_bytes < sizeof(posix_transaction_state_t)) {
650 return k_ra8_err_no_mem;
651 }
653 if (policy == k_fw_fs_txn_create_new) {
654 if (!state->atomic_noreplace) {
656 }
657 }
658 fw_fs_stat_t destination_stat = {};
659 const ra8_err_t stated = internal_stat(state, destination, &destination_stat);
660 if (stated != k_ra8_ok) {
661 return stated;
662 }
663 if (destination_stat.type == k_fw_fs_node_symlink) {
665 }
666 if (destination_stat.type == k_fw_fs_node_directory) {
668 }
669 if (policy == k_fw_fs_txn_create_new) {
670 if (destination_stat.exists) {
671 return k_ra8_err_exists;
672 }
673 }
674 posix_transaction_state_t* txn = (posix_transaction_state_t*)transaction_state;
675 (void)memset(txn, 0, sizeof(*txn));
676 txn->file_state.fd = -1;
677 txn->policy = policy;
678 const ra8_err_t copied = priv_fs_posix_copy_path(txn->destination, destination);
679 if (copied != k_ra8_ok) {
680 return copied;
681 }
682 return internal_stage_open(state, txn);
683}
684
686 void* transaction_state,
687 const uint8_t* src,
688 uint32_t len,
689 uint32_t* out_written)
690{
691 posix_transaction_state_t* txn = (posix_transaction_state_t*)transaction_state;
692 if (!txn->writer_open) {
694 }
695 return priv_fs_posix_write(ctx, &txn->file_state, src, len, out_written);
696}
697
698RA8_INTERNAL static ra8_err_t internal_txn_seek(void* ctx, void* transaction_state, uint64_t offset)
699{
700 posix_transaction_state_t* txn = (posix_transaction_state_t*)transaction_state;
701 if (!txn->writer_open) {
703 }
704 uint64_t size = 0U;
705 const ra8_err_t sized = priv_fs_posix_size(ctx, &txn->file_state, &size);
706 if (sized != k_ra8_ok) {
707 return sized;
708 }
709 if (offset > size) {
711 }
712 return priv_fs_posix_seek(ctx, &txn->file_state, offset);
713}
714
716 void* transaction_state,
717 fw_fs_validate_fn_t validator,
718 void* validator_ctx)
719{
720 posix_transaction_state_t* txn = (posix_transaction_state_t*)transaction_state;
721 if (!txn->writer_open) {
723 }
724 const ra8_err_t synced = priv_fs_posix_sync(ctx, &txn->file_state);
725 if (synced != k_ra8_ok) {
726 return synced;
727 }
728 const ra8_err_t closed = priv_fs_posix_close(ctx, &txn->file_state);
729 txn->writer_open = false;
730 if (closed != k_ra8_ok) {
731 return closed;
732 }
733 const ra8_err_t opened = priv_fs_posix_open(ctx,
734 txn->stage,
736 &txn->file_state,
737 sizeof(txn->file_state));
738 if (opened != k_ra8_ok) {
739 return opened;
740 }
741 fw_fs_file_t staged = {
743 .ctx = ctx,
744 .state = &txn->file_state,
745 .state_bytes = sizeof(txn->file_state),
746 .is_open = true,
747 };
748 const ra8_err_t checked = validator(validator_ctx, &staged);
749 const ra8_err_t shut = fw_fs_close(&staged);
750 if (checked != k_ra8_ok) {
751 return checked;
752 }
753 return shut;
754}
755
757{
758 int parent_fd = -1;
759 char leaf[k_posix_component_cap];
760 const ra8_err_t parent = priv_fs_posix_parent_open(state, path, &parent_fd, leaf);
761 if (parent != k_ra8_ok) {
762 return parent;
763 }
764 (void)leaf;
765 ra8_err_t result = k_ra8_ok;
766 if (fsync(parent_fd) != 0) {
767 result = priv_fs_posix_errno(errno);
768 }
769 const ra8_err_t closed = priv_fs_posix_close_fd(&parent_fd);
770 return (result == k_ra8_ok) ? closed : result;
771}
772
774internal_txn_commit(void* ctx, void* transaction_state, bool* out_published)
775{
777 posix_transaction_state_t* txn = (posix_transaction_state_t*)transaction_state;
778 if (txn->writer_open) {
780 }
781 const bool replace = txn->policy == k_fw_fs_txn_replace_atomic;
782 const ra8_err_t renamed = internal_rename(state, txn->stage, txn->destination, replace);
783 if (renamed != k_ra8_ok) {
784 return renamed;
785 }
786 txn->stage_exists = false;
787 *out_published = true;
788 return internal_parent_sync(state, txn->destination);
789}
790
791RA8_INTERNAL static ra8_err_t internal_txn_abort(void* ctx, void* transaction_state)
792{
793 posix_transaction_state_t* txn = (posix_transaction_state_t*)transaction_state;
794 ra8_err_t first = k_ra8_ok;
795 if (txn->writer_open) {
796 first = priv_fs_posix_close(ctx, &txn->file_state);
797 txn->writer_open = false;
798 }
799 if (txn->stage_exists) {
800 const ra8_err_t removed = internal_unlink(ctx, txn->stage);
801 if (first == k_ra8_ok) {
802 first = removed;
803 }
804 if (removed == k_ra8_ok) {
805 txn->stage_exists = false;
806 }
807 }
808 return first;
809}
810
813 .stat = internal_stat,
814 .listdir = priv_fs_posix_listdir,
815 .dir_open = priv_fs_posix_dir_open,
816 .dir_next = priv_fs_posix_dir_next,
817 .dir_close = priv_fs_posix_dir_close,
818 .mkdir = internal_mkdir,
819 .unlink = internal_unlink,
820 .rmdir = internal_rmdir,
821 .rename = internal_rename,
822 .space = internal_space,
823};
824
827 .begin = internal_txn_begin,
828 .write = internal_txn_write,
829 .seek = internal_txn_seek,
830 .validate = internal_txn_validate,
831 .commit = internal_txn_commit,
832 .abort = internal_txn_abort,
833};
834
836 fw_fs_posix_state_t* state,
837 const fw_fs_caps_t* caps)
838{
839 return fw_fs_bind(out,
843 state,
844 caps);
845}
#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.
Definition fw_if_fs.c:783
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.
Definition fw_if_fs.c:427
#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.
Definition ra8_err.h:180
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ 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_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_access_denied
Operation refused because the target is protected against it.
Definition ra8_err.h:276
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ 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 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.