]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/find-esp.c
find-esp: do not fail when /boot on btrfs RAID on searching ESP or xbootldr
[thirdparty/systemd.git] / src / shared / find-esp.c
CommitLineData
e94830c0
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <linux/magic.h>
7176f06c 4#include <sys/vfs.h>
e94830c0
LP
5
6#include "sd-device.h"
e1614484 7#include "sd-id128.h"
e94830c0
LP
8
9#include "alloc-util.h"
10#include "blkid-util.h"
bd80fd7e 11#include "btrfs-util.h"
f461a28d 12#include "chase.h"
13d7c841 13#include "device-util.h"
7176f06c 14#include "devnum-util.h"
e94830c0
LP
15#include "env-util.h"
16#include "errno-util.h"
bd80fd7e 17#include "fd-util.h"
e94830c0
LP
18#include "find-esp.h"
19#include "gpt.h"
506c1bb5 20#include "mount-util.h"
e94830c0
LP
21#include "parse-util.h"
22#include "path-util.h"
23#include "stat-util.h"
24#include "string-util.h"
25#include "virt.h"
26
80a2381d
LB
27typedef enum VerifyESPFlags {
28 VERIFY_ESP_SEARCHING = 1 << 0, /* Downgrade various "not found" logs to debug level */
29 VERIFY_ESP_UNPRIVILEGED_MODE = 1 << 1, /* Call into udev rather than blkid */
30 VERIFY_ESP_RELAX_CHECKS = 1 << 2, /* Do not validate ESP partition */
31} VerifyESPFlags;
32
e94830c0
LP
33static int verify_esp_blkid(
34 dev_t devid,
0b2aa206 35 VerifyESPFlags flags,
e94830c0
LP
36 uint32_t *ret_part,
37 uint64_t *ret_pstart,
38 uint64_t *ret_psize,
39 sd_id128_t *ret_uuid) {
40
41 sd_id128_t uuid = SD_ID128_NULL;
42 uint64_t pstart = 0, psize = 0;
43 uint32_t part = 0;
44
45#if HAVE_BLKID
46 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
47 _cleanup_free_ char *node = NULL;
0b2aa206 48 bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING);
e94830c0
LP
49 const char *v;
50 int r;
51
4fe46c34 52 r = devname_from_devnum(S_IFBLK, devid, &node);
e94830c0 53 if (r < 0)
ca822829 54 return log_error_errno(r, "Failed to get device path for " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(devid));
e94830c0
LP
55
56 errno = 0;
57 b = blkid_new_probe_from_filename(node);
58 if (!b)
59 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
60
61 blkid_probe_enable_superblocks(b, 1);
62 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
63 blkid_probe_enable_partitions(b, 1);
64 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
65
66 errno = 0;
67 r = blkid_do_safeprobe(b);
68 if (r == -2)
69 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
14e5c992 70 if (r == 1)
e94830c0 71 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
14e5c992 72 if (r != 0)
e94830c0
LP
73 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
74
75 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
76 if (r != 0)
77 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
78 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
79 "No filesystem found on \"%s\": %m", node);
80 if (!streq(v, "vfat"))
81 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
82 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
83 "File system \"%s\" is not FAT.", node);
84
85 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
86 if (r != 0)
87 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
88 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
89 "File system \"%s\" is not located on a partitioned block device.", node);
90 if (!streq(v, "gpt"))
91 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
92 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
93 "File system \"%s\" is not on a GPT partition table.", node);
94
95 errno = 0;
96 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
97 if (r != 0)
98 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
92e72028 99 if (sd_id128_string_equal(v, SD_GPT_ESP) <= 0)
e94830c0
LP
100 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
101 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
102 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
103
104 errno = 0;
105 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
106 if (r != 0)
107 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
108 r = sd_id128_from_string(v, &uuid);
109 if (r < 0)
110 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
111
112 errno = 0;
113 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
114 if (r != 0)
115 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
116 r = safe_atou32(v, &part);
117 if (r < 0)
118 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
119
120 errno = 0;
121 r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
122 if (r != 0)
123 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
124 r = safe_atou64(v, &pstart);
125 if (r < 0)
126 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
127
128 errno = 0;
129 r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
130 if (r != 0)
131 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
132 r = safe_atou64(v, &psize);
133 if (r < 0)
134 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
135#endif
136
137 if (ret_part)
138 *ret_part = part;
139 if (ret_pstart)
140 *ret_pstart = pstart;
141 if (ret_psize)
142 *ret_psize = psize;
143 if (ret_uuid)
144 *ret_uuid = uuid;
145
146 return 0;
147}
148
149static int verify_esp_udev(
150 dev_t devid,
0b2aa206 151 VerifyESPFlags flags,
e94830c0
LP
152 uint32_t *ret_part,
153 uint64_t *ret_pstart,
154 uint64_t *ret_psize,
155 sd_id128_t *ret_uuid) {
156
0b2aa206 157 bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING);
e94830c0 158 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
e94830c0
LP
159 sd_id128_t uuid = SD_ID128_NULL;
160 uint64_t pstart = 0, psize = 0;
161 uint32_t part = 0;
ca822829 162 const char *node, *v;
e94830c0
LP
163 int r;
164
e94830c0
LP
165 r = sd_device_new_from_devnum(&d, 'b', devid);
166 if (r < 0)
167 return log_error_errno(r, "Failed to get device from device number: %m");
168
ca822829
YW
169 r = sd_device_get_devname(d, &node);
170 if (r < 0)
388d1465 171 return log_device_error_errno(d, r, "Failed to get device node: %m");
ca822829 172
e94830c0
LP
173 r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
174 if (r < 0)
388d1465 175 return log_device_error_errno(d, r, "Failed to get device property: %m");
e94830c0 176 if (!streq(v, "vfat"))
388d1465
YW
177 return log_device_full_errno(d,
178 searching ? LOG_DEBUG : LOG_ERR,
179 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
180 "File system \"%s\" is not FAT.", node );
e94830c0
LP
181
182 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
183 if (r < 0)
388d1465
YW
184 return log_device_full_errno(d,
185 searching && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
186 searching && r == -ENOENT ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : r,
187 "Failed to get device property: %m");
e94830c0 188 if (!streq(v, "gpt"))
388d1465
YW
189 return log_device_full_errno(d,
190 searching ? LOG_DEBUG : LOG_ERR,
191 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
192 "File system \"%s\" is not on a GPT partition table.", node);
e94830c0
LP
193
194 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
195 if (r < 0)
388d1465 196 return log_device_error_errno(d, r, "Failed to get device property: %m");
92e72028 197 if (sd_id128_string_equal(v, SD_GPT_ESP) <= 0)
388d1465
YW
198 return log_device_full_errno(d,
199 searching ? LOG_DEBUG : LOG_ERR,
200 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
201 "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
e94830c0
LP
202
203 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
204 if (r < 0)
388d1465 205 return log_device_error_errno(d, r, "Failed to get device property: %m");
e94830c0
LP
206 r = sd_id128_from_string(v, &uuid);
207 if (r < 0)
388d1465 208 return log_device_error_errno(d, r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
e94830c0
LP
209
210 r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
211 if (r < 0)
388d1465 212 return log_device_error_errno(d, r, "Failed to get device property: %m");
e94830c0
LP
213 r = safe_atou32(v, &part);
214 if (r < 0)
388d1465 215 return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_NUMBER field.");
e94830c0
LP
216
217 r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
218 if (r < 0)
388d1465 219 return log_device_error_errno(d, r, "Failed to get device property: %m");
e94830c0
LP
220 r = safe_atou64(v, &pstart);
221 if (r < 0)
388d1465 222 return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_OFFSET field.");
e94830c0
LP
223
224 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
225 if (r < 0)
388d1465 226 return log_device_error_errno(d, r, "Failed to get device property: %m");
e94830c0
LP
227 r = safe_atou64(v, &psize);
228 if (r < 0)
388d1465 229 return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_SIZE field.");
e94830c0
LP
230
231 if (ret_part)
232 *ret_part = part;
233 if (ret_pstart)
234 *ret_pstart = pstart;
235 if (ret_psize)
236 *ret_psize = psize;
237 if (ret_uuid)
238 *ret_uuid = uuid;
239
240 return 0;
241}
242
243static int verify_fsroot_dir(
63105f33 244 int dir_fd,
e94830c0 245 const char *path,
0b2aa206 246 VerifyESPFlags flags,
e94830c0
LP
247 dev_t *ret_dev) {
248
0b2aa206
LP
249 bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
250 unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
63105f33 251 _cleanup_free_ char *f = NULL;
bd80fd7e
LP
252 STRUCT_NEW_STATX_DEFINE(sxa);
253 STRUCT_NEW_STATX_DEFINE(sxb);
e94830c0
LP
254 int r;
255
bd80fd7e
LP
256 /* Checks if the specified directory is at the root of its file system, and returns device
257 * major/minor of the device, if it is. */
258
63105f33 259 assert(dir_fd >= 0);
e94830c0 260 assert(path);
bd80fd7e 261
63105f33
DDM
262 /* We pass the full path from the root directory file descriptor so we can use it for logging, but
263 * dir_fd points to the parent directory of the final component of the given path, so we extract the
264 * filename and operate on that. */
e94830c0 265
63105f33
DDM
266 r = path_extract_filename(path, &f);
267 if (r < 0 && r != -EADDRNOTAVAIL)
268 return log_error_errno(r, "Failed to extract filename of %s: %m", path);
269
270 r = statx_fallback(dir_fd, strempty(f), AT_SYMLINK_NOFOLLOW|(isempty(f) ? AT_EMPTY_PATH : 0),
271 STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxa.sx);
bd80fd7e 272 if (r < 0)
63105f33
DDM
273 return log_full_errno((searching && r == -ENOENT) ||
274 (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, r,
e94830c0
LP
275 "Failed to determine block device node of \"%s\": %m", path);
276
bd80fd7e 277 assert(S_ISDIR(sxa.sx.stx_mode)); /* We used O_DIRECTORY above, when opening, so this must hold */
e94830c0 278
bd80fd7e 279 if (FLAGS_SET(sxa.sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) {
e94830c0 280
bd80fd7e
LP
281 /* If we have STATX_ATTR_MOUNT_ROOT, we are happy, that's all we need. We operate under the
282 * assumption that a top of a mount point is also the top of the file system. (Which of
283 * course is strictly speaking not always true...) */
e94830c0 284
bd80fd7e
LP
285 if (!FLAGS_SET(sxa.sx.stx_attributes, STATX_ATTR_MOUNT_ROOT))
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);
e94830c0 289
bd80fd7e
LP
290 goto success;
291 }
e94830c0 292
bd80fd7e 293 /* Now let's look at the parent */
63105f33 294 r = statx_fallback(dir_fd, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxb.sx);
bd80fd7e
LP
295 if (r < 0)
296 return log_full_errno(unprivileged_mode && ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_ERR, r,
297 "Failed to determine block device node of parent of \"%s\": %m", path);
298
299 if (statx_inode_same(&sxa.sx, &sxb.sx)) /* for the root dir inode nr for both inodes will be the same */
300 goto success;
e94830c0 301
bd80fd7e 302 if (statx_mount_same(&sxa.nsx, &sxb.nsx))
e94830c0
LP
303 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
304 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
305 "Directory \"%s\" is not the root of the file system.", path);
306
bd80fd7e
LP
307success:
308 if (!ret_dev)
309 return 0;
310
c706b27f 311 if (sxa.sx.stx_dev_major == 0) /* Hmm, maybe a btrfs device, and the caller asked for the backing device? Then let's try to get it. */
63105f33 312 return btrfs_get_block_device_at(dir_fd, strempty(f), ret_dev);
bd80fd7e
LP
313
314 *ret_dev = makedev(sxa.sx.stx_dev_major, sxa.sx.stx_dev_minor);
e94830c0
LP
315 return 0;
316}
317
318static int verify_esp(
63105f33
DDM
319 int rfd,
320 const char *path,
321 char **ret_path,
e94830c0
LP
322 uint32_t *ret_part,
323 uint64_t *ret_pstart,
324 uint64_t *ret_psize,
325 sd_id128_t *ret_uuid,
80a2381d
LB
326 dev_t *ret_devid,
327 VerifyESPFlags flags) {
e94830c0 328
3f9b0e13 329 bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
80a2381d 330 unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
63105f33
DDM
331 _cleanup_free_ char *p = NULL;
332 _cleanup_close_ int pfd = -EBADF;
bd80fd7e 333 dev_t devid = 0;
e94830c0
LP
334 int r;
335
63105f33
DDM
336 assert(rfd >= 0 || rfd == AT_FDCWD);
337 assert(path);
e94830c0
LP
338
339 /* This logs about all errors, except:
340 *
341 * -ENOENT → if 'searching' is set, and the dir doesn't exist
342 * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
343 * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
344 */
345
3f9b0e13
DDM
346 relax_checks =
347 getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 ||
348 FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS);
e94830c0 349
3f9b0e13
DDM
350 /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
351 * issues. Let's also, silence the error messages. */
e94830c0 352
63105f33
DDM
353 r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT, &p, &pfd);
354 if (r < 0)
355 return log_full_errno((searching && r == -ENOENT) ||
356 (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR,
357 r, "Failed to open parent directory of \"%s\": %m", path);
358
3f9b0e13 359 if (!relax_checks) {
63105f33 360 _cleanup_free_ char *f = NULL;
3f9b0e13 361 struct statfs sfs;
e94830c0 362
63105f33
DDM
363 r = path_extract_filename(p, &f);
364 if (r < 0 && r != -EADDRNOTAVAIL)
365 return log_error_errno(r, "Failed to extract filename of %s: %m", p);
366
506c1bb5
DDM
367 /* Trigger any automounts so that xstatfsat() operates on the mount instead of the mountpoint
368 * directory. */
369 r = trigger_automount_at(pfd, f);
370 if (r < 0)
371 return log_error_errno(r, "Failed to trigger automount at %s: %m", p);
372
63105f33
DDM
373 r = xstatfsat(pfd, strempty(f), &sfs);
374 if (r < 0)
3f9b0e13 375 /* If we are searching for the mount point, don't generate a log message if we can't find the path */
63105f33
DDM
376 return log_full_errno((searching && r == -ENOENT) ||
377 (unprivileged_mode && r == -EACCES) ? LOG_DEBUG : LOG_ERR, r,
3f9b0e13 378 "Failed to check file system type of \"%s\": %m", p);
bd80fd7e 379
3f9b0e13
DDM
380 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
381 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
382 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
383 "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
384 }
385
386 relax_checks =
387 relax_checks ||
388 detect_container() > 0;
389
0b2aa206 390 r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid);
e94830c0
LP
391 if (r < 0)
392 return r;
393
394 /* In a container we don't have access to block devices, skip this part of the verification, we trust
395 * the container manager set everything up correctly on its own. */
3f9b0e13 396 if (relax_checks)
e94830c0
LP
397 goto finish;
398
5c831dde
YW
399 if (devnum_is_zero(devid))
400 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
401 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
402 "Could not determine backing block device of directory \"%s\" (btrfs RAID?).", p);
403
e94830c0
LP
404 /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
405 * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
406 * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
407 * however blkid can't work if we have no privileges to access block devices directly, which is why
408 * we use udev in that case. */
409 if (unprivileged_mode)
0b2aa206 410 r = verify_esp_udev(devid, flags, ret_part, ret_pstart, ret_psize, ret_uuid);
e94830c0 411 else
0b2aa206 412 r = verify_esp_blkid(devid, flags, ret_part, ret_pstart, ret_psize, ret_uuid);
e94830c0
LP
413 if (r < 0)
414 return r;
415
63105f33
DDM
416 if (ret_path)
417 *ret_path = TAKE_PTR(p);
e94830c0
LP
418 if (ret_devid)
419 *ret_devid = devid;
420
421 return 0;
422
423finish:
63105f33
DDM
424 if (ret_path)
425 *ret_path = TAKE_PTR(p);
e94830c0
LP
426 if (ret_part)
427 *ret_part = 0;
428 if (ret_pstart)
429 *ret_pstart = 0;
430 if (ret_psize)
431 *ret_psize = 0;
432 if (ret_uuid)
433 *ret_uuid = SD_ID128_NULL;
434 if (ret_devid)
435 *ret_devid = 0;
436
437 return 0;
438}
439
63105f33
DDM
440int find_esp_and_warn_at(
441 int rfd,
e94830c0 442 const char *path,
0b2aa206 443 int unprivileged_mode,
e94830c0
LP
444 char **ret_path,
445 uint32_t *ret_part,
446 uint64_t *ret_pstart,
447 uint64_t *ret_psize,
448 sd_id128_t *ret_uuid,
449 dev_t *ret_devid) {
450
0b2aa206 451 VerifyESPFlags flags;
e94830c0
LP
452 int r;
453
454 /* This logs about all errors except:
455 *
456 * -ENOKEY → when we can't find the partition
457 * -EACCESS → when unprivileged_mode is true, and we can't access something
458 */
459
63105f33 460 assert(rfd >= 0 || rfd == AT_FDCWD);
80a2381d 461
0b2aa206
LP
462 if (unprivileged_mode < 0)
463 unprivileged_mode = geteuid() != 0;
464 flags = unprivileged_mode > 0 ? VERIFY_ESP_UNPRIVILEGED_MODE : 0;
465
e212f422 466 r = dir_fd_is_root_or_cwd(rfd);
63105f33
DDM
467 if (r < 0)
468 return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
469 if (r == 0)
470 flags |= VERIFY_ESP_RELAX_CHECKS;
e94830c0 471
63105f33
DDM
472 if (path)
473 return verify_esp(rfd, path, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
e94830c0
LP
474
475 path = getenv("SYSTEMD_ESP_PATH");
476 if (path) {
63105f33
DDM
477 _cleanup_free_ char *p = NULL;
478 _cleanup_close_ int fd = -EBADF;
e94830c0
LP
479 struct stat st;
480
63105f33 481 if (!path_is_valid(path) || !path_is_absolute(path))
e94830c0 482 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
63105f33
DDM
483 "$SYSTEMD_ESP_PATH does not refer to an absolute path, refusing to use it: %s",
484 path);
485
486 r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT, &p, &fd);
487 if (r < 0)
488 return log_error_errno(r, "Failed to resolve path %s: %m", path);
e94830c0
LP
489
490 /* Note: when the user explicitly configured things with an env var we won't validate the
491 * path beyond checking it refers to a directory. After all we want this to be useful for
492 * testing. */
493
63105f33 494 if (fstat(fd, &st) < 0)
80a2381d 495 return log_error_errno(errno, "Failed to stat '%s': %m", p);
e94830c0 496 if (!S_ISDIR(st.st_mode))
80a2381d 497 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", p);
e94830c0 498
63105f33
DDM
499 if (ret_path)
500 *ret_path = TAKE_PTR(p);
e94830c0
LP
501 if (ret_part)
502 *ret_part = 0;
503 if (ret_pstart)
504 *ret_pstart = 0;
505 if (ret_psize)
506 *ret_psize = 0;
507 if (ret_uuid)
508 *ret_uuid = SD_ID128_NULL;
509 if (ret_devid)
510 *ret_devid = st.st_dev;
511
63105f33 512 return 0;
e94830c0
LP
513 }
514
80a2381d 515 FOREACH_STRING(dir, "/efi", "/boot", "/boot/efi") {
63105f33 516 r = verify_esp(rfd, dir, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid,
80a2381d 517 flags | VERIFY_ESP_SEARCHING);
e94830c0 518 if (r >= 0)
63105f33 519 return 0;
ed89819f 520 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR, -ENOTTY)) /* This one is not it */
e94830c0
LP
521 return r;
522 }
523
524 /* No logging here */
525 return -ENOKEY;
63105f33 526}
e94830c0 527
63105f33
DDM
528int find_esp_and_warn(
529 const char *root,
530 const char *path,
0b2aa206 531 int unprivileged_mode,
63105f33
DDM
532 char **ret_path,
533 uint32_t *ret_part,
534 uint64_t *ret_pstart,
535 uint64_t *ret_psize,
536 sd_id128_t *ret_uuid,
537 dev_t *ret_devid) {
538
539 _cleanup_close_ int rfd = -EBADF;
540 _cleanup_free_ char *p = NULL;
541 uint32_t part;
542 uint64_t pstart, psize;
543 sd_id128_t uuid;
544 dev_t devid;
545 int r;
546
547 rfd = open(empty_to_root(root), O_PATH|O_DIRECTORY|O_CLOEXEC);
548 if (rfd < 0)
549 return -errno;
550
551 r = find_esp_and_warn_at(rfd, path, unprivileged_mode,
552 ret_path ? &p : NULL,
553 ret_part ? &part : NULL,
554 ret_pstart ? &pstart : NULL,
555 ret_psize ? &psize : NULL,
556 ret_uuid ? &uuid : NULL,
557 ret_devid ? &devid : NULL);
558 if (r < 0)
559 return r;
560
561 if (ret_path) {
60e761d8 562 r = chaseat_prefix_root(p, root, ret_path);
fb4d9bf4
YW
563 if (r < 0)
564 return r;
63105f33
DDM
565 }
566 if (ret_part)
567 *ret_part = part;
568 if (ret_pstart)
569 *ret_pstart = pstart;
570 if (ret_psize)
571 *ret_psize = psize;
572 if (ret_uuid)
573 *ret_uuid = uuid;
574 if (ret_devid)
575 *ret_devid = devid;
e94830c0
LP
576
577 return 0;
578}
579
580static int verify_xbootldr_blkid(
581 dev_t devid,
0b2aa206 582 VerifyESPFlags flags,
e94830c0
LP
583 sd_id128_t *ret_uuid) {
584
585 sd_id128_t uuid = SD_ID128_NULL;
586
587#if HAVE_BLKID
0b2aa206 588 bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING);
e94830c0
LP
589 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
590 _cleanup_free_ char *node = NULL;
13d7c841 591 const char *type, *v;
e94830c0
LP
592 int r;
593
4fe46c34 594 r = devname_from_devnum(S_IFBLK, devid, &node);
e94830c0 595 if (r < 0)
ca822829
YW
596 return log_error_errno(r, "Failed to get block device path for " DEVNUM_FORMAT_STR ": %m",
597 DEVNUM_FORMAT_VAL(devid));
13d7c841 598
e94830c0
LP
599 errno = 0;
600 b = blkid_new_probe_from_filename(node);
601 if (!b)
ef1f0a14 602 return log_error_errno(errno_or_else(ENOMEM), "%s: Failed to create blkid probe: %m", node);
e94830c0
LP
603
604 blkid_probe_enable_partitions(b, 1);
605 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
606
607 errno = 0;
608 r = blkid_do_safeprobe(b);
2e3944b8 609 if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
13d7c841 610 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system is ambiguous.", node);
2e3944b8 611 if (r == _BLKID_SAFEPROBE_NOT_FOUND)
13d7c841 612 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system does not contain a label.", node);
2e3944b8
LP
613 if (r == _BLKID_SAFEPROBE_ERROR)
614 return log_error_errno(errno_or_else(EIO), "%s: Failed to probe file system: %m", node);
615
616 assert(r == _BLKID_SAFEPROBE_FOUND);
e94830c0 617
13d7c841 618 r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &type, NULL);
e94830c0 619 if (r != 0)
01f234c6
YW
620 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
621 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(EIO),
622 "%s: Failed to probe PART_ENTRY_SCHEME: %m", node);
13d7c841 623 if (streq(type, "gpt")) {
e94830c0
LP
624
625 errno = 0;
626 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
627 if (r != 0)
ef1f0a14 628 return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
92e72028 629 if (sd_id128_string_equal(v, SD_GPT_XBOOTLDR) <= 0)
e94830c0
LP
630 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
631 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
dc3b5e04 632 "%s: Partition has wrong PART_ENTRY_TYPE=%s for XBOOTLDR partition.", node, v);
e94830c0
LP
633
634 errno = 0;
635 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
636 if (r != 0)
ef1f0a14 637 return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_UUID: %m", node);
e94830c0
LP
638 r = sd_id128_from_string(v, &uuid);
639 if (r < 0)
13d7c841 640 return log_error_errno(r, "%s: Partition has invalid UUID PART_ENTRY_TYPE=%s: %m", node, v);
e94830c0 641
13d7c841 642 } else if (streq(type, "dos")) {
e94830c0
LP
643
644 errno = 0;
645 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
646 if (r != 0)
ef1f0a14 647 return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
e94830c0
LP
648 if (!streq(v, "0xea"))
649 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
650 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
13d7c841 651 "%s: Wrong PART_ENTRY_TYPE=%s for XBOOTLDR partition.", node, v);
e94830c0
LP
652
653 } else
654 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
655 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
13d7c841 656 "%s: Not on a GPT or DOS partition table (PART_ENTRY_SCHEME=%s).", node, type);
e94830c0
LP
657#endif
658
659 if (ret_uuid)
660 *ret_uuid = uuid;
661
662 return 0;
663}
664
665static int verify_xbootldr_udev(
666 dev_t devid,
0b2aa206 667 VerifyESPFlags flags,
e94830c0
LP
668 sd_id128_t *ret_uuid) {
669
0b2aa206 670 bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING);
e94830c0 671 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
e94830c0 672 sd_id128_t uuid = SD_ID128_NULL;
ca822829 673 const char *node, *type, *v;
e94830c0
LP
674 int r;
675
ca822829 676 r = sd_device_new_from_devnum(&d, 'b', devid);
e94830c0 677 if (r < 0)
ca822829 678 return log_error_errno(r, "Failed to get block device for " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(devid));
e94830c0 679
ca822829 680 r = sd_device_get_devname(d, &node);
e94830c0 681 if (r < 0)
388d1465 682 return log_device_error_errno(d, r, "Failed to get device node: %m");
e94830c0 683
13d7c841 684 r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &type);
e94830c0 685 if (r < 0)
01f234c6
YW
686 return log_device_full_errno(d,
687 searching && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
688 searching && r == -ENOENT ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : r,
689 "Failed to query ID_PART_ENTRY_SCHEME: %m");
e94830c0 690
13d7c841 691 if (streq(type, "gpt")) {
e94830c0
LP
692
693 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
694 if (r < 0)
13d7c841 695 return log_device_error_errno(d, r, "Failed to query ID_PART_ENTRY_TYPE: %m");
4e124425 696
92e72028 697 r = sd_id128_string_equal(v, SD_GPT_XBOOTLDR);
4e124425 698 if (r < 0)
13d7c841 699 return log_device_error_errno(d, r, "Failed to parse ID_PART_ENTRY_TYPE=%s: %m", v);
4e124425 700 if (r == 0)
13d7c841
ZJS
701 return log_device_full_errno(
702 d,
703 searching ? LOG_DEBUG : LOG_ERR,
704 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
5473bc61 705 "Partition has wrong ID_PART_ENTRY_TYPE=%s for XBOOTLDR partition.", v);
e94830c0
LP
706
707 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
708 if (r < 0)
13d7c841 709 return log_device_error_errno(d, r, "Failed to query ID_PART_ENTRY_UUID: %m");
e94830c0
LP
710 r = sd_id128_from_string(v, &uuid);
711 if (r < 0)
13d7c841 712 return log_device_error_errno(d, r, "Partition has invalid UUID ID_PART_ENTRY_TYPE=%s: %m", v);
e94830c0 713
13d7c841 714 } else if (streq(type, "dos")) {
e94830c0
LP
715
716 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
717 if (r < 0)
13d7c841 718 return log_device_error_errno(d, r, "Failed to query ID_PART_ENTRY_TYPE: %m");
e94830c0 719 if (!streq(v, "0xea"))
13d7c841
ZJS
720 return log_device_full_errno(
721 d,
722 searching ? LOG_DEBUG : LOG_ERR,
723 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
724 "Wrong ID_PART_ENTRY_TYPE=%s for XBOOTLDR partition.", v);
725
e94830c0 726 } else
13d7c841
ZJS
727 return log_device_full_errno(
728 d,
729 searching ? LOG_DEBUG : LOG_ERR,
730 searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
731 "Not on a GPT or DOS partition table (ID_PART_ENTRY_SCHEME=%s).", type);
e94830c0
LP
732
733 if (ret_uuid)
734 *ret_uuid = uuid;
735
736 return 0;
737}
738
739static int verify_xbootldr(
63105f33
DDM
740 int rfd,
741 const char *path,
0b2aa206 742 VerifyESPFlags flags,
63105f33 743 char **ret_path,
e94830c0
LP
744 sd_id128_t *ret_uuid,
745 dev_t *ret_devid) {
746
63105f33
DDM
747 _cleanup_free_ char *p = NULL;
748 _cleanup_close_ int pfd = -EBADF;
0b2aa206
LP
749 bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
750 unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE),
751 relax_checks;
bd80fd7e 752 dev_t devid = 0;
e94830c0
LP
753 int r;
754
63105f33
DDM
755 assert(rfd >= 0 || rfd == AT_FDCWD);
756 assert(path);
757
758 r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT, &p, &pfd);
759 if (r < 0)
760 return log_full_errno((searching && r == -ENOENT) ||
761 (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR,
762 r, "Failed to open parent directory of \"%s\": %m", path);
e94830c0 763
3f9b0e13
DDM
764 relax_checks =
765 getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 ||
766 detect_container() > 0;
e94830c0 767
0b2aa206 768 r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid);
e94830c0
LP
769 if (r < 0)
770 return r;
771
3f9b0e13 772 if (relax_checks)
e94830c0
LP
773 goto finish;
774
5c831dde
YW
775 if (devnum_is_zero(devid))
776 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
777 SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
778 "Could not determine backing block device of directory \"%s\" (btrfs RAID?).%s",
779 p,
780 searching ? "" :
781 "\nHint: set $SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes environment variable "
782 "to bypass this and further verifications for the directory.");
783
e94830c0 784 if (unprivileged_mode)
0b2aa206 785 r = verify_xbootldr_udev(devid, flags, ret_uuid);
e94830c0 786 else
0b2aa206 787 r = verify_xbootldr_blkid(devid, flags, ret_uuid);
e94830c0
LP
788 if (r < 0)
789 return r;
790
63105f33
DDM
791 if (ret_path)
792 *ret_path = TAKE_PTR(p);
e94830c0
LP
793 if (ret_devid)
794 *ret_devid = devid;
795
796 return 0;
797
798finish:
63105f33
DDM
799 if (ret_path)
800 *ret_path = TAKE_PTR(p);
e94830c0
LP
801 if (ret_uuid)
802 *ret_uuid = SD_ID128_NULL;
803 if (ret_devid)
804 *ret_devid = 0;
805
806 return 0;
807}
808
63105f33
DDM
809int find_xbootldr_and_warn_at(
810 int rfd,
e94830c0 811 const char *path,
0b2aa206 812 int unprivileged_mode,
e94830c0
LP
813 char **ret_path,
814 sd_id128_t *ret_uuid,
815 dev_t *ret_devid) {
816
0b2aa206 817 VerifyESPFlags flags = 0;
e94830c0
LP
818 int r;
819
820 /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
821
63105f33 822 assert(rfd >= 0 || rfd == AT_FDCWD);
80a2381d 823
0b2aa206
LP
824 if (unprivileged_mode < 0)
825 unprivileged_mode = geteuid() != 0;
826 if (unprivileged_mode)
827 flags |= VERIFY_ESP_UNPRIVILEGED_MODE;
828
63105f33 829 if (path)
0b2aa206 830 return verify_xbootldr(rfd, path, flags, ret_path, ret_uuid, ret_devid);
e94830c0
LP
831
832 path = getenv("SYSTEMD_XBOOTLDR_PATH");
833 if (path) {
63105f33
DDM
834 _cleanup_free_ char *p = NULL;
835 _cleanup_close_ int fd = -EBADF;
e94830c0
LP
836 struct stat st;
837
63105f33 838 if (!path_is_valid(path) || !path_is_absolute(path))
e94830c0 839 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
63105f33
DDM
840 "$SYSTEMD_XBOOTLDR_PATH does not refer to an absolute path, refusing to use it: %s",
841 path);
e94830c0 842
63105f33
DDM
843 r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT, &p, &fd);
844 if (r < 0)
845 return log_error_errno(r, "Failed to resolve path %s: %m", p);
846
847 if (fstat(fd, &st) < 0)
80a2381d 848 return log_error_errno(errno, "Failed to stat '%s': %m", p);
e94830c0 849 if (!S_ISDIR(st.st_mode))
80a2381d 850 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", p);
e94830c0 851
63105f33
DDM
852 if (ret_path)
853 *ret_path = TAKE_PTR(p);
e94830c0
LP
854 if (ret_uuid)
855 *ret_uuid = SD_ID128_NULL;
856 if (ret_devid)
857 *ret_devid = st.st_dev;
858
63105f33 859 return 0;
e94830c0
LP
860 }
861
0b2aa206 862 r = verify_xbootldr(rfd, "/boot", flags | VERIFY_ESP_SEARCHING, ret_path, ret_uuid, ret_devid);
63105f33 863 if (r < 0) {
f26c7943 864 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR, -ENOTTY)) /* This one is not it */
63105f33
DDM
865 return r;
866
80a2381d 867 return -ENOKEY;
63105f33 868 }
80a2381d 869
63105f33
DDM
870 return 0;
871}
872
873int find_xbootldr_and_warn(
874 const char *root,
875 const char *path,
0b2aa206 876 int unprivileged_mode,
63105f33
DDM
877 char **ret_path,
878 sd_id128_t *ret_uuid,
879 dev_t *ret_devid) {
880
881 _cleanup_close_ int rfd = -EBADF;
882 _cleanup_free_ char *p = NULL;
883 sd_id128_t uuid;
884 dev_t devid;
885 int r;
886
887 rfd = open(empty_to_root(root), O_PATH|O_DIRECTORY|O_CLOEXEC);
888 if (rfd < 0)
889 return -errno;
890
891 r = find_xbootldr_and_warn_at(rfd, path, unprivileged_mode,
892 ret_path ? &p : NULL,
893 ret_uuid ? &uuid : NULL,
894 ret_devid ? &devid : NULL);
895 if (r < 0)
e94830c0
LP
896 return r;
897
63105f33 898 if (ret_path) {
60e761d8 899 r = chaseat_prefix_root(p, root, ret_path);
fb4d9bf4
YW
900 if (r < 0)
901 return r;
63105f33
DDM
902 }
903 if (ret_uuid)
904 *ret_uuid = uuid;
905 if (ret_devid)
906 *ret_devid = devid;
e94830c0
LP
907
908 return 0;
909}