]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull-raw.c
3befa96a042cb45d6e0718f1025d1e400fe54a5e
[thirdparty/systemd.git] / src / import / pull-raw.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <curl/curl.h>
4 #include <linux/fs.h>
5 #include <sys/xattr.h>
6
7 #include "sd-daemon.h"
8
9 #include "alloc-util.h"
10 #include "btrfs-util.h"
11 #include "copy.h"
12 #include "curl-util.h"
13 #include "fd-util.h"
14 #include "fs-util.h"
15 #include "hostname-util.h"
16 #include "import-common.h"
17 #include "import-util.h"
18 #include "install-file.h"
19 #include "macro.h"
20 #include "mkdir-label.h"
21 #include "path-util.h"
22 #include "pull-common.h"
23 #include "pull-job.h"
24 #include "pull-raw.h"
25 #include "qcow2-util.h"
26 #include "rm-rf.h"
27 #include "string-util.h"
28 #include "strv.h"
29 #include "tmpfile-util.h"
30 #include "utf8.h"
31 #include "web-util.h"
32
33 typedef enum RawProgress {
34 RAW_DOWNLOADING,
35 RAW_VERIFYING,
36 RAW_UNPACKING,
37 RAW_FINALIZING,
38 RAW_COPYING,
39 } RawProgress;
40
41 struct RawPull {
42 sd_event *event;
43 CurlGlue *glue;
44
45 PullFlags flags;
46 ImportVerify verify;
47 char *image_root;
48
49 uint64_t offset;
50
51 PullJob *raw_job;
52 PullJob *checksum_job;
53 PullJob *signature_job;
54 PullJob *settings_job;
55 PullJob *roothash_job;
56 PullJob *roothash_signature_job;
57 PullJob *verity_job;
58
59 RawPullFinished on_finished;
60 void *userdata;
61
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 */
64
65 char *final_path;
66 char *temp_path;
67
68 char *settings_path;
69 char *settings_temp_path;
70
71 char *roothash_path;
72 char *roothash_temp_path;
73
74 char *roothash_signature_path;
75 char *roothash_signature_temp_path;
76
77 char *verity_path;
78 char *verity_temp_path;
79
80 char *checksum;
81 };
82
83 RawPull* raw_pull_unref(RawPull *i) {
84 if (!i)
85 return NULL;
86
87 pull_job_unref(i->raw_job);
88 pull_job_unref(i->checksum_job);
89 pull_job_unref(i->signature_job);
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);
94
95 curl_glue_unref(i->glue);
96 sd_event_unref(i->event);
97
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);
103
104 free(i->final_path);
105 free(i->settings_path);
106 free(i->roothash_path);
107 free(i->roothash_signature_path);
108 free(i->verity_path);
109 free(i->image_root);
110 free(i->local);
111 free(i->checksum);
112
113 return mfree(i);
114 }
115
116 int raw_pull_new(
117 RawPull **ret,
118 sd_event *event,
119 const char *image_root,
120 RawPullFinished on_finished,
121 void *userdata) {
122
123 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
124 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
125 _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
126 _cleanup_free_ char *root = NULL;
127 int r;
128
129 assert(ret);
130
131 root = strdup(image_root ?: "/var/lib/machines");
132 if (!root)
133 return -ENOMEM;
134
135 if (event)
136 e = sd_event_ref(event);
137 else {
138 r = sd_event_default(&e);
139 if (r < 0)
140 return r;
141 }
142
143 r = curl_glue_new(&g, e);
144 if (r < 0)
145 return r;
146
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),
155 .event = TAKE_PTR(e),
156 .glue = TAKE_PTR(g),
157 .offset = UINT64_MAX,
158 };
159
160 i->glue->on_finished = pull_job_curl_on_finished;
161 i->glue->userdata = i;
162
163 *ret = TAKE_PTR(i);
164
165 return 0;
166 }
167
168 static void raw_pull_report_progress(RawPull *i, RawProgress p) {
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
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
190 if (i->settings_job) {
191 percent += i->settings_job->progress_percent * 5 / 100;
192 remain -= 5;
193 }
194
195 if (i->roothash_job) {
196 percent += i->roothash_job->progress_percent * 5 / 100;
197 remain -= 5;
198 }
199
200 if (i->roothash_signature_job) {
201 percent += i->roothash_signature_job->progress_percent * 5 / 100;
202 remain -= 5;
203 }
204
205 if (i->verity_job) {
206 percent += i->verity_job->progress_percent * 10 / 100;
207 remain -= 10;
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:
232 assert_not_reached();
233 }
234
235 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
236 log_debug("Combined progress %u%%", percent);
237 }
238
239 static int raw_pull_maybe_convert_qcow2(RawPull *i) {
240 _cleanup_(unlink_and_freep) char *t = NULL;
241 _cleanup_close_ int converted_fd = -EBADF;
242 _cleanup_free_ char *f = NULL;
243 int r;
244
245 assert(i);
246 assert(i->raw_job);
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);
254
255 r = qcow2_detect(i->raw_job->disk_fd);
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 */
262 r = tempfn_random(i->final_path, NULL, &f);
263 if (r < 0)
264 return log_oom();
265
266 converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
267 if (converted_fd < 0)
268 return log_error_errno(errno, "Failed to create %s: %m", f);
269
270 t = TAKE_PTR(f);
271
272 (void) import_set_nocow_and_log(converted_fd, t);
273
274 log_info("Unpacking QCOW2 file.");
275
276 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
277 if (r < 0)
278 return log_error_errno(r, "Failed to convert qcow2 image: %m");
279
280 unlink_and_free(i->temp_path);
281 i->temp_path = TAKE_PTR(t);
282 close_and_replace(i->raw_job->disk_fd, converted_fd);
283
284 return 1;
285 }
286
287 static int raw_pull_determine_path(
288 RawPull *i,
289 const char *suffix,
290 char **field /* input + output (!) */) {
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
308 static int raw_pull_copy_auxiliary_file(
309 RawPull *i,
310 const char *suffix,
311 char **path /* input + output (!) */) {
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
326 r = copy_file_atomic(
327 *path,
328 local,
329 0644,
330 COPY_REFLINK |
331 (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
332 (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 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 copy 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 *f = NULL;
348 _cleanup_close_ int dfd = -EBADF;
349 const char *p;
350 int r;
351
352 assert(i);
353 assert(i->raw_job);
354 assert(!FLAGS_SET(i->flags, PULL_DIRECT));
355
356 if (!i->local)
357 return 0;
358
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
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");
367 } else {
368 /* We freshly downloaded the image, use it */
369
370 assert(i->raw_job->disk_fd >= 0);
371 assert(i->offset == UINT64_MAX);
372
373 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
374 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
375 }
376
377 p = strjoina(i->image_root, "/", i->local, ".raw");
378
379 r = tempfn_random(p, NULL, &f);
380 if (r < 0)
381 return log_oom();
382
383 dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
384 if (dfd < 0)
385 return log_error_errno(errno, "Failed to create writable copy of image: %m");
386
387 tp = TAKE_PTR(f);
388
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);
392
393 r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
394 if (r < 0)
395 return log_error_errno(r, "Failed to make writable copy of image: %m");
396
397 (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
398 (void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
399
400 dfd = safe_close(dfd);
401
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);
411
412 log_info("Created new local image '%s'.", i->local);
413
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)) {
421 r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
422 if (r < 0)
423 return r;
424 }
425
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);
434 if (r < 0)
435 return r;
436 }
437
438 return 0;
439 }
440
441 static bool raw_pull_is_done(RawPull *i) {
442 assert(i);
443 assert(i->raw_job);
444
445 if (!PULL_JOB_IS_COMPLETE(i->raw_job))
446 return false;
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))
450 return false;
451 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
452 return false;
453 if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
454 return false;
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))
458 return false;
459
460 return true;
461 }
462
463 static 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);
472 assert(path);
473 assert(temp_path);
474 assert(*temp_path);
475 assert(suffix);
476
477 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
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
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));
489 if (r < 0)
490 return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
491
492 *temp_path = mfree(*temp_path);
493 return 1;
494 }
495
496 static void raw_pull_job_on_finished(PullJob *j) {
497 RawPull *i;
498 PullJob *jj;
499 int r;
500
501 assert(j);
502 assert(j->userdata);
503
504 i = j->userdata;
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)
524 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
525 else if (j == i->roothash_job)
526 log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
527 else if (j == i->roothash_signature_job)
528 log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
529 else if (j == i->verity_job)
530 log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
531 else
532 assert_not_reached();
533 }
534
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.
537 *
538 * We only do something when we got all files */
539
540 if (!raw_pull_is_done(i))
541 return;
542
543 if (i->signature_job && i->signature_job->error != 0) {
544 VerificationStyle style;
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);
554
555 r = verification_style_from_url(verify_job->url, &style);
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. */
565 r = log_error_errno(i->signature_job->error,
566 "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
567 goto finish;
568 }
569 }
570
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);
574
575 if (!i->raw_job->etag_exists) {
576 raw_pull_report_progress(i, RAW_VERIFYING);
577
578 r = pull_verify(i->verify,
579 i->checksum,
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);
587 if (r < 0)
588 goto finish;
589 }
590
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);
598
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);
611 if (r < 0)
612 goto finish;
613
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);
619
620 raw_pull_report_progress(i, RAW_UNPACKING);
621
622 r = raw_pull_maybe_convert_qcow2(i);
623 if (r < 0)
624 goto finish;
625
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));
632 if (r < 0) {
633 log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
634 goto finish;
635 }
636
637 i->temp_path = mfree(i->temp_path);
638
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 }
645
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 }
666 }
667
668 raw_pull_report_progress(i, RAW_COPYING);
669
670 r = raw_pull_make_local_copy(i);
671 if (r < 0)
672 goto finish;
673 }
674
675 r = 0;
676
677 finish:
678 if (i->on_finished)
679 i->on_finished(i, r, i->userdata);
680 else
681 sd_event_exit(i->event, r);
682 }
683
684 static int raw_pull_job_on_open_disk_generic(
685 RawPull *i,
686 PullJob *j,
687 const char *extra,
688 char **temp_path /* input + output */) {
689
690 int r;
691
692 assert(i);
693 assert(j);
694 assert(extra);
695 assert(temp_path);
696
697 assert(!FLAGS_SET(i->flags, PULL_DIRECT));
698
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
714 static int raw_pull_job_on_open_disk_raw(PullJob *j) {
715 RawPull *i;
716 int r;
717
718 assert(j);
719 assert(j->userdata);
720
721 i = j->userdata;
722 assert(i->raw_job == j);
723 assert(j->disk_fd < 0);
724
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 }
750
751 return 0;
752 }
753
754 static 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
766 static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
767 RawPull *i;
768
769 assert(j);
770 assert(j->userdata);
771
772 i = j->userdata;
773 assert(i->roothash_job == j);
774
775 return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
776 }
777
778 static int raw_pull_job_on_open_disk_roothash_signature(PullJob *j) {
779 RawPull *i;
780
781 assert(j);
782 assert(j->userdata);
783
784 i = j->userdata;
785 assert(i->roothash_signature_job == j);
786
787 return raw_pull_job_on_open_disk_generic(i, j, "roothash.p7s", &i->roothash_signature_temp_path);
788 }
789
790 static 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);
800 }
801
802 static void raw_pull_job_on_progress(PullJob *j) {
803 RawPull *i;
804
805 assert(j);
806 assert(j->userdata);
807
808 i = j->userdata;
809
810 raw_pull_report_progress(i, RAW_DOWNLOADING);
811 }
812
813 int raw_pull_start(
814 RawPull *i,
815 const char *url,
816 const char *local,
817 uint64_t offset,
818 uint64_t size_max,
819 PullFlags flags,
820 ImportVerify verify,
821 const char *checksum) {
822
823 PullJob *j;
824 int r;
825
826 assert(i);
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);
831 assert(!(flags & ~PULL_FLAGS_MASK_RAW));
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);
835
836 if (!http_url_is_valid(url) && !file_url_is_valid(url))
837 return -EINVAL;
838
839 if (local && !pull_validate_local(local, flags))
840 return -EINVAL;
841
842 if (i->raw_job)
843 return -EBUSY;
844
845 r = free_and_strdup(&i->local, local);
846 if (r < 0)
847 return r;
848
849 r = free_and_strdup(&i->checksum, checksum);
850 if (r < 0)
851 return r;
852
853 i->flags = flags;
854 i->verify = verify;
855
856 /* Queue job for the image itself */
857 r = pull_job_new(&i->raw_job, url, i->glue, i);
858 if (r < 0)
859 return r;
860
861 i->raw_job->on_finished = raw_pull_job_on_finished;
862 i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
863
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. */
869
870 r = pull_url_needs_checksum(url);
871 if (r < 0)
872 return r;
873
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 */
877 }
878
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;
883
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);
886 if (r < 0)
887 return r;
888 }
889
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);
899 if (r < 0)
900 return r;
901
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);
913 if (r < 0)
914 return r;
915 }
916
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);
928 if (r < 0)
929 return r;
930 }
931
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);
943 if (r < 0)
944 return r;
945 }
946
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);
958 if (r < 0)
959 return r;
960 }
961
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);
976
977 r = pull_job_begin(j);
978 if (r < 0)
979 return r;
980 }
981
982 return 0;
983 }