ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_cache_io.c
Go to the documentation of this file.
1
9#include <limits.h>
10#include <stdint.h>
11#include <string.h>
12
13#include "mdl_cache_internal.h"
14#include "mdl_hash.h"
15#include "mdl_sanitize.h"
16#include "mdl_url_guard.h"
17
38
40static const uint8_t s_cache_magic[k_cache_trailer_bytes] =
41 {'M', 'D', 'L', 'C', 'A', 'C', 'H', '1'};
42
56RA8_INTERNAL static void internal_cache_hex16(char* destination, uint64_t value)
57{
58 static const char k_hex[] = "0123456789abcdef";
59 for (uint8_t i = 0U; i < 16U; ++i) {
60 const uint8_t shift = (uint8_t)(k_cache_hex_high_shift - (4U * i));
61 destination[i] = k_hex[(value >> shift) & k_cache_nibble_mask];
62 }
63}
64
82RA8_INTERNAL static bool
83internal_cache_host_leaf(char* destination, size_t capacity, uint64_t host_hash)
84{
85 static const char k_prefix[] = "host-";
86 const size_t required = (sizeof(k_prefix) - 1U) + 16U + 1U;
87 if (capacity < required) {
88 return false;
89 }
90 memcpy(destination, k_prefix, sizeof(k_prefix) - 1U);
91 internal_cache_hex16(&destination[sizeof(k_prefix) - 1U], host_hash);
92 destination[required - 1U] = '\0';
93 return true;
94}
95
114RA8_INTERNAL static bool internal_cache_body_leaf(char* destination,
115 size_t capacity,
116 uint64_t url_hash,
117 uint64_t content_hash)
118{
119 static const char k_prefix[] = "body-";
120 static const char k_suffix[] = ".cache";
121 const size_t required = (sizeof(k_prefix) - 1U) + 16U + 1U + 16U + sizeof(k_suffix);
122 if (capacity < required) {
123 return false;
124 }
125 size_t cursor = 0U;
126 memcpy(&destination[cursor], k_prefix, sizeof(k_prefix) - 1U);
127 cursor += sizeof(k_prefix) - 1U;
128 internal_cache_hex16(&destination[cursor], url_hash);
129 cursor += 16U;
130 destination[cursor++] = '-';
131 internal_cache_hex16(&destination[cursor], content_hash);
132 cursor += 16U;
133 memcpy(&destination[cursor], k_suffix, sizeof(k_suffix));
134 return true;
135}
136
150RA8_INTERNAL static void internal_cache_put_u16(uint8_t* out, uint16_t value)
151{
152 out[0] = (uint8_t)(value >> 8U);
153 out[1] = (uint8_t)value;
154}
155
169RA8_INTERNAL static void internal_cache_put_u64(uint8_t* out, uint64_t value)
170{
171 for (uint8_t i = 0U; i < 8U; ++i) {
172 out[i] = (uint8_t)(value >> (k_cache_u64_high_shift - (8U * i)));
173 }
174}
175
190RA8_INTERNAL static uint16_t internal_cache_get_u16(const uint8_t* in)
191{
192 return (uint16_t)(((uint16_t)in[0] << 8U) | (uint16_t)in[1]);
193}
194
209RA8_INTERNAL static uint64_t internal_cache_get_u64(const uint8_t* in)
210{
211 uint64_t value = 0U;
212 for (uint8_t i = 0U; i < 8U; ++i) {
213 value = (value << 8U) | (uint64_t)in[i];
214 }
215 return value;
216}
217
239internal_cache_read_all(fw_fs_file_t* file, uint8_t* destination, uint32_t length, uint32_t* calls)
240{
241 uint32_t offset = 0U;
242 while (offset < length) {
243 if (*calls >= (uint32_t)k_cache_io_call_max) {
245 }
246 uint32_t received = 0U;
247 const ra8_err_t error = fw_fs_read(file, destination + offset, length - offset, &received);
248 ++(*calls);
249 if (error != k_ra8_ok) {
250 return error;
251 }
252 if (received == 0U) {
254 }
255 offset += received;
256 }
257 return k_ra8_ok;
258}
259
278 const char* path)
279{
280 fw_fs_stat_t node = {};
281 ra8_err_t error = fw_fs_stat(&storage->fs->names, path, &node);
282 if (error != k_ra8_ok) {
283 return error;
284 }
285 if (!node.exists) {
286 error = fw_fs_mkdir(&storage->fs->names, path);
287 if (error != k_ra8_ok) {
288 return error;
289 }
290 node = (fw_fs_stat_t){};
291 error = fw_fs_stat(&storage->fs->names, path, &node);
292 }
293 return ((error == k_ra8_ok) && node.exists && (node.type == k_fw_fs_node_directory))
294 ? k_ra8_ok
296}
297
318internal_cache_paths(mdl_cache_t* cache, const char* url, mdl_cache_paths_t* paths)
319{
320 *paths = (mdl_cache_paths_t){};
321 if ((cache == nullptr) || (cache->storage == nullptr) || (cache->root == nullptr) ||
322 (cache->root[0] != '/') || !mdl_url_host(url, paths->host, sizeof(paths->host))) {
324 }
325 paths->host_hash = mdl_hash_str(paths->host);
326 char leaf[32];
327 if (!internal_cache_host_leaf(leaf, sizeof(leaf), paths->host_hash) ||
328 !mdl_path_join(cache->root, leaf, paths->directory, sizeof(paths->directory)) ||
329 !mdl_path_join(paths->directory, "index.v1", paths->index_path, sizeof(paths->index_path))) {
331 }
333 return (error == k_ra8_ok) ? internal_cache_ensure_directory(cache->storage, paths->directory)
334 : error;
335}
336
354 const mdl_cache_paths_t* paths)
355{
356 char host[k_mdl_gov_host_max];
357 const bool fields = (record->url[0] != '\0') && (record->relative_path[0] != '\0') &&
358 (strpbrk(record->url, "\t\r\n") == nullptr) &&
359 (strpbrk(record->relative_path, "/\\\t\r\n") == nullptr) &&
360 (strpbrk(record->etag, "\r\n") == nullptr) &&
361 (strpbrk(record->last_modified, "\r\n") == nullptr) &&
362 (record->fetched_at >= 0) &&
363 (record->response_status >= (uint16_t)k_cache_status_min) &&
364 (record->response_status <= (uint16_t)k_cache_status_max);
365 return fields && (record->url_hash == mdl_hash_str(record->url)) &&
366 mdl_url_host(record->url, host, sizeof(host)) && (mdl_hash_str(host) == paths->host_hash);
367}
368
391 uint32_t* calls,
392 uint64_t* hash,
393 mdl_cache_record_t* record,
394 const mdl_cache_paths_t* paths)
395{
396 uint8_t header[k_cache_record_bytes];
397 ra8_err_t error = internal_cache_read_all(file, header, sizeof(header), calls);
398 if (error != k_ra8_ok) {
399 return error;
400 }
401 *hash = mdl_hash_bytes_seed(header, sizeof(header), *hash);
402 const uint16_t lengths[] = {internal_cache_get_u16(&header[k_cache_record_url_len]),
406 const size_t capacities[] = {sizeof(record->url),
407 sizeof(record->relative_path),
408 sizeof(record->etag),
409 sizeof(record->last_modified)};
410 char* fields[] = {record->url, record->relative_path, record->etag, record->last_modified};
411 if ((internal_cache_get_u16(&header[k_cache_record_reserved]) != 0U) || (lengths[0] == 0U)) {
413 }
414 *record =
415 (mdl_cache_record_t){.url_hash = internal_cache_get_u64(&header[0]),
416 .content_hash = internal_cache_get_u64(&header[8]),
417 .fetched_at = (int64_t)internal_cache_get_u64(&header[16]),
418 .response_status = internal_cache_get_u16(&header[k_cache_record_status])};
419 for (size_t i = 0U; i < 4U; ++i) {
420 if ((size_t)lengths[i] >= capacities[i]) {
422 }
423 error = internal_cache_read_all(file, (uint8_t*)fields[i], lengths[i], calls);
424 if (error != k_ra8_ok) {
425 return error;
426 }
427 fields[i][lengths[i]] = '\0';
428 *hash = mdl_hash_bytes_seed(fields[i], lengths[i], *hash);
429 }
431}
432
452internal_cache_decode(mdl_cache_t* cache, const mdl_cache_paths_t* paths, uint64_t size_bytes)
453{
454 fw_fs_file_t file = {};
455 ra8_err_t error = fw_fs_open(&cache->storage->fs->streams,
456 paths->index_path,
458 &file,
459 cache->storage->file_workspace,
461 uint32_t calls = 0U;
462 uint8_t header[k_cache_header_bytes] = {};
463 if (error == k_ra8_ok) {
464 error = internal_cache_read_all(&file, header, sizeof(header), &calls);
465 }
466 uint16_t count = 0U;
467 if (error == k_ra8_ok) {
469 const uint64_t payload_bytes = internal_cache_get_u64(&header[16]);
470 const uint64_t expected_size =
471 (uint64_t)k_cache_header_bytes + payload_bytes + (uint64_t)k_cache_trailer_bytes;
472 if ((memcmp(header, s_cache_magic, sizeof(s_cache_magic)) != 0) ||
473 (internal_cache_get_u16(&header[8]) != (uint16_t)k_mdl_cache_schema_version) ||
474 (internal_cache_get_u16(&header[10]) != (uint16_t)k_cache_header_bytes) ||
475 (count > (uint16_t)k_mdl_cache_record_max) || (internal_cache_get_u16(&header[14]) != 0U) ||
476 (expected_size != size_bytes) ||
479 }
480 }
481 *cache->index = (mdl_cache_index_t){.host_hash = paths->host_hash,
482 .schema_version = k_mdl_cache_schema_version};
483 uint64_t hash = (uint64_t)k_mdl_fnv_offset;
484 for (uint16_t i = 0U; (error == k_ra8_ok) && (i < count); ++i) {
485 error = internal_cache_read_record(&file, &calls, &hash, &cache->index->records[i], paths);
486 cache->index->record_count = (uint16_t)(i + 1U);
487 }
488 uint8_t trailer[k_cache_trailer_bytes];
489 if (error == k_ra8_ok) {
490 error = internal_cache_read_all(&file, trailer, sizeof(trailer), &calls);
491 }
492 if ((error == k_ra8_ok) && (internal_cache_get_u64(trailer) != hash)) {
494 }
495 const ra8_err_t closed = file.is_open ? fw_fs_close(&file) : k_ra8_ok;
496 return (error == k_ra8_ok) ? closed : error;
497}
498
517{
518 fw_fs_stat_t node = {};
519 const ra8_err_t error = fw_fs_stat(&cache->storage->fs->names, path, &node);
520 if (error != k_ra8_ok) {
521 return error;
522 }
523 if (!node.exists) {
524 return k_ra8_ok;
525 }
526 if (node.type != k_fw_fs_node_file) {
528 }
529 return fw_fs_unlink(&cache->storage->fs->names, path);
530}
531
533 const char* url,
534 mdl_cache_paths_t* paths,
535 bool* rebuilt)
536{
537 if ((cache == nullptr) || (cache->storage == nullptr) || (cache->index == nullptr) ||
538 (url == nullptr) || (paths == nullptr) || (rebuilt == nullptr)) {
540 }
541 *rebuilt = false;
542 ra8_err_t error = internal_cache_paths(cache, url, paths);
543 if (error != k_ra8_ok) {
544 return error;
545 }
546 fw_fs_stat_t node = {};
547 error = fw_fs_stat(&cache->storage->fs->names, paths->index_path, &node);
548 if (error != k_ra8_ok) {
549 return error;
550 }
551 if (!node.exists) {
552 *cache->index = (mdl_cache_index_t){.host_hash = paths->host_hash,
553 .schema_version = k_mdl_cache_schema_version};
554 return k_ra8_ok;
555 }
556 if (node.type != k_fw_fs_node_file) {
558 }
559 error = internal_cache_decode(cache, paths, node.size_bytes);
560 if (error == k_ra8_ok) {
561 return k_ra8_ok;
562 }
563 error = internal_cache_discard_index(cache, paths->index_path);
564 if (error != k_ra8_ok) {
565 return error;
566 }
567 *cache->index = (mdl_cache_index_t){.host_hash = paths->host_hash,
568 .schema_version = k_mdl_cache_schema_version};
569 *rebuilt = true;
570 return k_ra8_ok;
571}
572
590RA8_INTERNAL static bool
591internal_cache_encode_record(const mdl_cache_record_t* record, uint8_t* header, uint16_t* lengths)
592{
593 const char* fields[] = {record->url, record->relative_path, record->etag, record->last_modified};
594 for (size_t i = 0U; i < 4U; ++i) {
595 const size_t length = strlen(fields[i]);
596 if (length > UINT16_MAX) {
597 return false;
598 }
599 lengths[i] = (uint16_t)length;
600 }
601 memset(header, 0, k_cache_record_bytes);
602 internal_cache_put_u64(&header[0], record->url_hash);
603 internal_cache_put_u64(&header[8], record->content_hash);
604 internal_cache_put_u64(&header[16], (uint64_t)record->fetched_at);
610 return true;
611}
612
631 uint64_t* out_bytes,
632 uint64_t* out_hash)
633{
634 uint64_t bytes = 0U;
635 uint64_t hash = (uint64_t)k_mdl_fnv_offset;
636 for (uint16_t i = 0U; i < index->record_count; ++i) {
637 uint8_t header[k_cache_record_bytes];
638 uint16_t lengths[4];
639 if (!internal_cache_encode_record(&index->records[i], header, lengths)) {
641 }
642 hash = mdl_hash_bytes_seed(header, sizeof(header), hash);
643 bytes += sizeof(header);
644 const char* fields[] = {index->records[i].url,
645 index->records[i].relative_path,
646 index->records[i].etag,
647 index->records[i].last_modified};
648 for (size_t field = 0U; field < 4U; ++field) {
649 hash = mdl_hash_bytes_seed(fields[field], lengths[field], hash);
650 bytes += lengths[field];
651 }
652 }
653 *out_bytes = bytes;
654 *out_hash = hash;
655 return k_ra8_ok;
656}
657
676 const mdl_cache_index_t* index)
677{
678 for (uint16_t i = 0U; i < index->record_count; ++i) {
679 uint8_t header[k_cache_record_bytes];
680 uint16_t lengths[4];
681 if (!internal_cache_encode_record(&index->records[i], header, lengths)) {
683 }
684 ra8_err_t error = mdl_storage_txn_write(writer, header, sizeof(header));
685 const char* fields[] = {index->records[i].url,
686 index->records[i].relative_path,
687 index->records[i].etag,
688 index->records[i].last_modified};
689 for (size_t field = 0U; (error == k_ra8_ok) && (field < 4U); ++field) {
690 error = mdl_storage_txn_write(writer, (const uint8_t*)fields[field], lengths[field]);
691 }
692 if (error != k_ra8_ok) {
693 return error;
694 }
695 }
696 return k_ra8_ok;
697}
698
700{
701 if ((cache == nullptr) || (cache->storage == nullptr) || (cache->index == nullptr) ||
702 (paths == nullptr) || (cache->index->schema_version != k_mdl_cache_schema_version) ||
703 (cache->index->host_hash != paths->host_hash) ||
704 (cache->index->record_count > (uint16_t)k_mdl_cache_record_max)) {
706 }
707 for (uint16_t i = 0U; i < cache->index->record_count; ++i) {
708 if (!internal_cache_record_valid(&cache->index->records[i], paths)) {
710 }
711 }
712 uint64_t payload_bytes = 0U;
713 uint64_t payload_hash = 0U;
714 ra8_err_t error = internal_cache_payload_identity(cache->index, &payload_bytes, &payload_hash);
715 uint8_t header[k_cache_header_bytes] = {};
716 memcpy(header, s_cache_magic, sizeof(s_cache_magic));
718 internal_cache_put_u16(&header[10], (uint16_t)k_cache_header_bytes);
720 internal_cache_put_u64(&header[16], payload_bytes);
722 uint8_t trailer[k_cache_trailer_bytes];
723 internal_cache_put_u64(trailer, payload_hash);
724 mdl_storage_txn_t writer = {};
725 if (error == k_ra8_ok) {
726 error = mdl_storage_txn_begin(&writer, cache->storage, paths->index_path);
727 }
728 if (error == k_ra8_ok) {
729 error = mdl_storage_txn_write(&writer, header, sizeof(header));
730 }
731 if (error == k_ra8_ok) {
732 error = internal_cache_write_records(&writer, cache->index);
733 }
734 if (error == k_ra8_ok) {
735 error = mdl_storage_txn_write(&writer, trailer, sizeof(trailer));
736 }
737 if (error == k_ra8_ok) {
738 return mdl_storage_txn_commit(&writer);
739 }
740 const ra8_err_t aborted = mdl_storage_txn_abort(&writer);
741 return (aborted == k_ra8_ok) ? error : aborted;
742}
743
767 const char* body_path,
768 char* buffer,
769 uint64_t size_bytes)
770{
771 fw_fs_file_t file = {};
772 ra8_err_t error = fw_fs_open(&storage->fs->streams,
773 body_path,
775 &file,
776 storage->file_workspace,
777 storage->file_workspace_bytes);
778 uint32_t calls = 0U;
779 if (error == k_ra8_ok) {
780 error = internal_cache_read_all(&file, (uint8_t*)buffer, (uint32_t)size_bytes, &calls);
781 }
782 uint8_t extra = 0U;
783 uint32_t got = 0U;
784 if (error == k_ra8_ok) {
785 error = fw_fs_read(&file, &extra, 1U, &got);
786 }
787 if ((error == k_ra8_ok) && (got != 0U)) {
789 }
790 const ra8_err_t closed = file.is_open ? fw_fs_close(&file) : k_ra8_ok;
791 if (error == k_ra8_ok) {
792 error = closed;
793 }
794 return error;
795}
796
798 const mdl_cache_paths_t* paths,
799 const mdl_cache_record_t* record,
800 char* buffer,
801 size_t capacity,
802 size_t* out_length)
803{
804 if ((storage == nullptr) || (paths == nullptr) || (record == nullptr) || (buffer == nullptr) ||
805 (capacity == 0U) || (out_length == nullptr)) {
807 }
808 *out_length = 0U;
809 char body_path[k_fw_fs_path_cap];
810 if (!mdl_path_join(paths->directory, record->relative_path, body_path, sizeof(body_path))) {
812 }
813 fw_fs_stat_t node = {};
814 ra8_err_t error = fw_fs_stat(&storage->fs->names, body_path, &node);
815 if ((error != k_ra8_ok) || !node.exists || (node.type != k_fw_fs_node_file)) {
816 return (error != k_ra8_ok) ? error : k_ra8_err_not_found;
817 }
818 if ((node.size_bytes == 0U) || (node.size_bytes > capacity) || (node.size_bytes > UINT32_MAX)) {
820 }
821 error = internal_cache_read_body_exact(storage, body_path, buffer, node.size_bytes);
822 if ((error == k_ra8_ok) &&
823 (mdl_hash_bytes(buffer, (size_t)node.size_bytes) != record->content_hash)) {
825 }
826 if (error == k_ra8_ok) {
827 *out_length = (size_t)node.size_bytes;
828 }
829 return error;
830}
831
833 const mdl_cache_paths_t* paths,
834 uint64_t url_hash,
835 uint64_t content_hash,
836 const char* buffer,
837 size_t length,
838 char* relative_path,
839 size_t relative_capacity)
840{
841 if ((storage == nullptr) || (paths == nullptr) || (buffer == nullptr) || (length == 0U) ||
842 (length > UINT32_MAX) || (relative_path == nullptr) || (relative_capacity == 0U)) {
844 }
845 char body_path[k_fw_fs_path_cap];
846 if (!internal_cache_body_leaf(relative_path, relative_capacity, url_hash, content_hash) ||
847 !mdl_path_join(paths->directory, relative_path, body_path, sizeof(body_path))) {
849 }
850 mdl_storage_txn_t writer = {};
851 ra8_err_t error = mdl_storage_txn_begin(&writer, storage, body_path);
852 if (error == k_ra8_ok) {
853 error = mdl_storage_txn_write(&writer, (const uint8_t*)buffer, (uint32_t)length);
854 }
855 if (error == k_ra8_ok) {
856 return mdl_storage_txn_commit(&writer);
857 }
858 const ra8_err_t aborted = mdl_storage_txn_abort(&writer);
859 return (aborted == k_ra8_ok) ? error : aborted;
860}
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_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_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_close(fw_fs_file_t *file)
Close and consume an open handle.
Definition fw_if_fs.c:783
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_mkdir(const fw_fs_namespace_t *names, const char *path)
Create exactly one directory; parents must already exist.
Definition fw_if_fs.c:566
@ k_fw_fs_node_directory
Directory.
@ 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_mdl_cache_schema_version
Current binary index schema.
Definition mdl_cache.h:25
@ k_mdl_cache_record_max
Records retained per host.
Definition mdl_cache.h:26
Private binary-index and body I/O seams for mdl_cache.
static ra8_err_t internal_cache_decode(mdl_cache_t *cache, const mdl_cache_paths_t *paths, uint64_t size_bytes)
Decode and authenticate one complete index file.
ra8_err_t priv_mdl_cache_load(mdl_cache_t *cache, const char *url, mdl_cache_paths_t *paths, bool *rebuilt)
Prepare a host directory and load or recover its index.
mdl_cache_binary_limit_t
Canonical binary layout and bounded I/O constants.
@ k_cache_record_path_len
Record path-length offset.
@ k_cache_header_host
Header host-hash offset.
@ k_cache_trailer_bytes
Payload-hash trailer width.
@ k_cache_nibble_mask
Low hexadecimal nibble mask.
@ k_cache_header_count
Header record-count offset.
@ k_cache_status_max
Largest retained HTTP code.
@ k_cache_record_url_len
Record URL-length offset.
@ k_cache_io_call_max
Short-I/O progress ceiling.
@ k_cache_record_bytes
Fixed record header width.
@ k_cache_record_etag_len
Record ETag-length offset.
@ k_cache_record_status
Record HTTP-status offset.
@ k_cache_record_reserved
Record reserved-field offset.
@ k_cache_status_min
Smallest retained HTTP code.
@ k_cache_hex_high_shift
Shift of the first hash nibble.
@ k_cache_header_bytes
Fixed index header width.
@ k_cache_record_time_len
Record modified-length offset.
@ k_cache_u64_high_shift
Shift of the first uint64 byte.
ra8_err_t priv_mdl_cache_publish_body(mdl_storage_t *storage, const mdl_cache_paths_t *paths, uint64_t url_hash, uint64_t content_hash, const char *buffer, size_t length, char *relative_path, size_t relative_capacity)
Publish one new body under its immutable content-derived leaf.
static ra8_err_t internal_cache_read_record(fw_fs_file_t *file, uint32_t *calls, uint64_t *hash, mdl_cache_record_t *record, const mdl_cache_paths_t *paths)
Decode one variable-length record and update its payload hash.
static ra8_err_t internal_cache_discard_index(mdl_cache_t *cache, const char *path)
Remove one corrupt regular index.
ra8_err_t priv_mdl_cache_read_body(mdl_storage_t *storage, const mdl_cache_paths_t *paths, const mdl_cache_record_t *record, char *buffer, size_t capacity, size_t *out_length)
Read and hash-check one retained body into caller storage.
static bool internal_cache_encode_record(const mdl_cache_record_t *record, uint8_t *header, uint16_t *lengths)
Encode one record header and compute exact string lengths.
static void internal_cache_put_u16(uint8_t *out, uint16_t value)
Encode one big-endian uint16.
static bool internal_cache_record_valid(const mdl_cache_record_t *record, const mdl_cache_paths_t *paths)
Validate one decoded persistent record.
static void internal_cache_hex16(char *destination, uint64_t value)
Encode exactly sixteen lowercase hexadecimal digits.
static bool internal_cache_body_leaf(char *destination, size_t capacity, uint64_t url_hash, uint64_t content_hash)
Format one immutable body leaf without stdio.
ra8_err_t priv_mdl_cache_save(mdl_cache_t *cache, const mdl_cache_paths_t *paths)
Transactionally publish the current host index.
static ra8_err_t internal_cache_read_all(fw_fs_file_t *file, uint8_t *destination, uint32_t length, uint32_t *calls)
Read exactly one bounded span from an open file.
static ra8_err_t internal_cache_write_records(mdl_storage_txn_t *writer, const mdl_cache_index_t *index)
Stream every encoded record into an active transaction.
static ra8_err_t internal_cache_ensure_directory(mdl_storage_t *storage, const char *path)
Ensure a cache namespace component is a real directory.
static uint64_t internal_cache_get_u64(const uint8_t *in)
Decode one big-endian uint64.
static const uint8_t s_cache_magic[k_cache_trailer_bytes]
Canonical cache index magic.
static uint16_t internal_cache_get_u16(const uint8_t *in)
Decode one big-endian uint16.
static ra8_err_t internal_cache_paths(mdl_cache_t *cache, const char *url, mdl_cache_paths_t *paths)
Derive and prepare one host-specific cache namespace.
static ra8_err_t internal_cache_read_body_exact(mdl_storage_t *storage, const char *body_path, char *buffer, uint64_t size_bytes)
Read a body file's exact declared extent and confirm no trailer.
static bool internal_cache_host_leaf(char *destination, size_t capacity, uint64_t host_hash)
Format one host-directory leaf without stdio.
static void internal_cache_put_u64(uint8_t *out, uint64_t value)
Encode one big-endian uint64.
static ra8_err_t internal_cache_payload_identity(const mdl_cache_index_t *index, uint64_t *out_bytes, uint64_t *out_hash)
Compute payload extent and hash for the current index.
Content-identity hashing (FNV-1a 64) for the media downloader's persistent library state.
uint64_t mdl_hash_bytes(const void *data, size_t len)
FNV-1a 64 hash of a byte range.
Definition mdl_hash.c:35
@ k_mdl_fnv_offset
FNV-1a 64 offset basis.
Definition mdl_hash.h:35
uint64_t mdl_hash_bytes_seed(const void *data, size_t len, uint64_t seed)
Continue an FNV-1a 64 fold over a byte range from a running state.
Definition mdl_hash.c:21
uint64_t mdl_hash_str(const char *s)
FNV-1a 64 hash of a NUL-terminated string (excluding the NUL).
Definition mdl_hash.c:40
@ k_mdl_gov_host_max
Host-key buffer bytes (matches config).
Neutralise untrusted names before they reach a filesystem or XML sink.
bool mdl_path_join(const char *parent, const char *seg, char *out, size_t cap)
Join one safe child segment under a parent directory path.
ra8_err_t mdl_storage_txn_write(mdl_storage_txn_t *writer, const uint8_t *bytes, uint32_t length)
Append one complete caller chunk, tolerating bounded short writes.
ra8_err_t mdl_storage_txn_abort(mdl_storage_txn_t *writer)
Abort and clear one streamed transaction.
ra8_err_t mdl_storage_txn_commit(mdl_storage_txn_t *writer)
Independently validate and publish a completed streamed transaction.
ra8_err_t mdl_storage_txn_begin(mdl_storage_txn_t *writer, mdl_storage_t *storage, const char *destination)
Begin one streamed create or truthful atomic replacement.
Pure URL / address safety predicates for the libcurl backend.
bool mdl_url_host(const char *url, char *out, size_t cap)
Extract the authority (host and optional port) from an http(s) URL.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ 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_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ 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
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_namespace_t names
Namespace operations.
Caller-owned workspace for one loaded host index.
Definition mdl_cache.h:54
uint64_t host_hash
Bound host identity.
Definition mdl_cache.h:56
uint16_t schema_version
Binary schema version.
Definition mdl_cache.h:57
mdl_cache_record_t records[k_mdl_cache_record_max]
Retained observations.
Definition mdl_cache.h:55
uint16_t record_count
Populated rows.
Definition mdl_cache.h:58
Complete paths and host identity derived for one request.
uint64_t host_hash
Stable host identity.
char host[k_mdl_gov_host_max]
Lowercase host and optional port.
char index_path[k_fw_fs_path_cap]
Versioned host index path.
char directory[k_fw_fs_path_cap]
Host-specific cache directory.
One exact URL-keyed cached document observation.
Definition mdl_cache.h:37
uint64_t content_hash
Exact persisted body identity.
Definition mdl_cache.h:43
char url[k_mdl_url_max]
Exact canonical request URL.
Definition mdl_cache.h:38
char etag[k_mdl_etag_max]
Last response ETag, or empty.
Definition mdl_cache.h:40
char last_modified[k_mdl_last_mod_max]
Last-Modified, or empty.
Definition mdl_cache.h:41
uint64_t url_hash
FNV identity accelerator.
Definition mdl_cache.h:42
uint16_t response_status
Last observed HTTP status.
Definition mdl_cache.h:45
int64_t fetched_at
Completion epoch seconds.
Definition mdl_cache.h:44
char relative_path[k_mdl_relpath_max]
Body leaf beneath the host dir.
Definition mdl_cache.h:39
One non-reentrant cache binding over caller storage.
Definition mdl_cache.h:92
const char * root
Canonical cache root.
Definition mdl_cache.h:95
mdl_storage_t * storage
Injected portable storage binding.
Definition mdl_cache.h:93
mdl_cache_index_t * index
Caller-owned index workspace.
Definition mdl_cache.h:94
One non-reentrant downloader filesystem dependency bundle.
Definition mdl_storage.h:40
uint32_t file_workspace_bytes
File workspace extent.
Definition mdl_storage.h:45
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
Caller-owned streaming publication transaction with running identity.
Definition mdl_storage.h:58