• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14766779411

30 Apr 2025 04:55PM UTC coverage: 72.225% (-0.06%) from 72.282%
14766779411

push

github

web-flow
wait-online: handle varlink connection errors while waiting for DNS (#37283)

Currently, if systemd-networkd-wait-online is started with --dns, and
systemd-resolved is not running, it will exit with an error right away.
Similarly, if systemd-resolved is restarted while waiting for DNS
configuration, systemd-networkd-wait-online will not attempt to
re-connect, and will potentially never see subsequent DNS
configurations.

Improve this by adding socket units for the systemd-resolved varlink
servers, and re-establish the connection in systemd-networkd-wait-online
when we receive `SD_VARLINK_ERROR_DISCONNECTED`.

8 of 16 new or added lines in 2 files covered. (50.0%)

5825 existing lines in 217 files now uncovered.

297168 of 411450 relevant lines covered (72.22%)

695892.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.4
/src/import/pull-raw.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <curl/curl.h>
4
#include <sys/xattr.h>
5

6
#include "sd-daemon.h"
7

8
#include "alloc-util.h"
9
#include "btrfs-util.h"
10
#include "copy.h"
11
#include "curl-util.h"
12
#include "fd-util.h"
13
#include "fs-util.h"
14
#include "hostname-util.h"
15
#include "import-common.h"
16
#include "import-util.h"
17
#include "install-file.h"
18
#include "log.h"
19
#include "macro.h"
20
#include "missing_fs.h"
21
#include "mkdir-label.h"
22
#include "path-util.h"
23
#include "pull-common.h"
24
#include "pull-job.h"
25
#include "pull-raw.h"
26
#include "qcow2-util.h"
27
#include "rm-rf.h"
28
#include "string-util.h"
29
#include "strv.h"
30
#include "tmpfile-util.h"
31
#include "utf8.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
        ImportFlags 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
                      * image 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

84
RawPull* raw_pull_unref(RawPull *i) {
17✔
85
        if (!i)
17✔
86
                return NULL;
87

88
        pull_job_unref(i->raw_job);
17✔
89
        pull_job_unref(i->checksum_job);
17✔
90
        pull_job_unref(i->signature_job);
17✔
91
        pull_job_unref(i->settings_job);
17✔
92
        pull_job_unref(i->roothash_job);
17✔
93
        pull_job_unref(i->roothash_signature_job);
17✔
94
        pull_job_unref(i->verity_job);
17✔
95

96
        curl_glue_unref(i->glue);
17✔
97
        sd_event_unref(i->event);
17✔
98

99
        unlink_and_free(i->temp_path);
17✔
100
        unlink_and_free(i->settings_temp_path);
17✔
101
        unlink_and_free(i->roothash_temp_path);
17✔
102
        unlink_and_free(i->roothash_signature_temp_path);
17✔
103
        unlink_and_free(i->verity_temp_path);
17✔
104

105
        free(i->final_path);
17✔
106
        free(i->settings_path);
17✔
107
        free(i->roothash_path);
17✔
108
        free(i->roothash_signature_path);
17✔
109
        free(i->verity_path);
17✔
110
        free(i->image_root);
17✔
111
        free(i->local);
17✔
112
        free(i->checksum);
17✔
113

114
        return mfree(i);
17✔
115
}
116

117
int raw_pull_new(
17✔
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;
17✔
126
        _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
×
127
        _cleanup_free_ char *root = NULL;
17✔
128
        int r;
17✔
129

130
        assert(ret);
17✔
131
        assert(image_root);
17✔
132

133
        root = strdup(image_root);
17✔
134
        if (!root)
17✔
135
                return -ENOMEM;
136

137
        if (event)
17✔
138
                e = sd_event_ref(event);
17✔
139
        else {
140
                r = sd_event_default(&e);
×
141
                if (r < 0)
×
142
                        return r;
143
        }
144

145
        r = curl_glue_new(&g, e);
17✔
146
        if (r < 0)
17✔
147
                return r;
148

149
        i = new(RawPull, 1);
17✔
150
        if (!i)
17✔
151
                return -ENOMEM;
152

153
        *i = (RawPull) {
17✔
154
                .on_finished = on_finished,
155
                .userdata = userdata,
156
                .image_root = TAKE_PTR(root),
17✔
157
                .event = TAKE_PTR(e),
17✔
158
                .glue = TAKE_PTR(g),
17✔
159
                .offset = UINT64_MAX,
160
        };
161

162
        i->glue->on_finished = pull_job_curl_on_finished;
17✔
163
        i->glue->userdata = i;
17✔
164

165
        *ret = TAKE_PTR(i);
17✔
166

167
        return 0;
17✔
168
}
169

170
static void raw_pull_report_progress(RawPull *i, RawProgress p) {
41✔
171
        unsigned percent;
41✔
172

173
        assert(i);
41✔
174

175
        switch (p) {
41✔
176

177
        case RAW_DOWNLOADING: {
5✔
178
                unsigned remain = 80;
5✔
179

180
                percent = 0;
5✔
181

182
                if (i->checksum_job) {
5✔
183
                        percent += i->checksum_job->progress_percent * 5 / 100;
×
184
                        remain -= 5;
×
185
                }
186

187
                if (i->signature_job) {
5✔
188
                        percent += i->signature_job->progress_percent * 5 / 100;
×
189
                        remain -= 5;
×
190
                }
191

192
                if (i->settings_job) {
5✔
193
                        percent += i->settings_job->progress_percent * 5 / 100;
×
194
                        remain -= 5;
×
195
                }
196

197
                if (i->roothash_job) {
5✔
198
                        percent += i->roothash_job->progress_percent * 5 / 100;
1✔
199
                        remain -= 5;
1✔
200
                }
201

202
                if (i->roothash_signature_job) {
5✔
203
                        percent += i->roothash_signature_job->progress_percent * 5 / 100;
1✔
204
                        remain -= 5;
1✔
205
                }
206

207
                if (i->verity_job) {
5✔
208
                        percent += i->verity_job->progress_percent * 10 / 100;
1✔
209
                        remain -= 10;
1✔
210
                }
211

212
                if (i->raw_job)
5✔
213
                        percent += i->raw_job->progress_percent * remain / 100;
5✔
214
                break;
215
        }
216

217
        case RAW_VERIFYING:
218
                percent = 80;
219
                break;
220

221
        case RAW_UNPACKING:
1✔
222
                percent = 85;
1✔
223
                break;
1✔
224

225
        case RAW_FINALIZING:
17✔
226
                percent = 90;
17✔
227
                break;
17✔
228

229
        case RAW_COPYING:
1✔
230
                percent = 95;
1✔
231
                break;
1✔
232

233
        default:
×
234
                assert_not_reached();
×
235
        }
236

237
        sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
41✔
238
        log_debug("Combined progress %u%%", percent);
41✔
239
}
41✔
240

241
static int raw_pull_maybe_convert_qcow2(RawPull *i) {
1✔
242
        _cleanup_(unlink_and_freep) char *t = NULL;
×
243
        _cleanup_close_ int converted_fd = -EBADF;
1✔
244
        _cleanup_free_ char *f = NULL;
1✔
245
        int r;
1✔
246

247
        assert(i);
1✔
248
        assert(i->raw_job);
1✔
249
        assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
1✔
250

251
        if (!FLAGS_SET(i->flags, IMPORT_CONVERT_QCOW2))
1✔
252
                return 0;
253

254
        assert(i->final_path);
1✔
255
        assert(i->raw_job->close_disk_fd);
1✔
256

257
        r = qcow2_detect(i->raw_job->disk_fd);
1✔
258
        if (r < 0)
1✔
259
                return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
×
260
        if (r == 0)
1✔
261
                return 0;
262

263
        /* This is a QCOW2 image, let's convert it */
264
        r = tempfn_random(i->final_path, NULL, &f);
×
265
        if (r < 0)
×
266
                return log_oom();
×
267

268
        converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
×
269
        if (converted_fd < 0)
×
270
                return log_error_errno(errno, "Failed to create %s: %m", f);
×
271

272
        t = TAKE_PTR(f);
×
273

274
        (void) import_set_nocow_and_log(converted_fd, t);
×
275

276
        log_info("Unpacking QCOW2 file.");
×
277

278
        r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
×
279
        if (r < 0)
×
280
                return log_error_errno(r, "Failed to convert qcow2 image: %m");
×
281

282
        unlink_and_free(i->temp_path);
×
283
        i->temp_path = TAKE_PTR(t);
×
284
        close_and_replace(i->raw_job->disk_fd, converted_fd);
×
285

286
        return 1;
×
287
}
288

289
static int raw_pull_determine_path(
4✔
290
                RawPull *i,
291
                const char *suffix,
292
                char **field /* input + output (!) */) {
293
        int r;
4✔
294

295
        assert(i);
4✔
296
        assert(field);
4✔
297

298
        if (*field)
4✔
299
                return 0;
300

301
        assert(i->raw_job);
4✔
302

303
        r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", suffix, field);
4✔
304
        if (r < 0)
4✔
305
                return log_oom();
×
306

307
        return 1;
308
}
309

310
static int raw_pull_copy_auxiliary_file(
3✔
311
                RawPull *i,
312
                const char *suffix,
313
                char **path /* input + output (!) */) {
314

315
        _cleanup_free_ char *local = NULL;
3✔
316
        int r;
3✔
317

318
        assert(i);
3✔
319
        assert(suffix);
3✔
320
        assert(path);
3✔
321

322
        r = raw_pull_determine_path(i, suffix, path);
3✔
323
        if (r < 0)
3✔
324
                return r;
325

326
        local = strjoin(i->image_root, "/", i->local, suffix);
3✔
327
        if (!local)
3✔
328
                return log_oom();
×
329

330
        if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
3✔
331
                r = copy_file_atomic(
×
332
                                *path,
333
                                local,
334
                                0644,
335
                                COPY_REFLINK |
×
336
                                (FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
×
337
                                (FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
×
338
        else
339
                r = install_file(AT_FDCWD, *path,
3✔
340
                                 AT_FDCWD, local,
341
                                 (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
3✔
342
                                 (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
3✔
343
        if (r == -EEXIST)
3✔
344
                log_warning_errno(r, "File %s already exists, not replacing.", local);
×
345
        else if (r == -ENOENT)
3✔
346
                log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
3✔
347
        else if (r < 0)
×
348
                log_warning_errno(r, "Failed to install file %s, ignoring: %m", local);
3✔
349
        else
UNCOV
350
                log_info("Created new file %s.", local);
×
351

352
        return 0;
353
}
354

355
static int raw_pull_make_local_copy(RawPull *i) {
1✔
356
        _cleanup_(unlink_and_freep) char *tp = NULL;
×
357
        _cleanup_free_ char *p = NULL;
1✔
358
        int r;
1✔
359

360
        assert(i);
1✔
361
        assert(i->raw_job);
1✔
362
        assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
1✔
363

364
        if (!i->local)
1✔
365
                return 0;
366

367
        if (i->raw_job->etag_exists) {
1✔
368
                /* We have downloaded this one previously, reopen it */
369

370
                assert(i->raw_job->disk_fd < 0);
×
371

372
                i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
×
373
                if (i->raw_job->disk_fd < 0)
×
374
                        return log_error_errno(errno, "Failed to open vendor image: %m");
×
375
        } else {
376
                /* We freshly downloaded the image, use it */
377

378
                assert(i->raw_job->disk_fd >= 0);
1✔
379
                assert(i->offset == UINT64_MAX);
1✔
380

381
                if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) < 0)
1✔
382
                        return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
×
383
        }
384

385
        p = strjoin(i->image_root, "/", i->local, ".raw");
1✔
386
        if (!p)
1✔
387
                return log_oom();
×
388

389
        const char *source;
1✔
390
        if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
1✔
391
                _cleanup_close_ int dfd = -EBADF;
×
392
                _cleanup_free_ char *f = NULL;
×
393

394
                r = tempfn_random(p, NULL, &f);
×
395
                if (r < 0)
×
396
                        return log_oom();
×
397

398
                dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
×
399
                if (dfd < 0)
×
400
                        return log_error_errno(errno, "Failed to create writable copy of image: %m");
×
401

402
                tp = TAKE_PTR(f);
×
403

404
                /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
405
                 * since it reduces fragmentation caused by not allowing in-place writes. */
406
                (void) import_set_nocow_and_log(dfd, tp);
×
407

408
                r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
×
409
                if (r < 0)
×
410
                        return log_error_errno(r, "Failed to make writable copy of image: %m");
×
411

412
                (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
×
413
                (void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
×
414

415
                dfd = safe_close(dfd);
×
416

417
                source = tp;
×
418
        } else
419
                source = i->final_path;
1✔
420

421
        r = install_file(AT_FDCWD, source,
2✔
422
                         AT_FDCWD, p,
423
                         (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
1✔
424
                         (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
1✔
425
                         (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
1✔
426
        if (r < 0)
1✔
427
                return log_error_errno(r, "Failed to move local image into place '%s': %m", p);
×
428

429
        tp = mfree(tp);
1✔
430

431
        log_info("Created new local image '%s'.", i->local);
1✔
432

433
        if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) {
1✔
434
                r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
×
435
                if (r < 0)
×
436
                        return r;
437
        }
438

439
        if (FLAGS_SET(i->flags, IMPORT_PULL_ROOTHASH)) {
1✔
440
                r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
1✔
441
                if (r < 0)
1✔
442
                        return r;
443
        }
444

445
        if (FLAGS_SET(i->flags, IMPORT_PULL_ROOTHASH_SIGNATURE)) {
1✔
446
                r = raw_pull_copy_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_path);
1✔
447
                if (r < 0)
1✔
448
                        return r;
449
        }
450

451
        if (FLAGS_SET(i->flags, IMPORT_PULL_VERITY)) {
1✔
452
                r = raw_pull_copy_auxiliary_file(i, ".verity", &i->verity_path);
1✔
453
                if (r < 0)
1✔
454
                        return r;
×
455
        }
456

457
        return 0;
458
}
459

460
static bool raw_pull_is_done(RawPull *i) {
20✔
461
        assert(i);
20✔
462
        assert(i->raw_job);
20✔
463

464
        if (!PULL_JOB_IS_COMPLETE(i->raw_job))
20✔
465
                return false;
466
        if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
20✔
467
                return false;
468
        if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
20✔
469
                return false;
470
        if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
20✔
471
                return false;
472
        if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
20✔
473
                return false;
474
        if (i->roothash_signature_job && !PULL_JOB_IS_COMPLETE(i->roothash_signature_job))
19✔
475
                return false;
476
        if (i->verity_job && !PULL_JOB_IS_COMPLETE(i->verity_job))
18✔
477
                return false;
1✔
478

479
        return true;
480
}
481

482
static int raw_pull_rename_auxiliary_file(
×
483
                RawPull *i,
484
                const char *suffix,
485
                char **temp_path,
486
                char **path) {
487

488
        int r;
×
489

490
        assert(i);
×
491
        assert(path);
×
492
        assert(temp_path);
×
493
        assert(*temp_path);
×
494
        assert(suffix);
×
495

496
        /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
497
         * incorporate it in the file name if we can */
498
        *path = mfree(*path);
×
499
        r = raw_pull_determine_path(i, suffix, path);
×
500
        if (r < 0)
×
501
                return r;
502

503
        r = install_file(
×
504
                        AT_FDCWD, *temp_path,
505
                        AT_FDCWD, *path,
506
                        INSTALL_READ_ONLY|
×
507
                        (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
×
508
        if (r < 0)
×
509
                return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
×
510

511
        *temp_path = mfree(*temp_path);
×
512
        return 1;
×
513
}
514

515
static void raw_pull_job_on_finished(PullJob *j) {
20✔
516
        RawPull *i;
20✔
517
        int r;
20✔
518

519
        assert(j);
20✔
520
        assert(j->userdata);
20✔
521

522
        i = j->userdata;
20✔
523

524
        if (j->error != 0) {
20✔
525
                /* Only the main job and the checksum job are fatal if they fail. The other fails are just
526
                 * "decoration", that we'll download if we can. The signature job isn't fatal here because we
527
                 * might not actually need it in case Suse style signatures are used, that are inline in the
528
                 * checksum file. */
529

530
                if (j == i->raw_job) {
3✔
531
                        if (j->error == ENOMEDIUM) /* HTTP 404 */
×
532
                                r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
×
533
                        else
534
                                r = log_error_errno(j->error, "Failed to retrieve image file.");
×
535
                        goto finish;
×
536
                } else if (j == i->checksum_job) {
3✔
537
                        r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
×
538
                        goto finish;
×
539
                } else if (j == i->signature_job)
3✔
540
                        log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
×
541
                else if (j == i->settings_job)
3✔
542
                        log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
×
543
                else if (j == i->roothash_job)
3✔
544
                        log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
1✔
545
                else if (j == i->roothash_signature_job)
2✔
546
                        log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
1✔
547
                else if (j == i->verity_job)
1✔
548
                        log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
1✔
549
                else
550
                        assert_not_reached();
×
551
        }
552

553
        /* This is invoked if either the download completed successfully, or the download was skipped because
554
         * we already have the etag. In this case ->etag_exists is true.
555
         *
556
         * We only do something when we got all files */
557

558
        if (!raw_pull_is_done(i))
20✔
559
                return;
560

561
        if (i->signature_job && i->signature_job->error != 0) {
17✔
562
                VerificationStyle style;
×
563
                PullJob *verify_job;
×
564

565
                /* The signature job failed. Let's see if we actually need it */
566

567
                verify_job = i->checksum_job ?: i->raw_job; /* if the checksum job doesn't exist this must be
×
568
                                                             * because the main job is the checksum file
569
                                                             * itself */
570

571
                assert(verify_job);
×
572

573
                r = verification_style_from_url(verify_job->url, &style);
×
574
                if (r < 0) {
×
575
                        log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
×
576
                        goto finish;
×
577
                }
578

579
                if (style == VERIFICATION_PER_DIRECTORY) { /* A failed signature file download only matters
×
580
                                                            * in per-directory verification mode, since only
581
                                                            * then the signature is detached, and thus a file
582
                                                            * of its own. */
583
                        r = log_error_errno(i->signature_job->error,
×
584
                                            "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
585
                        goto finish;
×
586
                }
587
        }
588

589
        PullJob *jj;
17✔
590
        /* Let's close these auxiliary files now, we don't need access to them anymore. */
591
        FOREACH_ARGUMENT(jj, i->settings_job, i->roothash_job, i->roothash_signature_job, i->verity_job)
85✔
592
                pull_job_close_disk_fd(jj);
68✔
593

594
        if (!i->raw_job->etag_exists) {
17✔
595
                raw_pull_report_progress(i, RAW_VERIFYING);
17✔
596

597
                r = pull_verify(i->verify,
34✔
598
                                i->checksum,
17✔
599
                                i->raw_job,
600
                                i->checksum_job,
601
                                i->signature_job,
602
                                i->settings_job,
603
                                i->roothash_job,
604
                                i->roothash_signature_job,
605
                                i->verity_job);
606
                if (r < 0)
17✔
607
                        goto finish;
×
608
        }
609

610
        if (i->flags & IMPORT_DIRECT) {
17✔
611
                assert(!i->settings_job);
16✔
612
                assert(!i->roothash_job);
16✔
613
                assert(!i->roothash_signature_job);
16✔
614
                assert(!i->verity_job);
16✔
615

616
                raw_pull_report_progress(i, RAW_FINALIZING);
16✔
617

618
                if (i->local) {
16✔
619
                        r = install_file(AT_FDCWD, i->local,
12✔
620
                                         AT_FDCWD, NULL,
621
                                         ((i->flags & IMPORT_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
4✔
622
                                         (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
4✔
623
                        if (r < 0) {
4✔
624
                                log_error_errno(r, "Failed to finalize raw file to '%s': %m", i->local);
×
625
                                goto finish;
×
626
                        }
627
                }
628
        } else {
629
                r = raw_pull_determine_path(i, ".raw", &i->final_path);
1✔
630
                if (r < 0)
1✔
631
                        goto finish;
×
632

633
                if (!i->raw_job->etag_exists) {
1✔
634
                        /* This is a new download, verify it, and move it into place */
635

636
                        assert(i->temp_path);
1✔
637
                        assert(i->final_path);
1✔
638

639
                        raw_pull_report_progress(i, RAW_UNPACKING);
1✔
640

641
                        r = raw_pull_maybe_convert_qcow2(i);
1✔
642
                        if (r < 0)
1✔
643
                                goto finish;
×
644

645
                        raw_pull_report_progress(i, RAW_FINALIZING);
1✔
646

647
                        r = install_file(AT_FDCWD, i->temp_path,
2✔
648
                                         AT_FDCWD, i->final_path,
1✔
649
                                         (i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
1✔
650
                                         (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
1✔
651
                        if (r < 0) {
1✔
652
                                log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
×
653
                                goto finish;
×
654
                        }
655

656
                        i->temp_path = mfree(i->temp_path);
1✔
657

658
                        if (i->settings_job &&
1✔
659
                            i->settings_job->error == 0) {
×
660
                                r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
×
661
                                if (r < 0)
×
662
                                        goto finish;
×
663
                        }
664

665
                        if (i->roothash_job &&
1✔
666
                            i->roothash_job->error == 0) {
1✔
667
                                r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
×
668
                                if (r < 0)
×
669
                                        goto finish;
×
670
                        }
671

672
                        if (i->roothash_signature_job &&
1✔
673
                            i->roothash_signature_job->error == 0) {
1✔
674
                                r = raw_pull_rename_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_temp_path, &i->roothash_signature_path);
×
675
                                if (r < 0)
×
676
                                        goto finish;
×
677
                        }
678

679
                        if (i->verity_job &&
1✔
680
                            i->verity_job->error == 0) {
1✔
681
                                r = raw_pull_rename_auxiliary_file(i, ".verity", &i->verity_temp_path, &i->verity_path);
×
682
                                if (r < 0)
×
683
                                        goto finish;
×
684
                        }
685
                }
686

687
                raw_pull_report_progress(i, RAW_COPYING);
1✔
688

689
                r = raw_pull_make_local_copy(i);
1✔
690
                if (r < 0)
1✔
691
                        goto finish;
×
692
        }
693

694
        r = 0;
695

696
finish:
17✔
697
        if (i->on_finished)
17✔
698
                i->on_finished(i, r, i->userdata);
17✔
699
        else
700
                sd_event_exit(i->event, r);
×
701
}
702

703
static int raw_pull_job_on_open_disk_generic(
1✔
704
                RawPull *i,
705
                PullJob *j,
706
                const char *extra,
707
                char **temp_path /* input + output */) {
708

709
        int r;
1✔
710

711
        assert(i);
1✔
712
        assert(j);
1✔
713
        assert(extra);
1✔
714
        assert(temp_path);
1✔
715

716
        assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
1✔
717

718
        if (!*temp_path) {
1✔
719
                r = tempfn_random_child(i->image_root, extra, temp_path);
1✔
720
                if (r < 0)
1✔
721
                        return log_oom();
×
722
        }
723

724
        (void) mkdir_parents_label(*temp_path, 0700);
1✔
725

726
        j->disk_fd = open(*temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
1✔
727
        if (j->disk_fd < 0)
1✔
728
                return log_error_errno(errno, "Failed to create %s: %m", *temp_path);
×
729

730
        return 0;
731
}
732

733
static int raw_pull_job_on_open_disk_raw(PullJob *j) {
17✔
734
        RawPull *i;
17✔
735
        int r;
17✔
736

737
        assert(j);
17✔
738
        assert(j->userdata);
17✔
739

740
        i = j->userdata;
17✔
741
        assert(i->raw_job == j);
17✔
742
        assert(j->disk_fd < 0);
17✔
743

744
        if (i->flags & IMPORT_DIRECT) {
17✔
745

746
                if (!i->local) { /* If no local name specified, the pull job will write its data to stdout */
16✔
747
                        j->disk_fd = STDOUT_FILENO;
12✔
748
                        j->close_disk_fd = false;
12✔
749
                        return 0;
12✔
750
                }
751

752
                (void) mkdir_parents_label(i->local, 0700);
4✔
753

754
                j->disk_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
8✔
755
                if (j->disk_fd < 0)
4✔
756
                        return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
×
757

758
                if (i->offset == UINT64_MAX)
4✔
759
                        (void) import_set_nocow_and_log(j->disk_fd, i->local);
×
760

761
        } else {
762
                r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
1✔
763
                if (r < 0)
1✔
764
                        return r;
765

766
                assert(i->offset == UINT64_MAX);
1✔
767
                (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
1✔
768
        }
769

770
        return 0;
771
}
772

773
static int raw_pull_job_on_open_disk_settings(PullJob *j) {
×
774
        RawPull *i;
×
775

776
        assert(j);
×
777
        assert(j->userdata);
×
778

779
        i = j->userdata;
×
780
        assert(i->settings_job == j);
×
781

782
        return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path);
×
783
}
784

785
static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
×
786
        RawPull *i;
×
787

788
        assert(j);
×
789
        assert(j->userdata);
×
790

791
        i = j->userdata;
×
792
        assert(i->roothash_job == j);
×
793

794
        return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
×
795
}
796

797
static int raw_pull_job_on_open_disk_roothash_signature(PullJob *j) {
×
798
        RawPull *i;
×
799

800
        assert(j);
×
801
        assert(j->userdata);
×
802

803
        i = j->userdata;
×
804
        assert(i->roothash_signature_job == j);
×
805

806
        return raw_pull_job_on_open_disk_generic(i, j, "roothash.p7s", &i->roothash_signature_temp_path);
×
807
}
808

809
static int raw_pull_job_on_open_disk_verity(PullJob *j) {
×
810
        RawPull *i;
×
811

812
        assert(j);
×
813
        assert(j->userdata);
×
814

815
        i = j->userdata;
×
816
        assert(i->verity_job == j);
×
817

818
        return raw_pull_job_on_open_disk_generic(i, j, "verity", &i->verity_temp_path);
×
819
}
820

821
static void raw_pull_job_on_progress(PullJob *j) {
5✔
822
        RawPull *i;
5✔
823

824
        assert(j);
5✔
825
        assert(j->userdata);
5✔
826

827
        i = j->userdata;
5✔
828

829
        raw_pull_report_progress(i, RAW_DOWNLOADING);
5✔
830
}
5✔
831

832
int raw_pull_start(
17✔
833
                RawPull *i,
834
                const char *url,
835
                const char *local,
836
                uint64_t offset,
837
                uint64_t size_max,
838
                ImportFlags flags,
839
                ImportVerify verify,
840
                const char *checksum) {
841

842
        int r;
17✔
843

844
        assert(i);
17✔
845
        assert(url);
17✔
846
        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
17✔
847
        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
17✔
848
        assert((verify < 0) || !checksum);
17✔
849
        assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW));
17✔
850
        assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
17✔
851
        assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !(flags & IMPORT_DIRECT));
17✔
852
        assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !checksum);
1✔
853

854
        if (!http_url_is_valid(url) && !file_url_is_valid(url))
17✔
855
                return -EINVAL;
856

857
        if (local && !pull_validate_local(local, flags))
17✔
858
                return -EINVAL;
859

860
        if (i->raw_job)
17✔
861
                return -EBUSY;
862

863
        r = free_and_strdup(&i->local, local);
17✔
864
        if (r < 0)
17✔
865
                return r;
866

867
        r = free_and_strdup(&i->checksum, checksum);
17✔
868
        if (r < 0)
17✔
869
                return r;
870

871
        i->flags = flags;
17✔
872
        i->verify = verify;
17✔
873

874
        /* Queue job for the image itself */
875
        r = pull_job_new(&i->raw_job, url, i->glue, i);
17✔
876
        if (r < 0)
17✔
877
                return r;
878

879
        i->raw_job->on_finished = raw_pull_job_on_finished;
17✔
880
        i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
17✔
881

882
        if (checksum)
17✔
883
                i->raw_job->calc_checksum = true;
4✔
884
        else if (verify != IMPORT_VERIFY_NO) {
13✔
885
                /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
886
                 * signature, which we let gpg verify instead. */
887

888
                r = pull_url_needs_checksum(url);
×
889
                if (r < 0)
×
890
                        return r;
891

892
                i->raw_job->calc_checksum = r;
×
893
                i->raw_job->force_memory = true; /* make sure this is both written to disk if that's
×
894
                                                  * requested and into memory, since we need to verify it */
895
        }
896

897
        if (size_max != UINT64_MAX)
17✔
898
                i->raw_job->uncompressed_max = size_max;
4✔
899
        if (offset != UINT64_MAX)
17✔
900
                i->raw_job->offset = i->offset = offset;
4✔
901

902
        if (!FLAGS_SET(flags, IMPORT_DIRECT)) {
17✔
903
                r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
1✔
904
                if (r < 0)
1✔
905
                        return r;
906
        }
907

908
        r = pull_make_verification_jobs(
34✔
909
                        &i->checksum_job,
910
                        &i->signature_job,
911
                        verify,
912
                        i->checksum,
17✔
913
                        url,
914
                        i->glue,
915
                        raw_pull_job_on_finished,
916
                        i);
917
        if (r < 0)
17✔
918
                return r;
919

920
        if (FLAGS_SET(flags, IMPORT_PULL_SETTINGS)) {
17✔
921
                r = pull_make_auxiliary_job(
×
922
                                &i->settings_job,
923
                                url,
924
                                raw_strip_suffixes,
925
                                ".nspawn",
926
                                verify,
927
                                i->glue,
928
                                raw_pull_job_on_open_disk_settings,
929
                                raw_pull_job_on_finished,
930
                                i);
931
                if (r < 0)
×
932
                        return r;
933
        }
934

935
        if (FLAGS_SET(flags, IMPORT_PULL_ROOTHASH)) {
17✔
936
                r = pull_make_auxiliary_job(
1✔
937
                                &i->roothash_job,
938
                                url,
939
                                raw_strip_suffixes,
940
                                ".roothash",
941
                                verify,
942
                                i->glue,
943
                                raw_pull_job_on_open_disk_roothash,
944
                                raw_pull_job_on_finished,
945
                                i);
946
                if (r < 0)
1✔
947
                        return r;
948
        }
949

950
        if (FLAGS_SET(flags, IMPORT_PULL_ROOTHASH_SIGNATURE)) {
17✔
951
                r = pull_make_auxiliary_job(
1✔
952
                                &i->roothash_signature_job,
953
                                url,
954
                                raw_strip_suffixes,
955
                                ".roothash.p7s",
956
                                verify,
957
                                i->glue,
958
                                raw_pull_job_on_open_disk_roothash_signature,
959
                                raw_pull_job_on_finished,
960
                                i);
961
                if (r < 0)
1✔
962
                        return r;
963
        }
964

965
        if (FLAGS_SET(flags, IMPORT_PULL_VERITY)) {
17✔
966
                r = pull_make_auxiliary_job(
1✔
967
                                &i->verity_job,
968
                                url,
969
                                raw_strip_suffixes,
970
                                ".verity",
971
                                verify,
972
                                i->glue,
973
                                raw_pull_job_on_open_disk_verity,
974
                                raw_pull_job_on_finished,
975
                                i);
976
                if (r < 0)
1✔
977
                        return r;
978
        }
979

980
        PullJob *j;
17✔
981
        FOREACH_ARGUMENT(j,
136✔
982
                         i->raw_job,
983
                         i->checksum_job,
984
                         i->signature_job,
985
                         i->settings_job,
986
                         i->roothash_job,
987
                         i->roothash_signature_job,
988
                         i->verity_job) {
989

990
                if (!j)
119✔
991
                        continue;
99✔
992

993
                j->on_progress = raw_pull_job_on_progress;
20✔
994
                j->sync = FLAGS_SET(flags, IMPORT_SYNC);
20✔
995

996
                r = pull_job_begin(j);
20✔
997
                if (r < 0)
20✔
998
                        return r;
×
999
        }
1000

1001
        return 0;
17✔
1002
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc