1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
11 #include "import-common.h"
12 #include "import-util.h"
13 #include "install-file.h"
15 #include "mkdir-label.h"
16 #include "pull-common.h"
19 #include "qcow2-util.h"
20 #include "string-util.h"
21 #include "tmpfile-util.h"
24 typedef enum RawProgress
{
32 typedef struct RawPull
{
43 PullJob
*checksum_job
;
44 PullJob
*signature_job
;
45 PullJob
*settings_job
;
46 PullJob
*roothash_job
;
47 PullJob
*roothash_signature_job
;
50 RawPullFinished on_finished
;
53 char *local
; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
54 * image name of the final copy we make */
60 char *settings_temp_path
;
63 char *roothash_temp_path
;
65 char *roothash_signature_path
;
66 char *roothash_signature_temp_path
;
69 char *verity_temp_path
;
74 RawPull
* raw_pull_unref(RawPull
*i
) {
78 pull_job_unref(i
->raw_job
);
79 pull_job_unref(i
->checksum_job
);
80 pull_job_unref(i
->signature_job
);
81 pull_job_unref(i
->settings_job
);
82 pull_job_unref(i
->roothash_job
);
83 pull_job_unref(i
->roothash_signature_job
);
84 pull_job_unref(i
->verity_job
);
86 curl_glue_unref(i
->glue
);
87 sd_event_unref(i
->event
);
89 unlink_and_free(i
->temp_path
);
90 unlink_and_free(i
->settings_temp_path
);
91 unlink_and_free(i
->roothash_temp_path
);
92 unlink_and_free(i
->roothash_signature_temp_path
);
93 unlink_and_free(i
->verity_temp_path
);
96 free(i
->settings_path
);
97 free(i
->roothash_path
);
98 free(i
->roothash_signature_path
);
110 const char *image_root
,
111 RawPullFinished on_finished
,
114 _cleanup_(curl_glue_unrefp
) CurlGlue
*g
= NULL
;
115 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
116 _cleanup_(raw_pull_unrefp
) RawPull
*i
= NULL
;
117 _cleanup_free_
char *root
= NULL
;
123 root
= strdup(image_root
);
128 e
= sd_event_ref(event
);
130 r
= sd_event_default(&e
);
135 r
= curl_glue_new(&g
, e
);
144 .on_finished
= on_finished
,
145 .userdata
= userdata
,
146 .image_root
= TAKE_PTR(root
),
147 .event
= TAKE_PTR(e
),
149 .offset
= UINT64_MAX
,
152 i
->glue
->on_finished
= pull_job_curl_on_finished
;
153 i
->glue
->userdata
= i
;
160 static void raw_pull_report_progress(RawPull
*i
, RawProgress p
) {
167 case RAW_DOWNLOADING
: {
168 unsigned remain
= 80;
172 if (i
->checksum_job
) {
173 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
177 if (i
->signature_job
) {
178 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
182 if (i
->settings_job
) {
183 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
187 if (i
->roothash_job
) {
188 percent
+= i
->roothash_job
->progress_percent
* 5 / 100;
192 if (i
->roothash_signature_job
) {
193 percent
+= i
->roothash_signature_job
->progress_percent
* 5 / 100;
198 percent
+= i
->verity_job
->progress_percent
* 10 / 100;
203 percent
+= i
->raw_job
->progress_percent
* remain
/ 100;
224 assert_not_reached();
227 sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent
);
228 log_debug("Combined progress %u%%", percent
);
231 static int raw_pull_maybe_convert_qcow2(RawPull
*i
) {
232 _cleanup_(unlink_and_freep
) char *t
= NULL
;
233 _cleanup_close_
int converted_fd
= -EBADF
;
234 _cleanup_free_
char *f
= NULL
;
239 assert(!FLAGS_SET(i
->flags
, IMPORT_DIRECT
));
241 if (!FLAGS_SET(i
->flags
, IMPORT_CONVERT_QCOW2
))
244 assert(i
->final_path
);
245 assert(i
->raw_job
->close_disk_fd
);
247 r
= qcow2_detect(i
->raw_job
->disk_fd
);
249 return log_error_errno(r
, "Failed to detect whether this is a QCOW2 image: %m");
253 /* This is a QCOW2 image, let's convert it */
254 r
= tempfn_random(i
->final_path
, NULL
, &f
);
258 converted_fd
= open(f
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
259 if (converted_fd
< 0)
260 return log_error_errno(errno
, "Failed to create %s: %m", f
);
264 (void) import_set_nocow_and_log(converted_fd
, t
);
266 log_info("Unpacking QCOW2 file.");
268 r
= qcow2_convert(i
->raw_job
->disk_fd
, converted_fd
);
270 return log_error_errno(r
, "Failed to convert qcow2 image: %m");
272 unlink_and_free(i
->temp_path
);
273 i
->temp_path
= TAKE_PTR(t
);
274 close_and_replace(i
->raw_job
->disk_fd
, converted_fd
);
279 static int raw_pull_determine_path(
282 char **field
/* input + output (!) */) {
293 r
= pull_make_path(i
->raw_job
->url
, i
->raw_job
->etag
, i
->image_root
, ".raw-", suffix
, field
);
300 static int raw_pull_copy_auxiliary_file(
303 char **path
/* input + output (!) */) {
305 _cleanup_free_
char *local
= NULL
;
312 r
= raw_pull_determine_path(i
, suffix
, path
);
316 local
= strjoin(i
->image_root
, "/", i
->local
, suffix
);
320 if (FLAGS_SET(i
->flags
, IMPORT_PULL_KEEP_DOWNLOAD
))
321 r
= copy_file_atomic(
326 (FLAGS_SET(i
->flags
, IMPORT_FORCE
) ? COPY_REPLACE
: 0) |
327 (FLAGS_SET(i
->flags
, IMPORT_SYNC
) ? COPY_FSYNC_FULL
: 0));
329 r
= install_file(AT_FDCWD
, *path
,
331 (i
->flags
& IMPORT_FORCE
? INSTALL_REPLACE
: 0) |
332 (i
->flags
& IMPORT_SYNC
? INSTALL_SYNCFS
: 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 install 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 *p
= NULL
;
352 assert(!FLAGS_SET(i
->flags
, IMPORT_DIRECT
));
357 if (i
->raw_job
->etag_exists
) {
358 /* We have downloaded this one previously, reopen it */
360 assert(i
->raw_job
->disk_fd
< 0);
362 i
->raw_job
->disk_fd
= open(i
->final_path
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
363 if (i
->raw_job
->disk_fd
< 0)
364 return log_error_errno(errno
, "Failed to open vendor image: %m");
366 /* We freshly downloaded the image, use it */
368 assert(i
->raw_job
->disk_fd
>= 0);
369 assert(i
->offset
== UINT64_MAX
);
371 if (lseek(i
->raw_job
->disk_fd
, SEEK_SET
, 0) < 0)
372 return log_error_errno(errno
, "Failed to seek to beginning of vendor image: %m");
375 p
= strjoin(i
->image_root
, "/", i
->local
, ".raw");
380 if (FLAGS_SET(i
->flags
, IMPORT_PULL_KEEP_DOWNLOAD
)) {
381 _cleanup_close_
int dfd
= -EBADF
;
382 _cleanup_free_
char *f
= NULL
;
384 r
= tempfn_random(p
, NULL
, &f
);
388 dfd
= open(f
, O_WRONLY
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
390 return log_error_errno(errno
, "Failed to create writable copy of image: %m");
394 /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
395 * since it reduces fragmentation caused by not allowing in-place writes. */
396 (void) import_set_nocow_and_log(dfd
, tp
);
398 r
= copy_bytes(i
->raw_job
->disk_fd
, dfd
, UINT64_MAX
, COPY_REFLINK
);
400 return log_error_errno(r
, "Failed to make writable copy of image: %m");
402 (void) copy_times(i
->raw_job
->disk_fd
, dfd
, COPY_CRTIME
);
403 (void) copy_xattr(i
->raw_job
->disk_fd
, NULL
, dfd
, NULL
, 0);
405 dfd
= safe_close(dfd
);
409 source
= i
->final_path
;
411 r
= install_file(AT_FDCWD
, source
,
413 (i
->flags
& IMPORT_FORCE
? INSTALL_REPLACE
: 0) |
414 (i
->flags
& IMPORT_READ_ONLY
? INSTALL_READ_ONLY
: 0) |
415 (i
->flags
& IMPORT_SYNC
? INSTALL_FSYNC_FULL
: 0));
417 return log_error_errno(r
, "Failed to move local image into place '%s': %m", p
);
421 log_info("Created new local image '%s'.", i
->local
);
423 if (FLAGS_SET(i
->flags
, IMPORT_PULL_SETTINGS
)) {
424 r
= raw_pull_copy_auxiliary_file(i
, ".nspawn", &i
->settings_path
);
429 if (FLAGS_SET(i
->flags
, IMPORT_PULL_ROOTHASH
)) {
430 r
= raw_pull_copy_auxiliary_file(i
, ".roothash", &i
->roothash_path
);
435 if (FLAGS_SET(i
->flags
, IMPORT_PULL_ROOTHASH_SIGNATURE
)) {
436 r
= raw_pull_copy_auxiliary_file(i
, ".roothash.p7s", &i
->roothash_signature_path
);
441 if (FLAGS_SET(i
->flags
, IMPORT_PULL_VERITY
)) {
442 r
= raw_pull_copy_auxiliary_file(i
, ".verity", &i
->verity_path
);
450 static bool raw_pull_is_done(RawPull
*i
) {
454 if (!PULL_JOB_IS_COMPLETE(i
->raw_job
))
456 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
458 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
460 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
462 if (i
->roothash_job
&& !PULL_JOB_IS_COMPLETE(i
->roothash_job
))
464 if (i
->roothash_signature_job
&& !PULL_JOB_IS_COMPLETE(i
->roothash_signature_job
))
466 if (i
->verity_job
&& !PULL_JOB_IS_COMPLETE(i
->verity_job
))
472 static int raw_pull_rename_auxiliary_file(
486 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
487 * incorporate it in the file name if we can */
488 *path
= mfree(*path
);
489 r
= raw_pull_determine_path(i
, suffix
, path
);
494 AT_FDCWD
, *temp_path
,
497 (i
->flags
& IMPORT_SYNC
? INSTALL_FSYNC_FULL
: 0));
499 return log_error_errno(r
, "Failed to move '%s' into place: %m", *path
);
501 *temp_path
= mfree(*temp_path
);
505 static void raw_pull_job_on_finished(PullJob
*j
) {
515 /* Only the main job and the checksum job are fatal if they fail. The other fails are just
516 * "decoration", that we'll download if we can. The signature job isn't fatal here because we
517 * might not actually need it in case Suse style signatures are used, that are inline in the
520 if (j
== i
->raw_job
) {
521 if (j
->error
== ENOMEDIUM
) /* HTTP 404 */
522 r
= log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
524 r
= log_error_errno(j
->error
, "Failed to retrieve image file.");
526 } else if (j
== i
->checksum_job
) {
527 r
= log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
529 } else if (j
== i
->signature_job
)
530 log_debug_errno(j
->error
, "Signature job for %s failed, proceeding for now.", j
->url
);
531 else if (j
== i
->settings_job
)
532 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
533 else if (j
== i
->roothash_job
)
534 log_info_errno(j
->error
, "Root hash file could not be retrieved, proceeding without.");
535 else if (j
== i
->roothash_signature_job
)
536 log_info_errno(j
->error
, "Root hash signature file could not be retrieved, proceeding without.");
537 else if (j
== i
->verity_job
)
538 log_info_errno(j
->error
, "Verity integrity file could not be retrieved, proceeding without.");
540 assert_not_reached();
543 /* This is invoked if either the download completed successfully, or the download was skipped because
544 * we already have the etag. In this case ->etag_exists is true.
546 * We only do something when we got all files */
548 if (!raw_pull_is_done(i
))
551 if (i
->signature_job
&& i
->signature_job
->error
!= 0) {
552 VerificationStyle style
;
555 /* The signature job failed. Let's see if we actually need it */
557 verify_job
= i
->checksum_job
?: i
->raw_job
; /* if the checksum job doesn't exist this must be
558 * because the main job is the checksum file
563 r
= verification_style_from_url(verify_job
->url
, &style
);
565 log_error_errno(r
, "Failed to determine verification style from checksum URL: %m");
569 if (style
== VERIFICATION_PER_DIRECTORY
) { /* A failed signature file download only matters
570 * in per-directory verification mode, since only
571 * then the signature is detached, and thus a file
573 r
= log_error_errno(i
->signature_job
->error
,
574 "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
580 /* Let's close these auxiliary files now, we don't need access to them anymore. */
581 FOREACH_ARGUMENT(jj
, i
->settings_job
, i
->roothash_job
, i
->roothash_signature_job
, i
->verity_job
)
582 pull_job_close_disk_fd(jj
);
584 if (!i
->raw_job
->etag_exists
) {
585 raw_pull_report_progress(i
, RAW_VERIFYING
);
587 r
= pull_verify(i
->verify
,
594 i
->roothash_signature_job
,
600 if (i
->flags
& IMPORT_DIRECT
) {
601 assert(!i
->settings_job
);
602 assert(!i
->roothash_job
);
603 assert(!i
->roothash_signature_job
);
604 assert(!i
->verity_job
);
606 raw_pull_report_progress(i
, RAW_FINALIZING
);
609 r
= install_file(AT_FDCWD
, i
->local
,
611 ((i
->flags
& IMPORT_READ_ONLY
) && i
->offset
== UINT64_MAX
? INSTALL_READ_ONLY
: 0) |
612 (i
->flags
& IMPORT_SYNC
? INSTALL_FSYNC_FULL
: 0));
614 log_error_errno(r
, "Failed to finalize raw file to '%s': %m", i
->local
);
619 r
= raw_pull_determine_path(i
, ".raw", &i
->final_path
);
623 if (!i
->raw_job
->etag_exists
) {
624 /* This is a new download, verify it, and move it into place */
626 assert(i
->temp_path
);
627 assert(i
->final_path
);
629 raw_pull_report_progress(i
, RAW_UNPACKING
);
631 r
= raw_pull_maybe_convert_qcow2(i
);
635 raw_pull_report_progress(i
, RAW_FINALIZING
);
637 r
= install_file(AT_FDCWD
, i
->temp_path
,
638 AT_FDCWD
, i
->final_path
,
639 (i
->flags
& IMPORT_PULL_KEEP_DOWNLOAD
? INSTALL_READ_ONLY
: 0) |
640 (i
->flags
& IMPORT_SYNC
? INSTALL_FSYNC_FULL
: 0));
642 log_error_errno(r
, "Failed to move raw file to '%s': %m", i
->final_path
);
646 i
->temp_path
= mfree(i
->temp_path
);
648 if (i
->settings_job
&&
649 i
->settings_job
->error
== 0) {
650 r
= raw_pull_rename_auxiliary_file(i
, ".nspawn", &i
->settings_temp_path
, &i
->settings_path
);
655 if (i
->roothash_job
&&
656 i
->roothash_job
->error
== 0) {
657 r
= raw_pull_rename_auxiliary_file(i
, ".roothash", &i
->roothash_temp_path
, &i
->roothash_path
);
662 if (i
->roothash_signature_job
&&
663 i
->roothash_signature_job
->error
== 0) {
664 r
= raw_pull_rename_auxiliary_file(i
, ".roothash.p7s", &i
->roothash_signature_temp_path
, &i
->roothash_signature_path
);
670 i
->verity_job
->error
== 0) {
671 r
= raw_pull_rename_auxiliary_file(i
, ".verity", &i
->verity_temp_path
, &i
->verity_path
);
677 raw_pull_report_progress(i
, RAW_COPYING
);
679 r
= raw_pull_make_local_copy(i
);
688 i
->on_finished(i
, r
, i
->userdata
);
690 sd_event_exit(i
->event
, r
);
693 static int raw_pull_job_on_open_disk_generic(
697 char **temp_path
/* input + output */) {
706 assert(!FLAGS_SET(i
->flags
, IMPORT_DIRECT
));
709 r
= tempfn_random_child(i
->image_root
, extra
, temp_path
);
714 (void) mkdir_parents_label(*temp_path
, 0700);
716 j
->disk_fd
= open(*temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
718 return log_error_errno(errno
, "Failed to create %s: %m", *temp_path
);
723 static int raw_pull_job_on_open_disk_raw(PullJob
*j
) {
731 assert(i
->raw_job
== j
);
732 assert(j
->disk_fd
< 0);
734 if (i
->flags
& IMPORT_DIRECT
) {
736 if (!i
->local
) { /* If no local name specified, the pull job will write its data to stdout */
737 j
->disk_fd
= STDOUT_FILENO
;
738 j
->close_disk_fd
= false;
742 (void) mkdir_parents_label(i
->local
, 0700);
744 j
->disk_fd
= open(i
->local
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
|(i
->offset
== UINT64_MAX
? O_TRUNC
|O_CREAT
: 0), 0664);
746 return log_error_errno(errno
, "Failed to open destination '%s': %m", i
->local
);
748 if (i
->offset
== UINT64_MAX
)
749 (void) import_set_nocow_and_log(j
->disk_fd
, i
->local
);
752 r
= raw_pull_job_on_open_disk_generic(i
, j
, "raw", &i
->temp_path
);
756 assert(i
->offset
== UINT64_MAX
);
757 (void) import_set_nocow_and_log(j
->disk_fd
, i
->temp_path
);
763 static int raw_pull_job_on_open_disk_settings(PullJob
*j
) {
770 assert(i
->settings_job
== j
);
772 return raw_pull_job_on_open_disk_generic(i
, j
, "settings", &i
->settings_temp_path
);
775 static int raw_pull_job_on_open_disk_roothash(PullJob
*j
) {
782 assert(i
->roothash_job
== j
);
784 return raw_pull_job_on_open_disk_generic(i
, j
, "roothash", &i
->roothash_temp_path
);
787 static int raw_pull_job_on_open_disk_roothash_signature(PullJob
*j
) {
794 assert(i
->roothash_signature_job
== j
);
796 return raw_pull_job_on_open_disk_generic(i
, j
, "roothash.p7s", &i
->roothash_signature_temp_path
);
799 static int raw_pull_job_on_open_disk_verity(PullJob
*j
) {
806 assert(i
->verity_job
== j
);
808 return raw_pull_job_on_open_disk_generic(i
, j
, "verity", &i
->verity_temp_path
);
811 static void raw_pull_job_on_progress(PullJob
*j
) {
819 raw_pull_report_progress(i
, RAW_DOWNLOADING
);
830 const char *checksum
) {
836 assert(verify
== _IMPORT_VERIFY_INVALID
|| verify
< _IMPORT_VERIFY_MAX
);
837 assert(verify
== _IMPORT_VERIFY_INVALID
|| verify
>= 0);
838 assert((verify
< 0) || !checksum
);
839 assert(!(flags
& ~IMPORT_PULL_FLAGS_MASK_RAW
));
840 assert(offset
== UINT64_MAX
|| FLAGS_SET(flags
, IMPORT_DIRECT
));
841 assert(!(flags
& (IMPORT_PULL_SETTINGS
|IMPORT_PULL_ROOTHASH
|IMPORT_PULL_ROOTHASH_SIGNATURE
|IMPORT_PULL_VERITY
)) || !(flags
& IMPORT_DIRECT
));
842 assert(!(flags
& (IMPORT_PULL_SETTINGS
|IMPORT_PULL_ROOTHASH
|IMPORT_PULL_ROOTHASH_SIGNATURE
|IMPORT_PULL_VERITY
)) || !checksum
);
844 if (!http_url_is_valid(url
) && !file_url_is_valid(url
))
847 if (local
&& !pull_validate_local(local
, flags
))
853 r
= free_and_strdup(&i
->local
, local
);
857 r
= free_and_strdup(&i
->checksum
, checksum
);
864 /* Queue job for the image itself */
865 r
= pull_job_new(&i
->raw_job
, url
, i
->glue
, i
);
869 i
->raw_job
->on_finished
= raw_pull_job_on_finished
;
870 i
->raw_job
->on_open_disk
= raw_pull_job_on_open_disk_raw
;
873 i
->raw_job
->calc_checksum
= true;
874 else if (verify
!= IMPORT_VERIFY_NO
) {
875 /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
876 * signature, which we let gpg verify instead. */
878 r
= pull_url_needs_checksum(url
);
882 i
->raw_job
->calc_checksum
= r
;
883 i
->raw_job
->force_memory
= true; /* make sure this is both written to disk if that's
884 * requested and into memory, since we need to verify it */
887 if (size_max
!= UINT64_MAX
)
888 i
->raw_job
->uncompressed_max
= size_max
;
889 if (offset
!= UINT64_MAX
)
890 i
->raw_job
->offset
= i
->offset
= offset
;
892 if (!FLAGS_SET(flags
, IMPORT_DIRECT
)) {
893 r
= pull_find_old_etags(url
, i
->image_root
, DT_REG
, ".raw-", ".raw", &i
->raw_job
->old_etags
);
898 r
= pull_make_verification_jobs(
905 raw_pull_job_on_finished
,
910 if (FLAGS_SET(flags
, IMPORT_PULL_SETTINGS
)) {
911 r
= pull_make_auxiliary_job(
918 raw_pull_job_on_open_disk_settings
,
919 raw_pull_job_on_finished
,
925 if (FLAGS_SET(flags
, IMPORT_PULL_ROOTHASH
)) {
926 r
= pull_make_auxiliary_job(
933 raw_pull_job_on_open_disk_roothash
,
934 raw_pull_job_on_finished
,
940 if (FLAGS_SET(flags
, IMPORT_PULL_ROOTHASH_SIGNATURE
)) {
941 r
= pull_make_auxiliary_job(
942 &i
->roothash_signature_job
,
948 raw_pull_job_on_open_disk_roothash_signature
,
949 raw_pull_job_on_finished
,
955 if (FLAGS_SET(flags
, IMPORT_PULL_VERITY
)) {
956 r
= pull_make_auxiliary_job(
963 raw_pull_job_on_open_disk_verity
,
964 raw_pull_job_on_finished
,
977 i
->roothash_signature_job
,
983 j
->on_progress
= raw_pull_job_on_progress
;
984 j
->sync
= FLAGS_SET(flags
, IMPORT_SYNC
);
986 r
= pull_job_begin(j
);