1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/xattr.h>
24 #include <curl/curl.h>
29 #include "btrfs-util.h"
33 #include "curl-util.h"
34 #include "qcow2-util.h"
35 #include "import-job.h"
36 #include "import-util.h"
37 #include "import-raw.h"
39 typedef struct RawImportFile RawImportFile;
48 ImportJob *checksum_job;
49 ImportJob *signature_job;
51 RawImportFinished on_finished;
63 RawImport* raw_import_unref(RawImport *i) {
67 import_job_unref(i->raw_job);
68 import_job_unref(i->checksum_job);
69 import_job_unref(i->signature_job);
71 curl_glue_unref(i->glue);
72 sd_event_unref(i->event);
75 (void) unlink(i->temp_path);
87 int raw_import_new(RawImport **ret, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata) {
88 _cleanup_(raw_import_unrefp) RawImport *i = NULL;
93 i = new0(RawImport, 1);
97 i->on_finished = on_finished;
98 i->userdata = userdata;
100 i->image_root = strdup(image_root ?: "/var/lib/machines");
105 i->event = sd_event_ref(event);
107 r = sd_event_default(&i->event);
112 r = curl_glue_new(&i->glue, i->event);
116 i->glue->on_finished = import_job_curl_on_finished;
117 i->glue->userdata = i;
125 static int raw_import_maybe_convert_qcow2(RawImport *i) {
126 _cleanup_close_ int converted_fd = -1;
127 _cleanup_free_ char *t = NULL;
133 r = qcow2_detect(i->raw_job->disk_fd);
135 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
139 /* This is a QCOW2 image, let's convert it */
140 r = tempfn_random(i->final_path, &t);
144 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
145 if (converted_fd < 0)
146 return log_error_errno(errno, "Failed to create %s: %m", t);
148 r = chattr_fd(converted_fd, true, FS_NOCOW_FL);
150 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
152 log_info("Unpacking QCOW2 file.");
154 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
157 return log_error_errno(r, "Failed to convert qcow2 image: %m");
160 unlink(i->temp_path);
166 safe_close(i->raw_job->disk_fd);
167 i->raw_job->disk_fd = converted_fd;
173 static int raw_import_make_local_copy(RawImport *i) {
174 _cleanup_free_ char *tp = NULL;
175 _cleanup_close_ int dfd = -1;
185 if (i->raw_job->etag_exists) {
186 /* We have downloaded this one previously, reopen it */
188 assert(i->raw_job->disk_fd < 0);
190 if (!i->final_path) {
191 r = import_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
196 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
197 if (i->raw_job->disk_fd < 0)
198 return log_error_errno(errno, "Failed to open vendor image: %m");
200 /* We freshly downloaded the image, use it */
202 assert(i->raw_job->disk_fd >= 0);
204 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
205 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
208 p = strappenda(i->image_root, "/", i->local, ".raw");
210 if (i->force_local) {
211 (void) btrfs_subvol_remove(p);
212 (void) rm_rf_dangerous(p, false, true, false);
215 r = tempfn_random(p, &tp);
219 dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
221 return log_error_errno(errno, "Failed to create writable copy of image: %m");
223 /* Turn off COW writing. This should greatly improve
224 * performance on COW file systems like btrfs, since it
225 * reduces fragmentation caused by not allowing in-place
227 r = chattr_fd(dfd, true, FS_NOCOW_FL);
229 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
231 r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
234 return log_error_errno(r, "Failed to make writable copy of image: %m");
237 (void) copy_times(i->raw_job->disk_fd, dfd);
238 (void) copy_xattr(i->raw_job->disk_fd, dfd);
240 dfd = safe_close(dfd);
245 return log_error_errno(errno, "Failed to move writable image into place: %m");
248 log_info("Created new local image '%s'.", i->local);
252 static void raw_import_job_on_finished(ImportJob *j) {
261 if (j == i->checksum_job)
262 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
263 else if (j == i->signature_job)
264 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
266 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
272 /* This is invoked if either the download completed
273 * successfully, or the download was skipped because we
274 * already have the etag. In this case ->etag_exists is
277 * We only do something when we got all three files */
279 if (i->raw_job->state != IMPORT_JOB_DONE)
281 if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
283 if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
286 if (!i->raw_job->etag_exists) {
287 /* This is a new download, verify it, and move it into place */
288 assert(i->raw_job->disk_fd >= 0);
290 r = import_verify(i->raw_job, i->checksum_job, i->signature_job);
294 r = raw_import_maybe_convert_qcow2(i);
298 r = import_make_read_only_fd(i->raw_job->disk_fd);
302 r = rename(i->temp_path, i->final_path);
304 r = log_error_errno(errno, "Failed to move RAW file into place: %m");
312 r = raw_import_make_local_copy(i);
320 i->on_finished(i, r, i->userdata);
322 sd_event_exit(i->event, r);
325 static int raw_import_job_on_open_disk(ImportJob *j) {
334 r = import_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
338 r = tempfn_random(i->final_path, &i->temp_path);
342 mkdir_parents_label(i->temp_path, 0700);
344 j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
346 return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
348 r = chattr_fd(j->disk_fd, true, FS_NOCOW_FL);
350 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
355 int raw_import_pull(RawImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
359 assert(verify < _IMPORT_VERIFY_MAX);
365 if (!http_url_is_valid(url))
368 if (local && !machine_name_is_valid(local))
371 r = free_and_strdup(&i->local, local);
374 i->force_local = force_local;
377 /* Queue job for the image itself */
378 r = import_job_new(&i->raw_job, url, i->glue, i);
382 i->raw_job->on_finished = raw_import_job_on_finished;
383 i->raw_job->on_open_disk = raw_import_job_on_open_disk;
384 i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
386 r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
390 r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_import_job_on_finished, i);
394 r = import_job_begin(i->raw_job);
398 if (i->checksum_job) {
399 r = import_job_begin(i->checksum_job);
404 if (i->signature_job) {
405 r = import_job_begin(i->signature_job);