]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-raw.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / import / pull-raw.c
CommitLineData
90199220
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
90199220 22#include <curl/curl.h>
07630cea
LP
23#include <linux/fs.h>
24#include <sys/xattr.h>
90199220 25
7079cfef 26#include "sd-daemon.h"
07630cea 27
b5efdb8a 28#include "alloc-util.h"
0d6e763b 29#include "btrfs-util.h"
c8b3094d 30#include "chattr-util.h"
07630cea
LP
31#include "copy.h"
32#include "curl-util.h"
3ffd4af2 33#include "fd-util.h"
0d39fa9c 34#include "fileio.h"
f4f15635 35#include "fs-util.h"
07630cea
LP
36#include "hostname-util.h"
37#include "import-common.h"
38#include "import-util.h"
0d6e763b
LP
39#include "macro.h"
40#include "mkdir.h"
26166c88 41#include "path-util.h"
dc2c282b 42#include "pull-common.h"
07630cea 43#include "pull-job.h"
3ffd4af2 44#include "pull-raw.h"
07630cea
LP
45#include "qcow2-util.h"
46#include "rm-rf.h"
47#include "string-util.h"
48#include "strv.h"
49#include "utf8.h"
50#include "util.h"
49cf4170 51#include "web-util.h"
90199220 52
7079cfef
LP
53typedef enum RawProgress {
54 RAW_DOWNLOADING,
55 RAW_VERIFYING,
56 RAW_UNPACKING,
57 RAW_FINALIZING,
58 RAW_COPYING,
59} RawProgress;
90199220 60
dc2c282b 61struct RawPull {
90199220
LP
62 sd_event *event;
63 CurlGlue *glue;
64
087682d1 65 char *image_root;
90199220 66
dc2c282b 67 PullJob *raw_job;
9854730b 68 PullJob *settings_job;
dc2c282b
LP
69 PullJob *checksum_job;
70 PullJob *signature_job;
90199220 71
dc2c282b 72 RawPullFinished on_finished;
0d6e763b 73 void *userdata;
90199220 74
0d6e763b
LP
75 char *local;
76 bool force_local;
26166c88 77 bool grow_machine_directory;
9854730b 78 bool settings;
8620a9a3 79
0d6e763b 80 char *final_path;
9854730b
LP
81 char *temp_path;
82
83 char *settings_path;
84 char *settings_temp_path;
8f695058
LP
85
86 ImportVerify verify;
0d6e763b 87};
49bb233b 88
dc2c282b 89RawPull* raw_pull_unref(RawPull *i) {
0d6e763b 90 if (!i)
90199220
LP
91 return NULL;
92
dc2c282b 93 pull_job_unref(i->raw_job);
9854730b 94 pull_job_unref(i->settings_job);
dc2c282b
LP
95 pull_job_unref(i->checksum_job);
96 pull_job_unref(i->signature_job);
90199220 97
0d6e763b
LP
98 curl_glue_unref(i->glue);
99 sd_event_unref(i->event);
90199220 100
0d6e763b
LP
101 if (i->temp_path) {
102 (void) unlink(i->temp_path);
103 free(i->temp_path);
90199220
LP
104 }
105
9854730b
LP
106 if (i->settings_temp_path) {
107 (void) unlink(i->settings_temp_path);
108 free(i->settings_temp_path);
109 }
110
0d6e763b 111 free(i->final_path);
9854730b 112 free(i->settings_path);
0d6e763b
LP
113 free(i->image_root);
114 free(i->local);
115 free(i);
90199220
LP
116
117 return NULL;
118}
119
dc2c282b
LP
120int raw_pull_new(
121 RawPull **ret,
8b71fce8
LP
122 sd_event *event,
123 const char *image_root,
dc2c282b 124 RawPullFinished on_finished,
8b71fce8
LP
125 void *userdata) {
126
dc2c282b 127 _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
0d6e763b 128 int r;
8620a9a3 129
0d6e763b 130 assert(ret);
8620a9a3 131
dc2c282b 132 i = new0(RawPull, 1);
0d6e763b 133 if (!i)
8620a9a3
LP
134 return -ENOMEM;
135
0d6e763b
LP
136 i->on_finished = on_finished;
137 i->userdata = userdata;
8620a9a3 138
0d6e763b
LP
139 i->image_root = strdup(image_root ?: "/var/lib/machines");
140 if (!i->image_root)
8620a9a3
LP
141 return -ENOMEM;
142
26166c88
LP
143 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
144
0d6e763b
LP
145 if (event)
146 i->event = sd_event_ref(event);
147 else {
148 r = sd_event_default(&i->event);
dfd1520d 149 if (r < 0)
0d6e763b 150 return r;
2f64ba0e 151 }
8620a9a3 152
0d6e763b 153 r = curl_glue_new(&i->glue, i->event);
2f64ba0e 154 if (r < 0)
0d6e763b 155 return r;
8620a9a3 156
dc2c282b 157 i->glue->on_finished = pull_job_curl_on_finished;
0d6e763b 158 i->glue->userdata = i;
8620a9a3 159
0d6e763b
LP
160 *ret = i;
161 i = NULL;
8620a9a3 162
2f64ba0e
LP
163 return 0;
164}
165
dc2c282b 166static void raw_pull_report_progress(RawPull *i, RawProgress p) {
7079cfef
LP
167 unsigned percent;
168
169 assert(i);
170
171 switch (p) {
172
173 case RAW_DOWNLOADING: {
174 unsigned remain = 80;
175
176 percent = 0;
177
9854730b
LP
178 if (i->settings_job) {
179 percent += i->settings_job->progress_percent * 5 / 100;
180 remain -= 5;
181 }
182
7079cfef
LP
183 if (i->checksum_job) {
184 percent += i->checksum_job->progress_percent * 5 / 100;
185 remain -= 5;
186 }
187
188 if (i->signature_job) {
189 percent += i->signature_job->progress_percent * 5 / 100;
190 remain -= 5;
191 }
192
193 if (i->raw_job)
194 percent += i->raw_job->progress_percent * remain / 100;
195 break;
196 }
197
198 case RAW_VERIFYING:
199 percent = 80;
200 break;
201
202 case RAW_UNPACKING:
203 percent = 85;
204 break;
205
206 case RAW_FINALIZING:
207 percent = 90;
208 break;
209
210 case RAW_COPYING:
211 percent = 95;
212 break;
213
214 default:
215 assert_not_reached("Unknown progress state");
216 }
217
218 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
219 log_debug("Combined progress %u%%", percent);
220}
221
dc2c282b 222static int raw_pull_maybe_convert_qcow2(RawPull *i) {
edce2aed
LP
223 _cleanup_close_ int converted_fd = -1;
224 _cleanup_free_ char *t = NULL;
225 int r;
226
0d6e763b
LP
227 assert(i);
228 assert(i->raw_job);
edce2aed 229
0d6e763b 230 r = qcow2_detect(i->raw_job->disk_fd);
edce2aed
LP
231 if (r < 0)
232 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
233 if (r == 0)
234 return 0;
235
236 /* This is a QCOW2 image, let's convert it */
14bcf25c 237 r = tempfn_random(i->final_path, NULL, &t);
edce2aed
LP
238 if (r < 0)
239 return log_oom();
240
b6e676ce 241 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
edce2aed
LP
242 if (converted_fd < 0)
243 return log_error_errno(errno, "Failed to create %s: %m", t);
244
1ed8f8c1 245 r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
0d6e763b
LP
246 if (r < 0)
247 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
248
ec5cb56e
LP
249 log_info("Unpacking QCOW2 file.");
250
0d6e763b 251 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
edce2aed
LP
252 if (r < 0) {
253 unlink(t);
254 return log_error_errno(r, "Failed to convert qcow2 image: %m");
255 }
256
b6e676ce 257 (void) unlink(i->temp_path);
0d6e763b 258 free(i->temp_path);
0d6e763b 259 i->temp_path = t;
edce2aed
LP
260 t = NULL;
261
0d6e763b
LP
262 safe_close(i->raw_job->disk_fd);
263 i->raw_job->disk_fd = converted_fd;
edce2aed
LP
264 converted_fd = -1;
265
266 return 1;
267}
268
dc2c282b 269static int raw_pull_make_local_copy(RawPull *i) {
0d6e763b
LP
270 _cleanup_free_ char *tp = NULL;
271 _cleanup_close_ int dfd = -1;
272 const char *p;
90199220
LP
273 int r;
274
0d6e763b
LP
275 assert(i);
276 assert(i->raw_job);
90199220 277
0d6e763b 278 if (!i->local)
90199220
LP
279 return 0;
280
9854730b
LP
281 if (!i->final_path) {
282 r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
283 if (r < 0)
284 return log_oom();
285 }
286
85dbc41d
LP
287 if (i->raw_job->etag_exists) {
288 /* We have downloaded this one previously, reopen it */
289
290 assert(i->raw_job->disk_fd < 0);
291
0d6e763b
LP
292 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
293 if (i->raw_job->disk_fd < 0)
294 return log_error_errno(errno, "Failed to open vendor image: %m");
85dbc41d
LP
295 } else {
296 /* We freshly downloaded the image, use it */
297
298 assert(i->raw_job->disk_fd >= 0);
299
300 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
301 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
90199220
LP
302 }
303
63c372cb 304 p = strjoina(i->image_root, "/", i->local, ".raw");
49bb233b 305
d9e2daaf
LP
306 if (i->force_local)
307 (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
49bb233b 308
14bcf25c 309 r = tempfn_random(p, NULL, &tp);
49bb233b 310 if (r < 0)
0d6e763b 311 return log_oom();
49bb233b 312
0d6e763b
LP
313 dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
314 if (dfd < 0)
315 return log_error_errno(errno, "Failed to create writable copy of image: %m");
49bb233b 316
0d6e763b
LP
317 /* Turn off COW writing. This should greatly improve
318 * performance on COW file systems like btrfs, since it
319 * reduces fragmentation caused by not allowing in-place
320 * writes. */
1ed8f8c1 321 r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL);
0d6e763b
LP
322 if (r < 0)
323 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
90199220 324
59f448cf 325 r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true);
90199220 326 if (r < 0) {
0d6e763b
LP
327 unlink(tp);
328 return log_error_errno(r, "Failed to make writable copy of image: %m");
90199220
LP
329 }
330
0d6e763b
LP
331 (void) copy_times(i->raw_job->disk_fd, dfd);
332 (void) copy_xattr(i->raw_job->disk_fd, dfd);
1e20b411 333
0d6e763b 334 dfd = safe_close(dfd);
1e20b411 335
0d6e763b
LP
336 r = rename(tp, p);
337 if (r < 0) {
338 unlink(tp);
339 return log_error_errno(errno, "Failed to move writable image into place: %m");
1e20b411
LP
340 }
341
0d6e763b 342 log_info("Created new local image '%s'.", i->local);
9854730b
LP
343
344 if (i->settings) {
345 const char *local_settings;
346 assert(i->settings_job);
347
348 if (!i->settings_path) {
349 r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
350 if (r < 0)
351 return log_oom();
352 }
353
354 local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
355
356 r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0);
357 if (r == -EEXIST)
358 log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
359 else if (r < 0 && r != -ENOENT)
79b6198b
LP
360 log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
361 else
362 log_info("Created new settings file '%s.nspawn'", i->local);
9854730b
LP
363 }
364
1e20b411
LP
365 return 0;
366}
367
dc2c282b 368static bool raw_pull_is_done(RawPull *i) {
8b71fce8
LP
369 assert(i);
370 assert(i->raw_job);
371
9854730b 372 if (!PULL_JOB_IS_COMPLETE(i->raw_job))
8b71fce8 373 return false;
9854730b 374 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
8b71fce8 375 return false;
9854730b
LP
376 if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
377 return false;
378 if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
8b71fce8
LP
379 return false;
380
381 return true;
382}
383
dc2c282b
LP
384static void raw_pull_job_on_finished(PullJob *j) {
385 RawPull *i;
8620a9a3
LP
386 int r;
387
0d6e763b
LP
388 assert(j);
389 assert(j->userdata);
8620a9a3 390
0d6e763b 391 i = j->userdata;
9854730b
LP
392 if (j == i->settings_job) {
393 if (j->error != 0)
394 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
395 } else if (j->error != 0) {
98c38001 396 if (j == i->checksum_job)
3576d631
LP
397 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
398 else if (j == i->signature_job)
399 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
400 else
401 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
402
0d6e763b
LP
403 r = j->error;
404 goto finish;
8620a9a3
LP
405 }
406
0d6e763b
LP
407 /* This is invoked if either the download completed
408 * successfully, or the download was skipped because we
85dbc41d 409 * already have the etag. In this case ->etag_exists is
3576d631
LP
410 * true.
411 *
412 * We only do something when we got all three files */
85dbc41d 413
dc2c282b 414 if (!raw_pull_is_done(i))
3576d631 415 return;
8620a9a3 416
9854730b
LP
417 if (i->settings_job)
418 i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
419
3576d631 420 if (!i->raw_job->etag_exists) {
98c38001 421 /* This is a new download, verify it, and move it into place */
3576d631
LP
422 assert(i->raw_job->disk_fd >= 0);
423
dc2c282b 424 raw_pull_report_progress(i, RAW_VERIFYING);
7079cfef 425
9854730b 426 r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job);
0d6e763b
LP
427 if (r < 0)
428 goto finish;
8620a9a3 429
dc2c282b 430 raw_pull_report_progress(i, RAW_UNPACKING);
7079cfef 431
dc2c282b 432 r = raw_pull_maybe_convert_qcow2(i);
0d6e763b
LP
433 if (r < 0)
434 goto finish;
85dbc41d 435
dc2c282b 436 raw_pull_report_progress(i, RAW_FINALIZING);
7079cfef 437
b6e676ce 438 r = import_make_read_only_fd(i->raw_job->disk_fd);
3576d631
LP
439 if (r < 0)
440 goto finish;
85dbc41d 441
f85ef957 442 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
3576d631 443 if (r < 0) {
f85ef957 444 log_error_errno(r, "Failed to move RAW file into place: %m");
3576d631
LP
445 goto finish;
446 }
8f695058 447
9854730b
LP
448 i->temp_path = mfree(i->temp_path);
449
450 if (i->settings_job &&
451 i->settings_job->error == 0 &&
452 !i->settings_job->etag_exists) {
453
454 assert(i->settings_temp_path);
455 assert(i->settings_path);
456
457 r = import_make_read_only(i->settings_temp_path);
458 if (r < 0)
459 goto finish;
460
461 r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
462 if (r < 0) {
463 log_error_errno(r, "Failed to rename settings file: %m");
464 goto finish;
465 }
466
467 i->settings_temp_path = mfree(i->settings_temp_path);
468 }
8620a9a3
LP
469 }
470
dc2c282b 471 raw_pull_report_progress(i, RAW_COPYING);
7079cfef 472
dc2c282b 473 r = raw_pull_make_local_copy(i);
8620a9a3 474 if (r < 0)
0d6e763b 475 goto finish;
90199220 476
0d6e763b 477 r = 0;
3576d631 478
0d6e763b 479finish:
3576d631
LP
480 if (i->on_finished)
481 i->on_finished(i, r, i->userdata);
482 else
483 sd_event_exit(i->event, r);
0d6e763b 484}
90199220 485
9854730b 486static int raw_pull_job_on_open_disk_raw(PullJob *j) {
dc2c282b 487 RawPull *i;
0d6e763b 488 int r;
90199220 489
0d6e763b
LP
490 assert(j);
491 assert(j->userdata);
90199220 492
0d6e763b 493 i = j->userdata;
8b71fce8
LP
494 assert(i->raw_job == j);
495 assert(!i->final_path);
496 assert(!i->temp_path);
90199220 497
dc2c282b 498 r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
0d6e763b
LP
499 if (r < 0)
500 return log_oom();
90199220 501
14bcf25c 502 r = tempfn_random(i->final_path, NULL, &i->temp_path);
b6e676ce 503 if (r < 0)
0d6e763b 504 return log_oom();
1e20b411 505
b6e676ce 506 (void) mkdir_parents_label(i->temp_path, 0700);
1e20b411 507
b6e676ce 508 j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
0d6e763b
LP
509 if (j->disk_fd < 0)
510 return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
1e20b411 511
1ed8f8c1 512 r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
90199220 513 if (r < 0)
0d6e763b 514 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
90199220
LP
515
516 return 0;
517}
518
9854730b
LP
519static int raw_pull_job_on_open_disk_settings(PullJob *j) {
520 RawPull *i;
521 int r;
522
523 assert(j);
524 assert(j->userdata);
525
526 i = j->userdata;
527 assert(i->settings_job == j);
528 assert(!i->settings_path);
529 assert(!i->settings_temp_path);
530
531 r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
532 if (r < 0)
533 return log_oom();
534
535 r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
536 if (r < 0)
537 return log_oom();
538
539 mkdir_parents_label(i->settings_temp_path, 0700);
540
541 j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
542 if (j->disk_fd < 0)
543 return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path);
544
545 return 0;
546}
547
dc2c282b
LP
548static void raw_pull_job_on_progress(PullJob *j) {
549 RawPull *i;
7079cfef
LP
550
551 assert(j);
552 assert(j->userdata);
553
554 i = j->userdata;
555
dc2c282b 556 raw_pull_report_progress(i, RAW_DOWNLOADING);
7079cfef
LP
557}
558
9854730b
LP
559int raw_pull_start(
560 RawPull *i,
561 const char *url,
562 const char *local,
563 bool force_local,
564 ImportVerify verify,
565 bool settings) {
566
90199220
LP
567 int r;
568
0d6e763b 569 assert(i);
8f695058
LP
570 assert(verify < _IMPORT_VERIFY_MAX);
571 assert(verify >= 0);
90199220 572
0d6e763b
LP
573 if (!http_url_is_valid(url))
574 return -EINVAL;
90199220 575
0d6e763b
LP
576 if (local && !machine_name_is_valid(local))
577 return -EINVAL;
087682d1 578
8b71fce8
LP
579 if (i->raw_job)
580 return -EBUSY;
581
0d6e763b 582 r = free_and_strdup(&i->local, local);
90199220
LP
583 if (r < 0)
584 return r;
9854730b 585
0d6e763b 586 i->force_local = force_local;
8f695058 587 i->verify = verify;
9854730b 588 i->settings = settings;
90199220 589
85dbc41d 590 /* Queue job for the image itself */
dc2c282b 591 r = pull_job_new(&i->raw_job, url, i->glue, i);
90199220
LP
592 if (r < 0)
593 return r;
594
dc2c282b 595 i->raw_job->on_finished = raw_pull_job_on_finished;
9854730b 596 i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
dc2c282b 597 i->raw_job->on_progress = raw_pull_job_on_progress;
98c38001 598 i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 599 i->raw_job->grow_machine_directory = i->grow_machine_directory;
90199220 600
dc2c282b 601 r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
90199220
LP
602 if (r < 0)
603 return r;
604
9854730b
LP
605 if (settings) {
606 r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i);
607 if (r < 0)
608 return r;
609
610 i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings;
611 i->settings_job->on_progress = raw_pull_job_on_progress;
612 i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
613
614 r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
615 if (r < 0)
616 return r;
617 }
618
dc2c282b 619 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
98c38001
LP
620 if (r < 0)
621 return r;
85dbc41d 622
dc2c282b 623 r = pull_job_begin(i->raw_job);
85dbc41d
LP
624 if (r < 0)
625 return r;
626
9854730b
LP
627 if (i->settings_job) {
628 r = pull_job_begin(i->settings_job);
629 if (r < 0)
630 return r;
631 }
632
98c38001 633 if (i->checksum_job) {
dc2c282b 634 i->checksum_job->on_progress = raw_pull_job_on_progress;
7079cfef 635
dc2c282b 636 r = pull_job_begin(i->checksum_job);
3576d631
LP
637 if (r < 0)
638 return r;
639 }
640
641 if (i->signature_job) {
dc2c282b 642 i->signature_job->on_progress = raw_pull_job_on_progress;
7079cfef 643
dc2c282b 644 r = pull_job_begin(i->signature_job);
3576d631
LP
645 if (r < 0)
646 return r;
647 }
648
85dbc41d 649 return 0;
90199220 650}