]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bootspec.c
bootspec: fix build when EFI support is disabled
[thirdparty/systemd.git] / src / shared / bootspec.c
CommitLineData
58f21e63 1/* SPDX-License-Identifier: LGPL-2.1+ */
7e87c7d9
ZJS
2
3#include <stdio.h>
af918182 4#include <linux/magic.h>
ca78ad1d 5#include <unistd.h>
7e87c7d9 6
cedb9eec 7#include "sd-device.h"
dccca82b
LP
8#include "sd-id128.h"
9
7e87c7d9 10#include "alloc-util.h"
af918182 11#include "blkid-util.h"
7e87c7d9
ZJS
12#include "bootspec.h"
13#include "conf-files.h"
14#include "def.h"
c67f84b0 15#include "device-nodes.h"
5e146a75 16#include "dirent-util.h"
7e87c7d9 17#include "efivars.h"
5e146a75 18#include "env-file.h"
8cbb7d87 19#include "env-util.h"
7e87c7d9
ZJS
20#include "fd-util.h"
21#include "fileio.h"
af918182 22#include "parse-util.h"
cc7a0bfa 23#include "path-util.h"
5e146a75 24#include "pe-header.h"
760877e9 25#include "sort-util.h"
af918182 26#include "stat-util.h"
93f14ce2 27#include "string-table.h"
7e87c7d9
ZJS
28#include "string-util.h"
29#include "strv.h"
5e146a75 30#include "unaligned.h"
ca78ad1d 31#include "util.h"
af918182 32#include "virt.h"
7e87c7d9 33
0de2e1fd 34static void boot_entry_free(BootEntry *entry) {
4fe2ba0e 35 assert(entry);
7e87c7d9 36
12580bc3 37 free(entry->id);
2d3bfb69 38 free(entry->path);
43b736a8 39 free(entry->root);
7e87c7d9 40 free(entry->title);
64f05708 41 free(entry->show_title);
7e87c7d9
ZJS
42 free(entry->version);
43 free(entry->machine_id);
44 free(entry->architecture);
45 strv_free(entry->options);
46 free(entry->kernel);
47 free(entry->efi);
48 strv_free(entry->initrd);
49 free(entry->device_tree);
50}
51
43b736a8
LP
52static int boot_entry_load(
53 const char *root,
54 const char *path,
55 BootEntry *entry) {
56
93f14ce2
LP
57 _cleanup_(boot_entry_free) BootEntry tmp = {
58 .type = BOOT_ENTRY_CONF,
59 };
60
7e87c7d9
ZJS
61 _cleanup_fclose_ FILE *f = NULL;
62 unsigned line = 1;
263195c6 63 char *b, *c;
7e87c7d9
ZJS
64 int r;
65
43b736a8 66 assert(root);
4fe2ba0e
LP
67 assert(path);
68 assert(entry);
69
263195c6 70 c = endswith_no_case(path, ".conf");
46bba8a5 71 if (!c)
eed7210a 72 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path);
7e87c7d9 73
263195c6 74 b = basename(path);
12580bc3
LP
75 tmp.id = strndup(b, c - b);
76 if (!tmp.id)
7e87c7d9
ZJS
77 return log_oom();
78
eed7210a
LP
79 if (!efi_loader_entry_name_valid(tmp.id))
80 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
81
2d3bfb69
ZJS
82 tmp.path = strdup(path);
83 if (!tmp.path)
84 return log_oom();
85
43b736a8
LP
86 tmp.root = strdup(root);
87 if (!tmp.root)
88 return log_oom();
89
263195c6
YW
90 f = fopen(path, "re");
91 if (!f)
92 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
93
7e87c7d9 94 for (;;) {
f99fdc3e
YW
95 _cleanup_free_ char *buf = NULL, *field = NULL;
96 const char *p;
7e87c7d9
ZJS
97
98 r = read_line(f, LONG_LINE_MAX, &buf);
99 if (r == 0)
100 break;
101 if (r == -ENOBUFS)
102 return log_error_errno(r, "%s:%u: Line too long", path, line);
103 if (r < 0)
104 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
105
106 line++;
107
108 if (IN_SET(*strstrip(buf), '#', '\0'))
109 continue;
110
f99fdc3e
YW
111 p = buf;
112 r = extract_first_word(&p, &field, " \t", 0);
113 if (r < 0) {
114 log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
115 continue;
116 }
117 if (r == 0) {
7e87c7d9
ZJS
118 log_warning("%s:%u: Bad syntax", path, line);
119 continue;
120 }
7e87c7d9 121
f99fdc3e 122 if (streq(field, "title"))
7e87c7d9 123 r = free_and_strdup(&tmp.title, p);
f99fdc3e 124 else if (streq(field, "version"))
7e87c7d9 125 r = free_and_strdup(&tmp.version, p);
f99fdc3e 126 else if (streq(field, "machine-id"))
7e87c7d9 127 r = free_and_strdup(&tmp.machine_id, p);
f99fdc3e 128 else if (streq(field, "architecture"))
7e87c7d9 129 r = free_and_strdup(&tmp.architecture, p);
f99fdc3e 130 else if (streq(field, "options"))
7e87c7d9 131 r = strv_extend(&tmp.options, p);
f99fdc3e 132 else if (streq(field, "linux"))
7e87c7d9 133 r = free_and_strdup(&tmp.kernel, p);
f99fdc3e 134 else if (streq(field, "efi"))
7e87c7d9 135 r = free_and_strdup(&tmp.efi, p);
f99fdc3e 136 else if (streq(field, "initrd"))
7e87c7d9 137 r = strv_extend(&tmp.initrd, p);
f99fdc3e 138 else if (streq(field, "devicetree"))
7e87c7d9
ZJS
139 r = free_and_strdup(&tmp.device_tree, p);
140 else {
feb41f1f 141 log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
7e87c7d9
ZJS
142 continue;
143 }
144 if (r < 0)
145 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
146 }
147
148 *entry = tmp;
149 tmp = (BootEntry) {};
150 return 0;
151}
152
153void boot_config_free(BootConfig *config) {
da6053d0 154 size_t i;
7e87c7d9 155
4fe2ba0e
LP
156 assert(config);
157
7e87c7d9
ZJS
158 free(config->default_pattern);
159 free(config->timeout);
160 free(config->editor);
c1d4e298
JJ
161 free(config->auto_entries);
162 free(config->auto_firmware);
72263375 163 free(config->console_mode);
7e87c7d9
ZJS
164
165 free(config->entry_oneshot);
166 free(config->entry_default);
167
168 for (i = 0; i < config->n_entries; i++)
169 boot_entry_free(config->entries + i);
170 free(config->entries);
171}
172
0de2e1fd 173static int boot_loader_read_conf(const char *path, BootConfig *config) {
7e87c7d9
ZJS
174 _cleanup_fclose_ FILE *f = NULL;
175 unsigned line = 1;
176 int r;
177
4fe2ba0e
LP
178 assert(path);
179 assert(config);
180
7e87c7d9 181 f = fopen(path, "re");
f91ed3dc
LP
182 if (!f) {
183 if (errno == ENOENT)
184 return 0;
185
7e87c7d9 186 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
f91ed3dc 187 }
7e87c7d9
ZJS
188
189 for (;;) {
f99fdc3e
YW
190 _cleanup_free_ char *buf = NULL, *field = NULL;
191 const char *p;
7e87c7d9
ZJS
192
193 r = read_line(f, LONG_LINE_MAX, &buf);
194 if (r == 0)
195 break;
196 if (r == -ENOBUFS)
197 return log_error_errno(r, "%s:%u: Line too long", path, line);
198 if (r < 0)
199 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
200
201 line++;
202
203 if (IN_SET(*strstrip(buf), '#', '\0'))
204 continue;
205
f99fdc3e
YW
206 p = buf;
207 r = extract_first_word(&p, &field, " \t", 0);
208 if (r < 0) {
209 log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
210 continue;
211 }
212 if (r == 0) {
7e87c7d9
ZJS
213 log_warning("%s:%u: Bad syntax", path, line);
214 continue;
215 }
7e87c7d9 216
f99fdc3e 217 if (streq(field, "default"))
7e87c7d9 218 r = free_and_strdup(&config->default_pattern, p);
f99fdc3e 219 else if (streq(field, "timeout"))
7e87c7d9 220 r = free_and_strdup(&config->timeout, p);
f99fdc3e 221 else if (streq(field, "editor"))
7e87c7d9 222 r = free_and_strdup(&config->editor, p);
790f84eb 223 else if (streq(field, "auto-entries"))
c1d4e298 224 r = free_and_strdup(&config->auto_entries, p);
790f84eb 225 else if (streq(field, "auto-firmware"))
c1d4e298 226 r = free_and_strdup(&config->auto_firmware, p);
790f84eb 227 else if (streq(field, "console-mode"))
d37b0737 228 r = free_and_strdup(&config->console_mode, p);
7e87c7d9 229 else {
feb41f1f 230 log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
7e87c7d9
ZJS
231 continue;
232 }
233 if (r < 0)
234 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
235 }
236
f91ed3dc 237 return 1;
7e87c7d9
ZJS
238}
239
93bab288 240static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
12580bc3 241 return str_verscmp(a->id, b->id);
7e87c7d9
ZJS
242}
243
43b736a8
LP
244static int boot_entries_find(
245 const char *root,
246 const char *dir,
a2f8664e
LP
247 BootEntry **entries,
248 size_t *n_entries) {
43b736a8 249
7e87c7d9 250 _cleanup_strv_free_ char **files = NULL;
a2f8664e 251 size_t n_allocated = *n_entries;
7e87c7d9
ZJS
252 char **f;
253 int r;
7e87c7d9 254
43b736a8 255 assert(root);
4fe2ba0e 256 assert(dir);
a2f8664e
LP
257 assert(entries);
258 assert(n_entries);
4fe2ba0e 259
7e87c7d9
ZJS
260 r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL);
261 if (r < 0)
262 return log_error_errno(r, "Failed to list files in \"%s\": %m", dir);
263
264 STRV_FOREACH(f, files) {
a2f8664e 265 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
7e87c7d9
ZJS
266 return log_oom();
267
a2f8664e 268 r = boot_entry_load(root, *f, *entries + *n_entries);
7e87c7d9
ZJS
269 if (r < 0)
270 continue;
271
a2f8664e 272 (*n_entries) ++;
7e87c7d9
ZJS
273 }
274
7e87c7d9
ZJS
275 return 0;
276}
277
5e146a75
LP
278static int boot_entry_load_unified(
279 const char *root,
280 const char *path,
281 const char *osrelease,
282 const char *cmdline,
283 BootEntry *ret) {
284
285 _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL;
93f14ce2
LP
286 _cleanup_(boot_entry_free) BootEntry tmp = {
287 .type = BOOT_ENTRY_UNIFIED,
288 };
5e146a75
LP
289 _cleanup_fclose_ FILE *f = NULL;
290 const char *k;
291 int r;
292
293 assert(root);
294 assert(path);
295 assert(osrelease);
296
297 k = path_startswith(path, root);
298 if (!k)
299 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path);
300
673a1e6f 301 f = fmemopen_unlocked((void*) osrelease, strlen(osrelease), "r");
5e146a75
LP
302 if (!f)
303 return log_error_errno(errno, "Failed to open os-release buffer: %m");
304
305 r = parse_env_file(f, "os-release",
306 "PRETTY_NAME", &os_pretty_name,
307 "ID", &os_id,
308 "VERSION_ID", &version_id,
309 "BUILD_ID", &build_id);
310 if (r < 0)
311 return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path);
312
313 if (!os_pretty_name || !os_id || !(version_id || build_id))
314 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
315
316 tmp.id = strjoin(os_id, "-", version_id ?: build_id);
317 if (!tmp.id)
318 return log_oom();
319
eed7210a
LP
320 if (!efi_loader_entry_name_valid(tmp.id))
321 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id);
322
5e146a75
LP
323 tmp.path = strdup(path);
324 if (!tmp.path)
325 return log_oom();
326
327 tmp.root = strdup(root);
328 if (!tmp.root)
329 return log_oom();
330
331 tmp.kernel = strdup(skip_leading_chars(k, "/"));
332 if (!tmp.kernel)
333 return log_oom();
334
335 tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE));
336 if (!tmp.options)
337 return log_oom();
338
339 delete_trailing_chars(tmp.options[0], WHITESPACE);
340
341 tmp.title = TAKE_PTR(os_pretty_name);
342
343 *ret = tmp;
344 tmp = (BootEntry) {};
345 return 0;
346}
347
348/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
349 * the ones we do care about and we are willing to load into memory have this size limit.) */
350#define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
351
352static int find_sections(
353 int fd,
354 char **ret_osrelease,
355 char **ret_cmdline) {
356
357 _cleanup_free_ struct PeSectionHeader *sections = NULL;
358 _cleanup_free_ char *osrelease = NULL, *cmdline = NULL;
359 size_t i, n_sections;
360 struct DosFileHeader dos;
361 struct PeHeader pe;
362 uint64_t start;
363 ssize_t n;
364
365 n = pread(fd, &dos, sizeof(dos), 0);
366 if (n < 0)
367 return log_error_errno(errno, "Failed read DOS header: %m");
368 if (n != sizeof(dos))
369 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading DOS header, refusing.");
370
371 if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z')
372 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOS executable magic missing, refusing.");
373
374 start = unaligned_read_le32(&dos.ExeHeader);
375 n = pread(fd, &pe, sizeof(pe), start);
376 if (n < 0)
377 return log_error_errno(errno, "Failed to read PE header: %m");
378 if (n != sizeof(pe))
379 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading PE header, refusing.");
380
381 if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0)
382 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE executable magic missing, refusing.");
383
384 n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections);
385 if (n_sections > 96)
386 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE header has too many sections, refusing.");
387
388 sections = new(struct PeSectionHeader, n_sections);
389 if (!sections)
390 return log_oom();
391
392 n = pread(fd, sections,
393 n_sections * sizeof(struct PeSectionHeader),
394 start + sizeof(pe) + unaligned_read_le16(&pe.FileHeader.SizeOfOptionalHeader));
395 if (n < 0)
396 return log_error_errno(errno, "Failed to read section data: %m");
397 if ((size_t) n != n_sections * sizeof(struct PeSectionHeader))
398 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading sections, refusing.");
399
400 for (i = 0; i < n_sections; i++) {
401 _cleanup_free_ char *k = NULL;
402 uint32_t offset, size;
403 char **b;
404
405 if (strneq((char*) sections[i].Name, ".osrel", sizeof(sections[i].Name)))
406 b = &osrelease;
407 else if (strneq((char*) sections[i].Name, ".cmdline", sizeof(sections[i].Name)))
408 b = &cmdline;
409 else
410 continue;
411
412 if (*b)
413 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section %s, refusing.", sections[i].Name);
414
415 offset = unaligned_read_le32(&sections[i].PointerToRawData);
416 size = unaligned_read_le32(&sections[i].VirtualSize);
417
418 if (size > PE_SECTION_SIZE_MAX)
419 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section %s too large, refusing.", sections[i].Name);
420
421 k = new(char, size+1);
422 if (!k)
423 return log_oom();
424
425 n = pread(fd, k, size, offset);
426 if (n < 0)
427 return log_error_errno(errno, "Failed to read section payload: %m");
a75fcef8 428 if ((size_t) n != size)
5e146a75
LP
429 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading section payload, refusing:");
430
431 /* Allow one trailing NUL byte, but nothing more. */
432 if (size > 0 && memchr(k, 0, size - 1))
433 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section contains embedded NUL byte: %m");
434
435 k[size] = 0;
436 *b = TAKE_PTR(k);
437 }
438
439 if (!osrelease)
440 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Image lacks .osrel section, refusing.");
441
442 if (ret_osrelease)
443 *ret_osrelease = TAKE_PTR(osrelease);
444 if (ret_cmdline)
445 *ret_cmdline = TAKE_PTR(cmdline);
446
447 return 0;
448}
449
450static int boot_entries_find_unified(
451 const char *root,
452 const char *dir,
453 BootEntry **entries,
454 size_t *n_entries) {
455
456 _cleanup_(closedirp) DIR *d = NULL;
457 size_t n_allocated = *n_entries;
5e146a75
LP
458 struct dirent *de;
459 int r;
460
461 assert(root);
462 assert(dir);
463 assert(entries);
464 assert(n_entries);
465
466 d = opendir(dir);
467 if (!d) {
468 if (errno == ENOENT)
469 return 0;
470
471 return log_error_errno(errno, "Failed to open %s: %m", dir);
472 }
473
474 FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", dir)) {
475 _cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
476 _cleanup_close_ int fd = -1;
477
478 if (!dirent_is_file(de))
479 continue;
480
481 if (!endswith_no_case(de->d_name, ".efi"))
482 continue;
483
484 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
485 return log_oom();
486
487 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
488 if (fd < 0) {
489 log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", dir, de->d_name);
490 continue;
491 }
492
493 r = fd_verify_regular(fd);
494 if (r < 0) {
dba33c4a 495 log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
5e146a75
LP
496 continue;
497 }
498
499 r = find_sections(fd, &osrelease, &cmdline);
500 if (r < 0)
501 continue;
502
503 j = path_join(dir, de->d_name);
504 if (!j)
505 return log_oom();
506
507 r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries);
508 if (r < 0)
509 continue;
510
511 (*n_entries) ++;
5e146a75
LP
512 }
513
5e146a75
LP
514 return 0;
515}
516
64f05708 517static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
da6053d0 518 size_t i, j;
64f05708
ZJS
519 bool non_unique = false;
520
4fe2ba0e
LP
521 assert(entries || n_entries == 0);
522 assert(arr || n_entries == 0);
523
64f05708
ZJS
524 for (i = 0; i < n_entries; i++)
525 arr[i] = false;
526
527 for (i = 0; i < n_entries; i++)
528 for (j = 0; j < n_entries; j++)
529 if (i != j && streq(boot_entry_title(entries + i),
530 boot_entry_title(entries + j)))
531 non_unique = arr[i] = arr[j] = true;
532
533 return non_unique;
534}
535
536static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
537 char *s;
da6053d0 538 size_t i;
64f05708
ZJS
539 int r;
540 bool arr[n_entries];
541
4fe2ba0e
LP
542 assert(entries || n_entries == 0);
543
64f05708
ZJS
544 /* Find _all_ non-unique titles */
545 if (!find_nonunique(entries, n_entries, arr))
546 return 0;
547
548 /* Add version to non-unique titles */
549 for (i = 0; i < n_entries; i++)
550 if (arr[i] && entries[i].version) {
551 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
552 if (r < 0)
553 return -ENOMEM;
554
555 free_and_replace(entries[i].show_title, s);
556 }
557
558 if (!find_nonunique(entries, n_entries, arr))
559 return 0;
560
561 /* Add machine-id to non-unique titles */
562 for (i = 0; i < n_entries; i++)
563 if (arr[i] && entries[i].machine_id) {
564 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
565 if (r < 0)
566 return -ENOMEM;
567
568 free_and_replace(entries[i].show_title, s);
569 }
570
571 if (!find_nonunique(entries, n_entries, arr))
572 return 0;
573
574 /* Add file name to non-unique titles */
575 for (i = 0; i < n_entries; i++)
576 if (arr[i]) {
12580bc3 577 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
64f05708
ZJS
578 if (r < 0)
579 return -ENOMEM;
580
581 free_and_replace(entries[i].show_title, s);
582 }
583
584 return 0;
585}
586
ad1afd60 587static int boot_entries_select_default(const BootConfig *config) {
7e87c7d9
ZJS
588 int i;
589
4fe2ba0e 590 assert(config);
388d2993
ZJS
591 assert(config->entries || config->n_entries == 0);
592
593 if (config->n_entries == 0) {
594 log_debug("Found no default boot entry :(");
595 return -1; /* -1 means "no default" */
596 }
4fe2ba0e 597
7e87c7d9
ZJS
598 if (config->entry_oneshot)
599 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
600 if (streq(config->entry_oneshot, config->entries[i].id)) {
601 log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
602 config->entries[i].id);
7e87c7d9
ZJS
603 return i;
604 }
605
606 if (config->entry_default)
607 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
608 if (streq(config->entry_default, config->entries[i].id)) {
609 log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
610 config->entries[i].id);
7e87c7d9
ZJS
611 return i;
612 }
613
614 if (config->default_pattern)
615 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
616 if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
617 log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
618 config->entries[i].id, config->default_pattern);
7e87c7d9
ZJS
619 return i;
620 }
621
388d2993
ZJS
622 log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
623 return config->n_entries - 1;
7e87c7d9
ZJS
624}
625
a2f8664e
LP
626int boot_entries_load_config(
627 const char *esp_path,
628 const char *xbootldr_path,
629 BootConfig *config) {
630
7e87c7d9
ZJS
631 const char *p;
632 int r;
633
4fe2ba0e
LP
634 assert(config);
635
a2f8664e
LP
636 if (esp_path) {
637 p = strjoina(esp_path, "/loader/loader.conf");
638 r = boot_loader_read_conf(p, config);
639 if (r < 0)
640 return r;
7e87c7d9 641
a2f8664e
LP
642 p = strjoina(esp_path, "/loader/entries");
643 r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries);
644 if (r < 0)
645 return r;
5e146a75
LP
646
647 p = strjoina(esp_path, "/EFI/Linux/");
648 r = boot_entries_find_unified(esp_path, p, &config->entries, &config->n_entries);
649 if (r < 0)
650 return r;
a2f8664e
LP
651 }
652
653 if (xbootldr_path) {
654 p = strjoina(xbootldr_path, "/loader/entries");
655 r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries);
656 if (r < 0)
657 return r;
5e146a75
LP
658
659 p = strjoina(xbootldr_path, "/EFI/Linux/");
660 r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries);
661 if (r < 0)
662 return r;
a2f8664e 663 }
7e87c7d9 664
dd2bf34c
LP
665 typesafe_qsort(config->entries, config->n_entries, boot_entry_compare);
666
64f05708
ZJS
667 r = boot_entries_uniquify(config->entries, config->n_entries);
668 if (r < 0)
669 return log_error_errno(r, "Failed to uniquify boot entries: %m");
670
9c4a6c13
LP
671 if (is_efi_boot()) {
672 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
bd29f9de
ZJS
673 if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
674 log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m");
675 if (r == -ENOMEM)
676 return r;
677 }
9c4a6c13
LP
678
679 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
bd29f9de
ZJS
680 if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
681 log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m");
682 if (r == -ENOMEM)
683 return r;
684 }
9c4a6c13 685 }
7e87c7d9
ZJS
686
687 config->default_entry = boot_entries_select_default(config);
688 return 0;
689}
af918182 690
eea4ce1e
LP
691int boot_entries_load_config_auto(
692 const char *override_esp_path,
693 const char *override_xbootldr_path,
694 BootConfig *config) {
695
696 _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
697 int r;
698
699 assert(config);
700
701 /* This function is similar to boot_entries_load_config(), however we automatically search for the
702 * ESP and the XBOOTLDR partition unless it is explicitly specified. Also, if the user did not pass
703 * an ESP or XBOOTLDR path directly, let's see if /run/boot-loader-entries/ exists. If so, let's
704 * read data from there, as if it was an ESP (i.e. loading both entries and loader.conf data from
705 * it). This allows other boot loaders to pass boot loader entry information to our tools if they
706 * want to. */
707
708 if (!override_esp_path && !override_xbootldr_path) {
f40999f8
ZJS
709 if (access("/run/boot-loader-entries/", F_OK) >= 0)
710 return boot_entries_load_config("/run/boot-loader-entries/", NULL, config);
eea4ce1e 711
f40999f8
ZJS
712 if (errno != ENOENT)
713 return log_error_errno(errno,
714 "Failed to determine whether /run/boot-loader-entries/ exists: %m");
eea4ce1e
LP
715 }
716
717 r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
cc5957dc 718 if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
eea4ce1e
LP
719 return r;
720
721 r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL);
722 if (r < 0 && r != -ENOKEY)
723 return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
724
f40999f8 725 return boot_entries_load_config(esp_where, xbootldr_where, config);
eea4ce1e
LP
726}
727
528fcf8d 728#if ENABLE_EFI
93f14ce2 729int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
93f14ce2
LP
730 static const char * const title_table[] = {
731 /* Pretty names for a few well-known automatically discovered entries. */
732 "auto-osx", "macOS",
733 "auto-windows", "Windows Boot Manager",
734 "auto-efi-shell", "EFI Shell",
735 "auto-efi-default", "EFI Default Loader",
736 "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface",
737 };
738
18268197 739 _cleanup_strv_free_ char **found_by_loader = NULL;
93f14ce2
LP
740 size_t n_allocated;
741 char **i;
742 int r;
743
744 assert(config);
745
746 /* Let's add the entries discovered by the boot loader to the end of our list, unless they are
747 * already included there. */
748
749 r = efi_loader_get_entries(&found_by_loader);
750 if (IN_SET(r, -ENOENT, -EOPNOTSUPP))
751 return log_debug_errno(r, "Boot loader reported no entries.");
752 if (r < 0)
753 return log_error_errno(r, "Failed to determine entries reported by boot loader: %m");
754
755 n_allocated = config->n_entries;
756
757 STRV_FOREACH(i, found_by_loader) {
ce4c4f81 758 _cleanup_free_ char *c = NULL, *t = NULL, *p = NULL;
93f14ce2
LP
759 char **a, **b;
760
761 if (boot_config_has_entry(config, *i))
762 continue;
763
764 if (only_auto && !startswith(*i, "auto-"))
765 continue;
766
767 c = strdup(*i);
768 if (!c)
769 return log_oom();
770
771 STRV_FOREACH_PAIR(a, b, (char**) title_table)
772 if (streq(*a, *i)) {
773 t = strdup(*b);
774 if (!t)
775 return log_oom();
776 break;
777 }
778
ce4c4f81
ZJS
779 p = efi_variable_path(EFI_VENDOR_LOADER, "LoaderEntries");
780 if (!p)
781 return log_oom();
782
93f14ce2
LP
783 if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1))
784 return log_oom();
785
786 config->entries[config->n_entries++] = (BootEntry) {
787 .type = BOOT_ENTRY_LOADER,
788 .id = TAKE_PTR(c),
789 .title = TAKE_PTR(t),
ce4c4f81 790 .path = TAKE_PTR(p),
93f14ce2
LP
791 };
792 }
793
794 return 0;
795}
528fcf8d 796#endif
93f14ce2 797
af918182
ZJS
798/********************************************************************************/
799
575d4370
LP
800static int verify_esp_blkid(
801 dev_t devid,
5caa3167 802 bool searching,
af918182
ZJS
803 uint32_t *ret_part,
804 uint64_t *ret_pstart,
805 uint64_t *ret_psize,
806 sd_id128_t *ret_uuid) {
575d4370
LP
807
808 sd_id128_t uuid = SD_ID128_NULL;
809 uint64_t pstart = 0, psize = 0;
810 uint32_t part = 0;
811
4e066f7f 812#if HAVE_BLKID
8e766630 813 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
54b22b26 814 _cleanup_free_ char *node = NULL;
4e066f7f 815 const char *v;
af918182
ZJS
816 int r;
817
575d4370 818 r = device_path_make_major_minor(S_IFBLK, devid, &node);
54b22b26
LP
819 if (r < 0)
820 return log_error_errno(r, "Failed to format major/minor device path: %m");
575d4370 821
af918182 822 errno = 0;
54b22b26 823 b = blkid_new_probe_from_filename(node);
af918182 824 if (!b)
575d4370 825 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
af918182
ZJS
826
827 blkid_probe_enable_superblocks(b, 1);
828 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
829 blkid_probe_enable_partitions(b, 1);
830 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
831
832 errno = 0;
833 r = blkid_do_safeprobe(b);
a7afbd60 834 if (r == -2)
575d4370 835 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
a7afbd60 836 else if (r == 1)
575d4370 837 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
a7afbd60 838 else if (r != 0)
575d4370 839 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
af918182
ZJS
840
841 errno = 0;
842 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
843 if (r != 0)
575d4370 844 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node);
a7afbd60
LP
845 if (!streq(v, "vfat"))
846 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
847 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
575d4370 848 "File system \"%s\" is not FAT.", node);
af918182
ZJS
849
850 errno = 0;
851 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
852 if (r != 0)
575d4370 853 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
a7afbd60
LP
854 if (!streq(v, "gpt"))
855 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
856 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
575d4370 857 "File system \"%s\" is not on a GPT partition table.", node);
af918182
ZJS
858
859 errno = 0;
860 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
861 if (r != 0)
575d4370 862 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
a7afbd60
LP
863 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
864 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
865 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
575d4370 866 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
af918182
ZJS
867
868 errno = 0;
869 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
870 if (r != 0)
575d4370 871 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
af918182 872 r = sd_id128_from_string(v, &uuid);
a7afbd60 873 if (r < 0)
575d4370 874 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
af918182
ZJS
875
876 errno = 0;
877 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
878 if (r != 0)
575d4370 879 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node);
af918182
ZJS
880 r = safe_atou32(v, &part);
881 if (r < 0)
882 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
883
884 errno = 0;
885 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
886 if (r != 0)
575d4370 887 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
af918182
ZJS
888 r = safe_atou64(v, &pstart);
889 if (r < 0)
890 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
891
892 errno = 0;
893 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
894 if (r != 0)
575d4370 895 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
af918182
ZJS
896 r = safe_atou64(v, &psize);
897 if (r < 0)
898 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
4e066f7f 899#endif
af918182 900
af918182
ZJS
901 if (ret_part)
902 *ret_part = part;
903 if (ret_pstart)
904 *ret_pstart = pstart;
905 if (ret_psize)
906 *ret_psize = psize;
907 if (ret_uuid)
908 *ret_uuid = uuid;
909
910 return 0;
911}
912
cedb9eec
LP
913static int verify_esp_udev(
914 dev_t devid,
915 bool searching,
916 uint32_t *ret_part,
917 uint64_t *ret_pstart,
918 uint64_t *ret_psize,
919 sd_id128_t *ret_uuid) {
920
921 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
922 _cleanup_free_ char *node = NULL;
923 sd_id128_t uuid = SD_ID128_NULL;
924 uint64_t pstart = 0, psize = 0;
925 uint32_t part = 0;
926 const char *v;
927 int r;
928
929 r = device_path_make_major_minor(S_IFBLK, devid, &node);
930 if (r < 0)
931 return log_error_errno(r, "Failed to format major/minor device path: %m");
932
933 r = sd_device_new_from_devnum(&d, 'b', devid);
934 if (r < 0)
935 return log_error_errno(r, "Failed to get device from device number: %m");
936
937 r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
938 if (r < 0)
939 return log_error_errno(r, "Failed to get device property: %m");
940 if (!streq(v, "vfat"))
941 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
942 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
943 "File system \"%s\" is not FAT.", node );
944
945 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
946 if (r < 0)
947 return log_error_errno(r, "Failed to get device property: %m");
948 if (!streq(v, "gpt"))
949 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
950 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
951 "File system \"%s\" is not on a GPT partition table.", node);
952
953 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
954 if (r < 0)
955 return log_error_errno(r, "Failed to get device property: %m");
956 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
957 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
958 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
959 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
960
961 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
962 if (r < 0)
963 return log_error_errno(r, "Failed to get device property: %m");
964 r = sd_id128_from_string(v, &uuid);
965 if (r < 0)
966 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
967
968 r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
969 if (r < 0)
970 return log_error_errno(r, "Failed to get device property: %m");
971 r = safe_atou32(v, &part);
972 if (r < 0)
973 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
974
975 r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
976 if (r < 0)
977 return log_error_errno(r, "Failed to get device property: %m");
978 r = safe_atou64(v, &pstart);
979 if (r < 0)
980 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
981
982 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
983 if (r < 0)
984 return log_error_errno(r, "Failed to get device property: %m");
985 r = safe_atou64(v, &psize);
986 if (r < 0)
987 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
988
989 if (ret_part)
990 *ret_part = part;
991 if (ret_pstart)
992 *ret_pstart = pstart;
993 if (ret_psize)
994 *ret_psize = psize;
995 if (ret_uuid)
996 *ret_uuid = uuid;
997
998 return 0;
999}
1000
18ae9ef1
LP
1001static int verify_fsroot_dir(
1002 const char *path,
1003 bool searching,
1004 bool unprivileged_mode,
1005 dev_t *ret_dev) {
1006
1007 struct stat st, st2;
8312d694 1008 const char *t2, *trigger;
18ae9ef1
LP
1009 int r;
1010
1011 assert(path);
1012 assert(ret_dev);
1013
8312d694
LP
1014 /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
1015 * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
1016 * before stat()ing */
1017 trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
1018 (void) access(trigger, F_OK);
1019
18ae9ef1
LP
1020 if (stat(path, &st) < 0)
1021 return log_full_errno((searching && errno == ENOENT) ||
1022 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
1023 "Failed to determine block device node of \"%s\": %m", path);
1024
1025 if (major(st.st_dev) == 0)
1026 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1027 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1028 "Block device node of \"%s\" is invalid.", path);
1029
1030 t2 = strjoina(path, "/..");
1031 if (stat(t2, &st2) < 0) {
1032 if (errno != EACCES)
1033 r = -errno;
1034 else {
1035 _cleanup_free_ char *parent = NULL;
1036
1037 /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
1038 * directly instead. It's not as good, due to symlinks and such, but we can't do
1039 * anything better here. */
1040
1041 parent = dirname_malloc(path);
1042 if (!parent)
1043 return log_oom();
1044
1045 if (stat(parent, &st2) < 0)
1046 r = -errno;
1047 else
1048 r = 0;
1049 }
1050
1051 if (r < 0)
1052 return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
1053 "Failed to determine block device node of parent of \"%s\": %m", path);
1054 }
1055
1056 if (st.st_dev == st2.st_dev)
1057 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1058 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1059 "Directory \"%s\" is not the root of the file system.", path);
1060
1061 if (ret_dev)
1062 *ret_dev = st.st_dev;
1063
1064 return 0;
1065}
1066
575d4370
LP
1067static int verify_esp(
1068 const char *p,
1069 bool searching,
1070 bool unprivileged_mode,
1071 uint32_t *ret_part,
1072 uint64_t *ret_pstart,
1073 uint64_t *ret_psize,
1074 sd_id128_t *ret_uuid) {
1075
575d4370 1076 bool relax_checks;
18ae9ef1 1077 dev_t devid;
575d4370
LP
1078 int r;
1079
1080 assert(p);
1081
1082 /* This logs about all errors, except:
1083 *
1084 * -ENOENT → if 'searching' is set, and the dir doesn't exist
1085 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
1086 * -EACESS → if 'unprivileged_mode' is set, and we have trouble acessing the thing
1087 */
1088
1089 relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
1090
1091 /* Non-root user can only check the status, so if an error occured in the following, it does not cause any
1092 * issues. Let's also, silence the error messages. */
1093
1094 if (!relax_checks) {
18ae9ef1
LP
1095 struct statfs sfs;
1096
575d4370
LP
1097 if (statfs(p, &sfs) < 0)
1098 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
1099 return log_full_errno((searching && errno == ENOENT) ||
1100 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
1101 "Failed to check file system type of \"%s\": %m", p);
1102
1103 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
1104 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1105 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1106 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
1107 }
1108
18ae9ef1 1109 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
575d4370 1110 if (r < 0)
18ae9ef1 1111 return r;
575d4370
LP
1112
1113 /* In a container we don't have access to block devices, skip this part of the verification, we trust
cedb9eec
LP
1114 * the container manager set everything up correctly on its own. */
1115 if (detect_container() > 0 || relax_checks)
575d4370
LP
1116 goto finish;
1117
cedb9eec
LP
1118 /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
1119 * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
1120 * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
1121 * however blkid can't work if we have no privileges to access block devices directly, which is why
1122 * we use udev in that case. */
1123 if (unprivileged_mode)
18ae9ef1 1124 return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
cedb9eec 1125 else
18ae9ef1 1126 return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
575d4370
LP
1127
1128finish:
1129 if (ret_part)
1130 *ret_part = 0;
1131 if (ret_pstart)
1132 *ret_pstart = 0;
1133 if (ret_psize)
1134 *ret_psize = 0;
1135 if (ret_uuid)
1136 *ret_uuid = SD_ID128_NULL;
1137
1138 return 0;
1139}
1140
5caa3167
LP
1141int find_esp_and_warn(
1142 const char *path,
1143 bool unprivileged_mode,
1144 char **ret_path,
1145 uint32_t *ret_part,
1146 uint64_t *ret_pstart,
1147 uint64_t *ret_psize,
1148 sd_id128_t *ret_uuid) {
af918182 1149
af918182
ZJS
1150 int r;
1151
5caa3167
LP
1152 /* This logs about all errors except:
1153 *
1154 * -ENOKEY → when we can't find the partition
1155 * -EACCESS → when unprivileged_mode is true, and we can't access something
1156 */
af918182 1157
5caa3167
LP
1158 if (path) {
1159 r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
af918182
ZJS
1160 if (r < 0)
1161 return r;
1162
5caa3167
LP
1163 goto found;
1164 }
1165
cc7a0bfa
LP
1166 path = getenv("SYSTEMD_ESP_PATH");
1167 if (path) {
baaa35ad
ZJS
1168 if (!path_is_valid(path) || !path_is_absolute(path))
1169 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1170 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
1171 path);
cc7a0bfa
LP
1172
1173 /* Note: when the user explicitly configured things with an env var we won't validate the mount
1174 * point. After all we want this to be useful for testing. */
1175 goto found;
1176 }
1177
5caa3167
LP
1178 FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
1179
1180 r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
1181 if (r >= 0)
1182 goto found;
1183 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
1184 return r;
1185 }
1186
1187 /* No logging here */
1188 return -ENOKEY;
1189
15cb6c98
LP
1190found:
1191 if (ret_path) {
1192 char *c;
1193
1194 c = strdup(path);
1195 if (!c)
1196 return log_oom();
1197
1198 *ret_path = c;
1199 }
1200
1201 return 0;
1202}
1203
ad95aa44
LP
1204static int verify_xbootldr_blkid(
1205 dev_t devid,
15cb6c98 1206 bool searching,
15cb6c98 1207 sd_id128_t *ret_uuid) {
ad95aa44
LP
1208
1209 sd_id128_t uuid = SD_ID128_NULL;
1210
15cb6c98
LP
1211#if HAVE_BLKID
1212 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
1213 _cleanup_free_ char *node = NULL;
1214 const char *v;
15cb6c98
LP
1215 int r;
1216
ad95aa44 1217 r = device_path_make_major_minor(S_IFBLK, devid, &node);
15cb6c98
LP
1218 if (r < 0)
1219 return log_error_errno(r, "Failed to format major/minor device path: %m");
1220 errno = 0;
1221 b = blkid_new_probe_from_filename(node);
1222 if (!b)
ad95aa44 1223 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
15cb6c98
LP
1224
1225 blkid_probe_enable_partitions(b, 1);
1226 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
1227
1228 errno = 0;
1229 r = blkid_do_safeprobe(b);
1230 if (r == -2)
ad95aa44 1231 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
15cb6c98 1232 else if (r == 1)
ad95aa44 1233 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
15cb6c98 1234 else if (r != 0)
ad95aa44 1235 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
15cb6c98
LP
1236
1237 errno = 0;
1238 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
1239 if (r != 0)
ad95aa44 1240 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
15cb6c98
LP
1241 if (streq(v, "gpt")) {
1242
1243 errno = 0;
1244 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
1245 if (r != 0)
ad95aa44 1246 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
15cb6c98
LP
1247 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
1248 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1249 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
ad95aa44 1250 "File system \"%s\" has wrong type for extended boot loader partition.", node);
15cb6c98
LP
1251
1252 errno = 0;
1253 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
1254 if (r != 0)
ad95aa44 1255 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
15cb6c98
LP
1256 r = sd_id128_from_string(v, &uuid);
1257 if (r < 0)
ad95aa44 1258 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
15cb6c98
LP
1259
1260 } else if (streq(v, "dos")) {
1261
1262 errno = 0;
1263 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
1264 if (r != 0)
ad95aa44 1265 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
15cb6c98
LP
1266 if (!streq(v, "0xea"))
1267 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1268 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
ad95aa44 1269 "File system \"%s\" has wrong type for extended boot loader partition.", node);
15cb6c98
LP
1270
1271 } else
1272 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1273 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
ad95aa44 1274 "File system \"%s\" is not on a GPT or DOS partition table.", node);
15cb6c98
LP
1275#endif
1276
15cb6c98
LP
1277 if (ret_uuid)
1278 *ret_uuid = uuid;
1279
1280 return 0;
1281}
1282
85d02102
LP
1283static int verify_xbootldr_udev(
1284 dev_t devid,
1285 bool searching,
1286 sd_id128_t *ret_uuid) {
1287
1288 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
1289 _cleanup_free_ char *node = NULL;
1290 sd_id128_t uuid = SD_ID128_NULL;
1291 const char *v;
1292 int r;
1293
1294 r = device_path_make_major_minor(S_IFBLK, devid, &node);
1295 if (r < 0)
1296 return log_error_errno(r, "Failed to format major/minor device path: %m");
1297
1298 r = sd_device_new_from_devnum(&d, 'b', devid);
1299 if (r < 0)
1300 return log_error_errno(r, "Failed to get device from device number: %m");
1301
1302 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
1303 if (r < 0)
1304 return log_error_errno(r, "Failed to get device property: %m");
1305
1306 if (streq(v, "gpt")) {
1307
1308 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
1309 if (r < 0)
1310 return log_error_errno(r, "Failed to get device property: %m");
1311 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
1312 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1313 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1314 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1315
1316 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
1317 if (r < 0)
1318 return log_error_errno(r, "Failed to get device property: %m");
1319 r = sd_id128_from_string(v, &uuid);
1320 if (r < 0)
1321 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
1322
1323 } else if (streq(v, "dos")) {
1324
1325 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
1326 if (r < 0)
1327 return log_error_errno(r, "Failed to get device property: %m");
1328 if (!streq(v, "0xea"))
1329 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1330 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1331 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1332 } else
1333 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1334 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1335 "File system \"%s\" is not on a GPT or DOS partition table.", node);
1336
1337 if (ret_uuid)
1338 *ret_uuid = uuid;
1339
1340 return 0;
1341}
1342
ad95aa44
LP
1343static int verify_xbootldr(
1344 const char *p,
1345 bool searching,
1346 bool unprivileged_mode,
1347 sd_id128_t *ret_uuid) {
1348
ad95aa44 1349 bool relax_checks;
4a4994b6 1350 dev_t devid;
ad95aa44
LP
1351 int r;
1352
1353 assert(p);
1354
1355 relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
1356
4a4994b6 1357 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
ad95aa44 1358 if (r < 0)
4a4994b6 1359 return r;
ad95aa44 1360
85d02102 1361 if (detect_container() > 0 || relax_checks)
ad95aa44
LP
1362 goto finish;
1363
85d02102 1364 if (unprivileged_mode)
4a4994b6 1365 return verify_xbootldr_udev(devid, searching, ret_uuid);
85d02102 1366 else
4a4994b6 1367 return verify_xbootldr_blkid(devid, searching, ret_uuid);
ad95aa44
LP
1368
1369finish:
1370 if (ret_uuid)
1371 *ret_uuid = SD_ID128_NULL;
1372
1373 return 0;
1374}
1375
15cb6c98
LP
1376int find_xbootldr_and_warn(
1377 const char *path,
1378 bool unprivileged_mode,
1379 char **ret_path,
1380 sd_id128_t *ret_uuid) {
1381
1382 int r;
1383
1384 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
1385
1386 if (path) {
1387 r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
1388 if (r < 0)
1389 return r;
1390
1391 goto found;
1392 }
1393
1394 path = getenv("SYSTEMD_XBOOTLDR_PATH");
1395 if (path) {
1396 if (!path_is_valid(path) || !path_is_absolute(path))
1397 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1398 "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
1399 path);
1400
1401 goto found;
1402 }
1403
1404 r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
1405 if (r >= 0) {
1406 path = "/boot";
1407 goto found;
1408 }
1409 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
1410 return r;
1411
1412 return -ENOKEY;
1413
5caa3167
LP
1414found:
1415 if (ret_path) {
1416 char *c;
1417
1418 c = strdup(path);
1419 if (!c)
af918182
ZJS
1420 return log_oom();
1421
5caa3167 1422 *ret_path = c;
af918182
ZJS
1423 }
1424
5caa3167 1425 return 0;
af918182 1426}