]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-raw.c
io.systemd.Unit.List fix context/runtime split (#38172)
[thirdparty/systemd.git] / src / import / pull-raw.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
90199220 2
7079cfef 3#include "sd-daemon.h"
9412e9e9 4#include "sd-event.h"
07630cea 5
b5efdb8a 6#include "alloc-util.h"
07630cea
LP
7#include "copy.h"
8#include "curl-util.h"
3ffd4af2 9#include "fd-util.h"
f4f15635 10#include "fs-util.h"
07630cea
LP
11#include "import-common.h"
12#include "import-util.h"
c40d82ab 13#include "install-file.h"
93a1f792 14#include "log.h"
35cd0ba5 15#include "mkdir-label.h"
dc2c282b 16#include "pull-common.h"
07630cea 17#include "pull-job.h"
3ffd4af2 18#include "pull-raw.h"
07630cea 19#include "qcow2-util.h"
07630cea 20#include "string-util.h"
e4de7287 21#include "tmpfile-util.h"
49cf4170 22#include "web-util.h"
90199220 23
7079cfef
LP
24typedef enum RawProgress {
25 RAW_DOWNLOADING,
26 RAW_VERIFYING,
27 RAW_UNPACKING,
28 RAW_FINALIZING,
29 RAW_COPYING,
30} RawProgress;
90199220 31
9412e9e9 32typedef struct RawPull {
90199220
LP
33 sd_event *event;
34 CurlGlue *glue;
35
83d74112 36 ImportFlags flags;
133b34f6 37 ImportVerify verify;
087682d1 38 char *image_root;
90199220 39
c40d82ab
LP
40 uint64_t offset;
41
dc2c282b
LP
42 PullJob *raw_job;
43 PullJob *checksum_job;
44 PullJob *signature_job;
133b34f6
LP
45 PullJob *settings_job;
46 PullJob *roothash_job;
47 PullJob *roothash_signature_job;
48 PullJob *verity_job;
90199220 49
dc2c282b 50 RawPullFinished on_finished;
0d6e763b 51 void *userdata;
90199220 52
c40d82ab 53 char *local; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
7af5785d 54 * image name of the final copy we make */
8620a9a3 55
0d6e763b 56 char *final_path;
9854730b
LP
57 char *temp_path;
58
59 char *settings_path;
60 char *settings_temp_path;
8f695058 61
91359193
LP
62 char *roothash_path;
63 char *roothash_temp_path;
64
133b34f6
LP
65 char *roothash_signature_path;
66 char *roothash_signature_temp_path;
67
68 char *verity_path;
69 char *verity_temp_path;
c40d82ab
LP
70
71 char *checksum;
9412e9e9 72} RawPull;
49bb233b 73
dc2c282b 74RawPull* raw_pull_unref(RawPull *i) {
0d6e763b 75 if (!i)
90199220
LP
76 return NULL;
77
dc2c282b
LP
78 pull_job_unref(i->raw_job);
79 pull_job_unref(i->checksum_job);
80 pull_job_unref(i->signature_job);
133b34f6
LP
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);
90199220 85
0d6e763b
LP
86 curl_glue_unref(i->glue);
87 sd_event_unref(i->event);
90199220 88
133b34f6
LP
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);
9854730b 94
0d6e763b 95 free(i->final_path);
9854730b 96 free(i->settings_path);
133b34f6
LP
97 free(i->roothash_path);
98 free(i->roothash_signature_path);
99 free(i->verity_path);
0d6e763b
LP
100 free(i->image_root);
101 free(i->local);
c40d82ab 102 free(i->checksum);
133b34f6 103
6b430fdb 104 return mfree(i);
90199220
LP
105}
106
dc2c282b
LP
107int raw_pull_new(
108 RawPull **ret,
8b71fce8
LP
109 sd_event *event,
110 const char *image_root,
dc2c282b 111 RawPullFinished on_finished,
8b71fce8
LP
112 void *userdata) {
113
0d94088e
YW
114 _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
115 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
dc2c282b 116 _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
0d94088e 117 _cleanup_free_ char *root = NULL;
0d6e763b 118 int r;
8620a9a3 119
0d6e763b 120 assert(ret);
7af5785d 121 assert(image_root);
8620a9a3 122
7af5785d 123 root = strdup(image_root);
0d94088e 124 if (!root)
8620a9a3
LP
125 return -ENOMEM;
126
0d6e763b 127 if (event)
0d94088e 128 e = sd_event_ref(event);
0d6e763b 129 else {
0d94088e 130 r = sd_event_default(&e);
dfd1520d 131 if (r < 0)
0d6e763b 132 return r;
2f64ba0e 133 }
8620a9a3 134
0d94088e 135 r = curl_glue_new(&g, e);
2f64ba0e 136 if (r < 0)
0d6e763b 137 return r;
8620a9a3 138
0d94088e
YW
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),
0d94088e
YW
147 .event = TAKE_PTR(e),
148 .glue = TAKE_PTR(g),
c40d82ab 149 .offset = UINT64_MAX,
0d94088e
YW
150 };
151
dc2c282b 152 i->glue->on_finished = pull_job_curl_on_finished;
0d6e763b 153 i->glue->userdata = i;
8620a9a3 154
1cc6c93a 155 *ret = TAKE_PTR(i);
8620a9a3 156
2f64ba0e
LP
157 return 0;
158}
159
dc2c282b 160static void raw_pull_report_progress(RawPull *i, RawProgress p) {
7079cfef
LP
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
133b34f6
LP
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
9854730b
LP
182 if (i->settings_job) {
183 percent += i->settings_job->progress_percent * 5 / 100;
184 remain -= 5;
185 }
186
91359193
LP
187 if (i->roothash_job) {
188 percent += i->roothash_job->progress_percent * 5 / 100;
189 remain -= 5;
190 }
191
133b34f6
LP
192 if (i->roothash_signature_job) {
193 percent += i->roothash_signature_job->progress_percent * 5 / 100;
7079cfef
LP
194 remain -= 5;
195 }
196
133b34f6
LP
197 if (i->verity_job) {
198 percent += i->verity_job->progress_percent * 10 / 100;
199 remain -= 10;
7079cfef
LP
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:
04499a70 224 assert_not_reached();
7079cfef
LP
225 }
226
a986de68 227 sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
7079cfef
LP
228 log_debug("Combined progress %u%%", percent);
229}
230
dc2c282b 231static int raw_pull_maybe_convert_qcow2(RawPull *i) {
c40d82ab 232 _cleanup_(unlink_and_freep) char *t = NULL;
254d1313 233 _cleanup_close_ int converted_fd = -EBADF;
c40d82ab 234 _cleanup_free_ char *f = NULL;
edce2aed
LP
235 int r;
236
0d6e763b
LP
237 assert(i);
238 assert(i->raw_job);
83d74112 239 assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
c40d82ab 240
83d74112 241 if (!FLAGS_SET(i->flags, IMPORT_CONVERT_QCOW2))
c40d82ab
LP
242 return 0;
243
244 assert(i->final_path);
245 assert(i->raw_job->close_disk_fd);
edce2aed 246
0d6e763b 247 r = qcow2_detect(i->raw_job->disk_fd);
edce2aed
LP
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 */
c40d82ab 254 r = tempfn_random(i->final_path, NULL, &f);
edce2aed
LP
255 if (r < 0)
256 return log_oom();
257
c40d82ab 258 converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
edce2aed 259 if (converted_fd < 0)
c40d82ab
LP
260 return log_error_errno(errno, "Failed to create %s: %m", f);
261
262 t = TAKE_PTR(f);
edce2aed 263
137c6c6b 264 (void) import_set_nocow_and_log(converted_fd, t);
0d6e763b 265
ec5cb56e
LP
266 log_info("Unpacking QCOW2 file.");
267
0d6e763b 268 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
c40d82ab 269 if (r < 0)
edce2aed 270 return log_error_errno(r, "Failed to convert qcow2 image: %m");
edce2aed 271
c40d82ab
LP
272 unlink_and_free(i->temp_path);
273 i->temp_path = TAKE_PTR(t);
ee3455cf 274 close_and_replace(i->raw_job->disk_fd, converted_fd);
edce2aed
LP
275
276 return 1;
277}
278
c40d82ab
LP
279static int raw_pull_determine_path(
280 RawPull *i,
281 const char *suffix,
282 char **field /* input + output (!) */) {
91359193
LP
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
300static int raw_pull_copy_auxiliary_file(
301 RawPull *i,
302 const char *suffix,
c40d82ab 303 char **path /* input + output (!) */) {
91359193 304
b146afc4 305 _cleanup_free_ char *local = NULL;
91359193
LP
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
b146afc4
LP
316 local = strjoin(i->image_root, "/", i->local, suffix);
317 if (!local)
318 return log_oom();
91359193 319
b146afc4
LP
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));
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)
b146afc4 338 log_warning_errno(r, "Failed to install file %s, ignoring: %m", local);
91359193
LP
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 346 _cleanup_(unlink_and_freep) char *tp = NULL;
b146afc4 347 _cleanup_free_ char *p = NULL;
90199220
LP
348 int r;
349
0d6e763b
LP
350 assert(i);
351 assert(i->raw_job);
83d74112 352 assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
90199220 353
0d6e763b 354 if (!i->local)
90199220
LP
355 return 0;
356
85dbc41d
LP
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
0d6e763b
LP
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");
85dbc41d
LP
365 } else {
366 /* We freshly downloaded the image, use it */
367
368 assert(i->raw_job->disk_fd >= 0);
c40d82ab 369 assert(i->offset == UINT64_MAX);
85dbc41d 370
86cbbc6d 371 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) < 0)
85dbc41d 372 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
90199220
LP
373 }
374
b146afc4
LP
375 p = strjoin(i->image_root, "/", i->local, ".raw");
376 if (!p)
0d6e763b 377 return log_oom();
49bb233b 378
b146afc4
LP
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;
49bb233b 383
b146afc4
LP
384 r = tempfn_random(p, NULL, &f);
385 if (r < 0)
386 return log_oom();
c40d82ab 387
b146afc4
LP
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");
90199220 391
b146afc4
LP
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);
90199220 404
b146afc4 405 dfd = safe_close(dfd);
1e20b411 406
b146afc4
LP
407 source = tp;
408 } else
409 source = i->final_path;
1e20b411 410
b146afc4 411 r = install_file(AT_FDCWD, source,
c40d82ab 412 AT_FDCWD, p,
83d74112
LP
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));
c40d82ab 416 if (r < 0)
b146afc4 417 return log_error_errno(r, "Failed to move local image into place '%s': %m", p);
c40d82ab
LP
418
419 tp = mfree(tp);
1e20b411 420
0d6e763b 421 log_info("Created new local image '%s'.", i->local);
9854730b 422
83d74112 423 if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) {
133b34f6
LP
424 r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
425 if (r < 0)
426 return r;
427 }
428
83d74112 429 if (FLAGS_SET(i->flags, IMPORT_PULL_ROOTHASH)) {
91359193
LP
430 r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
431 if (r < 0)
432 return r;
433 }
9854730b 434
83d74112 435 if (FLAGS_SET(i->flags, IMPORT_PULL_ROOTHASH_SIGNATURE)) {
133b34f6
LP
436 r = raw_pull_copy_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_path);
437 if (r < 0)
438 return r;
439 }
440
83d74112 441 if (FLAGS_SET(i->flags, IMPORT_PULL_VERITY)) {
133b34f6 442 r = raw_pull_copy_auxiliary_file(i, ".verity", &i->verity_path);
91359193
LP
443 if (r < 0)
444 return r;
9854730b
LP
445 }
446
1e20b411
LP
447 return 0;
448}
449
dc2c282b 450static bool raw_pull_is_done(RawPull *i) {
8b71fce8
LP
451 assert(i);
452 assert(i->raw_job);
453
9854730b 454 if (!PULL_JOB_IS_COMPLETE(i->raw_job))
8b71fce8 455 return false;
133b34f6
LP
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))
91359193 459 return false;
9854730b 460 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
8b71fce8 461 return false;
133b34f6 462 if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
9854730b 463 return false;
133b34f6
LP
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))
8b71fce8
LP
467 return false;
468
469 return true;
470}
471
91359193
LP
472static 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);
c40d82ab 481 assert(path);
91359193 482 assert(temp_path);
c40d82ab 483 assert(*temp_path);
91359193 484 assert(suffix);
91359193 485
e0061812 486 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
91359193
LP
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
c40d82ab
LP
493 r = install_file(
494 AT_FDCWD, *temp_path,
495 AT_FDCWD, *path,
496 INSTALL_READ_ONLY|
83d74112 497 (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
91359193 498 if (r < 0)
c40d82ab 499 return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
91359193
LP
500
501 *temp_path = mfree(*temp_path);
91359193
LP
502 return 1;
503}
504
dc2c282b
LP
505static void raw_pull_job_on_finished(PullJob *j) {
506 RawPull *i;
8620a9a3
LP
507 int r;
508
0d6e763b
LP
509 assert(j);
510 assert(j->userdata);
8620a9a3 511
0d6e763b 512 i = j->userdata;
c40d82ab
LP
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)
133b34f6 532 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
c40d82ab 533 else if (j == i->roothash_job)
91359193 534 log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
c40d82ab 535 else if (j == i->roothash_signature_job)
133b34f6 536 log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
c40d82ab
LP
537 else if (j == i->verity_job)
538 log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
3576d631 539 else
c40d82ab 540 assert_not_reached();
8620a9a3
LP
541 }
542
133b34f6
LP
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.
3576d631 545 *
c40d82ab 546 * We only do something when we got all files */
85dbc41d 547
dc2c282b 548 if (!raw_pull_is_done(i))
3576d631 549 return;
8620a9a3 550
f14717a7
LP
551 if (i->signature_job && i->signature_job->error != 0) {
552 VerificationStyle style;
c40d82ab
LP
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);
697be0be 562
c40d82ab 563 r = verification_style_from_url(verify_job->url, &style);
f14717a7
LP
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. */
c40d82ab
LP
573 r = log_error_errno(i->signature_job->error,
574 "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
f14717a7
LP
575 goto finish;
576 }
697be0be
TB
577 }
578
2d708781 579 PullJob *jj;
c40d82ab 580 /* Let's close these auxiliary files now, we don't need access to them anymore. */
2d708781 581 FOREACH_ARGUMENT(jj, i->settings_job, i->roothash_job, i->roothash_signature_job, i->verity_job)
c40d82ab 582 pull_job_close_disk_fd(jj);
91359193 583
3576d631 584 if (!i->raw_job->etag_exists) {
dc2c282b 585 raw_pull_report_progress(i, RAW_VERIFYING);
7079cfef 586
ff2f7797 587 r = pull_verify(i->verify,
c40d82ab 588 i->checksum,
ff2f7797
LP
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);
0d6e763b
LP
596 if (r < 0)
597 goto finish;
c40d82ab 598 }
8620a9a3 599
83d74112 600 if (i->flags & IMPORT_DIRECT) {
c40d82ab
LP
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);
7079cfef 607
c40d82ab
LP
608 if (i->local) {
609 r = install_file(AT_FDCWD, i->local,
610 AT_FDCWD, NULL,
83d74112
LP
611 ((i->flags & IMPORT_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
612 (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
c40d82ab
LP
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);
0d6e763b
LP
620 if (r < 0)
621 goto finish;
85dbc41d 622
c40d82ab
LP
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);
7079cfef 628
c40d82ab
LP
629 raw_pull_report_progress(i, RAW_UNPACKING);
630
631 r = raw_pull_maybe_convert_qcow2(i);
50dfca2e
KK
632 if (r < 0)
633 goto finish;
634
c40d82ab
LP
635 raw_pull_report_progress(i, RAW_FINALIZING);
636
637 r = install_file(AT_FDCWD, i->temp_path,
638 AT_FDCWD, i->final_path,
b146afc4 639 (i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
83d74112 640 (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
50dfca2e 641 if (r < 0) {
c40d82ab 642 log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
50dfca2e
KK
643 goto finish;
644 }
8f695058 645
c40d82ab 646 i->temp_path = mfree(i->temp_path);
9854730b 647
c40d82ab
LP
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 }
9854730b 654
c40d82ab
LP
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 }
9854730b 675 }
8620a9a3 676
c40d82ab 677 raw_pull_report_progress(i, RAW_COPYING);
7079cfef 678
c40d82ab
LP
679 r = raw_pull_make_local_copy(i);
680 if (r < 0)
681 goto finish;
682 }
90199220 683
0d6e763b 684 r = 0;
3576d631 685
0d6e763b 686finish:
3576d631
LP
687 if (i->on_finished)
688 i->on_finished(i, r, i->userdata);
689 else
690 sd_event_exit(i->event, r);
0d6e763b 691}
90199220 692
91359193
LP
693static int raw_pull_job_on_open_disk_generic(
694 RawPull *i,
695 PullJob *j,
696 const char *extra,
c40d82ab 697 char **temp_path /* input + output */) {
91359193 698
91359193
LP
699 int r;
700
701 assert(i);
702 assert(j);
703 assert(extra);
704 assert(temp_path);
705
83d74112 706 assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
c40d82ab 707
91359193
LP
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
9854730b 723static int raw_pull_job_on_open_disk_raw(PullJob *j) {
dc2c282b 724 RawPull *i;
0d6e763b 725 int r;
90199220 726
0d6e763b
LP
727 assert(j);
728 assert(j->userdata);
90199220 729
0d6e763b 730 i = j->userdata;
8b71fce8 731 assert(i->raw_job == j);
c40d82ab 732 assert(j->disk_fd < 0);
90199220 733
83d74112 734 if (i->flags & IMPORT_DIRECT) {
c40d82ab
LP
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 }
1e20b411 759
90199220
LP
760 return 0;
761}
762
133b34f6
LP
763static 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
91359193 775static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
9854730b 776 RawPull *i;
9854730b
LP
777
778 assert(j);
779 assert(j->userdata);
780
781 i = j->userdata;
91359193 782 assert(i->roothash_job == j);
9854730b 783
91359193
LP
784 return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
785}
9854730b 786
133b34f6 787static int raw_pull_job_on_open_disk_roothash_signature(PullJob *j) {
91359193 788 RawPull *i;
9854730b 789
91359193
LP
790 assert(j);
791 assert(j->userdata);
9854730b 792
91359193 793 i = j->userdata;
133b34f6 794 assert(i->roothash_signature_job == j);
9854730b 795
133b34f6
LP
796 return raw_pull_job_on_open_disk_generic(i, j, "roothash.p7s", &i->roothash_signature_temp_path);
797}
798
799static 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);
9854730b
LP
809}
810
dc2c282b
LP
811static void raw_pull_job_on_progress(PullJob *j) {
812 RawPull *i;
7079cfef
LP
813
814 assert(j);
815 assert(j->userdata);
816
817 i = j->userdata;
818
dc2c282b 819 raw_pull_report_progress(i, RAW_DOWNLOADING);
7079cfef
LP
820}
821
9854730b
LP
822int raw_pull_start(
823 RawPull *i,
824 const char *url,
825 const char *local,
c40d82ab
LP
826 uint64_t offset,
827 uint64_t size_max,
83d74112 828 ImportFlags flags,
c40d82ab
LP
829 ImportVerify verify,
830 const char *checksum) {
9854730b 831
90199220
LP
832 int r;
833
0d6e763b 834 assert(i);
c40d82ab
LP
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);
83d74112
LP
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);
90199220 843
c456862f 844 if (!http_url_is_valid(url) && !file_url_is_valid(url))
0d6e763b 845 return -EINVAL;
90199220 846
c40d82ab 847 if (local && !pull_validate_local(local, flags))
0d6e763b 848 return -EINVAL;
087682d1 849
8b71fce8
LP
850 if (i->raw_job)
851 return -EBUSY;
852
0d6e763b 853 r = free_and_strdup(&i->local, local);
90199220
LP
854 if (r < 0)
855 return r;
9854730b 856
c40d82ab
LP
857 r = free_and_strdup(&i->checksum, checksum);
858 if (r < 0)
859 return r;
860
133b34f6 861 i->flags = flags;
8f695058 862 i->verify = verify;
90199220 863
85dbc41d 864 /* Queue job for the image itself */
dc2c282b 865 r = pull_job_new(&i->raw_job, url, i->glue, i);
90199220
LP
866 if (r < 0)
867 return r;
868
dc2c282b 869 i->raw_job->on_finished = raw_pull_job_on_finished;
9854730b 870 i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
133b34f6 871
c40d82ab
LP
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. */
133b34f6 877
c40d82ab 878 r = pull_url_needs_checksum(url);
91359193
LP
879 if (r < 0)
880 return r;
881
c40d82ab
LP
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 */
91359193
LP
885 }
886
c40d82ab
LP
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;
9854730b 891
83d74112 892 if (!FLAGS_SET(flags, IMPORT_DIRECT)) {
c40d82ab 893 r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
133b34f6
LP
894 if (r < 0)
895 return r;
133b34f6 896 }
85dbc41d 897
c40d82ab
LP
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);
85dbc41d
LP
907 if (r < 0)
908 return r;
909
83d74112 910 if (FLAGS_SET(flags, IMPORT_PULL_SETTINGS)) {
c40d82ab
LP
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);
133b34f6
LP
921 if (r < 0)
922 return r;
923 }
924
83d74112 925 if (FLAGS_SET(flags, IMPORT_PULL_ROOTHASH)) {
c40d82ab
LP
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);
91359193
LP
936 if (r < 0)
937 return r;
938 }
939
83d74112 940 if (FLAGS_SET(flags, IMPORT_PULL_ROOTHASH_SIGNATURE)) {
c40d82ab
LP
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);
9854730b
LP
951 if (r < 0)
952 return r;
953 }
954
83d74112 955 if (FLAGS_SET(flags, IMPORT_PULL_VERITY)) {
c40d82ab
LP
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);
3576d631
LP
966 if (r < 0)
967 return r;
968 }
969
2d708781
MY
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) {
c40d82ab
LP
979
980 if (!j)
981 continue;
982
983 j->on_progress = raw_pull_job_on_progress;
83d74112 984 j->sync = FLAGS_SET(flags, IMPORT_SYNC);
7079cfef 985
c40d82ab 986 r = pull_job_begin(j);
3576d631
LP
987 if (r < 0)
988 return r;
989 }
990
85dbc41d 991 return 0;
90199220 992}