]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bootspec.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[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
dccca82b
LP
6#include "sd-id128.h"
7
7e87c7d9 8#include "alloc-util.h"
af918182 9#include "blkid-util.h"
7e87c7d9
ZJS
10#include "bootspec.h"
11#include "conf-files.h"
12#include "def.h"
c67f84b0 13#include "device-nodes.h"
7e87c7d9 14#include "efivars.h"
8cbb7d87 15#include "env-util.h"
7e87c7d9
ZJS
16#include "fd-util.h"
17#include "fileio.h"
af918182 18#include "parse-util.h"
cc7a0bfa 19#include "path-util.h"
af918182 20#include "stat-util.h"
7e87c7d9
ZJS
21#include "string-util.h"
22#include "strv.h"
af918182 23#include "virt.h"
7e87c7d9 24
0de2e1fd 25static void boot_entry_free(BootEntry *entry) {
4fe2ba0e 26 assert(entry);
7e87c7d9 27
12580bc3 28 free(entry->id);
2d3bfb69 29 free(entry->path);
7e87c7d9 30 free(entry->title);
64f05708 31 free(entry->show_title);
7e87c7d9
ZJS
32 free(entry->version);
33 free(entry->machine_id);
34 free(entry->architecture);
35 strv_free(entry->options);
36 free(entry->kernel);
37 free(entry->efi);
38 strv_free(entry->initrd);
39 free(entry->device_tree);
40}
41
0de2e1fd 42static int boot_entry_load(const char *path, BootEntry *entry) {
263195c6 43 _cleanup_(boot_entry_free) BootEntry tmp = {};
7e87c7d9
ZJS
44 _cleanup_fclose_ FILE *f = NULL;
45 unsigned line = 1;
263195c6 46 char *b, *c;
7e87c7d9
ZJS
47 int r;
48
4fe2ba0e
LP
49 assert(path);
50 assert(entry);
51
263195c6
YW
52 c = endswith_no_case(path, ".conf");
53 if (!c) {
54 log_error("Invalid loader entry filename: %s", path);
55 return -EINVAL;
56 }
7e87c7d9 57
263195c6 58 b = basename(path);
12580bc3
LP
59 tmp.id = strndup(b, c - b);
60 if (!tmp.id)
7e87c7d9
ZJS
61 return log_oom();
62
2d3bfb69
ZJS
63 tmp.path = strdup(path);
64 if (!tmp.path)
65 return log_oom();
66
263195c6
YW
67 f = fopen(path, "re");
68 if (!f)
69 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
70
7e87c7d9 71 for (;;) {
f99fdc3e
YW
72 _cleanup_free_ char *buf = NULL, *field = NULL;
73 const char *p;
7e87c7d9
ZJS
74
75 r = read_line(f, LONG_LINE_MAX, &buf);
76 if (r == 0)
77 break;
78 if (r == -ENOBUFS)
79 return log_error_errno(r, "%s:%u: Line too long", path, line);
80 if (r < 0)
81 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
82
83 line++;
84
85 if (IN_SET(*strstrip(buf), '#', '\0'))
86 continue;
87
f99fdc3e
YW
88 p = buf;
89 r = extract_first_word(&p, &field, " \t", 0);
90 if (r < 0) {
91 log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
92 continue;
93 }
94 if (r == 0) {
7e87c7d9
ZJS
95 log_warning("%s:%u: Bad syntax", path, line);
96 continue;
97 }
7e87c7d9 98
f99fdc3e 99 if (streq(field, "title"))
7e87c7d9 100 r = free_and_strdup(&tmp.title, p);
f99fdc3e 101 else if (streq(field, "version"))
7e87c7d9 102 r = free_and_strdup(&tmp.version, p);
f99fdc3e 103 else if (streq(field, "machine-id"))
7e87c7d9 104 r = free_and_strdup(&tmp.machine_id, p);
f99fdc3e 105 else if (streq(field, "architecture"))
7e87c7d9 106 r = free_and_strdup(&tmp.architecture, p);
f99fdc3e 107 else if (streq(field, "options"))
7e87c7d9 108 r = strv_extend(&tmp.options, p);
f99fdc3e 109 else if (streq(field, "linux"))
7e87c7d9 110 r = free_and_strdup(&tmp.kernel, p);
f99fdc3e 111 else if (streq(field, "efi"))
7e87c7d9 112 r = free_and_strdup(&tmp.efi, p);
f99fdc3e 113 else if (streq(field, "initrd"))
7e87c7d9 114 r = strv_extend(&tmp.initrd, p);
f99fdc3e 115 else if (streq(field, "devicetree"))
7e87c7d9
ZJS
116 r = free_and_strdup(&tmp.device_tree, p);
117 else {
f99fdc3e 118 log_notice("%s:%u: Unknown line \"%s\"", path, line, field);
7e87c7d9
ZJS
119 continue;
120 }
121 if (r < 0)
122 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
123 }
124
125 *entry = tmp;
126 tmp = (BootEntry) {};
127 return 0;
128}
129
130void boot_config_free(BootConfig *config) {
da6053d0 131 size_t i;
7e87c7d9 132
4fe2ba0e
LP
133 assert(config);
134
7e87c7d9
ZJS
135 free(config->default_pattern);
136 free(config->timeout);
137 free(config->editor);
c1d4e298
JJ
138 free(config->auto_entries);
139 free(config->auto_firmware);
7e87c7d9
ZJS
140
141 free(config->entry_oneshot);
142 free(config->entry_default);
143
144 for (i = 0; i < config->n_entries; i++)
145 boot_entry_free(config->entries + i);
146 free(config->entries);
147}
148
0de2e1fd 149static int boot_loader_read_conf(const char *path, BootConfig *config) {
7e87c7d9
ZJS
150 _cleanup_fclose_ FILE *f = NULL;
151 unsigned line = 1;
152 int r;
153
4fe2ba0e
LP
154 assert(path);
155 assert(config);
156
7e87c7d9 157 f = fopen(path, "re");
f91ed3dc
LP
158 if (!f) {
159 if (errno == ENOENT)
160 return 0;
161
7e87c7d9 162 return log_error_errno(errno, "Failed to open \"%s\": %m", path);
f91ed3dc 163 }
7e87c7d9
ZJS
164
165 for (;;) {
f99fdc3e
YW
166 _cleanup_free_ char *buf = NULL, *field = NULL;
167 const char *p;
7e87c7d9
ZJS
168
169 r = read_line(f, LONG_LINE_MAX, &buf);
170 if (r == 0)
171 break;
172 if (r == -ENOBUFS)
173 return log_error_errno(r, "%s:%u: Line too long", path, line);
174 if (r < 0)
175 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
176
177 line++;
178
179 if (IN_SET(*strstrip(buf), '#', '\0'))
180 continue;
181
f99fdc3e
YW
182 p = buf;
183 r = extract_first_word(&p, &field, " \t", 0);
184 if (r < 0) {
185 log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
186 continue;
187 }
188 if (r == 0) {
7e87c7d9
ZJS
189 log_warning("%s:%u: Bad syntax", path, line);
190 continue;
191 }
7e87c7d9 192
f99fdc3e 193 if (streq(field, "default"))
7e87c7d9 194 r = free_and_strdup(&config->default_pattern, p);
f99fdc3e 195 else if (streq(field, "timeout"))
7e87c7d9 196 r = free_and_strdup(&config->timeout, p);
f99fdc3e 197 else if (streq(field, "editor"))
7e87c7d9 198 r = free_and_strdup(&config->editor, p);
790f84eb 199 else if (streq(field, "auto-entries"))
c1d4e298 200 r = free_and_strdup(&config->auto_entries, p);
790f84eb 201 else if (streq(field, "auto-firmware"))
c1d4e298 202 r = free_and_strdup(&config->auto_firmware, p);
790f84eb 203 else if (streq(field, "console-mode"))
d37b0737 204 r = free_and_strdup(&config->console_mode, p);
7e87c7d9 205 else {
f99fdc3e 206 log_notice("%s:%u: Unknown line \"%s\"", path, line, field);
7e87c7d9
ZJS
207 continue;
208 }
209 if (r < 0)
210 return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
211 }
212
f91ed3dc 213 return 1;
7e87c7d9
ZJS
214}
215
93bab288 216static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
12580bc3 217 return str_verscmp(a->id, b->id);
7e87c7d9
ZJS
218}
219
0de2e1fd 220static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
7e87c7d9
ZJS
221 _cleanup_strv_free_ char **files = NULL;
222 char **f;
223 int r;
7e87c7d9
ZJS
224 BootEntry *array = NULL;
225 size_t n_allocated = 0, n = 0;
226
4fe2ba0e
LP
227 assert(dir);
228 assert(ret_entries);
229 assert(ret_n_entries);
230
7e87c7d9
ZJS
231 r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL);
232 if (r < 0)
233 return log_error_errno(r, "Failed to list files in \"%s\": %m", dir);
234
235 STRV_FOREACH(f, files) {
236 if (!GREEDY_REALLOC0(array, n_allocated, n + 1))
237 return log_oom();
238
239 r = boot_entry_load(*f, array + n);
240 if (r < 0)
241 continue;
242
243 n++;
244 }
245
93bab288 246 typesafe_qsort(array, n, boot_entry_compare);
7e87c7d9 247
4fe2ba0e
LP
248 *ret_entries = array;
249 *ret_n_entries = n;
250
7e87c7d9
ZJS
251 return 0;
252}
253
64f05708 254static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
da6053d0 255 size_t i, j;
64f05708
ZJS
256 bool non_unique = false;
257
4fe2ba0e
LP
258 assert(entries || n_entries == 0);
259 assert(arr || n_entries == 0);
260
64f05708
ZJS
261 for (i = 0; i < n_entries; i++)
262 arr[i] = false;
263
264 for (i = 0; i < n_entries; i++)
265 for (j = 0; j < n_entries; j++)
266 if (i != j && streq(boot_entry_title(entries + i),
267 boot_entry_title(entries + j)))
268 non_unique = arr[i] = arr[j] = true;
269
270 return non_unique;
271}
272
273static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
274 char *s;
da6053d0 275 size_t i;
64f05708
ZJS
276 int r;
277 bool arr[n_entries];
278
4fe2ba0e
LP
279 assert(entries || n_entries == 0);
280
64f05708
ZJS
281 /* Find _all_ non-unique titles */
282 if (!find_nonunique(entries, n_entries, arr))
283 return 0;
284
285 /* Add version to non-unique titles */
286 for (i = 0; i < n_entries; i++)
287 if (arr[i] && entries[i].version) {
288 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
289 if (r < 0)
290 return -ENOMEM;
291
292 free_and_replace(entries[i].show_title, s);
293 }
294
295 if (!find_nonunique(entries, n_entries, arr))
296 return 0;
297
298 /* Add machine-id to non-unique titles */
299 for (i = 0; i < n_entries; i++)
300 if (arr[i] && entries[i].machine_id) {
301 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
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 file name to non-unique titles */
312 for (i = 0; i < n_entries; i++)
313 if (arr[i]) {
12580bc3 314 r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
64f05708
ZJS
315 if (r < 0)
316 return -ENOMEM;
317
318 free_and_replace(entries[i].show_title, s);
319 }
320
321 return 0;
322}
323
ad1afd60 324static int boot_entries_select_default(const BootConfig *config) {
7e87c7d9
ZJS
325 int i;
326
4fe2ba0e
LP
327 assert(config);
328
7e87c7d9
ZJS
329 if (config->entry_oneshot)
330 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
331 if (streq(config->entry_oneshot, config->entries[i].id)) {
332 log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
333 config->entries[i].id);
7e87c7d9
ZJS
334 return i;
335 }
336
337 if (config->entry_default)
338 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
339 if (streq(config->entry_default, config->entries[i].id)) {
340 log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
341 config->entries[i].id);
7e87c7d9
ZJS
342 return i;
343 }
344
345 if (config->default_pattern)
346 for (i = config->n_entries - 1; i >= 0; i--)
12580bc3
LP
347 if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
348 log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
349 config->entries[i].id, config->default_pattern);
7e87c7d9
ZJS
350 return i;
351 }
352
353 if (config->n_entries > 0)
12580bc3 354 log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
7e87c7d9
ZJS
355 else
356 log_debug("Found no default boot entry :(");
4fe2ba0e 357
7e87c7d9
ZJS
358 return config->n_entries - 1; /* -1 means "no default" */
359}
360
361int boot_entries_load_config(const char *esp_path, BootConfig *config) {
362 const char *p;
363 int r;
364
4fe2ba0e
LP
365 assert(esp_path);
366 assert(config);
367
7e87c7d9
ZJS
368 p = strjoina(esp_path, "/loader/loader.conf");
369 r = boot_loader_read_conf(p, config);
370 if (r < 0)
21f7a622 371 return r;
7e87c7d9
ZJS
372
373 p = strjoina(esp_path, "/loader/entries");
374 r = boot_entries_find(p, &config->entries, &config->n_entries);
375 if (r < 0)
21f7a622 376 return r;
7e87c7d9 377
64f05708
ZJS
378 r = boot_entries_uniquify(config->entries, config->n_entries);
379 if (r < 0)
380 return log_error_errno(r, "Failed to uniquify boot entries: %m");
381
9c4a6c13
LP
382 if (is_efi_boot()) {
383 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
384 if (r < 0 && r != -ENOENT)
385 return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
386
387 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
388 if (r < 0 && r != -ENOENT)
389 return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
390 }
7e87c7d9
ZJS
391
392 config->default_entry = boot_entries_select_default(config);
393 return 0;
394}
af918182
ZJS
395
396/********************************************************************************/
397
398static int verify_esp(
af918182 399 const char *p,
5caa3167
LP
400 bool searching,
401 bool unprivileged_mode,
af918182
ZJS
402 uint32_t *ret_part,
403 uint64_t *ret_pstart,
404 uint64_t *ret_psize,
405 sd_id128_t *ret_uuid) {
4e066f7f 406#if HAVE_BLKID
8e766630 407 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
54b22b26 408 _cleanup_free_ char *node = NULL;
4e066f7f
YW
409 const char *v;
410#endif
af918182
ZJS
411 uint64_t pstart = 0, psize = 0;
412 struct stat st, st2;
4e066f7f 413 const char *t2;
af918182
ZJS
414 struct statfs sfs;
415 sd_id128_t uuid = SD_ID128_NULL;
416 uint32_t part = 0;
8cbb7d87 417 bool relax_checks;
af918182
ZJS
418 int r;
419
420 assert(p);
421
8cbb7d87
LP
422 relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
423
5caa3167
LP
424 /* Non-root user can only check the status, so if an error occured in the following, it does not cause any
425 * issues. Let's also, silence the error messages. */
af918182 426
8cbb7d87
LP
427 if (!relax_checks) {
428 if (statfs(p, &sfs) < 0) {
429 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
430 if (errno == ENOENT && searching)
431 return -ENOENT;
af918182 432
8cbb7d87
LP
433 return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
434 "Failed to check file system type of \"%s\": %m", p);
435 }
af918182 436
8cbb7d87
LP
437 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
438 if (searching)
439 return -EADDRNOTAVAIL;
af918182 440
8cbb7d87
LP
441 log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
442 return -ENODEV;
443 }
af918182
ZJS
444 }
445
446 if (stat(p, &st) < 0)
5caa3167 447 return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
af918182
ZJS
448 "Failed to determine block device node of \"%s\": %m", p);
449
450 if (major(st.st_dev) == 0) {
451 log_error("Block device node of %p is invalid.", p);
452 return -ENODEV;
453 }
454
455 t2 = strjoina(p, "/..");
456 r = stat(t2, &st2);
457 if (r < 0)
5caa3167 458 return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
af918182
ZJS
459 "Failed to determine block device node of parent of \"%s\": %m", p);
460
461 if (st.st_dev == st2.st_dev) {
462 log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
463 return -ENODEV;
464 }
465
466 /* In a container we don't have access to block devices, skip this part of the verification, we trust the
467 * container manager set everything up correctly on its own. Also skip the following verification for non-root user. */
8cbb7d87 468 if (detect_container() > 0 || unprivileged_mode || relax_checks)
af918182
ZJS
469 goto finish;
470
4e066f7f 471#if HAVE_BLKID
54b22b26
LP
472 r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node);
473 if (r < 0)
474 return log_error_errno(r, "Failed to format major/minor device path: %m");
af918182 475 errno = 0;
54b22b26 476 b = blkid_new_probe_from_filename(node);
af918182
ZJS
477 if (!b)
478 return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p);
479
480 blkid_probe_enable_superblocks(b, 1);
481 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
482 blkid_probe_enable_partitions(b, 1);
483 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
484
485 errno = 0;
486 r = blkid_do_safeprobe(b);
487 if (r == -2) {
488 log_error("File system \"%s\" is ambiguous.", p);
489 return -ENODEV;
490 } else if (r == 1) {
491 log_error("File system \"%s\" does not contain a label.", p);
492 return -ENODEV;
493 } else if (r != 0)
494 return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p);
495
496 errno = 0;
497 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
498 if (r != 0)
499 return log_error_errno(errno ?: EIO, "Failed to probe file system type \"%s\": %m", p);
500 if (!streq(v, "vfat")) {
501 log_error("File system \"%s\" is not FAT.", p);
502 return -ENODEV;
503 }
504
505 errno = 0;
506 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
507 if (r != 0)
508 return log_error_errno(errno ?: EIO, "Failed to probe partition scheme \"%s\": %m", p);
509 if (!streq(v, "gpt")) {
510 log_error("File system \"%s\" is not on a GPT partition table.", p);
511 return -ENODEV;
512 }
513
514 errno = 0;
515 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
516 if (r != 0)
517 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p);
518 if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) {
519 log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
520 return -ENODEV;
521 }
522
523 errno = 0;
524 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
525 if (r != 0)
526 return log_error_errno(errno ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p);
527 r = sd_id128_from_string(v, &uuid);
528 if (r < 0) {
529 log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
530 return -EIO;
531 }
532
533 errno = 0;
534 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
535 if (r != 0)
536 return log_error_errno(errno ?: EIO, "Failed to probe partition number \"%s\": m", p);
537 r = safe_atou32(v, &part);
538 if (r < 0)
539 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
540
541 errno = 0;
542 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
543 if (r != 0)
544 return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p);
545 r = safe_atou64(v, &pstart);
546 if (r < 0)
547 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
548
549 errno = 0;
550 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
551 if (r != 0)
552 return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p);
553 r = safe_atou64(v, &psize);
554 if (r < 0)
555 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
4e066f7f 556#endif
af918182
ZJS
557
558finish:
559 if (ret_part)
560 *ret_part = part;
561 if (ret_pstart)
562 *ret_pstart = pstart;
563 if (ret_psize)
564 *ret_psize = psize;
565 if (ret_uuid)
566 *ret_uuid = uuid;
567
568 return 0;
569}
570
5caa3167
LP
571int find_esp_and_warn(
572 const char *path,
573 bool unprivileged_mode,
574 char **ret_path,
575 uint32_t *ret_part,
576 uint64_t *ret_pstart,
577 uint64_t *ret_psize,
578 sd_id128_t *ret_uuid) {
af918182 579
af918182
ZJS
580 int r;
581
5caa3167
LP
582 /* This logs about all errors except:
583 *
584 * -ENOKEY → when we can't find the partition
585 * -EACCESS → when unprivileged_mode is true, and we can't access something
586 */
af918182 587
5caa3167
LP
588 if (path) {
589 r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
af918182
ZJS
590 if (r < 0)
591 return r;
592
5caa3167
LP
593 goto found;
594 }
595
cc7a0bfa
LP
596 path = getenv("SYSTEMD_ESP_PATH");
597 if (path) {
baaa35ad
ZJS
598 if (!path_is_valid(path) || !path_is_absolute(path))
599 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
600 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
601 path);
cc7a0bfa
LP
602
603 /* Note: when the user explicitly configured things with an env var we won't validate the mount
604 * point. After all we want this to be useful for testing. */
605 goto found;
606 }
607
5caa3167
LP
608 FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
609
610 r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
611 if (r >= 0)
612 goto found;
613 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
614 return r;
615 }
616
617 /* No logging here */
618 return -ENOKEY;
619
620found:
621 if (ret_path) {
622 char *c;
623
624 c = strdup(path);
625 if (!c)
af918182
ZJS
626 return log_oom();
627
5caa3167 628 *ret_path = c;
af918182
ZJS
629 }
630
5caa3167 631 return 0;
af918182 632}
1b20d889
ZJS
633
634int find_default_boot_entry(
635 const char *esp_path,
636 char **esp_where,
637 BootConfig *config,
638 const BootEntry **e) {
639
640 _cleanup_free_ char *where = NULL;
641 int r;
642
643 assert(config);
644 assert(e);
645
646 r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL);
647 if (r < 0)
648 return r;
649
650 r = boot_entries_load_config(where, config);
651 if (r < 0)
652 return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where);
653
baaa35ad
ZJS
654 if (config->default_entry < 0)
655 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
656 "No entry suitable as default, refusing to guess.");
1b20d889
ZJS
657
658 *e = &config->entries[config->default_entry];
2d3bfb69 659 log_debug("Found default boot entry in file \"%s\"", (*e)->path);
1b20d889
ZJS
660
661 if (esp_where)
662 *esp_where = TAKE_PTR(where);
663
664 return 0;
665}