]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bootspec.c
Handle d_type == DT_UNKNOWN correctly
[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
36b12282 260 r = conf_files_list(&files, ".conf", NULL, 0, dir);
7e87c7d9
ZJS
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
28e68bb2 478 dirent_ensure_type(d, de);
5e146a75
LP
479 if (!dirent_is_file(de))
480 continue;
481
482 if (!endswith_no_case(de->d_name, ".efi"))
483 continue;
484
485 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
486 return log_oom();
487
488 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
489 if (fd < 0) {
490 log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", dir, de->d_name);
491 continue;
492 }
493
494 r = fd_verify_regular(fd);
495 if (r < 0) {
dba33c4a 496 log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
5e146a75
LP
497 continue;
498 }
499
500 r = find_sections(fd, &osrelease, &cmdline);
501 if (r < 0)
502 continue;
503
504 j = path_join(dir, de->d_name);
505 if (!j)
506 return log_oom();
507
508 r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries);
509 if (r < 0)
510 continue;
511
512 (*n_entries) ++;
5e146a75
LP
513 }
514
5e146a75
LP
515 return 0;
516}
517
64f05708 518static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
da6053d0 519 size_t i, j;
64f05708
ZJS
520 bool non_unique = false;
521
4fe2ba0e
LP
522 assert(entries || n_entries == 0);
523 assert(arr || n_entries == 0);
524
64f05708
ZJS
525 for (i = 0; i < n_entries; i++)
526 arr[i] = false;
527
528 for (i = 0; i < n_entries; i++)
529 for (j = 0; j < n_entries; j++)
530 if (i != j && streq(boot_entry_title(entries + i),
531 boot_entry_title(entries + j)))
532 non_unique = arr[i] = arr[j] = true;
533
534 return non_unique;
535}
536
537static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
538 char *s;
da6053d0 539 size_t i;
64f05708
ZJS
540 int r;
541 bool arr[n_entries];
542
4fe2ba0e
LP
543 assert(entries || n_entries == 0);
544
64f05708
ZJS
545 /* Find _all_ non-unique titles */
546 if (!find_nonunique(entries, n_entries, arr))
547 return 0;
548
549 /* Add version to non-unique titles */
550 for (i = 0; i < n_entries; i++)
551 if (arr[i] && entries[i].version) {
552 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
553 if (r < 0)
554 return -ENOMEM;
555
556 free_and_replace(entries[i].show_title, s);
557 }
558
559 if (!find_nonunique(entries, n_entries, arr))
560 return 0;
561
562 /* Add machine-id to non-unique titles */
563 for (i = 0; i < n_entries; i++)
564 if (arr[i] && entries[i].machine_id) {
565 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
566 if (r < 0)
567 return -ENOMEM;
568
569 free_and_replace(entries[i].show_title, s);
570 }
571
572 if (!find_nonunique(entries, n_entries, arr))
573 return 0;
574
575 /* Add file name to non-unique titles */
576 for (i = 0; i < n_entries; i++)
577 if (arr[i]) {
12580bc3 578 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
64f05708
ZJS
579 if (r < 0)
580 return -ENOMEM;
581
582 free_and_replace(entries[i].show_title, s);
583 }
584
585 return 0;
586}
587
ad1afd60 588static int boot_entries_select_default(const BootConfig *config) {
7e87c7d9
ZJS
589 int i;
590
4fe2ba0e 591 assert(config);
388d2993
ZJS
592 assert(config->entries || config->n_entries == 0);
593
594 if (config->n_entries == 0) {
595 log_debug("Found no default boot entry :(");
596 return -1; /* -1 means "no default" */
597 }
4fe2ba0e 598
7e87c7d9
ZJS
599 if (config->entry_oneshot)
600 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
601 if (streq(config->entry_oneshot, config->entries[i].id)) {
602 log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
603 config->entries[i].id);
7e87c7d9
ZJS
604 return i;
605 }
606
607 if (config->entry_default)
608 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
609 if (streq(config->entry_default, config->entries[i].id)) {
610 log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
611 config->entries[i].id);
7e87c7d9
ZJS
612 return i;
613 }
614
615 if (config->default_pattern)
616 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
617 if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
618 log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
619 config->entries[i].id, config->default_pattern);
7e87c7d9
ZJS
620 return i;
621 }
622
388d2993
ZJS
623 log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
624 return config->n_entries - 1;
7e87c7d9
ZJS
625}
626
a2f8664e
LP
627int boot_entries_load_config(
628 const char *esp_path,
629 const char *xbootldr_path,
630 BootConfig *config) {
631
7e87c7d9
ZJS
632 const char *p;
633 int r;
634
4fe2ba0e
LP
635 assert(config);
636
a2f8664e
LP
637 if (esp_path) {
638 p = strjoina(esp_path, "/loader/loader.conf");
639 r = boot_loader_read_conf(p, config);
640 if (r < 0)
641 return r;
7e87c7d9 642
a2f8664e
LP
643 p = strjoina(esp_path, "/loader/entries");
644 r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries);
645 if (r < 0)
646 return r;
5e146a75
LP
647
648 p = strjoina(esp_path, "/EFI/Linux/");
649 r = boot_entries_find_unified(esp_path, p, &config->entries, &config->n_entries);
650 if (r < 0)
651 return r;
a2f8664e
LP
652 }
653
654 if (xbootldr_path) {
655 p = strjoina(xbootldr_path, "/loader/entries");
656 r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries);
657 if (r < 0)
658 return r;
5e146a75
LP
659
660 p = strjoina(xbootldr_path, "/EFI/Linux/");
661 r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries);
662 if (r < 0)
663 return r;
a2f8664e 664 }
7e87c7d9 665
dd2bf34c
LP
666 typesafe_qsort(config->entries, config->n_entries, boot_entry_compare);
667
64f05708
ZJS
668 r = boot_entries_uniquify(config->entries, config->n_entries);
669 if (r < 0)
670 return log_error_errno(r, "Failed to uniquify boot entries: %m");
671
9c4a6c13
LP
672 if (is_efi_boot()) {
673 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
bd29f9de
ZJS
674 if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
675 log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m");
676 if (r == -ENOMEM)
677 return r;
678 }
9c4a6c13
LP
679
680 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
bd29f9de
ZJS
681 if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) {
682 log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m");
683 if (r == -ENOMEM)
684 return r;
685 }
9c4a6c13 686 }
7e87c7d9
ZJS
687
688 config->default_entry = boot_entries_select_default(config);
689 return 0;
690}
af918182 691
eea4ce1e
LP
692int boot_entries_load_config_auto(
693 const char *override_esp_path,
694 const char *override_xbootldr_path,
695 BootConfig *config) {
696
697 _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
698 int r;
699
700 assert(config);
701
702 /* This function is similar to boot_entries_load_config(), however we automatically search for the
703 * ESP and the XBOOTLDR partition unless it is explicitly specified. Also, if the user did not pass
704 * an ESP or XBOOTLDR path directly, let's see if /run/boot-loader-entries/ exists. If so, let's
705 * read data from there, as if it was an ESP (i.e. loading both entries and loader.conf data from
706 * it). This allows other boot loaders to pass boot loader entry information to our tools if they
707 * want to. */
708
709 if (!override_esp_path && !override_xbootldr_path) {
f40999f8
ZJS
710 if (access("/run/boot-loader-entries/", F_OK) >= 0)
711 return boot_entries_load_config("/run/boot-loader-entries/", NULL, config);
eea4ce1e 712
f40999f8
ZJS
713 if (errno != ENOENT)
714 return log_error_errno(errno,
715 "Failed to determine whether /run/boot-loader-entries/ exists: %m");
eea4ce1e
LP
716 }
717
718 r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
cc5957dc 719 if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
eea4ce1e
LP
720 return r;
721
722 r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL);
723 if (r < 0 && r != -ENOKEY)
724 return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
725
f40999f8 726 return boot_entries_load_config(esp_where, xbootldr_where, config);
eea4ce1e
LP
727}
728
528fcf8d 729#if ENABLE_EFI
93f14ce2 730int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
93f14ce2
LP
731 static const char * const title_table[] = {
732 /* Pretty names for a few well-known automatically discovered entries. */
733 "auto-osx", "macOS",
734 "auto-windows", "Windows Boot Manager",
735 "auto-efi-shell", "EFI Shell",
736 "auto-efi-default", "EFI Default Loader",
737 "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface",
738 };
739
18268197 740 _cleanup_strv_free_ char **found_by_loader = NULL;
93f14ce2
LP
741 size_t n_allocated;
742 char **i;
743 int r;
744
745 assert(config);
746
747 /* Let's add the entries discovered by the boot loader to the end of our list, unless they are
748 * already included there. */
749
750 r = efi_loader_get_entries(&found_by_loader);
751 if (IN_SET(r, -ENOENT, -EOPNOTSUPP))
752 return log_debug_errno(r, "Boot loader reported no entries.");
753 if (r < 0)
754 return log_error_errno(r, "Failed to determine entries reported by boot loader: %m");
755
756 n_allocated = config->n_entries;
757
758 STRV_FOREACH(i, found_by_loader) {
ce4c4f81 759 _cleanup_free_ char *c = NULL, *t = NULL, *p = NULL;
93f14ce2
LP
760 char **a, **b;
761
762 if (boot_config_has_entry(config, *i))
763 continue;
764
765 if (only_auto && !startswith(*i, "auto-"))
766 continue;
767
768 c = strdup(*i);
769 if (!c)
770 return log_oom();
771
772 STRV_FOREACH_PAIR(a, b, (char**) title_table)
773 if (streq(*a, *i)) {
774 t = strdup(*b);
775 if (!t)
776 return log_oom();
777 break;
778 }
779
ce4c4f81
ZJS
780 p = efi_variable_path(EFI_VENDOR_LOADER, "LoaderEntries");
781 if (!p)
782 return log_oom();
783
93f14ce2
LP
784 if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1))
785 return log_oom();
786
787 config->entries[config->n_entries++] = (BootEntry) {
788 .type = BOOT_ENTRY_LOADER,
789 .id = TAKE_PTR(c),
790 .title = TAKE_PTR(t),
ce4c4f81 791 .path = TAKE_PTR(p),
93f14ce2
LP
792 };
793 }
794
795 return 0;
796}
528fcf8d 797#endif
93f14ce2 798
af918182
ZJS
799/********************************************************************************/
800
575d4370
LP
801static int verify_esp_blkid(
802 dev_t devid,
5caa3167 803 bool searching,
af918182
ZJS
804 uint32_t *ret_part,
805 uint64_t *ret_pstart,
806 uint64_t *ret_psize,
807 sd_id128_t *ret_uuid) {
575d4370
LP
808
809 sd_id128_t uuid = SD_ID128_NULL;
810 uint64_t pstart = 0, psize = 0;
811 uint32_t part = 0;
812
4e066f7f 813#if HAVE_BLKID
8e766630 814 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
54b22b26 815 _cleanup_free_ char *node = NULL;
4e066f7f 816 const char *v;
af918182
ZJS
817 int r;
818
575d4370 819 r = device_path_make_major_minor(S_IFBLK, devid, &node);
54b22b26
LP
820 if (r < 0)
821 return log_error_errno(r, "Failed to format major/minor device path: %m");
575d4370 822
af918182 823 errno = 0;
54b22b26 824 b = blkid_new_probe_from_filename(node);
af918182 825 if (!b)
575d4370 826 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
af918182
ZJS
827
828 blkid_probe_enable_superblocks(b, 1);
829 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
830 blkid_probe_enable_partitions(b, 1);
831 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
832
833 errno = 0;
834 r = blkid_do_safeprobe(b);
a7afbd60 835 if (r == -2)
575d4370 836 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
a7afbd60 837 else if (r == 1)
575d4370 838 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
a7afbd60 839 else if (r != 0)
575d4370 840 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
af918182
ZJS
841
842 errno = 0;
843 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
844 if (r != 0)
575d4370 845 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node);
a7afbd60
LP
846 if (!streq(v, "vfat"))
847 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
848 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
575d4370 849 "File system \"%s\" is not FAT.", node);
af918182
ZJS
850
851 errno = 0;
852 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
853 if (r != 0)
575d4370 854 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
a7afbd60
LP
855 if (!streq(v, "gpt"))
856 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
857 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
575d4370 858 "File system \"%s\" is not on a GPT partition table.", node);
af918182
ZJS
859
860 errno = 0;
861 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
862 if (r != 0)
575d4370 863 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
a7afbd60
LP
864 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
865 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
866 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
575d4370 867 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
af918182
ZJS
868
869 errno = 0;
870 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
871 if (r != 0)
575d4370 872 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
af918182 873 r = sd_id128_from_string(v, &uuid);
a7afbd60 874 if (r < 0)
575d4370 875 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
af918182
ZJS
876
877 errno = 0;
878 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
879 if (r != 0)
28b77ab2 880 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
af918182
ZJS
881 r = safe_atou32(v, &part);
882 if (r < 0)
883 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
884
885 errno = 0;
886 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
887 if (r != 0)
575d4370 888 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
af918182
ZJS
889 r = safe_atou64(v, &pstart);
890 if (r < 0)
891 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
892
893 errno = 0;
894 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
895 if (r != 0)
575d4370 896 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
af918182
ZJS
897 r = safe_atou64(v, &psize);
898 if (r < 0)
899 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
4e066f7f 900#endif
af918182 901
af918182
ZJS
902 if (ret_part)
903 *ret_part = part;
904 if (ret_pstart)
905 *ret_pstart = pstart;
906 if (ret_psize)
907 *ret_psize = psize;
908 if (ret_uuid)
909 *ret_uuid = uuid;
910
911 return 0;
912}
913
cedb9eec
LP
914static int verify_esp_udev(
915 dev_t devid,
916 bool searching,
917 uint32_t *ret_part,
918 uint64_t *ret_pstart,
919 uint64_t *ret_psize,
920 sd_id128_t *ret_uuid) {
921
922 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
923 _cleanup_free_ char *node = NULL;
924 sd_id128_t uuid = SD_ID128_NULL;
925 uint64_t pstart = 0, psize = 0;
926 uint32_t part = 0;
927 const char *v;
928 int r;
929
930 r = device_path_make_major_minor(S_IFBLK, devid, &node);
931 if (r < 0)
932 return log_error_errno(r, "Failed to format major/minor device path: %m");
933
934 r = sd_device_new_from_devnum(&d, 'b', devid);
935 if (r < 0)
936 return log_error_errno(r, "Failed to get device from device number: %m");
937
938 r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
939 if (r < 0)
940 return log_error_errno(r, "Failed to get device property: %m");
941 if (!streq(v, "vfat"))
942 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
943 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
944 "File system \"%s\" is not FAT.", node );
945
946 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
947 if (r < 0)
948 return log_error_errno(r, "Failed to get device property: %m");
949 if (!streq(v, "gpt"))
950 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
951 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
952 "File system \"%s\" is not on a GPT partition table.", node);
953
954 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
955 if (r < 0)
956 return log_error_errno(r, "Failed to get device property: %m");
957 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
958 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
959 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
960 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
961
962 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
963 if (r < 0)
964 return log_error_errno(r, "Failed to get device property: %m");
965 r = sd_id128_from_string(v, &uuid);
966 if (r < 0)
967 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
968
969 r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
970 if (r < 0)
971 return log_error_errno(r, "Failed to get device property: %m");
972 r = safe_atou32(v, &part);
973 if (r < 0)
974 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
975
976 r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
977 if (r < 0)
978 return log_error_errno(r, "Failed to get device property: %m");
979 r = safe_atou64(v, &pstart);
980 if (r < 0)
981 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
982
983 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
984 if (r < 0)
985 return log_error_errno(r, "Failed to get device property: %m");
986 r = safe_atou64(v, &psize);
987 if (r < 0)
988 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
989
990 if (ret_part)
991 *ret_part = part;
992 if (ret_pstart)
993 *ret_pstart = pstart;
994 if (ret_psize)
995 *ret_psize = psize;
996 if (ret_uuid)
997 *ret_uuid = uuid;
998
999 return 0;
1000}
1001
18ae9ef1
LP
1002static int verify_fsroot_dir(
1003 const char *path,
1004 bool searching,
1005 bool unprivileged_mode,
1006 dev_t *ret_dev) {
1007
1008 struct stat st, st2;
8312d694 1009 const char *t2, *trigger;
18ae9ef1
LP
1010 int r;
1011
1012 assert(path);
1013 assert(ret_dev);
1014
8312d694
LP
1015 /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
1016 * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
1017 * before stat()ing */
1018 trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
1019 (void) access(trigger, F_OK);
1020
18ae9ef1
LP
1021 if (stat(path, &st) < 0)
1022 return log_full_errno((searching && errno == ENOENT) ||
1023 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
1024 "Failed to determine block device node of \"%s\": %m", path);
1025
1026 if (major(st.st_dev) == 0)
1027 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1028 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1029 "Block device node of \"%s\" is invalid.", path);
1030
1031 t2 = strjoina(path, "/..");
1032 if (stat(t2, &st2) < 0) {
1033 if (errno != EACCES)
1034 r = -errno;
1035 else {
1036 _cleanup_free_ char *parent = NULL;
1037
1038 /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
1039 * directly instead. It's not as good, due to symlinks and such, but we can't do
1040 * anything better here. */
1041
1042 parent = dirname_malloc(path);
1043 if (!parent)
1044 return log_oom();
1045
1046 if (stat(parent, &st2) < 0)
1047 r = -errno;
1048 else
1049 r = 0;
1050 }
1051
1052 if (r < 0)
1053 return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
1054 "Failed to determine block device node of parent of \"%s\": %m", path);
1055 }
1056
1057 if (st.st_dev == st2.st_dev)
1058 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1059 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1060 "Directory \"%s\" is not the root of the file system.", path);
1061
1062 if (ret_dev)
1063 *ret_dev = st.st_dev;
1064
1065 return 0;
1066}
1067
575d4370
LP
1068static int verify_esp(
1069 const char *p,
1070 bool searching,
1071 bool unprivileged_mode,
1072 uint32_t *ret_part,
1073 uint64_t *ret_pstart,
1074 uint64_t *ret_psize,
1075 sd_id128_t *ret_uuid) {
1076
575d4370 1077 bool relax_checks;
18ae9ef1 1078 dev_t devid;
575d4370
LP
1079 int r;
1080
1081 assert(p);
1082
1083 /* This logs about all errors, except:
1084 *
1085 * -ENOENT → if 'searching' is set, and the dir doesn't exist
1086 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
5238e957 1087 * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
575d4370
LP
1088 */
1089
1090 relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
1091
5238e957 1092 /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
575d4370
LP
1093 * issues. Let's also, silence the error messages. */
1094
1095 if (!relax_checks) {
18ae9ef1
LP
1096 struct statfs sfs;
1097
575d4370
LP
1098 if (statfs(p, &sfs) < 0)
1099 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
1100 return log_full_errno((searching && errno == ENOENT) ||
1101 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
1102 "Failed to check file system type of \"%s\": %m", p);
1103
1104 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
1105 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1106 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1107 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
1108 }
1109
18ae9ef1 1110 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
575d4370 1111 if (r < 0)
18ae9ef1 1112 return r;
575d4370
LP
1113
1114 /* In a container we don't have access to block devices, skip this part of the verification, we trust
cedb9eec
LP
1115 * the container manager set everything up correctly on its own. */
1116 if (detect_container() > 0 || relax_checks)
575d4370
LP
1117 goto finish;
1118
cedb9eec
LP
1119 /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
1120 * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
1121 * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
1122 * however blkid can't work if we have no privileges to access block devices directly, which is why
1123 * we use udev in that case. */
1124 if (unprivileged_mode)
18ae9ef1 1125 return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
cedb9eec 1126 else
18ae9ef1 1127 return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
575d4370
LP
1128
1129finish:
1130 if (ret_part)
1131 *ret_part = 0;
1132 if (ret_pstart)
1133 *ret_pstart = 0;
1134 if (ret_psize)
1135 *ret_psize = 0;
1136 if (ret_uuid)
1137 *ret_uuid = SD_ID128_NULL;
1138
1139 return 0;
1140}
1141
5caa3167
LP
1142int find_esp_and_warn(
1143 const char *path,
1144 bool unprivileged_mode,
1145 char **ret_path,
1146 uint32_t *ret_part,
1147 uint64_t *ret_pstart,
1148 uint64_t *ret_psize,
1149 sd_id128_t *ret_uuid) {
af918182 1150
af918182
ZJS
1151 int r;
1152
5caa3167
LP
1153 /* This logs about all errors except:
1154 *
1155 * -ENOKEY → when we can't find the partition
1156 * -EACCESS → when unprivileged_mode is true, and we can't access something
1157 */
af918182 1158
5caa3167
LP
1159 if (path) {
1160 r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
af918182
ZJS
1161 if (r < 0)
1162 return r;
1163
5caa3167
LP
1164 goto found;
1165 }
1166
cc7a0bfa
LP
1167 path = getenv("SYSTEMD_ESP_PATH");
1168 if (path) {
baaa35ad
ZJS
1169 if (!path_is_valid(path) || !path_is_absolute(path))
1170 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1171 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
1172 path);
cc7a0bfa
LP
1173
1174 /* Note: when the user explicitly configured things with an env var we won't validate the mount
1175 * point. After all we want this to be useful for testing. */
1176 goto found;
1177 }
1178
5caa3167
LP
1179 FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
1180
1181 r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
1182 if (r >= 0)
1183 goto found;
1184 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
1185 return r;
1186 }
1187
1188 /* No logging here */
1189 return -ENOKEY;
1190
15cb6c98
LP
1191found:
1192 if (ret_path) {
1193 char *c;
1194
1195 c = strdup(path);
1196 if (!c)
1197 return log_oom();
1198
1199 *ret_path = c;
1200 }
1201
1202 return 0;
1203}
1204
ad95aa44
LP
1205static int verify_xbootldr_blkid(
1206 dev_t devid,
15cb6c98 1207 bool searching,
15cb6c98 1208 sd_id128_t *ret_uuid) {
ad95aa44
LP
1209
1210 sd_id128_t uuid = SD_ID128_NULL;
1211
15cb6c98
LP
1212#if HAVE_BLKID
1213 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
1214 _cleanup_free_ char *node = NULL;
1215 const char *v;
15cb6c98
LP
1216 int r;
1217
ad95aa44 1218 r = device_path_make_major_minor(S_IFBLK, devid, &node);
15cb6c98
LP
1219 if (r < 0)
1220 return log_error_errno(r, "Failed to format major/minor device path: %m");
1221 errno = 0;
1222 b = blkid_new_probe_from_filename(node);
1223 if (!b)
ad95aa44 1224 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
15cb6c98
LP
1225
1226 blkid_probe_enable_partitions(b, 1);
1227 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
1228
1229 errno = 0;
1230 r = blkid_do_safeprobe(b);
1231 if (r == -2)
ad95aa44 1232 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
15cb6c98 1233 else if (r == 1)
ad95aa44 1234 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
15cb6c98 1235 else if (r != 0)
ad95aa44 1236 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
15cb6c98
LP
1237
1238 errno = 0;
1239 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
1240 if (r != 0)
ad95aa44 1241 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
15cb6c98
LP
1242 if (streq(v, "gpt")) {
1243
1244 errno = 0;
1245 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
1246 if (r != 0)
ad95aa44 1247 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
15cb6c98
LP
1248 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
1249 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1250 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
ad95aa44 1251 "File system \"%s\" has wrong type for extended boot loader partition.", node);
15cb6c98
LP
1252
1253 errno = 0;
1254 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
1255 if (r != 0)
ad95aa44 1256 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
15cb6c98
LP
1257 r = sd_id128_from_string(v, &uuid);
1258 if (r < 0)
ad95aa44 1259 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
15cb6c98
LP
1260
1261 } else if (streq(v, "dos")) {
1262
1263 errno = 0;
1264 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
1265 if (r != 0)
ad95aa44 1266 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
15cb6c98
LP
1267 if (!streq(v, "0xea"))
1268 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1269 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
ad95aa44 1270 "File system \"%s\" has wrong type for extended boot loader partition.", node);
15cb6c98
LP
1271
1272 } else
1273 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1274 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
ad95aa44 1275 "File system \"%s\" is not on a GPT or DOS partition table.", node);
15cb6c98
LP
1276#endif
1277
15cb6c98
LP
1278 if (ret_uuid)
1279 *ret_uuid = uuid;
1280
1281 return 0;
1282}
1283
85d02102
LP
1284static int verify_xbootldr_udev(
1285 dev_t devid,
1286 bool searching,
1287 sd_id128_t *ret_uuid) {
1288
1289 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
1290 _cleanup_free_ char *node = NULL;
1291 sd_id128_t uuid = SD_ID128_NULL;
1292 const char *v;
1293 int r;
1294
1295 r = device_path_make_major_minor(S_IFBLK, devid, &node);
1296 if (r < 0)
1297 return log_error_errno(r, "Failed to format major/minor device path: %m");
1298
1299 r = sd_device_new_from_devnum(&d, 'b', devid);
1300 if (r < 0)
1301 return log_error_errno(r, "Failed to get device from device number: %m");
1302
1303 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
1304 if (r < 0)
1305 return log_error_errno(r, "Failed to get device property: %m");
1306
1307 if (streq(v, "gpt")) {
1308
1309 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
1310 if (r < 0)
1311 return log_error_errno(r, "Failed to get device property: %m");
1312 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
1313 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1314 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1315 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1316
1317 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
1318 if (r < 0)
1319 return log_error_errno(r, "Failed to get device property: %m");
1320 r = sd_id128_from_string(v, &uuid);
1321 if (r < 0)
1322 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
1323
1324 } else if (streq(v, "dos")) {
1325
1326 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
1327 if (r < 0)
1328 return log_error_errno(r, "Failed to get device property: %m");
1329 if (!streq(v, "0xea"))
1330 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1331 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1332 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1333 } else
1334 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1335 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1336 "File system \"%s\" is not on a GPT or DOS partition table.", node);
1337
1338 if (ret_uuid)
1339 *ret_uuid = uuid;
1340
1341 return 0;
1342}
1343
ad95aa44
LP
1344static int verify_xbootldr(
1345 const char *p,
1346 bool searching,
1347 bool unprivileged_mode,
1348 sd_id128_t *ret_uuid) {
1349
ad95aa44 1350 bool relax_checks;
4a4994b6 1351 dev_t devid;
ad95aa44
LP
1352 int r;
1353
1354 assert(p);
1355
1356 relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
1357
4a4994b6 1358 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
ad95aa44 1359 if (r < 0)
4a4994b6 1360 return r;
ad95aa44 1361
85d02102 1362 if (detect_container() > 0 || relax_checks)
ad95aa44
LP
1363 goto finish;
1364
85d02102 1365 if (unprivileged_mode)
4a4994b6 1366 return verify_xbootldr_udev(devid, searching, ret_uuid);
85d02102 1367 else
4a4994b6 1368 return verify_xbootldr_blkid(devid, searching, ret_uuid);
ad95aa44
LP
1369
1370finish:
1371 if (ret_uuid)
1372 *ret_uuid = SD_ID128_NULL;
1373
1374 return 0;
1375}
1376
15cb6c98
LP
1377int find_xbootldr_and_warn(
1378 const char *path,
1379 bool unprivileged_mode,
1380 char **ret_path,
1381 sd_id128_t *ret_uuid) {
1382
1383 int r;
1384
1385 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
1386
1387 if (path) {
1388 r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
1389 if (r < 0)
1390 return r;
1391
1392 goto found;
1393 }
1394
1395 path = getenv("SYSTEMD_XBOOTLDR_PATH");
1396 if (path) {
1397 if (!path_is_valid(path) || !path_is_absolute(path))
1398 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1399 "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
1400 path);
1401
1402 goto found;
1403 }
1404
1405 r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
1406 if (r >= 0) {
1407 path = "/boot";
1408 goto found;
1409 }
1410 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
1411 return r;
1412
1413 return -ENOKEY;
1414
5caa3167
LP
1415found:
1416 if (ret_path) {
1417 char *c;
1418
1419 c = strdup(path);
1420 if (!c)
af918182
ZJS
1421 return log_oom();
1422
5caa3167 1423 *ret_path = c;
af918182
ZJS
1424 }
1425
5caa3167 1426 return 0;
af918182 1427}