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