1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/magic.h>
8 #include "alloc-util.h"
9 #include "blkid-util.h"
10 #include "device-util.h"
11 #include "devnum-util.h"
13 #include "errno-util.h"
16 #include "id128-util.h"
17 #include "parse-util.h"
18 #include "path-util.h"
19 #include "stat-util.h"
20 #include "string-util.h"
23 static int verify_esp_blkid(
29 sd_id128_t
*ret_uuid
) {
31 sd_id128_t uuid
= SD_ID128_NULL
;
32 uint64_t pstart
= 0, psize
= 0;
36 _cleanup_(blkid_free_probep
) blkid_probe b
= NULL
;
37 _cleanup_free_
char *node
= NULL
;
41 r
= device_path_make_major_minor(S_IFBLK
, devid
, &node
);
43 return log_error_errno(r
, "Failed to format major/minor device path: %m");
46 b
= blkid_new_probe_from_filename(node
);
48 return log_error_errno(errno
?: SYNTHETIC_ERRNO(ENOMEM
), "Failed to open file system \"%s\": %m", node
);
50 blkid_probe_enable_superblocks(b
, 1);
51 blkid_probe_set_superblocks_flags(b
, BLKID_SUBLKS_TYPE
);
52 blkid_probe_enable_partitions(b
, 1);
53 blkid_probe_set_partitions_flags(b
, BLKID_PARTS_ENTRY_DETAILS
);
56 r
= blkid_do_safeprobe(b
);
58 return log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "File system \"%s\" is ambiguous.", node
);
60 return log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "File system \"%s\" does not contain a label.", node
);
62 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "Failed to probe file system \"%s\": %m", node
);
64 r
= blkid_probe_lookup_value(b
, "TYPE", &v
, NULL
);
66 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
67 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
68 "No filesystem found on \"%s\": %m", node
);
69 if (!streq(v
, "vfat"))
70 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
71 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
72 "File system \"%s\" is not FAT.", node
);
74 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_SCHEME", &v
, NULL
);
76 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
77 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
78 "File system \"%s\" is not located on a partitioned block device.", node
);
80 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
81 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
82 "File system \"%s\" is not on a GPT partition table.", node
);
85 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_TYPE", &v
, NULL
);
87 return log_error_errno(errno
?: EIO
, "Failed to probe partition type UUID of \"%s\": %m", node
);
88 if (id128_equal_string(v
, GPT_ESP
) <= 0)
89 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
90 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
91 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node
);
94 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_UUID", &v
, NULL
);
96 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "Failed to probe partition entry UUID of \"%s\": %m", node
);
97 r
= sd_id128_from_string(v
, &uuid
);
99 return log_error_errno(r
, "Partition \"%s\" has invalid UUID \"%s\".", node
, v
);
102 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_NUMBER", &v
, NULL
);
104 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "Failed to probe partition number of \"%s\": %m", node
);
105 r
= safe_atou32(v
, &part
);
107 return log_error_errno(r
, "Failed to parse PART_ENTRY_NUMBER field.");
110 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_OFFSET", &v
, NULL
);
112 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "Failed to probe partition offset of \"%s\": %m", node
);
113 r
= safe_atou64(v
, &pstart
);
115 return log_error_errno(r
, "Failed to parse PART_ENTRY_OFFSET field.");
118 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_SIZE", &v
, NULL
);
120 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "Failed to probe partition size of \"%s\": %m", node
);
121 r
= safe_atou64(v
, &psize
);
123 return log_error_errno(r
, "Failed to parse PART_ENTRY_SIZE field.");
129 *ret_pstart
= pstart
;
138 static int verify_esp_udev(
142 uint64_t *ret_pstart
,
144 sd_id128_t
*ret_uuid
) {
146 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
147 _cleanup_free_
char *node
= NULL
;
148 sd_id128_t uuid
= SD_ID128_NULL
;
149 uint64_t pstart
= 0, psize
= 0;
154 r
= device_path_make_major_minor(S_IFBLK
, devid
, &node
);
156 return log_error_errno(r
, "Failed to format major/minor device path: %m");
158 r
= sd_device_new_from_devnum(&d
, 'b', devid
);
160 return log_error_errno(r
, "Failed to get device from device number: %m");
162 r
= sd_device_get_property_value(d
, "ID_FS_TYPE", &v
);
164 return log_error_errno(r
, "Failed to get device property: %m");
165 if (!streq(v
, "vfat"))
166 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
167 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
168 "File system \"%s\" is not FAT.", node
);
170 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_SCHEME", &v
);
172 return log_error_errno(r
, "Failed to get device property: %m");
173 if (!streq(v
, "gpt"))
174 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
175 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
176 "File system \"%s\" is not on a GPT partition table.", node
);
178 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_TYPE", &v
);
180 return log_error_errno(r
, "Failed to get device property: %m");
181 if (id128_equal_string(v
, GPT_ESP
) <= 0)
182 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
183 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
184 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node
);
186 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_UUID", &v
);
188 return log_error_errno(r
, "Failed to get device property: %m");
189 r
= sd_id128_from_string(v
, &uuid
);
191 return log_error_errno(r
, "Partition \"%s\" has invalid UUID \"%s\".", node
, v
);
193 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_NUMBER", &v
);
195 return log_error_errno(r
, "Failed to get device property: %m");
196 r
= safe_atou32(v
, &part
);
198 return log_error_errno(r
, "Failed to parse PART_ENTRY_NUMBER field.");
200 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_OFFSET", &v
);
202 return log_error_errno(r
, "Failed to get device property: %m");
203 r
= safe_atou64(v
, &pstart
);
205 return log_error_errno(r
, "Failed to parse PART_ENTRY_OFFSET field.");
207 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_SIZE", &v
);
209 return log_error_errno(r
, "Failed to get device property: %m");
210 r
= safe_atou64(v
, &psize
);
212 return log_error_errno(r
, "Failed to parse PART_ENTRY_SIZE field.");
217 *ret_pstart
= pstart
;
226 static int verify_fsroot_dir(
229 bool unprivileged_mode
,
233 const char *t2
, *trigger
;
239 /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
240 * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
241 * before stat()ing */
242 trigger
= strjoina(path
, "/trigger"); /* Filename doesn't matter... */
243 (void) access(trigger
, F_OK
);
245 if (stat(path
, &st
) < 0)
246 return log_full_errno((searching
&& errno
== ENOENT
) ||
247 (unprivileged_mode
&& errno
== EACCES
) ? LOG_DEBUG
: LOG_ERR
, errno
,
248 "Failed to determine block device node of \"%s\": %m", path
);
250 if (major(st
.st_dev
) == 0)
251 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
252 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
253 "Block device node of \"%s\" is invalid.", path
);
255 if (path_equal(path
, "/")) {
256 /* Let's assume that the root directory of the OS is always the root of its file system
257 * (which technically doesn't have to be the case, but it's close enough, and it's not easy
258 * to be fully correct for it, since we can't look further up than the root dir easily.) */
260 *ret_dev
= st
.st_dev
;
265 t2
= strjoina(path
, "/..");
266 if (stat(t2
, &st2
) < 0) {
270 _cleanup_free_
char *parent
= NULL
;
272 /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
273 * directly instead. It's not as good, due to symlinks and such, but we can't do
274 * anything better here. */
276 parent
= dirname_malloc(path
);
280 r
= RET_NERRNO(stat(parent
, &st2
));
284 return log_full_errno(unprivileged_mode
&& r
== -EACCES
? LOG_DEBUG
: LOG_ERR
, r
,
285 "Failed to determine block device node of parent of \"%s\": %m", path
);
288 if (st
.st_dev
== st2
.st_dev
)
289 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
290 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
291 "Directory \"%s\" is not the root of the file system.", path
);
294 *ret_dev
= st
.st_dev
;
299 static int verify_esp(
302 bool unprivileged_mode
,
304 uint64_t *ret_pstart
,
306 sd_id128_t
*ret_uuid
,
315 /* This logs about all errors, except:
317 * -ENOENT → if 'searching' is set, and the dir doesn't exist
318 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
319 * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
322 relax_checks
= getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
324 /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
325 * issues. Let's also, silence the error messages. */
330 if (statfs(p
, &sfs
) < 0)
331 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
332 return log_full_errno((searching
&& errno
== ENOENT
) ||
333 (unprivileged_mode
&& errno
== EACCES
) ? LOG_DEBUG
: LOG_ERR
, errno
,
334 "Failed to check file system type of \"%s\": %m", p
);
336 if (!F_TYPE_EQUAL(sfs
.f_type
, MSDOS_SUPER_MAGIC
))
337 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
338 SYNTHETIC_ERRNO(searching
? EADDRNOTAVAIL
: ENODEV
),
339 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p
);
342 r
= verify_fsroot_dir(p
, searching
, unprivileged_mode
, &devid
);
346 /* In a container we don't have access to block devices, skip this part of the verification, we trust
347 * the container manager set everything up correctly on its own. */
348 if (detect_container() > 0 || relax_checks
)
351 /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
352 * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
353 * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
354 * however blkid can't work if we have no privileges to access block devices directly, which is why
355 * we use udev in that case. */
356 if (unprivileged_mode
)
357 r
= verify_esp_udev(devid
, searching
, ret_part
, ret_pstart
, ret_psize
, ret_uuid
);
359 r
= verify_esp_blkid(devid
, searching
, ret_part
, ret_pstart
, ret_psize
, ret_uuid
);
376 *ret_uuid
= SD_ID128_NULL
;
383 int find_esp_and_warn(
385 bool unprivileged_mode
,
388 uint64_t *ret_pstart
,
390 sd_id128_t
*ret_uuid
,
395 /* This logs about all errors except:
397 * -ENOKEY → when we can't find the partition
398 * -EACCESS → when unprivileged_mode is true, and we can't access something
402 r
= verify_esp(path
, /* searching= */ false, unprivileged_mode
, ret_part
, ret_pstart
, ret_psize
, ret_uuid
, ret_devid
);
409 path
= getenv("SYSTEMD_ESP_PATH");
413 if (!path_is_valid(path
) || !path_is_absolute(path
))
414 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
415 "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
418 /* Note: when the user explicitly configured things with an env var we won't validate the
419 * path beyond checking it refers to a directory. After all we want this to be useful for
422 if (stat(path
, &st
) < 0)
423 return log_error_errno(errno
, "Failed to stat '%s': %m", path
);
424 if (!S_ISDIR(st
.st_mode
))
425 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR
), "ESP path '%s' is not a directory.", path
);
434 *ret_uuid
= SD_ID128_NULL
;
436 *ret_devid
= st
.st_dev
;
441 FOREACH_STRING(_path
, "/efi", "/boot", "/boot/efi") {
444 r
= verify_esp(path
, /* searching= */ true, unprivileged_mode
, ret_part
, ret_pstart
, ret_psize
, ret_uuid
, ret_devid
);
447 if (!IN_SET(r
, -ENOENT
, -EADDRNOTAVAIL
)) /* This one is not it */
451 /* No logging here */
468 static int verify_xbootldr_blkid(
471 sd_id128_t
*ret_uuid
) {
473 sd_id128_t uuid
= SD_ID128_NULL
;
476 _cleanup_(blkid_free_probep
) blkid_probe b
= NULL
;
477 _cleanup_free_
char *node
= NULL
;
478 const char *type
, *v
;
481 r
= device_path_make_major_minor(S_IFBLK
, devid
, &node
);
483 return log_error_errno(r
, "Failed to format block device path for %u:%u: %m",
484 major(devid
), minor(devid
));
487 b
= blkid_new_probe_from_filename(node
);
489 return log_error_errno(errno
?: SYNTHETIC_ERRNO(ENOMEM
), "%s: Failed to create blkid probe: %m", node
);
491 blkid_probe_enable_partitions(b
, 1);
492 blkid_probe_set_partitions_flags(b
, BLKID_PARTS_ENTRY_DETAILS
);
495 r
= blkid_do_safeprobe(b
);
497 return log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "%s: File system is ambiguous.", node
);
499 return log_error_errno(SYNTHETIC_ERRNO(ENODEV
), "%s: File system does not contain a label.", node
);
501 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "%s: Failed to probe file system: %m", node
);
504 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_SCHEME", &type
, NULL
);
506 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "%s: Failed to probe PART_ENTRY_SCHEME: %m", node
);
507 if (streq(type
, "gpt")) {
510 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_TYPE", &v
, NULL
);
512 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "%s: Failed to probe PART_ENTRY_TYPE: %m", node
);
513 if (id128_equal_string(v
, GPT_XBOOTLDR
) <= 0)
514 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
515 searching
? SYNTHETIC_ERRNO(EADDRNOTAVAIL
) : SYNTHETIC_ERRNO(ENODEV
),
516 "%s: Partitition has wrong PART_ENTRY_TYPE=%s for XBOOTLDR partition.", node
, v
);
519 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_UUID", &v
, NULL
);
521 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "%s: Failed to probe PART_ENTRY_UUID: %m", node
);
522 r
= sd_id128_from_string(v
, &uuid
);
524 return log_error_errno(r
, "%s: Partition has invalid UUID PART_ENTRY_TYPE=%s: %m", node
, v
);
526 } else if (streq(type
, "dos")) {
529 r
= blkid_probe_lookup_value(b
, "PART_ENTRY_TYPE", &v
, NULL
);
531 return log_error_errno(errno
?: SYNTHETIC_ERRNO(EIO
), "%s: Failed to probe PART_ENTRY_TYPE: %m", node
);
532 if (!streq(v
, "0xea"))
533 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
534 searching
? SYNTHETIC_ERRNO(EADDRNOTAVAIL
) : SYNTHETIC_ERRNO(ENODEV
),
535 "%s: Wrong PART_ENTRY_TYPE=%s for XBOOTLDR partition.", node
, v
);
538 return log_full_errno(searching
? LOG_DEBUG
: LOG_ERR
,
539 searching
? SYNTHETIC_ERRNO(EADDRNOTAVAIL
) : SYNTHETIC_ERRNO(ENODEV
),
540 "%s: Not on a GPT or DOS partition table (PART_ENTRY_SCHEME=%s).", node
, type
);
549 static int verify_xbootldr_udev(
552 sd_id128_t
*ret_uuid
) {
554 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
555 _cleanup_free_
char *node
= NULL
;
556 sd_id128_t uuid
= SD_ID128_NULL
;
557 const char *type
, *v
;
560 r
= device_path_make_major_minor(S_IFBLK
, devid
, &node
);
562 return log_error_errno(r
, "Failed to format block device path for %u:%u: %m",
563 major(devid
), minor(devid
));
565 r
= sd_device_new_from_devnum(&d
, 'b', devid
);
567 return log_error_errno(r
, "%s: Failed to get block device: %m", node
);
569 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_SCHEME", &type
);
571 return log_device_error_errno(d
, r
, "Failed to query ID_PART_ENTRY_SCHEME: %m");
573 if (streq(type
, "gpt")) {
575 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_TYPE", &v
);
577 return log_device_error_errno(d
, r
, "Failed to query ID_PART_ENTRY_TYPE: %m");
579 r
= id128_equal_string(v
, GPT_XBOOTLDR
);
581 return log_device_error_errno(d
, r
, "Failed to parse ID_PART_ENTRY_TYPE=%s: %m", v
);
583 return log_device_full_errno(
585 searching
? LOG_DEBUG
: LOG_ERR
,
586 searching
? SYNTHETIC_ERRNO(EADDRNOTAVAIL
) : SYNTHETIC_ERRNO(ENODEV
),
587 "Partition has wrong ID_PART_ENTRY_TYPE=%s for XBOOTLDR partition.", v
);
589 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_UUID", &v
);
591 return log_device_error_errno(d
, r
, "Failed to query ID_PART_ENTRY_UUID: %m");
592 r
= sd_id128_from_string(v
, &uuid
);
594 return log_device_error_errno(d
, r
, "Partition has invalid UUID ID_PART_ENTRY_TYPE=%s: %m", v
);
596 } else if (streq(type
, "dos")) {
598 r
= sd_device_get_property_value(d
, "ID_PART_ENTRY_TYPE", &v
);
600 return log_device_error_errno(d
, r
, "Failed to query ID_PART_ENTRY_TYPE: %m");
601 if (!streq(v
, "0xea"))
602 return log_device_full_errno(
604 searching
? LOG_DEBUG
: LOG_ERR
,
605 searching
? SYNTHETIC_ERRNO(EADDRNOTAVAIL
) : SYNTHETIC_ERRNO(ENODEV
),
606 "Wrong ID_PART_ENTRY_TYPE=%s for XBOOTLDR partition.", v
);
609 return log_device_full_errno(
611 searching
? LOG_DEBUG
: LOG_ERR
,
612 searching
? SYNTHETIC_ERRNO(EADDRNOTAVAIL
) : SYNTHETIC_ERRNO(ENODEV
),
613 "Not on a GPT or DOS partition table (ID_PART_ENTRY_SCHEME=%s).", type
);
621 static int verify_xbootldr(
624 bool unprivileged_mode
,
625 sd_id128_t
*ret_uuid
,
634 relax_checks
= getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
636 r
= verify_fsroot_dir(p
, searching
, unprivileged_mode
, &devid
);
640 if (detect_container() > 0 || relax_checks
)
643 if (unprivileged_mode
)
644 r
= verify_xbootldr_udev(devid
, searching
, ret_uuid
);
646 r
= verify_xbootldr_blkid(devid
, searching
, ret_uuid
);
657 *ret_uuid
= SD_ID128_NULL
;
664 int find_xbootldr_and_warn(
666 bool unprivileged_mode
,
668 sd_id128_t
*ret_uuid
,
673 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
676 r
= verify_xbootldr(path
, /* searching= */ false, unprivileged_mode
, ret_uuid
, ret_devid
);
683 path
= getenv("SYSTEMD_XBOOTLDR_PATH");
687 if (!path_is_valid(path
) || !path_is_absolute(path
))
688 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
689 "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
692 if (stat(path
, &st
) < 0)
693 return log_error_errno(errno
, "Failed to stat '%s': %m", path
);
694 if (!S_ISDIR(st
.st_mode
))
695 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR
), "XBOOTLDR path '%s' is not a directory.", path
);
698 *ret_uuid
= SD_ID128_NULL
;
700 *ret_devid
= st
.st_dev
;
705 r
= verify_xbootldr("/boot", true, unprivileged_mode
, ret_uuid
, ret_devid
);
710 if (!IN_SET(r
, -ENOENT
, -EADDRNOTAVAIL
)) /* This one is not it */