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