import-job.c revision 7079cfeffb6d520f20ddff53fd78467e72e6cc94
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering/***
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering This file is part of systemd.
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering Copyright 2015 Lennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering systemd is free software; you can redistribute it and/or modify it
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering under the terms of the GNU Lesser General Public License as published by
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering (at your option) any later version.
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering systemd is distributed in the hope that it will be useful, but
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering Lesser General Public License for more details.
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering You should have received a copy of the GNU Lesser General Public License
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering***/
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering#include <sys/xattr.h>
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering#include "strv.h"
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering#include "import-job.h"
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart PoetteringImportJob* import_job_unref(ImportJob *j) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (!j)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return NULL;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering curl_glue_remove_and_free(j->glue, j->curl);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering curl_slist_free_all(j->request_header);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering safe_close(j->disk_fd);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->compressed == IMPORT_JOB_XZ)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering lzma_end(&j->xz);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering else if (j->compressed == IMPORT_JOB_GZIP)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering inflateEnd(&j->gzip);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering else if (j->compressed == IMPORT_JOB_BZIP2)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering BZ2_bzDecompressEnd(&j->bzip2);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->checksum_context)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering gcry_md_close(j->checksum_context);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering free(j->url);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering free(j->etag);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering strv_free(j->old_etags);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering free(j->payload);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering free(j->checksum);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering free(j);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering return NULL;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering}
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poetteringstatic void import_job_finish(ImportJob *j, int ret) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering assert(j);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->state == IMPORT_JOB_DONE ||
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->state == IMPORT_JOB_FAILED)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering return;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (ret == 0) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->state = IMPORT_JOB_DONE;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->progress_percent = 100;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_info("Download of %s complete.", j->url);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering } else {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->state = IMPORT_JOB_FAILED;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->error = ret;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering }
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->on_finished)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->on_finished(j);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering}
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poetteringvoid import_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering ImportJob *j = NULL;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering CURLcode code;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering long status;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering int r;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &j) != CURLE_OK)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering return;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (!j || j->state == IMPORT_JOB_DONE || j->state == IMPORT_JOB_FAILED)
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering return;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (result != CURLE_OK) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_error("Transfer failed: %s", curl_easy_strerror(result));
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = -EIO;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering }
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (code != CURLE_OK) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = -EIO;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering } else if (status == 304) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_info("Image already downloaded. Skipping download.");
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->etag_exists = true;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = 0;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering } else if (status >= 300) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_error("HTTP request to %s failed with code %li.", j->url, status);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = -EIO;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering } else if (status < 200) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_error("HTTP request to %s finished with unexpected code %li.", j->url, status);
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = -EIO;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering }
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->state != IMPORT_JOB_RUNNING) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_error("Premature connection termination.");
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = -EIO;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering }
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->content_length != (uint64_t) -1 &&
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering j->content_length != j->written_compressed) {
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering log_error("Download truncated.");
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering r = -EIO;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering goto finish;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering }
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->checksum_context) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering uint8_t *k;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (!k) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("Failed to get checksum.");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = -EIO;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering goto finish;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->checksum = hexmem(k, gcry_md_get_algo_dlen(GCRY_MD_SHA256));
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (!j->checksum) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = log_oom();
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering goto finish;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_debug("SHA256 of %s is %s.", j->url, j->checksum);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->disk_fd >= 0 && j->allow_sparse) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering /* Make sure the file size is right, in case the file was
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering * sparse and we just seeked for the last part */
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error_errno(errno, "Failed to truncate file: %m");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = -errno;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering goto finish;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
547973dea7abd6c124ff6c79fe2bbe322a7314aeLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->etag)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->url)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->mtime != 0) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering struct timespec ut[2];
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering timespec_store(&ut[0], j->mtime);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering ut[1] = ut[0];
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering (void) futimens(j->disk_fd, ut);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering (void) fd_setcrtime(j->disk_fd, j->mtime);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = 0;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poetteringfinish:
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering import_job_finish(j, r);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering}
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poetteringstatic int import_job_write_uncompressed(ImportJob *j, void *p, size_t sz) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering ssize_t n;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering assert(j);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering assert(p);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (sz <= 0)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return 0;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->written_uncompressed + sz < j->written_uncompressed) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("File too large, overflow");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EOVERFLOW;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->written_uncompressed + sz > j->uncompressed_max) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("File overly large, refusing");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EFBIG;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->disk_fd >= 0) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->allow_sparse)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering n = sparse_write(j->disk_fd, p, sz, 64);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering else
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering n = write(j->disk_fd, p, sz);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (n < 0) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error_errno(errno, "Failed to write file: %m");
2a326321594f752b73a5aec0eb73e5bf59f76b3cLennart Poettering return -errno;
547973dea7abd6c124ff6c79fe2bbe322a7314aeLennart Poettering }
547973dea7abd6c124ff6c79fe2bbe322a7314aeLennart Poettering if ((size_t) n < sz) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("Short write");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EIO;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering } else {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz))
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return log_oom();
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering memcpy(j->payload + j->payload_size, p, sz);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->payload_size += sz;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->written_uncompressed += sz;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return 0;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering}
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poetteringstatic int import_job_write_compressed(ImportJob *j, void *p, size_t sz) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering int r;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering assert(j);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering assert(p);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (sz <= 0)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return 0;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->written_compressed + sz < j->written_compressed) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("File too large, overflow");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EOVERFLOW;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->written_compressed + sz > j->compressed_max) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("File overly large, refusing.");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EFBIG;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->content_length != (uint64_t) -1 &&
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->written_compressed + sz > j->content_length) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("Content length incorrect.");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EFBIG;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (j->checksum_context)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering gcry_md_write(j->checksum_context, p, sz);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering switch (j->compressed) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering case IMPORT_JOB_UNCOMPRESSED:
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = import_job_write_uncompressed(j, p, sz);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (r < 0)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return r;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering break;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering case IMPORT_JOB_XZ:
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->xz.next_in = p;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->xz.avail_in = sz;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering while (j->xz.avail_in > 0) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering uint8_t buffer[16 * 1024];
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering lzma_ret lzr;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->xz.next_out = buffer;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->xz.avail_out = sizeof(buffer);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering lzr = lzma_code(&j->xz, LZMA_RUN);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("Decompression error.");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EIO;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = import_job_write_uncompressed(j, buffer, sizeof(buffer) - j->xz.avail_out);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (r < 0)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return r;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering break;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering case IMPORT_JOB_GZIP:
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->gzip.next_in = p;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->gzip.avail_in = sz;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering while (j->gzip.avail_in > 0) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering uint8_t buffer[16 * 1024];
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->gzip.next_out = buffer;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering j->gzip.avail_out = sizeof(buffer);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = inflate(&j->gzip, Z_NO_FLUSH);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (r != Z_OK && r != Z_STREAM_END) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering log_error("Decompression error.");
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return -EIO;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering r = import_job_write_uncompressed(j, buffer, sizeof(buffer) - j->gzip.avail_out);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (r < 0)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return r;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering break;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering case IMPORT_JOB_BZIP2:
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering j->bzip2.next_in = p;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering j->bzip2.avail_in = sz;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering while (j->bzip2.avail_in > 0) {
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering uint8_t buffer[16 * 1024];
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering j->bzip2.next_out = (char*) buffer;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering j->bzip2.avail_out = sizeof(buffer);
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering r = BZ2_bzDecompress(&j->bzip2);
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering if (r != BZ_OK && r != BZ_STREAM_END) {
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering log_error("Decompression error.");
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering return -EIO;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering }
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering r = import_job_write_uncompressed(j, buffer, sizeof(buffer) - j->bzip2.avail_out);
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering if (r < 0)
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering return r;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering }
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering break;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering default:
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering assert_not_reached("Unknown compression");
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering }
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering j->written_compressed += sz;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering return 0;
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering}
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poetteringstatic int import_job_open_disk(ImportJob *j) {
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering int r;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering assert(j);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering if (j->on_open_disk) {
72667f0890372a952a7c5b8cc498ec3cf9440973Lennart Poettering r = j->on_open_disk(j);
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering if (r < 0)
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering return r;
2b442ac87838be7c326c984d8751c96dee7258abLennart Poettering }
if (j->disk_fd >= 0) {
/* Check if we can do sparse files */
if (lseek(j->disk_fd, SEEK_SET, 0) == 0)
j->allow_sparse = true;
else {
if (errno != ESPIPE)
return log_error_errno(errno, "Failed to seek on file descriptor: %m");
j->allow_sparse = false;
}
}
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;
}
}
return 0;
}
static int import_job_detect_compression(ImportJob *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;
int r;
assert(j);
if (j->payload_size < MAX3(sizeof(xz_signature),
sizeof(gzip_signature),
sizeof(bzip2_signature)))
return 0;
if (memcmp(j->payload, xz_signature, sizeof(xz_signature)) == 0)
j->compressed = IMPORT_JOB_XZ;
else if (memcmp(j->payload, gzip_signature, sizeof(gzip_signature)) == 0)
j->compressed = IMPORT_JOB_GZIP;
else if (memcmp(j->payload, bzip2_signature, sizeof(bzip2_signature)) == 0)
j->compressed = IMPORT_JOB_BZIP2;
else
j->compressed = IMPORT_JOB_UNCOMPRESSED;
log_debug("Stream is XZ compressed: %s", yes_no(j->compressed == IMPORT_JOB_XZ));
log_debug("Stream is GZIP compressed: %s", yes_no(j->compressed == IMPORT_JOB_GZIP));
log_debug("Stream is BZIP2 compressed: %s", yes_no(j->compressed == IMPORT_JOB_BZIP2));
if (j->compressed == IMPORT_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 == IMPORT_JOB_GZIP) {
r = inflateInit2(&j->gzip, 15+16);
if (r != Z_OK) {
log_error("Failed to initialize gzip decoder.");
return -EIO;
}
}
if (j->compressed == IMPORT_JOB_BZIP2) {
r = BZ2_bzDecompressInit(&j->bzip2, 0, 0);
if (r != BZ_OK) {
log_error("Failed to initialize bzip2 decoder.");
return -EIO;
}
}
r = import_job_open_disk(j);
if (r < 0)
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;
j->payload_allocated = 0;
j->state = IMPORT_JOB_RUNNING;
r = import_job_write_compressed(j, stub, stub_size);
if (r < 0)
return r;
return 0;
}
static size_t import_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
ImportJob *j = userdata;
size_t sz = size * nmemb;
int r;
assert(contents);
assert(j);
switch (j->state) {
case IMPORT_JOB_ANALYZING:
/* Let's first check what it actually is */
if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) {
r = log_oom();
goto fail;
}
memcpy(j->payload + j->payload_size, contents, sz);
j->payload_size += sz;
r = import_job_detect_compression(j);
if (r < 0)
goto fail;
break;
case IMPORT_JOB_RUNNING:
r = import_job_write_compressed(j, contents, sz);
if (r < 0)
goto fail;
break;
case IMPORT_JOB_DONE:
case IMPORT_JOB_FAILED:
r = -ESTALE;
goto fail;
default:
assert_not_reached("Impossible state.");
}
return sz;
fail:
import_job_finish(j, r);
return 0;
}
static size_t import_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
ImportJob *j = userdata;
size_t sz = size * nmemb;
_cleanup_free_ char *length = NULL, *last_modified = NULL;
char *etag;
int r;
assert(contents);
assert(j);
if (j->state == IMPORT_JOB_DONE || j->state == IMPORT_JOB_FAILED) {
r = -ESTALE;
goto fail;
}
assert(j->state == IMPORT_JOB_ANALYZING);
r = curl_header_strdup(contents, sz, "ETag:", &etag);
if (r < 0) {
log_oom();
goto fail;
}
if (r > 0) {
free(j->etag);
j->etag = etag;
if (strv_contains(j->old_etags, j->etag)) {
log_info("Image already downloaded. Skipping download.");
j->etag_exists = true;
import_job_finish(j, 0);
return sz;
}
return sz;
}
r = curl_header_strdup(contents, sz, "Content-Length:", &length);
if (r < 0) {
log_oom();
goto fail;
}
if (r > 0) {
(void) safe_atou64(length, &j->content_length);
if (j->content_length != (uint64_t) -1) {
char bytes[FORMAT_BYTES_MAX];
if (j->content_length > j->compressed_max) {
log_error("Content too large.");
r = -EFBIG;
goto fail;
}
log_info("Downloading %s for %s.", format_bytes(bytes, sizeof(bytes), j->content_length), j->url);
}
return sz;
}
r = curl_header_strdup(contents, sz, "Last-Modified:", &last_modified);
if (r < 0) {
log_oom();
goto fail;
}
if (r > 0) {
(void) curl_parse_http_time(last_modified, &j->mtime);
return sz;
}
if (j->on_header) {
r = j->on_header(j, contents, sz);
if (r < 0)
goto fail;
}
return sz;
fail:
import_job_finish(j, r);
return 0;
}
static int import_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
ImportJob *j = userdata;
unsigned percent;
usec_t n;
assert(j);
if (dltotal <= 0)
return 0;
percent = ((100 * dlnow) / dltotal);
n = now(CLOCK_MONOTONIC);
if (n > j->last_status_usec + USEC_PER_SEC &&
percent != j->progress_percent &&
dlnow < dltotal) {
char buf[FORMAT_TIMESPAN_MAX];
if (n - j->start_usec > USEC_PER_SEC && dlnow > 0) {
char y[FORMAT_BYTES_MAX];
usec_t left, done;
done = n - j->start_usec;
left = (usec_t) (((double) done * (double) dltotal) / dlnow) - done;
log_info("Got %u%% of %s. %s left at %s/s.",
percent,
j->url,
format_timespan(buf, sizeof(buf), left, USEC_PER_SEC),
format_bytes(y, sizeof(y), (uint64_t) ((double) dlnow / ((double) done / (double) USEC_PER_SEC))));
} else
log_info("Got %u%% of %s.", percent, j->url);
j->progress_percent = percent;
j->last_status_usec = n;
if (j->on_progress)
j->on_progress(j);
}
return 0;
}
int import_job_new(ImportJob **ret, const char *url, CurlGlue *glue, void *userdata) {
_cleanup_(import_job_unrefp) ImportJob *j = NULL;
assert(url);
assert(glue);
assert(ret);
j = new0(ImportJob, 1);
if (!j)
return -ENOMEM;
j->state = IMPORT_JOB_INIT;
j->disk_fd = -1;
j->userdata = userdata;
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->url = strdup(url);
if (!j->url)
return -ENOMEM;
*ret = j;
j = NULL;
return 0;
}
int import_job_begin(ImportJob *j) {
int r;
assert(j);
if (j->state != IMPORT_JOB_INIT)
return -EBUSY;
r = curl_glue_make(&j->curl, j->url, j);
if (r < 0)
return r;
if (!strv_isempty(j->old_etags)) {
_cleanup_free_ char *cc = NULL, *hdr = NULL;
cc = strv_join(j->old_etags, ", ");
if (!cc)
return -ENOMEM;
hdr = strappend("If-None-Match: ", cc);
if (!hdr)
return -ENOMEM;
if (!j->request_header) {
j->request_header = curl_slist_new(hdr, NULL);
if (!j->request_header)
return -ENOMEM;
} else {
struct curl_slist *l;
l = curl_slist_append(j->request_header, hdr);
if (!l)
return -ENOMEM;
j->request_header = l;
}
}
if (j->request_header) {
if (curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK)
return -EIO;
}
if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, import_job_write_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, import_job_header_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, import_job_progress_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK)
return -EIO;
r = curl_glue_add(j->glue, j->curl);
if (r < 0)
return r;
j->state = IMPORT_JOB_ANALYZING;
return 0;
}