]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bootspec.c
bootspec: add comment explaining verify_esp() return codes
[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-id128.h"
7
8 #include "alloc-util.h"
9 #include "blkid-util.h"
10 #include "bootspec.h"
11 #include "conf-files.h"
12 #include "def.h"
13 #include "device-nodes.h"
14 #include "efivars.h"
15 #include "env-util.h"
16 #include "fd-util.h"
17 #include "fileio.h"
18 #include "parse-util.h"
19 #include "path-util.h"
20 #include "stat-util.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "virt.h"
24
25 static void boot_entry_free(BootEntry *entry) {
26 assert(entry);
27
28 free(entry->id);
29 free(entry->path);
30 free(entry->root);
31 free(entry->title);
32 free(entry->show_title);
33 free(entry->version);
34 free(entry->machine_id);
35 free(entry->architecture);
36 strv_free(entry->options);
37 free(entry->kernel);
38 free(entry->efi);
39 strv_free(entry->initrd);
40 free(entry->device_tree);
41 }
42
43 static int boot_entry_load(
44 const char *root,
45 const char *path,
46 BootEntry *entry) {
47
48 _cleanup_(boot_entry_free) BootEntry tmp = {};
49 _cleanup_fclose_ FILE *f = NULL;
50 unsigned line = 1;
51 char *b, *c;
52 int r;
53
54 assert(root);
55 assert(path);
56 assert(entry);
57
58 c = endswith_no_case(path, ".conf");
59 if (!c)
60 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
61
62 b = basename(path);
63 tmp.id = strndup(b, c - b);
64 if (!tmp.id)
65 return log_oom();
66
67 tmp.path = strdup(path);
68 if (!tmp.path)
69 return log_oom();
70
71 tmp.root = strdup(root);
72 if (!tmp.root)
73 return log_oom();
74
75 f = fopen(path, "re");
76 if (!f)
77 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
78
79 for (;;) {
80 _cleanup_free_ char *buf = NULL, *field = NULL;
81 const char *p;
82
83 r = read_line(f, LONG_LINE_MAX, &buf);
84 if (r == 0)
85 break;
86 if (r == -ENOBUFS)
87 return log_error_errno(r, "%s:%u: Line too long", path, line);
88 if (r < 0)
89 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
90
91 line++;
92
93 if (IN_SET(*strstrip(buf), '#', '\0'))
94 continue;
95
96 p = buf;
97 r = extract_first_word(&p, &field, " \t", 0);
98 if (r < 0) {
99 log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
100 continue;
101 }
102 if (r == 0) {
103 log_warning("%s:%u: Bad syntax", path, line);
104 continue;
105 }
106
107 if (streq(field, "title"))
108 r = free_and_strdup(&tmp.title, p);
109 else if (streq(field, "version"))
110 r = free_and_strdup(&tmp.version, p);
111 else if (streq(field, "machine-id"))
112 r = free_and_strdup(&tmp.machine_id, p);
113 else if (streq(field, "architecture"))
114 r = free_and_strdup(&tmp.architecture, p);
115 else if (streq(field, "options"))
116 r = strv_extend(&tmp.options, p);
117 else if (streq(field, "linux"))
118 r = free_and_strdup(&tmp.kernel, p);
119 else if (streq(field, "efi"))
120 r = free_and_strdup(&tmp.efi, p);
121 else if (streq(field, "initrd"))
122 r = strv_extend(&tmp.initrd, p);
123 else if (streq(field, "devicetree"))
124 r = free_and_strdup(&tmp.device_tree, p);
125 else {
126 log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
127 continue;
128 }
129 if (r < 0)
130 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
131 }
132
133 *entry = tmp;
134 tmp = (BootEntry) {};
135 return 0;
136 }
137
138 void boot_config_free(BootConfig *config) {
139 size_t i;
140
141 assert(config);
142
143 free(config->default_pattern);
144 free(config->timeout);
145 free(config->editor);
146 free(config->auto_entries);
147 free(config->auto_firmware);
148
149 free(config->entry_oneshot);
150 free(config->entry_default);
151
152 for (i = 0; i < config->n_entries; i++)
153 boot_entry_free(config->entries + i);
154 free(config->entries);
155 }
156
157 static int boot_loader_read_conf(const char *path, BootConfig *config) {
158 _cleanup_fclose_ FILE *f = NULL;
159 unsigned line = 1;
160 int r;
161
162 assert(path);
163 assert(config);
164
165 f = fopen(path, "re");
166 if (!f) {
167 if (errno == ENOENT)
168 return 0;
169
170 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
171 }
172
173 for (;;) {
174 _cleanup_free_ char *buf = NULL, *field = NULL;
175 const char *p;
176
177 r = read_line(f, LONG_LINE_MAX, &buf);
178 if (r == 0)
179 break;
180 if (r == -ENOBUFS)
181 return log_error_errno(r, "%s:%u: Line too long", path, line);
182 if (r < 0)
183 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
184
185 line++;
186
187 if (IN_SET(*strstrip(buf), '#', '\0'))
188 continue;
189
190 p = buf;
191 r = extract_first_word(&p, &field, " \t", 0);
192 if (r < 0) {
193 log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
194 continue;
195 }
196 if (r == 0) {
197 log_warning("%s:%u: Bad syntax", path, line);
198 continue;
199 }
200
201 if (streq(field, "default"))
202 r = free_and_strdup(&config->default_pattern, p);
203 else if (streq(field, "timeout"))
204 r = free_and_strdup(&config->timeout, p);
205 else if (streq(field, "editor"))
206 r = free_and_strdup(&config->editor, p);
207 else if (streq(field, "auto-entries"))
208 r = free_and_strdup(&config->auto_entries, p);
209 else if (streq(field, "auto-firmware"))
210 r = free_and_strdup(&config->auto_firmware, p);
211 else if (streq(field, "console-mode"))
212 r = free_and_strdup(&config->console_mode, p);
213 else {
214 log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
215 continue;
216 }
217 if (r < 0)
218 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
219 }
220
221 return 1;
222 }
223
224 static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
225 return str_verscmp(a->id, b->id);
226 }
227
228 static int boot_entries_find(
229 const char *root,
230 const char *dir,
231 BootEntry **entries,
232 size_t *n_entries) {
233
234 _cleanup_strv_free_ char **files = NULL;
235 size_t n_allocated = *n_entries;
236 bool added = false;
237 char **f;
238 int r;
239
240 assert(root);
241 assert(dir);
242 assert(entries);
243 assert(n_entries);
244
245 r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL);
246 if (r < 0)
247 return log_error_errno(r, "Failed to list files in \"%s\": %m", dir);
248
249 STRV_FOREACH(f, files) {
250 if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
251 return log_oom();
252
253 r = boot_entry_load(root, *f, *entries + *n_entries);
254 if (r < 0)
255 continue;
256
257 (*n_entries) ++;
258 added = true;
259 }
260
261 if (added)
262 typesafe_qsort(*entries, *n_entries, boot_entry_compare);
263
264 return 0;
265 }
266
267 static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
268 size_t i, j;
269 bool non_unique = false;
270
271 assert(entries || n_entries == 0);
272 assert(arr || n_entries == 0);
273
274 for (i = 0; i < n_entries; i++)
275 arr[i] = false;
276
277 for (i = 0; i < n_entries; i++)
278 for (j = 0; j < n_entries; j++)
279 if (i != j && streq(boot_entry_title(entries + i),
280 boot_entry_title(entries + j)))
281 non_unique = arr[i] = arr[j] = true;
282
283 return non_unique;
284 }
285
286 static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
287 char *s;
288 size_t i;
289 int r;
290 bool arr[n_entries];
291
292 assert(entries || n_entries == 0);
293
294 /* Find _all_ non-unique titles */
295 if (!find_nonunique(entries, n_entries, arr))
296 return 0;
297
298 /* Add version to non-unique titles */
299 for (i = 0; i < n_entries; i++)
300 if (arr[i] && entries[i].version) {
301 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
302 if (r < 0)
303 return -ENOMEM;
304
305 free_and_replace(entries[i].show_title, s);
306 }
307
308 if (!find_nonunique(entries, n_entries, arr))
309 return 0;
310
311 /* Add machine-id to non-unique titles */
312 for (i = 0; i < n_entries; i++)
313 if (arr[i] && entries[i].machine_id) {
314 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
315 if (r < 0)
316 return -ENOMEM;
317
318 free_and_replace(entries[i].show_title, s);
319 }
320
321 if (!find_nonunique(entries, n_entries, arr))
322 return 0;
323
324 /* Add file name to non-unique titles */
325 for (i = 0; i < n_entries; i++)
326 if (arr[i]) {
327 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
328 if (r < 0)
329 return -ENOMEM;
330
331 free_and_replace(entries[i].show_title, s);
332 }
333
334 return 0;
335 }
336
337 static int boot_entries_select_default(const BootConfig *config) {
338 int i;
339
340 assert(config);
341
342 if (config->entry_oneshot)
343 for (i = config->n_entries - 1; i >= 0; i--)
344 if (streq(config->entry_oneshot, config->entries[i].id)) {
345 log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
346 config->entries[i].id);
347 return i;
348 }
349
350 if (config->entry_default)
351 for (i = config->n_entries - 1; i >= 0; i--)
352 if (streq(config->entry_default, config->entries[i].id)) {
353 log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
354 config->entries[i].id);
355 return i;
356 }
357
358 if (config->default_pattern)
359 for (i = config->n_entries - 1; i >= 0; i--)
360 if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
361 log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
362 config->entries[i].id, config->default_pattern);
363 return i;
364 }
365
366 if (config->n_entries > 0)
367 log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
368 else
369 log_debug("Found no default boot entry :(");
370
371 return config->n_entries - 1; /* -1 means "no default" */
372 }
373
374 int boot_entries_load_config(
375 const char *esp_path,
376 const char *xbootldr_path,
377 BootConfig *config) {
378
379 const char *p;
380 int r;
381
382 assert(config);
383
384 if (esp_path) {
385 p = strjoina(esp_path, "/loader/loader.conf");
386 r = boot_loader_read_conf(p, config);
387 if (r < 0)
388 return r;
389
390 p = strjoina(esp_path, "/loader/entries");
391 r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries);
392 if (r < 0)
393 return r;
394 }
395
396 if (xbootldr_path) {
397 p = strjoina(xbootldr_path, "/loader/entries");
398 r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries);
399 if (r < 0)
400 return r;
401 }
402
403 r = boot_entries_uniquify(config->entries, config->n_entries);
404 if (r < 0)
405 return log_error_errno(r, "Failed to uniquify boot entries: %m");
406
407 if (is_efi_boot()) {
408 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
409 if (r < 0 && r != -ENOENT)
410 return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
411
412 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
413 if (r < 0 && r != -ENOENT)
414 return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
415 }
416
417 config->default_entry = boot_entries_select_default(config);
418 return 0;
419 }
420
421 /********************************************************************************/
422
423 static int verify_esp(
424 const char *p,
425 bool searching,
426 bool unprivileged_mode,
427 uint32_t *ret_part,
428 uint64_t *ret_pstart,
429 uint64_t *ret_psize,
430 sd_id128_t *ret_uuid) {
431 #if HAVE_BLKID
432 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
433 _cleanup_free_ char *node = NULL;
434 const char *v;
435 #endif
436 uint64_t pstart = 0, psize = 0;
437 struct stat st, st2;
438 const char *t2;
439 struct statfs sfs;
440 sd_id128_t uuid = SD_ID128_NULL;
441 uint32_t part = 0;
442 bool relax_checks;
443 int r;
444
445 assert(p);
446
447 /* This logs about all errors, except:
448 *
449 * -ENOENT → if 'searching' is set, and the dir doesn't exist
450 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
451 * -EACESS → if 'unprivileged_mode' is set, and we have trouble acessing the thing
452 */
453
454 relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
455
456 /* Non-root user can only check the status, so if an error occured in the following, it does not cause any
457 * issues. Let's also, silence the error messages. */
458
459 if (!relax_checks) {
460 if (statfs(p, &sfs) < 0)
461 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
462 return log_full_errno((searching && errno == ENOENT) ||
463 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
464 "Failed to check file system type of \"%s\": %m", p);
465
466 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
467 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
468 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
469 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
470 }
471
472 if (stat(p, &st) < 0)
473 return log_full_errno((searching && errno == ENOENT) ||
474 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
475 "Failed to determine block device node of \"%s\": %m", p);
476
477 if (major(st.st_dev) == 0)
478 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
479 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
480 "Block device node of \"%s\" is invalid.", p);
481
482 t2 = strjoina(p, "/..");
483 r = stat(t2, &st2);
484 if (r < 0)
485 return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
486 "Failed to determine block device node of parent of \"%s\": %m", p);
487
488 if (st.st_dev == st2.st_dev)
489 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
490 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
491 "Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
492
493 /* In a container we don't have access to block devices, skip this part of the verification, we trust the
494 * container manager set everything up correctly on its own. Also skip the following verification for non-root user. */
495 if (detect_container() > 0 || unprivileged_mode || relax_checks)
496 goto finish;
497
498 #if HAVE_BLKID
499 r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node);
500 if (r < 0)
501 return log_error_errno(r, "Failed to format major/minor device path: %m");
502 errno = 0;
503 b = blkid_new_probe_from_filename(node);
504 if (!b)
505 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", p);
506
507 blkid_probe_enable_superblocks(b, 1);
508 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
509 blkid_probe_enable_partitions(b, 1);
510 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
511
512 errno = 0;
513 r = blkid_do_safeprobe(b);
514 if (r == -2)
515 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", p);
516 else if (r == 1)
517 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", p);
518 else if (r != 0)
519 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", p);
520
521 errno = 0;
522 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
523 if (r != 0)
524 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type \"%s\": %m", p);
525 if (!streq(v, "vfat"))
526 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
527 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
528 "File system \"%s\" is not FAT.", p);
529
530 errno = 0;
531 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
532 if (r != 0)
533 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme \"%s\": %m", p);
534 if (!streq(v, "gpt"))
535 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
536 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
537 "File system \"%s\" is not on a GPT partition table.", p);
538
539 errno = 0;
540 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
541 if (r != 0)
542 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p);
543 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
544 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
545 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
546 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
547
548 errno = 0;
549 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
550 if (r != 0)
551 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID \"%s\": %m", p);
552 r = sd_id128_from_string(v, &uuid);
553 if (r < 0)
554 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", p, v);
555
556 errno = 0;
557 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
558 if (r != 0)
559 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number \"%s\": m", p);
560 r = safe_atou32(v, &part);
561 if (r < 0)
562 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
563
564 errno = 0;
565 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
566 if (r != 0)
567 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset \"%s\": %m", p);
568 r = safe_atou64(v, &pstart);
569 if (r < 0)
570 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
571
572 errno = 0;
573 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
574 if (r != 0)
575 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size \"%s\": %m", p);
576 r = safe_atou64(v, &psize);
577 if (r < 0)
578 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
579 #endif
580
581 finish:
582 if (ret_part)
583 *ret_part = part;
584 if (ret_pstart)
585 *ret_pstart = pstart;
586 if (ret_psize)
587 *ret_psize = psize;
588 if (ret_uuid)
589 *ret_uuid = uuid;
590
591 return 0;
592 }
593
594 int find_esp_and_warn(
595 const char *path,
596 bool unprivileged_mode,
597 char **ret_path,
598 uint32_t *ret_part,
599 uint64_t *ret_pstart,
600 uint64_t *ret_psize,
601 sd_id128_t *ret_uuid) {
602
603 int r;
604
605 /* This logs about all errors except:
606 *
607 * -ENOKEY → when we can't find the partition
608 * -EACCESS → when unprivileged_mode is true, and we can't access something
609 */
610
611 if (path) {
612 r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
613 if (r < 0)
614 return r;
615
616 goto found;
617 }
618
619 path = getenv("SYSTEMD_ESP_PATH");
620 if (path) {
621 if (!path_is_valid(path) || !path_is_absolute(path))
622 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
623 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
624 path);
625
626 /* Note: when the user explicitly configured things with an env var we won't validate the mount
627 * point. After all we want this to be useful for testing. */
628 goto found;
629 }
630
631 FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
632
633 r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
634 if (r >= 0)
635 goto found;
636 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
637 return r;
638 }
639
640 /* No logging here */
641 return -ENOKEY;
642
643 found:
644 if (ret_path) {
645 char *c;
646
647 c = strdup(path);
648 if (!c)
649 return log_oom();
650
651 *ret_path = c;
652 }
653
654 return 0;
655 }
656
657 static int verify_xbootldr(
658 const char *p,
659 bool searching,
660 bool unprivileged_mode,
661 sd_id128_t *ret_uuid) {
662 #if HAVE_BLKID
663 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
664 _cleanup_free_ char *node = NULL;
665 const char *v;
666 #endif
667 struct stat st, st2;
668 const char *t2;
669 sd_id128_t uuid = SD_ID128_NULL;
670 bool relax_checks;
671 int r;
672
673 assert(p);
674
675 relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
676
677 if (stat(p, &st) < 0)
678 return log_full_errno((searching && errno == ENOENT) ||
679 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
680 "Failed to determine block device node of \"%s\": %m", p);
681
682 if (major(st.st_dev) == 0)
683 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
684 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
685 "Block device node of \"%s\" is invalid.", p);
686
687 t2 = strjoina(p, "/..");
688 r = stat(t2, &st2);
689 if (r < 0)
690 return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
691 "Failed to determine block device node of parent of \"%s\": %m", p);
692
693 if (st.st_dev == st2.st_dev)
694 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
695 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
696 "Directory \"%s\" is not the root of the XBOOTLDR file system.", p);
697
698 /* In a container we don't have access to block devices, skip this part of the verification, we trust
699 * the container manager set everything up correctly on its own. Also skip the following verification
700 * for non-root user. */
701 if (detect_container() > 0 || unprivileged_mode || relax_checks)
702 goto finish;
703
704 #if HAVE_BLKID
705 r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node);
706 if (r < 0)
707 return log_error_errno(r, "Failed to format major/minor device path: %m");
708 errno = 0;
709 b = blkid_new_probe_from_filename(node);
710 if (!b)
711 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", p);
712
713 blkid_probe_enable_partitions(b, 1);
714 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
715
716 errno = 0;
717 r = blkid_do_safeprobe(b);
718 if (r == -2)
719 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", p);
720 else if (r == 1)
721 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", p);
722 else if (r != 0)
723 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", p);
724
725 errno = 0;
726 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
727 if (r != 0)
728 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme \"%s\": %m", p);
729 if (streq(v, "gpt")) {
730
731 errno = 0;
732 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
733 if (r != 0)
734 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID \"%s\": %m", p);
735 if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
736 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
737 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
738 "File system \"%s\" has wrong type for extended boot loader partition.", p);
739
740 errno = 0;
741 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
742 if (r != 0)
743 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID \"%s\": %m", p);
744 r = sd_id128_from_string(v, &uuid);
745 if (r < 0)
746 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", p, v);
747
748 } else if (streq(v, "dos")) {
749
750 errno = 0;
751 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
752 if (r != 0)
753 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID \"%s\": %m", p);
754 if (!streq(v, "0xea"))
755 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
756 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
757 "File system \"%s\" has wrong type for extended boot loader partition.", p);
758
759 } else
760 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
761 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
762 "File system \"%s\" is not on a GPT or DOS partition table.", p);
763
764 #endif
765
766 finish:
767 if (ret_uuid)
768 *ret_uuid = uuid;
769
770 return 0;
771 }
772
773 int find_xbootldr_and_warn(
774 const char *path,
775 bool unprivileged_mode,
776 char **ret_path,
777 sd_id128_t *ret_uuid) {
778
779 int r;
780
781 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
782
783 if (path) {
784 r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
785 if (r < 0)
786 return r;
787
788 goto found;
789 }
790
791 path = getenv("SYSTEMD_XBOOTLDR_PATH");
792 if (path) {
793 if (!path_is_valid(path) || !path_is_absolute(path))
794 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
795 "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
796 path);
797
798 goto found;
799 }
800
801 r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
802 if (r >= 0) {
803 path = "/boot";
804 goto found;
805 }
806 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
807 return r;
808
809 return -ENOKEY;
810
811 found:
812 if (ret_path) {
813 char *c;
814
815 c = strdup(path);
816 if (!c)
817 return log_oom();
818
819 *ret_path = c;
820 }
821
822 return 0;
823 }
824
825 int find_default_boot_entry(
826 const char *esp_path,
827 const char *xbootldr_path,
828 BootConfig *config,
829 const BootEntry **e) {
830
831 _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
832 int r;
833
834 assert(config);
835 assert(e);
836
837 r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
838 if (r < 0)
839 return r;
840
841 r = find_xbootldr_and_warn(xbootldr_path, false, &xbootldr_where, NULL);
842 if (r < 0 && r != -ENOKEY)
843 return r;
844
845 r = boot_entries_load_config(esp_where, xbootldr_where, config);
846 if (r < 0)
847 return log_error_errno(r, "Failed to load boot loader entries: %m");
848
849 if (config->default_entry < 0)
850 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
851 "No boot loader entry suitable as default, refusing to guess.");
852
853 *e = &config->entries[config->default_entry];
854 log_debug("Found default boot loader entry in file \"%s\"", (*e)->path);
855
856 return 0;
857 }