]>
Commit | Line | Data |
---|---|---|
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 |
27 | typedef 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 |
33 | static 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 | ||
149 | static 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 | ||
243 | static 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 |
307 | success: |
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 | ||
318 | static 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 | ||
423 | finish: | |
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 |
440 | int 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 |
528 | int 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 | ||
580 | static 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 | ||
665 | static 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 | ||
739 | static 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 | ||
798 | finish: | |
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 |
809 | int 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 | ||
873 | int 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 | } |