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