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 "hostname-util.h"
37 #include "import-util.h"
38 #include "import-common.h"
39 #include "curl-util.h"
40 #include "qcow2-util.h"
42 #include "pull-common.h"
45 typedef enum RawProgress
{
60 PullJob
*checksum_job
;
61 PullJob
*signature_job
;
63 RawPullFinished on_finished
;
68 bool grow_machine_directory
;
76 RawPull
* raw_pull_unref(RawPull
*i
) {
80 pull_job_unref(i
->raw_job
);
81 pull_job_unref(i
->checksum_job
);
82 pull_job_unref(i
->signature_job
);
84 curl_glue_unref(i
->glue
);
85 sd_event_unref(i
->event
);
88 (void) unlink(i
->temp_path
);
103 const char *image_root
,
104 RawPullFinished on_finished
,
107 _cleanup_(raw_pull_unrefp
) RawPull
*i
= NULL
;
112 i
= new0(RawPull
, 1);
116 i
->on_finished
= on_finished
;
117 i
->userdata
= userdata
;
119 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
123 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
126 i
->event
= sd_event_ref(event
);
128 r
= sd_event_default(&i
->event
);
133 r
= curl_glue_new(&i
->glue
, i
->event
);
137 i
->glue
->on_finished
= pull_job_curl_on_finished
;
138 i
->glue
->userdata
= i
;
146 static void raw_pull_report_progress(RawPull
*i
, RawProgress p
) {
153 case RAW_DOWNLOADING
: {
154 unsigned remain
= 80;
158 if (i
->checksum_job
) {
159 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
163 if (i
->signature_job
) {
164 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
169 percent
+= i
->raw_job
->progress_percent
* remain
/ 100;
190 assert_not_reached("Unknown progress state");
193 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
194 log_debug("Combined progress %u%%", percent
);
197 static int raw_pull_maybe_convert_qcow2(RawPull
*i
) {
198 _cleanup_close_
int converted_fd
= -1;
199 _cleanup_free_
char *t
= NULL
;
205 r
= qcow2_detect(i
->raw_job
->disk_fd
);
207 return log_error_errno(r
, "Failed to detect whether this is a QCOW2 image: %m");
211 /* This is a QCOW2 image, let's convert it */
212 r
= tempfn_random(i
->final_path
, NULL
, &t
);
216 converted_fd
= open(t
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
217 if (converted_fd
< 0)
218 return log_error_errno(errno
, "Failed to create %s: %m", t
);
220 r
= chattr_fd(converted_fd
, FS_NOCOW_FL
, FS_NOCOW_FL
);
222 log_warning_errno(errno
, "Failed to set file attributes on %s: %m", t
);
224 log_info("Unpacking QCOW2 file.");
226 r
= qcow2_convert(i
->raw_job
->disk_fd
, converted_fd
);
229 return log_error_errno(r
, "Failed to convert qcow2 image: %m");
232 (void) unlink(i
->temp_path
);
237 safe_close(i
->raw_job
->disk_fd
);
238 i
->raw_job
->disk_fd
= converted_fd
;
244 static int raw_pull_make_local_copy(RawPull
*i
) {
245 _cleanup_free_
char *tp
= NULL
;
246 _cleanup_close_
int dfd
= -1;
256 if (i
->raw_job
->etag_exists
) {
257 /* We have downloaded this one previously, reopen it */
259 assert(i
->raw_job
->disk_fd
< 0);
261 if (!i
->final_path
) {
262 r
= pull_make_path(i
->raw_job
->url
, i
->raw_job
->etag
, i
->image_root
, ".raw-", ".raw", &i
->final_path
);
267 i
->raw_job
->disk_fd
= open(i
->final_path
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
268 if (i
->raw_job
->disk_fd
< 0)
269 return log_error_errno(errno
, "Failed to open vendor image: %m");
271 /* We freshly downloaded the image, use it */
273 assert(i
->raw_job
->disk_fd
>= 0);
275 if (lseek(i
->raw_job
->disk_fd
, SEEK_SET
, 0) == (off_t
) -1)
276 return log_error_errno(errno
, "Failed to seek to beginning of vendor image: %m");
279 p
= strjoina(i
->image_root
, "/", i
->local
, ".raw");
282 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
284 r
= tempfn_random(p
, NULL
, &tp
);
288 dfd
= open(tp
, O_WRONLY
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
290 return log_error_errno(errno
, "Failed to create writable copy of image: %m");
292 /* Turn off COW writing. This should greatly improve
293 * performance on COW file systems like btrfs, since it
294 * reduces fragmentation caused by not allowing in-place
296 r
= chattr_fd(dfd
, FS_NOCOW_FL
, FS_NOCOW_FL
);
298 log_warning_errno(errno
, "Failed to set file attributes on %s: %m", tp
);
300 r
= copy_bytes(i
->raw_job
->disk_fd
, dfd
, (off_t
) -1, true);
303 return log_error_errno(r
, "Failed to make writable copy of image: %m");
306 (void) copy_times(i
->raw_job
->disk_fd
, dfd
);
307 (void) copy_xattr(i
->raw_job
->disk_fd
, dfd
);
309 dfd
= safe_close(dfd
);
314 return log_error_errno(errno
, "Failed to move writable image into place: %m");
317 log_info("Created new local image '%s'.", i
->local
);
321 static bool raw_pull_is_done(RawPull
*i
) {
325 if (i
->raw_job
->state
!= PULL_JOB_DONE
)
327 if (i
->checksum_job
&& i
->checksum_job
->state
!= PULL_JOB_DONE
)
329 if (i
->signature_job
&& i
->signature_job
->state
!= PULL_JOB_DONE
)
335 static void raw_pull_job_on_finished(PullJob
*j
) {
344 if (j
== i
->checksum_job
)
345 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
346 else if (j
== i
->signature_job
)
347 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
349 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
355 /* This is invoked if either the download completed
356 * successfully, or the download was skipped because we
357 * already have the etag. In this case ->etag_exists is
360 * We only do something when we got all three files */
362 if (!raw_pull_is_done(i
))
365 if (!i
->raw_job
->etag_exists
) {
366 /* This is a new download, verify it, and move it into place */
367 assert(i
->raw_job
->disk_fd
>= 0);
369 raw_pull_report_progress(i
, RAW_VERIFYING
);
371 r
= pull_verify(i
->raw_job
, i
->checksum_job
, i
->signature_job
);
375 raw_pull_report_progress(i
, RAW_UNPACKING
);
377 r
= raw_pull_maybe_convert_qcow2(i
);
381 raw_pull_report_progress(i
, RAW_FINALIZING
);
383 r
= import_make_read_only_fd(i
->raw_job
->disk_fd
);
387 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
389 log_error_errno(r
, "Failed to move RAW file into place: %m");
397 raw_pull_report_progress(i
, RAW_COPYING
);
399 r
= raw_pull_make_local_copy(i
);
407 i
->on_finished(i
, r
, i
->userdata
);
409 sd_event_exit(i
->event
, r
);
412 static int raw_pull_job_on_open_disk(PullJob
*j
) {
420 assert(i
->raw_job
== j
);
421 assert(!i
->final_path
);
422 assert(!i
->temp_path
);
424 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".raw-", ".raw", &i
->final_path
);
428 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
432 (void) mkdir_parents_label(i
->temp_path
, 0700);
434 j
->disk_fd
= open(i
->temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
436 return log_error_errno(errno
, "Failed to create %s: %m", i
->temp_path
);
438 r
= chattr_fd(j
->disk_fd
, FS_NOCOW_FL
, FS_NOCOW_FL
);
440 log_warning_errno(errno
, "Failed to set file attributes on %s: %m", i
->temp_path
);
445 static void raw_pull_job_on_progress(PullJob
*j
) {
453 raw_pull_report_progress(i
, RAW_DOWNLOADING
);
456 int raw_pull_start(RawPull
*i
, const char *url
, const char *local
, bool force_local
, ImportVerify verify
) {
460 assert(verify
< _IMPORT_VERIFY_MAX
);
463 if (!http_url_is_valid(url
))
466 if (local
&& !machine_name_is_valid(local
))
472 r
= free_and_strdup(&i
->local
, local
);
475 i
->force_local
= force_local
;
478 /* Queue job for the image itself */
479 r
= pull_job_new(&i
->raw_job
, url
, i
->glue
, i
);
483 i
->raw_job
->on_finished
= raw_pull_job_on_finished
;
484 i
->raw_job
->on_open_disk
= raw_pull_job_on_open_disk
;
485 i
->raw_job
->on_progress
= raw_pull_job_on_progress
;
486 i
->raw_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
487 i
->raw_job
->grow_machine_directory
= i
->grow_machine_directory
;
489 r
= pull_find_old_etags(url
, i
->image_root
, DT_REG
, ".raw-", ".raw", &i
->raw_job
->old_etags
);
493 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, raw_pull_job_on_finished
, i
);
497 r
= pull_job_begin(i
->raw_job
);
501 if (i
->checksum_job
) {
502 i
->checksum_job
->on_progress
= raw_pull_job_on_progress
;
504 r
= pull_job_begin(i
->checksum_job
);
509 if (i
->signature_job
) {
510 i
->signature_job
->on_progress
= raw_pull_job_on_progress
;
512 r
= pull_job_begin(i
->signature_job
);