ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_state_store.c
Go to the documentation of this file.
1
8#include <inttypes.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <string.h>
12
13#include "mdl_state.h"
14#include "mdl_state_internal.h"
15#include "ra8_attributes.h"
16
36
38static const uint8_t s_state_magic[k_state_magic_bytes] = {'M', 'D', 'L', 'S', 'T', 'J', 'N', 'L'};
39
41typedef struct {
42 uint64_t sequence;
43 uint64_t payload_bytes;
44 uint32_t payload_crc32;
46
57
59typedef struct {
61 uint64_t payload_bytes;
62 uint32_t crc_state;
63 uint32_t calls;
65
71
79RA8_INTERNAL static uint32_t
80internal_mdl_state_crc32_update(uint32_t state, const uint8_t* bytes, uint32_t length)
81{
82 for (uint32_t i = 0U; i < length; ++i) {
83 state ^= bytes[i];
84 for (uint8_t bit = 0U; bit < 8U; ++bit) {
85 const uint32_t mask = (uint32_t)(0U - (state & 1U));
86 state = (state >> 1U) ^ ((uint32_t)k_state_crc32_polynomial & mask);
87 }
88 }
89 return state;
90}
91
99RA8_INTERNAL static void internal_mdl_state_put_be16(uint8_t* out, uint16_t value)
100{
101 out[0] = (uint8_t)(value >> 8U);
102 out[1] = (uint8_t)value;
103}
104
112RA8_INTERNAL static void internal_mdl_state_put_be32(uint8_t* out, uint32_t value)
113{
114 out[0] = (uint8_t)(value >> (uint32_t)k_state_be32_high_shift);
115 out[1] = (uint8_t)(value >> 16U);
116 out[2] = (uint8_t)(value >> 8U);
117 out[3] = (uint8_t)value;
118}
119
127RA8_INTERNAL static void internal_mdl_state_put_be64(uint8_t* out, uint64_t value)
128{
129 for (uint8_t i = 0U; i < 8U; ++i) {
130 out[i] = (uint8_t)(value >> ((uint32_t)k_state_be64_high_shift - (8U * i)));
131 }
132}
133
141RA8_INTERNAL static uint16_t internal_mdl_state_get_be16(const uint8_t* in)
142{
143 return (uint16_t)(((uint16_t)in[0] << 8U) | (uint16_t)in[1]);
144}
145
153RA8_INTERNAL static uint32_t internal_mdl_state_get_be32(const uint8_t* in)
154{
155 return ((uint32_t)in[0] << (uint32_t)k_state_be32_high_shift) | ((uint32_t)in[1] << 16U) |
156 ((uint32_t)in[2] << 8U) | (uint32_t)in[3];
157}
158
166RA8_INTERNAL static uint64_t internal_mdl_state_get_be64(const uint8_t* in)
167{
168 uint64_t value = 0U;
169 for (uint8_t i = 0U; i < 8U; ++i) {
170 value = (value << 8U) | (uint64_t)in[i];
171 }
172 return value;
173}
174
196
204RA8_INTERNAL static bool internal_mdl_state_decode_header(const uint8_t* bytes,
206{
207 const uint32_t expected = internal_mdl_state_get_be32(&bytes[k_state_header_crc_offset]);
208 const uint32_t actual =
209 ~internal_mdl_state_crc32_update(UINT32_MAX, bytes, (uint32_t)k_state_header_crc_span);
210 if ((memcmp(bytes, s_state_magic, sizeof(s_state_magic)) != 0) ||
212 (uint16_t)k_state_envelope_v1) ||
214 (uint16_t)k_state_header_bytes) ||
216 (actual != expected)) {
217 return false;
218 }
222 return out->sequence != 0U;
223}
224
233internal_mdl_state_read_all(fw_fs_file_t* file, uint8_t* out, uint32_t length, uint32_t* calls)
234{
235 uint32_t offset = 0U;
236 while (offset < length) {
237 if (*calls >= (uint32_t)k_state_io_call_max) {
239 }
240 uint32_t count = 0U;
241 const ra8_err_t err = fw_fs_read(file, out + offset, length - offset, &count);
242 ++(*calls);
243 if (err != k_ra8_ok) {
244 return err;
245 }
246 if (count == 0U) {
248 }
249 offset += count;
250 }
251 return k_ra8_ok;
252}
253
262 fw_fs_file_t* file,
263 uint64_t length,
264 uint32_t* out_crc)
265{
266 uint64_t remaining = length;
267 uint32_t state = UINT32_MAX;
268 uint32_t calls = 0U;
269 while (remaining > 0U) {
270 if (calls >= (uint32_t)k_state_io_call_max) {
272 }
273 const uint32_t wanted = (remaining < (uint64_t)storage->io_buffer_bytes)
274 ? (uint32_t)remaining
275 : storage->io_buffer_bytes;
276 uint32_t count = 0U;
277 const ra8_err_t err = fw_fs_read(file, storage->io_buffer, wanted, &count);
278 ++calls;
279 if (err != k_ra8_ok) {
280 return err;
281 }
282 if (count == 0U) {
284 }
285 state = internal_mdl_state_crc32_update(state, storage->io_buffer, count);
286 remaining -= count;
287 }
288 *out_crc = ~state;
289 return k_ra8_ok;
290}
291
300 fw_fs_file_t* file,
302{
303 uint64_t file_bytes = 0U;
304 ra8_err_t err = fw_fs_file_size(file, &file_bytes);
305 if ((err != k_ra8_ok) || (file_bytes < (uint64_t)k_state_header_bytes)) {
306 return (err == k_ra8_ok) ? k_ra8_err_invalid_state : err;
307 }
308 err = fw_fs_seek(file, 0U);
309 uint8_t header[k_state_header_bytes];
310 uint32_t calls = 0U;
311 if (err == k_ra8_ok) {
312 err = internal_mdl_state_read_all(file, header, sizeof(header), &calls);
313 }
314 if ((err != k_ra8_ok) || !internal_mdl_state_decode_header(header, out)) {
315 return (err == k_ra8_ok) ? k_ra8_err_invalid_state : err;
316 }
317 if ((out->payload_bytes > (UINT64_MAX - (uint64_t)k_state_header_bytes)) ||
318 (file_bytes != ((uint64_t)k_state_header_bytes + out->payload_bytes))) {
320 }
321 uint32_t crc = 0U;
322 err = internal_mdl_state_hash_payload(storage, file, out->payload_bytes, &crc);
323 if (err != k_ra8_ok) {
324 return err;
325 }
326 return (crc == out->payload_crc32) ? k_ra8_ok : k_ra8_err_invalid_state;
327}
328
337 const char* path,
338 char* base,
339 char* alternate)
340{
341 if ((storage == nullptr) || (storage->fs == nullptr) || (path == nullptr)) {
343 }
344 if ((storage->file_workspace == nullptr) || (storage->transaction_workspace == nullptr) ||
345 (storage->io_buffer == nullptr) || (storage->io_buffer_bytes == 0U)) {
347 }
348 ra8_err_t err = fw_fs_path_validate(&storage->fs->caps, path);
349 if (err != k_ra8_ok) {
350 return err;
351 }
352 const size_t length = strlen(path);
353 if ((length == 0U) || (length + (size_t)k_state_alt_suffix >= (size_t)k_fw_fs_path_cap)) {
355 }
356 memcpy(base, path, length + 1U);
357 memcpy(alternate, path, length + 1U);
358 memcpy(&alternate[length], ".alt", (size_t)k_state_alt_suffix + 1U);
359 return fw_fs_path_validate(&storage->fs->caps, alternate);
360}
361
370 const char* path,
371 bool allow_legacy,
372 mdl_state_slot_t* out)
373{
374 *out = (mdl_state_slot_t){.path = path, .error = k_ra8_ok};
375 fw_fs_stat_t stat = {};
376 out->error = fw_fs_stat(&storage->fs->names, path, &stat);
377 if ((out->error != k_ra8_ok) || !stat.exists) {
378 return;
379 }
380 out->exists = true;
381 out->file_bytes = stat.size_bytes;
382 if (stat.type != k_fw_fs_node_file) {
384 return;
385 }
386 fw_fs_file_t file = {};
387 out->error = fw_fs_open(&storage->fs->streams,
388 path,
390 &file,
391 storage->file_workspace,
392 storage->file_workspace_bytes);
393 if (out->error == k_ra8_ok) {
394 uint8_t prefix[k_state_magic_bytes] = {};
395 uint32_t calls = 0U;
396 const bool too_short = stat.size_bytes < sizeof(prefix);
397 out->error =
398 too_short ? k_ra8_ok : internal_mdl_state_read_all(&file, prefix, sizeof(prefix), &calls);
399 const bool legacy_magic = (out->error == k_ra8_ok) &&
400 (too_short || (memcmp(prefix, s_state_magic, sizeof(prefix)) != 0));
401 if (legacy_magic && allow_legacy) {
402 out->legacy = true;
403 out->valid = true;
404 } else if (legacy_magic) {
406 } else if (out->error == k_ra8_ok) {
407 out->error = internal_mdl_state_validate_open(storage, &file, &out->envelope);
408 out->valid = out->error == k_ra8_ok;
409 }
410 }
411 const ra8_err_t closed = file.is_open ? fw_fs_close(&file) : k_ra8_ok;
412 if (closed != k_ra8_ok) {
413 out->error = closed;
414 out->valid = false;
415 }
416}
417
426 const mdl_state_slot_t* b)
427{
428 return a->envelope.sequence == b->envelope.sequence &&
429 a->envelope.payload_bytes == b->envelope.payload_bytes &&
430 a->envelope.payload_crc32 == b->envelope.payload_crc32;
431}
432
441 const mdl_state_slot_t* alternate,
442 const mdl_state_slot_t** newest,
443 const mdl_state_slot_t** older)
444{
445 *newest = nullptr;
446 *older = nullptr;
447 if (!base->valid && !alternate->valid) {
448 return k_ra8_ok;
449 }
450 if (!base->valid || !alternate->valid) {
451 *newest = base->valid ? base : alternate;
452 return k_ra8_ok;
453 }
454 if (base->envelope.sequence == alternate->envelope.sequence) {
455 if (!internal_mdl_state_same_generation(base, alternate)) {
457 }
458 *newest = base;
459 *older = alternate;
460 return k_ra8_ok;
461 }
462 const bool base_newer = base->envelope.sequence > alternate->envelope.sequence;
463 *newest = base_newer ? base : alternate;
464 *older = base_newer ? alternate : base;
465 return k_ra8_ok;
466}
467
477{
478 fw_fs_file_t file = {};
479 ra8_err_t err = fw_fs_open(&storage->fs->streams,
480 slot->path,
482 &file,
483 storage->file_workspace,
484 storage->file_workspace_bytes);
485 mdl_state_envelope_t observed = {};
486 if ((err == k_ra8_ok) && !slot->legacy) {
487 err = internal_mdl_state_validate_open(storage, &file, &observed);
488 if ((err == k_ra8_ok) && ((observed.sequence != slot->envelope.sequence) ||
489 (observed.payload_bytes != slot->envelope.payload_bytes) ||
490 (observed.payload_crc32 != slot->envelope.payload_crc32))) {
492 }
493 }
494 if (err == k_ra8_ok) {
495 const uint64_t offset = slot->legacy ? 0U : (uint64_t)k_state_header_bytes;
496 const uint64_t length = slot->legacy ? slot->file_bytes : slot->envelope.payload_bytes;
497 const uint16_t max_schema =
498 slot->legacy ? (uint16_t)k_mdl_state_version_v2 : (uint16_t)k_mdl_state_version;
499 err = priv_mdl_state_parse_file(storage, &file, offset, length, max_schema, st);
500 }
501 const ra8_err_t closed = file.is_open ? fw_fs_close(&file) : k_ra8_ok;
502 if (closed != k_ra8_ok) {
503 err = closed;
504 }
505 if (err != k_ra8_ok) {
506 mdl_state_init(st);
507 }
508 return err;
509}
510
519internal_mdl_state_write_all(mdl_state_writer_t* writer, const uint8_t* bytes, uint32_t length)
520{
521 uint32_t offset = 0U;
522 while (offset < length) {
523 if (writer->calls >= (uint32_t)k_state_io_call_max) {
525 }
526 uint32_t count = 0U;
527 const ra8_err_t err =
528 fw_fs_transaction_write(writer->transaction, bytes + offset, length - offset, &count);
529 ++writer->calls;
530 if (err != k_ra8_ok) {
531 return err;
532 }
533 if (count == 0U) {
535 }
536 offset += count;
537 }
538 return k_ra8_ok;
539}
540
549internal_mdl_state_emit(mdl_state_writer_t* writer, const char* line, int length)
550{
551 if ((length < 0) || ((size_t)length >= (size_t)k_mdl_state_line_max) ||
552 (writer->payload_bytes > (UINT64_MAX - (uint64_t)(uint32_t)length))) {
554 }
555 const ra8_err_t err =
556 internal_mdl_state_write_all(writer, (const uint8_t*)line, (uint32_t)length);
557 if (err != k_ra8_ok) {
558 return err;
559 }
560 writer->crc_state =
561 internal_mdl_state_crc32_update(writer->crc_state, (const uint8_t*)line, (uint32_t)length);
562 writer->payload_bytes += (uint64_t)(uint32_t)length;
563 return k_ra8_ok;
564}
565
574internal_mdl_state_emit_kv(mdl_state_writer_t* writer, char type, const char* value)
575{
576 char line[k_mdl_state_line_max];
577 const int length = snprintf(line, sizeof(line), "%c\t%s\n", type, value);
578 return internal_mdl_state_emit(writer, line, length);
579}
580
589 const mdl_state_t* st)
590{
591 static const char types[] = {'S', 'T', 'N', 'H', 'G', 'D', 'W', 'A', 'O', 'K', 'L'};
592 const char* const values[] = {st->series_url,
593 st->series_title,
594 st->site_name,
595 st->site_host,
596 st->config_path,
597 st->summary,
598 st->writer,
599 st->artist,
600 st->cover_url,
601 st->cover_path,
602 st->language};
603 ra8_err_t err = k_ra8_ok;
604 for (size_t i = 0U; (i < sizeof(types)) && (err == k_ra8_ok); ++i) {
605 err = internal_mdl_state_emit_kv(writer, types[i], values[i]);
606 }
607 if (err == k_ra8_ok) {
608 char line[k_mdl_state_line_max];
609 const int length = snprintf(line, sizeof(line), "R\t%u\n", (unsigned)st->reading_direction);
610 err = internal_mdl_state_emit(writer, line, length);
611 }
612 return err;
613}
614
623 const mdl_state_t* st)
624{
625 char line[k_mdl_state_line_max];
626 ra8_err_t err = k_ra8_ok;
627 for (uint16_t i = 0U; (i < st->chapter_count) && (err == k_ra8_ok); ++i) {
628 const mdl_chapter_rec_t* chapter = &st->chapters[i];
629 uint64_t number_bits = 0U;
630 memcpy(&number_bits, &chapter->number, sizeof(number_bits));
631 const int length = snprintf(line,
632 sizeof(line),
633 "C\t%s\t%u\t%016" PRIx64 "\t%u\t%u\t%u\t%" PRId64 "\t%s\t%s\n",
634 chapter->chapter_id,
635 chapter->number_known ? 1U : 0U,
636 number_bits,
637 chapter->complete ? 1U : 0U,
638 (unsigned)chapter->page_count,
639 (unsigned)chapter->pages_done,
640 chapter->fetched_at,
641 chapter->source_url,
642 chapter->title);
643 err = internal_mdl_state_emit(writer, line, length);
644 }
645 return err;
646}
647
656 const mdl_state_t* st)
657{
658 char line[k_mdl_state_line_max];
659 ra8_err_t err = k_ra8_ok;
660 for (uint32_t i = 0U; (i < st->page_rec_count) && (err == k_ra8_ok); ++i) {
661 const mdl_page_rec_t* page = &st->pages[i];
662 const int length = snprintf(line,
663 sizeof(line),
664 "P\t%016" PRIx64 "\t%016" PRIx64 "\t%s\t%s\t%s\t%" PRId64 "\t%u\n",
665 page->url_hash,
666 page->content_hash,
667 page->rel_path,
668 page->etag,
669 page->last_modified,
670 page->fetched_at,
671 (unsigned)page->response_status);
672 err = internal_mdl_state_emit(writer, line, length);
673 }
674 return err;
675}
676
685 const mdl_state_t* st)
686{
687 char line[k_mdl_state_line_max];
688 const int length = snprintf(line,
689 sizeof(line),
690 "# mdl library state v%u\nV\t%u\n",
691 (unsigned)k_mdl_state_version,
692 (unsigned)k_mdl_state_version);
693 ra8_err_t err = internal_mdl_state_emit(writer, line, length);
694 if (err == k_ra8_ok) {
695 err = internal_mdl_state_emit_metadata(writer, st);
696 }
697 if (err == k_ra8_ok) {
698 err = internal_mdl_state_emit_chapters(writer, st);
699 }
700 return (err == k_ra8_ok) ? internal_mdl_state_emit_pages(writer, st) : err;
701}
702
711{
712 if ((ctx == nullptr) || (staged == nullptr)) {
714 }
715 const mdl_state_validation_t* expected = (const mdl_state_validation_t*)ctx;
716 mdl_state_envelope_t observed = {};
717 const ra8_err_t err = internal_mdl_state_validate_open(expected->storage, staged, &observed);
718 if (err != k_ra8_ok) {
719 return err;
720 }
721 if ((observed.sequence != expected->envelope.sequence) ||
722 (observed.payload_bytes != expected->envelope.payload_bytes) ||
723 (observed.payload_crc32 != expected->envelope.payload_crc32)) {
725 }
726 return k_ra8_ok;
727}
728
737 ra8_err_t primary)
738{
739 if (!transaction->active) {
740 return primary;
741 }
742 const ra8_err_t aborted = fw_fs_transaction_abort(transaction);
743 return (aborted == k_ra8_ok) ? primary : aborted;
744}
745
754 const char* target,
755 uint64_t sequence,
756 const mdl_state_t* st,
757 fw_fs_transaction_t* transaction,
758 mdl_state_envelope_t* envelope)
759{
761 target,
763 transaction,
764 storage->transaction_workspace,
766 mdl_state_writer_t writer = {.transaction = transaction,
767 .payload_bytes = 0U,
768 .crc_state = UINT32_MAX,
769 .calls = 0U};
770 if (err == k_ra8_ok) {
771 const uint8_t placeholder[k_state_header_bytes] = {};
772 err = internal_mdl_state_write_all(&writer, placeholder, sizeof(placeholder));
773 }
774 if (err == k_ra8_ok) {
775 err = internal_mdl_state_serialize(&writer, st);
776 }
777 *envelope = (mdl_state_envelope_t){.sequence = sequence,
778 .payload_bytes = writer.payload_bytes,
779 .payload_crc32 = ~writer.crc_state};
780 uint8_t header[k_state_header_bytes];
781 internal_mdl_state_encode_header(envelope, header);
782 if (err == k_ra8_ok) {
783 err = fw_fs_transaction_seek(transaction, 0U);
784 }
785 if (err == k_ra8_ok) {
786 err = internal_mdl_state_write_all(&writer, header, sizeof(header));
787 }
788 const mdl_state_validation_t validation = {.storage = storage, .envelope = *envelope};
789 return (err == k_ra8_ok) ? fw_fs_transaction_validate(transaction,
791 (void*)&validation)
792 : err;
793}
794
803 const mdl_state_slot_t* alternate,
804 const mdl_state_slot_t** target,
805 uint64_t* sequence)
806{
807 const mdl_state_slot_t* newest = nullptr;
808 const mdl_state_slot_t* older = nullptr;
809 ra8_err_t err = internal_mdl_state_order(base, alternate, &newest, &older);
810 if (err != k_ra8_ok) {
811 return err;
812 }
813 if (newest == nullptr) {
814 if ((base->error != k_ra8_ok) || (alternate->error != k_ra8_ok)) {
815 return (base->error != k_ra8_ok) ? base->error : alternate->error;
816 }
817 *target = base;
818 *sequence = 1U;
819 return k_ra8_ok;
820 }
821 if (newest->envelope.sequence == UINT64_MAX) {
823 }
824 *target = (newest == base) ? alternate : base;
825 if ((older != nullptr) && (*target != older)) {
827 }
828 if (!(*target)->exists && ((*target)->error != k_ra8_ok)) {
829 return (*target)->error;
830 }
831 *sequence = newest->envelope.sequence + 1U;
832 return k_ra8_ok;
833}
834
835ra8_err_t mdl_state_probe(mdl_storage_t* storage, const char* path, bool* out_exists)
836{
837 if (out_exists == nullptr) {
839 }
840 *out_exists = false;
841 char base[k_fw_fs_path_cap];
842 char alternate[k_fw_fs_path_cap];
843 ra8_err_t err = internal_mdl_state_paths(storage, path, base, alternate);
844 if (err != k_ra8_ok) {
845 return err;
846 }
847 const char* paths[] = {base, alternate};
848 for (size_t i = 0U; i < 2U; ++i) {
849 fw_fs_stat_t stat = {};
850 err = fw_fs_stat(&storage->fs->names, paths[i], &stat);
851 if (err != k_ra8_ok) {
852 return err;
853 }
854 if (stat.exists && (stat.type != k_fw_fs_node_file)) {
856 }
857 *out_exists = *out_exists || stat.exists;
858 }
859 return k_ra8_ok;
860}
861
882 const char* path,
883 mdl_state_t* st,
884 bool allow_legacy)
885{
886 if (st == nullptr) {
888 }
889 mdl_state_init(st);
890 char base_path[k_fw_fs_path_cap];
891 char alternate_path[k_fw_fs_path_cap];
892 ra8_err_t err = internal_mdl_state_paths(storage, path, base_path, alternate_path);
893 if (err != k_ra8_ok) {
894 return err;
895 }
896 mdl_state_slot_t base;
897 mdl_state_slot_t alternate;
898 internal_mdl_state_scan_slot(storage, base_path, allow_legacy, &base);
899 internal_mdl_state_scan_slot(storage, alternate_path, false, &alternate);
900 const mdl_state_slot_t* newest = nullptr;
901 const mdl_state_slot_t* older = nullptr;
902 err = internal_mdl_state_order(&base, &alternate, &newest, &older);
903 if (err != k_ra8_ok) {
904 return err;
905 }
906 if (newest == nullptr) {
907 if (!base.exists && !alternate.exists && (base.error == k_ra8_ok) &&
908 (alternate.error == k_ra8_ok)) {
909 return k_ra8_ok;
910 }
911 return (base.error != k_ra8_ok) ? base.error : alternate.error;
912 }
913 err = internal_mdl_state_load_slot(storage, newest, st);
914 if ((err != k_ra8_ok) && (older != nullptr)) {
915 err = internal_mdl_state_load_slot(storage, older, st);
916 }
917 return err;
918}
919
920ra8_err_t mdl_state_load(mdl_storage_t* storage, const char* path, mdl_state_t* st)
921{
922 return internal_mdl_state_load_mode(storage, path, st, true);
923}
924
926{
927 return internal_mdl_state_load_mode(storage, path, st, false);
928}
929
936 const char* path,
937 char* base,
938 char* alternate,
939 const char** out_target,
940 uint64_t* out_sequence)
941{
942 ra8_err_t err = internal_mdl_state_paths(storage, path, base, alternate);
943 if (err != k_ra8_ok) {
944 return err;
945 }
946 mdl_state_slot_t base_slot;
947 mdl_state_slot_t alternate_slot;
948 internal_mdl_state_scan_slot(storage, base, true, &base_slot);
949 internal_mdl_state_scan_slot(storage, alternate, false, &alternate_slot);
950 const mdl_state_slot_t* target = nullptr;
951 err = internal_mdl_state_save_plan(&base_slot, &alternate_slot, &target, out_sequence);
952 if (err != k_ra8_ok) {
953 return err;
954 }
955 if (target->exists) {
956 err = fw_fs_unlink(&storage->fs->names, target->path);
957 if (err != k_ra8_ok) {
958 return err;
959 }
960 }
961 *out_target = target->path;
962 return k_ra8_ok;
963}
964
966mdl_state_save(mdl_storage_t* storage, const char* path, const mdl_state_t* st, bool* out_published)
967{
968 if (out_published == nullptr) {
970 }
971 *out_published = false;
972 if ((st == nullptr) || !priv_mdl_state_valid(st)) {
973 return (st == nullptr) ? k_ra8_err_invalid_arg : k_ra8_err_invalid_state;
974 }
975 char base_path[k_fw_fs_path_cap];
976 char alternate_path[k_fw_fs_path_cap];
977 const char* target = nullptr;
978 uint64_t sequence = 0U;
979 ra8_err_t err =
980 internal_mdl_state_prepare_target(storage, path, base_path, alternate_path, &target, &sequence);
981 if (err != k_ra8_ok) {
982 return err;
983 }
984 fw_fs_transaction_t transaction = {};
985 mdl_state_envelope_t envelope = {};
986 err = internal_mdl_state_build_stage(storage, target, sequence, st, &transaction, &envelope);
987 if (err != k_ra8_ok) {
988 return internal_mdl_state_abort(&transaction, err);
989 }
990 bool published = false;
991 err = fw_fs_transaction_commit(&transaction, &published);
992 *out_published = published;
993 if (published) {
994 return err;
995 }
996 if (err == k_ra8_ok) {
998 }
999 return internal_mdl_state_abort(&transaction, err);
1000}
ra8_err_t fw_fs_open(const fw_fs_stream_port_t *streams, const char *path, fw_fs_open_mode_t mode, fw_fs_file_t *file, void *workspace, uint32_t workspace_size)
Open a file into a caller-owned handle and backend workspace.
Definition fw_if_fs.c:648
ra8_err_t fw_fs_transaction_seek(fw_fs_transaction_t *transaction, uint64_t absolute_offset)
Seek the staging writer to an absolute byte offset for bounded backfill.
Definition fw_if_fs.c:917
ra8_err_t fw_fs_transaction_abort(fw_fs_transaction_t *transaction)
Close and remove an unpublished staging artifact.
Definition fw_if_fs.c:976
ra8_err_t fw_fs_stat(const fw_fs_namespace_t *names, const char *path, fw_fs_stat_t *out)
Query a path; a miss is success with out->exists == false.
Definition fw_if_fs.c:480
ra8_err_t fw_fs_transaction_validate(fw_fs_transaction_t *transaction, fw_fs_validate_fn_t validator, void *validator_ctx)
Flush/reopen the stage and ask validator to inspect it read-only.
Definition fw_if_fs.c:929
ra8_err_t fw_fs_read(fw_fs_file_t *file, uint8_t *dst, uint32_t cap, uint32_t *out_read)
Read up to cap bytes; zero bytes is EOF.
Definition fw_if_fs.c:699
ra8_err_t fw_fs_transaction_begin(const fw_fs_transaction_port_t *port, const char *destination, fw_fs_transaction_policy_t policy, fw_fs_transaction_t *transaction, void *workspace, uint32_t workspace_size)
Create a hidden sibling staging file for one destination.
Definition fw_if_fs.c:850
ra8_err_t fw_fs_transaction_write(fw_fs_transaction_t *transaction, const uint8_t *source, uint32_t length, uint32_t *out_written)
Append bytes to the private staging artifact.
Definition fw_if_fs.c:892
ra8_err_t fw_fs_close(fw_fs_file_t *file)
Close and consume an open handle.
Definition fw_if_fs.c:783
ra8_err_t fw_fs_seek(fw_fs_file_t *file, uint64_t absolute_offset)
Seek to an absolute byte offset from the beginning.
Definition fw_if_fs.c:736
ra8_err_t fw_fs_transaction_commit(fw_fs_transaction_t *transaction, bool *out_published)
Publish a validated stage.
Definition fw_if_fs.c:951
ra8_err_t fw_fs_path_validate(const fw_fs_caps_t *caps, const char *path)
Validate a canonical portable path against a binding's limits.
Definition fw_if_fs.c:286
ra8_err_t fw_fs_unlink(const fw_fs_namespace_t *names, const char *path)
Remove one regular file; directories require fw_fs_rmdir.
Definition fw_if_fs.c:575
ra8_err_t fw_fs_file_size(fw_fs_file_t *file, uint64_t *out_size)
Report the open file's current length.
Definition fw_if_fs.c:758
@ k_fw_fs_node_file
Regular byte stream.
@ k_fw_fs_path_cap
Largest portable path including its NUL.
@ k_fw_fs_open_read
Existing file, read-only.
@ k_fw_fs_txn_create_new
Commit only when destination is absent.
bool priv_mdl_state_valid(const mdl_state_t *st)
Validate every bound and cross-field invariant before persistence.
Definition mdl_state.c:142
Persistent per-series library state for the media downloader.
@ k_mdl_state_version
Timestamped cache schema written now.
Definition mdl_state.h:96
@ k_mdl_state_version_v2
Legacy decimal schema accepted.
Definition mdl_state.h:94
void mdl_state_init(mdl_state_t *st)
Reset a state object to an empty, current-version library.
Definition mdl_state.c:52
ra8_err_t priv_mdl_state_parse_file(mdl_storage_t *storage, fw_fs_file_t *file, uint64_t offset, uint64_t length, uint16_t max_schema_version, mdl_state_t *st)
Parse one exact state payload from an open portable file.
Module-private validation shared by the state model and codec.
@ k_mdl_state_line_max
Serialized line cap.
static ra8_err_t internal_mdl_state_read_all(fw_fs_file_t *file, uint8_t *out, uint32_t length, uint32_t *calls)
Read an exact byte count while rejecting zero progress.
static uint16_t internal_mdl_state_get_be16(const uint8_t *in)
Decode one canonical big-endian uint16.
static void internal_mdl_state_put_be16(uint8_t *out, uint16_t value)
Encode one uint16 in canonical big-endian order.
static uint64_t internal_mdl_state_get_be64(const uint8_t *in)
Decode one canonical big-endian uint64.
static void internal_mdl_state_encode_header(const mdl_state_envelope_t *envelope, uint8_t *out)
Encode one self-checking canonical envelope.
static bool internal_mdl_state_decode_header(const uint8_t *bytes, mdl_state_envelope_t *out)
Decode and authenticate one canonical envelope.
static ra8_err_t internal_mdl_state_hash_payload(mdl_storage_t *storage, fw_fs_file_t *file, uint64_t length, uint32_t *out_crc)
Hash one exact payload extent and reject early EOF.
mdl_state_store_limit_t
Canonical journal-envelope and bounded-I/O constants.
@ k_state_be64_high_shift
High-byte shift for uint64.
@ k_state_header_crc_offset
Header-CRC byte offset.
@ k_state_magic_bytes
Journal magic width.
@ k_state_header_reserved_offset
Reserved field byte offset.
@ k_state_header_crc_span
Header bytes authenticated.
@ k_state_header_payload_size_offset
Payload-size byte offset.
@ k_state_header_payload_crc_offset
Payload-CRC byte offset.
@ k_state_be32_high_shift
High-byte shift for uint32.
@ k_state_header_bytes
Canonical envelope width.
@ k_state_crc32_polynomial
Reflected CRC-32 polynomial.
@ k_state_header_size_offset
Header-size byte offset.
@ k_state_io_call_max
Retry ceiling.
@ k_state_header_version_offset
Envelope-version byte offset.
@ k_state_envelope_v1
Envelope schema version.
@ k_state_header_sequence_offset
Sequence field byte offset.
@ k_state_alt_suffix
Bytes in .alt.
static ra8_err_t internal_mdl_state_emit_pages(mdl_state_writer_t *writer, const mdl_state_t *st)
Serialize every page identity and cache record.
static ra8_err_t internal_mdl_state_build_stage(mdl_storage_t *storage, const char *target, uint64_t sequence, const mdl_state_t *st, fw_fs_transaction_t *transaction, mdl_state_envelope_t *envelope)
Build and validate a private staged journal.
static ra8_err_t internal_mdl_state_abort(fw_fs_transaction_t *transaction, ra8_err_t primary)
Abort an unpublished transaction with cleanup-error precedence.
static ra8_err_t internal_mdl_state_validate_stage(void *ctx, fw_fs_file_t *staged)
Independently validate the staged journal before publication.
static uint32_t internal_mdl_state_crc32_update(uint32_t state, const uint8_t *bytes, uint32_t length)
Update an unfinalized reflected CRC-32 state.
static ra8_err_t internal_mdl_state_prepare_target(mdl_storage_t *storage, const char *path, char *base, char *alternate, const char **out_target, uint64_t *out_sequence)
Decide which generation slot the next save occupies, and clear it.
static void internal_mdl_state_put_be32(uint8_t *out, uint32_t value)
Encode one uint32 in canonical big-endian order.
static void internal_mdl_state_put_be64(uint8_t *out, uint64_t value)
Encode one uint64 in canonical big-endian order.
ra8_err_t mdl_state_save(mdl_storage_t *storage, const char *path, const mdl_state_t *st, bool *out_published)
Publish a checksummed successor without sacrificing the newest state.
static ra8_err_t internal_mdl_state_load_slot(mdl_storage_t *storage, const mdl_state_slot_t *slot, mdl_state_t *st)
Load and revalidate one previously scanned generation.
static ra8_err_t internal_mdl_state_paths(const mdl_storage_t *storage, const char *path, char *base, char *alternate)
Derive and validate both physical generation paths.
static ra8_err_t internal_mdl_state_load_mode(mdl_storage_t *storage, const char *path, mdl_state_t *st, bool allow_legacy)
Load the newest usable state with an explicit legacy policy.
static ra8_err_t internal_mdl_state_save_plan(const mdl_state_slot_t *base, const mdl_state_slot_t *alternate, const mdl_state_slot_t **target, uint64_t *sequence)
Select the preserved generation, rewrite target, and next sequence.
static ra8_err_t internal_mdl_state_emit(mdl_state_writer_t *writer, const char *line, int length)
Append one payload record and update its identity.
static uint32_t internal_mdl_state_get_be32(const uint8_t *in)
Decode one canonical big-endian uint32.
ra8_err_t mdl_state_load(mdl_storage_t *storage, const char *path, mdl_state_t *st)
Load a series' newest valid state through injected portable storage.
static ra8_err_t internal_mdl_state_emit_chapters(mdl_state_writer_t *writer, const mdl_state_t *st)
Serialize every chapter with exact v3 numeric identity.
static bool internal_mdl_state_same_generation(const mdl_state_slot_t *a, const mdl_state_slot_t *b)
Check whether two slots describe the same generation.
static const uint8_t s_state_magic[k_state_magic_bytes]
Canonical journal magic, encoded byte-for-byte.
static ra8_err_t internal_mdl_state_emit_kv(mdl_state_writer_t *writer, char type, const char *value)
Serialize one bounded key/value record.
ra8_err_t mdl_state_load_authenticated(mdl_storage_t *storage, const char *path, mdl_state_t *st)
Load only an authenticated checksummed state generation.
static ra8_err_t internal_mdl_state_order(const mdl_state_slot_t *base, const mdl_state_slot_t *alternate, const mdl_state_slot_t **newest, const mdl_state_slot_t **older)
Order valid generations newest first.
static ra8_err_t internal_mdl_state_serialize(mdl_state_writer_t *writer, const mdl_state_t *st)
Serialize one complete current-schema payload.
static ra8_err_t internal_mdl_state_validate_open(mdl_storage_t *storage, fw_fs_file_t *file, mdl_state_envelope_t *out)
Validate an open journal envelope, extent, and checksum.
static void internal_mdl_state_scan_slot(mdl_storage_t *storage, const char *path, bool allow_legacy, mdl_state_slot_t *out)
Scan one physical generation without parsing its payload.
static ra8_err_t internal_mdl_state_write_all(mdl_state_writer_t *writer, const uint8_t *bytes, uint32_t length)
Write all bytes to an active transaction with bounded retry.
static ra8_err_t internal_mdl_state_emit_metadata(mdl_state_writer_t *writer, const mdl_state_t *st)
Serialize every fixed series identity and metadata record.
ra8_err_t mdl_state_probe(mdl_storage_t *storage, const char *path, bool *out_exists)
Probe the complete two-generation state marker through portable storage.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ 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_protocol_error
Protocol-level error (e.g.
Definition ra8_err.h:429
@ 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.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Caller-owned open file; fields are private to the facade.
Result of a portable metadata query.
uint64_t size_bytes
File length; zero for a directory.
bool exists
False means a clean lookup miss.
fw_fs_node_type_t type
Kind of node at the path.
fw_fs_stream_port_t streams
Byte-stream operations.
fw_fs_caps_t caps
Shared capabilities.
fw_fs_namespace_t names
Namespace operations.
fw_fs_transaction_port_t transactions
Staged publication.
Caller-owned transaction; fields are private to the facade.
bool active
Begin succeeded.
One chapter's coverage in library state.
Definition mdl_state.h:128
char chapter_id[k_mdl_chapter_id_max]
Stable identifier (URL leaf).
Definition mdl_state.h:129
double number
Parsed number; 0 may be valid.
Definition mdl_state.h:132
int64_t fetched_at
Completion time (epoch s).
Definition mdl_state.h:137
char source_url[k_mdl_url_max]
Chapter page URL.
Definition mdl_state.h:130
uint16_t pages_done
Pages fetched and verified.
Definition mdl_state.h:135
bool number_known
Whether number is known.
Definition mdl_state.h:133
char title[k_mdl_title_max]
Display title, or empty.
Definition mdl_state.h:131
uint16_t page_count
Total pages known (0 = ?).
Definition mdl_state.h:134
bool complete
All pages present + verified.
Definition mdl_state.h:136
One page's dedup/verify record in the series-wide pool.
Definition mdl_state.h:151
char etag[k_mdl_etag_max]
Cached ETag for conditional GET.
Definition mdl_state.h:155
char last_modified[k_mdl_last_mod_max]
Cached Last-Modified response value.
Definition mdl_state.h:157
uint64_t content_hash
FNV-1a 64 of the fetched bytes.
Definition mdl_state.h:153
char rel_path[k_mdl_relpath_max]
Path under the series directory.
Definition mdl_state.h:154
uint64_t url_hash
FNV-1a 64 of the source URL.
Definition mdl_state.h:152
uint16_t response_status
Most recent HTTP status; zero if legacy.
Definition mdl_state.h:159
int64_t fetched_at
Most recent HTTP result time (epoch s).
Definition mdl_state.h:158
Decoded canonical journal envelope.
uint64_t payload_bytes
Exact v1/v2 text payload size.
uint32_t payload_crc32
CRC-32 of payload bytes.
uint64_t sequence
Monotonic generation sequence.
One physical generation discovered under the logical path.
uint64_t file_bytes
Observed file extent.
bool legacy
Slot is legacy text.
bool valid
Slot is intact.
const char * path
Physical path.
ra8_err_t error
Scan status.
bool exists
Entry exists.
mdl_state_envelope_t envelope
Journal identity.
One series' complete persistent state (declare at file scope).
Definition mdl_state.h:175
char summary[k_mdl_summary_max]
Series synopsis.
Definition mdl_state.h:182
uint16_t chapter_count
Chapters recorded.
Definition mdl_state.h:191
char cover_path[k_mdl_relpath_max]
Local cover path.
Definition mdl_state.h:186
char series_title[k_mdl_title_max]
Series title.
Definition mdl_state.h:178
mdl_chapter_rec_t chapters[k_mdl_max_chapters]
Per-chapter coverage.
Definition mdl_state.h:195
char series_url[k_mdl_url_max]
Series page URL.
Definition mdl_state.h:177
char site_name[k_mdl_name_max]
Descriptor name.
Definition mdl_state.h:179
mdl_state_reading_direction_t reading_direction
Page progression.
Definition mdl_state.h:189
char language[k_mdl_language_max]
BCP-47 language tag.
Definition mdl_state.h:187
char artist[k_mdl_person_max]
Artist/illustrator.
Definition mdl_state.h:184
uint32_t page_rec_count
Page records recorded.
Definition mdl_state.h:193
mdl_page_rec_t pages[k_mdl_max_page_recs]
Per-page identities.
Definition mdl_state.h:197
char site_host[k_mdl_host_max]
Site host.
Definition mdl_state.h:180
char cover_url[k_mdl_url_max]
Remote cover URL.
Definition mdl_state.h:185
char config_path[k_mdl_cfgpath_max]
Descriptor used.
Definition mdl_state.h:181
char writer[k_mdl_person_max]
Writer/author.
Definition mdl_state.h:183
Expected stage identity passed to the independent validator.
mdl_storage_t * storage
Validation scratch.
mdl_state_envelope_t envelope
Expected identity.
Streaming serialization state for one transaction.
uint32_t calls
Bounded write attempts.
fw_fs_transaction_t * transaction
Active private stage.
uint32_t crc_state
Unfinalized CRC-32.
uint64_t payload_bytes
Bytes after envelope.
One non-reentrant downloader filesystem dependency bundle.
Definition mdl_storage.h:40
uint32_t transaction_workspace_bytes
Transaction workspace extent.
Definition mdl_storage.h:46
uint32_t file_workspace_bytes
File workspace extent.
Definition mdl_storage.h:45
void * transaction_workspace
Transaction backend state.
Definition mdl_storage.h:43
uint8_t * io_buffer
Caller-owned stream scratch.
Definition mdl_storage.h:44
const fw_fs_t * fs
Injected portable filesystem.
Definition mdl_storage.h:41
void * file_workspace
Open-file backend state.
Definition mdl_storage.h:42
uint32_t io_buffer_bytes
Stream scratch extent.
Definition mdl_storage.h:47