Coverage Report

Created: 2026-03-12 06:26

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