1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "btrfs-util.h"
12 #include "curl-util.h"
15 #include "hostname-util.h"
16 #include "import-common.h"
17 #include "import-util.h"
18 #include "install-file.h"
20 #include "mkdir-label.h"
21 #include "path-util.h"
22 #include "pull-common.h"
25 #include "qcow2-util.h"
27 #include "string-util.h"
29 #include "tmpfile-util.h"
33 typedef enum RawProgress
{
52 PullJob
*checksum_job
;
53 PullJob
*signature_job
;
54 PullJob
*settings_job
;
55 PullJob
*roothash_job
;
56 PullJob
*roothash_signature_job
;
59 RawPullFinished on_finished
;
62 char *local
; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
63 * machine name of the final copy we make */
69 char *settings_temp_path
;
72 char *roothash_temp_path
;
74 char *roothash_signature_path
;
75 char *roothash_signature_temp_path
;
78 char *verity_temp_path
;
83 RawPull
* raw_pull_unref(RawPull
*i
) {
87 pull_job_unref(i
->raw_job
);
88 pull_job_unref(i
->checksum_job
);
89 pull_job_unref(i
->signature_job
);
90 pull_job_unref(i
->settings_job
);
91 pull_job_unref(i
->roothash_job
);
92 pull_job_unref(i
->roothash_signature_job
);
93 pull_job_unref(i
->verity_job
);
95 curl_glue_unref(i
->glue
);
96 sd_event_unref(i
->event
);
98 unlink_and_free(i
->temp_path
);
99 unlink_and_free(i
->settings_temp_path
);
100 unlink_and_free(i
->roothash_temp_path
);
101 unlink_and_free(i
->roothash_signature_temp_path
);
102 unlink_and_free(i
->verity_temp_path
);
105 free(i
->settings_path
);
106 free(i
->roothash_path
);
107 free(i
->roothash_signature_path
);
108 free(i
->verity_path
);
119 const char *image_root
,
120 RawPullFinished on_finished
,
123 _cleanup_(curl_glue_unrefp
) CurlGlue
*g
= NULL
;
124 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
125 _cleanup_(raw_pull_unrefp
) RawPull
*i
= NULL
;
126 _cleanup_free_
char *root
= NULL
;
131 root
= strdup(image_root
?: "/var/lib/machines");
136 e
= sd_event_ref(event
);
138 r
= sd_event_default(&e
);
143 r
= curl_glue_new(&g
, e
);
152 .on_finished
= on_finished
,
153 .userdata
= userdata
,
154 .image_root
= TAKE_PTR(root
),
155 .event
= TAKE_PTR(e
),
157 .offset
= UINT64_MAX
,
160 i
->glue
->on_finished
= pull_job_curl_on_finished
;
161 i
->glue
->userdata
= i
;
168 static void raw_pull_report_progress(RawPull
*i
, RawProgress p
) {
175 case RAW_DOWNLOADING
: {
176 unsigned remain
= 80;
180 if (i
->checksum_job
) {
181 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
185 if (i
->signature_job
) {
186 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
190 if (i
->settings_job
) {
191 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
195 if (i
->roothash_job
) {
196 percent
+= i
->roothash_job
->progress_percent
* 5 / 100;
200 if (i
->roothash_signature_job
) {
201 percent
+= i
->roothash_signature_job
->progress_percent
* 5 / 100;
206 percent
+= i
->verity_job
->progress_percent
* 10 / 100;
211 percent
+= i
->raw_job
->progress_percent
* remain
/ 100;
232 assert_not_reached();
235 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
236 log_debug("Combined progress %u%%", percent
);
239 static int raw_pull_maybe_convert_qcow2(RawPull
*i
) {
240 _cleanup_(unlink_and_freep
) char *t
= NULL
;
241 _cleanup_close_
int converted_fd
= -EBADF
;
242 _cleanup_free_
char *f
= NULL
;
247 assert(!FLAGS_SET(i
->flags
, PULL_DIRECT
));
249 if (!FLAGS_SET(i
->flags
, PULL_CONVERT_QCOW2
))
252 assert(i
->final_path
);
253 assert(i
->raw_job
->close_disk_fd
);
255 r
= qcow2_detect(i
->raw_job
->disk_fd
);
257 return log_error_errno(r
, "Failed to detect whether this is a QCOW2 image: %m");
261 /* This is a QCOW2 image, let's convert it */
262 r
= tempfn_random(i
->final_path
, NULL
, &f
);
266 converted_fd
= open(f
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
267 if (converted_fd
< 0)
268 return log_error_errno(errno
, "Failed to create %s: %m", f
);
272 (void) import_set_nocow_and_log(converted_fd
, t
);
274 log_info("Unpacking QCOW2 file.");
276 r
= qcow2_convert(i
->raw_job
->disk_fd
, converted_fd
);
278 return log_error_errno(r
, "Failed to convert qcow2 image: %m");
280 unlink_and_free(i
->temp_path
);
281 i
->temp_path
= TAKE_PTR(t
);
282 close_and_replace(i
->raw_job
->disk_fd
, converted_fd
);
287 static int raw_pull_determine_path(
290 char **field
/* input + output (!) */) {
301 r
= pull_make_path(i
->raw_job
->url
, i
->raw_job
->etag
, i
->image_root
, ".raw-", suffix
, field
);
308 static int raw_pull_copy_auxiliary_file(
311 char **path
/* input + output (!) */) {
320 r
= raw_pull_determine_path(i
, suffix
, path
);
324 local
= strjoina(i
->image_root
, "/", i
->local
, suffix
);
326 r
= copy_file_atomic(
331 (FLAGS_SET(i
->flags
, PULL_FORCE
) ? COPY_REPLACE
: 0) |
332 (FLAGS_SET(i
->flags
, PULL_SYNC
) ? COPY_FSYNC_FULL
: 0));
334 log_warning_errno(r
, "File %s already exists, not replacing.", local
);
335 else if (r
== -ENOENT
)
336 log_debug_errno(r
, "Skipping creation of auxiliary file, since none was found.");
338 log_warning_errno(r
, "Failed to copy file %s, ignoring: %m", local
);
340 log_info("Created new file %s.", local
);
345 static int raw_pull_make_local_copy(RawPull
*i
) {
346 _cleanup_(unlink_and_freep
) char *tp
= NULL
;
347 _cleanup_free_
char *f
= NULL
;
348 _cleanup_close_
int dfd
= -EBADF
;
354 assert(!FLAGS_SET(i
->flags
, PULL_DIRECT
));
359 if (i
->raw_job
->etag_exists
) {
360 /* We have downloaded this one previously, reopen it */
362 assert(i
->raw_job
->disk_fd
< 0);
364 i
->raw_job
->disk_fd
= open(i
->final_path
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
365 if (i
->raw_job
->disk_fd
< 0)
366 return log_error_errno(errno
, "Failed to open vendor image: %m");
368 /* We freshly downloaded the image, use it */
370 assert(i
->raw_job
->disk_fd
>= 0);
371 assert(i
->offset
== UINT64_MAX
);
373 if (lseek(i
->raw_job
->disk_fd
, SEEK_SET
, 0) == (off_t
) -1)
374 return log_error_errno(errno
, "Failed to seek to beginning of vendor image: %m");
377 p
= strjoina(i
->image_root
, "/", i
->local
, ".raw");
379 r
= tempfn_random(p
, NULL
, &f
);
383 dfd
= open(f
, O_WRONLY
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
385 return log_error_errno(errno
, "Failed to create writable copy of image: %m");
389 /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
390 * since it reduces fragmentation caused by not allowing in-place writes. */
391 (void) import_set_nocow_and_log(dfd
, tp
);
393 r
= copy_bytes(i
->raw_job
->disk_fd
, dfd
, UINT64_MAX
, COPY_REFLINK
);
395 return log_error_errno(r
, "Failed to make writable copy of image: %m");
397 (void) copy_times(i
->raw_job
->disk_fd
, dfd
, COPY_CRTIME
);
398 (void) copy_xattr(i
->raw_job
->disk_fd
, NULL
, dfd
, NULL
, 0);
400 dfd
= safe_close(dfd
);
402 r
= install_file(AT_FDCWD
, tp
,
404 (i
->flags
& PULL_FORCE
? INSTALL_REPLACE
: 0) |
405 (i
->flags
& PULL_READ_ONLY
? INSTALL_READ_ONLY
: 0) |
406 (i
->flags
& PULL_SYNC
? INSTALL_FSYNC_FULL
: 0));
408 return log_error_errno(errno
, "Failed to move local image into place '%s': %m", p
);
412 log_info("Created new local image '%s'.", i
->local
);
414 if (FLAGS_SET(i
->flags
, PULL_SETTINGS
)) {
415 r
= raw_pull_copy_auxiliary_file(i
, ".nspawn", &i
->settings_path
);
420 if (FLAGS_SET(i
->flags
, PULL_ROOTHASH
)) {
421 r
= raw_pull_copy_auxiliary_file(i
, ".roothash", &i
->roothash_path
);
426 if (FLAGS_SET(i
->flags
, PULL_ROOTHASH_SIGNATURE
)) {
427 r
= raw_pull_copy_auxiliary_file(i
, ".roothash.p7s", &i
->roothash_signature_path
);
432 if (FLAGS_SET(i
->flags
, PULL_VERITY
)) {
433 r
= raw_pull_copy_auxiliary_file(i
, ".verity", &i
->verity_path
);
441 static bool raw_pull_is_done(RawPull
*i
) {
445 if (!PULL_JOB_IS_COMPLETE(i
->raw_job
))
447 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
449 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
451 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
453 if (i
->roothash_job
&& !PULL_JOB_IS_COMPLETE(i
->roothash_job
))
455 if (i
->roothash_signature_job
&& !PULL_JOB_IS_COMPLETE(i
->roothash_signature_job
))
457 if (i
->verity_job
&& !PULL_JOB_IS_COMPLETE(i
->verity_job
))
463 static int raw_pull_rename_auxiliary_file(
477 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
478 * incorporate it in the file name if we can */
479 *path
= mfree(*path
);
480 r
= raw_pull_determine_path(i
, suffix
, path
);
485 AT_FDCWD
, *temp_path
,
488 (i
->flags
& PULL_SYNC
? INSTALL_FSYNC_FULL
: 0));
490 return log_error_errno(r
, "Failed to move '%s' into place: %m", *path
);
492 *temp_path
= mfree(*temp_path
);
496 static void raw_pull_job_on_finished(PullJob
*j
) {
507 /* Only the main job and the checksum job are fatal if they fail. The other fails are just
508 * "decoration", that we'll download if we can. The signature job isn't fatal here because we
509 * might not actually need it in case Suse style signatures are used, that are inline in the
512 if (j
== i
->raw_job
) {
513 if (j
->error
== ENOMEDIUM
) /* HTTP 404 */
514 r
= log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
516 r
= log_error_errno(j
->error
, "Failed to retrieve image file.");
518 } else if (j
== i
->checksum_job
) {
519 r
= log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
521 } else if (j
== i
->signature_job
)
522 log_debug_errno(j
->error
, "Signature job for %s failed, proceeding for now.", j
->url
);
523 else if (j
== i
->settings_job
)
524 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
525 else if (j
== i
->roothash_job
)
526 log_info_errno(j
->error
, "Root hash file could not be retrieved, proceeding without.");
527 else if (j
== i
->roothash_signature_job
)
528 log_info_errno(j
->error
, "Root hash signature file could not be retrieved, proceeding without.");
529 else if (j
== i
->verity_job
)
530 log_info_errno(j
->error
, "Verity integrity file could not be retrieved, proceeding without.");
532 assert_not_reached();
535 /* This is invoked if either the download completed successfully, or the download was skipped because
536 * we already have the etag. In this case ->etag_exists is true.
538 * We only do something when we got all files */
540 if (!raw_pull_is_done(i
))
543 if (i
->signature_job
&& i
->signature_job
->error
!= 0) {
544 VerificationStyle style
;
547 /* The signature job failed. Let's see if we actually need it */
549 verify_job
= i
->checksum_job
?: i
->raw_job
; /* if the checksum job doesn't exist this must be
550 * because the main job is the checksum file
555 r
= verification_style_from_url(verify_job
->url
, &style
);
557 log_error_errno(r
, "Failed to determine verification style from checksum URL: %m");
561 if (style
== VERIFICATION_PER_DIRECTORY
) { /* A failed signature file download only matters
562 * in per-directory verification mode, since only
563 * then the signature is detached, and thus a file
565 r
= log_error_errno(i
->signature_job
->error
,
566 "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
571 /* Let's close these auxiliary files now, we don't need access to them anymore. */
572 FOREACH_POINTER(jj
, i
->settings_job
, i
->roothash_job
, i
->roothash_signature_job
, i
->verity_job
)
573 pull_job_close_disk_fd(jj
);
575 if (!i
->raw_job
->etag_exists
) {
576 raw_pull_report_progress(i
, RAW_VERIFYING
);
578 r
= pull_verify(i
->verify
,
585 i
->roothash_signature_job
,
591 if (i
->flags
& PULL_DIRECT
) {
592 assert(!i
->settings_job
);
593 assert(!i
->roothash_job
);
594 assert(!i
->roothash_signature_job
);
595 assert(!i
->verity_job
);
597 raw_pull_report_progress(i
, RAW_FINALIZING
);
600 r
= install_file(AT_FDCWD
, i
->local
,
602 ((i
->flags
& PULL_READ_ONLY
) && i
->offset
== UINT64_MAX
? INSTALL_READ_ONLY
: 0) |
603 (i
->flags
& PULL_SYNC
? INSTALL_FSYNC_FULL
: 0));
605 log_error_errno(r
, "Failed to finalize raw file to '%s': %m", i
->local
);
610 r
= raw_pull_determine_path(i
, ".raw", &i
->final_path
);
614 if (!i
->raw_job
->etag_exists
) {
615 /* This is a new download, verify it, and move it into place */
617 assert(i
->temp_path
);
618 assert(i
->final_path
);
620 raw_pull_report_progress(i
, RAW_UNPACKING
);
622 r
= raw_pull_maybe_convert_qcow2(i
);
626 raw_pull_report_progress(i
, RAW_FINALIZING
);
628 r
= install_file(AT_FDCWD
, i
->temp_path
,
629 AT_FDCWD
, i
->final_path
,
631 (i
->flags
& PULL_SYNC
? INSTALL_FSYNC_FULL
: 0));
633 log_error_errno(r
, "Failed to move raw file to '%s': %m", i
->final_path
);
637 i
->temp_path
= mfree(i
->temp_path
);
639 if (i
->settings_job
&&
640 i
->settings_job
->error
== 0) {
641 r
= raw_pull_rename_auxiliary_file(i
, ".nspawn", &i
->settings_temp_path
, &i
->settings_path
);
646 if (i
->roothash_job
&&
647 i
->roothash_job
->error
== 0) {
648 r
= raw_pull_rename_auxiliary_file(i
, ".roothash", &i
->roothash_temp_path
, &i
->roothash_path
);
653 if (i
->roothash_signature_job
&&
654 i
->roothash_signature_job
->error
== 0) {
655 r
= raw_pull_rename_auxiliary_file(i
, ".roothash.p7s", &i
->roothash_signature_temp_path
, &i
->roothash_signature_path
);
661 i
->verity_job
->error
== 0) {
662 r
= raw_pull_rename_auxiliary_file(i
, ".verity", &i
->verity_temp_path
, &i
->verity_path
);
668 raw_pull_report_progress(i
, RAW_COPYING
);
670 r
= raw_pull_make_local_copy(i
);
679 i
->on_finished(i
, r
, i
->userdata
);
681 sd_event_exit(i
->event
, r
);
684 static int raw_pull_job_on_open_disk_generic(
688 char **temp_path
/* input + output */) {
697 assert(!FLAGS_SET(i
->flags
, PULL_DIRECT
));
700 r
= tempfn_random_child(i
->image_root
, extra
, temp_path
);
705 (void) mkdir_parents_label(*temp_path
, 0700);
707 j
->disk_fd
= open(*temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
709 return log_error_errno(errno
, "Failed to create %s: %m", *temp_path
);
714 static int raw_pull_job_on_open_disk_raw(PullJob
*j
) {
722 assert(i
->raw_job
== j
);
723 assert(j
->disk_fd
< 0);
725 if (i
->flags
& PULL_DIRECT
) {
727 if (!i
->local
) { /* If no local name specified, the pull job will write its data to stdout */
728 j
->disk_fd
= STDOUT_FILENO
;
729 j
->close_disk_fd
= false;
733 (void) mkdir_parents_label(i
->local
, 0700);
735 j
->disk_fd
= open(i
->local
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
|(i
->offset
== UINT64_MAX
? O_TRUNC
|O_CREAT
: 0), 0664);
737 return log_error_errno(errno
, "Failed to open destination '%s': %m", i
->local
);
739 if (i
->offset
== UINT64_MAX
)
740 (void) import_set_nocow_and_log(j
->disk_fd
, i
->local
);
743 r
= raw_pull_job_on_open_disk_generic(i
, j
, "raw", &i
->temp_path
);
747 assert(i
->offset
== UINT64_MAX
);
748 (void) import_set_nocow_and_log(j
->disk_fd
, i
->temp_path
);
754 static int raw_pull_job_on_open_disk_settings(PullJob
*j
) {
761 assert(i
->settings_job
== j
);
763 return raw_pull_job_on_open_disk_generic(i
, j
, "settings", &i
->settings_temp_path
);
766 static int raw_pull_job_on_open_disk_roothash(PullJob
*j
) {
773 assert(i
->roothash_job
== j
);
775 return raw_pull_job_on_open_disk_generic(i
, j
, "roothash", &i
->roothash_temp_path
);
778 static int raw_pull_job_on_open_disk_roothash_signature(PullJob
*j
) {
785 assert(i
->roothash_signature_job
== j
);
787 return raw_pull_job_on_open_disk_generic(i
, j
, "roothash.p7s", &i
->roothash_signature_temp_path
);
790 static int raw_pull_job_on_open_disk_verity(PullJob
*j
) {
797 assert(i
->verity_job
== j
);
799 return raw_pull_job_on_open_disk_generic(i
, j
, "verity", &i
->verity_temp_path
);
802 static void raw_pull_job_on_progress(PullJob
*j
) {
810 raw_pull_report_progress(i
, RAW_DOWNLOADING
);
821 const char *checksum
) {
828 assert(verify
== _IMPORT_VERIFY_INVALID
|| verify
< _IMPORT_VERIFY_MAX
);
829 assert(verify
== _IMPORT_VERIFY_INVALID
|| verify
>= 0);
830 assert((verify
< 0) || !checksum
);
831 assert(!(flags
& ~PULL_FLAGS_MASK_RAW
));
832 assert(offset
== UINT64_MAX
|| FLAGS_SET(flags
, PULL_DIRECT
));
833 assert(!(flags
& (PULL_SETTINGS
|PULL_ROOTHASH
|PULL_ROOTHASH_SIGNATURE
|PULL_VERITY
)) || !(flags
& PULL_DIRECT
));
834 assert(!(flags
& (PULL_SETTINGS
|PULL_ROOTHASH
|PULL_ROOTHASH_SIGNATURE
|PULL_VERITY
)) || !checksum
);
836 if (!http_url_is_valid(url
) && !file_url_is_valid(url
))
839 if (local
&& !pull_validate_local(local
, flags
))
845 r
= free_and_strdup(&i
->local
, local
);
849 r
= free_and_strdup(&i
->checksum
, checksum
);
856 /* Queue job for the image itself */
857 r
= pull_job_new(&i
->raw_job
, url
, i
->glue
, i
);
861 i
->raw_job
->on_finished
= raw_pull_job_on_finished
;
862 i
->raw_job
->on_open_disk
= raw_pull_job_on_open_disk_raw
;
865 i
->raw_job
->calc_checksum
= true;
866 else if (verify
!= IMPORT_VERIFY_NO
) {
867 /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
868 * signature, which we let gpg verify instead. */
870 r
= pull_url_needs_checksum(url
);
874 i
->raw_job
->calc_checksum
= r
;
875 i
->raw_job
->force_memory
= true; /* make sure this is both written to disk if that's
876 * requested and into memory, since we need to verify it */
879 if (size_max
!= UINT64_MAX
)
880 i
->raw_job
->uncompressed_max
= size_max
;
881 if (offset
!= UINT64_MAX
)
882 i
->raw_job
->offset
= i
->offset
= offset
;
884 if (!FLAGS_SET(flags
, PULL_DIRECT
)) {
885 r
= pull_find_old_etags(url
, i
->image_root
, DT_REG
, ".raw-", ".raw", &i
->raw_job
->old_etags
);
890 r
= pull_make_verification_jobs(
897 raw_pull_job_on_finished
,
902 if (FLAGS_SET(flags
, PULL_SETTINGS
)) {
903 r
= pull_make_auxiliary_job(
910 raw_pull_job_on_open_disk_settings
,
911 raw_pull_job_on_finished
,
917 if (FLAGS_SET(flags
, PULL_ROOTHASH
)) {
918 r
= pull_make_auxiliary_job(
925 raw_pull_job_on_open_disk_roothash
,
926 raw_pull_job_on_finished
,
932 if (FLAGS_SET(flags
, PULL_ROOTHASH_SIGNATURE
)) {
933 r
= pull_make_auxiliary_job(
934 &i
->roothash_signature_job
,
940 raw_pull_job_on_open_disk_roothash_signature
,
941 raw_pull_job_on_finished
,
947 if (FLAGS_SET(flags
, PULL_VERITY
)) {
948 r
= pull_make_auxiliary_job(
955 raw_pull_job_on_open_disk_verity
,
956 raw_pull_job_on_finished
,
968 i
->roothash_signature_job
,
974 j
->on_progress
= raw_pull_job_on_progress
;
975 j
->sync
= FLAGS_SET(flags
, PULL_SYNC
);
977 r
= pull_job_begin(j
);