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 <curl/curl.h>
26 #include "sd-daemon.h"
30 #include "btrfs-util.h"
35 #include "path-util.h"
36 #include "import-util.h"
37 #include "import-common.h"
38 #include "curl-util.h"
39 #include "qcow2-util.h"
41 #include "pull-common.h"
44 typedef enum RawProgress
{
59 PullJob
*checksum_job
;
60 PullJob
*signature_job
;
62 RawPullFinished on_finished
;
67 bool grow_machine_directory
;
75 RawPull
* raw_pull_unref(RawPull
*i
) {
79 pull_job_unref(i
->raw_job
);
80 pull_job_unref(i
->checksum_job
);
81 pull_job_unref(i
->signature_job
);
83 curl_glue_unref(i
->glue
);
84 sd_event_unref(i
->event
);
87 (void) unlink(i
->temp_path
);
102 const char *image_root
,
103 RawPullFinished on_finished
,
106 _cleanup_(raw_pull_unrefp
) RawPull
*i
= NULL
;
111 i
= new0(RawPull
, 1);
115 i
->on_finished
= on_finished
;
116 i
->userdata
= userdata
;
118 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
122 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
125 i
->event
= sd_event_ref(event
);
127 r
= sd_event_default(&i
->event
);
132 r
= curl_glue_new(&i
->glue
, i
->event
);
136 i
->glue
->on_finished
= pull_job_curl_on_finished
;
137 i
->glue
->userdata
= i
;
145 static void raw_pull_report_progress(RawPull
*i
, RawProgress p
) {
152 case RAW_DOWNLOADING
: {
153 unsigned remain
= 80;
157 if (i
->checksum_job
) {
158 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
162 if (i
->signature_job
) {
163 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
168 percent
+= i
->raw_job
->progress_percent
* remain
/ 100;
189 assert_not_reached("Unknown progress state");
192 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
193 log_debug("Combined progress %u%%", percent
);
196 static int raw_pull_maybe_convert_qcow2(RawPull
*i
) {
197 _cleanup_close_
int converted_fd
= -1;
198 _cleanup_free_
char *t
= NULL
;
204 r
= qcow2_detect(i
->raw_job
->disk_fd
);
206 return log_error_errno(r
, "Failed to detect whether this is a QCOW2 image: %m");
210 /* This is a QCOW2 image, let's convert it */
211 r
= tempfn_random(i
->final_path
, &t
);
215 converted_fd
= open(t
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
216 if (converted_fd
< 0)
217 return log_error_errno(errno
, "Failed to create %s: %m", t
);
219 r
= chattr_fd(converted_fd
, true, FS_NOCOW_FL
);
221 log_warning_errno(errno
, "Failed to set file attributes on %s: %m", t
);
223 log_info("Unpacking QCOW2 file.");
225 r
= qcow2_convert(i
->raw_job
->disk_fd
, converted_fd
);
228 return log_error_errno(r
, "Failed to convert qcow2 image: %m");
231 (void) unlink(i
->temp_path
);
236 safe_close(i
->raw_job
->disk_fd
);
237 i
->raw_job
->disk_fd
= converted_fd
;
243 static int raw_pull_make_local_copy(RawPull
*i
) {
244 _cleanup_free_
char *tp
= NULL
;
245 _cleanup_close_
int dfd
= -1;
255 if (i
->raw_job
->etag_exists
) {
256 /* We have downloaded this one previously, reopen it */
258 assert(i
->raw_job
->disk_fd
< 0);
260 if (!i
->final_path
) {
261 r
= pull_make_path(i
->raw_job
->url
, i
->raw_job
->etag
, i
->image_root
, ".raw-", ".raw", &i
->final_path
);
266 i
->raw_job
->disk_fd
= open(i
->final_path
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
267 if (i
->raw_job
->disk_fd
< 0)
268 return log_error_errno(errno
, "Failed to open vendor image: %m");
270 /* We freshly downloaded the image, use it */
272 assert(i
->raw_job
->disk_fd
>= 0);
274 if (lseek(i
->raw_job
->disk_fd
, SEEK_SET
, 0) == (off_t
) -1)
275 return log_error_errno(errno
, "Failed to seek to beginning of vendor image: %m");
278 p
= strjoina(i
->image_root
, "/", i
->local
, ".raw");
280 if (i
->force_local
) {
281 (void) btrfs_subvol_remove(p
);
282 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
285 r
= tempfn_random(p
, &tp
);
289 dfd
= open(tp
, O_WRONLY
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
291 return log_error_errno(errno
, "Failed to create writable copy of image: %m");
293 /* Turn off COW writing. This should greatly improve
294 * performance on COW file systems like btrfs, since it
295 * reduces fragmentation caused by not allowing in-place
297 r
= chattr_fd(dfd
, true, FS_NOCOW_FL
);
299 log_warning_errno(errno
, "Failed to set file attributes on %s: %m", tp
);
301 r
= copy_bytes(i
->raw_job
->disk_fd
, dfd
, (off_t
) -1, true);
304 return log_error_errno(r
, "Failed to make writable copy of image: %m");
307 (void) copy_times(i
->raw_job
->disk_fd
, dfd
);
308 (void) copy_xattr(i
->raw_job
->disk_fd
, dfd
);
310 dfd
= safe_close(dfd
);
315 return log_error_errno(errno
, "Failed to move writable image into place: %m");
318 log_info("Created new local image '%s'.", i
->local
);
322 static bool raw_pull_is_done(RawPull
*i
) {
326 if (i
->raw_job
->state
!= PULL_JOB_DONE
)
328 if (i
->checksum_job
&& i
->checksum_job
->state
!= PULL_JOB_DONE
)
330 if (i
->signature_job
&& i
->signature_job
->state
!= PULL_JOB_DONE
)
336 static void raw_pull_job_on_finished(PullJob
*j
) {
345 if (j
== i
->checksum_job
)
346 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
347 else if (j
== i
->signature_job
)
348 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
350 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
356 /* This is invoked if either the download completed
357 * successfully, or the download was skipped because we
358 * already have the etag. In this case ->etag_exists is
361 * We only do something when we got all three files */
363 if (!raw_pull_is_done(i
))
366 if (!i
->raw_job
->etag_exists
) {
367 /* This is a new download, verify it, and move it into place */
368 assert(i
->raw_job
->disk_fd
>= 0);
370 raw_pull_report_progress(i
, RAW_VERIFYING
);
372 r
= pull_verify(i
->raw_job
, i
->checksum_job
, i
->signature_job
);
376 raw_pull_report_progress(i
, RAW_UNPACKING
);
378 r
= raw_pull_maybe_convert_qcow2(i
);
382 raw_pull_report_progress(i
, RAW_FINALIZING
);
384 r
= import_make_read_only_fd(i
->raw_job
->disk_fd
);
388 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
390 log_error_errno(r
, "Failed to move RAW file into place: %m");
398 raw_pull_report_progress(i
, RAW_COPYING
);
400 r
= raw_pull_make_local_copy(i
);
408 i
->on_finished(i
, r
, i
->userdata
);
410 sd_event_exit(i
->event
, r
);
413 static int raw_pull_job_on_open_disk(PullJob
*j
) {
421 assert(i
->raw_job
== j
);
422 assert(!i
->final_path
);
423 assert(!i
->temp_path
);
425 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".raw-", ".raw", &i
->final_path
);
429 r
= tempfn_random(i
->final_path
, &i
->temp_path
);
433 (void) mkdir_parents_label(i
->temp_path
, 0700);
435 j
->disk_fd
= open(i
->temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
437 return log_error_errno(errno
, "Failed to create %s: %m", i
->temp_path
);
439 r
= chattr_fd(j
->disk_fd
, true, FS_NOCOW_FL
);
441 log_warning_errno(errno
, "Failed to set file attributes on %s: %m", i
->temp_path
);
446 static void raw_pull_job_on_progress(PullJob
*j
) {
454 raw_pull_report_progress(i
, RAW_DOWNLOADING
);
457 int raw_pull_start(RawPull
*i
, const char *url
, const char *local
, bool force_local
, ImportVerify verify
) {
461 assert(verify
< _IMPORT_VERIFY_MAX
);
464 if (!http_url_is_valid(url
))
467 if (local
&& !machine_name_is_valid(local
))
473 r
= free_and_strdup(&i
->local
, local
);
476 i
->force_local
= force_local
;
479 /* Queue job for the image itself */
480 r
= pull_job_new(&i
->raw_job
, url
, i
->glue
, i
);
484 i
->raw_job
->on_finished
= raw_pull_job_on_finished
;
485 i
->raw_job
->on_open_disk
= raw_pull_job_on_open_disk
;
486 i
->raw_job
->on_progress
= raw_pull_job_on_progress
;
487 i
->raw_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
488 i
->raw_job
->grow_machine_directory
= i
->grow_machine_directory
;
490 r
= pull_find_old_etags(url
, i
->image_root
, DT_REG
, ".raw-", ".raw", &i
->raw_job
->old_etags
);
494 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, raw_pull_job_on_finished
, i
);
498 r
= pull_job_begin(i
->raw_job
);
502 if (i
->checksum_job
) {
503 i
->checksum_job
->on_progress
= raw_pull_job_on_progress
;
505 r
= pull_job_begin(i
->checksum_job
);
510 if (i
->signature_job
) {
511 i
->signature_job
->on_progress
= raw_pull_job_on_progress
;
513 r
= pull_job_begin(i
->signature_job
);