-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2015 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
#include <sys/xattr.h>
-#include "strv.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "gcrypt-util.h"
+#include "hexdecoct.h"
+#include "import-util.h"
+#include "io-util.h"
#include "machine-pool.h"
+#include "parse-util.h"
+#include "pull-common.h"
#include "pull-job.h"
-
-/* Grow the /var/lib/machines directory after each 10MiB written */
-#define PULL_GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024))
+#include "string-util.h"
+#include "strv.h"
+#include "xattr-util.h"
PullJob* pull_job_unref(PullJob *j) {
if (!j)
safe_close(j->disk_fd);
- if (j->compressed == PULL_JOB_XZ)
- lzma_end(&j->xz);
- else if (j->compressed == PULL_JOB_GZIP)
- inflateEnd(&j->gzip);
- else if (j->compressed == PULL_JOB_BZIP2)
- BZ2_bzDecompressEnd(&j->bzip2);
+ import_compress_free(&j->compress);
if (j->checksum_context)
gcry_md_close(j->checksum_context);
free(j->payload);
free(j->checksum);
- free(j);
-
- return NULL;
+ return mfree(j);
}
static void pull_job_finish(PullJob *j, int ret) {
assert(j);
- if (j->state == PULL_JOB_DONE ||
- j->state == PULL_JOB_FAILED)
+ if (IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED))
return;
if (ret == 0) {
j->on_finished(j);
}
+static int pull_job_restart(PullJob *j) {
+ int r;
+ char *chksum_url = NULL;
+
+ r = import_url_change_last_component(j->url, "SHA256SUMS", &chksum_url);
+ if (r < 0)
+ return r;
+
+ free(j->url);
+ j->url = chksum_url;
+ j->state = PULL_JOB_INIT;
+ j->payload = mfree(j->payload);
+ j->payload_size = 0;
+ j->payload_allocated = 0;
+ j->written_compressed = 0;
+ j->written_uncompressed = 0;
+ j->written_since_last_grow = 0;
+
+ r = pull_job_begin(j);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
PullJob *j = NULL;
CURLcode code;
long status;
int r;
- if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &j) != CURLE_OK)
+ if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK)
return;
- if (!j || j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED)
+ if (!j || IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED))
return;
if (result != CURLE_OK) {
r = 0;
goto finish;
} else if (status >= 300) {
+ if (status == 404 && j->style == VERIFICATION_PER_FILE) {
+
+ /* retry pull job with SHA256SUMS file */
+ r = pull_job_restart(j);
+ if (r < 0)
+ goto finish;
+
+ code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
+ if (code != CURLE_OK) {
+ log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (status == 0) {
+ j->style = VERIFICATION_PER_DIRECTORY;
+ return;
+ }
+ }
+
log_error("HTTP request to %s failed with code %li.", j->url, status);
r = -EIO;
goto finish;
* sparse and we just seeked for the last part */
if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
- log_error_errno(errno, "Failed to truncate file: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to truncate file: %m");
goto finish;
}
pull_job_finish(j, r);
}
-static int pull_job_write_uncompressed(PullJob *j, void *p, size_t sz) {
+static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) {
+ PullJob *j = userdata;
ssize_t n;
assert(j);
if (sz <= 0)
return 0;
- if (j->written_uncompressed + sz < j->written_uncompressed) {
- log_error("File too large, overflow");
- return -EOVERFLOW;
- }
+ if (j->written_uncompressed + sz < j->written_uncompressed)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW),
+ "File too large, overflow");
- if (j->written_uncompressed + sz > j->uncompressed_max) {
- log_error("File overly large, refusing");
- return -EFBIG;
- }
+ if (j->written_uncompressed + sz > j->uncompressed_max)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
+ "File overly large, refusing");
if (j->disk_fd >= 0) {
- if (j->grow_machine_directory && j->written_since_last_grow >= PULL_GROW_INTERVAL_BYTES) {
+ if (j->grow_machine_directory && j->written_since_last_grow >= GROW_INTERVAL_BYTES) {
j->written_since_last_grow = 0;
grow_machine_directory();
}
if (j->allow_sparse)
n = sparse_write(j->disk_fd, p, sz, 64);
- else
+ else {
n = write(j->disk_fd, p, sz);
- if (n < 0) {
- log_error_errno(errno, "Failed to write file: %m");
- return -errno;
- }
- if ((size_t) n < sz) {
- log_error("Short write");
- return -EIO;
+ if (n < 0)
+ n = -errno;
}
+ if (n < 0)
+ return log_error_errno((int) n, "Failed to write file: %m");
+ if ((size_t) n < sz)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
} else {
if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz))
if (sz <= 0)
return 0;
- if (j->written_compressed + sz < j->written_compressed) {
- log_error("File too large, overflow");
- return -EOVERFLOW;
- }
+ if (j->written_compressed + sz < j->written_compressed)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
- if (j->written_compressed + sz > j->compressed_max) {
- log_error("File overly large, refusing.");
- return -EFBIG;
- }
+ if (j->written_compressed + sz > j->compressed_max)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing.");
if (j->content_length != (uint64_t) -1 &&
- j->written_compressed + sz > j->content_length) {
- log_error("Content length incorrect.");
- return -EFBIG;
- }
+ j->written_compressed + sz > j->content_length)
+ return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
+ "Content length incorrect.");
if (j->checksum_context)
gcry_md_write(j->checksum_context, p, sz);
- switch (j->compressed) {
-
- case PULL_JOB_UNCOMPRESSED:
- r = pull_job_write_uncompressed(j, p, sz);
- if (r < 0)
- return r;
-
- break;
-
- case PULL_JOB_XZ:
- j->xz.next_in = p;
- j->xz.avail_in = sz;
-
- while (j->xz.avail_in > 0) {
- uint8_t buffer[16 * 1024];
- lzma_ret lzr;
-
- j->xz.next_out = buffer;
- j->xz.avail_out = sizeof(buffer);
-
- lzr = lzma_code(&j->xz, LZMA_RUN);
- if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) {
- log_error("Decompression error.");
- return -EIO;
- }
-
- r = pull_job_write_uncompressed(j, buffer, sizeof(buffer) - j->xz.avail_out);
- if (r < 0)
- return r;
- }
-
- break;
-
- case PULL_JOB_GZIP:
- j->gzip.next_in = p;
- j->gzip.avail_in = sz;
-
- while (j->gzip.avail_in > 0) {
- uint8_t buffer[16 * 1024];
-
- j->gzip.next_out = buffer;
- j->gzip.avail_out = sizeof(buffer);
-
- r = inflate(&j->gzip, Z_NO_FLUSH);
- if (r != Z_OK && r != Z_STREAM_END) {
- log_error("Decompression error.");
- return -EIO;
- }
-
- r = pull_job_write_uncompressed(j, buffer, sizeof(buffer) - j->gzip.avail_out);
- if (r < 0)
- return r;
- }
-
- break;
-
- case PULL_JOB_BZIP2:
- j->bzip2.next_in = p;
- j->bzip2.avail_in = sz;
-
- while (j->bzip2.avail_in > 0) {
- uint8_t buffer[16 * 1024];
-
- j->bzip2.next_out = (char*) buffer;
- j->bzip2.avail_out = sizeof(buffer);
-
- r = BZ2_bzDecompress(&j->bzip2);
- if (r != BZ_OK && r != BZ_STREAM_END) {
- log_error("Decompression error.");
- return -EIO;
- }
-
- r = pull_job_write_uncompressed(j, buffer, sizeof(buffer) - j->bzip2.avail_out);
- if (r < 0)
- return r;
- }
-
- break;
-
- default:
- assert_not_reached("Unknown compression");
- }
+ r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j);
+ if (r < 0)
+ return r;
j->written_compressed += sz;
}
if (j->calc_checksum) {
- if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0) {
- log_error("Failed to initialize hash context.");
- return -EIO;
- }
+ initialize_libgcrypt(false);
+
+ if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to initialize hash context.");
}
return 0;
}
static int pull_job_detect_compression(PullJob *j) {
- static const uint8_t xz_signature[] = {
- 0xfd, '7', 'z', 'X', 'Z', 0x00
- };
- static const uint8_t gzip_signature[] = {
- 0x1f, 0x8b
- };
- static const uint8_t bzip2_signature[] = {
- 'B', 'Z', 'h'
- };
-
_cleanup_free_ uint8_t *stub = NULL;
size_t stub_size;
assert(j);
- if (j->payload_size < MAX3(sizeof(xz_signature),
- sizeof(gzip_signature),
- sizeof(bzip2_signature)))
+ r = import_uncompress_detect(&j->compress, j->payload, j->payload_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize compressor: %m");
+ if (r == 0)
return 0;
- if (memcmp(j->payload, xz_signature, sizeof(xz_signature)) == 0)
- j->compressed = PULL_JOB_XZ;
- else if (memcmp(j->payload, gzip_signature, sizeof(gzip_signature)) == 0)
- j->compressed = PULL_JOB_GZIP;
- else if (memcmp(j->payload, bzip2_signature, sizeof(bzip2_signature)) == 0)
- j->compressed = PULL_JOB_BZIP2;
- else
- j->compressed = PULL_JOB_UNCOMPRESSED;
-
- log_debug("Stream is XZ compressed: %s", yes_no(j->compressed == PULL_JOB_XZ));
- log_debug("Stream is GZIP compressed: %s", yes_no(j->compressed == PULL_JOB_GZIP));
- log_debug("Stream is BZIP2 compressed: %s", yes_no(j->compressed == PULL_JOB_BZIP2));
-
- if (j->compressed == PULL_JOB_XZ) {
- lzma_ret xzr;
-
- xzr = lzma_stream_decoder(&j->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
- if (xzr != LZMA_OK) {
- log_error("Failed to initialize XZ decoder.");
- return -EIO;
- }
- }
- if (j->compressed == PULL_JOB_GZIP) {
- r = inflateInit2(&j->gzip, 15+16);
- if (r != Z_OK) {
- log_error("Failed to initialize gzip decoder.");
- return -EIO;
- }
- }
- if (j->compressed == PULL_JOB_BZIP2) {
- r = BZ2_bzDecompressInit(&j->bzip2, 0, 0);
- if (r != BZ_OK) {
- log_error("Failed to initialize bzip2 decoder.");
- return -EIO;
- }
- }
+ log_debug("Stream is compressed: %s", import_compress_type_to_string(j->compress.type));
r = pull_job_open_disk(j);
if (r < 0)
assert(contents);
assert(j);
- if (j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) {
+ if (IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED)) {
r = -ESTALE;
goto fail;
}
j->glue = glue;
j->content_length = (uint64_t) -1;
j->start_usec = now(CLOCK_MONOTONIC);
- j->compressed_max = j->uncompressed_max = 8LLU * 1024LLU * 1024LLU * 1024LLU; /* 8GB */
+ j->compressed_max = j->uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU; /* 64GB safety limit */
+ j->style = VERIFICATION_STYLE_UNSET;
j->url = strdup(url);
if (!j->url)
return -ENOMEM;
- *ret = j;
- j = NULL;
+ *ret = TAKE_PTR(j);
return 0;
}