From bf51a545a4fd60ce27da74bed98c139157fb1984 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 16:48:24 +0100 Subject: [PATCH 1/9] pull-job: always implicitly NUL terminate downloaded payload stored in memory Just as a safety measure, let's always NUL terminate what we are downloading, maybe future code will parse it as string, and is sloppy by accident. (We have similar logic in read_full_file(), and I think it's a really good rule, to always implicitly NUL terminate blobs we acquire that might very well be used as text later on) --- src/import/pull-job.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/import/pull-job.c b/src/import/pull-job.c index b457c39ed1..6c7b328a0c 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -343,10 +343,10 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) } if (j->disk_fd < 0 || j->force_memory) { - if (!GREEDY_REALLOC(j->payload, j->payload_size + sz)) + if (!GREEDY_REALLOC(j->payload, j->payload_size + sz + 1)) return log_oom(); - memcpy(j->payload + j->payload_size, p, sz); + *((char*) mempcpy(j->payload + j->payload_size, p, sz)) = 0; j->payload_size += sz; } From cb1caab98ee495df63ade4095e96ea07eae05843 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 16:58:09 +0100 Subject: [PATCH 2/9] pull-job: optionally take expected content length and compare it with what we are downloading --- src/import/pull-job.c | 27 +++++++++++++++++++++++---- src/import/pull-job.h | 2 ++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 6c7b328a0c..9459a89cea 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -93,6 +93,7 @@ static int pull_job_restart(PullJob *j, const char *new_url) { j->etag_exists = false; j->mtime = 0; j->checksum = mfree(j->checksum); + j->expected_content_length = UINT64_MAX; curl_glue_remove_and_free(j->glue, j->curl); j->curl = NULL; @@ -114,6 +115,15 @@ static int pull_job_restart(PullJob *j, const char *new_url) { return 0; } +static uint64_t pull_job_content_length_effective(PullJob *j) { + assert(j); + + if (j->expected_content_length != UINT64_MAX) + return j->expected_content_length; + + return j->content_length; +} + void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { PullJob *j = NULL; char *scheme = NULL; @@ -214,8 +224,9 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { goto finish; } - if (j->content_length != UINT64_MAX && - j->content_length != j->written_compressed) { + uint64_t cl = pull_job_content_length_effective(j); + if (cl != UINT64_MAX && + cl != j->written_compressed) { r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Download truncated."); goto finish; } @@ -374,8 +385,9 @@ static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { if (j->written_compressed + sz > j->compressed_max) return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing."); - if (j->content_length != UINT64_MAX && - j->written_compressed + sz > j->content_length) + uint64_t cl = pull_job_content_length_effective(j); + if (cl != UINT64_MAX && + j->written_compressed + sz > cl) return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "Content length incorrect."); @@ -589,6 +601,12 @@ static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb goto fail; } + if (j->expected_content_length != UINT64_MAX && + j->expected_content_length != j->content_length) { + r = log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Content does not have expected size."); + goto fail; + } + log_info("Downloading %s for %s.", FORMAT_BYTES(j->content_length), j->url); } @@ -691,6 +709,7 @@ int pull_job_new( .url = TAKE_PTR(u), .offset = UINT64_MAX, .sync = true, + .expected_content_length = UINT64_MAX, }; *ret = TAKE_PTR(j); diff --git a/src/import/pull-job.h b/src/import/pull-job.h index b39e11929f..34156e9dd6 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -58,6 +58,8 @@ typedef struct PullJob { uint64_t uncompressed_max; uint64_t compressed_max; + uint64_t expected_content_length; + uint8_t *payload; size_t payload_size; From e825635af62552f51a9ce1fe2dd132c7be4bbf87 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 17:31:24 +0100 Subject: [PATCH 3/9] import: rework pull logic to store download digests in binary form rather than string We generally want to store data in parsed form, not formatted form, hence let's follow our own rules on this, and store the message digest as "struct iovec" rather than as string. This is generally more efficient and safer, simply because of case issues. --- src/import/pull-common.c | 21 +++++++++++++-------- src/import/pull-common.h | 4 ++-- src/import/pull-job.c | 35 ++++++++++++++++++++++------------- src/import/pull-job.h | 3 ++- src/import/pull-raw.c | 21 ++++++++++----------- src/import/pull-raw.h | 2 +- src/import/pull-tar.c | 17 ++++++++--------- src/import/pull-tar.h | 2 +- src/import/pull.c | 19 +++++++++---------- 9 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 08a5a6192d..61ac57e555 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -6,6 +6,7 @@ #include "dirent-util.h" #include "escape.h" #include "fd-util.h" +#include "hexdecoct.h" #include "io-util.h" #include "log.h" #include "memory-util.h" @@ -247,7 +248,7 @@ int pull_make_verification_jobs( PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, - const char *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */ + const struct iovec *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */ const char *url, CurlGlue *glue, PullJobFinished on_finished, @@ -267,7 +268,7 @@ int pull_make_verification_jobs( /* If verification is turned off, or if the checksum to validate is already specified we don't need * to download a checksum file or signature, hence shortcut things */ - if (verify == IMPORT_VERIFY_NO || checksum) { + if (verify == IMPORT_VERIFY_NO || iovec_is_set(checksum)) { *ret_checksum_job = *ret_signature_job = NULL; return 0; } @@ -351,7 +352,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { return 0; assert(job->calc_checksum); - assert(job->checksum); + assert(iovec_is_set(&job->checksum)); r = import_url_last_component(job->url, &fn); if (r < 0) @@ -365,6 +366,10 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { return log_error_errno(SYNTHETIC_ERRNO(ELOOP), "Cannot verify checksum/signature files via themselves."); + _cleanup_free_ char *cs = hexmem(job->checksum.iov_base, job->checksum.iov_len); + if (!cs) + return log_oom(); + const char *p = NULL; FOREACH_STRING(separator, " *", /* separator for binary mode */ @@ -372,7 +377,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { " " /* non-standard separator used by linuxcontainers.org */) { _cleanup_free_ char *line = NULL; - line = strjoin(job->checksum, separator, fn, "\n"); + line = strjoin(cs, separator, fn, "\n"); if (!line) return log_oom(); @@ -510,7 +515,7 @@ finish: } int pull_verify(ImportVerify verify, - const char *checksum, /* Verify with literal checksum */ + const struct iovec *checksum, /* Verify with literal checksum */ PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, @@ -543,9 +548,9 @@ int pull_verify(ImportVerify verify, assert(!verity_job); assert(main_job->calc_checksum); - assert(main_job->checksum); + assert(iovec_is_set(&main_job->checksum)); - if (!strcaseeq(checksum, main_job->checksum)) + if (iovec_memcmp(checksum, &main_job->checksum) != 0) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", main_job->url); @@ -566,7 +571,7 @@ int pull_verify(ImportVerify verify, verify_job = main_job; } else { assert(main_job->calc_checksum); - assert(main_job->checksum); + assert(iovec_is_set(&main_job->checksum)); assert(checksum_job); assert(checksum_job->state == PULL_JOB_DONE); diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 160c035a21..48cb6c5ec4 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -14,9 +14,9 @@ int pull_find_old_etags(const char *url, const char *root, int dt, const char *p int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, ImportVerify verify, CurlGlue *glue, PullJobOpenDisk on_open_disk, PullJobFinished on_finished, void *userdata); -int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); +int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const struct iovec *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); -int pull_verify(ImportVerify verify, const char *checksum, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job); +int pull_verify(ImportVerify verify, const struct iovec *checksum, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job); typedef enum VerificationStyle { VERIFICATION_PER_FILE, /* SUSE-style ".sha256" files with detached gpg signature */ diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 9459a89cea..57e9e95939 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -48,7 +48,7 @@ PullJob* pull_job_unref(PullJob *j) { free(j->etag); strv_free(j->old_etags); free(j->payload); - free(j->checksum); + iovec_done(&j->checksum); return mfree(j); } @@ -92,7 +92,7 @@ static int pull_job_restart(PullJob *j, const char *new_url) { j->etag = mfree(j->etag); j->etag_exists = false; j->mtime = 0; - j->checksum = mfree(j->checksum); + iovec_done(&j->checksum); j->expected_content_length = UINT64_MAX; curl_glue_remove_and_free(j->glue, j->curl); @@ -233,22 +233,31 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { if (j->checksum_ctx) { unsigned checksum_len; - uint8_t k[EVP_MAX_MD_SIZE]; - r = EVP_DigestFinal_ex(j->checksum_ctx, k, &checksum_len); - if (r == 0) { - r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); - goto finish; - } - assert(checksum_len <= sizeof k); - - j->checksum = hexmem(k, checksum_len); - if (!j->checksum) { + iovec_done(&j->checksum); + j->checksum.iov_base = malloc(EVP_MAX_MD_SIZE); + if (!j->checksum.iov_base) { r = log_oom(); goto finish; } - log_debug("SHA256 of %s is %s.", j->url, j->checksum); + r = EVP_DigestFinal_ex(j->checksum_ctx, j->checksum.iov_base, &checksum_len); + if (r == 0) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); + goto finish; + } + assert(checksum_len <= EVP_MAX_MD_SIZE); + j->checksum.iov_len = checksum_len; + + if (DEBUG_LOGGING) { + _cleanup_free_ char *h = hexmem(j->checksum.iov_base, j->checksum.iov_len); + if (!h) { + r = log_oom(); + goto finish; + } + + log_debug("%s of %s is %s.", EVP_MD_CTX_get0_name(j->checksum_ctx), j->url, h); + } } /* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 34156e9dd6..7437aab01a 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -78,7 +78,8 @@ typedef struct PullJob { bool calc_checksum; EVP_MD_CTX *checksum_ctx; - char *checksum; + struct iovec checksum; + bool sync; bool force_memory; } PullJob; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 6ba8f6cdc9..274bd3ec0d 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -68,7 +68,7 @@ typedef struct RawPull { char *verity_path; char *verity_temp_path; - char *checksum; + struct iovec checksum; } RawPull; RawPull* raw_pull_unref(RawPull *i) { @@ -99,7 +99,7 @@ RawPull* raw_pull_unref(RawPull *i) { free(i->verity_path); free(i->image_root); free(i->local); - free(i->checksum); + iovec_done(&i->checksum); return mfree(i); } @@ -585,7 +585,7 @@ static void raw_pull_job_on_finished(PullJob *j) { raw_pull_report_progress(i, RAW_VERIFYING); r = pull_verify(i->verify, - i->checksum, + &i->checksum, i->raw_job, i->checksum_job, i->signature_job, @@ -827,7 +827,7 @@ int raw_pull_start( uint64_t size_max, ImportFlags flags, ImportVerify verify, - const char *checksum) { + const struct iovec *checksum) { int r; @@ -835,11 +835,11 @@ int raw_pull_start( assert(url); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); + assert((verify < 0) || !iovec_is_set(checksum)); assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW)); assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT)); assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !(flags & IMPORT_DIRECT)); - assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !checksum); + assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !iovec_is_set(checksum)); if (!http_url_is_valid(url) && !file_url_is_valid(url)) return -EINVAL; @@ -854,9 +854,8 @@ int raw_pull_start( if (r < 0) return r; - r = free_and_strdup(&i->checksum, checksum); - if (r < 0) - return r; + if (!iovec_memdup(checksum, &i->checksum)) + return -ENOMEM; i->flags = flags; i->verify = verify; @@ -869,7 +868,7 @@ int raw_pull_start( i->raw_job->on_finished = raw_pull_job_on_finished; i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; - if (checksum) + if (iovec_is_set(checksum)) i->raw_job->calc_checksum = true; else if (verify != IMPORT_VERIFY_NO) { /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its @@ -899,7 +898,7 @@ int raw_pull_start( &i->checksum_job, &i->signature_job, verify, - i->checksum, + &i->checksum, url, i->glue, raw_pull_job_on_finished, diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h index c8126d4ff1..0dbd4dcd20 100644 --- a/src/import/pull-raw.h +++ b/src/import/pull-raw.h @@ -14,4 +14,4 @@ RawPull* raw_pull_unref(RawPull *pull); DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); -int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags, ImportVerify verify, const char *checksum); +int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags, ImportVerify verify, const struct iovec *checksum); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 23bdfb186a..a899e9a225 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -65,7 +65,7 @@ typedef struct TarPull { char *settings_path; char *settings_temp_path; - char *checksum; + struct iovec checksum; int tree_fd; int userns_fd; @@ -98,7 +98,7 @@ TarPull* tar_pull_unref(TarPull *i) { free(i->settings_path); free(i->image_root); free(i->local); - free(i->checksum); + iovec_done(&i->checksum); safe_close(i->tree_fd); safe_close(i->userns_fd); @@ -478,7 +478,7 @@ static void tar_pull_job_on_finished(PullJob *j) { clear_progress_bar(/* prefix= */ NULL); r = pull_verify(i->verify, - i->checksum, + &i->checksum, i->tar_job, i->checksum_job, i->signature_job, @@ -698,17 +698,17 @@ int tar_pull_start( const char *local, ImportFlags flags, ImportVerify verify, - const char *checksum) { + const struct iovec *checksum) { int r; assert(i); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); + assert((verify < 0) || !iovec_is_set(checksum)); assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_TAR)); assert(!(flags & IMPORT_PULL_SETTINGS) || !(flags & IMPORT_DIRECT)); - assert(!(flags & IMPORT_PULL_SETTINGS) || !checksum); + assert(!(flags & IMPORT_PULL_SETTINGS) || !iovec_is_set(checksum)); if (!http_url_is_valid(url) && !file_url_is_valid(url)) return -EINVAL; @@ -723,9 +723,8 @@ int tar_pull_start( if (r < 0) return r; - r = free_and_strdup(&i->checksum, checksum); - if (r < 0) - return r; + if (!iovec_memdup(checksum, &i->checksum)) + return -ENOMEM; i->flags = flags; i->verify = verify; diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h index 8f622dff3a..d2118272ff 100644 --- a/src/import/pull-tar.h +++ b/src/import/pull-tar.h @@ -14,4 +14,4 @@ TarPull* tar_pull_unref(TarPull *pull); DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref); -int tar_pull_start(TarPull *pull, const char *url, const char *local, ImportFlags flags, ImportVerify verify, const char *checksum); +int tar_pull_start(TarPull *pull, const char *url, const char *local, ImportFlags flags, ImportVerify verify, const struct iovec *checksum); diff --git a/src/import/pull.c b/src/import/pull.c index a1b9b940fd..a57c1c039d 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -15,6 +15,7 @@ #include "import-common.h" #include "import-util.h" #include "io-util.h" +#include "iovec-util.h" #include "log.h" #include "main-func.h" #include "parse-argument.h" @@ -32,11 +33,11 @@ static char *arg_image_root = NULL; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static ImportFlags arg_import_flags = IMPORT_PULL_SETTINGS | IMPORT_PULL_ROOTHASH | IMPORT_PULL_ROOTHASH_SIGNATURE | IMPORT_PULL_VERITY | IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC; static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX; -static char *arg_checksum = NULL; +static struct iovec arg_checksum = {}; static ImageClass arg_class = IMAGE_MACHINE; static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID; -STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep); +STATIC_DESTRUCTOR_REGISTER(arg_checksum, iovec_done); STATIC_DESTRUCTOR_REGISTER(arg_image_root, freep); static int normalize_local(const char *local, const char *url, char **ret) { @@ -162,7 +163,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { normalized, arg_import_flags & IMPORT_PULL_FLAGS_MASK_TAR, arg_verify, - arg_checksum); + &arg_checksum); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -231,7 +232,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { arg_size_max, arg_import_flags & IMPORT_PULL_FLAGS_MASK_RAW, arg_verify, - arg_checksum); + &arg_checksum); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -371,7 +372,6 @@ static int parse_argv(int argc, char *argv[]) { v = import_verify_from_string(optarg); if (v < 0) { _cleanup_free_ void *h = NULL; - char *hh; size_t n; /* If this is not a valid verification mode, maybe it's a literally specified @@ -385,11 +385,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2); - hh = hexmem(h, n); /* bring into canonical (lowercase) form */ - if (!hh) - return log_oom(); + iovec_done(&arg_checksum); + arg_checksum.iov_base = TAKE_PTR(h); + arg_checksum.iov_len = n; - free_and_replace(arg_checksum, hh); arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY); arg_verify = _IMPORT_VERIFY_INVALID; } else @@ -542,7 +541,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode."); - if (arg_checksum && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0) + if (iovec_is_set(&arg_checksum) && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded."); if (!arg_image_root) { From 6f16ab48930b7ce3649bb57cf39facac38341799 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 17:45:09 +0100 Subject: [PATCH 4/9] pull-job: optionally store an expected checksum in PullJob object --- src/import/pull-job.c | 8 ++++++++ src/import/pull-job.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 57e9e95939..98849a104c 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -49,6 +49,7 @@ PullJob* pull_job_unref(PullJob *j) { strv_free(j->old_etags); free(j->payload); iovec_done(&j->checksum); + iovec_done(&j->expected_checksum); return mfree(j); } @@ -93,6 +94,7 @@ static int pull_job_restart(PullJob *j, const char *new_url) { j->etag_exists = false; j->mtime = 0; iovec_done(&j->checksum); + iovec_done(&j->expected_checksum); j->expected_content_length = UINT64_MAX; curl_glue_remove_and_free(j->glue, j->curl); @@ -258,6 +260,12 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { log_debug("%s of %s is %s.", EVP_MD_CTX_get0_name(j->checksum_ctx), j->url, h); } + + if (iovec_is_set(&j->expected_checksum) && + iovec_memcmp(&j->checksum, &j->expected_checksum) != 0) { + r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Checksum of downloaded resource does not match expected checksum, yikes."); + goto finish; + } } /* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 7437aab01a..178cb8a30a 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -79,6 +79,7 @@ typedef struct PullJob { EVP_MD_CTX *checksum_ctx; struct iovec checksum; + struct iovec expected_checksum; bool sync; bool force_memory; From 5bba33f5e9251a4e97ceac085091244df5c9e711 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 22:24:01 +0100 Subject: [PATCH 5/9] pull: now that PullJob can verify expected digests, let's rely on it for tar/raw pulling Instead of authenticating the downloaded image explicity in the tar and in the raw downloader, we can now rely on the checksum checking in the generic PullJob code. Hence do so: drop tep the checksum field from TarPull and RawPull, and just initialized the ->expected_checksum in the relevant PullJob instead. --- src/import/pull-common.c | 30 ++++-------------------------- src/import/pull-common.h | 4 ++-- src/import/pull-raw.c | 15 +++++---------- src/import/pull-tar.c | 17 ++++++++--------- 4 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 61ac57e555..a58b8b8185 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -248,7 +248,6 @@ int pull_make_verification_jobs( PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, - const struct iovec *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */ const char *url, CurlGlue *glue, PullJobFinished on_finished, @@ -262,13 +261,13 @@ int pull_make_verification_jobs( assert(ret_signature_job); assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); assert(url); assert(glue); /* If verification is turned off, or if the checksum to validate is already specified we don't need * to download a checksum file or signature, hence shortcut things */ - if (verify == IMPORT_VERIFY_NO || iovec_is_set(checksum)) { + if (verify < 0 || /* verification already done (via literal checksum) */ + verify == IMPORT_VERIFY_NO) { /* verification turned off */ *ret_checksum_job = *ret_signature_job = NULL; return 0; } @@ -515,7 +514,6 @@ finish: } int pull_verify(ImportVerify verify, - const struct iovec *checksum, /* Verify with literal checksum */ PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, @@ -531,33 +529,13 @@ int pull_verify(ImportVerify verify, assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX); assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0); - assert((verify < 0) || !checksum); assert(main_job); assert(main_job->state == PULL_JOB_DONE); - if (verify == IMPORT_VERIFY_NO) /* verification turned off */ + if (verify < 0 || /* verification already done (via literal checksum) */ + verify == IMPORT_VERIFY_NO) /* verification turned off */ return 0; - if (checksum) { - /* Verification by literal checksum */ - assert(!checksum_job); - assert(!signature_job); - assert(!settings_job); - assert(!roothash_job); - assert(!roothash_signature_job); - assert(!verity_job); - - assert(main_job->calc_checksum); - assert(iovec_is_set(&main_job->checksum)); - - if (iovec_memcmp(checksum, &main_job->checksum) != 0) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", - main_job->url); - - return 0; - } - r = import_url_last_component(main_job->url, &fn); if (r < 0) return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url); diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 48cb6c5ec4..ffdabcb249 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -14,9 +14,9 @@ int pull_find_old_etags(const char *url, const char *root, int dt, const char *p int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, ImportVerify verify, CurlGlue *glue, PullJobOpenDisk on_open_disk, PullJobFinished on_finished, void *userdata); -int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const struct iovec *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); +int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); -int pull_verify(ImportVerify verify, const struct iovec *checksum, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job); +int pull_verify(ImportVerify verify, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job); typedef enum VerificationStyle { VERIFICATION_PER_FILE, /* SUSE-style ".sha256" files with detached gpg signature */ diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 274bd3ec0d..bf662c24b9 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -67,8 +67,6 @@ typedef struct RawPull { char *verity_path; char *verity_temp_path; - - struct iovec checksum; } RawPull; RawPull* raw_pull_unref(RawPull *i) { @@ -99,7 +97,6 @@ RawPull* raw_pull_unref(RawPull *i) { free(i->verity_path); free(i->image_root); free(i->local); - iovec_done(&i->checksum); return mfree(i); } @@ -585,7 +582,6 @@ static void raw_pull_job_on_finished(PullJob *j) { raw_pull_report_progress(i, RAW_VERIFYING); r = pull_verify(i->verify, - &i->checksum, i->raw_job, i->checksum_job, i->signature_job, @@ -854,9 +850,6 @@ int raw_pull_start( if (r < 0) return r; - if (!iovec_memdup(checksum, &i->checksum)) - return -ENOMEM; - i->flags = flags; i->verify = verify; @@ -868,9 +861,12 @@ int raw_pull_start( i->raw_job->on_finished = raw_pull_job_on_finished; i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; - if (iovec_is_set(checksum)) + if (iovec_is_set(checksum)) { + if (!iovec_memdup(checksum, &i->raw_job->expected_checksum)) + return -ENOMEM; + i->raw_job->calc_checksum = true; - else if (verify != IMPORT_VERIFY_NO) { + } else if (verify != IMPORT_VERIFY_NO) { /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its * signature, which we let gpg verify instead. */ @@ -898,7 +894,6 @@ int raw_pull_start( &i->checksum_job, &i->signature_job, verify, - &i->checksum, url, i->glue, raw_pull_job_on_finished, diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index a899e9a225..168ea08de8 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -65,8 +65,6 @@ typedef struct TarPull { char *settings_path; char *settings_temp_path; - struct iovec checksum; - int tree_fd; int userns_fd; @@ -98,7 +96,6 @@ TarPull* tar_pull_unref(TarPull *i) { free(i->settings_path); free(i->image_root); free(i->local); - iovec_done(&i->checksum); safe_close(i->tree_fd); safe_close(i->userns_fd); @@ -478,7 +475,6 @@ static void tar_pull_job_on_finished(PullJob *j) { clear_progress_bar(/* prefix= */ NULL); r = pull_verify(i->verify, - &i->checksum, i->tar_job, i->checksum_job, i->signature_job, @@ -723,9 +719,6 @@ int tar_pull_start( if (r < 0) return r; - if (!iovec_memdup(checksum, &i->checksum)) - return -ENOMEM; - i->flags = flags; i->verify = verify; @@ -736,7 +729,14 @@ int tar_pull_start( i->tar_job->on_finished = tar_pull_job_on_finished; i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar; - i->tar_job->calc_checksum = checksum || IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE); + + if (iovec_is_set(checksum)) { + if (!iovec_memdup(checksum, &i->tar_job->expected_checksum)) + return -ENOMEM; + + i->tar_job->calc_checksum = true; + } else + i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; if (!FLAGS_SET(flags, IMPORT_DIRECT)) { r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); @@ -749,7 +749,6 @@ int tar_pull_start( &i->checksum_job, &i->signature_job, verify, - checksum, url, i->glue, tar_pull_job_on_finished, From 7aa847933f8aaf3ce4bd6e22d3c59c5691c0cbe3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 22:27:33 +0100 Subject: [PATCH 6/9] pull: use ASSERT_PTR() to shorten code a tiny bit --- src/import/pull-raw.c | 5 +---- src/import/pull-tar.c | 9 ++------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index bf662c24b9..ef10745ba8 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -500,13 +500,10 @@ static int raw_pull_rename_auxiliary_file( } static void raw_pull_job_on_finished(PullJob *j) { - RawPull *i; int r; assert(j); - assert(j->userdata); - - i = j->userdata; + RawPull *i = ASSERT_PTR(j->userdata); if (j->error != 0) { /* Only the main job and the checksum job are fatal if they fail. The other fails are just diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 168ea08de8..05b58f7976 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -399,13 +399,10 @@ static bool tar_pull_is_done(TarPull *i) { } static void tar_pull_job_on_finished(PullJob *j) { - TarPull *i; int r; assert(j); - assert(j->userdata); - - i = j->userdata; + TarPull *i = ASSERT_PTR(j->userdata); if (j->error != 0) { clear_progress_bar(/* prefix= */ NULL); @@ -582,13 +579,11 @@ finish: static int tar_pull_job_on_open_disk_tar(PullJob *j) { const char *where; - TarPull *i; int r; assert(j); - assert(j->userdata); - i = j->userdata; + TarPull *i = ASSERT_PTR(j->userdata); assert(i->tar_job == j); assert(!pidref_is_set(&i->tar_pid)); assert(i->tree_fd < 0); From f56531c25ccd0e8cf8658e49d79b6a8cae3ccba9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Nov 2025 22:28:08 +0100 Subject: [PATCH 7/9] pull: there's no need to keep the downloaded image in memory, except for the sha256sums/gpg file This seems to be a mistake, in place since the first commit: we only want the downloaded data in memory if this is a sha256sums or gpg file, which we need to prorcess ourselves. --- src/import/pull-raw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index ef10745ba8..80ef38f50a 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -872,8 +872,8 @@ int raw_pull_start( return r; i->raw_job->calc_checksum = r; - i->raw_job->force_memory = true; /* make sure this is both written to disk if that's - * requested and into memory, since we need to verify it */ + i->raw_job->force_memory = !r; /* make sure this is both written to disk if that's + * requested and into memory, since we need to verify it */ } if (size_max != UINT64_MAX) From 33f49312f8c02d1692df7eb2f73cc4b598c96fda Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 6 Nov 2025 09:32:22 +0100 Subject: [PATCH 8/9] pull-job: use http_status_etag_exists() at a second place --- src/import/pull-job.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 98849a104c..1945106099 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -20,6 +20,16 @@ #include "time-util.h" #include "xattr-util.h" +static int http_status_ok(CURLcode status) { + /* Consider all HTTP status code in the 2xx range as OK */ + return status >= 200 && status <= 299; +} + +static int http_status_etag_exists(CURLcode status) { + /* This one is special, it's triggered by our etag mgmt logic */ + return status == 304; +} + void pull_job_close_disk_fd(PullJob *j) { if (!j) return; @@ -178,7 +188,7 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { goto finish; } - if (status == 304) { + if (http_status_etag_exists(status)) { log_info("Image already downloaded. Skipping download."); j->etag_exists = true; r = 0; @@ -545,16 +555,6 @@ fail: return 0; } -static int http_status_ok(CURLcode status) { - /* Consider all HTTP status code in the 2xx range as OK */ - return status >= 200 && status <= 299; -} - -static int http_status_etag_exists(CURLcode status) { - /* This one is special, it's triggered by our etag mgmt logic */ - return status == 304; -} - static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) { _cleanup_free_ char *length = NULL, *last_modified = NULL, *etag = NULL; size_t sz = size * nmemb; From 729e1f24c55b6bc6cfa1042cfbbaa36e48d7f40f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Nov 2025 13:52:38 +0100 Subject: [PATCH 9/9] pull-job: port .payload field to struct iovec struct iovec is really how we should encode any form of arbitrary blob data --- src/import/pull-common.c | 36 ++++++++++++++------------- src/import/pull-job.c | 54 ++++++++++++++++------------------------ src/import/pull-job.h | 3 +-- 3 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/import/pull-common.c b/src/import/pull-common.c index a58b8b8185..12d9736725 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -380,8 +380,8 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { if (!line) return log_oom(); - p = memmem_safe(checksum_job->payload, - checksum_job->payload_size, + p = memmem_safe(checksum_job->payload.iov_base, + checksum_job->payload.iov_len, line, strlen(line)); if (p) @@ -389,7 +389,7 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { } /* Only counts if found at beginning of a line */ - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) + if (!p || (p != (char*) checksum_job->payload.iov_base && p[-1] != '\n')) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn); @@ -398,8 +398,8 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { } static int verify_gpg( - const void *payload, size_t payload_size, - const void *signature, size_t signature_size) { + const struct iovec *payload, + const struct iovec *signature) { _cleanup_close_pair_ int gpg_pipe[2] = EBADF_PAIR; _cleanup_(rm_rf_physical_and_freep) char *gpg_home = NULL; @@ -407,21 +407,21 @@ static int verify_gpg( _cleanup_(sigkill_waitp) pid_t pid = 0; int r; - assert(payload || payload_size == 0); - assert(signature || signature_size == 0); + assert(iovec_is_valid(payload)); + assert(iovec_is_valid(signature)); r = pipe2(gpg_pipe, O_CLOEXEC); if (r < 0) return log_error_errno(errno, "Failed to create pipe for gpg: %m"); - if (signature_size > 0) { + if (iovec_is_set(signature)) { _cleanup_close_ int sig_file = -EBADF; sig_file = mkostemp(sig_file_path, O_RDWR); if (sig_file < 0) return log_error_errno(errno, "Failed to create temporary file: %m"); - r = loop_write(sig_file, signature, signature_size); + r = loop_write(sig_file, signature->iov_base, signature->iov_len); if (r < 0) { log_error_errno(r, "Failed to write to temporary file: %m"); goto finish; @@ -487,10 +487,12 @@ static int verify_gpg( gpg_pipe[0] = safe_close(gpg_pipe[0]); - r = loop_write(gpg_pipe[1], payload, payload_size); - if (r < 0) { - log_error_errno(r, "Failed to write to pipe: %m"); - goto finish; + if (iovec_is_set(payload)) { + r = loop_write(gpg_pipe[1], payload->iov_base, payload->iov_len); + if (r < 0) { + log_error_errno(r, "Failed to write to pipe: %m"); + goto finish; + } } gpg_pipe[1] = safe_close(gpg_pipe[1]); @@ -507,7 +509,7 @@ static int verify_gpg( } finish: - if (signature_size > 0) + if (iovec_is_set(signature)) (void) unlink(sig_file_path); return r; @@ -553,7 +555,7 @@ int pull_verify(ImportVerify verify, assert(checksum_job); assert(checksum_job->state == PULL_JOB_DONE); - if (!checksum_job->payload || checksum_job->payload_size <= 0) + if (!iovec_is_set(&checksum_job->payload)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Checksum is empty, cannot verify."); @@ -579,11 +581,11 @@ int pull_verify(ImportVerify verify, assert(signature_job); assert(signature_job->state == PULL_JOB_DONE); - if (!signature_job->payload || signature_job->payload_size <= 0) + if (!iovec_is_set(&signature_job->payload)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Signature is empty, cannot verify."); - return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size); + return verify_gpg(&verify_job->payload, &signature_job->payload); } int verification_style_from_url(const char *url, VerificationStyle *ret) { diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 1945106099..621a90a86e 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -57,7 +57,7 @@ PullJob* pull_job_unref(PullJob *j) { free(j->url); free(j->etag); strv_free(j->old_etags); - free(j->payload); + iovec_done(&j->payload); iovec_done(&j->checksum); iovec_done(&j->expected_checksum); @@ -95,8 +95,7 @@ static int pull_job_restart(PullJob *j, const char *new_url) { j->state = PULL_JOB_INIT; j->error = 0; - j->payload = mfree(j->payload); - j->payload_size = 0; + iovec_done(&j->payload); j->written_compressed = 0; j->written_uncompressed = 0; j->content_length = UINT64_MAX; @@ -381,11 +380,14 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) } if (j->disk_fd < 0 || j->force_memory) { - if (!GREEDY_REALLOC(j->payload, j->payload_size + sz + 1)) + uint8_t *a = j->payload.iov_base; + + if (!GREEDY_REALLOC(a, j->payload.iov_len + sz + 1)) return log_oom(); - *((char*) mempcpy(j->payload + j->payload_size, p, sz)) = 0; - j->payload_size += sz; + *((uint8_t*) mempcpy(a + j->payload.iov_len, p, sz)) = 0; + j->payload.iov_base = a; + j->payload.iov_len += sz; } j->written_uncompressed += sz; @@ -397,39 +399,39 @@ finish: return 0; } -static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { +static int pull_job_write_compressed(PullJob *j, const struct iovec *data) { int r; assert(j); - assert(p); + assert(iovec_is_valid(data)); - if (sz <= 0) + if (!iovec_is_set(data)) return 0; - if (j->written_compressed + sz < j->written_compressed) + if (j->written_compressed + data->iov_len < j->written_compressed) return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow"); - if (j->written_compressed + sz > j->compressed_max) + if (j->written_compressed + data->iov_len > j->compressed_max) return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing."); uint64_t cl = pull_job_content_length_effective(j); if (cl != UINT64_MAX && - j->written_compressed + sz > cl) + j->written_compressed + data->iov_len > cl) return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "Content length incorrect."); if (j->checksum_ctx) { - r = EVP_DigestUpdate(j->checksum_ctx, p, sz); + r = EVP_DigestUpdate(j->checksum_ctx, data->iov_base, data->iov_len); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Could not hash chunk."); } - r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j); + r = import_uncompress(&j->compress, data->iov_base, data->iov_len, pull_job_write_uncompressed, j); if (r < 0) return r; - j->written_compressed += sz; + j->written_compressed += data->iov_len; return 0; } @@ -470,14 +472,11 @@ static int pull_job_open_disk(PullJob *j) { } static int pull_job_detect_compression(PullJob *j) { - _cleanup_free_ uint8_t *stub = NULL; - size_t stub_size; - int r; assert(j); - r = import_uncompress_detect(&j->compress, j->payload, j->payload_size); + r = import_uncompress_detect(&j->compress, j->payload.iov_base, j->payload.iov_len); if (r < 0) return log_error_errno(r, "Failed to initialize compressor: %m"); if (r == 0) @@ -490,15 +489,11 @@ static int pull_job_detect_compression(PullJob *j) { return r; /* Now, take the payload we read so far, and decompress it */ - stub = j->payload; - stub_size = j->payload_size; - - j->payload = NULL; - j->payload_size = 0; + _cleanup_(iovec_done) struct iovec stub = TAKE_STRUCT(j->payload); j->state = PULL_JOB_RUNNING; - r = pull_job_write_compressed(j, stub, stub_size); + r = pull_job_write_compressed(j, &stub); if (r < 0) return r; @@ -516,15 +511,11 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, case PULL_JOB_ANALYZING: /* Let's first check what it actually is */ - - if (!GREEDY_REALLOC(j->payload, j->payload_size + sz)) { + if (!iovec_append(&j->payload, &IOVEC_MAKE(contents, sz))) { r = log_oom(); goto fail; } - memcpy(j->payload + j->payload_size, contents, sz); - j->payload_size += sz; - r = pull_job_detect_compression(j); if (r < 0) goto fail; @@ -532,8 +523,7 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, break; case PULL_JOB_RUNNING: - - r = pull_job_write_compressed(j, contents, sz); + r = pull_job_write_compressed(j, &IOVEC_MAKE(contents, sz)); if (r < 0) goto fail; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 178cb8a30a..b074238174 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -60,8 +60,7 @@ typedef struct PullJob { uint64_t expected_content_length; - uint8_t *payload; - size_t payload_size; + struct iovec payload; int disk_fd; bool close_disk_fd;