]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-raw.c
import: append % to X_IMPORT_PROGRESS=
[thirdparty/systemd.git] / src / import / pull-raw.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
90199220 2
90199220 3#include <curl/curl.h>
07630cea
LP
4#include <linux/fs.h>
5#include <sys/xattr.h>
90199220 6
7079cfef 7#include "sd-daemon.h"
07630cea 8
b5efdb8a 9#include "alloc-util.h"
0d6e763b 10#include "btrfs-util.h"
07630cea
LP
11#include "copy.h"
12#include "curl-util.h"
3ffd4af2 13#include "fd-util.h"
f4f15635 14#include "fs-util.h"
07630cea
LP
15#include "hostname-util.h"
16#include "import-common.h"
17#include "import-util.h"
c40d82ab 18#include "install-file.h"
0d6e763b 19#include "macro.h"
35cd0ba5 20#include "mkdir-label.h"
26166c88 21#include "path-util.h"
dc2c282b 22#include "pull-common.h"
07630cea 23#include "pull-job.h"
3ffd4af2 24#include "pull-raw.h"
07630cea
LP
25#include "qcow2-util.h"
26#include "rm-rf.h"
27#include "string-util.h"
28#include "strv.h"
e4de7287 29#include "tmpfile-util.h"
07630cea 30#include "utf8.h"
49cf4170 31#include "web-util.h"
90199220 32
7079cfef
LP
33typedef enum RawProgress {
34 RAW_DOWNLOADING,
35 RAW_VERIFYING,
36 RAW_UNPACKING,
37 RAW_FINALIZING,
38 RAW_COPYING,
39} RawProgress;
90199220 40
dc2c282b 41struct RawPull {
90199220
LP
42 sd_event *event;
43 CurlGlue *glue;
44
133b34f6
LP
45 PullFlags flags;
46 ImportVerify verify;
087682d1 47 char *image_root;
90199220 48
c40d82ab
LP
49 uint64_t offset;
50
dc2c282b
LP
51 PullJob *raw_job;
52 PullJob *checksum_job;
53 PullJob *signature_job;
133b34f6
LP
54 PullJob *settings_job;
55 PullJob *roothash_job;
56 PullJob *roothash_signature_job;
57 PullJob *verity_job;
90199220 58
dc2c282b 59 RawPullFinished on_finished;
0d6e763b 60 void *userdata;
90199220 61
c40d82ab
LP
62 char *local; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
63 * machine name of the final copy we make */
8620a9a3 64
0d6e763b 65 char *final_path;
9854730b
LP
66 char *temp_path;
67
68 char *settings_path;
69 char *settings_temp_path;
8f695058 70
91359193
LP
71 char *roothash_path;
72 char *roothash_temp_path;
73
133b34f6
LP
74 char *roothash_signature_path;
75 char *roothash_signature_temp_path;
76
77 char *verity_path;
78 char *verity_temp_path;
c40d82ab
LP
79
80 char *checksum;
0d6e763b 81};
49bb233b 82
dc2c282b 83RawPull* raw_pull_unref(RawPull *i) {
0d6e763b 84 if (!i)
90199220
LP
85 return NULL;
86
dc2c282b
LP
87 pull_job_unref(i->raw_job);
88 pull_job_unref(i->checksum_job);
89 pull_job_unref(i->signature_job);
133b34f6
LP
90 pull_job_unref(i->settings_job);
91 pull_job_unref(i->roothash_job);
92 pull_job_unref(i->roothash_signature_job);
93 pull_job_unref(i->verity_job);
90199220 94
0d6e763b
LP
95 curl_glue_unref(i->glue);
96 sd_event_unref(i->event);
90199220 97
133b34f6
LP
98 unlink_and_free(i->temp_path);
99 unlink_and_free(i->settings_temp_path);
100 unlink_and_free(i->roothash_temp_path);
101 unlink_and_free(i->roothash_signature_temp_path);
102 unlink_and_free(i->verity_temp_path);
9854730b 103
0d6e763b 104 free(i->final_path);
9854730b 105 free(i->settings_path);
133b34f6
LP
106 free(i->roothash_path);
107 free(i->roothash_signature_path);
108 free(i->verity_path);
0d6e763b
LP
109 free(i->image_root);
110 free(i->local);
c40d82ab 111 free(i->checksum);
133b34f6 112
6b430fdb 113 return mfree(i);
90199220
LP
114}
115
dc2c282b
LP
116int raw_pull_new(
117 RawPull **ret,
8b71fce8
LP
118 sd_event *event,
119 const char *image_root,
dc2c282b 120 RawPullFinished on_finished,
8b71fce8
LP
121 void *userdata) {
122
0d94088e
YW
123 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
124 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
dc2c282b 125 _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
0d94088e 126 _cleanup_free_ char *root = NULL;
0d6e763b 127 int r;
8620a9a3 128
0d6e763b 129 assert(ret);
8620a9a3 130
0d94088e
YW
131 root = strdup(image_root ?: "/var/lib/machines");
132 if (!root)
8620a9a3
LP
133 return -ENOMEM;
134
0d6e763b 135 if (event)
0d94088e 136 e = sd_event_ref(event);
0d6e763b 137 else {
0d94088e 138 r = sd_event_default(&e);
dfd1520d 139 if (r < 0)
0d6e763b 140 return r;
2f64ba0e 141 }
8620a9a3 142
0d94088e 143 r = curl_glue_new(&g, e);
2f64ba0e 144 if (r < 0)
0d6e763b 145 return r;
8620a9a3 146
0d94088e
YW
147 i = new(RawPull, 1);
148 if (!i)
149 return -ENOMEM;
150
151 *i = (RawPull) {
152 .on_finished = on_finished,
153 .userdata = userdata,
154 .image_root = TAKE_PTR(root),
0d94088e
YW
155 .event = TAKE_PTR(e),
156 .glue = TAKE_PTR(g),
c40d82ab 157 .offset = UINT64_MAX,
0d94088e
YW
158 };
159
dc2c282b 160 i->glue->on_finished = pull_job_curl_on_finished;
0d6e763b 161 i->glue->userdata = i;
8620a9a3 162
1cc6c93a 163 *ret = TAKE_PTR(i);
8620a9a3 164
2f64ba0e
LP
165 return 0;
166}
167
dc2c282b 168static void raw_pull_report_progress(RawPull *i, RawProgress p) {
7079cfef
LP
169 unsigned percent;
170
171 assert(i);
172
173 switch (p) {
174
175 case RAW_DOWNLOADING: {
176 unsigned remain = 80;
177
178 percent = 0;
179
133b34f6
LP
180 if (i->checksum_job) {
181 percent += i->checksum_job->progress_percent * 5 / 100;
182 remain -= 5;
183 }
184
185 if (i->signature_job) {
186 percent += i->signature_job->progress_percent * 5 / 100;
187 remain -= 5;
188 }
189
9854730b
LP
190 if (i->settings_job) {
191 percent += i->settings_job->progress_percent * 5 / 100;
192 remain -= 5;
193 }
194
91359193
LP
195 if (i->roothash_job) {
196 percent += i->roothash_job->progress_percent * 5 / 100;
197 remain -= 5;
198 }
199
133b34f6
LP
200 if (i->roothash_signature_job) {
201 percent += i->roothash_signature_job->progress_percent * 5 / 100;
7079cfef
LP
202 remain -= 5;
203 }
204
133b34f6
LP
205 if (i->verity_job) {
206 percent += i->verity_job->progress_percent * 10 / 100;
207 remain -= 10;
7079cfef
LP
208 }
209
210 if (i->raw_job)
211 percent += i->raw_job->progress_percent * remain / 100;
212 break;
213 }
214
215 case RAW_VERIFYING:
216 percent = 80;
217 break;
218
219 case RAW_UNPACKING:
220 percent = 85;
221 break;
222
223 case RAW_FINALIZING:
224 percent = 90;
225 break;
226
227 case RAW_COPYING:
228 percent = 95;
229 break;
230
231 default:
04499a70 232 assert_not_reached();
7079cfef
LP
233 }
234
a986de68 235 sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
7079cfef
LP
236 log_debug("Combined progress %u%%", percent);
237}
238
dc2c282b 239static int raw_pull_maybe_convert_qcow2(RawPull *i) {
c40d82ab 240 _cleanup_(unlink_and_freep) char *t = NULL;
254d1313 241 _cleanup_close_ int converted_fd = -EBADF;
c40d82ab 242 _cleanup_free_ char *f = NULL;
edce2aed
LP
243 int r;
244
0d6e763b
LP
245 assert(i);
246 assert(i->raw_job);
c40d82ab
LP
247 assert(!FLAGS_SET(i->flags, PULL_DIRECT));
248
249 if (!FLAGS_SET(i->flags, PULL_CONVERT_QCOW2))
250 return 0;
251
252 assert(i->final_path);
253 assert(i->raw_job->close_disk_fd);
edce2aed 254
0d6e763b 255 r = qcow2_detect(i->raw_job->disk_fd);
edce2aed
LP
256 if (r < 0)
257 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
258 if (r == 0)
259 return 0;
260
261 /* This is a QCOW2 image, let's convert it */
c40d82ab 262 r = tempfn_random(i->final_path, NULL, &f);
edce2aed
LP
263 if (r < 0)
264 return log_oom();
265
c40d82ab 266 converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
edce2aed 267 if (converted_fd < 0)
c40d82ab
LP
268 return log_error_errno(errno, "Failed to create %s: %m", f);
269
270 t = TAKE_PTR(f);
edce2aed 271
137c6c6b 272 (void) import_set_nocow_and_log(converted_fd, t);
0d6e763b 273
ec5cb56e
LP
274 log_info("Unpacking QCOW2 file.");
275
0d6e763b 276 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
c40d82ab 277 if (r < 0)
edce2aed 278 return log_error_errno(r, "Failed to convert qcow2 image: %m");
edce2aed 279
c40d82ab
LP
280 unlink_and_free(i->temp_path);
281 i->temp_path = TAKE_PTR(t);
ee3455cf 282 close_and_replace(i->raw_job->disk_fd, converted_fd);
edce2aed
LP
283
284 return 1;
285}
286
c40d82ab
LP
287static int raw_pull_determine_path(
288 RawPull *i,
289 const char *suffix,
290 char **field /* input + output (!) */) {
91359193
LP
291 int r;
292
293 assert(i);
294 assert(field);
295
296 if (*field)
297 return 0;
298
299 assert(i->raw_job);
300
301 r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", suffix, field);
302 if (r < 0)
303 return log_oom();
304
305 return 1;
306}
307
308static int raw_pull_copy_auxiliary_file(
309 RawPull *i,
310 const char *suffix,
c40d82ab 311 char **path /* input + output (!) */) {
91359193
LP
312
313 const char *local;
314 int r;
315
316 assert(i);
317 assert(suffix);
318 assert(path);
319
320 r = raw_pull_determine_path(i, suffix, path);
321 if (r < 0)
322 return r;
323
324 local = strjoina(i->image_root, "/", i->local, suffix);
325
c40d82ab
LP
326 r = copy_file_atomic(
327 *path,
328 local,
329 0644,
c40d82ab
LP
330 COPY_REFLINK |
331 (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
332 (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
91359193
LP
333 if (r == -EEXIST)
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.");
337 else if (r < 0)
338 log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local);
339 else
340 log_info("Created new file %s.", local);
341
342 return 0;
343}
344
dc2c282b 345static int raw_pull_make_local_copy(RawPull *i) {
c40d82ab
LP
346 _cleanup_(unlink_and_freep) char *tp = NULL;
347 _cleanup_free_ char *f = NULL;
254d1313 348 _cleanup_close_ int dfd = -EBADF;
0d6e763b 349 const char *p;
90199220
LP
350 int r;
351
0d6e763b
LP
352 assert(i);
353 assert(i->raw_job);
c40d82ab 354 assert(!FLAGS_SET(i->flags, PULL_DIRECT));
90199220 355
0d6e763b 356 if (!i->local)
90199220
LP
357 return 0;
358
85dbc41d
LP
359 if (i->raw_job->etag_exists) {
360 /* We have downloaded this one previously, reopen it */
361
362 assert(i->raw_job->disk_fd < 0);
363
0d6e763b
LP
364 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
365 if (i->raw_job->disk_fd < 0)
366 return log_error_errno(errno, "Failed to open vendor image: %m");
85dbc41d
LP
367 } else {
368 /* We freshly downloaded the image, use it */
369
370 assert(i->raw_job->disk_fd >= 0);
c40d82ab 371 assert(i->offset == UINT64_MAX);
85dbc41d 372
86cbbc6d 373 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) < 0)
85dbc41d 374 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
90199220
LP
375 }
376
63c372cb 377 p = strjoina(i->image_root, "/", i->local, ".raw");
49bb233b 378
c40d82ab 379 r = tempfn_random(p, NULL, &f);
49bb233b 380 if (r < 0)
0d6e763b 381 return log_oom();
49bb233b 382
c40d82ab 383 dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
0d6e763b
LP
384 if (dfd < 0)
385 return log_error_errno(errno, "Failed to create writable copy of image: %m");
49bb233b 386
c40d82ab
LP
387 tp = TAKE_PTR(f);
388
137c6c6b
LP
389 /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
390 * since it reduces fragmentation caused by not allowing in-place writes. */
391 (void) import_set_nocow_and_log(dfd, tp);
90199220 392
f5fbe71d 393 r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
c40d82ab 394 if (r < 0)
0d6e763b 395 return log_error_errno(r, "Failed to make writable copy of image: %m");
90199220 396
adc6f43b 397 (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
c17cfe6e 398 (void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
1e20b411 399
0d6e763b 400 dfd = safe_close(dfd);
1e20b411 401
c40d82ab
LP
402 r = install_file(AT_FDCWD, tp,
403 AT_FDCWD, p,
404 (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
405 (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
406 (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
407 if (r < 0)
408 return log_error_errno(errno, "Failed to move local image into place '%s': %m", p);
409
410 tp = mfree(tp);
1e20b411 411
0d6e763b 412 log_info("Created new local image '%s'.", i->local);
9854730b 413
133b34f6
LP
414 if (FLAGS_SET(i->flags, PULL_SETTINGS)) {
415 r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
416 if (r < 0)
417 return r;
418 }
419
420 if (FLAGS_SET(i->flags, PULL_ROOTHASH)) {
91359193
LP
421 r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
422 if (r < 0)
423 return r;
424 }
9854730b 425
133b34f6
LP
426 if (FLAGS_SET(i->flags, PULL_ROOTHASH_SIGNATURE)) {
427 r = raw_pull_copy_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_path);
428 if (r < 0)
429 return r;
430 }
431
432 if (FLAGS_SET(i->flags, PULL_VERITY)) {
433 r = raw_pull_copy_auxiliary_file(i, ".verity", &i->verity_path);
91359193
LP
434 if (r < 0)
435 return r;
9854730b
LP
436 }
437
1e20b411
LP
438 return 0;
439}
440
dc2c282b 441static bool raw_pull_is_done(RawPull *i) {
8b71fce8
LP
442 assert(i);
443 assert(i->raw_job);
444
9854730b 445 if (!PULL_JOB_IS_COMPLETE(i->raw_job))
8b71fce8 446 return false;
133b34f6
LP
447 if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
448 return false;
449 if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
91359193 450 return false;
9854730b 451 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
8b71fce8 452 return false;
133b34f6 453 if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
9854730b 454 return false;
133b34f6
LP
455 if (i->roothash_signature_job && !PULL_JOB_IS_COMPLETE(i->roothash_signature_job))
456 return false;
457 if (i->verity_job && !PULL_JOB_IS_COMPLETE(i->verity_job))
8b71fce8
LP
458 return false;
459
460 return true;
461}
462
91359193
LP
463static int raw_pull_rename_auxiliary_file(
464 RawPull *i,
465 const char *suffix,
466 char **temp_path,
467 char **path) {
468
469 int r;
470
471 assert(i);
c40d82ab 472 assert(path);
91359193 473 assert(temp_path);
c40d82ab 474 assert(*temp_path);
91359193 475 assert(suffix);
91359193 476
e0061812 477 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
91359193
LP
478 * incorporate it in the file name if we can */
479 *path = mfree(*path);
480 r = raw_pull_determine_path(i, suffix, path);
481 if (r < 0)
482 return r;
483
c40d82ab
LP
484 r = install_file(
485 AT_FDCWD, *temp_path,
486 AT_FDCWD, *path,
487 INSTALL_READ_ONLY|
488 (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
91359193 489 if (r < 0)
c40d82ab 490 return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
91359193
LP
491
492 *temp_path = mfree(*temp_path);
91359193
LP
493 return 1;
494}
495
dc2c282b
LP
496static void raw_pull_job_on_finished(PullJob *j) {
497 RawPull *i;
c40d82ab 498 PullJob *jj;
8620a9a3
LP
499 int r;
500
0d6e763b
LP
501 assert(j);
502 assert(j->userdata);
8620a9a3 503
0d6e763b 504 i = j->userdata;
c40d82ab
LP
505
506 if (j->error != 0) {
507 /* Only the main job and the checksum job are fatal if they fail. The other fails are just
508 * "decoration", that we'll download if we can. The signature job isn't fatal here because we
509 * might not actually need it in case Suse style signatures are used, that are inline in the
510 * checksum file. */
511
512 if (j == i->raw_job) {
513 if (j->error == ENOMEDIUM) /* HTTP 404 */
514 r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
515 else
516 r = log_error_errno(j->error, "Failed to retrieve image file.");
517 goto finish;
518 } else if (j == i->checksum_job) {
519 r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
520 goto finish;
521 } else if (j == i->signature_job)
522 log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
523 else if (j == i->settings_job)
133b34f6 524 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
c40d82ab 525 else if (j == i->roothash_job)
91359193 526 log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
c40d82ab 527 else if (j == i->roothash_signature_job)
133b34f6 528 log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
c40d82ab
LP
529 else if (j == i->verity_job)
530 log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
3576d631 531 else
c40d82ab 532 assert_not_reached();
8620a9a3
LP
533 }
534
133b34f6
LP
535 /* This is invoked if either the download completed successfully, or the download was skipped because
536 * we already have the etag. In this case ->etag_exists is true.
3576d631 537 *
c40d82ab 538 * We only do something when we got all files */
85dbc41d 539
dc2c282b 540 if (!raw_pull_is_done(i))
3576d631 541 return;
8620a9a3 542
f14717a7
LP
543 if (i->signature_job && i->signature_job->error != 0) {
544 VerificationStyle style;
c40d82ab
LP
545 PullJob *verify_job;
546
547 /* The signature job failed. Let's see if we actually need it */
548
549 verify_job = i->checksum_job ?: i->raw_job; /* if the checksum job doesn't exist this must be
550 * because the main job is the checksum file
551 * itself */
552
553 assert(verify_job);
697be0be 554
c40d82ab 555 r = verification_style_from_url(verify_job->url, &style);
f14717a7
LP
556 if (r < 0) {
557 log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
558 goto finish;
559 }
560
561 if (style == VERIFICATION_PER_DIRECTORY) { /* A failed signature file download only matters
562 * in per-directory verification mode, since only
563 * then the signature is detached, and thus a file
564 * of its own. */
c40d82ab
LP
565 r = log_error_errno(i->signature_job->error,
566 "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
f14717a7
LP
567 goto finish;
568 }
697be0be
TB
569 }
570
c40d82ab
LP
571 /* Let's close these auxiliary files now, we don't need access to them anymore. */
572 FOREACH_POINTER(jj, i->settings_job, i->roothash_job, i->roothash_signature_job, i->verity_job)
573 pull_job_close_disk_fd(jj);
91359193 574
3576d631 575 if (!i->raw_job->etag_exists) {
dc2c282b 576 raw_pull_report_progress(i, RAW_VERIFYING);
7079cfef 577
ff2f7797 578 r = pull_verify(i->verify,
c40d82ab 579 i->checksum,
ff2f7797
LP
580 i->raw_job,
581 i->checksum_job,
582 i->signature_job,
583 i->settings_job,
584 i->roothash_job,
585 i->roothash_signature_job,
586 i->verity_job);
0d6e763b
LP
587 if (r < 0)
588 goto finish;
c40d82ab 589 }
8620a9a3 590
c40d82ab
LP
591 if (i->flags & PULL_DIRECT) {
592 assert(!i->settings_job);
593 assert(!i->roothash_job);
594 assert(!i->roothash_signature_job);
595 assert(!i->verity_job);
596
597 raw_pull_report_progress(i, RAW_FINALIZING);
7079cfef 598
c40d82ab
LP
599 if (i->local) {
600 r = install_file(AT_FDCWD, i->local,
601 AT_FDCWD, NULL,
602 ((i->flags & PULL_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
603 (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
604 if (r < 0) {
605 log_error_errno(r, "Failed to finalize raw file to '%s': %m", i->local);
606 goto finish;
607 }
608 }
609 } else {
610 r = raw_pull_determine_path(i, ".raw", &i->final_path);
0d6e763b
LP
611 if (r < 0)
612 goto finish;
85dbc41d 613
c40d82ab
LP
614 if (!i->raw_job->etag_exists) {
615 /* This is a new download, verify it, and move it into place */
616
617 assert(i->temp_path);
618 assert(i->final_path);
7079cfef 619
c40d82ab
LP
620 raw_pull_report_progress(i, RAW_UNPACKING);
621
622 r = raw_pull_maybe_convert_qcow2(i);
50dfca2e
KK
623 if (r < 0)
624 goto finish;
625
c40d82ab
LP
626 raw_pull_report_progress(i, RAW_FINALIZING);
627
628 r = install_file(AT_FDCWD, i->temp_path,
629 AT_FDCWD, i->final_path,
630 INSTALL_READ_ONLY|
631 (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
50dfca2e 632 if (r < 0) {
c40d82ab 633 log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
50dfca2e
KK
634 goto finish;
635 }
8f695058 636
c40d82ab 637 i->temp_path = mfree(i->temp_path);
9854730b 638
c40d82ab
LP
639 if (i->settings_job &&
640 i->settings_job->error == 0) {
641 r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
642 if (r < 0)
643 goto finish;
644 }
9854730b 645
c40d82ab
LP
646 if (i->roothash_job &&
647 i->roothash_job->error == 0) {
648 r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
649 if (r < 0)
650 goto finish;
651 }
652
653 if (i->roothash_signature_job &&
654 i->roothash_signature_job->error == 0) {
655 r = raw_pull_rename_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_temp_path, &i->roothash_signature_path);
656 if (r < 0)
657 goto finish;
658 }
659
660 if (i->verity_job &&
661 i->verity_job->error == 0) {
662 r = raw_pull_rename_auxiliary_file(i, ".verity", &i->verity_temp_path, &i->verity_path);
663 if (r < 0)
664 goto finish;
665 }
9854730b 666 }
8620a9a3 667
c40d82ab 668 raw_pull_report_progress(i, RAW_COPYING);
7079cfef 669
c40d82ab
LP
670 r = raw_pull_make_local_copy(i);
671 if (r < 0)
672 goto finish;
673 }
90199220 674
0d6e763b 675 r = 0;
3576d631 676
0d6e763b 677finish:
3576d631
LP
678 if (i->on_finished)
679 i->on_finished(i, r, i->userdata);
680 else
681 sd_event_exit(i->event, r);
0d6e763b 682}
90199220 683
91359193
LP
684static int raw_pull_job_on_open_disk_generic(
685 RawPull *i,
686 PullJob *j,
687 const char *extra,
c40d82ab 688 char **temp_path /* input + output */) {
91359193 689
91359193
LP
690 int r;
691
692 assert(i);
693 assert(j);
694 assert(extra);
695 assert(temp_path);
696
c40d82ab
LP
697 assert(!FLAGS_SET(i->flags, PULL_DIRECT));
698
91359193
LP
699 if (!*temp_path) {
700 r = tempfn_random_child(i->image_root, extra, temp_path);
701 if (r < 0)
702 return log_oom();
703 }
704
705 (void) mkdir_parents_label(*temp_path, 0700);
706
707 j->disk_fd = open(*temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
708 if (j->disk_fd < 0)
709 return log_error_errno(errno, "Failed to create %s: %m", *temp_path);
710
711 return 0;
712}
713
9854730b 714static int raw_pull_job_on_open_disk_raw(PullJob *j) {
dc2c282b 715 RawPull *i;
0d6e763b 716 int r;
90199220 717
0d6e763b
LP
718 assert(j);
719 assert(j->userdata);
90199220 720
0d6e763b 721 i = j->userdata;
8b71fce8 722 assert(i->raw_job == j);
c40d82ab 723 assert(j->disk_fd < 0);
90199220 724
c40d82ab
LP
725 if (i->flags & PULL_DIRECT) {
726
727 if (!i->local) { /* If no local name specified, the pull job will write its data to stdout */
728 j->disk_fd = STDOUT_FILENO;
729 j->close_disk_fd = false;
730 return 0;
731 }
732
733 (void) mkdir_parents_label(i->local, 0700);
734
735 j->disk_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
736 if (j->disk_fd < 0)
737 return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
738
739 if (i->offset == UINT64_MAX)
740 (void) import_set_nocow_and_log(j->disk_fd, i->local);
741
742 } else {
743 r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
744 if (r < 0)
745 return r;
746
747 assert(i->offset == UINT64_MAX);
748 (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
749 }
1e20b411 750
90199220
LP
751 return 0;
752}
753
133b34f6
LP
754static int raw_pull_job_on_open_disk_settings(PullJob *j) {
755 RawPull *i;
756
757 assert(j);
758 assert(j->userdata);
759
760 i = j->userdata;
761 assert(i->settings_job == j);
762
763 return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path);
764}
765
91359193 766static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
9854730b 767 RawPull *i;
9854730b
LP
768
769 assert(j);
770 assert(j->userdata);
771
772 i = j->userdata;
91359193 773 assert(i->roothash_job == j);
9854730b 774
91359193
LP
775 return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
776}
9854730b 777
133b34f6 778static int raw_pull_job_on_open_disk_roothash_signature(PullJob *j) {
91359193 779 RawPull *i;
9854730b 780
91359193
LP
781 assert(j);
782 assert(j->userdata);
9854730b 783
91359193 784 i = j->userdata;
133b34f6 785 assert(i->roothash_signature_job == j);
9854730b 786
133b34f6
LP
787 return raw_pull_job_on_open_disk_generic(i, j, "roothash.p7s", &i->roothash_signature_temp_path);
788}
789
790static int raw_pull_job_on_open_disk_verity(PullJob *j) {
791 RawPull *i;
792
793 assert(j);
794 assert(j->userdata);
795
796 i = j->userdata;
797 assert(i->verity_job == j);
798
799 return raw_pull_job_on_open_disk_generic(i, j, "verity", &i->verity_temp_path);
9854730b
LP
800}
801
dc2c282b
LP
802static void raw_pull_job_on_progress(PullJob *j) {
803 RawPull *i;
7079cfef
LP
804
805 assert(j);
806 assert(j->userdata);
807
808 i = j->userdata;
809
dc2c282b 810 raw_pull_report_progress(i, RAW_DOWNLOADING);
7079cfef
LP
811}
812
9854730b
LP
813int raw_pull_start(
814 RawPull *i,
815 const char *url,
816 const char *local,
c40d82ab
LP
817 uint64_t offset,
818 uint64_t size_max,
133b34f6 819 PullFlags flags,
c40d82ab
LP
820 ImportVerify verify,
821 const char *checksum) {
9854730b 822
c40d82ab 823 PullJob *j;
90199220
LP
824 int r;
825
0d6e763b 826 assert(i);
c40d82ab
LP
827 assert(url);
828 assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
829 assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
830 assert((verify < 0) || !checksum);
133b34f6 831 assert(!(flags & ~PULL_FLAGS_MASK_RAW));
c40d82ab
LP
832 assert(offset == UINT64_MAX || FLAGS_SET(flags, PULL_DIRECT));
833 assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !(flags & PULL_DIRECT));
834 assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !checksum);
90199220 835
c456862f 836 if (!http_url_is_valid(url) && !file_url_is_valid(url))
0d6e763b 837 return -EINVAL;
90199220 838
c40d82ab 839 if (local && !pull_validate_local(local, flags))
0d6e763b 840 return -EINVAL;
087682d1 841
8b71fce8
LP
842 if (i->raw_job)
843 return -EBUSY;
844
0d6e763b 845 r = free_and_strdup(&i->local, local);
90199220
LP
846 if (r < 0)
847 return r;
9854730b 848
c40d82ab
LP
849 r = free_and_strdup(&i->checksum, checksum);
850 if (r < 0)
851 return r;
852
133b34f6 853 i->flags = flags;
8f695058 854 i->verify = verify;
90199220 855
85dbc41d 856 /* Queue job for the image itself */
dc2c282b 857 r = pull_job_new(&i->raw_job, url, i->glue, i);
90199220
LP
858 if (r < 0)
859 return r;
860
dc2c282b 861 i->raw_job->on_finished = raw_pull_job_on_finished;
9854730b 862 i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
133b34f6 863
c40d82ab
LP
864 if (checksum)
865 i->raw_job->calc_checksum = true;
866 else if (verify != IMPORT_VERIFY_NO) {
867 /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
868 * signature, which we let gpg verify instead. */
133b34f6 869
c40d82ab 870 r = pull_url_needs_checksum(url);
91359193
LP
871 if (r < 0)
872 return r;
873
c40d82ab
LP
874 i->raw_job->calc_checksum = r;
875 i->raw_job->force_memory = true; /* make sure this is both written to disk if that's
876 * requested and into memory, since we need to verify it */
91359193
LP
877 }
878
c40d82ab
LP
879 if (size_max != UINT64_MAX)
880 i->raw_job->uncompressed_max = size_max;
881 if (offset != UINT64_MAX)
882 i->raw_job->offset = i->offset = offset;
9854730b 883
c40d82ab
LP
884 if (!FLAGS_SET(flags, PULL_DIRECT)) {
885 r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
133b34f6
LP
886 if (r < 0)
887 return r;
133b34f6 888 }
85dbc41d 889
c40d82ab
LP
890 r = pull_make_verification_jobs(
891 &i->checksum_job,
892 &i->signature_job,
893 verify,
894 i->checksum,
895 url,
896 i->glue,
897 raw_pull_job_on_finished,
898 i);
85dbc41d
LP
899 if (r < 0)
900 return r;
901
c40d82ab
LP
902 if (FLAGS_SET(flags, PULL_SETTINGS)) {
903 r = pull_make_auxiliary_job(
904 &i->settings_job,
905 url,
906 raw_strip_suffixes,
907 ".nspawn",
908 verify,
909 i->glue,
910 raw_pull_job_on_open_disk_settings,
911 raw_pull_job_on_finished,
912 i);
133b34f6
LP
913 if (r < 0)
914 return r;
915 }
916
c40d82ab
LP
917 if (FLAGS_SET(flags, PULL_ROOTHASH)) {
918 r = pull_make_auxiliary_job(
919 &i->roothash_job,
920 url,
921 raw_strip_suffixes,
922 ".roothash",
923 verify,
924 i->glue,
925 raw_pull_job_on_open_disk_roothash,
926 raw_pull_job_on_finished,
927 i);
91359193
LP
928 if (r < 0)
929 return r;
930 }
931
c40d82ab
LP
932 if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
933 r = pull_make_auxiliary_job(
934 &i->roothash_signature_job,
935 url,
936 raw_strip_suffixes,
937 ".roothash.p7s",
938 verify,
939 i->glue,
940 raw_pull_job_on_open_disk_roothash_signature,
941 raw_pull_job_on_finished,
942 i);
9854730b
LP
943 if (r < 0)
944 return r;
945 }
946
c40d82ab
LP
947 if (FLAGS_SET(flags, PULL_VERITY)) {
948 r = pull_make_auxiliary_job(
949 &i->verity_job,
950 url,
951 raw_strip_suffixes,
952 ".verity",
953 verify,
954 i->glue,
955 raw_pull_job_on_open_disk_verity,
956 raw_pull_job_on_finished,
957 i);
3576d631
LP
958 if (r < 0)
959 return r;
960 }
961
c40d82ab
LP
962 FOREACH_POINTER(j,
963 i->raw_job,
964 i->checksum_job,
965 i->signature_job,
966 i->settings_job,
967 i->roothash_job,
968 i->roothash_signature_job,
969 i->verity_job) {
970
971 if (!j)
972 continue;
973
974 j->on_progress = raw_pull_job_on_progress;
975 j->sync = FLAGS_SET(flags, PULL_SYNC);
7079cfef 976
c40d82ab 977 r = pull_job_begin(j);
3576d631
LP
978 if (r < 0)
979 return r;
980 }
981
85dbc41d 982 return 0;
90199220 983}