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