]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bootspec.c
Merge pull request #12030 from poettering/condition-memory
[thirdparty/systemd.git] / src / shared / bootspec.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <stdio.h>
4 #include <linux/magic.h>
5 #include <unistd.h>
6
7 #include "sd-device.h"
8 #include "sd-id128.h"
9
10 #include "alloc-util.h"
11 #include "blkid-util.h"
12 #include "bootspec.h"
13 #include "conf-files.h"
14 #include "def.h"
15 #include "device-nodes.h"
16 #include "dirent-util.h"
17 #include "efivars.h"
18 #include "env-file.h"
19 #include "env-util.h"
20 #include "fd-util.h"
21 #include "fileio.h"
22 #include "parse-util.h"
23 #include "path-util.h"
24 #include "pe-header.h"
25 #include "sort-util.h"
26 #include "stat-util.h"
27 #include "string-table.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "unaligned.h"
31 #include "util.h"
32 #include "virt.h"
33
34 static void boot_entry_free(BootEntry *entry) {
35 assert(entry);
36
37 free(entry->id);
38 free(entry->path);
39 free(entry->root);
40 free(entry->title);
41 free(entry->show_title);
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
52 static int boot_entry_load(
53 const char *root,
54 const char *path,
55 BootEntry *entry) {
56
57 _cleanup_(boot_entry_free) BootEntry tmp = {
58 .type = BOOT_ENTRY_CONF,
59 };
60
61 _cleanup_fclose_ FILE *f = NULL;
62 unsigned line = 1;
63 char *b, *c;
64 int r;
65
66 assert(root);
67 assert(path);
68 assert(entry);
69
70 c = endswith_no_case(path, ".conf");
71 if (!c)
72 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path);
73
74 b = basename(path);
75 tmp.id = strndup(b, c - b);
76 if (!tmp.id)
77 return log_oom();
78
79 if (!efi_loader_entry_name_valid(tmp.id))
80 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
81
82 tmp.path = strdup(path);
83 if (!tmp.path)
84 return log_oom();
85
86 tmp.root = strdup(root);
87 if (!tmp.root)
88 return log_oom();
89
90 f = fopen(path, "re");
91 if (!f)
92 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
93
94 for (;;) {
95 _cleanup_free_ char *buf = NULL, *field = NULL;
96 const char *p;
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
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) {
118 log_warning("%s:%u: Bad syntax", path, line);
119 continue;
120 }
121
122 if (streq(field, "title"))
123 r = free_and_strdup(&tmp.title, p);
124 else if (streq(field, "version"))
125 r = free_and_strdup(&tmp.version, p);
126 else if (streq(field, "machine-id"))
127 r = free_and_strdup(&tmp.machine_id, p);
128 else if (streq(field, "architecture"))
129 r = free_and_strdup(&tmp.architecture, p);
130 else if (streq(field, "options"))
131 r = strv_extend(&tmp.options, p);
132 else if (streq(field, "linux"))
133 r = free_and_strdup(&tmp.kernel, p);
134 else if (streq(field, "efi"))
135 r = free_and_strdup(&tmp.efi, p);
136 else if (streq(field, "initrd"))
137 r = strv_extend(&tmp.initrd, p);
138 else if (streq(field, "devicetree"))
139 r = free_and_strdup(&tmp.device_tree, p);
140 else {
141 log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
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
153 void boot_config_free(BootConfig *config) {
154 size_t i;
155
156 assert(config);
157
158 free(config->default_pattern);
159 free(config->timeout);
160 free(config->editor);
161 free(config->auto_entries);
162 free(config->auto_firmware);
163 free(config->console_mode);
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
173 static int boot_loader_read_conf(const char *path, BootConfig *config) {
174 _cleanup_fclose_ FILE *f = NULL;
175 unsigned line = 1;
176 int r;
177
178 assert(path);
179 assert(config);
180
181 f = fopen(path, "re");
182 if (!f) {
183 if (errno == ENOENT)
184 return 0;
185
186 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
187 }
188
189 for (;;) {
190 _cleanup_free_ char *buf = NULL, *field = NULL;
191 const char *p;
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
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) {
213 log_warning("%s:%u: Bad syntax", path, line);
214 continue;
215 }
216
217 if (streq(field, "default"))
218 r = free_and_strdup(&config->default_pattern, p);
219 else if (streq(field, "timeout"))
220 r = free_and_strdup(&config->timeout, p);
221 else if (streq(field, "editor"))
222 r = free_and_strdup(&config->editor, p);
223 else if (streq(field, "auto-entries"))
224 r = free_and_strdup(&config->auto_entries, p);
225 else if (streq(field, "auto-firmware"))
226 r = free_and_strdup(&config->auto_firmware, p);
227 else if (streq(field, "console-mode"))
228 r = free_and_strdup(&config->console_mode, p);
229 else {
230 log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
231 continue;
232 }
233 if (r < 0)
234 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
235 }
236
237 return 1;
238 }
239
240 static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
241 return str_verscmp(a->id, b->id);
242 }
243
244 static int boot_entries_find(
245 const char *root,
246 const char *dir,
247 BootEntry **entries,
248 size_t *n_entries) {
249
250 _cleanup_strv_free_ char **files = NULL;
251 size_t n_allocated = *n_entries;
252 char **f;
253 int r;
254
255 assert(root);
256 assert(dir);
257 assert(entries);
258 assert(n_entries);
259
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) {
265 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
266 return log_oom();
267
268 r = boot_entry_load(root, *f, *entries + *n_entries);
269 if (r < 0)
270 continue;
271
272 (*n_entries) ++;
273 }
274
275 return 0;
276 }
277
278 static 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;
286 _cleanup_(boot_entry_free) BootEntry tmp = {
287 .type = BOOT_ENTRY_UNIFIED,
288 };
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
301 f = fmemopen((void*) osrelease, strlen(osrelease), "r");
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
320 if (!efi_loader_entry_name_valid(tmp.id))
321 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id);
322
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
352 static 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");
428 if ((size_t) n != size)
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
450 static 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;
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) {
495 log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
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) ++;
512 }
513
514 return 0;
515 }
516
517 static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
518 size_t i, j;
519 bool non_unique = false;
520
521 assert(entries || n_entries == 0);
522 assert(arr || n_entries == 0);
523
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
536 static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
537 char *s;
538 size_t i;
539 int r;
540 bool arr[n_entries];
541
542 assert(entries || n_entries == 0);
543
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]) {
577 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
578 if (r < 0)
579 return -ENOMEM;
580
581 free_and_replace(entries[i].show_title, s);
582 }
583
584 return 0;
585 }
586
587 static int boot_entries_select_default(const BootConfig *config) {
588 int i;
589
590 assert(config);
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 }
597
598 if (config->entry_oneshot)
599 for (i = config->n_entries - 1; i >= 0; i--)
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);
603 return i;
604 }
605
606 if (config->entry_default)
607 for (i = config->n_entries - 1; i >= 0; i--)
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);
611 return i;
612 }
613
614 if (config->default_pattern)
615 for (i = config->n_entries - 1; i >= 0; i--)
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);
619 return i;
620 }
621
622 log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
623 return config->n_entries - 1;
624 }
625
626 int boot_entries_load_config(
627 const char *esp_path,
628 const char *xbootldr_path,
629 BootConfig *config) {
630
631 const char *p;
632 int r;
633
634 assert(config);
635
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;
641
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;
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;
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;
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;
663 }
664
665 typesafe_qsort(config->entries, config->n_entries, boot_entry_compare);
666
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
671 if (is_efi_boot()) {
672 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
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 }
678
679 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
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 }
685 }
686
687 config->default_entry = boot_entries_select_default(config);
688 return 0;
689 }
690
691 int 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) {
709 if (access("/run/boot-loader-entries/", F_OK) >= 0)
710 return boot_entries_load_config("/run/boot-loader-entries/", NULL, config);
711
712 if (errno != ENOENT)
713 return log_error_errno(errno,
714 "Failed to determine whether /run/boot-loader-entries/ exists: %m");
715 }
716
717 r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
718 if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */
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
725 return boot_entries_load_config(esp_where, xbootldr_where, config);
726 }
727
728 int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
729
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
739 _cleanup_strv_free_ char **found_by_loader = NULL;
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) {
758 _cleanup_free_ char *c = NULL, *t = NULL;
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
779 if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1))
780 return log_oom();
781
782 config->entries[config->n_entries++] = (BootEntry) {
783 .type = BOOT_ENTRY_LOADER,
784 .id = TAKE_PTR(c),
785 .title = TAKE_PTR(t),
786 };
787 }
788
789 return 0;
790 }
791
792 /********************************************************************************/
793
794 static int verify_esp_blkid(
795 dev_t devid,
796 bool searching,
797 uint32_t *ret_part,
798 uint64_t *ret_pstart,
799 uint64_t *ret_psize,
800 sd_id128_t *ret_uuid) {
801
802 sd_id128_t uuid = SD_ID128_NULL;
803 uint64_t pstart = 0, psize = 0;
804 uint32_t part = 0;
805
806 #if HAVE_BLKID
807 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
808 _cleanup_free_ char *node = NULL;
809 const char *v;
810 int r;
811
812 r = device_path_make_major_minor(S_IFBLK, devid, &node);
813 if (r < 0)
814 return log_error_errno(r, "Failed to format major/minor device path: %m");
815
816 errno = 0;
817 b = blkid_new_probe_from_filename(node);
818 if (!b)
819 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
820
821 blkid_probe_enable_superblocks(b, 1);
822 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
823 blkid_probe_enable_partitions(b, 1);
824 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
825
826 errno = 0;
827 r = blkid_do_safeprobe(b);
828 if (r == -2)
829 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
830 else if (r == 1)
831 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
832 else if (r != 0)
833 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
834
835 errno = 0;
836 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
837 if (r != 0)
838 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node);
839 if (!streq(v, "vfat"))
840 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
841 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
842 "File system \"%s\" is not FAT.", node);
843
844 errno = 0;
845 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
846 if (r != 0)
847 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
848 if (!streq(v, "gpt"))
849 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
850 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
851 "File system \"%s\" is not on a GPT partition table.", node);
852
853 errno = 0;
854 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
855 if (r != 0)
856 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
857 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
858 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
859 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
860 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
861
862 errno = 0;
863 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
864 if (r != 0)
865 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
866 r = sd_id128_from_string(v, &uuid);
867 if (r < 0)
868 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
869
870 errno = 0;
871 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
872 if (r != 0)
873 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node);
874 r = safe_atou32(v, &part);
875 if (r < 0)
876 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
877
878 errno = 0;
879 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
880 if (r != 0)
881 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
882 r = safe_atou64(v, &pstart);
883 if (r < 0)
884 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
885
886 errno = 0;
887 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
888 if (r != 0)
889 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
890 r = safe_atou64(v, &psize);
891 if (r < 0)
892 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
893 #endif
894
895 if (ret_part)
896 *ret_part = part;
897 if (ret_pstart)
898 *ret_pstart = pstart;
899 if (ret_psize)
900 *ret_psize = psize;
901 if (ret_uuid)
902 *ret_uuid = uuid;
903
904 return 0;
905 }
906
907 static int verify_esp_udev(
908 dev_t devid,
909 bool searching,
910 uint32_t *ret_part,
911 uint64_t *ret_pstart,
912 uint64_t *ret_psize,
913 sd_id128_t *ret_uuid) {
914
915 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
916 _cleanup_free_ char *node = NULL;
917 sd_id128_t uuid = SD_ID128_NULL;
918 uint64_t pstart = 0, psize = 0;
919 uint32_t part = 0;
920 const char *v;
921 int r;
922
923 r = device_path_make_major_minor(S_IFBLK, devid, &node);
924 if (r < 0)
925 return log_error_errno(r, "Failed to format major/minor device path: %m");
926
927 r = sd_device_new_from_devnum(&d, 'b', devid);
928 if (r < 0)
929 return log_error_errno(r, "Failed to get device from device number: %m");
930
931 r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
932 if (r < 0)
933 return log_error_errno(r, "Failed to get device property: %m");
934 if (!streq(v, "vfat"))
935 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
936 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
937 "File system \"%s\" is not FAT.", node );
938
939 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
940 if (r < 0)
941 return log_error_errno(r, "Failed to get device property: %m");
942 if (!streq(v, "gpt"))
943 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
944 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
945 "File system \"%s\" is not on a GPT partition table.", node);
946
947 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
948 if (r < 0)
949 return log_error_errno(r, "Failed to get device property: %m");
950 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
951 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
952 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
953 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
954
955 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
956 if (r < 0)
957 return log_error_errno(r, "Failed to get device property: %m");
958 r = sd_id128_from_string(v, &uuid);
959 if (r < 0)
960 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
961
962 r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
963 if (r < 0)
964 return log_error_errno(r, "Failed to get device property: %m");
965 r = safe_atou32(v, &part);
966 if (r < 0)
967 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
968
969 r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
970 if (r < 0)
971 return log_error_errno(r, "Failed to get device property: %m");
972 r = safe_atou64(v, &pstart);
973 if (r < 0)
974 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
975
976 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
977 if (r < 0)
978 return log_error_errno(r, "Failed to get device property: %m");
979 r = safe_atou64(v, &psize);
980 if (r < 0)
981 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
982
983 if (ret_part)
984 *ret_part = part;
985 if (ret_pstart)
986 *ret_pstart = pstart;
987 if (ret_psize)
988 *ret_psize = psize;
989 if (ret_uuid)
990 *ret_uuid = uuid;
991
992 return 0;
993 }
994
995 static int verify_fsroot_dir(
996 const char *path,
997 bool searching,
998 bool unprivileged_mode,
999 dev_t *ret_dev) {
1000
1001 struct stat st, st2;
1002 const char *t2, *trigger;
1003 int r;
1004
1005 assert(path);
1006 assert(ret_dev);
1007
1008 /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
1009 * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
1010 * before stat()ing */
1011 trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
1012 (void) access(trigger, F_OK);
1013
1014 if (stat(path, &st) < 0)
1015 return log_full_errno((searching && errno == ENOENT) ||
1016 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
1017 "Failed to determine block device node of \"%s\": %m", path);
1018
1019 if (major(st.st_dev) == 0)
1020 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1021 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1022 "Block device node of \"%s\" is invalid.", path);
1023
1024 t2 = strjoina(path, "/..");
1025 if (stat(t2, &st2) < 0) {
1026 if (errno != EACCES)
1027 r = -errno;
1028 else {
1029 _cleanup_free_ char *parent = NULL;
1030
1031 /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
1032 * directly instead. It's not as good, due to symlinks and such, but we can't do
1033 * anything better here. */
1034
1035 parent = dirname_malloc(path);
1036 if (!parent)
1037 return log_oom();
1038
1039 if (stat(parent, &st2) < 0)
1040 r = -errno;
1041 else
1042 r = 0;
1043 }
1044
1045 if (r < 0)
1046 return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
1047 "Failed to determine block device node of parent of \"%s\": %m", path);
1048 }
1049
1050 if (st.st_dev == st2.st_dev)
1051 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1052 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1053 "Directory \"%s\" is not the root of the file system.", path);
1054
1055 if (ret_dev)
1056 *ret_dev = st.st_dev;
1057
1058 return 0;
1059 }
1060
1061 static int verify_esp(
1062 const char *p,
1063 bool searching,
1064 bool unprivileged_mode,
1065 uint32_t *ret_part,
1066 uint64_t *ret_pstart,
1067 uint64_t *ret_psize,
1068 sd_id128_t *ret_uuid) {
1069
1070 bool relax_checks;
1071 dev_t devid;
1072 int r;
1073
1074 assert(p);
1075
1076 /* This logs about all errors, except:
1077 *
1078 * -ENOENT → if 'searching' is set, and the dir doesn't exist
1079 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
1080 * -EACESS → if 'unprivileged_mode' is set, and we have trouble acessing the thing
1081 */
1082
1083 relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
1084
1085 /* Non-root user can only check the status, so if an error occured in the following, it does not cause any
1086 * issues. Let's also, silence the error messages. */
1087
1088 if (!relax_checks) {
1089 struct statfs sfs;
1090
1091 if (statfs(p, &sfs) < 0)
1092 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
1093 return log_full_errno((searching && errno == ENOENT) ||
1094 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
1095 "Failed to check file system type of \"%s\": %m", p);
1096
1097 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
1098 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1099 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
1100 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
1101 }
1102
1103 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
1104 if (r < 0)
1105 return r;
1106
1107 /* In a container we don't have access to block devices, skip this part of the verification, we trust
1108 * the container manager set everything up correctly on its own. */
1109 if (detect_container() > 0 || relax_checks)
1110 goto finish;
1111
1112 /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
1113 * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
1114 * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
1115 * however blkid can't work if we have no privileges to access block devices directly, which is why
1116 * we use udev in that case. */
1117 if (unprivileged_mode)
1118 return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
1119 else
1120 return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
1121
1122 finish:
1123 if (ret_part)
1124 *ret_part = 0;
1125 if (ret_pstart)
1126 *ret_pstart = 0;
1127 if (ret_psize)
1128 *ret_psize = 0;
1129 if (ret_uuid)
1130 *ret_uuid = SD_ID128_NULL;
1131
1132 return 0;
1133 }
1134
1135 int find_esp_and_warn(
1136 const char *path,
1137 bool unprivileged_mode,
1138 char **ret_path,
1139 uint32_t *ret_part,
1140 uint64_t *ret_pstart,
1141 uint64_t *ret_psize,
1142 sd_id128_t *ret_uuid) {
1143
1144 int r;
1145
1146 /* This logs about all errors except:
1147 *
1148 * -ENOKEY → when we can't find the partition
1149 * -EACCESS → when unprivileged_mode is true, and we can't access something
1150 */
1151
1152 if (path) {
1153 r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
1154 if (r < 0)
1155 return r;
1156
1157 goto found;
1158 }
1159
1160 path = getenv("SYSTEMD_ESP_PATH");
1161 if (path) {
1162 if (!path_is_valid(path) || !path_is_absolute(path))
1163 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1164 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
1165 path);
1166
1167 /* Note: when the user explicitly configured things with an env var we won't validate the mount
1168 * point. After all we want this to be useful for testing. */
1169 goto found;
1170 }
1171
1172 FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
1173
1174 r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
1175 if (r >= 0)
1176 goto found;
1177 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
1178 return r;
1179 }
1180
1181 /* No logging here */
1182 return -ENOKEY;
1183
1184 found:
1185 if (ret_path) {
1186 char *c;
1187
1188 c = strdup(path);
1189 if (!c)
1190 return log_oom();
1191
1192 *ret_path = c;
1193 }
1194
1195 return 0;
1196 }
1197
1198 static int verify_xbootldr_blkid(
1199 dev_t devid,
1200 bool searching,
1201 sd_id128_t *ret_uuid) {
1202
1203 sd_id128_t uuid = SD_ID128_NULL;
1204
1205 #if HAVE_BLKID
1206 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
1207 _cleanup_free_ char *node = NULL;
1208 const char *v;
1209 int r;
1210
1211 r = device_path_make_major_minor(S_IFBLK, devid, &node);
1212 if (r < 0)
1213 return log_error_errno(r, "Failed to format major/minor device path: %m");
1214 errno = 0;
1215 b = blkid_new_probe_from_filename(node);
1216 if (!b)
1217 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
1218
1219 blkid_probe_enable_partitions(b, 1);
1220 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
1221
1222 errno = 0;
1223 r = blkid_do_safeprobe(b);
1224 if (r == -2)
1225 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
1226 else if (r == 1)
1227 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
1228 else if (r != 0)
1229 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
1230
1231 errno = 0;
1232 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
1233 if (r != 0)
1234 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
1235 if (streq(v, "gpt")) {
1236
1237 errno = 0;
1238 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
1239 if (r != 0)
1240 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
1241 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
1242 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1243 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1244 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1245
1246 errno = 0;
1247 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
1248 if (r != 0)
1249 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
1250 r = sd_id128_from_string(v, &uuid);
1251 if (r < 0)
1252 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
1253
1254 } else if (streq(v, "dos")) {
1255
1256 errno = 0;
1257 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
1258 if (r != 0)
1259 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
1260 if (!streq(v, "0xea"))
1261 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1262 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1263 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1264
1265 } else
1266 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1267 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1268 "File system \"%s\" is not on a GPT or DOS partition table.", node);
1269 #endif
1270
1271 if (ret_uuid)
1272 *ret_uuid = uuid;
1273
1274 return 0;
1275 }
1276
1277 static int verify_xbootldr_udev(
1278 dev_t devid,
1279 bool searching,
1280 sd_id128_t *ret_uuid) {
1281
1282 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
1283 _cleanup_free_ char *node = NULL;
1284 sd_id128_t uuid = SD_ID128_NULL;
1285 const char *v;
1286 int r;
1287
1288 r = device_path_make_major_minor(S_IFBLK, devid, &node);
1289 if (r < 0)
1290 return log_error_errno(r, "Failed to format major/minor device path: %m");
1291
1292 r = sd_device_new_from_devnum(&d, 'b', devid);
1293 if (r < 0)
1294 return log_error_errno(r, "Failed to get device from device number: %m");
1295
1296 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
1297 if (r < 0)
1298 return log_error_errno(r, "Failed to get device property: %m");
1299
1300 if (streq(v, "gpt")) {
1301
1302 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
1303 if (r < 0)
1304 return log_error_errno(r, "Failed to get device property: %m");
1305 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
1306 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1307 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1308 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1309
1310 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
1311 if (r < 0)
1312 return log_error_errno(r, "Failed to get device property: %m");
1313 r = sd_id128_from_string(v, &uuid);
1314 if (r < 0)
1315 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
1316
1317 } else if (streq(v, "dos")) {
1318
1319 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
1320 if (r < 0)
1321 return log_error_errno(r, "Failed to get device property: %m");
1322 if (!streq(v, "0xea"))
1323 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1324 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1325 "File system \"%s\" has wrong type for extended boot loader partition.", node);
1326 } else
1327 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
1328 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
1329 "File system \"%s\" is not on a GPT or DOS partition table.", node);
1330
1331 if (ret_uuid)
1332 *ret_uuid = uuid;
1333
1334 return 0;
1335 }
1336
1337 static int verify_xbootldr(
1338 const char *p,
1339 bool searching,
1340 bool unprivileged_mode,
1341 sd_id128_t *ret_uuid) {
1342
1343 bool relax_checks;
1344 dev_t devid;
1345 int r;
1346
1347 assert(p);
1348
1349 relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
1350
1351 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
1352 if (r < 0)
1353 return r;
1354
1355 if (detect_container() > 0 || relax_checks)
1356 goto finish;
1357
1358 if (unprivileged_mode)
1359 return verify_xbootldr_udev(devid, searching, ret_uuid);
1360 else
1361 return verify_xbootldr_blkid(devid, searching, ret_uuid);
1362
1363 finish:
1364 if (ret_uuid)
1365 *ret_uuid = SD_ID128_NULL;
1366
1367 return 0;
1368 }
1369
1370 int find_xbootldr_and_warn(
1371 const char *path,
1372 bool unprivileged_mode,
1373 char **ret_path,
1374 sd_id128_t *ret_uuid) {
1375
1376 int r;
1377
1378 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
1379
1380 if (path) {
1381 r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
1382 if (r < 0)
1383 return r;
1384
1385 goto found;
1386 }
1387
1388 path = getenv("SYSTEMD_XBOOTLDR_PATH");
1389 if (path) {
1390 if (!path_is_valid(path) || !path_is_absolute(path))
1391 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1392 "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
1393 path);
1394
1395 goto found;
1396 }
1397
1398 r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
1399 if (r >= 0) {
1400 path = "/boot";
1401 goto found;
1402 }
1403 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
1404 return r;
1405
1406 return -ENOKEY;
1407
1408 found:
1409 if (ret_path) {
1410 char *c;
1411
1412 c = strdup(path);
1413 if (!c)
1414 return log_oom();
1415
1416 *ret_path = c;
1417 }
1418
1419 return 0;
1420 }
1421
1422 static const char* const boot_entry_type_table[_BOOT_ENTRY_MAX] = {
1423 [BOOT_ENTRY_CONF] = "conf",
1424 [BOOT_ENTRY_UNIFIED] = "unified",
1425 [BOOT_ENTRY_LOADER] = "loader",
1426 };
1427
1428 DEFINE_STRING_TABLE_LOOKUP(boot_entry_type, BootEntryType);