64typedef enum :
size_t {
123 struct sigaction action = {.sa_handler = SIG_IGN};
124 if ((sigemptyset(&action.sa_mask) != 0) || (sigaction(SIGPIPE, &action,
nullptr) != 0)) {
178 while (offset < length) {
179 ssize_t received = pread(fd, destination + offset, length - offset, (off_t)offset);
180 if ((received < 0) && (errno == EINTR)) {
186 offset += (size_t)received;
189 ssize_t overflow = 0;
191 overflow = pread(fd, &extra, 1U, (off_t)length);
192 }
while ((overflow < 0) && (errno == EINTR));
193 struct stat after = {};
194 if ((overflow != 0) || (fstat(fd, &after) != 0) ||
224 uint8_t* destination,
235 struct stat before = {};
237 if ((fstat(fd, &before) != 0) || (before.st_size < 0)) {
239 }
else if (!S_ISREG(before.st_mode)) {
241 }
else if ((uintmax_t)before.st_size > (uintmax_t)SIZE_MAX) {
242 *required = SIZE_MAX;
245 *required = (size_t)before.st_size;
249 if ((close(fd) != 0) && (error ==
k_ra8_ok)) {
284 error =
priv_mdl_stream_text(error, diagnostic,
" input exceeds credential capacity (required ");
314 size_t required = 0U;
330 (void)
ra8_io_stream_puts(diagnostic,
"mdl: could not load safe --cookie-file input\n");
338 if (args->
ca_file ==
nullptr) {
350 }
else if ((error !=
k_ra8_ok) || (used == 0U)) {
351 (void)
ra8_io_stream_puts(diagnostic,
"mdl: could not load safe nonempty --ca-file input\n");
353 if ((error !=
k_ra8_ok) || (used == 0U)) {
446 if (args->
cfg ==
nullptr) {
480 if (realpath(input, destination) !=
nullptr) {
486 const char* slash =
strrchr(input,
'/');
487 const char* leaf = (slash ==
nullptr) ? input : slash + 1;
488 if ((leaf[0] ==
'\0') || (
strcmp(leaf,
".") == 0) || (
strcmp(leaf,
"..") == 0)) {
491 char parent[PATH_MAX];
492 if (slash ==
nullptr) {
493 (void)
memcpy(parent,
".", 2U);
494 }
else if (slash == input) {
495 (void)
memcpy(parent,
"/", 2U);
497 const size_t parent_bytes = (size_t)(slash - input);
498 if (parent_bytes >=
sizeof(parent)) {
501 memcpy(parent, input, parent_bytes);
502 parent[parent_bytes] =
'\0';
504 char canonical_parent[PATH_MAX];
505 if (realpath(parent, canonical_parent) ==
nullptr) {
508 if (!
mdl_path_join(canonical_parent, leaf, destination, capacity)) {
663 const char*
const saved_out = args->
out;
672 args->
out = saved_out;
708 struct stat status = {};
709 if (lstat(sites, &status) == 0) {
710 if (S_ISDIR(status.st_mode) && !S_ISLNK(status.st_mode)) {
713 }
else if (errno != ENOENT) {
742 if (args->
out ==
nullptr) {
743 args->
out =
"downloads";
745 if (args->
attr ==
nullptr) {
746 args->
attr =
"data-src";
763 (void)
ra8_io_stream_puts(diagnostic,
"mdl: could not resolve portable command path\n");
767 (void)
ra8_io_stream_puts(diagnostic,
"mdl: could not resolve persistent cache path\n");
772 "mdl: WARNING: --ignore-robots set; "
773 "robots.txt will NOT be honoured\n");
802 const char*
const parts[] = {
"mdl: bad --format '",
804 "' (loose|cbz|cbt|cbt.gz|epub|jof|rabook)\n"};
928 if (policy_status != 0) {
929 return policy_status;
void main(void)
Secure fallback main entry point.
static int internal_prepare_args(mdl_args_t *args, mdl_cli_mode_t mode, ra8_io_stream_t *diagnostic)
Apply host defaults and canonicalize paths for the selected mode.
static export_arena_storage_t s_export_arena
Process-lifetime exporter workspace storage (zero heap).
static char s_cache_path[PATH_MAX]
Canonical host cache root retained through one command run.
static ra8_err_t internal_read_credential(int fd, const struct stat *before, uint8_t *destination, size_t length)
Read one already-open credential file and reject concurrent mutation.
static fw_fs_posix_state_t s_fs_posix
static char s_mode_path[PATH_MAX]
Canonical verify, pack, or descriptor path retained for one run.
static ra8_io_stream_posix_state_t s_diagnostic_posix
static ra8_err_t internal_resolve_pack_path(mdl_args_t *args)
Canonicalize the existing pack input directory.
static ra8_err_t internal_storage_init(void)
Bind the host root through the POSIX composition adapter.
static uint8_t s_ca_pem_input[k_ca_pem_input_capacity]
Process-lifetime, caller-owned custom CA PEM bytes.
static ra8_err_t internal_resolve_verify_path(mdl_args_t *args)
Canonicalize a verify target while preserving absent-leaf diagnostics.
credential_input_capacity_t
Explicit host credential input capacities.
@ k_cookie_input_capacity
Maximum cookie-file bytes.
@ k_ca_pem_input_capacity
Maximum custom CA bytes.
static ra8_io_stream_t s_output
Process stdout and stderr exposed through the portable byte-stream facade.
static bool internal_uses_output_path(mdl_cli_mode_t mode)
True when a validated mode consumes the canonical output root.
static ra8_err_t internal_output_init(void)
Bind process output descriptors and surface broken pipes as errors.
static ra8_err_t internal_resolve_config_path(mdl_args_t *args)
Resolve an optional host config argument into the root-bound namespace.
static mdl_app_context_t s_app
Process-lifetime bounded application state.
static char s_config_path[PATH_MAX]
Canonical host config path retained through one command run.
static ra8_io_stream_posix_state_t s_output_posix
Borrowed raw-descriptor backend state for process output streams.
static ra8_err_t internal_resolve_one_leaf(const char *input, char *destination, size_t capacity)
Resolve a library root, including one absent final directory.
static fw_fs_t s_fs
Host-selected filesystem facade and adapter state.
static ra8_err_t internal_prepare_credentials(const mdl_args_t *args, mdl_net_policy_t *policy, ra8_io_stream_t *diagnostic)
Snapshot optional host credential paths into portable byte policy.
static int internal_main_init(int argc, char **argv, mdl_args_t *a)
Bootstrap process I/O and workspace state, then parse arguments.
static directory_workspace_t s_fs_directory_work
One bounded host directory-cursor workspace.
static ra8_err_t internal_resolve_cache_path(mdl_args_t *args)
Resolve or derive the persistent per-host cache root.
static int internal_exit_from_error(ra8_err_t err, bool usage_error)
Convert one CLI or output status to the process exit convention.
static ra8_err_t internal_resolve_descriptor_path(mdl_args_t *args)
Select the canonical init-site descriptor directory.
static storage_workspace_t s_fs_file_work
One file and one transaction workspace for single-threaded storage.
static ra8_err_t internal_credential_capacity_error(ra8_io_stream_t *diagnostic, const char *option, size_t required, size_t supplied)
Emit an exact required-versus-supplied credential capacity error.
static uint8_t s_fs_io_buffer[k_mdl_storage_io_bytes]
Caller-owned streaming buffer shared by serial storage operations.
static storage_workspace_t s_fs_transaction_work
static ra8_io_stream_t s_diagnostic
static char s_library_path[PATH_MAX]
Canonical host library root retained through one command run.
static ra8_err_t internal_resolve_output_path(mdl_args_t *args)
Canonicalize the command output root with one absent leaf allowed.
static ra8_err_t internal_diagnostic(const char *message)
Write one fixed process diagnostic through the injected sink.
static ra8_err_t internal_load_credential(const char *path, uint8_t *destination, size_t supplied, size_t *required, size_t *used)
Load one path argument into exact caller-supplied credential storage.
static int internal_main_run(const mdl_args_t *a, mdl_cli_mode_t mode, mdl_format_t format, const mdl_run_opts_t *opts, const mdl_nums_t *nums)
Build the series-run plan, then dispatch and shut down cleanly.
static int internal_prepare_run_policy(const mdl_args_t *args, mdl_run_opts_t *opts, mdl_format_t *format)
Validate output format and attach bounded credential byte views.
static uint8_t s_cookie_input[k_cookie_input_capacity]
Process-lifetime, caller-owned cookie input bytes.
static bool internal_uses_cache_path(mdl_cli_mode_t mode)
Classify modes that perform persistent document caching.
#define O_NOFOLLOW
Zero fallback paired with explicit no-follow metadata validation.
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
Root-confined hosted POSIX adapter for fw_if_fs.
ra8_err_t fw_fs_posix_deinit(fw_fs_posix_state_t *state)
Close the root descriptor; no bound operation is valid afterward.
ra8_err_t fw_fs_posix_init(fw_fs_t *out, fw_fs_posix_state_t *state, const fw_fs_posix_cfg_t *cfg)
Open/configure a root-confined POSIX binding.
@ k_fw_fs_path_cap
Largest portable path including its NUL.
The downloader's portable application layer: one run mode, one call.
void mdl_app_bind(mdl_app_context_t *ctx)
Bind the caller-owned working set every application mode will use.
@ k_storage_work_bytes
Per-handle FS backend state.
@ k_export_arena_bytes
Host export scratch ceiling.
ra8_err_t mdl_cli_validate(const mdl_args_t *a, ra8_io_stream_t *diagnostic, mdl_cli_mode_t *mode)
Validate mode selection, required arguments, and per-mode options.
ra8_err_t mdl_cli_parse_nums(const mdl_args_t *a, ra8_io_stream_t *diagnostic, mdl_nums_t *n)
Strictly parse and validate every numeric CLI field.
mdl_cli_mode_t
Exactly one command mode selected by a valid invocation.
@ k_mdl_cli_mode_verify
Verify tracked state/pages/containers.
@ k_mdl_cli_mode_series
Download or update one configured series.
@ k_mdl_cli_mode_artifact
Download one verified HTTPS artifact.
@ k_mdl_cli_mode_page
Debug-download images from one page URL.
@ k_mdl_cli_mode_invalid
No valid primary mode.
@ k_mdl_cli_mode_search
Search a descriptor and optionally pick.
@ k_mdl_cli_mode_pack
Package a local page-image directory.
@ k_mdl_cli_mode_update_all
Update every tracked local series.
@ k_mdl_cli_mode_help
Print command help without running a mode.
@ k_mdl_cli_mode_list
List tracked local series.
@ k_mdl_cli_mode_remove
Remove one tracked local series.
@ k_mdl_cli_mode_browse
Browse a descriptor and optionally pick.
@ k_mdl_cli_mode_version
Print the program version and exit.
@ k_mdl_cli_mode_init_site
Generate a starter descriptor template.
mdl_run_opts_t mdl_cli_run_opts(const mdl_args_t *a)
Fold parsed args into the cross-cutting run options.
void mdl_cli_parse(int argc, char **argv, mdl_args_t *a)
Parse argv into a; numeric fields stay as strings for main.
ra8_err_t mdl_cli_usage(ra8_io_stream_t *diagnostic, const char *a0)
Write the complete usage block to an injected byte stream.
Private byte-stream helpers shared by mdl CLI units.
ra8_err_t priv_mdl_cli_reject_parts(ra8_io_stream_t *stream, const char *const *parts, size_t count)
Write a rejection diagnostic and return invalid-argument status.
const mdl_net_provider_t * priv_mdl_compose_net_provider(void)
The libcurl transport factory this form injects into every run.
mdl_series_run_t priv_mdl_compose_build_run(const mdl_args_t *a, mdl_format_t format, const mdl_run_opts_t *opts, const mdl_nums_t *n)
Translate validated argv state into one portable series-run value.
int priv_mdl_compose_dispatch(const mdl_args_t *a, mdl_cli_mode_t mode, mdl_format_t format, const mdl_run_opts_t *opts, const mdl_nums_t *nums, const mdl_series_run_t *run)
Route the one validated mode to its portable application entry point.
What this FORM injects, and how one parsed invocation reaches a mode.
mdl_format_t mdl_format_from_str(const char *s)
Map a --format string to a container kind.
void mdl_export_workspace_init(mdl_export_workspace_t *ws, void *data, size_t cap)
Bind an exporter arena without allocating memory.
bool priv_mdl_host_credential_stat_unchanged(const struct stat *before, const struct stat *after)
Compare identity, size, mode, and nanosecond mutation metadata.
Private host credential-snapshot hardening helpers.
ra8_err_t mdl_library_workspace_init(mdl_library_workspace_t *workspace, void *directory_workspace, uint32_t directory_workspace_bytes)
Bind caller-owned directory storage to a reusable library workspace.
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_init(mdl_storage_t *storage, const fw_fs_t *fs, void *file_workspace, uint32_t file_workspace_bytes, void *transaction_workspace, uint32_t transaction_workspace_bytes, uint8_t *io_buffer, uint32_t io_buffer_bytes)
Validate and retain one filesystem plus caller-owned workspaces.
@ k_mdl_storage_io_bytes
One bounded read/write chunk.
ra8_err_t priv_mdl_stream_text(ra8_err_t prior, ra8_io_stream_t *stream, const char *text)
Append text unless an earlier operation already failed.
ra8_err_t priv_mdl_stream_u64(ra8_err_t prior, ra8_io_stream_t *stream, uint64_t value)
Append one unsigned integer unless an earlier append failed.
Private bounded text helpers over the portable byte-stream contract.
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.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
@ k_ra8_err_access_denied
Operation refused because the target is protected against it.
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
@ k_ra8_err_comm_error
Generic communication error (use a more specific code when possible).
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
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.
char * strrchr(const char *s, int c)
Locate last occurrence of character in string.
ra8_err_t ra8_io_stream_puts(ra8_io_stream_t *s, const char *str)
Write a NUL-terminated string (without the NUL) to the bound sink.
Borrowed raw-descriptor backend for the portable byte-stream facade.
ra8_err_t ra8_io_stream_posix_init(ra8_io_stream_t *stream, ra8_io_stream_posix_state_t *state, int fd)
Bind a borrowed writable descriptor as a byte-stream sink.
Maximally aligned storage for one filesystem directory cursor.
uint8_t bytes[k_mdl_storage_io_bytes]
Opaque cursor state.
Naturally aligned, caller-owned exporter workspace storage.
uint8_t bytes[k_export_arena_bytes]
Bounded scratch bytes.
POSIX composition-root settings.
Caller-owned POSIX adapter state.
One complete composition-root filesystem binding.
The one bounded working set every application mode shares.
Parsed command-line options in string form (converted by main).
const char * verify_dir
–verify [DIR]: directory to verify.
const char * pack
–pack DIR: package an existing folder, no network.
const char * cfg
–config path.
const char * ca_file
–ca-file FILE: host composition CA input path.
bool ignore_robots
–ignore-robots: escape hatch (off by default).
const char * out
–out dir.
const char * cookie_file
–cookie-file FILE: host composition input path.
const char * cache_dir
–cache-dir: persistent per-host cache root.
const char * format
–format (cbz/cbt/cbt.gz/epub/jof/rabook).
Read-only caller-owned byte view retained by a network backend.
Session-wide security policy for the network backend.
mdl_net_bytes_t cookies
Newline-delimited input cookies.
mdl_net_bytes_t ca_pem
Complete PEM CA bundle, or an empty view.
The validated numeric CLI scalars, parsed once before any run mode.
Cross-cutting policy and injected transport threaded into every mode.
mdl_net_policy_t policy
Backend security policy.
const mdl_net_provider_t * net
Injected transport factory.
Parameters of one series download, as values rather than arguments.
Caller-owned state retained by one POSIX stream binding.
Caller-allocated byte-stream handle binding a sink to its context.
Maximally aligned storage for one filesystem backend handle.
uint8_t bytes[k_storage_work_bytes]
Opaque backend state.