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