ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_storage_vfs.c
Go to the documentation of this file.
1
15
16#include "mdl_storage_vfs.h"
17
18#include <stdint.h>
19
20#include "ra8_attributes.h"
21#include "ra8_err.h"
22#include "ra8_io_vfs.h"
23
25typedef struct {
26 uint16_t length;
27 uint16_t last_slash;
29
45RA8_INTERNAL static bool internal_is_dot_component(const char* text, uint16_t len)
46{
47 if (len == 1U) {
48 return text[0] == '.';
49 }
50 if (len == 2U) {
51 if (text[0] == '.') {
52 return text[1] == '.';
53 }
54 }
55 return false;
56}
57
76internal_bounded_length(const char* text, uint16_t cap, uint16_t* out_len)
77{
78 if (text == nullptr) {
79 return k_ra8_err_null_ptr;
80 }
81 if (out_len == nullptr) {
82 return k_ra8_err_null_ptr;
83 }
84 for (uint16_t i = 0U; i < cap; ++i) {
85 if (text[i] == '\0') {
86 *out_len = i;
87 return k_ra8_ok;
88 }
89 }
91}
92
110internal_mount_prefix(const char* path, uint16_t len, uint16_t* out_colon)
111{
112 if (len < 4U) {
114 }
115 for (uint16_t i = 0U; i < (uint16_t)k_ra8_io_vfs_name_max; ++i) {
116 if (i >= len) {
118 }
119 if (path[i] == ':') {
120 if (i == 0U) {
122 }
123 *out_colon = i;
124 return k_ra8_ok;
125 }
126 if (path[i] == '/') {
128 }
129 if (path[i] == '\\') {
131 }
132 if ((uint8_t)path[i] < 0x20U) {
134 }
135 }
137}
138
157internal_path_components(const char* path, uint16_t len, uint16_t colon, uint16_t* out_slash)
158{
159 if (path[colon + 1U] != '/') {
161 }
162 uint16_t start = (uint16_t)(colon + 2U);
163 uint16_t slash = (uint16_t)(colon + 1U);
164 for (uint16_t i = start; i <= len; ++i) {
165 const char c = path[i];
166 bool boundary = c == '/';
167 if (c == '\0') {
168 boundary = true;
169 }
170 if (boundary) {
171 const uint16_t component_len = (uint16_t)(i - start);
172 if (component_len == 0U) {
174 }
175 if (internal_is_dot_component(&path[start], component_len)) {
177 }
178 if (c == '/') {
179 slash = i;
180 start = (uint16_t)(i + 1U);
181 }
182 continue;
183 }
184 if (c == ':') {
186 }
187 if (c == '\\') {
189 }
190 if ((uint8_t)c < 0x20U) {
192 }
193 }
194 *out_slash = slash;
195 return k_ra8_ok;
196}
197
215{
216 uint16_t len = 0U;
218 if (err != k_ra8_ok) {
219 return err;
220 }
221 uint16_t colon = 0U;
222 err = internal_mount_prefix(path, len, &colon);
223 if (err != k_ra8_ok) {
224 return err;
225 }
226 uint16_t slash = 0U;
227 err = internal_path_components(path, len, colon, &slash);
228 if (err != k_ra8_ok) {
229 return err;
230 }
231 out->length = len;
232 out->last_slash = slash;
233 return k_ra8_ok;
234}
235
253RA8_INTERNAL static ra8_err_t internal_stage_leaf_check(const char* leaf, uint16_t* out_len)
254{
256 if (err != k_ra8_ok) {
257 return err;
258 }
259 if (*out_len == 0U) {
261 }
262 if (internal_is_dot_component(leaf, *out_len)) {
264 }
265 for (uint16_t i = 0U; i < *out_len; ++i) {
266 const char c = leaf[i];
267 if (c == '/') {
269 }
270 if (c == '\\') {
272 }
273 if (c == ':') {
275 }
276 if ((uint8_t)c < 0x20U) {
278 }
279 }
280 return k_ra8_ok;
281}
282
296RA8_INTERNAL static void internal_copy(char* dst, const char* src, uint16_t len)
297{
298 for (uint16_t i = 0U; i < len; ++i) {
299 dst[i] = src[i];
300 }
301 dst[len] = '\0';
302}
303
319RA8_INTERNAL static bool internal_leaf_equals(const char* destination_leaf, const char* stage_leaf)
320{
321 for (uint16_t i = 0U; i < k_mdl_storage_vfs_stage_leaf_capacity; ++i) {
322 if (destination_leaf[i] != stage_leaf[i]) {
323 return false;
324 }
325 if (destination_leaf[i] == '\0') {
326 return true;
327 }
328 }
329 return false;
330}
331
350internal_build_stage(mdl_storage_vfs_t* ctx, const path_facts_t* facts, uint16_t stage_len)
351{
352 const uint32_t prefix = (uint32_t)facts->last_slash + 1U;
353 const uint32_t needed = prefix + (uint32_t)stage_len + 1U;
354 if (needed > (uint32_t)k_mdl_storage_vfs_path_capacity) {
356 }
357 const char* final_leaf = &ctx->destination[facts->last_slash + 1U];
358 if (internal_leaf_equals(final_leaf, ctx->stage_leaf)) {
360 }
361 for (uint32_t i = 0U; i < prefix; ++i) {
362 ctx->staging_path[i] = ctx->destination[i];
363 }
364 for (uint16_t i = 0U; i < stage_len; ++i) {
365 ctx->staging_path[prefix + i] = ctx->stage_leaf[i];
366 }
367 ctx->staging_path[prefix + stage_len] = '\0';
368 return k_ra8_ok;
369}
370
389{
390 const bool root_parent = ctx->destination[last_slash - 1U] == ':';
391 const uint16_t cut = root_parent ? (uint16_t)(last_slash + 1U) : last_slash;
392 const char saved = ctx->destination[cut];
393 ctx->destination[cut] = '\0';
394 ra8_io_vfs_stat_t stat = {};
395 const ra8_err_t err = ra8_io_vfs_stat(ctx->destination, &stat);
396 ctx->destination[cut] = saved;
397 if (err != k_ra8_ok) {
398 return err;
399 }
400 if (!stat.exists) {
401 return k_ra8_err_not_found;
402 }
403 if (!stat.is_directory) {
405 }
406 return k_ra8_ok;
407}
408
425RA8_INTERNAL static ra8_err_t internal_final_absent(const char* destination)
426{
427 ra8_io_vfs_stat_t stat = {};
428 const ra8_err_t err = ra8_io_vfs_stat(destination, &stat);
429 if (err != k_ra8_ok) {
430 return err;
431 }
432 if (!stat.exists) {
433 return k_ra8_ok;
434 }
435 if (stat.is_directory) {
437 }
438 return k_ra8_err_exists;
439}
440
456RA8_INTERNAL static ra8_err_t internal_remove_stale_stage(const char* staging_path)
457{
458 ra8_io_vfs_stat_t stat = {};
459 const ra8_err_t err = ra8_io_vfs_stat(staging_path, &stat);
460 if (err != k_ra8_ok) {
461 return err;
462 }
463 if (!stat.exists) {
464 return k_ra8_ok;
465 }
466 if (stat.is_directory) {
468 }
469 return ra8_io_vfs_unlink(staging_path);
470}
471
487{
488 if (ctx->file == nullptr) {
489 return k_ra8_ok;
490 }
491 ra8_io_vfs_file_t* const file = ctx->file;
492 ctx->file = nullptr;
494 ra8_err_t sync_err = ra8_io_vfs_file_sync(file);
495 if (sync_err == k_ra8_err_not_supported) {
496 sync_err = k_ra8_ok;
497 }
498 const ra8_err_t close_err = ra8_io_vfs_file_close(file);
499 return (sync_err == k_ra8_ok) ? close_err : sync_err;
500}
501
519 const path_facts_t* facts)
520{
521 uint16_t stage_len = 0U;
522 ra8_err_t err = internal_stage_leaf_check(ctx->stage_leaf, &stage_len);
523 if (err == k_ra8_ok) {
524 err = internal_build_stage(ctx, facts, stage_len);
525 }
526 if (err == k_ra8_ok) {
527 err = internal_parent_check(ctx, facts->last_slash);
528 }
529 if (err == k_ra8_ok) {
531 }
532 if (err == k_ra8_ok) {
534 }
535 return err;
536}
537
556RA8_INTERNAL static ra8_err_t internal_begin(void* opaque, const char* destination)
557{
558 if (opaque == nullptr) {
559 return k_ra8_err_null_ptr;
560 }
561 if (destination == nullptr) {
562 return k_ra8_err_null_ptr;
563 }
564 mdl_storage_vfs_t* const ctx = opaque;
565 if (ctx->state != k_mdl_storage_vfs_idle) {
568 }
569 }
571 path_facts_t facts = {};
572 ra8_err_t err = internal_path_facts(destination, &facts);
573 if (err != k_ra8_ok) {
574 return err;
575 }
576 internal_copy(ctx->destination, destination, facts.length);
577 err = internal_begin_prepare_stage(ctx, &facts);
578 if (err != k_ra8_ok) {
579 return err;
580 }
582 if (err == k_ra8_ok) {
583 ctx->bytes_written = 0U;
585 }
586 return err;
587}
588
610internal_write(void* opaque, const uint8_t* data, uint16_t len, uint16_t* written)
611{
612 if (opaque == nullptr) {
613 return k_ra8_err_null_ptr;
614 }
615 if (written == nullptr) {
616 return k_ra8_err_null_ptr;
617 }
618 *written = 0U;
619 if (data == nullptr) {
620 return k_ra8_err_null_ptr;
621 }
622 mdl_storage_vfs_t* const ctx = opaque;
623 if (ctx->state != k_mdl_storage_vfs_writing) {
625 }
626 if (ctx->file == nullptr) {
628 }
629 if (ctx->bytes_written > (UINT64_MAX - (uint64_t)len)) {
631 }
632 const ra8_err_t err = ra8_io_vfs_file_write(ctx->file, data, (uint32_t)len);
633 if (err == k_ra8_ok) {
634 ctx->bytes_written += (uint64_t)len;
635 *written = len;
636 }
637 return err;
638}
639
659 uint64_t expected)
660{
661 ra8_io_vfs_stat_t stat = {};
662 const ra8_err_t err = ra8_io_vfs_stat(ctx->staging_path, &stat);
663 if (err != k_ra8_ok) {
664 return err;
665 }
666 if (!stat.exists) {
667 return k_ra8_err_not_found;
668 }
669 if (stat.is_directory) {
671 }
672 if (stat.size_bytes != expected) {
674 }
675 return k_ra8_ok;
676}
677
699internal_validate(void* opaque, uint64_t total_bytes, const uint8_t sha256[k_ra8_mdl_sha256_bytes])
700{
701 if (opaque == nullptr) {
702 return k_ra8_err_null_ptr;
703 }
704 if (sha256 == nullptr) {
705 return k_ra8_err_null_ptr;
706 }
707 mdl_storage_vfs_t* const ctx = opaque;
708 if (ctx->state != k_mdl_storage_vfs_writing) {
710 }
711 if (total_bytes != ctx->bytes_written) {
713 }
715 if (err == k_ra8_ok) {
716 err = internal_staged_file_check(ctx, total_bytes);
717 }
718 if (err == k_ra8_ok) {
719 if (ctx->validate != nullptr) {
720 err = ctx->validate(ctx->validate_ctx, ctx->staging_path, total_bytes, sha256);
721 }
722 }
723 if (err == k_ra8_ok) {
725 }
726 return err;
727}
728
746{
747 if (opaque == nullptr) {
748 return k_ra8_err_null_ptr;
749 }
750 mdl_storage_vfs_t* const ctx = opaque;
752 return k_ra8_ok;
753 }
754 if (ctx->state != k_mdl_storage_vfs_ready) {
756 }
758 if (err == k_ra8_ok) {
760 }
761 if (err == k_ra8_ok) {
763 }
764 if (err == k_ra8_ok) {
766 }
767 return err;
768}
769
786{
787 ra8_io_vfs_stat_t stat = {};
788 const ra8_err_t stat_err = ra8_io_vfs_stat(ctx->staging_path, &stat);
789 if (stat_err != k_ra8_ok) {
790 return (first_err == k_ra8_ok) ? stat_err : first_err;
791 }
792 if (!stat.exists) {
794 return first_err;
795 }
796 if (stat.is_directory) {
797 return (first_err == k_ra8_ok) ? k_ra8_err_invalid_state : first_err;
798 }
799 const ra8_err_t unlink_err = ra8_io_vfs_unlink(ctx->staging_path);
800 if (unlink_err == k_ra8_ok) {
802 }
803 return (first_err == k_ra8_ok) ? unlink_err : first_err;
804}
805
822{
823 if (opaque == nullptr) {
824 return k_ra8_err_null_ptr;
825 }
826 mdl_storage_vfs_t* const ctx = opaque;
827 if (ctx->state == k_mdl_storage_vfs_idle) {
828 return k_ra8_ok;
829 }
831 return k_ra8_ok;
832 }
833 const ra8_err_t close_err = internal_close_writer(ctx);
834 return internal_abort_stage(ctx, close_err);
835}
836
838 const mdl_storage_vfs_config_t* config,
839 ra8_mdl_storage_iface_t* out_iface)
840{
841 if (storage == nullptr) {
842 return k_ra8_err_null_ptr;
843 }
844 if (config == nullptr) {
845 return k_ra8_err_null_ptr;
846 }
847 if (out_iface == nullptr) {
848 return k_ra8_err_null_ptr;
849 }
850 if (config->stage_leaf == nullptr) {
851 return k_ra8_err_null_ptr;
852 }
853 uint16_t stage_len = 0U;
854 const ra8_err_t err = internal_stage_leaf_check(config->stage_leaf, &stage_len);
855 if (err != k_ra8_ok) {
856 return err;
857 }
858 *storage = (mdl_storage_vfs_t){};
859 storage->validate = config->validate;
860 storage->validate_ctx = config->validate_ctx;
861 internal_copy(storage->stage_leaf, config->stage_leaf, stage_len);
862 out_iface->begin = internal_begin;
863 out_iface->write = internal_write;
864 out_iface->validate = internal_validate;
865 out_iface->commit = internal_commit;
866 out_iface->abort = internal_abort;
867 out_iface->ctx = storage;
868 return k_ra8_ok;
869}
static ra8_err_t internal_write(void *ctx, void *file_state, const uint8_t *src, uint32_t len, uint32_t *out_written)
static ra8_err_t internal_bounded_length(const char *text, uint16_t cap, uint16_t *out_len)
Measure one string without reading past a fixed capacity.
static ra8_err_t internal_write(void *opaque, const uint8_t *data, uint16_t len, uint16_t *written)
Coordinator callback: append one all-or-error bounded chunk.
static ra8_err_t internal_abort(void *opaque)
Coordinator callback: best-effort, retryable, idempotent cleanup.
static ra8_err_t internal_path_facts(const char *path, path_facts_t *out)
Prove a destination is a bounded canonical named-VFS file path.
static ra8_err_t internal_final_absent(const char *destination)
Refuse an existing final, distinguishing directories as bad paths.
static ra8_err_t internal_build_stage(mdl_storage_vfs_t *ctx, const path_facts_t *facts, uint16_t stage_len)
Build the caller-owned sibling staging path without truncation.
static ra8_err_t internal_stage_leaf_check(const char *leaf, uint16_t *out_len)
Validate the caller-reserved simple staging leaf.
static ra8_err_t internal_close_writer(mdl_storage_vfs_t *ctx)
Close the writer exactly once and retain staged cleanup state.
static ra8_err_t internal_abort_stage(mdl_storage_vfs_t *ctx, ra8_err_t first_err)
Finish cleanup after the writer, if any, has been closed.
static ra8_err_t internal_validate(void *opaque, uint64_t total_bytes, const uint8_t sha256[k_ra8_mdl_sha256_bytes])
Coordinator callback: close, size-check, and delegate validation.
static void internal_copy(char *dst, const char *src, uint16_t len)
Copy a bounded NUL-terminated byte string.
static ra8_err_t internal_remove_stale_stage(const char *staging_path)
Remove a stale owned regular stage, but never a directory.
static ra8_err_t internal_path_components(const char *path, uint16_t len, uint16_t colon, uint16_t *out_slash)
Validate canonical non-traversing components after mount:/.
static ra8_err_t internal_begin_prepare_stage(mdl_storage_vfs_t *ctx, const path_facts_t *facts)
Stage-preparation chain: leaf check, build, parent check, and cleanup.
static ra8_err_t internal_begin(void *opaque, const char *destination)
Coordinator callback: create the private sibling transaction file.
static ra8_err_t internal_commit(void *opaque)
Coordinator callback: one serialized no-replace same-mount publish.
static bool internal_leaf_equals(const char *destination_leaf, const char *stage_leaf)
Compare a destination leaf with the reserved staging leaf.
static ra8_err_t internal_staged_file_check(const mdl_storage_vfs_t *ctx, uint64_t expected)
Check the closed stage is still a regular file of the exact size.
static ra8_err_t internal_parent_check(mdl_storage_vfs_t *ctx, uint16_t last_slash)
Require the destination parent to exist and be a directory.
static bool internal_is_dot_component(const char *text, uint16_t len)
Return whether one component is exactly "." or "..".
ra8_err_t mdl_storage_vfs_init(mdl_storage_vfs_t *storage, const mdl_storage_vfs_config_t *config, ra8_mdl_storage_iface_t *out_iface)
Initialise one VFS storage adapter and bind its coordinator interface.
static ra8_err_t internal_mount_prefix(const char *path, uint16_t len, uint16_t *out_colon)
Validate the mount prefix and locate its colon.
No-heap VFS storage binding for the media-download coordinator.
@ k_mdl_storage_vfs_path_capacity
VFS path incl NUL.
@ k_mdl_storage_vfs_stage_leaf_capacity
Stage leaf incl NUL.
@ k_mdl_storage_vfs_ready
Validated and ready to rename.
@ k_mdl_storage_vfs_committed
Destination was published.
@ k_mdl_storage_vfs_idle
No owned private object.
@ k_mdl_storage_vfs_staged
Writer closed; not validated.
@ k_mdl_storage_vfs_writing
Private writer is open.
Annotation-attribute framework macros for ra8-firmware.
#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_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_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ 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
static void internal_abort(ra8_fmt_transaction_t *transaction)
Abort an active transaction after any pre-publication error.
@ k_ra8_fs_mode_write
Truncate (or create) for writing.
static ra8_err_t internal_validate(const ra8_io_blockdev_t *bd)
Reject a handle that is NULL or has no backend bound.
ra8_io virtual filesystem – mount many volumes, address them by name.
ra8_err_t ra8_io_vfs_file_close(ra8_io_vfs_file_t *file)
Close and always release one format-neutral stream facade.
Definition ra8_io_vfs.c:625
@ k_ra8_io_vfs_name_max
Mount name length incl NUL.
Definition ra8_io_vfs.h:58
ra8_err_t ra8_io_vfs_file_sync(ra8_io_vfs_file_t *file)
Explicitly sync a stream, or return not-supported when unavailable.
Definition ra8_io_vfs.c:696
ra8_err_t ra8_io_vfs_unlink(const char *path)
Delete a file by "name:/path".
ra8_err_t ra8_io_vfs_file_open(const char *path, ra8_fs_mode_t mode, ra8_io_vfs_file_t **out_file)
Open a format-neutral stream through a mounted format's ops.
Definition ra8_io_vfs.c:598
ra8_err_t ra8_io_vfs_stat(const char *path, ra8_io_vfs_stat_t *out)
Query metadata for "name:/path".
ra8_err_t ra8_io_vfs_rename(const char *old_path, const char *new_path)
Rename a file within one mount.
ra8_err_t ra8_io_vfs_file_write(ra8_io_vfs_file_t *file, const void *buf, uint32_t bytes)
Write bytes, or return not-supported when capability-gated.
Definition ra8_io_vfs.c:648
@ k_ra8_mdl_sha256_bytes
SHA-256 digest size in bytes.
Immutable policy copied or retained by an adapter instance.
void * validate_ctx
Validator context.
mdl_storage_vfs_validate_fn validate
Optional artifact validator.
const char * stage_leaf
Reserved sibling staging leaf.
Caller-owned VFS transaction context.
uint64_t bytes_written
Successful appended bytes.
mdl_storage_vfs_validate_fn validate
Optional delegated check.
void * validate_ctx
Delegated check context.
char destination[k_mdl_storage_vfs_path_capacity]
Final path.
char staging_path[k_mdl_storage_vfs_path_capacity]
Private path.
mdl_storage_vfs_state_t state
Transaction lifecycle.
ra8_io_vfs_file_t * file
Open private writer.
char stage_leaf[k_mdl_storage_vfs_stage_leaf_capacity]
Owned leaf.
Module-local path facts proven before any filesystem mutation.
uint16_t last_slash
Final component separator.
uint16_t length
Bytes before NUL.
Opaque, statically-pooled format-neutral file stream.
Metadata returned by ra8_io_vfs_stat.
Definition ra8_io_vfs.h:96
uint64_t size_bytes
File size in bytes (0 for directories).
Definition ra8_io_vfs.h:97
bool is_directory
true => path names a directory.
Definition ra8_io_vfs.h:102
bool exists
true => the path resolves to an entry.
Definition ra8_io_vfs.h:103
ra8_err_t(* write)(void *ctx, const uint8_t *data, uint16_t len, uint16_t *written)
Append len bytes and report the exact number persisted.
ra8_err_t(* commit)(void *ctx)
Atomically publish the complete temporary object.
void * ctx
Backend context passed to every function.
ra8_err_t(* begin)(void *ctx, const char *destination)
Create private temporary state for destination.
ra8_err_t(* validate)(void *ctx, uint64_t total_bytes, const uint8_t sha256[k_ra8_mdl_sha256_bytes])
Validate artifact identity and structure before publication.
ra8_err_t(* abort)(void *ctx)
Destroy temporary state; valid after every successful begin.