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