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 <sys/prctl.h>
25 #include <curl/curl.h>
31 #include "btrfs-util.h"
35 #include "curl-util.h"
36 #include "qcow2-util.h"
37 #include "import-job.h"
38 #include "import-util.h"
39 #include "import-raw.h"
41 typedef struct RawImportFile RawImportFile;
50 ImportJob *sha256sums_job;
51 ImportJob *signature_job;
53 RawImportFinished on_finished;
65 RawImport* raw_import_unref(RawImport *i) {
69 import_job_unref(i->raw_job);
70 import_job_unref(i->sha256sums_job);
71 import_job_unref(i->signature_job);
73 curl_glue_unref(i->glue);
74 sd_event_unref(i->event);
77 (void) unlink(i->temp_path);
89 int raw_import_new(RawImport **ret, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata) {
90 _cleanup_(raw_import_unrefp) RawImport *i = NULL;
95 i = new0(RawImport, 1);
99 i->on_finished = on_finished;
100 i->userdata = userdata;
102 i->image_root = strdup(image_root ?: "/var/lib/machines");
107 i->event = sd_event_ref(event);
109 r = sd_event_default(&i->event);
114 r = curl_glue_new(&i->glue, i->event);
118 i->glue->on_finished = import_job_curl_on_finished;
119 i->glue->userdata = i;
127 static int raw_import_maybe_convert_qcow2(RawImport *i) {
128 _cleanup_close_ int converted_fd = -1;
129 _cleanup_free_ char *t = NULL;
135 r = qcow2_detect(i->raw_job->disk_fd);
137 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
141 /* This is a QCOW2 image, let's convert it */
142 r = tempfn_random(i->final_path, &t);
146 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
147 if (converted_fd < 0)
148 return log_error_errno(errno, "Failed to create %s: %m", t);
150 r = chattr_fd(converted_fd, true, FS_NOCOW_FL);
152 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
154 log_info("Unpacking QCOW2 file.");
156 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
159 return log_error_errno(r, "Failed to convert qcow2 image: %m");
162 unlink(i->temp_path);
168 safe_close(i->raw_job->disk_fd);
169 i->raw_job->disk_fd = converted_fd;
175 static int raw_import_make_local_copy(RawImport *i) {
176 _cleanup_free_ char *tp = NULL;
177 _cleanup_close_ int dfd = -1;
187 if (i->raw_job->etag_exists) {
188 /* We have downloaded this one previously, reopen it */
190 assert(i->raw_job->disk_fd < 0);
192 if (!i->final_path) {
193 r = import_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
198 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
199 if (i->raw_job->disk_fd < 0)
200 return log_error_errno(errno, "Failed to open vendor image: %m");
202 /* We freshly downloaded the image, use it */
204 assert(i->raw_job->disk_fd >= 0);
206 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
207 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
210 p = strappenda(i->image_root, "/", i->local, ".raw");
212 if (i->force_local) {
213 (void) btrfs_subvol_remove(p);
214 (void) rm_rf_dangerous(p, false, true, false);
217 r = tempfn_random(p, &tp);
221 dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
223 return log_error_errno(errno, "Failed to create writable copy of image: %m");
225 /* Turn off COW writing. This should greatly improve
226 * performance on COW file systems like btrfs, since it
227 * reduces fragmentation caused by not allowing in-place
229 r = chattr_fd(dfd, true, FS_NOCOW_FL);
231 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
233 r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
236 return log_error_errno(r, "Failed to make writable copy of image: %m");
239 (void) copy_times(i->raw_job->disk_fd, dfd);
240 (void) copy_xattr(i->raw_job->disk_fd, dfd);
242 dfd = safe_close(dfd);
247 return log_error_errno(errno, "Failed to move writable image into place: %m");
250 log_info("Created new local image '%s'.", i->local);
254 static int raw_import_verify_sha256sum(RawImport *i) {
255 _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
256 _cleanup_free_ char *fn = NULL;
257 _cleanup_close_ int sig_file = -1;
258 const char *p, *line;
259 char sig_file_path[] = "/tmp/sigXXXXXX";
260 _cleanup_sigkill_wait_ pid_t pid = 0;
267 if (!i->sha256sums_job)
270 assert(i->raw_job->state == IMPORT_JOB_DONE);
271 assert(i->raw_job->sha256);
273 assert(i->sha256sums_job->state == IMPORT_JOB_DONE);
274 assert(i->sha256sums_job->payload);
275 assert(i->sha256sums_job->payload_size > 0);
277 r = import_url_last_component(i->raw_job->url, &fn);
281 if (!filename_is_valid(fn)) {
282 log_error("Cannot verify checksum, could not determine valid server-side file name.");
286 line = strappenda(i->raw_job->sha256, " *", fn, "\n");
288 p = memmem(i->sha256sums_job->payload,
289 i->sha256sums_job->payload_size,
293 if (!p || (p != (char*) i->sha256sums_job->payload && p[-1] != '\n')) {
294 log_error("Checksum did not check out, payload has been tempered with.");
298 log_info("SHA256 checksum of %s is valid.", i->raw_job->url);
300 if (!i->signature_job)
303 assert(i->signature_job->state == IMPORT_JOB_DONE);
304 assert(i->signature_job->payload);
305 assert(i->signature_job->payload_size > 0);
307 r = pipe2(gpg_pipe, O_CLOEXEC);
309 return log_error_errno(errno, "Failed to create pipe: %m");
311 sig_file = mkostemp(sig_file_path, O_RDWR);
313 return log_error_errno(errno, "Failed to create temporary file: %m");
315 r = loop_write(sig_file, i->signature_job->payload, i->signature_job->payload_size, false);
317 log_error_errno(r, "Failed to write to temporary file: %m");
323 return log_error_errno(errno, "Failed to fork off gpg: %m");
325 const char *cmd[] = {
328 "--no-default-keyring",
329 "--no-auto-key-locate",
330 "--no-auto-check-trustdb",
332 "--trust-model=always",
333 "--keyring=" VENDOR_KEYRING_PATH,
334 NULL, /* maybe user keyring */
336 NULL, /* signature file */
338 NULL /* trailing NULL */
340 unsigned k = ELEMENTSOF(cmd) - 5;
345 reset_all_signal_handlers();
347 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
349 gpg_pipe[1] = safe_close(gpg_pipe[1]);
351 if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
352 log_error_errno(errno, "Failed to dup2() fd: %m");
356 if (gpg_pipe[0] != STDIN_FILENO)
357 gpg_pipe[0] = safe_close(gpg_pipe[0]);
359 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
361 log_error_errno(errno, "Failed to open /dev/null: %m");
365 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
366 log_error_errno(errno, "Failed to dup2() fd: %m");
370 if (null_fd != STDOUT_FILENO)
371 null_fd = safe_close(null_fd);
373 /* We add the user keyring only to the command line
374 * arguments, if it's around since gpg fails
376 if (access(USER_KEYRING_PATH, F_OK) >= 0)
377 cmd[k++] = "--keyring=" USER_KEYRING_PATH;
379 cmd[k++] = "--verify";
380 cmd[k++] = sig_file_path;
384 execvp("gpg", (char * const *) cmd);
385 log_error_errno(errno, "Failed to execute gpg: %m");
389 gpg_pipe[0] = safe_close(gpg_pipe[0]);
391 r = loop_write(gpg_pipe[1], i->sha256sums_job->payload, i->sha256sums_job->payload_size, false);
393 log_error_errno(r, "Failed to write to pipe: %m");
397 gpg_pipe[1] = safe_close(gpg_pipe[1]);
399 r = wait_for_terminate_and_warn("gpg", pid, true);
404 log_error("Signature verification failed.");
407 log_info("Signature verification succeeded.");
413 unlink(sig_file_path);
418 static void raw_import_job_on_finished(ImportJob *j) {
427 if (j == i->sha256sums_job)
428 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
429 else if (j == i->signature_job)
430 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
432 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
438 /* This is invoked if either the download completed
439 * successfully, or the download was skipped because we
440 * already have the etag. In this case ->etag_exists is
443 * We only do something when we got all three files */
445 if (!IMPORT_JOB_STATE_IS_COMPLETE(i->raw_job))
447 if (i->sha256sums_job && !IMPORT_JOB_STATE_IS_COMPLETE(i->sha256sums_job))
449 if (i->signature_job && !IMPORT_JOB_STATE_IS_COMPLETE(i->signature_job))
452 if (!i->raw_job->etag_exists) {
453 assert(i->raw_job->disk_fd >= 0);
455 r = raw_import_verify_sha256sum(i);
459 r = raw_import_maybe_convert_qcow2(i);
463 r = import_make_read_only_fd(i->raw_job->disk_fd);
467 r = rename(i->temp_path, i->final_path);
469 r = log_error_errno(errno, "Failed to move RAW file into place: %m");
477 r = raw_import_make_local_copy(i);
485 i->on_finished(i, r, i->userdata);
487 sd_event_exit(i->event, r);
490 static int raw_import_job_on_open_disk(ImportJob *j) {
499 r = import_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
503 r = tempfn_random(i->final_path, &i->temp_path);
507 mkdir_parents_label(i->temp_path, 0700);
509 j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
511 return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
513 r = chattr_fd(j->disk_fd, true, FS_NOCOW_FL);
515 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
520 int raw_import_pull(RawImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
524 assert(verify < _IMPORT_VERIFY_MAX);
530 if (!http_url_is_valid(url))
533 if (local && !machine_name_is_valid(local))
536 r = free_and_strdup(&i->local, local);
539 i->force_local = force_local;
542 /* Queue job for the image itself */
543 r = import_job_new(&i->raw_job, url, i->glue, i);
547 i->raw_job->on_finished = raw_import_job_on_finished;
548 i->raw_job->on_open_disk = raw_import_job_on_open_disk;
549 i->raw_job->calc_hash = true;
551 r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
555 if (verify != IMPORT_VERIFY_NO) {
556 _cleanup_free_ char *sha256sums_url = NULL;
558 /* Queue job for the SHA256SUMS file for the image */
559 r = import_url_change_last_component(url, "SHA256SUMS", &sha256sums_url);
563 r = import_job_new(&i->sha256sums_job, sha256sums_url, i->glue, i);
567 i->sha256sums_job->on_finished = raw_import_job_on_finished;
568 i->sha256sums_job->uncompressed_max = i->sha256sums_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
571 if (verify == IMPORT_VERIFY_SIGNATURE) {
572 _cleanup_free_ char *sha256sums_sig_url = NULL;
574 /* Queue job for the SHA256SUMS.gpg file for the image. */
575 r = import_url_change_last_component(url, "SHA256SUMS.gpg", &sha256sums_sig_url);
579 r = import_job_new(&i->signature_job, sha256sums_sig_url, i->glue, i);
583 i->signature_job->on_finished = raw_import_job_on_finished;
584 i->signature_job->uncompressed_max = i->signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
587 r = import_job_begin(i->raw_job);
591 if (i->sha256sums_job) {
592 r = import_job_begin(i->sha256sums_job);
597 if (i->signature_job) {
598 r = import_job_begin(i->signature_job);