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