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