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