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