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