]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/find-esp.c
Merge pull request #22791 from keszybz/bootctl-invert-order
[thirdparty/systemd.git] / src / shared / find-esp.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/magic.h>
4
5 #include "sd-device.h"
6
7 #include "alloc-util.h"
8 #include "blkid-util.h"
9 #include "env-util.h"
10 #include "errno-util.h"
11 #include "find-esp.h"
12 #include "gpt.h"
13 #include "id128-util.h"
14 #include "parse-util.h"
15 #include "path-util.h"
16 #include "stat-util.h"
17 #include "string-util.h"
18 #include "virt.h"
19
20 static int verify_esp_blkid(
21 dev_t devid,
22 bool searching,
23 uint32_t *ret_part,
24 uint64_t *ret_pstart,
25 uint64_t *ret_psize,
26 sd_id128_t *ret_uuid) {
27
28 sd_id128_t uuid = SD_ID128_NULL;
29 uint64_t pstart = 0, psize = 0;
30 uint32_t part = 0;
31
32 #if HAVE_BLKID
33 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
34 _cleanup_free_ char *node = NULL;
35 const char *v;
36 int r;
37
38 r = device_path_make_major_minor(S_IFBLK, devid, &node);
39 if (r < 0)
40 return log_error_errno(r, "Failed to format major/minor device path: %m");
41
42 errno = 0;
43 b = blkid_new_probe_from_filename(node);
44 if (!b)
45 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
46
47 blkid_probe_enable_superblocks(b, 1);
48 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
49 blkid_probe_enable_partitions(b, 1);
50 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
51
52 errno = 0;
53 r = blkid_do_safeprobe(b);
54 if (r == -2)
55 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
56 else if (r == 1)
57 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
58 else if (r != 0)
59 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
60
61 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
62 if (r != 0)
63 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
64 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
65 "No filesystem found on \"%s\": %m", node);
66 if (!streq(v, "vfat"))
67 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
68 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
69 "File system \"%s\" is not FAT.", node);
70
71 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
72 if (r != 0)
73 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
74 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
75 "File system \"%s\" is not located on a partitioned block device.", node);
76 if (!streq(v, "gpt"))
77 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
78 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
79 "File system \"%s\" is not on a GPT partition table.", node);
80
81 errno = 0;
82 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
83 if (r != 0)
84 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
85 if (id128_equal_string(v, GPT_ESP) <= 0)
86 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
87 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
88 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
89
90 errno = 0;
91 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
92 if (r != 0)
93 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
94 r = sd_id128_from_string(v, &uuid);
95 if (r < 0)
96 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
97
98 errno = 0;
99 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
100 if (r != 0)
101 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
102 r = safe_atou32(v, &part);
103 if (r < 0)
104 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
105
106 errno = 0;
107 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
108 if (r != 0)
109 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
110 r = safe_atou64(v, &pstart);
111 if (r < 0)
112 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
113
114 errno = 0;
115 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
116 if (r != 0)
117 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
118 r = safe_atou64(v, &psize);
119 if (r < 0)
120 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
121 #endif
122
123 if (ret_part)
124 *ret_part = part;
125 if (ret_pstart)
126 *ret_pstart = pstart;
127 if (ret_psize)
128 *ret_psize = psize;
129 if (ret_uuid)
130 *ret_uuid = uuid;
131
132 return 0;
133 }
134
135 static int verify_esp_udev(
136 dev_t devid,
137 bool searching,
138 uint32_t *ret_part,
139 uint64_t *ret_pstart,
140 uint64_t *ret_psize,
141 sd_id128_t *ret_uuid) {
142
143 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
144 _cleanup_free_ char *node = NULL;
145 sd_id128_t uuid = SD_ID128_NULL;
146 uint64_t pstart = 0, psize = 0;
147 uint32_t part = 0;
148 const char *v;
149 int r;
150
151 r = device_path_make_major_minor(S_IFBLK, devid, &node);
152 if (r < 0)
153 return log_error_errno(r, "Failed to format major/minor device path: %m");
154
155 r = sd_device_new_from_devnum(&d, 'b', devid);
156 if (r < 0)
157 return log_error_errno(r, "Failed to get device from device number: %m");
158
159 r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
160 if (r < 0)
161 return log_error_errno(r, "Failed to get device property: %m");
162 if (!streq(v, "vfat"))
163 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
164 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
165 "File system \"%s\" is not FAT.", node );
166
167 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
168 if (r < 0)
169 return log_error_errno(r, "Failed to get device property: %m");
170 if (!streq(v, "gpt"))
171 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
172 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
173 "File system \"%s\" is not on a GPT partition table.", node);
174
175 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
176 if (r < 0)
177 return log_error_errno(r, "Failed to get device property: %m");
178 if (id128_equal_string(v, GPT_ESP) <= 0)
179 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
180 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
181 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
182
183 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
184 if (r < 0)
185 return log_error_errno(r, "Failed to get device property: %m");
186 r = sd_id128_from_string(v, &uuid);
187 if (r < 0)
188 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
189
190 r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
191 if (r < 0)
192 return log_error_errno(r, "Failed to get device property: %m");
193 r = safe_atou32(v, &part);
194 if (r < 0)
195 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
196
197 r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
198 if (r < 0)
199 return log_error_errno(r, "Failed to get device property: %m");
200 r = safe_atou64(v, &pstart);
201 if (r < 0)
202 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
203
204 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
205 if (r < 0)
206 return log_error_errno(r, "Failed to get device property: %m");
207 r = safe_atou64(v, &psize);
208 if (r < 0)
209 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
210
211 if (ret_part)
212 *ret_part = part;
213 if (ret_pstart)
214 *ret_pstart = pstart;
215 if (ret_psize)
216 *ret_psize = psize;
217 if (ret_uuid)
218 *ret_uuid = uuid;
219
220 return 0;
221 }
222
223 static int verify_fsroot_dir(
224 const char *path,
225 bool searching,
226 bool unprivileged_mode,
227 dev_t *ret_dev) {
228
229 struct stat st, st2;
230 const char *t2, *trigger;
231 int r;
232
233 assert(path);
234 assert(ret_dev);
235
236 /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
237 * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
238 * before stat()ing */
239 trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
240 (void) access(trigger, F_OK);
241
242 if (stat(path, &st) < 0)
243 return log_full_errno((searching && errno == ENOENT) ||
244 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
245 "Failed to determine block device node of \"%s\": %m", path);
246
247 if (major(st.st_dev) == 0)
248 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
249 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
250 "Block device node of \"%s\" is invalid.", path);
251
252 if (path_equal(path, "/")) {
253 /* Let's assume that the root directory of the OS is always the root of its file system
254 * (which technically doesn't have to be the case, but it's close enough, and it's not easy
255 * to be fully correct for it, since we can't look further up than the root dir easily.) */
256 if (ret_dev)
257 *ret_dev = st.st_dev;
258
259 return 0;
260 }
261
262 t2 = strjoina(path, "/..");
263 if (stat(t2, &st2) < 0) {
264 if (errno != EACCES)
265 r = -errno;
266 else {
267 _cleanup_free_ char *parent = NULL;
268
269 /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
270 * directly instead. It's not as good, due to symlinks and such, but we can't do
271 * anything better here. */
272
273 parent = dirname_malloc(path);
274 if (!parent)
275 return log_oom();
276
277 r = RET_NERRNO(stat(parent, &st2));
278 }
279
280 if (r < 0)
281 return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
282 "Failed to determine block device node of parent of \"%s\": %m", path);
283 }
284
285 if (st.st_dev == st2.st_dev)
286 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
287 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
288 "Directory \"%s\" is not the root of the file system.", path);
289
290 if (ret_dev)
291 *ret_dev = st.st_dev;
292
293 return 0;
294 }
295
296 static int verify_esp(
297 const char *p,
298 bool searching,
299 bool unprivileged_mode,
300 uint32_t *ret_part,
301 uint64_t *ret_pstart,
302 uint64_t *ret_psize,
303 sd_id128_t *ret_uuid,
304 dev_t *ret_devid) {
305
306 bool relax_checks;
307 dev_t devid;
308 int r;
309
310 assert(p);
311
312 /* This logs about all errors, except:
313 *
314 * -ENOENT → if 'searching' is set, and the dir doesn't exist
315 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
316 * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
317 */
318
319 relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
320
321 /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
322 * issues. Let's also, silence the error messages. */
323
324 if (!relax_checks) {
325 struct statfs sfs;
326
327 if (statfs(p, &sfs) < 0)
328 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
329 return log_full_errno((searching && errno == ENOENT) ||
330 (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
331 "Failed to check file system type of \"%s\": %m", p);
332
333 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
334 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
335 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
336 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
337 }
338
339 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
340 if (r < 0)
341 return r;
342
343 /* In a container we don't have access to block devices, skip this part of the verification, we trust
344 * the container manager set everything up correctly on its own. */
345 if (detect_container() > 0 || relax_checks)
346 goto finish;
347
348 /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
349 * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
350 * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
351 * however blkid can't work if we have no privileges to access block devices directly, which is why
352 * we use udev in that case. */
353 if (unprivileged_mode)
354 r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
355 else
356 r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
357 if (r < 0)
358 return r;
359
360 if (ret_devid)
361 *ret_devid = devid;
362
363 return 0;
364
365 finish:
366 if (ret_part)
367 *ret_part = 0;
368 if (ret_pstart)
369 *ret_pstart = 0;
370 if (ret_psize)
371 *ret_psize = 0;
372 if (ret_uuid)
373 *ret_uuid = SD_ID128_NULL;
374 if (ret_devid)
375 *ret_devid = 0;
376
377 return 0;
378 }
379
380 int find_esp_and_warn(
381 const char *path,
382 bool unprivileged_mode,
383 char **ret_path,
384 uint32_t *ret_part,
385 uint64_t *ret_pstart,
386 uint64_t *ret_psize,
387 sd_id128_t *ret_uuid,
388 dev_t *ret_devid) {
389
390 int r;
391
392 /* This logs about all errors except:
393 *
394 * -ENOKEY → when we can't find the partition
395 * -EACCESS → when unprivileged_mode is true, and we can't access something
396 */
397
398 if (path) {
399 r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
400 if (r < 0)
401 return r;
402
403 goto found;
404 }
405
406 path = getenv("SYSTEMD_ESP_PATH");
407 if (path) {
408 struct stat st;
409
410 if (!path_is_valid(path) || !path_is_absolute(path))
411 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
412 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
413 path);
414
415 /* Note: when the user explicitly configured things with an env var we won't validate the
416 * path beyond checking it refers to a directory. After all we want this to be useful for
417 * testing. */
418
419 if (stat(path, &st) < 0)
420 return log_error_errno(errno, "Failed to stat '%s': %m", path);
421 if (!S_ISDIR(st.st_mode))
422 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
423
424 if (ret_part)
425 *ret_part = 0;
426 if (ret_pstart)
427 *ret_pstart = 0;
428 if (ret_psize)
429 *ret_psize = 0;
430 if (ret_uuid)
431 *ret_uuid = SD_ID128_NULL;
432 if (ret_devid)
433 *ret_devid = st.st_dev;
434
435 goto found;
436 }
437
438 FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
439
440 r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
441 if (r >= 0)
442 goto found;
443 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
444 return r;
445 }
446
447 /* No logging here */
448 return -ENOKEY;
449
450 found:
451 if (ret_path) {
452 char *c;
453
454 c = strdup(path);
455 if (!c)
456 return log_oom();
457
458 *ret_path = c;
459 }
460
461 return 0;
462 }
463
464 static int verify_xbootldr_blkid(
465 dev_t devid,
466 bool searching,
467 sd_id128_t *ret_uuid) {
468
469 sd_id128_t uuid = SD_ID128_NULL;
470
471 #if HAVE_BLKID
472 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
473 _cleanup_free_ char *node = NULL;
474 const char *v;
475 int r;
476
477 r = device_path_make_major_minor(S_IFBLK, devid, &node);
478 if (r < 0)
479 return log_error_errno(r, "Failed to format major/minor device path: %m");
480 errno = 0;
481 b = blkid_new_probe_from_filename(node);
482 if (!b)
483 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
484
485 blkid_probe_enable_partitions(b, 1);
486 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
487
488 errno = 0;
489 r = blkid_do_safeprobe(b);
490 if (r == -2)
491 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
492 else if (r == 1)
493 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
494 else if (r != 0)
495 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
496
497 errno = 0;
498 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
499 if (r != 0)
500 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
501 if (streq(v, "gpt")) {
502
503 errno = 0;
504 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
505 if (r != 0)
506 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
507 if (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
508 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
509 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
510 "File system \"%s\" has wrong type for extended boot loader partition.", node);
511
512 errno = 0;
513 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
514 if (r != 0)
515 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
516 r = sd_id128_from_string(v, &uuid);
517 if (r < 0)
518 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
519
520 } else if (streq(v, "dos")) {
521
522 errno = 0;
523 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
524 if (r != 0)
525 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
526 if (!streq(v, "0xea"))
527 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
528 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
529 "File system \"%s\" has wrong type for extended boot loader partition.", node);
530
531 } else
532 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
533 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
534 "File system \"%s\" is not on a GPT or DOS partition table.", node);
535 #endif
536
537 if (ret_uuid)
538 *ret_uuid = uuid;
539
540 return 0;
541 }
542
543 static int verify_xbootldr_udev(
544 dev_t devid,
545 bool searching,
546 sd_id128_t *ret_uuid) {
547
548 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
549 _cleanup_free_ char *node = NULL;
550 sd_id128_t uuid = SD_ID128_NULL;
551 const char *v;
552 int r;
553
554 r = device_path_make_major_minor(S_IFBLK, devid, &node);
555 if (r < 0)
556 return log_error_errno(r, "Failed to format major/minor device path: %m");
557
558 r = sd_device_new_from_devnum(&d, 'b', devid);
559 if (r < 0)
560 return log_error_errno(r, "Failed to get device from device number: %m");
561
562 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
563 if (r < 0)
564 return log_error_errno(r, "Failed to get device property: %m");
565
566 if (streq(v, "gpt")) {
567
568 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
569 if (r < 0)
570 return log_error_errno(r, "Failed to get device property: %m");
571 if (id128_equal_string(v, GPT_XBOOTLDR))
572 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
573 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
574 "File system \"%s\" has wrong type for extended boot loader partition.", node);
575
576 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
577 if (r < 0)
578 return log_error_errno(r, "Failed to get device property: %m");
579 r = sd_id128_from_string(v, &uuid);
580 if (r < 0)
581 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
582
583 } else if (streq(v, "dos")) {
584
585 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
586 if (r < 0)
587 return log_error_errno(r, "Failed to get device property: %m");
588 if (!streq(v, "0xea"))
589 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
590 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
591 "File system \"%s\" has wrong type for extended boot loader partition.", node);
592 } else
593 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
594 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
595 "File system \"%s\" is not on a GPT or DOS partition table.", node);
596
597 if (ret_uuid)
598 *ret_uuid = uuid;
599
600 return 0;
601 }
602
603 static int verify_xbootldr(
604 const char *p,
605 bool searching,
606 bool unprivileged_mode,
607 sd_id128_t *ret_uuid,
608 dev_t *ret_devid) {
609
610 bool relax_checks;
611 dev_t devid;
612 int r;
613
614 assert(p);
615
616 relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
617
618 r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
619 if (r < 0)
620 return r;
621
622 if (detect_container() > 0 || relax_checks)
623 goto finish;
624
625 if (unprivileged_mode)
626 r = verify_xbootldr_udev(devid, searching, ret_uuid);
627 else
628 r = verify_xbootldr_blkid(devid, searching, ret_uuid);
629 if (r < 0)
630 return r;
631
632 if (ret_devid)
633 *ret_devid = devid;
634
635 return 0;
636
637 finish:
638 if (ret_uuid)
639 *ret_uuid = SD_ID128_NULL;
640 if (ret_devid)
641 *ret_devid = 0;
642
643 return 0;
644 }
645
646 int find_xbootldr_and_warn(
647 const char *path,
648 bool unprivileged_mode,
649 char **ret_path,
650 sd_id128_t *ret_uuid,
651 dev_t *ret_devid) {
652
653 int r;
654
655 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
656
657 if (path) {
658 r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
659 if (r < 0)
660 return r;
661
662 goto found;
663 }
664
665 path = getenv("SYSTEMD_XBOOTLDR_PATH");
666 if (path) {
667 struct stat st;
668
669 if (!path_is_valid(path) || !path_is_absolute(path))
670 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
671 "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
672 path);
673
674 if (stat(path, &st) < 0)
675 return log_error_errno(errno, "Failed to stat '%s': %m", path);
676 if (!S_ISDIR(st.st_mode))
677 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
678
679 if (ret_uuid)
680 *ret_uuid = SD_ID128_NULL;
681 if (ret_devid)
682 *ret_devid = st.st_dev;
683
684 goto found;
685 }
686
687 r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
688 if (r >= 0) {
689 path = "/boot";
690 goto found;
691 }
692 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
693 return r;
694
695 return -ENOKEY;
696
697 found:
698 if (ret_path) {
699 char *c;
700
701 c = strdup(path);
702 if (!c)
703 return log_oom();
704
705 *ret_path = c;
706 }
707
708 return 0;
709 }