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

systemd / systemd / 28485131107

30 Jun 2026 03:24PM UTC coverage: 72.877% (+0.07%) from 72.809%
28485131107

push

github

bluca
test: reduce number of disks in TEST-64-UDEV-STORAGE-simultaneous_events on Debian/Ubuntu

This test never finishes in Ubuntu autopkgtest with the current values,
and is currently skipped all together on Debian. When running on either,
reduce the number of disks to make the test more reliable.

341909 of 469157 relevant lines covered (72.88%)

1343417.0 hits per line

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

74.63
/src/bootctl/bootctl-install.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdlib.h>
4
#include <unistd.h>
5

6
#include "sd-device.h"
7
#include "sd-varlink.h"
8

9
#include "alloc-util.h"
10
#include "ask-password-api.h"
11
#include "blockdev-util.h"
12
#include "boot-entry.h"
13
#include "bootctl.h"
14
#include "bootctl-install.h"
15
#include "bootctl-random-seed.h"
16
#include "bootctl-util.h"
17
#include "chase.h"
18
#include "copy.h"
19
#include "crypto-util.h"
20
#include "dirent-util.h"
21
#include "efi-api.h"
22
#include "efi.h"
23
#include "efivars.h"
24
#include "env-file.h"
25
#include "fd-util.h"
26
#include "fileio.h"
27
#include "find-esp.h"
28
#include "fs-util.h"
29
#include "glyph-util.h"
30
#include "id128-util.h"
31
#include "install-file.h"
32
#include "io-util.h"
33
#include "json-util.h"
34
#include "kernel-config.h"
35
#include "log.h"
36
#include "parse-argument.h"
37
#include "path-util.h"
38
#include "pe-binary.h"
39
#include "rm-rf.h"
40
#include "stat-util.h"
41
#include "string-table.h"
42
#include "string-util.h"
43
#include "strv.h"
44
#include "time-util.h"
45
#include "tmpfile-util.h"
46
#include "umask-util.h"
47
#include "utf8.h"
48
#include "varlink-util.h"
49

50
typedef enum InstallOperation {
51
        INSTALL_NEW,
52
        INSTALL_UPDATE,
53
        INSTALL_REMOVE,
54
        INSTALL_TEST,
55
        _INSTALL_OPERATION_MAX,
56
        _INSTALL_OPERATION_INVALID = -1,
57
} InstallOperation;
58

59
typedef struct InstallContext {
60
        InstallOperation operation;
61
        bool graceful;
62
        char *root;
63
        int root_fd;
64
        sd_id128_t machine_id;
65
        char *install_layout;
66
        BootEntryTokenType entry_token_type;
67
        char *entry_token;
68
        int make_entry_directory; /* tri-state */
69
        InstallSource install_source;
70
        char *esp_path;
71
        int esp_fd;
72
        uint32_t esp_part;
73
        uint64_t esp_pstart;
74
        uint64_t esp_psize;
75
        sd_id128_t esp_uuid;
76
        char *xbootldr_path;
77
        int xbootldr_fd;
78
#if HAVE_OPENSSL
79
        X509 *secure_boot_certificate;
80
        EVP_PKEY *secure_boot_private_key;
81
#endif
82
        int touch_variables; /* tri-state */
83
} InstallContext;
84

85
#define INSTALL_CONTEXT_NULL                                            \
86
        (InstallContext) {                                              \
87
                .operation = _INSTALL_OPERATION_INVALID,                \
88
                .root_fd = -EBADF,                                      \
89
                .entry_token_type = _BOOT_ENTRY_TOKEN_TYPE_INVALID,     \
90
                .make_entry_directory = -1,                             \
91
                .install_source = _INSTALL_SOURCE_INVALID,              \
92
                .esp_part = UINT32_MAX,                                 \
93
                .esp_pstart = UINT64_MAX,                               \
94
                .esp_psize = UINT64_MAX,                                \
95
                .esp_fd = -EBADF,                                       \
96
                .xbootldr_fd = -EBADF,                                  \
97
                .touch_variables = -1,                                  \
98
        }
99

100
static const char* install_operation_table[_INSTALL_OPERATION_MAX] = {
101
        [INSTALL_NEW]    = "new",
102
        [INSTALL_UPDATE] = "update",
103
        [INSTALL_REMOVE] = "remove",
104
        [INSTALL_TEST]   = "test",
105
};
106

107
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_operation, InstallOperation);
×
108

109
static void install_context_done(InstallContext *c) {
138✔
110
        assert(c);
138✔
111

112
        c->root = mfree(c->root);
138✔
113
        c->root_fd = safe_close(c->root_fd);
138✔
114
        c->install_layout = mfree(c->install_layout);
138✔
115
        c->entry_token = mfree(c->entry_token);
138✔
116
        c->esp_path = mfree(c->esp_path);
138✔
117
        c->esp_fd = safe_close(c->esp_fd);
138✔
118
        c->xbootldr_path = mfree(c->xbootldr_path);
138✔
119
        c->xbootldr_fd = safe_close(c->xbootldr_fd);
138✔
120
#if HAVE_OPENSSL
121
        if (c->secure_boot_private_key) {
138✔
122
                sym_EVP_PKEY_free(c->secure_boot_private_key);
1✔
123
                c->secure_boot_private_key = NULL;
1✔
124
        }
125
        if (c->secure_boot_certificate) {
138✔
126
                sym_X509_free(c->secure_boot_certificate);
1✔
127
                c->secure_boot_certificate = NULL;
1✔
128
        }
129
#endif
130
}
138✔
131

132
static int install_context_from_cmdline(
69✔
133
                InstallContext *ret,
134
                InstallOperation operation) {
135

136
        int r;
69✔
137

138
        assert(ret);
69✔
139
        assert(operation >= 0);
69✔
140
        assert(operation < _INSTALL_OPERATION_MAX);
69✔
141

142
        _cleanup_(install_context_done) InstallContext b = INSTALL_CONTEXT_NULL;
69✔
143
        b.operation = operation;
69✔
144
        b.graceful = arg_graceful() == ARG_GRACEFUL_FORCE ||
69✔
145
                (operation == INSTALL_UPDATE && arg_graceful() != ARG_GRACEFUL_NO);
26✔
146
        b.machine_id = arg_machine_id;
69✔
147
        b.entry_token_type = arg_entry_token_type;
69✔
148
        b.make_entry_directory = arg_make_entry_directory;
69✔
149
        b.install_source = arg_install_source;
69✔
150

151
        if (strdup_to(&b.entry_token, arg_entry_token) < 0 ||
69✔
152
            strdup_to(&b.install_layout, arg_install_layout) < 0)
69✔
153
                return log_oom();
×
154

155
        if (arg_root) {
69✔
156
                b.root_fd = open(arg_root, O_CLOEXEC|O_DIRECTORY|O_PATH);
28✔
157
                if (b.root_fd < 0)
28✔
158
                        return log_error_errno(errno, "Failed to open root directory '%s': %m", arg_root);
×
159

160
                r = strdup_to(&b.root, arg_root);
28✔
161
                if (r < 0)
28✔
162
                        return log_oom();
×
163
        } else
164
                b.root_fd = XAT_FDROOT;
41✔
165

166
        r = acquire_esp(/* unprivileged_mode= */ false,
69✔
167
                        b.graceful,
168
                        &b.esp_fd,
169
                        &b.esp_part,
170
                        &b.esp_pstart,
171
                        &b.esp_psize,
172
                        &b.esp_uuid,
173
                        /* ret_devid= */ NULL);
174
        /* If --graceful is specified and we can't find an ESP, handle this cleanly */
175
        if (r < 0 && (!b.graceful || r != -ENOKEY))
69✔
176
                return r;
177

178
        if (r >= 0) { /* An ESP has been found */
69✔
179
                assert(arg_esp_path);
69✔
180

181
                if (arg_root) {
69✔
182
                        const char *e = path_startswith(arg_esp_path, arg_root);
28✔
183
                        if (!e)
28✔
184
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "ESP path '%s' not below specified root '%s', refusing.", arg_esp_path, arg_root);
×
185

186
                        r = strdup_to(&b.esp_path, e);
28✔
187
                } else
188
                        r = strdup_to(&b.esp_path, arg_esp_path);
41✔
189
                if (r < 0)
69✔
190
                        return log_oom();
×
191
        }
192

193
        r = acquire_xbootldr(
69✔
194
                        /* unprivileged_mode= */ false,
195
                        &b.xbootldr_fd,
196
                        /* ret_uuid= */ NULL,
197
                        /* ret_devid= */ NULL);
198
        if (r < 0)
69✔
199
                return r;
200
        if (r > 0) { /* XBOOTLDR has been found */
69✔
201
                assert(arg_xbootldr_path);
28✔
202

203
                if (arg_root) {
28✔
204
                        const char *e = path_startswith(arg_xbootldr_path, arg_root);
28✔
205
                        if (!e)
28✔
206
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "XBOOTLDR path '%s' not below specified root '%s', refusing.", arg_xbootldr_path, arg_root);
×
207

208
                        r = strdup_to(&b.xbootldr_path, e);
28✔
209
                } else
210
                        r = strdup_to(&b.xbootldr_path, arg_xbootldr_path);
×
211
                if (r < 0)
28✔
212
                        return log_oom();
×
213
        }
214

215
        b.touch_variables = arg_touch_variables;
69✔
216

217
        *ret = TAKE_GENERIC(b, InstallContext, INSTALL_CONTEXT_NULL);
69✔
218

219
        return !!ret->esp_path; /* return positive if we found an ESP */
69✔
220
}
221

222
static int acquire_dollar_boot_fd(InstallContext *c) {
103✔
223
        assert(c);
103✔
224

225
        if (c->xbootldr_fd >= 0)
103✔
226
                return c->xbootldr_fd;
227

228
        if (c->esp_fd >= 0)
69✔
229
                return c->esp_fd;
230

231
        return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Cannot access $BOOT, as neither ESP nor XBOOTLDR have been found.");
×
232
}
233

234
static const char* dollar_boot_path(InstallContext *c) {
103✔
235
        assert(c);
103✔
236

237
        return c->xbootldr_path ?: c->esp_path;
103✔
238
}
239

240
static bool should_touch_install_variables(InstallContext *c) {
52✔
241
        assert(c);
52✔
242

243
        if (c->touch_variables >= 0)
52✔
244
                return c->touch_variables;
20✔
245

246
        if (!is_efi_boot())  /* NB: this internally checks if we run in a container */
32✔
247
                return false;
248

249
        return empty_or_root(c->root);
32✔
250
}
251

252
static int load_etc_machine_id(InstallContext *c) {
55✔
253
        int r;
55✔
254

255
        assert(c);
55✔
256

257
        r = id128_get_machine_at(c->root_fd, &c->machine_id);
55✔
258
        if (ERRNO_IS_NEG_MACHINE_ID_UNSET(r)) /* Not set or empty */
55✔
259
                return 0;
260
        if (r < 0)
35✔
261
                return log_error_errno(r, "Failed to get machine-id: %m");
×
262

263
        log_debug("Loaded machine ID %s from '%s/etc/machine-id'.", strempty(c->root), SD_ID128_TO_STRING(c->machine_id));
55✔
264
        return 0;
35✔
265
}
266

267
static int load_etc_machine_info(InstallContext *c) {
55✔
268
        /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
269
         * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
270
         * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
271
         * has been deprecated and is only returned for compatibility. */
272
        _cleanup_free_ char *s = NULL, *layout = NULL;
×
273
        int r;
55✔
274

275
        assert(c);
55✔
276

277
        _cleanup_free_ char *j = path_join(c->root, "/etc/machine-info");
110✔
278
        if (!j)
55✔
279
                return log_oom();
×
280

281
        _cleanup_close_ int fd =
55✔
282
                chase_and_openat(
55✔
283
                                c->root_fd,
284
                                c->root_fd,
285
                                "/etc/machine-info",
286
                                CHASE_MUST_BE_REGULAR,
287
                                O_RDONLY|O_CLOEXEC,
288
                                /* ret_path= */ NULL);
289
        if (fd == -ENOENT)
55✔
290
                return 0;
291
        if (fd < 0)
×
292
                return log_error_errno(fd, "Failed to open '%s': %m", j);
×
293

294
        r = parse_env_file_fd(
×
295
                        fd, "/etc/machine-info",
296
                        "KERNEL_INSTALL_LAYOUT", &layout,
297
                        "KERNEL_INSTALL_MACHINE_ID", &s);
298
        if (r < 0)
×
299
                return log_error_errno(r, "Failed to parse '%s': %m", j);
×
300

301
        if (!isempty(s)) {
×
302
                if (!arg_quiet)
×
303
                        log_notice("Read $KERNEL_INSTALL_MACHINE_ID from '%s'. "
×
304
                                   "Please move it to '%s/etc/kernel/entry-token'.", j, strempty(c->root));
305

306
                r = sd_id128_from_string(s, &c->machine_id);
×
307
                if (r < 0)
×
308
                        return log_error_errno(r, "Failed to parse KERNEL_INSTALL_MACHINE_ID=\"%s\" in '%s': %m", s, j);
×
309

310
                log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=\"%s\" from '%s'.",
×
311
                          SD_ID128_TO_STRING(c->machine_id), j);
312
        }
313

314
        if (!isempty(layout)) {
55✔
315
                if (!arg_quiet)
×
316
                        log_notice("Read $KERNEL_INSTALL_LAYOUT from '%s'. "
×
317
                                   "Please move it to the layout= setting of '%s/etc/kernel/install.conf'.", j, strempty(c->root));
318

319
                log_debug("KERNEL_INSTALL_LAYOUT=\"%s\" is specified in '%s'.", layout, j);
×
320
                free_and_replace(c->install_layout, layout);
×
321
        }
322

323
        return 0;
324
}
325

326
static int load_kernel_install_layout(InstallContext *c) {
55✔
327
        _cleanup_free_ char *layout = NULL;
55✔
328
        int r;
55✔
329

330
        assert(c);
55✔
331

332
        const char *e = secure_getenv("KERNEL_INSTALL_CONF_ROOT");
55✔
333
        r = load_kernel_install_conf_at(
55✔
334
                        e ? NULL : c->root,
335
                        e ? XAT_FDROOT : c->root_fd,
336
                        e,
337
                        /* ret_machine_id= */ NULL,
338
                        /* ret_boot_root= */ NULL,
339
                        &layout,
340
                        /* ret_initrd_generator= */ NULL,
341
                        /* ret_uki_generator= */ NULL);
342
        if (r <= 0)
55✔
343
                return r;
344

345
        if (!isempty(layout)) {
55✔
346
                log_debug("layout=\"%s\" is specified in config.", layout);
×
347
                free_and_replace(c->install_layout, layout);
×
348
        }
349

350
        return 0;
351
}
352

353
static bool use_boot_loader_spec_type1(InstallContext *c) {
55✔
354
        assert(c);
55✔
355
        /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
356
         * Specification Type #1 is the chosen format for our boot loader entries */
357
        return !c->install_layout || streq(c->install_layout, "bls");
55✔
358
}
359

360
static int settle_make_entry_directory(InstallContext *c) {
55✔
361
        int r;
55✔
362

363
        assert(c);
55✔
364

365
        r = load_etc_machine_id(c);
55✔
366
        if (r < 0)
55✔
367
                return r;
368

369
        r = load_etc_machine_info(c);
55✔
370
        if (r < 0)
55✔
371
                return r;
372

373
        r = load_kernel_install_layout(c);
55✔
374
        if (r < 0)
55✔
375
                return r;
376

377
        const char *e = secure_getenv("KERNEL_INSTALL_CONF_ROOT");
55✔
378
        r = boot_entry_token_ensure_at(
55✔
379
                        e ? XAT_FDROOT : c->root_fd,
380
                        e,
381
                        c->machine_id,
382
                        /* machine_id_is_random= */ false,
383
                        &c->entry_token_type,
384
                        &c->entry_token);
385
        if (r < 0)
55✔
386
                return r;
387

388
        log_debug("Using entry token: %s", c->entry_token);
55✔
389

390
        bool layout_type1 = use_boot_loader_spec_type1(c);
55✔
391
        if (c->make_entry_directory < 0) { /* Automatic mode */
55✔
392
                if (layout_type1) {
×
393
                        if (c->entry_token_type == BOOT_ENTRY_TOKEN_MACHINE_ID) {
×
394
                                _cleanup_free_ char *j = path_join(c->root, "/etc/machine-id");
×
395
                                if (!j)
×
396
                                        return log_oom();
×
397

398
                                _cleanup_close_ int fd = -EBADF;
×
399
                                r = chaseat(c->root_fd,
×
400
                                            c->root_fd,
401
                                            "/etc/machine-id",
402
                                            CHASE_MUST_BE_REGULAR,
403
                                            /* ret_path= */ NULL,
404
                                            &fd);
405
                                if (r < 0)
×
406
                                        return log_debug_errno(r, "Unable to open '%s': %m", j);
×
407

408
                                r = fd_is_temporary_fs(fd);
×
409
                                if (r < 0)
×
410
                                        return log_debug_errno(r, "Couldn't determine whether '%s' is on a temporary file system: %m", j);
×
411

412
                                c->make_entry_directory = r == 0;
×
413
                        } else
414
                                c->make_entry_directory = true;
×
415
                } else
416
                        c->make_entry_directory = false;
×
417
        }
418

419
        if (c->make_entry_directory > 0 && !layout_type1)
55✔
420
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
421
                                       "KERNEL_INSTALL_LAYOUT=\"%s\" is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
422
                                       c->install_layout);
423

424
        return 0;
425
}
426

427
static int compare_product(const char *a, const char *b) {
52✔
428
        size_t x, y;
52✔
429

430
        assert(a);
52✔
431
        assert(b);
52✔
432

433
        x = strcspn(a, " ");
52✔
434
        y = strcspn(b, " ");
52✔
435
        if (x != y)
52✔
436
                return x < y ? -1 : x > y ? 1 : 0;
×
437

438
        return strncmp(a, b, x);
52✔
439
}
440

441
static int compare_version(const char *a, const char *b) {
52✔
442
        assert(a);
52✔
443
        assert(b);
52✔
444

445
        a += strcspn(a, " ");
52✔
446
        a += strspn(a, " ");
52✔
447
        b += strcspn(b, " ");
52✔
448
        b += strspn(b, " ");
52✔
449

450
        return strverscmp_improved(a, b);
52✔
451
}
452

453
static int version_check(int fd_from, const char *from, int fd_to, const char *to) {
52✔
454
        _cleanup_free_ char *a = NULL, *b = NULL;
52✔
455
        int r;
52✔
456

457
        assert(fd_from >= 0);
52✔
458
        assert(from);
52✔
459
        assert(fd_to >= 0);
52✔
460
        assert(to);
52✔
461

462
        /* Does not reposition file offset */
463

464
        r = get_file_version(fd_from, &a);
52✔
465
        if (r == -ESRCH)
52✔
466
                return log_notice_errno(r, "Source file \"%s\" does not carry version information!", from);
×
467
        if (r < 0)
52✔
468
                return log_error_errno(r, "Failed to get file version of '%s': %m", from);
×
469

470
        r = get_file_version(fd_to, &b);
52✔
471
        if (r == -ESRCH)
52✔
472
                return log_info_errno(r, "Skipping \"%s\", it's owned by another boot loader (no version info found).", to);
×
473
        if (r < 0)
52✔
474
                return log_error_errno(r, "Failed to get file version of '%s': %m", to);
×
475
        if (compare_product(a, b) != 0)
52✔
476
                return log_info_errno(SYNTHETIC_ERRNO(ESRCH),
×
477
                                      "Skipping \"%s\", it's owned by another boot loader.", to);
478

479
        r = compare_version(a, b);
52✔
480
        log_debug("Comparing versions: \"%s\" %s \"%s\"", a, comparison_operator(r), b);
92✔
481
        if (r < 0)
52✔
482
                return log_warning_errno(SYNTHETIC_ERRNO(ESTALE),
×
483
                                         "Skipping \"%s\", newer boot loader version in place already.", to);
484
        if (r == 0)
52✔
485
                return log_info_errno(SYNTHETIC_ERRNO(ESTALE),
52✔
486
                                      "Skipping \"%s\", same boot loader version in place already.", to);
487

488
        return 0;
489
}
490

491
static int copy_file_with_version_check(
70✔
492
                const char *source_path,
493
                int source_fd,
494
                const char *dest_path,
495
                int dest_parent_fd,
496
                const char *dest_filename,
497
                int dest_fd,
498
                bool force) {
499

500
        int r;
70✔
501

502
        assert(source_path);
70✔
503
        assert(source_fd >= 0);
70✔
504
        assert(dest_path);
70✔
505
        assert(dest_parent_fd >= 0);
70✔
506
        assert(dest_filename);
70✔
507

508
        if (!force && dest_fd >= 0) {
70✔
509
                r = version_check(source_fd, source_path, dest_fd, dest_path);
26✔
510
                if (r < 0)
26✔
511
                        return r;
70✔
512
        }
513

514
        _cleanup_free_ char *t = NULL;
44✔
515
        _cleanup_close_ int write_fd = -EBADF;
44✔
516
        write_fd = open_tmpfile_linkable_at(dest_parent_fd, dest_filename, O_WRONLY|O_CLOEXEC, &t);
44✔
517
        if (write_fd < 0)
44✔
518
                return log_error_errno(write_fd, "Failed to open \"%s\" for writing: %m", dest_path);
×
519

520
        CLEANUP_TMPFILE_AT(dest_parent_fd, t);
44✔
521

522
        /* Reset file offset before we start copying, since we copy this file multiple times, and the offset
523
         * might be left at the end of the file. (Resetting before rather than after a copy attempt is safer
524
         * because a previous attempt might have failed half-way, leaving the file offset at some undefined
525
         * place.) */
526
        r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_REFLINK|COPY_SEEK0_SOURCE);
44✔
527
        if (r < 0)
44✔
528
                return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", source_path, dest_path);
×
529

530
        (void) copy_times(source_fd, write_fd, /* flags= */ 0);
44✔
531
        (void) fchmod(write_fd, 0644);
44✔
532

533
        r = link_tmpfile_at(write_fd, dest_parent_fd, t, dest_filename, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
44✔
534
        if (r < 0)
44✔
535
                return log_error_errno(r, "Failed to move data from \"%s\" to \"%s\": %m", source_path, dest_path);
×
536

537
        t = mfree(t); /* disarm CLEANUP_TMPFILE_AT() */
44✔
538

539
        log_info("Copied \"%s\" to \"%s\".", source_path, dest_path);
44✔
540
        return 0;
541
}
542

543
static int mkdir_one(const char *root, int root_fd, const char *path) {
153✔
544
        int r;
153✔
545

546
        assert(root);
153✔
547
        assert(root_fd >= 0);
153✔
548
        assert(path);
153✔
549

550
        _cleanup_free_ char *p = path_join(empty_to_root(root), path);
306✔
551
        if (!p)
153✔
552
                return log_oom();
×
553

554
        r = chaseat(root_fd,
153✔
555
                    root_fd,
556
                    path,
557
                    CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
558
                    /* ret_path= */ NULL,
559
                    /* ret_fd= */ NULL);
560
        if (r < 0)
153✔
561
                return log_error_errno(r, "Failed to create \"%s\": %m", p);
×
562

563
        log_info("Created directory \"%s\".", p);
153✔
564
        return 0;
565
}
566

567
static const char *const esp_subdirs[] = {
568
        /* The directories to place in the ESP */
569
        "EFI",
570
        "EFI/systemd",
571
        "EFI/BOOT",
572
        "loader",
573
        "loader/keys",
574
        NULL
575
};
576

577
static const char *const dollar_boot_subdirs[] = {
578
        /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
579
        "loader",
580
        "loader/entries",  /* Type #1 entries */
581
        "EFI",
582
        "EFI/Linux",       /* Type #2 entries */
583
        NULL
584
};
585

586
static int create_subdirs(const char *root, int root_fd, const char * const *subdirs) {
32✔
587
        int r;
32✔
588

589
        assert(root);
32✔
590
        assert(root_fd >= 0);
32✔
591

592
        STRV_FOREACH(i, subdirs) {
176✔
593
                r = mkdir_one(root, root_fd, *i);
144✔
594
                if (r < 0)
144✔
595
                        return r;
596
        }
597

598
        return 0;
599
}
600

601
static int update_efi_boot_binaries(
26✔
602
                InstallContext *c,
603
                const char *source_path,
604
                int source_fd,
605
                const char *ignore_filename) {
606

607
        int r, ret = 0;
26✔
608

609
        assert(c);
26✔
610
        assert(source_path);
26✔
611

612
        if (c->esp_fd < 0)
26✔
613
                return c->esp_fd;
26✔
614

615
        _cleanup_free_ char *j = path_join(c->root, c->esp_path);
52✔
616
        if (!j)
26✔
617
                return log_oom();
×
618

619
        _cleanup_closedir_ DIR *d = NULL;
26✔
620
        r = chase_and_opendirat(
26✔
621
                        c->esp_fd,
622
                        c->esp_fd,
623
                        "/EFI/BOOT",
624
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_DIRECTORY,
625
                        /* ret_path= */ NULL,
626
                        &d);
627
        if (r == -ENOENT)
26✔
628
                return 0;
629
        if (r < 0)
26✔
630
                return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", j);
×
631

632
        FOREACH_DIRENT(de, d, break) {
130✔
633
                _cleanup_close_ int fd = -EBADF;
130✔
634

635
                if (!endswith_no_case(de->d_name, ".efi"))
52✔
636
                        continue;
×
637

638
                if (strcaseeq_ptr(ignore_filename, de->d_name))
52✔
639
                        continue;
26✔
640

641
                fd = xopenat_full(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_NOFOLLOW, XO_REGULAR, /* mode= */ MODE_INVALID);
26✔
642
                if (fd < 0)
26✔
643
                        return log_error_errno(fd, "Failed to open \"%s/%s\" for reading: %m", j, de->d_name);
×
644

645
                r = pe_is_native_fd(fd);
26✔
646
                if (r < 0) {
26✔
647
                        log_warning_errno(r, "Failed to detect if \"%s/%s\" is for native architecture, ignoring: %m", j, de->d_name);
×
648
                        continue;
×
649
                }
650
                if (r == 0)
26✔
651
                        continue;
26✔
652

653
                _cleanup_free_ char *dest_path = path_join(j, "/EFI/BOOT", de->d_name);
×
654
                if (!dest_path)
×
655
                        return log_oom();
×
656

657
                r = copy_file_with_version_check(source_path, source_fd, dest_path, dirfd(d), de->d_name, fd, /* force= */ false);
×
658
                if (IN_SET(r, -ESTALE, -ESRCH))
×
659
                        continue;
×
660
                RET_GATHER(ret, r);
×
661
        }
662

663
        return ret;
664
}
665

666
static int copy_one_file(
48✔
667
                InstallContext *c,
668
                const char *name,
669
                bool force) {
670

671
        int r, ret = 0;
48✔
672

673
        assert(c);
48✔
674

675
        _cleanup_free_ char *dest_name = strdup(name);
48✔
676
        if (!dest_name)
48✔
677
                return log_oom();
×
678
        char *s = endswith_no_case(dest_name, ".signed");
48✔
679
        if (s)
48✔
680
                *s = 0;
48✔
681

682
        _cleanup_free_ char *sp = path_join(BOOTLIBDIR, name);
96✔
683
        if (!sp)
48✔
684
                return log_oom();
×
685

686
        _cleanup_free_ char *source_path = NULL;
48✔
687
        _cleanup_close_ int source_fd = -EBADF;
48✔
688
        if (IN_SET(c->install_source, INSTALL_SOURCE_AUTO, INSTALL_SOURCE_IMAGE)) {
48✔
689
                source_fd = chase_and_openat(
48✔
690
                                c->root_fd,
691
                                c->root_fd,
692
                                sp,
693
                                CHASE_MUST_BE_REGULAR,
694
                                O_RDONLY|O_CLOEXEC,
695
                                &source_path);
696
                if (source_fd < 0 && (source_fd != -ENOENT || c->install_source != INSTALL_SOURCE_AUTO))
48✔
697
                        return log_error_errno(source_fd, "Failed to resolve path '%s' under directory '%s': %m", sp, c->root);
×
698

699
                /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
700
        }
701
        if (source_fd < 0) {
16✔
702
                source_fd = chase_and_open(
16✔
703
                                sp,
704
                                /* root= */ NULL,
705
                                CHASE_MUST_BE_REGULAR,
706
                                O_RDONLY|O_CLOEXEC,
707
                                &source_path);
708
                if (source_fd < 0)
16✔
709
                        return log_error_errno(source_fd, "Failed to resolve path '%s': %m", sp);
×
710
        }
711

712
        if (c->esp_fd < 0)
48✔
713
                return c->esp_fd;
714

715
        _cleanup_free_ char *j = path_join(c->root, c->esp_path);
96✔
716
        if (!j)
48✔
717
                return log_oom();
×
718

719
        _cleanup_close_ int dest_parent_fd = -EBADF;
48✔
720
        r = chaseat(c->esp_fd,
48✔
721
                    c->esp_fd,
722
                    "/EFI/systemd",
723
                    CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
724
                    /* ret_path= */ NULL,
725
                    &dest_parent_fd);
726
        if (r < 0)
48✔
727
                return log_error_errno(r, "Failed to resolve path '/EFI/systemd' under directory '%s': %m", j);
×
728

729
        _cleanup_free_ char *dest_path = path_join(j, "/EFI/systemd", dest_name);
96✔
730
        if (!dest_path)
48✔
731
                return log_oom();
×
732

733
        _cleanup_close_ int dest_fd = xopenat_full(dest_parent_fd, dest_name, O_RDONLY|O_CLOEXEC, XO_REGULAR, MODE_INVALID);
96✔
734
        if (dest_fd < 0 && dest_fd != -ENOENT)
48✔
735
                return log_error_errno(dest_fd, "Failed to open '%s' under '%s/EFI/systemd' directory: %m", dest_name, j);
×
736

737
        const char *e = startswith(dest_name, "systemd-boot");
48✔
738

739
        /* If a primary sd-boot binary already exists and the source is a newer version, copy
740
         * the existing primary to systemd-boot-fallback{arch}.efi before installing the new
741
         * one, so firmware has a fallback to the previous binary. The fallback is left alone
742
         * when its product and version match the currently booted bootloader (from LoaderInfo),
743
         * so a known good binary stays as the fallback. In all other cases, like no fallback yet,
744
         * LoaderInfo is unavailable, or product/version differs from what booted, it is
745
         * overwritten with the current primary. */
746
        if (e && dest_fd >= 0 && !force) {
48✔
747
                r = version_check(source_fd, source_path, dest_fd, dest_path);
26✔
748
                if (r < 0)
26✔
749
                        /* Stash the error and fall through; the BOOT{arch}.EFI updates below still run. */
750
                        ret = r;
751
                else {
752
                        _cleanup_free_ char *fallback_name = strjoin("systemd-boot-fallback", e);
×
753
                        if (!fallback_name)
×
754
                                return log_oom();
×
755

756
                        _cleanup_free_ char *fallback_path = path_join(j, "/EFI/systemd", fallback_name);
×
757
                        if (!fallback_path)
×
758
                                return log_oom();
×
759

760
                        /* Leave the fallback alone if it already holds the currently booted product
761
                         * and version, so a known good binary stays as the fallback. If there is no
762
                         * fallback yet, LoaderInfo is unavailable, or there is a mismatch, then
763
                         * overwrite it with the current primary. */
764
                        bool should_rotate = true;
×
765
                        _cleanup_close_ int fallback_fd = xopenat_full(dest_parent_fd, fallback_name, O_RDONLY|O_CLOEXEC, XO_REGULAR, MODE_INVALID);
×
766
                        if (fallback_fd >= 0) {
×
767
                                _cleanup_free_ char *loader_info = NULL, *fallback_version = NULL;
×
768

769
                                if (efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderInfo"), &loader_info) >= 0 &&
×
770
                                    get_file_version(fallback_fd, &fallback_version) >= 0)
×
771
                                        should_rotate = compare_product(loader_info, fallback_version) != 0 ||
×
772
                                                        compare_version(loader_info, fallback_version) != 0;
×
773
                        }
774

775
                        if (should_rotate) {
×
776
                                r = copy_file_with_version_check(dest_path, dest_fd, fallback_path, dest_parent_fd, fallback_name, /* dest_fd= */ -EBADF, /* force= */ true);
×
777
                                if (r < 0)
×
778
                                        log_warning_errno(r, "Failed to back up sd-boot binary to fallback path, continuing: %m");
×
779
                        }
780

781
                        ret = copy_file_with_version_check(source_path, source_fd, dest_path, dest_parent_fd, dest_name, dest_fd, /* force= */ true);
×
782
                }
783
        } else
784
                /* Note that if this fails we do the second copy anyway, but return this error code,
785
                 * so we stash it away in a separate variable. */
786
                ret = copy_file_with_version_check(source_path, source_fd, dest_path, dest_parent_fd, dest_name, dest_fd, force);
22✔
787

788
        if (e) {
48✔
789

790
                /* Create the EFI default boot loader name (specified for removable devices) */
791
                _cleanup_free_ char *boot_dot_efi = strjoin("BOOT", e);
96✔
792
                if (!boot_dot_efi)
48✔
793
                        return log_oom();
×
794

795
                ascii_strupper(boot_dot_efi);
48✔
796

797
                _cleanup_close_ int default_dest_parent_fd = -EBADF;
48✔
798
                r = chaseat(c->esp_fd,
48✔
799
                            c->esp_fd,
800
                            "/EFI/BOOT",
801
                            CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
802
                            /* ret_path= */ NULL,
803
                            &default_dest_parent_fd);
804
                if (r < 0)
48✔
805
                        return log_error_errno(r, "Failed to resolve path '/EFI/BOOT/' under directory '%s': %m", j);
×
806

807
                _cleanup_free_ char *default_dest_path = path_join(j, "/EFI/BOOT", boot_dot_efi);
96✔
808
                if (!default_dest_path)
48✔
809
                        return log_oom();
×
810

811
                _cleanup_close_ int default_dest_fd = xopenat_full(default_dest_parent_fd, boot_dot_efi, O_RDONLY|O_CLOEXEC, XO_REGULAR, MODE_INVALID);
96✔
812
                if (default_dest_fd < 0 && default_dest_fd != -ENOENT)
48✔
813
                        return log_error_errno(default_dest_fd, "Failed to open '%s' under '%s/EFI/BOOT' directory: %m", boot_dot_efi, j);
×
814

815
                RET_GATHER(ret, copy_file_with_version_check(source_path, source_fd, default_dest_path, default_dest_parent_fd, boot_dot_efi, default_dest_fd, force));
48✔
816

817
                /* If we were installed under any other name in /EFI/BOOT/, make sure we update those
818
                 * binaries as well. */
819
                if (!force)
48✔
820
                        RET_GATHER(ret, update_efi_boot_binaries(c, source_path, source_fd, boot_dot_efi));
26✔
821
        }
822

823
        return ret;
824
}
825

826
static int install_binaries(
42✔
827
                InstallContext *c,
828
                const char *arch) {
829

830
        int r;
42✔
831

832
        assert(c);
42✔
833

834
        _cleanup_free_ char *source_path = NULL;
42✔
835
        _cleanup_closedir_ DIR *d = NULL;
42✔
836
        if (IN_SET(c->install_source, INSTALL_SOURCE_AUTO, INSTALL_SOURCE_IMAGE)) {
42✔
837
                r = chase_and_opendirat(
42✔
838
                                c->root_fd,
839
                                c->root_fd,
840
                                BOOTLIBDIR,
841
                                CHASE_MUST_BE_DIRECTORY,
842
                                &source_path,
843
                                &d);
844
                if (r < 0 && (r != -ENOENT || c->install_source != INSTALL_SOURCE_AUTO))
42✔
845
                        return log_error_errno(r, "Failed to resolve path '%s' under directory '%s': %m", BOOTLIBDIR, c->root);
×
846

847
                /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
848
        }
849
        if (!d) {
42✔
850
                r = chase_and_opendir(
12✔
851
                                BOOTLIBDIR,
852
                                /* root= */ NULL,
853
                                CHASE_MUST_BE_DIRECTORY,
854
                                &source_path,
855
                                &d);
856
                if (r == -ENOENT && c->graceful) {
12✔
857
                        log_debug("Source directory '%s' does not exist, ignoring.", BOOTLIBDIR);
×
858
                        return 0;
859
                }
860
                if (r < 0)
12✔
861
                        return log_error_errno(r, "Failed to resolve path '%s': %m", BOOTLIBDIR);
×
862
        }
863

864
        const char *suffix = strjoina(arch, ".efi");
210✔
865
        const char *suffix_signed = strjoina(arch, ".efi.signed");
210✔
866

867
        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \"%s\": %m", source_path)) {
462✔
868
                int k;
336✔
869

870
                if (endswith_no_case(de->d_name, suffix)) {
336✔
871
                        /* skip the .efi file, if there's a .signed version of it */
872
                        _cleanup_free_ const char *s = strjoin(de->d_name, ".signed");
96✔
873
                        if (!s)
48✔
874
                                return log_oom();
×
875
                        if (faccessat(dirfd(d), s, F_OK, 0) >= 0)
48✔
876
                                continue;
48✔
877
                } else if (!endswith_no_case(de->d_name, suffix_signed))
288✔
878
                        continue;
240✔
879

880
                k = copy_one_file(c, de->d_name, c->operation == INSTALL_NEW);
48✔
881
                /* Don't propagate an error code if no update necessary, installed version already equal or
882
                 * newer version, or other boot loader in place. */
883
                if (c->graceful && IN_SET(k, -ESTALE, -ESRCH))
48✔
884
                        continue;
23✔
885
                RET_GATHER(r, k);
25✔
886
        }
887

888
        return r;
889
}
890

891
static int install_loader_config(InstallContext *c) {
16✔
892
        int r;
16✔
893

894
        assert(c);
16✔
895
        assert(c->make_entry_directory >= 0);
16✔
896

897
        if (c->esp_fd < 0)
16✔
898
                return c->esp_fd;
16✔
899

900
        _cleanup_free_ char *j = path_join(c->root, c->esp_path);
32✔
901
        if (!j)
16✔
902
                return log_oom();
×
903

904
        _cleanup_close_ int loader_dir_fd = -EBADF;
16✔
905
        r = chaseat(c->esp_fd,
16✔
906
                    c->esp_fd,
907
                    "loader",
908
                    CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
909
                    /* ret_path= */ NULL,
910
                    &loader_dir_fd);
911
        if (r < 0)
16✔
912
                return log_error_errno(r, "Failed to open '/loader/' directory below '%s': %m", j);
×
913

914
        if (faccessat(loader_dir_fd, "loader.conf", F_OK, AT_SYMLINK_NOFOLLOW) < 0) {
16✔
915
                if (errno != ENOENT)
16✔
916
                        return log_error_errno(errno, "Failed to check if '/loader/loader.conf' exists below '%s': %m", j);
×
917
        } else /* Silently skip creation if the file already exists (early check) */
918
                return 0;
919

920
        _cleanup_free_ char *t = NULL;
16✔
921
        _cleanup_fclose_ FILE *f = NULL;
16✔
922
        r = fopen_tmpfile_linkable_at(loader_dir_fd, "loader.conf", O_WRONLY|O_CLOEXEC, &t, &f);
16✔
923
        if (r < 0)
16✔
924
                return log_error_errno(r, "Failed to open '%s/loader/loader.conf' for writing: %m", j);
×
925

926
        CLEANUP_TMPFILE_AT(loader_dir_fd, t);
16✔
927

928
        fprintf(f, "#timeout 3\n"
16✔
929
                   "#console-mode keep\n");
930

931
        if (c->make_entry_directory) {
16✔
932
                assert(c->entry_token);
9✔
933
                fprintf(f, "default %s-*\n", c->entry_token);
9✔
934
        }
935

936
        r = flink_tmpfile_at(f, loader_dir_fd, t, "loader.conf", LINK_TMPFILE_SYNC);
16✔
937
        if (r == -EEXIST)
16✔
938
                return 0; /* Silently skip creation if the file exists now (recheck) */
939
        if (r < 0)
16✔
940
                return log_error_errno(r, "Failed to move '%s/loader/loader.conf' into place: %m", j);
×
941

942
        t = mfree(t); /* disarm CLEANUP_TMPFILE_AT() */
16✔
943
        return 1;
16✔
944
}
945

946
static int install_loader_specification(InstallContext *c) {
39✔
947
        int r;
39✔
948

949
        assert(c);
39✔
950

951
        int dollar_boot_fd = acquire_dollar_boot_fd(c);
39✔
952
        if (dollar_boot_fd < 0)
39✔
953
                return dollar_boot_fd;
39✔
954

955
        _cleanup_free_ char *j = path_join(c->root, dollar_boot_path(c));
78✔
956
        if (!j)
39✔
957
                return log_oom();
×
958

959
        _cleanup_close_ int loader_dir_fd = -EBADF;
39✔
960
        r = chaseat(dollar_boot_fd,
39✔
961
                    dollar_boot_fd,
962
                    "loader",
963
                    CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
964
                    /* ret_path= */ NULL,
965
                    &loader_dir_fd);
966
        if (r < 0)
39✔
967
                return log_error_errno(r, "Failed to pin '/loader' directory below '%s': %m", j);
×
968

969
        if (faccessat(loader_dir_fd, "entries.srel", F_OK, AT_SYMLINK_NOFOLLOW) < 0) {
39✔
970
                if (errno != ENOENT)
16✔
971
                        return log_error_errno(errno, "Failed to check if '/loader/entries.srel' exists below '%s': %m", j);
×
972
        } else /* Silently skip creation if the file already exists (early check) */
973
                return 0;
974

975
        _cleanup_free_ char *t = NULL;
16✔
976
        _cleanup_fclose_ FILE *f = NULL;
16✔
977
        r = fopen_tmpfile_linkable_at(loader_dir_fd, "entries.srel", O_WRONLY|O_CLOEXEC, &t, &f);
16✔
978
        if (r < 0)
16✔
979
                return log_error_errno(r, "Failed to open '%s/loader/entries.srel' for writing: %m", j);
×
980

981
        CLEANUP_TMPFILE_AT(loader_dir_fd, t);
16✔
982

983
        fprintf(f, "type1\n");
16✔
984

985
        r = flink_tmpfile_at(f, loader_dir_fd, t, "entries.srel", LINK_TMPFILE_SYNC);
16✔
986
        if (r == -EEXIST)
16✔
987
                return 0; /* Silently skip creation if the file exists now (recheck) */
988
        if (r < 0)
16✔
989
                return log_error_errno(r, "Failed to move '%s/loader/entries.srel' into place: %m", j);
×
990

991
        t = mfree(t); /* disarm CLEANUP_TMPFILE_AT() */
16✔
992
        return 1;
16✔
993
}
994

995
static int install_entry_directory(InstallContext *c) {
16✔
996
        assert(c);
16✔
997
        assert(c->make_entry_directory >= 0);
16✔
998

999
        if (!c->make_entry_directory)
16✔
1000
                return 0;
16✔
1001

1002
        assert(c->entry_token);
9✔
1003

1004
        int dollar_boot_fd = acquire_dollar_boot_fd(c);
9✔
1005
        if (dollar_boot_fd < 0)
9✔
1006
                return dollar_boot_fd;
1007

1008
        _cleanup_free_ char *j = path_join(c->root, dollar_boot_path(c));
18✔
1009
        if (!j)
9✔
1010
                return log_oom();
×
1011

1012
        return mkdir_one(j, dollar_boot_fd, c->entry_token);
9✔
1013
}
1014

1015
static int install_entry_token(InstallContext *c) {
16✔
1016
        int r;
16✔
1017

1018
        assert(c);
16✔
1019
        assert(c->make_entry_directory >= 0);
16✔
1020
        assert(c->entry_token);
16✔
1021

1022
        /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
1023
         * directory, or if anything else but the machine ID */
1024

1025
        if (!c->make_entry_directory && c->entry_token_type == BOOT_ENTRY_TOKEN_MACHINE_ID)
16✔
1026
                return 0;
16✔
1027

1028
        const char *confdir = secure_getenv("KERNEL_INSTALL_CONF_ROOT") ?: "/etc/kernel/";
16✔
1029

1030
        _cleanup_free_ char *j = path_join(c->root, confdir);
32✔
1031
        if (!j)
16✔
1032
                return log_oom();
×
1033

1034
        _cleanup_close_ int dfd = -EBADF;
16✔
1035
        r = chaseat(c->root_fd,
16✔
1036
                    c->root_fd,
1037
                    confdir,
1038
                    CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
1039
                    /* ret_path= */ NULL,
1040
                    &dfd);
1041
        if (r < 0)
16✔
1042
                return log_error_errno(r, "Failed to open '%s': %m", j);
×
1043

1044
        r = write_string_file_at(dfd, "entry-token", c->entry_token, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
16✔
1045
        if (r < 0)
16✔
1046
                return log_error_errno(r, "Failed to write entry token '%s' to '%s/entry-token': %m", c->entry_token, j);
×
1047

1048
        return 0;
1049
}
1050

1051
#if HAVE_OPENSSL
1052
static int efi_timestamp(EFI_TIME *ret) {
1✔
1053
        struct tm tm = {};
1✔
1054
        int r;
1✔
1055

1056
        assert(ret);
1✔
1057

1058
        r = localtime_or_gmtime_usec(source_date_epoch_or_now(), /* utc= */ true, &tm);
1✔
1059
        if (r < 0)
1✔
1060
                return log_error_errno(r, "Failed to convert timestamp to calendar time: %m");
×
1061

1062
        *ret = (EFI_TIME) {
1✔
1063
                .Year = 1900 + tm.tm_year,
1✔
1064
                /* tm_mon starts at 0, EFI_TIME months start at 1. */
1065
                .Month = tm.tm_mon + 1,
1✔
1066
                .Day = tm.tm_mday,
1✔
1067
                .Hour = tm.tm_hour,
1✔
1068
                .Minute = tm.tm_min,
1✔
1069
                .Second = tm.tm_sec,
1✔
1070
        };
1071

1072
        return 0;
1✔
1073
}
1074
#endif
1075

1076
static int install_secure_boot_auto_enroll(InstallContext *c) {
16✔
1077
#if HAVE_OPENSSL
1078
        int r;
16✔
1079
#endif
1080

1081
        if (!arg_secure_boot_auto_enroll)
16✔
1082
                return 0;
16✔
1083

1084
#if HAVE_OPENSSL
1085
        if (!c->secure_boot_certificate || !c->secure_boot_private_key)
1✔
1086
                return 0;
1087

1088
        r = DLOPEN_LIBCRYPTO(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED);
1✔
1089
        if (r < 0)
1✔
1090
                return r;
1091

1092
        _cleanup_free_ uint8_t *dercert = NULL;
1✔
1093
        int dercertsz;
1✔
1094
        dercertsz = sym_i2d_X509(c->secure_boot_certificate, &dercert);
1✔
1095
        if (dercertsz < 0)
1✔
1096
                return log_openssl_errors(LOG_ERR, "Failed to convert X.509 certificate to DER");
×
1097

1098
        if (c->esp_fd < 0)
1✔
1099
                return c->esp_fd;
1100

1101
        _cleanup_free_ char *j = path_join(c->root, c->esp_path);
2✔
1102
        if (!j)
1✔
1103
                return log_oom();
×
1104

1105
        _cleanup_close_ int keys_fd = -EBADF;
1✔
1106
        r = chaseat(c->esp_fd,
1✔
1107
                    c->esp_fd,
1108
                    "loader/keys/auto",
1109
                    CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY,
1110
                    /* ret_path= */ NULL,
1111
                    &keys_fd);
1112
        if (r < 0)
1✔
1113
                return log_error_errno(r, "Failed to chase /loader/keys/auto/ below '%s': %m", j);
×
1114

1115
        uint32_t siglistsz = offsetof(EFI_SIGNATURE_LIST, Signatures) + offsetof(EFI_SIGNATURE_DATA, SignatureData) + dercertsz;
1✔
1116
        /* We use malloc0() to zero-initialize the SignatureOwner field of Signatures[0]. */
1117
        _cleanup_free_ EFI_SIGNATURE_LIST *siglist = malloc0(siglistsz);
2✔
1118
        if (!siglist)
1✔
1119
                return log_oom();
×
1120

1121
        *siglist = (EFI_SIGNATURE_LIST) {
1✔
1122
                .SignatureType = EFI_CERT_X509_GUID,
1123
                .SignatureListSize = siglistsz,
1124
                .SignatureSize = offsetof(EFI_SIGNATURE_DATA, SignatureData) + dercertsz,
1✔
1125
        };
1126

1127
        memcpy(siglist->Signatures[0].SignatureData, dercert, dercertsz);
1✔
1128

1129
        EFI_TIME timestamp;
1✔
1130
        r = efi_timestamp(&timestamp);
1✔
1131
        if (r < 0)
1✔
1132
                return r;
1133

1134
        uint32_t attrs =
1✔
1135
                EFI_VARIABLE_NON_VOLATILE|
1136
                EFI_VARIABLE_BOOTSERVICE_ACCESS|
1137
                EFI_VARIABLE_RUNTIME_ACCESS|
1138
                EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
1139

1140
        FOREACH_STRING(db, "PK", "KEK", "db") {
4✔
1141
                _cleanup_(BIO_freep) BIO *bio = NULL;
×
1142

1143
                bio = sym_BIO_new(sym_BIO_s_mem());
3✔
1144
                if (!bio)
3✔
1145
                        return log_oom();
×
1146

1147
                _cleanup_free_ char16_t *db16 = utf8_to_utf16(db, SIZE_MAX);
6✔
1148
                if (!db16)
3✔
1149
                        return log_oom();
×
1150

1151
                /* Don't count the trailing NUL terminator. */
1152
                if (sym_BIO_write(bio, db16, char16_strsize(db16) - sizeof(char16_t)) < 0)
3✔
1153
                        return log_openssl_errors(LOG_ERR, "Failed to write variable name to bio");
×
1154

1155
                EFI_GUID *guid = STR_IN_SET(db, "PK", "KEK") ? &(EFI_GUID) EFI_GLOBAL_VARIABLE : &(EFI_GUID) EFI_IMAGE_SECURITY_DATABASE_GUID;
3✔
1156

1157
                if (sym_BIO_write(bio, guid, sizeof(*guid)) < 0)
3✔
1158
                        return log_openssl_errors(LOG_ERR, "Failed to write variable GUID to bio");
×
1159

1160
                if (sym_BIO_write(bio, &attrs, sizeof(attrs)) < 0)
3✔
1161
                        return log_openssl_errors(LOG_ERR, "Failed to write variable attributes to bio");
×
1162

1163
                if (sym_BIO_write(bio, &timestamp, sizeof(timestamp)) < 0)
3✔
1164
                        return log_openssl_errors(LOG_ERR, "Failed to write timestamp to bio");
×
1165

1166
                if (sym_BIO_write(bio, siglist, siglistsz) < 0)
3✔
1167
                        return log_openssl_errors(LOG_ERR, "Failed to write signature list to bio");
×
1168

1169
                _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
×
1170
                p7 = sym_PKCS7_sign(c->secure_boot_certificate, c->secure_boot_private_key, /* certs= */ NULL, bio, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY|PKCS7_NOSMIMECAP);
3✔
1171
                if (!p7)
3✔
1172
                        return log_openssl_errors(LOG_ERR, "Failed to calculate PKCS7 signature");
×
1173

1174
                _cleanup_free_ uint8_t *sig = NULL;
×
1175
                int sigsz = sym_i2d_PKCS7(p7, &sig);
3✔
1176
                if (sigsz < 0)
3✔
1177
                        return log_openssl_errors(LOG_ERR, "Failed to convert PKCS7 signature to DER");
×
1178

1179
                size_t authsz = offsetof(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo.CertData) + sigsz;
3✔
1180
                _cleanup_free_ EFI_VARIABLE_AUTHENTICATION_2 *auth = malloc(authsz);
×
1181
                if (!auth)
3✔
1182
                        return log_oom();
×
1183

1184
                *auth = (EFI_VARIABLE_AUTHENTICATION_2) {
3✔
1185
                        .TimeStamp = timestamp,
1186
                        .AuthInfo = {
1187
                                .Hdr = {
1188
                                        .dwLength = offsetof(WIN_CERTIFICATE_UEFI_GUID, CertData) + sigsz,
3✔
1189
                                        .wRevision = 0x0200,
1190
                                        .wCertificateType = 0x0EF1, /* WIN_CERT_TYPE_EFI_GUID */
1191
                                },
1192
                                .CertType = EFI_CERT_TYPE_PKCS7_GUID,
1193
                        }
1194
                };
1195

1196
                memcpy(auth->AuthInfo.CertData, sig, sigsz);
3✔
1197

1198
                _cleanup_free_ char *filename = strjoin(db, ".auth");
3✔
1199
                if (!filename)
3✔
1200
                        return log_oom();
×
1201

1202
                _cleanup_free_ char *t = NULL;
3✔
1203
                _cleanup_close_ int fd = open_tmpfile_linkable_at(keys_fd, filename, O_WRONLY|O_CLOEXEC, &t);
6✔
1204
                if (fd < 0)
3✔
1205
                        return log_error_errno(fd, "Failed to open secure boot auto-enrollment file for writing: %m");
×
1206

1207
                CLEANUP_TMPFILE_AT(keys_fd, t);
×
1208

1209
                r = loop_write(fd, auth, authsz);
3✔
1210
                if (r < 0)
3✔
1211
                        return log_error_errno(r, "Failed to write authentication descriptor to secure boot auto-enrollment file: %m");
×
1212

1213
                r = loop_write(fd, siglist, siglistsz);
3✔
1214
                if (r < 0)
3✔
1215
                        return log_error_errno(r, "Failed to write signature list to secure boot auto-enrollment file: %m");
×
1216

1217
                r = link_tmpfile_at(fd, keys_fd, t, filename, LINK_TMPFILE_SYNC);
3✔
1218
                if (r < 0)
3✔
1219
                        return log_error_errno(errno, "Failed to link secure boot auto-enrollment file: %m");
×
1220

1221
                t = mfree(t); /* Disarm CLEANUP_TMPFILE_AT() */
3✔
1222

1223
                log_info("Secure boot auto-enrollment file '%s/loader/keys/auto/%s' successfully written.", j, filename);
3✔
1224
        }
1225

1226
        return 0;
1✔
1227
#else
1228
        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Built without OpenSSL support, cannot set up auto-enrollment.");
1229
#endif
1230
}
1231

1232
static bool same_entry(uint16_t id, sd_id128_t uuid, const char *path) {
81✔
1233
        _cleanup_free_ char *opath = NULL;
81✔
1234
        sd_id128_t ouuid;
81✔
1235
        int r;
81✔
1236

1237
        r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
81✔
1238
        if (r < 0)
81✔
1239
                return false;
1240
        if (!sd_id128_equal(uuid, ouuid))
81✔
1241
                return false;
60✔
1242

1243
        /* Some motherboards convert the path to uppercase under certain circumstances
1244
         * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
1245
         * so use case-insensitive checking */
1246
        if (!strcaseeq_ptr(path, opath))
21✔
1247
                return false;
7✔
1248

1249
        return true;
1250
}
1251

1252
static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
20✔
1253
        _cleanup_free_ uint16_t *options = NULL;
20✔
1254

1255
        assert(id);
20✔
1256

1257
        int n = efi_get_boot_options(&options);
20✔
1258
        if (n < 0)
20✔
1259
                return n;
1260

1261
        /* find already existing systemd-boot entry */
1262
        for (int i = 0; i < n; i++)
87✔
1263
                if (same_entry(options[i], uuid, path)) {
81✔
1264
                        *id = options[i];
14✔
1265
                        return 1;
14✔
1266
                }
1267

1268
        /* find free slot in the sorted BootXXXX variable list */
1269
        for (int i = 0; i < n; i++)
27✔
1270
                if (i != options[i]) {
21✔
1271
                        *id = i;
×
1272
                        return 0;
×
1273
                }
1274

1275
        /* use the next one */
1276
        if (n == 0xffff)
6✔
1277
                return -ENOSPC;
1278
        *id = n;
6✔
1279
        return 0;
6✔
1280
}
1281

1282
static int insert_into_order(InstallContext *c, uint16_t slot, uint16_t after_slot) {
14✔
1283
        _cleanup_free_ uint16_t *order = NULL;
14✔
1284
        uint16_t *t;
14✔
1285
        int n;
14✔
1286

1287
        assert(c);
14✔
1288

1289
        n = efi_get_boot_order(&order);
14✔
1290
        if (n <= 0)
14✔
1291
                /* no entry, add us */
1292
                return efi_set_boot_order(&slot, 1);
×
1293

1294
        /* are we the first and only one? */
1295
        if (n == 1 && order[0] == slot)
14✔
1296
                return 0;
1297

1298
        /* are we already in the boot order? */
1299
        for (int i = 0; i < n; i++) {
57✔
1300
                if (order[i] != slot)
51✔
1301
                        continue;
43✔
1302

1303
                /* we do not require to be the first one, all is fine */
1304
                /* if after_slot is set, leave existing position alone to preserve user reordering. */
1305
                if (i == 0 || c->operation != INSTALL_NEW || after_slot != UINT16_MAX)
8✔
1306
                        return 0;
1307

1308
                /* move us to the first slot */
1309
                memmove(order + 1, order, i * sizeof(uint16_t));
1✔
1310
                order[0] = slot;
1✔
1311
                return efi_set_boot_order(order, n);
1✔
1312
        }
1313

1314
        /* slot is not yet in the order, so insert after a specific slot if requested */
1315
        if (after_slot != UINT16_MAX) {
6✔
1316
                t = reallocarray(order, n + 1, sizeof(uint16_t));
3✔
1317
                if (!t)
3✔
1318
                        return -ENOMEM;
1319
                order = t;
3✔
1320

1321
                for (int i = 0; i < n; i++) {
12✔
1322
                        if (order[i] != after_slot)
12✔
1323
                                continue;
9✔
1324

1325
                        memmove(order + i + 2, order + i + 1, (n - i - 1) * sizeof(uint16_t));
3✔
1326
                        order[i + 1] = slot;
3✔
1327
                        return efi_set_boot_order(order, n + 1);
3✔
1328
                }
1329

1330
                log_warning("Boot entry %04" PRIx16 " not found in BootOrder, appending new entry at the end.", after_slot);
×
1331
                order[n] = slot;
×
1332
                return efi_set_boot_order(order, n + 1);
×
1333
        }
1334

1335
        /* extend array */
1336
        t = reallocarray(order, n + 1, sizeof(uint16_t));
3✔
1337
        if (!t)
3✔
1338
                return -ENOMEM;
1339
        order = t;
3✔
1340

1341
        /* add us to the top or end of the list */
1342
        if (c->operation != INSTALL_NEW) {
3✔
1343
                memmove(order + 1, order, n * sizeof(uint16_t));
×
1344
                order[0] = slot;
×
1345
        } else
1346
                order[n] = slot;
3✔
1347

1348
        return efi_set_boot_order(order, n + 1);
3✔
1349
}
1350

1351
static int remove_from_order(uint16_t slot) {
6✔
1352
        _cleanup_free_ uint16_t *order = NULL;
6✔
1353
        int n;
6✔
1354

1355
        n = efi_get_boot_order(&order);
6✔
1356
        if (n <= 0)
6✔
1357
                return n;
1358

1359
        for (int i = 0; i < n; i++) {
21✔
1360
                if (order[i] != slot)
21✔
1361
                        continue;
15✔
1362

1363
                if (i + 1 < n)
6✔
1364
                        memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t));
3✔
1365
                return efi_set_boot_order(order, n - 1);
6✔
1366
        }
1367

1368
        return 0;
1369
}
1370

1371
static int pick_efi_boot_option_description(int esp_fd, char **ret) {
7✔
1372
        int r;
7✔
1373

1374
        assert(esp_fd >= 0);
7✔
1375
        assert(ret);
7✔
1376

1377
        /* early declarations, so that they are definitely initialized even if we follow any of the gotos */
1378
        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
7✔
1379
        _cleanup_free_ char *j = NULL;
7✔
1380

1381
        const char *b = arg_efi_boot_option_description ?: "Linux Boot Manager";
7✔
1382
        if (!arg_efi_boot_option_description_with_device)
7✔
1383
                goto fallback;
7✔
1384

1385
        r = block_device_new_from_fd(
×
1386
                        esp_fd,
1387
                        BLOCK_DEVICE_LOOKUP_WHOLE_DISK|BLOCK_DEVICE_LOOKUP_BACKING,
1388
                        &d);
1389
        if (r < 0) {
×
1390
                log_debug_errno(r, "Failed to find backing device of ESP: %m");
×
1391
                goto fallback;
×
1392
        }
1393

1394
        const char *serial;
×
1395
        r = sd_device_get_property_value(d, "ID_SERIAL", &serial);
×
1396
        if (r < 0) {
×
1397
                log_debug_errno(r, "Unable to read ID_SERIAL field of backing device of ESP: %m");
×
1398
                goto fallback;
×
1399
        }
1400

1401
        j = strjoin(b, " (", serial, ")");
×
1402
        if (!j)
×
1403
                return log_oom();
×
1404

1405
        if (strlen(j) > EFI_BOOT_OPTION_DESCRIPTION_MAX) {
×
1406
                log_debug("Boot option string suffixed with device serial would be too long, skipping: %s", j);
×
1407
                j = mfree(j);
×
1408
                goto fallback;
×
1409
        }
1410

1411
        *ret = TAKE_PTR(j);
×
1412
        return 0;
×
1413

1414
fallback:
7✔
1415
        j = strdup(b);
7✔
1416
        if (!j)
7✔
1417
                return log_oom();
×
1418

1419
        *ret = TAKE_PTR(j);
7✔
1420
        return 0;
7✔
1421
}
1422

1423
static int install_boot_option(
14✔
1424
                InstallContext *c,
1425
                const char *path,
1426
                const char *description,
1427
                bool require_existing,
1428
                uint16_t after_slot,
1429
                uint16_t *ret_slot) {
1430

1431
        uint16_t slot;
14✔
1432
        int r;
14✔
1433

1434
        assert(c);
14✔
1435
        assert(path);
14✔
1436
        assert(description);
14✔
1437

1438
        if (c->esp_fd < 0)
14✔
1439
                return c->esp_fd;
14✔
1440

1441
        _cleanup_free_ char *j = path_join(c->root, c->esp_path);
28✔
1442
        if (!j)
14✔
1443
                return log_oom();
×
1444

1445
        r = chase_and_accessat(
14✔
1446
                        c->esp_fd,
1447
                        c->esp_fd,
1448
                        path,
1449
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_REGULAR,
1450
                        F_OK,
1451
                        /* ret_path= */ NULL);
1452
        if (r == -ENOENT && require_existing)
14✔
1453
                return 0;
1454
        if (r < 0 && r != -ENOENT)
14✔
1455
                return log_error_errno(r, "Cannot access \"%s/%s\": %m", j, skip_leading_slash(path));
×
1456

1457
        r = find_slot(c->esp_uuid, path, &slot);
14✔
1458
        if (r < 0) {
14✔
1459
                int level = c->graceful ? arg_quiet ? LOG_DEBUG : LOG_INFO : LOG_ERR;
×
1460
                const char *skip = c->graceful ? ", skipping" : "";
×
1461

1462
                log_full_errno(level, r,
×
1463
                               r == -ENOENT ?
1464
                               "Failed to access EFI variables%s. Is the \"efivarfs\" filesystem mounted?" :
1465
                               "Failed to determine current boot order%s: %m", skip);
1466

1467
                return c->graceful ? 0 : r;
×
1468
        }
1469

1470
        bool existing = r > 0;
14✔
1471

1472
        if (c->operation == INSTALL_NEW || !existing) {
14✔
1473
                r = efi_add_boot_option(
12✔
1474
                                slot,
1475
                                description,
1476
                                c->esp_part,
1477
                                c->esp_pstart,
1478
                                c->esp_psize,
1479
                                c->esp_uuid,
1480
                                path);
1481
                if (r < 0) {
12✔
1482
                        int level = c->graceful ? arg_quiet ? LOG_DEBUG : LOG_INFO : LOG_ERR;
×
1483
                        const char *skip = c->graceful ? ", skipping" : "";
×
1484

1485
                        log_full_errno(level, r, "Failed to create EFI Boot variable entry%s: %m", skip);
×
1486

1487
                        return c->graceful ? 0 : r;
×
1488
                }
1489

1490
                log_info("%s EFI boot entry \"%s\".",
18✔
1491
                         existing ? "Updated" : "Created",
1492
                         description);
1493
        }
1494

1495
        r = insert_into_order(c, slot, after_slot);
14✔
1496
        if (r < 0)
14✔
1497
                return r;
1498

1499
        if (ret_slot)
14✔
1500
                *ret_slot = slot;
7✔
1501

1502
        return 0;
1503
}
1504

1505
static int are_we_installed(InstallContext *c) {
40✔
1506
        int r;
40✔
1507

1508
        assert(c);
40✔
1509

1510
        /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
1511
         * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
1512
         * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
1513
         * should be a suitable and very minimal check for a number of reasons:
1514
         *
1515
         *  → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
1516
         *    not a specific one.)
1517
         *
1518
         *  → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
1519
         *    /EFI/BOOT/BOOT*.EFI fallback binary.
1520
         *
1521
         *  → It specifically checks for systemd-boot, not for other boot loaders (which a check for
1522
         *    /boot/loader/entries would do). */
1523

1524
        _cleanup_free_ char *p = path_join(c->esp_path, "/EFI/systemd");
80✔
1525
        if (!p)
40✔
1526
                return log_oom();
×
1527

1528
        if (c->esp_fd < 0)
40✔
1529
                return c->esp_fd;
1530

1531
        _cleanup_close_ int fd = chase_and_openat(
80✔
1532
                        c->esp_fd,
1533
                        c->esp_fd,
1534
                        "/EFI/systemd",
1535
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_DIRECTORY,
1536
                        O_RDONLY|O_CLOEXEC|O_DIRECTORY,
1537
                        /* ret_path= */ NULL);
1538
        if (fd == -ENOENT)
40✔
1539
                return 0;
1540
        if (fd < 0)
33✔
1541
                return log_error_errno(fd, "Failed to open '%s': %m", p);
×
1542

1543
        log_debug("Checking whether '%s' contains any files%s", p, glyph(GLYPH_ELLIPSIS));
53✔
1544
        r = dir_is_empty_at(fd, /* path= */ NULL, /* ignore_hidden_or_backup= */ false);
33✔
1545
        if (r < 0 && r != -ENOENT)
33✔
1546
                return log_error_errno(r, "Failed to check whether '%s' contains any files: %m", p);
×
1547

1548
        return r == 0;
33✔
1549
}
1550

1551
#if HAVE_OPENSSL
1552
static int load_secure_boot_auto_enroll(
42✔
1553
                X509 **ret_certificate,
1554
                EVP_PKEY **ret_private_key,
1555
                OpenSSLAskPasswordUI **ret_ui) {
1556

1557
        int r;
42✔
1558

1559
        assert(ret_certificate);
42✔
1560
        assert(ret_private_key);
42✔
1561
        assert(ret_ui);
42✔
1562

1563
        if (!arg_secure_boot_auto_enroll) {
42✔
1564
                *ret_certificate = NULL;
41✔
1565
                *ret_private_key = NULL;
41✔
1566
                return 0;
42✔
1567
        }
1568

1569
        if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
1✔
1570
                r = parse_path_argument(arg_certificate, /* suppress_root= */ false, &arg_certificate);
1✔
1571
                if (r < 0)
1✔
1572
                        return r;
1573
        }
1574

1575
        _cleanup_(X509_freep) X509 *certificate = NULL;
1✔
1576
        r = openssl_load_x509_certificate(
1✔
1577
                        arg_certificate_source_type,
1578
                        arg_certificate_source,
1579
                        arg_certificate,
1580
                        &certificate);
1581
        if (r < 0)
1✔
1582
                return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
×
1583

1584
        if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
1✔
1585
                r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
1✔
1586
                if (r < 0)
1✔
1587
                        return r;
1588
        }
1589

1590
        r = openssl_load_private_key(
2✔
1591
                        arg_private_key_source_type,
1592
                        arg_private_key_source,
1593
                        arg_private_key,
1594
                        &(AskPasswordRequest) {
1✔
1595
                                .tty_fd = -EBADF,
1596
                                .id = "bootctl-private-key-pin",
1597
                                .keyring = arg_private_key,
1598
                                .credential = "bootctl.private-key-pin",
1599
                                .until = USEC_INFINITY,
1600
                                .hup_fd = -EBADF,
1601
                        },
1602
                        ret_private_key,
1603
                        ret_ui);
1604
        if (r < 0)
1✔
1605
                return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
×
1606

1607
        *ret_certificate = TAKE_PTR(certificate);
1✔
1608

1609
        return 0;
1✔
1610
}
1611
#endif
1612

1613
static int install_variables(InstallContext *c, const char *arch) {
7✔
1614
        int r;
7✔
1615

1616
        assert(c);
7✔
1617

1618
        const char *path = strjoina("/EFI/systemd/systemd-boot", arch, ".efi");
49✔
1619

1620
        _cleanup_free_ char *description = NULL;
7✔
1621
        r = pick_efi_boot_option_description(c->esp_fd, &description);
7✔
1622
        if (r < 0)
7✔
1623
                return r;
1624

1625
        uint16_t primary_slot = UINT16_MAX;
7✔
1626
        r = install_boot_option(c, path, description, /* require_existing= */ true, /* after_slot= */ UINT16_MAX, &primary_slot);
7✔
1627
        if (r < 0)
7✔
1628
                return r;
1629
        /* If primary registration was skipped (e.g. binary not on ESP), skip the fallback too
1630
         * or else it would land at position 0 in BootOrder with no primary ahead of it. */
1631
        if (primary_slot == UINT16_MAX)
7✔
1632
                return 0;
1633

1634
        const char *fallback_path = strjoina("/EFI/systemd/systemd-boot-fallback", arch, ".efi");
49✔
1635

1636
        _cleanup_free_ char *fallback_description = strjoin("Fallback ", description);
14✔
1637
        if (!fallback_description)
7✔
1638
                return log_oom();
×
1639

1640
        strshorten(fallback_description, EFI_BOOT_OPTION_DESCRIPTION_MAX);
7✔
1641

1642
        return install_boot_option(c, fallback_path, fallback_description, /* require_existing= */ false, /* after_slot= */ primary_slot, /* ret_slot= */ NULL);
7✔
1643
}
1644

1645
static int run_install(InstallContext *c) {
42✔
1646
        int r;
42✔
1647

1648
        assert(c);
42✔
1649
        assert(c->operation >= 0);
42✔
1650

1651
        if (c->operation == INSTALL_UPDATE) {
42✔
1652
                /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
1653
                r = are_we_installed(c);
26✔
1654
                if (r < 0)
26✔
1655
                        return r;
42✔
1656
                if (r == 0) {
26✔
1657
                        log_debug("Skipping update because sd-boot is not installed in the ESP.");
×
1658
                        return 0;
1659
                }
1660
        }
1661

1662
        r = settle_make_entry_directory(c);
42✔
1663
        if (r < 0)
42✔
1664
                return r;
1665

1666
        const char *arch = arg_arch_all ? "" : get_efi_arch();
42✔
1667

1668
        if (c->esp_fd < 0)
42✔
1669
                return c->esp_fd;
1670

1671
        _cleanup_free_ char *j = path_join(c->root, c->esp_path);
84✔
1672
        if (!j)
42✔
1673
                return log_oom();
×
1674

1675
        int dollar_boot_fd = acquire_dollar_boot_fd(c);
42✔
1676
        if (dollar_boot_fd < 0)
42✔
1677
                return dollar_boot_fd;
1678

1679
        _cleanup_free_ char *w = path_join(c->root, dollar_boot_path(c));
84✔
1680
        if (!w)
42✔
1681
                return log_oom();
×
1682

1683
        WITH_UMASK(0002) {
84✔
1684
                if (c->operation == INSTALL_NEW) {
42✔
1685
                        /* Don't create any of these directories when we are just updating. When we update
1686
                         * we'll drop-in our files (unless there are newer ones already), but we won't create
1687
                         * the directories for them in the first place. */
1688

1689
                        r = create_subdirs(j, c->esp_fd, esp_subdirs);
16✔
1690
                        if (r < 0)
16✔
1691
                                return r;
1692

1693
                        r = create_subdirs(w, dollar_boot_fd, dollar_boot_subdirs);
16✔
1694
                        if (r < 0)
16✔
1695
                                return r;
1696
                }
1697

1698
                r = install_binaries(c, arch);
42✔
1699
                if (r < 0)
42✔
1700
                        return r;
1701

1702
                if (c->operation == INSTALL_NEW) {
39✔
1703
                        r = install_loader_config(c);
16✔
1704
                        if (r < 0)
16✔
1705
                                return r;
1706

1707
                        r = install_entry_directory(c);
16✔
1708
                        if (r < 0)
16✔
1709
                                return r;
1710

1711
                        r = install_entry_token(c);
16✔
1712
                        if (r < 0)
16✔
1713
                                return r;
1714

1715
                        if (arg_install_random_seed && !c->root) {
16✔
1716
                                r = install_random_seed(c->esp_path, c->esp_fd);
8✔
1717
                                if (r < 0)
8✔
1718
                                        return r;
1719
                        }
1720

1721
                        r = install_secure_boot_auto_enroll(c);
16✔
1722
                        if (r < 0)
16✔
1723
                                return r;
1724
                }
1725

1726
                r = install_loader_specification(c);
39✔
1727
                if (r < 0)
39✔
1728
                        return r;
1729
        }
1730

1731
        (void) sync_everything();
39✔
1732

1733
        if (!should_touch_install_variables(c))
39✔
1734
                return 0;
1735

1736
        if (arg_arch_all) {
9✔
1737
                log_info("Not changing EFI variables with --all-architectures.");
2✔
1738
                return 0;
1739
        }
1740

1741
        return install_variables(c, arch);
7✔
1742
}
1743

1744
int verb_install(int argc, char *argv[], uintptr_t _data, void *userdata) {
42✔
1745
        int r;
42✔
1746

1747
        /* Invoked for both "update" and "install" */
1748

1749
        _cleanup_(install_context_done) InstallContext c = INSTALL_CONTEXT_NULL;
×
1750
        r = install_context_from_cmdline(&c, streq(argv[0], "install") ? INSTALL_NEW : INSTALL_UPDATE);
42✔
1751
        if (r < 0)
42✔
1752
                return r;
1753
        if (r == 0) {
42✔
1754
                log_debug("No ESP found and operating in graceful mode, skipping.");
×
1755
                return 0;
1756
        }
1757

1758
#if HAVE_OPENSSL
1759
        _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
42✔
1760
        r = load_secure_boot_auto_enroll(&c.secure_boot_certificate, &c.secure_boot_private_key, &ui);
42✔
1761
        if (r < 0)
42✔
1762
                return r;
1763
#endif
1764

1765
        return run_install(&c);
42✔
1766
}
1767

1768
static int remove_boot_efi(InstallContext *c) {
13✔
1769
        int r, n = 0;
13✔
1770

1771
        assert(c);
13✔
1772

1773
        if (c->esp_fd < 0)
13✔
1774
                return c->esp_fd;
13✔
1775

1776
        _cleanup_free_ char *w = path_join(c->root, c->esp_path);
26✔
1777
        if (!w)
13✔
1778
                return log_oom();
×
1779

1780
        _cleanup_closedir_ DIR *d = NULL;
13✔
1781
        _cleanup_free_ char *p = NULL;
13✔
1782
        r = chase_and_opendirat(
13✔
1783
                        c->esp_fd,
1784
                        c->esp_fd,
1785
                        "/EFI/BOOT",
1786
                        CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_DIRECTORY,
1787
                        &p,
1788
                        &d);
1789
        if (r == -ENOENT)
13✔
1790
                return 0;
1791
        if (r < 0)
13✔
1792
                return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", w);
×
1793

1794
        _cleanup_free_ char *j = path_join(w, p);
26✔
1795
        if (!j)
13✔
1796
                return log_oom();
×
1797

1798
        FOREACH_DIRENT(de, d, break) {
61✔
1799
                _cleanup_close_ int fd = -EBADF;
22✔
1800

1801
                if (!endswith_no_case(de->d_name, ".efi"))
22✔
1802
                        continue;
×
1803

1804
                _cleanup_free_ char *z = path_join(j, de->d_name);
31✔
1805
                if (!z)
22✔
1806
                        return log_oom();
×
1807

1808
                fd = xopenat_full(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_NOFOLLOW, XO_REGULAR, /* mode= */ MODE_INVALID);
22✔
1809
                if (fd == -ENOENT)
22✔
1810
                        continue;
×
1811
                if (fd < 0)
22✔
1812
                        return log_error_errno(fd, "Failed to open '%s' for reading: %m", z);
×
1813

1814
                r = pe_is_native_fd(fd);
22✔
1815
                if (r < 0) {
22✔
1816
                        log_warning_errno(r, "Failed to detect if '%s' is native architecture, ignoring: %m", z);
×
1817
                        continue;
×
1818
                }
1819
                if (r == 0)
22✔
1820
                        continue;
9✔
1821

1822
                _cleanup_free_ char *v = NULL;
13✔
1823
                r = get_file_version(fd, &v);
13✔
1824
                if (r == -ESRCH)
13✔
1825
                        continue;  /* No version information */
×
1826
                if (r < 0) {
13✔
1827
                        log_warning_errno(r, "Failed to get file version of '%s', skipping: %m", de->d_name);
×
1828
                        continue;
×
1829
                }
1830
                if (!startswith(v, "systemd-boot "))
13✔
1831
                        continue;
×
1832

1833
                if (unlinkat(dirfd(d), de->d_name, 0) < 0)
13✔
1834
                        return log_error_errno(errno, "Failed to remove '%s': %m", z);
×
1835

1836
                log_info("Removed '%s'.", z);
13✔
1837

1838
                n++;
13✔
1839
        }
1840

1841
        log_debug("Removed %i EFI binaries from '%s'.", n, j);
13✔
1842
        return n;
1843
}
1844

1845
static int unlink_inode(const char *root, int root_fd, const char *path, mode_t type) {
271✔
1846
        int r;
271✔
1847

1848
        assert(root);
271✔
1849
        assert(root_fd >= 0);
271✔
1850
        assert(path);
271✔
1851
        assert(IN_SET(type, S_IFREG, S_IFDIR));
271✔
1852

1853
        _cleanup_free_ char *p = path_join(empty_to_root(root), path);
542✔
1854
        if (!p)
271✔
1855
                return log_oom();
×
1856

1857
        r = chase_and_unlinkat(
271✔
1858
                        root_fd,
1859
                        root_fd,
1860
                        path,
1861
                        CHASE_PROHIBIT_SYMLINKS,
1862
                        S_ISDIR(type) ? AT_REMOVEDIR : 0,
271✔
1863
                        /* ret_path= */ NULL);
1864
        if (r < 0) {
271✔
1865
                bool ignore = IN_SET(r, -ENOENT, -ENOTEMPTY);
152✔
1866
                log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r, "Failed to remove '%s': %m", p);
152✔
1867
                return ignore ? 0 : r;
152✔
1868
        }
1869

1870
        log_info("Removed %s\"%s\".", S_ISDIR(type) ? "directory " : "", p);
155✔
1871
        return 0;
1872
}
1873

1874
static int remove_subdirs(const char *root, int root_fd, const char *const *subdirs) {
34✔
1875
        int r = 0;
34✔
1876

1877
        assert(root);
34✔
1878
        assert(root_fd);
34✔
1879

1880
        STRV_FOREACH_BACKWARDS(i, (char**) subdirs)
183✔
1881
                RET_GATHER(r, unlink_inode(root, root_fd, *i, S_IFDIR));
149✔
1882

1883
        return r;
34✔
1884
}
1885

1886
static int remove_entry_directory(InstallContext *c, const char *path, int fd) {
21✔
1887
        assert(c);
21✔
1888
        assert(c->make_entry_directory >= 0);
21✔
1889
        assert(path);
21✔
1890
        assert(fd >= 0);
21✔
1891

1892
        if (!c->make_entry_directory || !c->entry_token)
21✔
1893
                return 0;
1894

1895
        return unlink_inode(path, fd, c->entry_token, S_IFDIR);
10✔
1896
}
1897

1898
static int remove_binaries(InstallContext *c) {
13✔
1899
        int r;
13✔
1900

1901
        _cleanup_free_ char *p = path_join(c->root, "/EFI/systemd");
26✔
1902
        if (!p)
13✔
1903
                return log_oom();
×
1904

1905
        _cleanup_close_ int efi_fd = -EBADF;
13✔
1906
        r = chaseat(c->esp_fd,
13✔
1907
                    c->esp_fd,
1908
                    "EFI",
1909
                    CHASE_PROHIBIT_SYMLINKS|CHASE_MUST_BE_DIRECTORY,
1910
                    /* ret_path= */ NULL,
1911
                    &efi_fd);
1912
        if (r < 0) {
13✔
1913
                if (r != -ENOENT)
×
1914
                        return log_error_errno(r, "Failed to remove '%s': %m", p);
×
1915

1916
                r = 0;
1917
        } else
1918
                r = rm_rf_at(efi_fd, "systemd", REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK);
13✔
1919

1920
        return RET_GATHER(r, remove_boot_efi(c));
13✔
1921
}
1922

1923
static int remove_boot_option(sd_id128_t uuid, const char *path, bool in_order) {
6✔
1924
        uint16_t slot;
6✔
1925
        int r;
6✔
1926

1927
        r = find_slot(uuid, path, &slot);
6✔
1928
        if (r != 1)
6✔
1929
                return 0;
6✔
1930

1931
        r = efi_remove_boot_option(slot);
6✔
1932
        if (r < 0)
6✔
1933
                return r;
1934

1935
        if (in_order)
6✔
1936
                return remove_from_order(slot);
6✔
1937

1938
        return 0;
1939
}
1940

1941
static int remove_loader_variables(void) {
3✔
1942
        int r = 0;
3✔
1943

1944
        /* Remove all persistent loader variables we define */
1945

1946
        FOREACH_STRING(var,
30✔
1947
                       EFI_LOADER_VARIABLE_STR("LoaderConfigConsoleMode"),
1948
                       EFI_LOADER_VARIABLE_STR("LoaderConfigTimeout"),
1949
                       EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"),
1950
                       EFI_LOADER_VARIABLE_STR("LoaderEntryPreferred"),
1951
                       EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"),
1952
                       EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"),
1953
                       EFI_LOADER_VARIABLE_STR("LoaderEntryLastBooted"),
1954
                       EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"),
1955
                       EFI_LOADER_VARIABLE_STR("LoaderSystemToken")) {
1956

1957
                int q;
27✔
1958

1959
                q = efi_set_variable(var, NULL, 0);
27✔
1960
                if (q == -ENOENT)
27✔
1961
                        continue;
24✔
1962
                if (q < 0)
3✔
1963
                        RET_GATHER(r, log_warning_errno(q, "Failed to remove EFI variable %s: %m", var));
×
1964
                else
1965
                        log_info("Removed EFI variable %s.", var);
3✔
1966
        }
1967

1968
        return r;
3✔
1969
}
1970

1971
static int remove_variables(sd_id128_t uuid) {
3✔
1972
        int r = 0;
3✔
1973

1974
        const char *path = strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
21✔
1975
        RET_GATHER(r, remove_boot_option(uuid, path, /* in_order= */ true));
3✔
1976

1977
        const char *fallback_path = strjoina("/EFI/systemd/systemd-boot-fallback", get_efi_arch(), ".efi");
21✔
1978
        RET_GATHER(r, remove_boot_option(uuid, fallback_path, /* in_order= */ true));
3✔
1979

1980
        return RET_GATHER(r, remove_loader_variables());
3✔
1981
}
1982

1983
int verb_remove(int argc, char *argv[], uintptr_t _data, void *userdata) {
13✔
1984
        int r;
13✔
1985

1986
        _cleanup_(install_context_done) InstallContext c = INSTALL_CONTEXT_NULL;
13✔
1987
        r = install_context_from_cmdline(&c, INSTALL_REMOVE);
13✔
1988
        if (r < 0)
13✔
1989
                return r;
1990
        if (r == 0) {
13✔
1991
                log_debug("No ESP found and operating in graceful mode, skipping.");
×
1992
                return 0;
1993
        }
1994

1995
        r = settle_make_entry_directory(&c);
13✔
1996
        if (r < 0)
13✔
1997
                return r;
1998

1999
        if (c.esp_fd < 0)
13✔
2000
                return c.esp_fd;
2001

2002
        _cleanup_free_ char *j = path_join(c.root, c.esp_path);
26✔
2003
        if (!j)
13✔
2004
                return log_oom();
×
2005

2006
        int dollar_boot_fd = acquire_dollar_boot_fd(&c);
13✔
2007
        if (dollar_boot_fd < 0)
13✔
2008
                return dollar_boot_fd;
2009

2010
        _cleanup_free_ char *w = path_join(c.root, dollar_boot_path(&c));
26✔
2011
        if (!w)
13✔
2012
                return log_oom();
×
2013

2014
        r = remove_binaries(&c);
13✔
2015
        RET_GATHER(r, unlink_inode(j, c.esp_fd, "/loader/loader.conf", S_IFREG));
13✔
2016
        RET_GATHER(r, unlink_inode(j, c.esp_fd, "/loader/random-seed", S_IFREG));
13✔
2017
        RET_GATHER(r, unlink_inode(j, c.esp_fd, "/loader/entries.srel", S_IFREG));
13✔
2018

2019
        FOREACH_STRING(db, "PK.auth", "KEK.auth", "db.auth") {
52✔
2020
                _cleanup_free_ char *p = path_join("/loader/keys/auto", db);
78✔
2021
                if (!p)
39✔
2022
                        return log_oom();
×
2023

2024
                RET_GATHER(r, unlink_inode(j, c.esp_fd, p, S_IFREG));
39✔
2025
        }
2026
        RET_GATHER(r, unlink_inode(j, c.esp_fd, "/loader/keys/auto", S_IFDIR));
13✔
2027
        RET_GATHER(r, unlink_inode(j, c.esp_fd, "/loader/entries.srel", S_IFREG));
13✔
2028

2029
        RET_GATHER(r, remove_subdirs(j, c.esp_fd, esp_subdirs));
13✔
2030
        RET_GATHER(r, remove_subdirs(j, c.esp_fd, dollar_boot_subdirs));
13✔
2031
        RET_GATHER(r, remove_entry_directory(&c, j, c.esp_fd));
13✔
2032

2033
        if (c.xbootldr_fd >= 0) {
13✔
2034
                /* Remove a subset of these also from the XBOOTLDR partition if it exists */
2035
                RET_GATHER(r, unlink_inode(w, c.xbootldr_fd, "/loader/entries.srel", S_IFREG));
8✔
2036
                RET_GATHER(r, remove_subdirs(w, c.xbootldr_fd, dollar_boot_subdirs));
8✔
2037
                RET_GATHER(r, remove_entry_directory(&c, w, c.xbootldr_fd));
8✔
2038
        }
2039

2040
        (void) sync_everything();
13✔
2041

2042
        if (!should_touch_install_variables(&c))
13✔
2043
                return r;
2044

2045
        if (arg_arch_all) {
5✔
2046
                log_info("Not changing EFI variables with --all-architectures.");
2✔
2047
                return r;
2048
        }
2049

2050
        return remove_variables(c.esp_uuid);
3✔
2051
}
2052

2053
int verb_is_installed(int argc, char *argv[], uintptr_t _data, void *userdata) {
14✔
2054
        int r;
14✔
2055

2056
        _cleanup_(install_context_done) InstallContext c = INSTALL_CONTEXT_NULL;
14✔
2057
        r = install_context_from_cmdline(&c, INSTALL_TEST);
14✔
2058
        if (r < 0)
14✔
2059
                return r;
2060
        if (r == 0) {
14✔
2061
                log_debug("No ESP found and operating in graceful mode, claiming not installed.");
×
2062
                if (!arg_quiet)
×
2063
                        puts("no");
×
2064
                return EXIT_FAILURE;
2065
        }
2066

2067
        r = are_we_installed(&c);
14✔
2068
        if (r < 0)
14✔
2069
                return r;
2070

2071
        if (r > 0) {
14✔
2072
                if (!arg_quiet)
7✔
2073
                        puts("yes");
7✔
2074
                return EXIT_SUCCESS;
2075
        } else {
2076
                if (!arg_quiet)
7✔
2077
                        puts("no");
7✔
2078
                return EXIT_FAILURE;
2079
        }
2080
}
2081

2082
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_install_operation, InstallOperation, install_operation_from_string);
×
2083
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryTokenType, boot_entry_token_type_from_string);
×
2084

2085
typedef struct InstallParameters {
2086
        InstallContext context;
2087
        unsigned root_fd_index;
2088
} InstallParameters;
2089

2090
static void install_parameters_done(InstallParameters *p) {
×
2091
        assert(p);
×
2092

2093
        install_context_done(&p->context);
×
2094
}
×
2095

2096
int vl_method_install(
×
2097
                sd_varlink *link,
2098
                sd_json_variant *parameters,
2099
                sd_varlink_method_flags_t flags,
2100
                void *userdata) {
2101

2102
        int r;
×
2103

2104
        assert(link);
×
2105

2106
        _cleanup_(install_parameters_done) InstallParameters p = {
×
2107
                .context = INSTALL_CONTEXT_NULL,
2108
                .root_fd_index = UINT_MAX,
2109
        };
2110

2111
        static const sd_json_dispatch_field dispatch_table[] = {
×
2112
                { "operation",          SD_JSON_VARIANT_STRING,        json_dispatch_install_operation,     voffsetof(p, context.operation),        SD_JSON_MANDATORY },
2113
                { "graceful",           SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,            voffsetof(p, context.graceful),         0                 },
2114
                { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,               voffsetof(p, root_fd_index),            0                 },
2115
                { "rootDirectory",      SD_JSON_VARIANT_STRING,        json_dispatch_path,                  voffsetof(p, context.root),             0                 },
2116
                { "bootEntryTokenType", SD_JSON_VARIANT_STRING,        json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0                 },
2117
                { "touchVariables",     SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,           voffsetof(p, context.touch_variables),  0                 },
2118
                {},
2119
        };
2120

2121
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
×
2122
        if (r != 0)
×
2123
                return r;
2124

2125
        r = varlink_check_privileged_peer(link);
×
2126
        if (r < 0)
×
2127
                return r;
2128

2129
        if (!IN_SET(p.context.operation, INSTALL_NEW, INSTALL_UPDATE))
×
2130
                return sd_varlink_error_invalid_parameter_name(link, "operation");
×
2131

2132
        if (p.root_fd_index != UINT_MAX) {
×
2133
                p.context.root_fd = sd_varlink_peek_dup_fd(link, p.root_fd_index);
×
2134
                if (p.context.root_fd < 0)
×
2135
                        return log_debug_errno(p.context.root_fd, "Failed to acquire root fd from Varlink: %m");
×
2136

2137
                r = fd_verify_safe_flags_full(p.context.root_fd, O_DIRECTORY);
×
2138
                if (r < 0)
×
2139
                        return sd_varlink_error_invalid_parameter_name(link, "rootFileDescriptor");
×
2140

2141
                r = fd_verify_directory(p.context.root_fd);
×
2142
                if (r < 0)
×
2143
                        return log_debug_errno(r, "Specified file descriptor does not refer to a directory: %m");
×
2144

2145
                if (!p.context.root) {
×
2146
                        r = fd_get_path(p.context.root_fd, &p.context.root);
×
2147
                        if (r < 0)
×
2148
                                return log_debug_errno(r, "Failed to get path of file descriptor: %m");
×
2149

2150
                        if (empty_or_root(p.context.root))
×
2151
                                p.context.root = mfree(p.context.root);
×
2152
                }
2153
        } else if (p.context.root) {
×
2154
                p.context.root_fd = open(p.context.root, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
×
2155
                if (p.context.root_fd < 0)
×
2156
                        return log_debug_errno(errno, "Failed to open '%s': %m", p.context.root);
×
2157
        } else
2158
                p.context.root_fd = XAT_FDROOT;
×
2159

2160
        if (p.context.entry_token_type < 0)
×
2161
                p.context.entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
×
2162

2163
        r = find_esp_and_warn_at_full(
×
2164
                        p.context.root_fd,
2165
                        /* path= */ NULL,
2166
                        /* unprivileged_mode= */ false,
2167
                        &p.context.esp_path,
2168
                        &p.context.esp_fd,
2169
                        &p.context.esp_part,
2170
                        &p.context.esp_pstart,
2171
                        &p.context.esp_psize,
2172
                        &p.context.esp_uuid,
2173
                        /* ret_devid= */ NULL);
2174
        if (r == -ENOKEY)
×
2175
                return sd_varlink_error(link, "io.systemd.BootControl.NoESPFound", NULL);
×
2176
        if (r < 0)
×
2177
                return r;
2178

2179
        r = find_xbootldr_and_warn_at(
×
2180
                        p.context.root_fd,
2181
                        /* path= */ NULL,
2182
                        /* unprivileged_mode= */ false,
2183
                        &p.context.xbootldr_path,
2184
                        &p.context.xbootldr_fd);
2185
        if (r == -ENOKEY)
×
2186
                log_debug_errno(r, "Didn't find an XBOOTLDR partition, using ESP as $BOOT.");
×
2187
        else if (r < 0)
×
2188
                return r;
2189

2190
        r = run_install(&p.context);
×
2191
        if (r == -EUNATCH) /* no boot entry token is set */
×
2192
                return sd_varlink_error(link, "io.systemd.BootControl.BootEntryTokenUnavailable", NULL);
×
2193
        if (r < 0)
×
2194
                return r;
2195

2196
        return sd_varlink_reply(link, NULL);
×
2197
}
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