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