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