import-raw.c revision 0716faad4a846d5f3cdce4bf37648d3254b9332f
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek This file is part of systemd.
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek Copyright 2014 Lennart Poettering
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek (at your option) any later version.
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmektypedef struct RawImportFile RawImportFile;
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek struct curl_slist *request_header;
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek raw_import_on_finished on_finished;
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek#define FILENAME_ESCAPE "/.#\"\'"
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek#define RAW_MAX_SIZE (1024LLU*1024LLU*1024LLU*8) /* 8 GB */
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmekstatic RawImportFile *raw_import_file_unref(RawImportFile *f) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering curl_glue_remove_and_free(f->import->glue, f->curl);
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek curl_slist_free_all(f->request_header);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(RawImportFile*, raw_import_file_unref);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringstatic void raw_import_finish(RawImport *import, int error) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering import->on_finished(import, error, import->userdata);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringstatic int raw_import_file_make_final_path(RawImportFile *f) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering escaped_url = xescape(f->url, FILENAME_ESCAPE);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering escaped_etag = xescape(f->etag, FILENAME_ESCAPE);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering f->final_path = strjoin(f->import->image_root, "/.raw-", escaped_url, ".", escaped_etag, ".raw", NULL);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering f->final_path = strjoin(f->import->image_root, "/.raw-", escaped_url, ".raw", NULL);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringstatic int raw_import_file_make_local_copy(RawImportFile *f) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering const char *p;
7f3fdb7f19a109fa3d1be92926bfe4cea1817da5Jakub Wilk if (lseek(f->disk_fd, SEEK_SET, 0) == (off_t) -1)
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering f->disk_fd = open(f->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(errno, "Failed to open vendor image: %m");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering p = strappenda(f->import->image_root, "/", f->local, ".raw");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering (void) rm_rf_dangerous(p, false, true, false);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(errno, "Failed to create writable copy of image: %m");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* Turn off COW writing. This should greatly improve
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * performance on COW file systems like btrfs, since it
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * reduces fragmentation caused by not allowing in-place
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering r = copy_bytes(f->disk_fd, dfd, (off_t) -1, true);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(r, "Failed to make writable copy of image: %m");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(errno, "Failed to move writable image into place: %m");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering log_info("Created new local image %s.", p);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringstatic void raw_import_file_success(RawImportFile *f) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poetteringstatic int raw_import_maybe_convert_qcow2(RawImportFile *f) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* This is a QCOW2 image, let's convert it */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering return log_error_errno(errno, "Failed to create %s: %m", t);
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering r = qcow2_convert(f->disk_fd, converted_fd);
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek return log_error_errno(r, "Failed to convert qcow2 image: %m");
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmekstatic void raw_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &f) != CURLE_OK)
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt if (!f || f->done)
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek log_error("Transfer failed: %s", curl_easy_strerror(result));
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek log_info("Image already downloaded. Skipping download.");
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek log_error("HTTP request to %s failed with code %li.", f->url, status);
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek log_error("HTTP request to %s finished with unexpected code %li.", f->url, status);
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek if (f->content_length != (uint64_t) -1 &&
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek f->content_length != f->written_compressed) {
42f1ab5009eed71f0d4f83681b8fdbed8664fca3Zbigniew Jędrzejewski-Szmek log_error("Download truncated.");
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering /* Make sure the file size is right, in case the file was
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering * sparse and we just seeked for the last part */
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering if (ftruncate(f->disk_fd, f->written_uncompressed) < 0) {
dc83f27a7cf03757dec11a69ec18504ad4ea8f89Lennart Poettering log_error_errno(errno, "Failed to truncate file: %m");
r = raw_import_maybe_convert_qcow2(f);
goto fail;
if (f->etag)
if (f->url)
if (f->mtime != 0) {
goto fail;
goto fail;
fail:
assert(f);
if (f->disk_fd >= 0)
r = raw_import_file_make_final_path(f);
return log_oom();
if (!f->temp_path) {
return log_oom();
if (f->disk_fd < 0)
ssize_t n;
assert(f);
assert(p);
return -EOVERFLOW;
return -EFBIG;
return -errno;
return -EIO;
assert(f);
assert(p);
return -EOVERFLOW;
return -EFBIG;
if (!f->compressed) {
return -EIO;
assert(f);
if (f->compressed) {
return -EIO;
f->payload_size = 0;
static size_t raw_import_file_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
assert(f);
if (f->done) {
r = -ESTALE;
goto fail;
if (f->disk_fd < 0) {
uint8_t *p;
r = log_oom();
goto fail;
f->payload = p;
r = raw_import_file_detect_xz(f);
goto fail;
return sz;
goto fail;
return sz;
fail:
static size_t raw_import_file_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
char *etag;
assert(f);
if (f->done) {
r = -ESTALE;
goto fail;
log_oom();
goto fail;
return sz;
return sz;
log_oom();
goto fail;
return sz;
log_oom();
goto fail;
return sz;
return sz;
fail:
static int raw_import_file_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
unsigned percent;
usec_t n;
assert(f);
if (dltotal <= 0)
f->last_status_usec = n;
if (!escaped_url)
return -ENOMEM;
return -errno;
u = cunescape_length(a, b - a);
return -ENOMEM;
if (!etag_is_valid(u)) {
free(u);
assert(f);
r = raw_import_file_find_old_etags(f);
if (!cc)
return -ENOMEM;
if (!hdr)
return -ENOMEM;
if (!f->request_header)
return -ENOMEM;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
return -EIO;
if (curl_easy_setopt(f->curl, CURLOPT_XFERINFOFUNCTION, raw_import_file_progress_callback) != CURLE_OK)
return -EIO;
return -EIO;
return -EIO;
int raw_import_new(RawImport **import, sd_event *event, const char *image_root, raw_import_on_finished on_finished, void *userdata) {
return -ENOMEM;
if (!i->image_root)
return -ENOMEM;
if (event)
*import = i;
i = NULL;
RawImportFile *f;
if (!import)
return NULL;
return NULL;
RawImportFile *f;
return -EEXIST;
return -ENOMEM;
if (!f->url)
return -ENOMEM;
if (local) {
if (!f->local)
return -ENOMEM;
r = raw_import_file_begin(f);
f = NULL;
f = NULL;