]>
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 */ | |
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 |
34 | static 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 |
57 | static 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 | ||
173 | static 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 | ||
267 | static 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 |
331 | success: |
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 | ||
342 | static 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 | ||
439 | finish: | |
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 |
456 | int 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 |
536 | int 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 | ||
591 | static 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 | ||
676 | static 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 | ||
750 | static 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 | ||
804 | finish: | |
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 |
815 | int 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 | ||
876 | int 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 | } |