1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
11 #include "curl-util.h"
14 #include "hostname-util.h"
15 #include "import-common.h"
16 #include "import-util.h"
19 #include "path-util.h"
20 #include "process-util.h"
21 #include "pull-common.h"
25 #include "string-util.h"
27 #include "tmpfile-util.h"
32 typedef enum TarProgress
{
46 PullJob
*settings_job
;
47 PullJob
*checksum_job
;
48 PullJob
*signature_job
;
50 TarPullFinished on_finished
;
63 char *settings_temp_path
;
68 TarPull
* tar_pull_unref(TarPull
*i
) {
73 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
74 (void) wait_for_terminate(i
->tar_pid
, NULL
);
77 pull_job_unref(i
->tar_job
);
78 pull_job_unref(i
->settings_job
);
79 pull_job_unref(i
->checksum_job
);
80 pull_job_unref(i
->signature_job
);
82 curl_glue_unref(i
->glue
);
83 sd_event_unref(i
->event
);
86 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
90 if (i
->settings_temp_path
) {
91 (void) unlink(i
->settings_temp_path
);
92 free(i
->settings_temp_path
);
96 free(i
->settings_path
);
106 const char *image_root
,
107 TarPullFinished on_finished
,
110 _cleanup_(curl_glue_unrefp
) CurlGlue
*g
= NULL
;
111 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
112 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
113 _cleanup_free_
char *root
= NULL
;
118 root
= strdup(image_root
?: "/var/lib/machines");
123 e
= sd_event_ref(event
);
125 r
= sd_event_default(&e
);
130 r
= curl_glue_new(&g
, e
);
139 .on_finished
= on_finished
,
140 .userdata
= userdata
,
141 .image_root
= TAKE_PTR(root
),
142 .event
= TAKE_PTR(e
),
146 i
->glue
->on_finished
= pull_job_curl_on_finished
;
147 i
->glue
->userdata
= i
;
154 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
161 case TAR_DOWNLOADING
: {
162 unsigned remain
= 85;
166 if (i
->settings_job
) {
167 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
171 if (i
->checksum_job
) {
172 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
176 if (i
->signature_job
) {
177 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
182 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
199 assert_not_reached("Unknown progress state");
202 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
203 log_debug("Combined progress %u%%", percent
);
206 static int tar_pull_determine_path(TarPull
*i
, const char *suffix
, char **field
) {
217 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", suffix
, field
);
224 static int tar_pull_make_local_copy(TarPull
*i
) {
233 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
238 const char *local_settings
;
239 assert(i
->settings_job
);
241 r
= tar_pull_determine_path(i
, ".nspawn", &i
->settings_path
);
245 local_settings
= strjoina(i
->image_root
, "/", i
->local
, ".nspawn");
247 r
= copy_file_atomic(i
->settings_path
, local_settings
, 0664, 0, 0, COPY_REFLINK
| (i
->force_local
? COPY_REPLACE
: 0));
249 log_warning_errno(r
, "Settings file %s already exists, not replacing.", local_settings
);
250 else if (r
== -ENOENT
)
251 log_debug_errno(r
, "Skipping creation of settings file, since none was found.");
253 log_warning_errno(r
, "Failed to copy settings files %s, ignoring: %m", local_settings
);
255 log_info("Created new settings file %s.", local_settings
);
261 static bool tar_pull_is_done(TarPull
*i
) {
265 if (!PULL_JOB_IS_COMPLETE(i
->tar_job
))
267 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
269 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
271 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
277 static void tar_pull_job_on_finished(PullJob
*j
) {
286 if (j
== i
->settings_job
) {
288 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
289 } else if (j
->error
!= 0 && j
!= i
->signature_job
) {
290 if (j
== i
->checksum_job
)
291 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
293 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
299 /* This is invoked if either the download completed successfully, or the download was skipped because
300 * we already have the etag. */
302 if (!tar_pull_is_done(i
))
305 if (i
->signature_job
&& i
->signature_job
->error
!= 0) {
306 VerificationStyle style
;
308 r
= verification_style_from_url(i
->checksum_job
->url
, &style
);
310 log_error_errno(r
, "Failed to determine verification style from checksum URL: %m");
314 if (style
== VERIFICATION_PER_DIRECTORY
) { /* A failed signature file download only matters
315 * in per-directory verification mode, since only
316 * then the signature is detached, and thus a file
318 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
319 r
= i
->signature_job
->error
;
324 i
->tar_job
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
326 i
->settings_job
->disk_fd
= safe_close(i
->settings_job
->disk_fd
);
328 r
= tar_pull_determine_path(i
, NULL
, &i
->final_path
);
332 if (i
->tar_pid
> 0) {
333 r
= wait_for_terminate_and_check("tar", i
->tar_pid
, WAIT_LOG
);
337 if (r
!= EXIT_SUCCESS
) {
343 if (!i
->tar_job
->etag_exists
) {
344 /* This is a new download, verify it, and move it into place */
346 tar_pull_report_progress(i
, TAR_VERIFYING
);
348 r
= pull_verify(i
->tar_job
, NULL
, i
->settings_job
, i
->checksum_job
, i
->signature_job
);
352 tar_pull_report_progress(i
, TAR_FINALIZING
);
354 r
= import_mangle_os_tree(i
->temp_path
);
358 r
= import_make_read_only(i
->temp_path
);
362 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
364 log_error_errno(r
, "Failed to rename to final image name to %s: %m", i
->final_path
);
368 i
->temp_path
= mfree(i
->temp_path
);
370 if (i
->settings_job
&&
371 i
->settings_job
->error
== 0) {
373 /* Also move the settings file into place, if it exists. Note that we do so only if we also
374 * moved the tar file in place, to keep things strictly in sync. */
375 assert(i
->settings_temp_path
);
377 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
378 * we should incorporate it in the file name if we can */
379 i
->settings_path
= mfree(i
->settings_path
);
381 r
= tar_pull_determine_path(i
, ".nspawn", &i
->settings_path
);
385 r
= import_make_read_only(i
->settings_temp_path
);
389 r
= rename_noreplace(AT_FDCWD
, i
->settings_temp_path
, AT_FDCWD
, i
->settings_path
);
391 log_error_errno(r
, "Failed to rename settings file to %s: %m", i
->settings_path
);
395 i
->settings_temp_path
= mfree(i
->settings_temp_path
);
399 tar_pull_report_progress(i
, TAR_COPYING
);
401 r
= tar_pull_make_local_copy(i
);
409 i
->on_finished(i
, r
, i
->userdata
);
411 sd_event_exit(i
->event
, r
);
414 static int tar_pull_job_on_open_disk_tar(PullJob
*j
) {
422 assert(i
->tar_job
== j
);
423 assert(i
->tar_pid
<= 0);
426 r
= tempfn_random_child(i
->image_root
, "tar", &i
->temp_path
);
431 mkdir_parents_label(i
->temp_path
, 0700);
433 r
= btrfs_subvol_make_fallback(i
->temp_path
, 0755);
435 return log_error_errno(r
, "Failed to create directory/subvolume %s: %m", i
->temp_path
);
436 if (r
> 0) /* actually btrfs subvol */
437 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
439 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
446 static int tar_pull_job_on_open_disk_settings(PullJob
*j
) {
454 assert(i
->settings_job
== j
);
456 if (!i
->settings_temp_path
) {
457 r
= tempfn_random_child(i
->image_root
, "settings", &i
->settings_temp_path
);
462 mkdir_parents_label(i
->settings_temp_path
, 0700);
464 j
->disk_fd
= open(i
->settings_temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
466 return log_error_errno(errno
, "Failed to create %s: %m", i
->settings_temp_path
);
471 static void tar_pull_job_on_progress(PullJob
*j
) {
479 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
493 assert(verify
< _IMPORT_VERIFY_MAX
);
496 if (!http_url_is_valid(url
))
499 if (local
&& !hostname_is_valid(local
, 0))
505 r
= free_and_strdup(&i
->local
, local
);
509 i
->force_local
= force_local
;
511 i
->settings
= settings
;
513 /* Set up download job for TAR file */
514 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
518 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
519 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk_tar
;
520 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
521 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
523 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
527 /* Set up download job for the settings file (.nspawn) */
529 r
= pull_make_auxiliary_job(&i
->settings_job
, url
, tar_strip_suffixes
, ".nspawn", i
->glue
, tar_pull_job_on_finished
, i
);
533 i
->settings_job
->on_open_disk
= tar_pull_job_on_open_disk_settings
;
534 i
->settings_job
->on_progress
= tar_pull_job_on_progress
;
535 i
->settings_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
538 /* Set up download of checksum/signature files */
539 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
543 r
= pull_job_begin(i
->tar_job
);
547 if (i
->settings_job
) {
548 r
= pull_job_begin(i
->settings_job
);
553 if (i
->checksum_job
) {
554 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
555 i
->checksum_job
->on_not_found
= pull_job_restart_with_sha256sum
;
557 r
= pull_job_begin(i
->checksum_job
);
562 if (i
->signature_job
) {
563 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
565 r
= pull_job_begin(i
->signature_job
);