1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <curl/curl.h>
22 #include <sys/prctl.h>
24 #include "sd-daemon.h"
26 #include "alloc-util.h"
27 #include "btrfs-util.h"
29 #include "curl-util.h"
33 #include "hostname-util.h"
34 #include "import-common.h"
35 #include "import-util.h"
38 #include "path-util.h"
39 #include "process-util.h"
40 #include "pull-common.h"
44 #include "string-util.h"
50 typedef enum TarProgress
{
64 PullJob
*settings_job
;
65 PullJob
*checksum_job
;
66 PullJob
*signature_job
;
68 TarPullFinished on_finished
;
73 bool grow_machine_directory
;
82 char *settings_temp_path
;
87 TarPull
* tar_pull_unref(TarPull
*i
) {
92 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
93 (void) wait_for_terminate(i
->tar_pid
, NULL
);
96 pull_job_unref(i
->tar_job
);
97 pull_job_unref(i
->settings_job
);
98 pull_job_unref(i
->checksum_job
);
99 pull_job_unref(i
->signature_job
);
101 curl_glue_unref(i
->glue
);
102 sd_event_unref(i
->event
);
105 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
109 if (i
->settings_temp_path
) {
110 (void) unlink(i
->settings_temp_path
);
111 free(i
->settings_temp_path
);
115 free(i
->settings_path
);
125 const char *image_root
,
126 TarPullFinished on_finished
,
129 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
134 i
= new0(TarPull
, 1);
138 i
->on_finished
= on_finished
;
139 i
->userdata
= userdata
;
141 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
145 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
148 i
->event
= sd_event_ref(event
);
150 r
= sd_event_default(&i
->event
);
155 r
= curl_glue_new(&i
->glue
, i
->event
);
159 i
->glue
->on_finished
= pull_job_curl_on_finished
;
160 i
->glue
->userdata
= i
;
168 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
175 case TAR_DOWNLOADING
: {
176 unsigned remain
= 85;
180 if (i
->settings_job
) {
181 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
185 if (i
->checksum_job
) {
186 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
190 if (i
->signature_job
) {
191 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
196 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
213 assert_not_reached("Unknown progress state");
216 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
217 log_debug("Combined progress %u%%", percent
);
220 static int tar_pull_determine_path(TarPull
*i
, const char *suffix
, char **field
) {
231 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", suffix
, field
);
238 static int tar_pull_make_local_copy(TarPull
*i
) {
247 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
252 const char *local_settings
;
253 assert(i
->settings_job
);
255 r
= tar_pull_determine_path(i
, ".nspawn", &i
->settings_path
);
259 local_settings
= strjoina(i
->image_root
, "/", i
->local
, ".nspawn");
261 r
= copy_file_atomic(i
->settings_path
, local_settings
, 0664, 0, COPY_REFLINK
| (i
->force_local
? COPY_REPLACE
: 0));
263 log_warning_errno(r
, "Settings file %s already exists, not replacing.", local_settings
);
264 else if (r
== -ENOENT
)
265 log_debug_errno(r
, "Skipping creation of settings file, since none was found.");
267 log_warning_errno(r
, "Failed to copy settings files %s, ignoring: %m", local_settings
);
269 log_info("Created new settings file %s.", local_settings
);
275 static bool tar_pull_is_done(TarPull
*i
) {
279 if (!PULL_JOB_IS_COMPLETE(i
->tar_job
))
281 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
283 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
285 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
291 static void tar_pull_job_on_finished(PullJob
*j
) {
300 if (j
== i
->settings_job
) {
302 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
303 } else if (j
->error
!= 0 && j
!= i
->signature_job
) {
304 if (j
== i
->checksum_job
)
305 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
307 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
313 /* This is invoked if either the download completed
314 * successfully, or the download was skipped because we
315 * already have the etag. */
317 if (!tar_pull_is_done(i
))
320 if (i
->signature_job
&& i
->checksum_job
->style
== VERIFICATION_PER_DIRECTORY
&& i
->signature_job
->error
!= 0) {
321 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
323 r
= i
->signature_job
->error
;
327 i
->tar_job
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
329 i
->settings_job
->disk_fd
= safe_close(i
->settings_job
->disk_fd
);
331 r
= tar_pull_determine_path(i
, NULL
, &i
->final_path
);
335 if (i
->tar_pid
> 0) {
336 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
346 if (!i
->tar_job
->etag_exists
) {
347 /* This is a new download, verify it, and move it into place */
349 tar_pull_report_progress(i
, TAR_VERIFYING
);
351 r
= pull_verify(i
->tar_job
, NULL
, i
->settings_job
, i
->checksum_job
, i
->signature_job
);
355 tar_pull_report_progress(i
, TAR_FINALIZING
);
357 r
= import_make_read_only(i
->temp_path
);
361 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
363 log_error_errno(r
, "Failed to rename to final image name to %s: %m", i
->final_path
);
367 i
->temp_path
= mfree(i
->temp_path
);
369 if (i
->settings_job
&&
370 i
->settings_job
->error
== 0) {
372 /* Also move the settings file into place, if it exists. Note that we do so only if we also
373 * moved the tar file in place, to keep things strictly in sync. */
374 assert(i
->settings_temp_path
);
376 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
377 * we should incorporate it in the file name if we can */
378 i
->settings_path
= mfree(i
->settings_path
);
380 r
= tar_pull_determine_path(i
, ".nspawn", &i
->settings_path
);
384 r
= import_make_read_only(i
->settings_temp_path
);
388 r
= rename_noreplace(AT_FDCWD
, i
->settings_temp_path
, AT_FDCWD
, i
->settings_path
);
390 log_error_errno(r
, "Failed to rename settings file to %s: %m", i
->settings_path
);
394 i
->settings_temp_path
= mfree(i
->settings_temp_path
);
398 tar_pull_report_progress(i
, TAR_COPYING
);
400 r
= tar_pull_make_local_copy(i
);
408 i
->on_finished(i
, r
, i
->userdata
);
410 sd_event_exit(i
->event
, r
);
413 static int tar_pull_job_on_open_disk_tar(PullJob
*j
) {
421 assert(i
->tar_job
== j
);
422 assert(i
->tar_pid
<= 0);
425 r
= tempfn_random_child(i
->image_root
, "tar", &i
->temp_path
);
430 mkdir_parents_label(i
->temp_path
, 0700);
432 r
= btrfs_subvol_make(i
->temp_path
);
434 if (mkdir(i
->temp_path
, 0755) < 0)
435 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
437 return log_error_errno(r
, "Failed to create subvolume %s: %m", i
->temp_path
);
439 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
441 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
448 static int tar_pull_job_on_open_disk_settings(PullJob
*j
) {
456 assert(i
->settings_job
== j
);
458 if (!i
->settings_temp_path
) {
459 r
= tempfn_random_child(i
->image_root
, "settings", &i
->settings_temp_path
);
464 mkdir_parents_label(i
->settings_temp_path
, 0700);
466 j
->disk_fd
= open(i
->settings_temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
468 return log_error_errno(errno
, "Failed to create %s: %m", i
->settings_temp_path
);
473 static void tar_pull_job_on_progress(PullJob
*j
) {
481 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
495 assert(verify
< _IMPORT_VERIFY_MAX
);
498 if (!http_url_is_valid(url
))
501 if (local
&& !machine_name_is_valid(local
))
507 r
= free_and_strdup(&i
->local
, local
);
511 i
->force_local
= force_local
;
513 i
->settings
= settings
;
515 /* Set up download job for TAR file */
516 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
520 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
521 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk_tar
;
522 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
523 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
524 i
->tar_job
->grow_machine_directory
= i
->grow_machine_directory
;
526 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
530 /* Set up download job for the settings file (.nspawn) */
532 r
= pull_make_auxiliary_job(&i
->settings_job
, url
, tar_strip_suffixes
, ".nspawn", i
->glue
, tar_pull_job_on_finished
, i
);
536 i
->settings_job
->on_open_disk
= tar_pull_job_on_open_disk_settings
;
537 i
->settings_job
->on_progress
= tar_pull_job_on_progress
;
538 i
->settings_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
541 /* Set up download of checksum/signature files */
542 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
546 r
= pull_job_begin(i
->tar_job
);
550 if (i
->settings_job
) {
551 r
= pull_job_begin(i
->settings_job
);
556 if (i
->checksum_job
) {
557 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
558 i
->checksum_job
->style
= VERIFICATION_PER_FILE
;
560 r
= pull_job_begin(i
->checksum_job
);
565 if (i
->signature_job
) {
566 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
568 r
= pull_job_begin(i
->signature_job
);