ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
fw_if_fs_ra8_vfs.c
Go to the documentation of this file.
1
18
19#include "fw_if_fs_ra8_vfs.h"
20
21#include <stddef.h>
22#include <stdint.h>
23#include <string.h>
24
25#include "fw_if_fs.h"
26#include "fw_if_fs_backend.h"
28#include "ra8_attributes.h"
29#include "ra8_err.h"
30#include "ra8_fs.h"
31#include "ra8_fs_meta.h"
32#include "ra8_io_vfs.h"
33
43
50
52typedef enum : uint32_t {
56
61
66
76
86
88
89/* see header for full description */
91{
92 fw_fs_timestamp_t portable = {};
93 if (native->valid) {
94 portable.value.nanosecond =
96 portable.value.year = native->value.year;
97 portable.value.utc_offset_min = native->value.utc_offset_min;
98 portable.value.month = native->value.month;
99 portable.value.day = native->value.day;
100 portable.value.hour = native->value.hour;
101 portable.value.minute = native->value.minute;
102 portable.value.second = native->value.second;
103 portable.valid = true;
104 portable.utc_offset_valid = native->utc_offset_valid;
105 }
106 return portable;
107}
108
109/* see header for full description */
110RA8_INTERNAL static uint16_t internal_len(const char* text, uint16_t cap)
111{
112 uint16_t length = 0U;
113 while (length < cap) {
114 if (text[length] == '\0') {
115 break;
116 }
117 ++length;
118 }
119 return length;
120}
121
122/* see header for full description */
124internal_full_path(fw_fs_ra8_vfs_state_t* state, const char* path, char* out)
125{
126 const uint16_t mount_len = internal_len(state->mount_name, k_ra8_io_vfs_name_max);
127 const uint16_t path_len = internal_len(path, (uint16_t)k_fw_fs_path_cap);
128 if (mount_len >= (uint16_t)k_ra8_io_vfs_name_max) {
130 }
131 if (path_len >= (uint16_t)k_fw_fs_path_cap) {
133 }
134 uint16_t cursor = 0U;
135 for (uint16_t i = 0U; i < mount_len; ++i) {
136 out[cursor] = state->mount_name[i];
137 ++cursor;
138 }
139 out[cursor] = ':';
140 ++cursor;
141 for (uint16_t i = 0U; i <= path_len; ++i) {
142 out[cursor] = path[i];
143 ++cursor;
144 }
145 return k_ra8_ok;
146}
147
148/* see header for full description */
149RA8_INTERNAL static ra8_err_t internal_stat(void* ctx, const char* path, fw_fs_stat_t* out)
150{
152 const ra8_err_t built = internal_full_path(state, path, state->path_a);
153 if (built != k_ra8_ok) {
154 return built;
155 }
156 ra8_io_vfs_stat_t native = {};
157 const ra8_err_t result = ra8_io_vfs_stat(state->path_a, &native);
158 if (result != k_ra8_ok) {
159 return result;
160 }
161 out->exists = native.exists;
162 out->size_bytes = native.size_bytes;
163 out->created = internal_timestamp(&native.created);
164 out->modified = internal_timestamp(&native.modified);
165 out->accessed = internal_timestamp(&native.accessed);
166 out->type = k_fw_fs_node_none;
167 if (native.exists) {
169 }
170 return k_ra8_ok;
171}
172
174typedef struct {
175 void* cursor;
176 void* workspace;
177 size_t cursor_end;
178 size_t consumed;
180
203 uint8_t native_align)
204{
205 const uintptr_t base = (uintptr_t)directory_state;
206 const uintptr_t cursor_align = (uintptr_t)alignof(vfs_directory_state_t);
207 const uintptr_t cursor = (base + cursor_align - 1U) & ~(cursor_align - 1U);
208 const uintptr_t cursor_end = cursor + (uintptr_t)sizeof(vfs_directory_state_t);
209 const uintptr_t align = (uintptr_t)native_align;
210 const uintptr_t native = (cursor_end + align - 1U) & ~(align - 1U);
211 return (vfs_dir_layout_t){.cursor = (void*)cursor,
212 .workspace = (void*)native,
213 .cursor_end = (size_t)(cursor_end - base),
214 .consumed = (size_t)(native - base)};
215}
216
233{
234 return internal_dir_layout(directory_state, (uint8_t)alignof(vfs_directory_state_t)).cursor;
235}
236
237/* see header for full description */
239internal_dir_open(void* ctx, const char* path, void* directory_state, uint32_t state_bytes)
240{
242 const vfs_dir_layout_t layout =
243 internal_dir_layout(directory_state, state->directory_workspace_align);
244 if ((uint64_t)state_bytes < (uint64_t)layout.cursor_end) {
245 return k_ra8_err_no_mem;
246 }
247 vfs_directory_state_t* directory = layout.cursor;
248 *directory = (vfs_directory_state_t){};
249 const ra8_err_t built = internal_full_path(state, path, state->path_a);
250 if (built != k_ra8_ok) {
251 return built;
252 }
253 if (layout.consumed > (size_t)state_bytes) {
254 return k_ra8_err_no_mem;
255 }
256 if ((uint64_t)state->directory_workspace_bytes > ((uint64_t)state_bytes - layout.consumed)) {
257 return k_ra8_err_no_mem;
258 }
259 return ra8_io_vfs_dir_open(state->path_a,
260 &directory->native,
261 layout.workspace,
262 state_bytes - (uint32_t)layout.consumed);
263}
264
265/* see header for full description */
267internal_dir_next(void* ctx, void* directory_state, fw_fs_dirent_value_t* out, bool* out_entry)
268{
269 (void)ctx;
270 vfs_directory_state_t* directory = internal_dir_cursor(directory_state);
271 ra8_fs_dirent_t native = {};
272 const ra8_err_t err = ra8_io_vfs_dir_next(&directory->native, &native, out_entry);
273 if (err != k_ra8_ok) {
274 return err;
275 }
276 if (!*out_entry) {
277 return err;
278 }
279 const uint16_t length = internal_len(native.name, (uint16_t)k_fw_fs_path_cap);
280 if (length >= (uint16_t)k_fw_fs_path_cap) {
282 }
283 (void)memcpy(out->name, native.name, (size_t)length + 1U);
284 out->name_bytes = length;
285 out->size_bytes = native.size_bytes;
286 out->type = ((native.attr & (uint8_t)k_ra8_fs_attr_directory) != 0U) ? k_fw_fs_node_directory
288 return k_ra8_ok;
289}
290
291/* see header for full description */
292RA8_INTERNAL static ra8_err_t internal_dir_close(void* ctx, void* directory_state)
293{
294 (void)ctx;
295 vfs_directory_state_t* directory = internal_dir_cursor(directory_state);
296 return ra8_io_vfs_dir_close(&directory->native);
297}
298
314RA8_INTERNAL static void
315internal_list_entry(const char* name, uint8_t attr, uint64_t size, void* ctx)
316{
317 vfs_list_state_t* state = (vfs_list_state_t*)ctx;
318 if (state->stopped) {
319 return;
320 }
321 if (state->count >= state->max_entries) {
322 state->stopped = true;
323 return;
324 }
325 const uint16_t length = internal_len(name, (uint16_t)k_fw_fs_path_cap);
326 if (length >= (uint16_t)k_fw_fs_path_cap) {
328 state->stopped = true;
329 return;
330 }
331 const fw_fs_dirent_t entry = {
332 .name = name,
333 .size_bytes = size,
334 .name_bytes = length,
335 .type = ((attr & (uint8_t)k_ra8_fs_attr_directory) != 0U) ? k_fw_fs_node_directory
337 };
338 bool keep_going = true;
339 state->callback_error = state->callback(state->callback_ctx, &entry, &keep_going);
340 ++state->count;
341 state->stopped = state->callback_error != k_ra8_ok;
342 if (!keep_going) {
343 state->stopped = true;
344 }
345}
346
347/* see header for full description */
349 const char* path,
350 uint32_t max_entries,
351 fw_fs_list_fn_t callback,
352 void* callback_ctx,
353 uint32_t* out_count,
354 bool* out_complete)
355{
357 const ra8_err_t built = internal_full_path(state, path, state->path_a);
358 if (built != k_ra8_ok) {
359 return built;
360 }
361 vfs_list_state_t bridge = {.callback = callback,
362 .callback_ctx = callback_ctx,
363 .max_entries = max_entries,
364 .callback_error = k_ra8_ok};
365 const ra8_err_t listed = ra8_io_vfs_listdir(state->path_a, internal_list_entry, &bridge);
366 *out_count = bridge.count;
367 *out_complete = !bridge.stopped;
368 return (bridge.callback_error == k_ra8_ok) ? listed : bridge.callback_error;
369}
370
373internal_path_op(void* ctx, const char* path, ra8_err_t (*operation)(const char*))
374{
376 const ra8_err_t built = internal_full_path(state, path, state->path_a);
377 if (built != k_ra8_ok) {
378 return built;
379 }
380 return operation(state->path_a);
381}
382
383/* see header for full description */
384RA8_INTERNAL static ra8_err_t internal_mkdir(void* ctx, const char* path)
385{
386 return internal_path_op(ctx, path, ra8_io_vfs_mkdir);
387}
388
389/* see header for full description */
390RA8_INTERNAL static ra8_err_t internal_unlink(void* ctx, const char* path)
391{
392 return internal_path_op(ctx, path, ra8_io_vfs_unlink);
393}
394
395/* see header for full description */
396RA8_INTERNAL static ra8_err_t internal_rmdir(void* ctx, const char* path)
397{
398 return internal_path_op(ctx, path, ra8_io_vfs_rmdir);
399}
400
401/* see header for full description */
403internal_rename(void* ctx, const char* old_path, const char* new_path, bool replace)
404{
405 if (replace) {
407 }
409 const ra8_err_t old_built = internal_full_path(state, old_path, state->path_a);
410 if (old_built != k_ra8_ok) {
411 return old_built;
412 }
413 const ra8_err_t new_built = internal_full_path(state, new_path, state->path_b);
414 if (new_built != k_ra8_ok) {
415 return new_built;
416 }
417 return ra8_io_vfs_rename(state->path_a, state->path_b);
418}
419
420/* see header for full description */
422{
424 ra8_fs_space_t native = {};
425 const ra8_err_t result = ra8_io_vfs_free_space(state->mount_name, &native);
426 if (result != k_ra8_ok) {
427 return result;
428 }
429 out->total_bytes = native.total_bytes;
430 out->free_bytes = native.free_bytes;
431 out->used_bytes = native.used_bytes;
432 return k_ra8_ok;
433}
434
435/* see header for full description */
437{
438 if (mode == k_fw_fs_open_read) {
439 *out = k_ra8_fs_mode_read;
440 return k_ra8_ok;
441 }
442 if (mode == k_fw_fs_open_write_truncate) {
443 *out = k_ra8_fs_mode_write;
444 return k_ra8_ok;
445 }
446 if (mode == k_fw_fs_open_append) {
448 return k_ra8_ok;
449 }
451}
452
453/* see header for full description */
455 const char* path,
457 void* file_state,
458 uint32_t state_bytes)
459{
460 if (state_bytes < sizeof(vfs_file_state_t)) {
461 return k_ra8_err_no_mem;
462 }
463 ra8_fs_mode_t native_mode = k_ra8_fs_mode_read;
464 const ra8_err_t mode_err = internal_mode(mode, &native_mode);
465 if (mode_err != k_ra8_ok) {
466 return mode_err;
467 }
469 const ra8_err_t built = internal_full_path(state, path, state->path_a);
470 if (built != k_ra8_ok) {
471 return built;
472 }
473 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
474 file->native = nullptr;
475 return ra8_io_vfs_open(state->path_a, native_mode, &file->native);
476}
477
478/* see header for full description */
480internal_read(void* ctx, void* file_state, uint8_t* dst, uint32_t cap, uint32_t* out_read)
481{
482 (void)ctx;
483 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
484 return ra8_fs_read(file->native, dst, cap, out_read);
485}
486
487/* see header for full description */
489internal_write(void* ctx, void* file_state, const uint8_t* src, uint32_t len, uint32_t* out_written)
490{
491 (void)ctx;
492 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
493 const ra8_err_t result = ra8_fs_write(file->native, src, len);
494 if (result == k_ra8_ok) {
495 *out_written = len;
496 }
497 return result;
498}
499
500/* see header for full description */
501RA8_INTERNAL static ra8_err_t internal_seek(void* ctx, void* file_state, uint64_t offset)
502{
503 (void)ctx;
504 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
505 return ra8_fs_seek(file->native, offset);
506}
507
508/* see header for full description */
509RA8_INTERNAL static ra8_err_t internal_tell(void* ctx, void* file_state, uint64_t* out_offset)
510{
511 (void)ctx;
512 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
513 return ra8_fs_tell(file->native, out_offset);
514}
515
516/* see header for full description */
517RA8_INTERNAL static ra8_err_t internal_size(void* ctx, void* file_state, uint64_t* out_size)
518{
519 (void)ctx;
520 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
521 return ra8_fs_size(file->native, out_size);
522}
523
524/* see header for full description */
525RA8_INTERNAL static ra8_err_t internal_close(void* ctx, void* file_state)
526{
527 (void)ctx;
528 vfs_file_state_t* file = (vfs_file_state_t*)file_state;
529 const ra8_err_t result = ra8_fs_close(file->native);
530 file->native = nullptr;
531 return result;
532}
533
534/* see header for full description */
535RA8_INTERNAL static ra8_err_t internal_copy_path(char* out, const char* path)
536{
537 for (uint16_t i = 0U; i < (uint16_t)k_fw_fs_path_cap; ++i) {
538 out[i] = path[i];
539 if (path[i] == '\0') {
540 return k_ra8_ok;
541 }
542 }
544}
545
559RA8_INTERNAL static void internal_hex6(char out[k_vfs_stage_hex_digits], uint32_t value)
560{
561 static const char digits[] = "0123456789ABCDEF";
562 for (uint8_t i = 0U; i < (uint8_t)k_vfs_stage_hex_digits; ++i) {
563 const uint8_t shift =
564 (uint8_t)(((uint8_t)k_vfs_hex_last_digit - i) * (uint8_t)k_vfs_hex_nibble_bits);
565 out[i] = digits[(value >> shift) & (uint32_t)k_vfs_hex_nibble_mask];
566 }
567}
568
569/* see header for full description */
570RA8_INTERNAL static ra8_err_t internal_stage_path(const char* destination, uint32_t id, char* out)
571{
572 uint16_t last_slash = 0U;
573 uint16_t length = 0U;
574 while (length < (uint16_t)k_fw_fs_path_cap) {
575 const char value = destination[length];
576 if (value == '\0') {
577 break;
578 }
579 if (value == '/') {
580 last_slash = length;
581 }
582 ++length;
583 }
584 if (length >= (uint16_t)k_fw_fs_path_cap) {
586 }
587 const uint16_t stage_length = (uint16_t)(last_slash + 1U + k_vfs_stage_leaf_bytes);
588 if (stage_length >= (uint16_t)k_fw_fs_path_cap) {
590 }
591 for (uint16_t i = 0U; i <= last_slash; ++i) {
592 out[i] = destination[i];
593 }
594 uint16_t cursor = (uint16_t)(last_slash + 1U);
595 out[cursor++] = 'T';
596 out[cursor++] = 'X';
597 internal_hex6(&out[cursor], id & k_vfs_transaction_id_mask);
598 cursor = (uint16_t)(cursor + k_vfs_stage_hex_digits);
599 out[cursor++] = '.';
600 out[cursor++] = 'T';
601 out[cursor++] = 'M';
602 out[cursor++] = 'P';
603 out[cursor] = '\0';
604 return k_ra8_ok;
605}
606
626{
627 for (uint8_t attempt = 0U; attempt < (uint8_t)k_vfs_stage_attempts; ++attempt) {
628 ++state->transaction_id;
629 const ra8_err_t named =
631 if (named != k_ra8_ok) {
632 return named;
633 }
634 fw_fs_stat_t stage_stat = {};
635 const ra8_err_t stated = internal_stat(state, txn->stage, &stage_stat);
636 if (stated != k_ra8_ok) {
637 return stated;
638 }
639 if (stage_stat.exists) {
640 continue;
641 }
642 const ra8_err_t opened = internal_open(state,
643 txn->stage,
645 &txn->file_state,
646 sizeof(txn->file_state));
647 if (opened == k_ra8_ok) {
648 txn->writer_open = true;
649 txn->stage_exists = true;
650 return k_ra8_ok;
651 }
652 if (opened != k_ra8_err_exists) {
653 return opened;
654 }
655 }
656 return k_ra8_err_no_mem;
657}
658
659/* see header for full description */
661 void* transaction_state,
662 uint32_t state_bytes,
663 const char* destination,
665{
666 if (state_bytes < sizeof(vfs_transaction_state_t)) {
667 return k_ra8_err_no_mem;
668 }
669 if (policy != k_fw_fs_txn_create_new) {
671 }
673 fw_fs_stat_t destination_stat = {};
674 const ra8_err_t stated = internal_stat(state, destination, &destination_stat);
675 if (stated != k_ra8_ok) {
676 return stated;
677 }
678 if (destination_stat.exists) {
679 return k_ra8_err_exists;
680 }
681 vfs_transaction_state_t* txn = (vfs_transaction_state_t*)transaction_state;
682 (void)memset(txn, 0, sizeof(*txn));
683 txn->policy = policy;
684 const ra8_err_t copied = internal_copy_path(txn->destination, destination);
685 if (copied != k_ra8_ok) {
686 return copied;
687 }
688 return internal_stage_open(state, txn);
689}
690
691/* see header for full description */
693 void* transaction_state,
694 const uint8_t* src,
695 uint32_t len,
696 uint32_t* out_written)
697{
698 vfs_transaction_state_t* txn = (vfs_transaction_state_t*)transaction_state;
699 if (!txn->writer_open) {
701 }
702 return internal_write(ctx, &txn->file_state, src, len, out_written);
703}
704
705/* see header for full description */
706RA8_INTERNAL static ra8_err_t internal_txn_seek(void* ctx, void* transaction_state, uint64_t offset)
707{
708 vfs_transaction_state_t* txn = (vfs_transaction_state_t*)transaction_state;
709 if (!txn->writer_open) {
711 }
712 uint64_t size = 0U;
713 const ra8_err_t sized = internal_size(ctx, &txn->file_state, &size);
714 if (sized != k_ra8_ok) {
715 return sized;
716 }
717 if (offset > size) {
719 }
720 return internal_seek(ctx, &txn->file_state, offset);
721}
722
723/* see header for full description */
725 void* transaction_state,
726 fw_fs_validate_fn_t validator,
727 void* validator_ctx)
728{
729 vfs_transaction_state_t* txn = (vfs_transaction_state_t*)transaction_state;
730 if (!txn->writer_open) {
732 }
733 const ra8_err_t closed = internal_close(ctx, &txn->file_state);
734 txn->writer_open = false;
735 if (closed != k_ra8_ok) {
736 return closed;
737 }
738 const ra8_err_t opened =
739 internal_open(ctx, txn->stage, k_fw_fs_open_read, &txn->file_state, sizeof(txn->file_state));
740 if (opened != k_ra8_ok) {
741 return opened;
742 }
743 fw_fs_file_t staged = {
744 .iface = &s_stream_iface,
745 .ctx = ctx,
746 .state = &txn->file_state,
747 .state_bytes = sizeof(txn->file_state),
748 .is_open = true,
749 };
750 const ra8_err_t checked = validator(validator_ctx, &staged);
751 const ra8_err_t shut = fw_fs_close(&staged);
752 if (checked != k_ra8_ok) {
753 return checked;
754 }
755 return shut;
756}
757
758/* see header for full description */
760internal_txn_commit(void* ctx, void* transaction_state, bool* out_published)
761{
762 vfs_transaction_state_t* txn = (vfs_transaction_state_t*)transaction_state;
763 if (txn->writer_open) {
765 }
766 const ra8_err_t renamed = internal_rename(ctx, txn->stage, txn->destination, false);
767 if (renamed == k_ra8_ok) {
768 txn->stage_exists = false;
769 *out_published = true;
770 }
771 return renamed;
772}
773
774/* see header for full description */
775RA8_INTERNAL static ra8_err_t internal_txn_abort(void* ctx, void* transaction_state)
776{
777 vfs_transaction_state_t* txn = (vfs_transaction_state_t*)transaction_state;
778 ra8_err_t first = k_ra8_ok;
779 if (txn->writer_open) {
780 first = internal_close(ctx, &txn->file_state);
781 txn->writer_open = false;
782 }
783 if (txn->stage_exists) {
784 const ra8_err_t removed = internal_unlink(ctx, txn->stage);
785 if (first == k_ra8_ok) {
786 first = removed;
787 }
788 if (removed == k_ra8_ok) {
789 txn->stage_exists = false;
790 }
791 }
792 return first;
793}
794
797 .stat = internal_stat,
798 .listdir = internal_listdir,
799 .dir_open = internal_dir_open,
800 .dir_next = internal_dir_next,
801 .dir_close = internal_dir_close,
802 .mkdir = internal_mkdir,
803 .unlink = internal_unlink,
804 .rmdir = internal_rmdir,
805 .rename = internal_rename,
806 .space = internal_space,
807};
808
811 .open = internal_open,
812 .read = internal_read,
813 .write = internal_write,
814 .seek = internal_seek,
815 .tell = internal_tell,
816 .size = internal_size,
817 .sync = nullptr,
818 .close = internal_close,
819};
820
823 .begin = internal_txn_begin,
824 .write = internal_txn_write,
825 .seek = internal_txn_seek,
826 .validate = internal_txn_validate,
827 .commit = internal_txn_commit,
828 .abort = internal_txn_abort,
829};
830
831/* see header for full description */
833{
834 uint16_t length = 0U;
835 while (length < (uint16_t)k_ra8_io_vfs_name_max) {
836 const char value = name[length];
837 if (value == '\0') {
838 break;
839 }
840 if (value == ':') {
842 }
843 if (value == '/') {
845 }
846 ++length;
847 }
848 if (length == 0U) {
850 }
851 if (length >= (uint16_t)k_ra8_io_vfs_name_max) {
853 }
854 for (uint16_t i = 0U; i <= length; ++i) {
855 state->mount_name[i] = name[i];
856 }
857 return k_ra8_ok;
858}
859
879 uint32_t* out_bytes,
880 uint8_t* out_align,
881 uint16_t* out_max_open)
882{
883 ra8_err_t err = internal_full_path(state, "/", state->path_a);
884 if (err != k_ra8_ok) {
885 return err;
886 }
887 uint32_t native_bytes = 0U;
888 uint8_t native_align = 0U;
889 err = ra8_io_vfs_dir_requirements(state->path_a, &native_bytes, &native_align, out_max_open);
890 if (err != k_ra8_ok) {
891 return err;
892 }
893 const uint8_t outer_align = (native_align > (uint8_t)_Alignof(vfs_directory_state_t))
894 ? native_align
895 : (uint8_t)_Alignof(vfs_directory_state_t);
896 const uint64_t total =
897 (uint64_t)sizeof(vfs_directory_state_t) + (uint64_t)native_align - 1U + (uint64_t)native_bytes;
898 if (total > (uint64_t)UINT32_MAX) {
900 }
901 state->directory_workspace_bytes = native_bytes;
902 state->directory_workspace_align = native_align;
903 state->max_open_directories = *out_max_open;
904 *out_bytes = (uint32_t)total;
905 *out_align = outer_align;
906 return k_ra8_ok;
907}
908
927 uint32_t directory_bytes,
928 uint8_t directory_alignment,
929 uint16_t max_directories)
930{
931 fw_fs_caps_t caps = {
932 .max_file_bytes = (cfg->mount->type == k_ra8_fs_type_exfat)
933 ? UINT64_MAX
934 : (uint64_t)k_ra8_fs_fat_max_file_bytes,
935 .flags = (uint32_t)k_fw_fs_cap_namespace | (uint32_t)k_fw_fs_cap_stream |
939 .file_workspace_bytes = sizeof(vfs_file_state_t),
940 .directory_workspace_bytes = directory_bytes,
941 .transaction_workspace_bytes = sizeof(vfs_transaction_state_t),
942 .path_max_bytes = (uint16_t)k_fw_fs_path_cap,
943 .name_max_bytes = (cfg->mount->type == k_ra8_fs_type_exfat) ? k_vfs_exfat_name_max_bytes
945 .max_open_files = (uint16_t)k_ra8_fs_max_files,
946 .max_open_directories = max_directories,
947 .file_workspace_align = (uint8_t)_Alignof(vfs_file_state_t),
948 .directory_workspace_align = directory_alignment,
949 .transaction_workspace_align = (uint8_t)_Alignof(vfs_transaction_state_t),
950 };
951 caps.flags |= (uint32_t)k_fw_fs_cap_created_time | (uint32_t)k_fw_fs_cap_modified_time |
953 if (cfg->removable_media) {
954 caps.flags |= (uint32_t)k_fw_fs_cap_removable_media;
955 }
956 return caps;
957}
958
961{
962 if (out == nullptr) {
963 return k_ra8_err_null_ptr;
964 }
965 if (state == nullptr) {
966 return k_ra8_err_null_ptr;
967 }
968 if (cfg == nullptr) {
969 return k_ra8_err_null_ptr;
970 }
971 if (cfg->mount_name == nullptr) {
972 return k_ra8_err_null_ptr;
973 }
974 if (cfg->mount == nullptr) {
975 return k_ra8_err_null_ptr;
976 }
977 if (cfg->mount->in_use == 0U) {
979 }
980 (void)memset(state, 0, sizeof(*state));
981 const ra8_err_t named = internal_mount_name(state, cfg->mount_name);
982 if (named != k_ra8_ok) {
983 return named;
984 }
985 state->mount = cfg->mount;
986 state->removable_media = cfg->removable_media;
987 uint32_t directory_bytes = 0U;
988 uint16_t max_directories = 0U;
989 uint8_t directory_alignment = 0U;
990 const ra8_err_t directory_requirements =
991 internal_dir_requirements(state, &directory_bytes, &directory_alignment, &max_directories);
992 if (directory_requirements != k_ra8_ok) {
993 return directory_requirements;
994 }
995 const fw_fs_caps_t caps =
996 internal_capabilities(cfg, directory_bytes, directory_alignment, max_directories);
997 return fw_fs_bind(out, &s_namespace_iface, &s_stream_iface, &s_transaction_iface, state, &caps);
998}
static uint32_t internal_read(void)
Read current GPT counter ticks, or return UINT32_MAX on err.
Definition main.c:150
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
static ra8_err_t internal_stage_path(const char *destination, uint32_t id, char *out)
static void internal_hex6(char out[k_vfs_stage_hex_digits], uint32_t value)
Render a fixed-width six-digit hexadecimal transaction id.
vfs_numeric_limits_t
Timestamp and transaction arithmetic constants.
@ k_vfs_transaction_id_mask
Six hexadecimal digits.
@ k_vfs_nanoseconds_per_centisecond
Nanoseconds in 0.01 s.
ra8_err_t fw_fs_ra8_vfs_init(fw_fs_t *out, fw_fs_ra8_vfs_state_t *state, const fw_fs_ra8_vfs_cfg_t *cfg)
Bind a live named VFS mount into the portable filesystem facade.
static ra8_err_t internal_txn_commit(void *ctx, void *transaction_state, bool *out_published)
static void internal_list_entry(const char *name, uint8_t attr, uint64_t size, void *ctx)
Translate one format-list callback into the legacy bounded facade.
static ra8_err_t internal_seek(void *ctx, void *file_state, uint64_t offset)
vfs_path_limits_t
Portable-path limits exposed by the FAT/exFAT adapter.
@ k_vfs_fat_name_max_bytes
FAT LFN component byte cap.
@ k_vfs_stage_leaf_bytes
TX + six hex digits + .TMP.
@ k_vfs_exfat_name_max_bytes
exFAT component byte cap.
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 vfs_directory_state_t * internal_dir_cursor(void *directory_state)
Round a caller directory-state span up to the cursor's alignment.
vfs_stage_limits_t
Bounded attempts to find an unused short staging name.
@ k_vfs_stage_hex_digits
Hex digits in a stage identifier.
@ k_vfs_hex_last_digit
Highest valid index in a stage identifier.
@ k_vfs_hex_nibble_bits
Bits represented by one hex digit.
@ k_vfs_stage_attempts
Collision-search cap.
@ k_vfs_hex_nibble_mask
Low-nibble mask.
static ra8_err_t internal_full_path(fw_fs_ra8_vfs_state_t *state, const char *path, char *out)
static ra8_err_t internal_stage_open(fw_fs_ra8_vfs_state_t *state, vfs_transaction_state_t *txn)
Open an unused VFS staging file after a bounded collision search.
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_write(void *ctx, void *file_state, const uint8_t *src, uint32_t len, uint32_t *out_written)
static ra8_err_t internal_dir_close(void *ctx, void *directory_state)
static ra8_err_t internal_read(void *ctx, void *file_state, uint8_t *dst, uint32_t cap, uint32_t *out_read)
static uint16_t internal_len(const char *text, uint16_t cap)
static ra8_err_t internal_close(void *ctx, void *file_state)
static ra8_err_t internal_path_op(void *ctx, const char *path, ra8_err_t(*operation)(const char *))
One-path VFS dispatch helper.
static vfs_dir_layout_t internal_dir_layout(void *directory_state, uint8_t native_align)
Derive both aligned bases inside a caller directory-state span.
static ra8_err_t internal_mkdir(void *ctx, const char *path)
static ra8_err_t internal_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)
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 const fw_fs_stream_iface_t s_stream_iface
Immutable firmware stream vtable.
static ra8_err_t internal_txn_seek(void *ctx, void *transaction_state, uint64_t offset)
static ra8_err_t internal_copy_path(char *out, const char *path)
static fw_fs_caps_t internal_capabilities(const fw_fs_ra8_vfs_cfg_t *cfg, uint32_t directory_bytes, uint8_t directory_alignment, uint16_t max_directories)
Compose the portable capability record for one mounted VFS format.
static ra8_err_t internal_rmdir(void *ctx, const char *path)
static ra8_err_t internal_mount_name(fw_fs_ra8_vfs_state_t *state, const char *name)
static ra8_err_t internal_size(void *ctx, void *file_state, uint64_t *out_size)
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_mode(fw_fs_open_mode_t mode, ra8_fs_mode_t *out)
static ra8_err_t internal_tell(void *ctx, void *file_state, uint64_t *out_offset)
static fw_fs_timestamp_t internal_timestamp(const ra8_fs_timestamp_t *native)
static ra8_err_t internal_unlink(void *ctx, const char *path)
static ra8_err_t internal_dir_next(void *ctx, void *directory_state, fw_fs_dirent_value_t *out, bool *out_entry)
static ra8_err_t internal_dir_requirements(fw_fs_ra8_vfs_state_t *state, uint32_t *out_bytes, uint8_t *out_align, uint16_t *out_max_open)
Query the named format and compose aligned adapter cursor requirements.
static ra8_err_t internal_dir_open(void *ctx, const char *path, void *directory_state, uint32_t state_bytes)
static ra8_err_t internal_stat(void *ctx, const char *path, fw_fs_stat_t *out)
static ra8_err_t internal_open(void *ctx, const char *path, fw_fs_open_mode_t mode, void *file_state, uint32_t state_bytes)
Firmware filesystem-port adapter over ra8_io_vfs and ra8_fs.
File-local contracts for the firmware VFS filesystem adapter.
@ 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.
struct fw_fs_stream_iface fw_fs_stream_iface_t
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
fw_fs_open_mode_t
Open intent for fw_fs_open.
@ k_fw_fs_open_append
Create/append, write-only.
@ k_fw_fs_open_read
Existing file, read-only.
@ k_fw_fs_open_write_truncate
Create/truncate, write-only.
ra8_err_t(* fw_fs_list_fn_t)(void *ctx, const fw_fs_dirent_t *entry, bool *out_continue)
Bounded list callback.
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_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_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.
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_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_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
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.
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
ra8_err_t ra8_fs_write(ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
Write len bytes; allocate new clusters from FAT free-space scan as needed.
ra8_err_t ra8_fs_size(const ra8_fs_file_t *file, uint64_t *out_bytes)
Report the file's size in bytes (64-bit on exFAT, #676).
ra8_err_t ra8_fs_read(ra8_fs_file_t *file, uint8_t *buf, uint32_t max_len, uint32_t *got_len)
Read up to max_len bytes; advance the cluster chain on cluster crossings.
ra8_err_t ra8_fs_close(ra8_fs_file_t *file)
Close an open file, stamping its final modification time.
ra8_err_t ra8_fs_seek(ra8_fs_file_t *file, uint64_t offset_bytes)
Move the file offset to offset_bytes (clamped to size).
ra8_err_t ra8_fs_tell(const ra8_fs_file_t *file, uint64_t *out_offset)
Report the current offset (64-bit; see ra8_fs_seek).
Volume-level metadata: free space, volume label, and per-entry utime.
@ k_ra8_fs_fat_max_file_bytes
4 GiB - 1: max DIR_FileSize.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_attr_directory
MS FAT spec sec 6 "ATTR_DIRECTORY".
@ k_ra8_fs_max_files
Max concurrent open file handles.
ra8_fs_mode_t
File-open modes accepted by ra8_fs_open().
@ k_ra8_fs_mode_append
Open at EOF for writing.
@ k_ra8_fs_mode_read
Read-only, must exist.
@ k_ra8_fs_mode_write
Truncate (or create) for writing.
ra8_io virtual filesystem – mount many volumes, address them by name.
ra8_err_t ra8_io_vfs_dir_requirements(const char *path, uint32_t *out_bytes, uint8_t *out_align, uint16_t *out_max_open)
Query cursor workspace requirements for a qualified directory path.
ra8_err_t ra8_io_vfs_listdir(const char *path, ra8_fs_listdir_cb_t cb, void *ctx)
Enumerate a directory named "name:/path".
ra8_err_t ra8_io_vfs_free_space(const char *name, ra8_fs_space_t *out)
Query free and total bytes through a mounted format's ops.
Definition ra8_io_vfs.c:723
ra8_err_t ra8_io_vfs_open(const char *path, ra8_fs_mode_t mode, ra8_fs_file_t **out_file)
Open a file by "name:/path".
Definition ra8_io_vfs.c:547
@ k_ra8_io_vfs_name_max
Mount name length incl NUL.
Definition ra8_io_vfs.h:58
ra8_err_t ra8_io_vfs_dir_next(ra8_io_vfs_dir_t *directory, ra8_fs_dirent_t *out, bool *out_entry)
Copy one stable entry or report clean end-of-directory.
ra8_err_t ra8_io_vfs_mkdir(const char *path)
Create a directory named "name:/path".
ra8_err_t ra8_io_vfs_unlink(const char *path)
Delete a file by "name:/path".
ra8_err_t ra8_io_vfs_dir_close(ra8_io_vfs_dir_t *directory)
Close and consume one caller-owned VFS directory cursor.
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_rmdir(const char *path)
Remove the empty directory named "name:/path".
ra8_err_t ra8_io_vfs_dir_open(const char *path, ra8_io_vfs_dir_t *directory, void *workspace, uint32_t workspace_bytes)
Open a format-neutral incremental directory cursor.
ra8_err_t ra8_io_vfs_rename(const char *old_path, const char *new_path)
Rename a file within one mount.
Static properties and workspace requirements of one bound port.
uint32_t flags
OR of fw_fs_capability_t.
uint16_t year
Full civil year.
int16_t utc_offset_min
Offset from UTC when the validity flag is set.
uint8_t month
Month, 1..12.
uint32_t nanosecond
Fraction within second, 0..999,999,999.
uint8_t second
Second, 0..59.
uint8_t day
Day, 1..31.
uint8_t hour
Hour, 0..23.
uint8_t minute
Minute, 0..59.
One directory entry, valid only for the callback invocation.
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.
Composition-root configuration for one firmware filesystem binding.
const char * mount_name
Registered VFS name without :.
bool removable_media
True for hot-removable media such as SD.
ra8_fs_mount_t * mount
Matching live mount for space/caps.
Caller-owned adapter context.
uint32_t directory_workspace_bytes
Native cursor workspace bytes.
char mount_name[k_ra8_io_vfs_name_max]
Bounded mount name.
ra8_fs_mount_t * mount
Live mount.
char path_a[k_fw_fs_ra8_vfs_full_path_cap]
First path scratch.
uint16_t max_open_directories
Native concurrent cursor limit.
bool removable_media
Capability input.
uint8_t directory_workspace_align
Native cursor alignment.
uint32_t transaction_id
Stage-name counter.
char path_b[k_fw_fs_ra8_vfs_full_path_cap]
Rename path scratch.
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.
One portable timestamp and independent availability facts.
fw_fs_datetime_t value
Decoded civil date and time.
bool utc_offset_valid
True when utc_offset_min is known.
bool valid
True when the field is meaningful.
uint8_t hour
Hour of day, 0..23.
uint8_t minute
Minute of hour, 0..59.
int16_t utc_offset_min
Offset of the civil fields from UTC, in minutes.
uint8_t second
Second of minute, 0..59.
uint8_t month
Month of year, 1..12.
uint8_t centisecond
Hundredths within second, 0..99.
uint16_t year
Full civil year, e.g.
uint8_t day
Day of month, 1..31.
Stable directory-entry value copied by ra8_fs_dir_next.
char name[k_ra8_fs_dir_name_cap]
NUL-terminated visible leaf.
uint8_t attr
On-disk FAT attributes.
uint64_t size_bytes
File bytes; zero for dirs.
Open-file state.
ra8_fs_type_t type
FAT12 / FAT16 / FAT32.
uint8_t in_use
0 = slot free, 1 = mounted.
A mounted volume's capacity, free space, and cluster geometry.
Definition ra8_fs_meta.h:84
uint64_t used_bytes
Allocated data region, in bytes.
Definition ra8_fs_meta.h:87
uint64_t free_bytes
Unallocated data region, in bytes.
Definition ra8_fs_meta.h:86
uint64_t total_bytes
Whole data region, in bytes.
Definition ra8_fs_meta.h:85
One decoded on-disk timestamp plus availability facts.
ra8_fs_datetime_t value
Decoded civil date/time.
bool valid
true => the on-disk date is legal.
bool utc_offset_valid
true => utc_offset_min is known.
Caller-owned format-neutral directory cursor.
Definition ra8_io_vfs.h:69
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
ra8_fs_timestamp_t modified
Last-modified time, when present.
Definition ra8_io_vfs.h:99
bool is_directory
true => path names a directory.
Definition ra8_io_vfs.h:102
ra8_fs_timestamp_t accessed
Last-accessed time/date, when present.
Definition ra8_io_vfs.h:100
ra8_fs_timestamp_t created
Creation time, when present.
Definition ra8_io_vfs.h:98
bool exists
true => the path resolves to an entry.
Definition ra8_io_vfs.h:103
Where the cursor and the native workspace sit inside a caller span.
size_t cursor_end
Span bytes up to one past the cursor.
void * workspace
First native-aligned base past the cursor.
size_t consumed
Span bytes ahead of workspace.
void * cursor
First alignof(vfs_directory_state_t) base in the span.
Backend state stored in a caller's directory workspace.
ra8_io_vfs_dir_t native
Independent format-neutral VFS cursor.
Backend state stored in a caller's file workspace.
ra8_fs_file_t * native
Open repository VFS handle, or NULL when consumed.
Callback-list compatibility bridge over a format-owned enumeration.
uint32_t count
Deliveries attempted.
fw_fs_list_fn_t callback
Portable callback.
ra8_err_t callback_error
First callback/entry error.
void * callback_ctx
Portable callback context.
bool stopped
Budget or callback stop seen.
uint32_t max_entries
Delivery ceiling.
Backend state stored in a caller's transaction workspace.
bool writer_open
Writer is owned.
bool stage_exists
Stage is owned.
vfs_file_state_t file_state
Stage handle state.
char destination[k_fw_fs_path_cap]
Final portable path.
char stage[k_fw_fs_path_cap]
Private stage path.
fw_fs_transaction_policy_t policy
Fixed publish policy.