1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <linux/magic.h>
4 #include <sys/vfs.h>
5 
6 #include "sd-device.h"
7 
8 #include "alloc-util.h"
9 #include "blkid-util.h"
10 #include "devnum-util.h"
11 #include "env-util.h"
12 #include "errno-util.h"
13 #include "find-esp.h"
14 #include "gpt.h"
15 #include "id128-util.h"
16 #include "parse-util.h"
17 #include "path-util.h"
18 #include "stat-util.h"
19 #include "string-util.h"
20 #include "virt.h"
21 
verify_esp_blkid(dev_t devid,bool searching,uint32_t * ret_part,uint64_t * ret_pstart,uint64_t * ret_psize,sd_id128_t * ret_uuid)22 static int verify_esp_blkid(
23                 dev_t devid,
24                 bool searching,
25                 uint32_t *ret_part,
26                 uint64_t *ret_pstart,
27                 uint64_t *ret_psize,
28                 sd_id128_t *ret_uuid) {
29 
30         sd_id128_t uuid = SD_ID128_NULL;
31         uint64_t pstart = 0, psize = 0;
32         uint32_t part = 0;
33 
34 #if HAVE_BLKID
35         _cleanup_(blkid_free_probep) blkid_probe b = NULL;
36         _cleanup_free_ char *node = NULL;
37         const char *v;
38         int r;
39 
40         r = device_path_make_major_minor(S_IFBLK, devid, &node);
41         if (r < 0)
42                 return log_error_errno(r, "Failed to format major/minor device path: %m");
43 
44         errno = 0;
45         b = blkid_new_probe_from_filename(node);
46         if (!b)
47                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
48 
49         blkid_probe_enable_superblocks(b, 1);
50         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
51         blkid_probe_enable_partitions(b, 1);
52         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
53 
54         errno = 0;
55         r = blkid_do_safeprobe(b);
56         if (r == -2)
57                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
58         else if (r == 1)
59                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
60         else if (r != 0)
61                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
62 
63         r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
64         if (r != 0)
65                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
66                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
67                                       "No filesystem found on \"%s\": %m", node);
68         if (!streq(v, "vfat"))
69                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
70                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
71                                       "File system \"%s\" is not FAT.", node);
72 
73         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
74         if (r != 0)
75                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
76                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
77                                       "File system \"%s\" is not located on a partitioned block device.", node);
78         if (!streq(v, "gpt"))
79                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
80                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
81                                       "File system \"%s\" is not on a GPT partition table.", node);
82 
83         errno = 0;
84         r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
85         if (r != 0)
86                 return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
87         if (id128_equal_string(v, GPT_ESP) <= 0)
88                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
89                                        SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
90                                        "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
91 
92         errno = 0;
93         r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
94         if (r != 0)
95                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
96         r = sd_id128_from_string(v, &uuid);
97         if (r < 0)
98                 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
99 
100         errno = 0;
101         r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
102         if (r != 0)
103                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
104         r = safe_atou32(v, &part);
105         if (r < 0)
106                 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
107 
108         errno = 0;
109         r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
110         if (r != 0)
111                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
112         r = safe_atou64(v, &pstart);
113         if (r < 0)
114                 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
115 
116         errno = 0;
117         r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
118         if (r != 0)
119                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
120         r = safe_atou64(v, &psize);
121         if (r < 0)
122                 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
123 #endif
124 
125         if (ret_part)
126                 *ret_part = part;
127         if (ret_pstart)
128                 *ret_pstart = pstart;
129         if (ret_psize)
130                 *ret_psize = psize;
131         if (ret_uuid)
132                 *ret_uuid = uuid;
133 
134         return 0;
135 }
136 
verify_esp_udev(dev_t devid,bool searching,uint32_t * ret_part,uint64_t * ret_pstart,uint64_t * ret_psize,sd_id128_t * ret_uuid)137 static int verify_esp_udev(
138                 dev_t devid,
139                 bool searching,
140                 uint32_t *ret_part,
141                 uint64_t *ret_pstart,
142                 uint64_t *ret_psize,
143                 sd_id128_t *ret_uuid) {
144 
145         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
146         _cleanup_free_ char *node = NULL;
147         sd_id128_t uuid = SD_ID128_NULL;
148         uint64_t pstart = 0, psize = 0;
149         uint32_t part = 0;
150         const char *v;
151         int r;
152 
153         r = device_path_make_major_minor(S_IFBLK, devid, &node);
154         if (r < 0)
155                 return log_error_errno(r, "Failed to format major/minor device path: %m");
156 
157         r = sd_device_new_from_devnum(&d, 'b', devid);
158         if (r < 0)
159                 return log_error_errno(r, "Failed to get device from device number: %m");
160 
161         r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
162         if (r < 0)
163                 return log_error_errno(r, "Failed to get device property: %m");
164         if (!streq(v, "vfat"))
165                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
166                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
167                                       "File system \"%s\" is not FAT.", node );
168 
169         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
170         if (r < 0)
171                 return log_error_errno(r, "Failed to get device property: %m");
172         if (!streq(v, "gpt"))
173                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
174                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
175                                       "File system \"%s\" is not on a GPT partition table.", node);
176 
177         r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
178         if (r < 0)
179                 return log_error_errno(r, "Failed to get device property: %m");
180         if (id128_equal_string(v, GPT_ESP) <= 0)
181                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
182                                        SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
183                                        "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
184 
185         r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
186         if (r < 0)
187                 return log_error_errno(r, "Failed to get device property: %m");
188         r = sd_id128_from_string(v, &uuid);
189         if (r < 0)
190                 return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
191 
192         r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
193         if (r < 0)
194                 return log_error_errno(r, "Failed to get device property: %m");
195         r = safe_atou32(v, &part);
196         if (r < 0)
197                 return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
198 
199         r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
200         if (r < 0)
201                 return log_error_errno(r, "Failed to get device property: %m");
202         r = safe_atou64(v, &pstart);
203         if (r < 0)
204                 return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
205 
206         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
207         if (r < 0)
208                 return log_error_errno(r, "Failed to get device property: %m");
209         r = safe_atou64(v, &psize);
210         if (r < 0)
211                 return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
212 
213         if (ret_part)
214                 *ret_part = part;
215         if (ret_pstart)
216                 *ret_pstart = pstart;
217         if (ret_psize)
218                 *ret_psize = psize;
219         if (ret_uuid)
220                 *ret_uuid = uuid;
221 
222         return 0;
223 }
224 
verify_fsroot_dir(const char * path,bool searching,bool unprivileged_mode,dev_t * ret_dev)225 static int verify_fsroot_dir(
226                 const char *path,
227                 bool searching,
228                 bool unprivileged_mode,
229                 dev_t *ret_dev) {
230 
231         struct stat st, st2;
232         const char *t2, *trigger;
233         int r;
234 
235         assert(path);
236         assert(ret_dev);
237 
238         /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
239          * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
240          * before stat()ing */
241         trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
242         (void) access(trigger, F_OK);
243 
244         if (stat(path, &st) < 0)
245                 return log_full_errno((searching && errno == ENOENT) ||
246                                       (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
247                                       "Failed to determine block device node of \"%s\": %m", path);
248 
249         if (major(st.st_dev) == 0)
250                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
251                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
252                                       "Block device node of \"%s\" is invalid.", path);
253 
254         if (path_equal(path, "/")) {
255                 /* Let's assume that the root directory of the OS is always the root of its file system
256                  * (which technically doesn't have to be the case, but it's close enough, and it's not easy
257                  * to be fully correct for it, since we can't look further up than the root dir easily.) */
258                 if (ret_dev)
259                         *ret_dev = st.st_dev;
260 
261                 return 0;
262         }
263 
264         t2 = strjoina(path, "/..");
265         if (stat(t2, &st2) < 0) {
266                 if (errno != EACCES)
267                         r = -errno;
268                 else {
269                         _cleanup_free_ char *parent = NULL;
270 
271                         /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
272                          * directly instead. It's not as good, due to symlinks and such, but we can't do
273                          * anything better here. */
274 
275                         parent = dirname_malloc(path);
276                         if (!parent)
277                                 return log_oom();
278 
279                         r = RET_NERRNO(stat(parent, &st2));
280                 }
281 
282                 if (r < 0)
283                         return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
284                                               "Failed to determine block device node of parent of \"%s\": %m", path);
285         }
286 
287         if (st.st_dev == st2.st_dev)
288                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
289                                       SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
290                                       "Directory \"%s\" is not the root of the file system.", path);
291 
292         if (ret_dev)
293                 *ret_dev = st.st_dev;
294 
295         return 0;
296 }
297 
verify_esp(const char * p,bool searching,bool unprivileged_mode,uint32_t * ret_part,uint64_t * ret_pstart,uint64_t * ret_psize,sd_id128_t * ret_uuid,dev_t * ret_devid)298 static int verify_esp(
299                 const char *p,
300                 bool searching,
301                 bool unprivileged_mode,
302                 uint32_t *ret_part,
303                 uint64_t *ret_pstart,
304                 uint64_t *ret_psize,
305                 sd_id128_t *ret_uuid,
306                 dev_t *ret_devid) {
307 
308         bool relax_checks;
309         dev_t devid;
310         int r;
311 
312         assert(p);
313 
314         /* This logs about all errors, except:
315          *
316          *  -ENOENT        → if 'searching' is set, and the dir doesn't exist
317          *  -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
318          *  -EACESS        → if 'unprivileged_mode' is set, and we have trouble accessing the thing
319          */
320 
321         relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
322 
323         /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
324          * issues. Let's also, silence the error messages. */
325 
326         if (!relax_checks) {
327                 struct statfs sfs;
328 
329                 if (statfs(p, &sfs) < 0)
330                         /* If we are searching for the mount point, don't generate a log message if we can't find the path */
331                         return log_full_errno((searching && errno == ENOENT) ||
332                                               (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
333                                               "Failed to check file system type of \"%s\": %m", p);
334 
335                 if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
336                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
337                                               SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
338                                               "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
339         }
340 
341         r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
342         if (r < 0)
343                 return r;
344 
345         /* In a container we don't have access to block devices, skip this part of the verification, we trust
346          * the container manager set everything up correctly on its own. */
347         if (detect_container() > 0 || relax_checks)
348                 goto finish;
349 
350         /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
351          * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
352          * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
353          * however blkid can't work if we have no privileges to access block devices directly, which is why
354          * we use udev in that case. */
355         if (unprivileged_mode)
356                 r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
357         else
358                 r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
359         if (r < 0)
360                 return r;
361 
362         if (ret_devid)
363                 *ret_devid = devid;
364 
365         return 0;
366 
367 finish:
368         if (ret_part)
369                 *ret_part = 0;
370         if (ret_pstart)
371                 *ret_pstart = 0;
372         if (ret_psize)
373                 *ret_psize = 0;
374         if (ret_uuid)
375                 *ret_uuid = SD_ID128_NULL;
376         if (ret_devid)
377                 *ret_devid = 0;
378 
379         return 0;
380 }
381 
find_esp_and_warn(const char * path,bool unprivileged_mode,char ** ret_path,uint32_t * ret_part,uint64_t * ret_pstart,uint64_t * ret_psize,sd_id128_t * ret_uuid,dev_t * ret_devid)382 int find_esp_and_warn(
383                 const char *path,
384                 bool unprivileged_mode,
385                 char **ret_path,
386                 uint32_t *ret_part,
387                 uint64_t *ret_pstart,
388                 uint64_t *ret_psize,
389                 sd_id128_t *ret_uuid,
390                 dev_t *ret_devid) {
391 
392         int r;
393 
394         /* This logs about all errors except:
395          *
396          *    -ENOKEY → when we can't find the partition
397          *   -EACCESS → when unprivileged_mode is true, and we can't access something
398          */
399 
400         if (path) {
401                 r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
402                 if (r < 0)
403                         return r;
404 
405                 goto found;
406         }
407 
408         path = getenv("SYSTEMD_ESP_PATH");
409         if (path) {
410                 struct stat st;
411 
412                 if (!path_is_valid(path) || !path_is_absolute(path))
413                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
414                                                "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
415                                                path);
416 
417                 /* Note: when the user explicitly configured things with an env var we won't validate the
418                  * path beyond checking it refers to a directory. After all we want this to be useful for
419                  * testing. */
420 
421                 if (stat(path, &st) < 0)
422                         return log_error_errno(errno, "Failed to stat '%s': %m", path);
423                 if (!S_ISDIR(st.st_mode))
424                         return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
425 
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 = st.st_dev;
436 
437                 goto found;
438         }
439 
440         FOREACH_STRING(_path, "/efi", "/boot", "/boot/efi") {
441                 path = _path;
442 
443                 r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
444                 if (r >= 0)
445                         goto found;
446                 if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
447                         return r;
448         }
449 
450         /* No logging here */
451         return -ENOKEY;
452 
453 found:
454         if (ret_path) {
455                 char *c;
456 
457                 c = strdup(path);
458                 if (!c)
459                         return log_oom();
460 
461                 *ret_path = c;
462         }
463 
464         return 0;
465 }
466 
verify_xbootldr_blkid(dev_t devid,bool searching,sd_id128_t * ret_uuid)467 static int verify_xbootldr_blkid(
468                 dev_t devid,
469                 bool searching,
470                 sd_id128_t *ret_uuid) {
471 
472         sd_id128_t uuid = SD_ID128_NULL;
473 
474 #if HAVE_BLKID
475         _cleanup_(blkid_free_probep) blkid_probe b = NULL;
476         _cleanup_free_ char *node = NULL;
477         const char *v;
478         int r;
479 
480         r = device_path_make_major_minor(S_IFBLK, devid, &node);
481         if (r < 0)
482                 return log_error_errno(r, "Failed to format major/minor device path: %m");
483         errno = 0;
484         b = blkid_new_probe_from_filename(node);
485         if (!b)
486                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
487 
488         blkid_probe_enable_partitions(b, 1);
489         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
490 
491         errno = 0;
492         r = blkid_do_safeprobe(b);
493         if (r == -2)
494                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
495         else if (r == 1)
496                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
497         else if (r != 0)
498                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
499 
500         errno = 0;
501         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
502         if (r != 0)
503                 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
504         if (streq(v, "gpt")) {
505 
506                 errno = 0;
507                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
508                 if (r != 0)
509                         return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
510                 if (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
511                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
512                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
513                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
514 
515                 errno = 0;
516                 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
517                 if (r != 0)
518                         return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
519                 r = sd_id128_from_string(v, &uuid);
520                 if (r < 0)
521                         return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
522 
523         } else if (streq(v, "dos")) {
524 
525                 errno = 0;
526                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
527                 if (r != 0)
528                         return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
529                 if (!streq(v, "0xea"))
530                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
531                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
532                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
533 
534         } else
535                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
536                                       searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
537                                       "File system \"%s\" is not on a GPT or DOS partition table.", node);
538 #endif
539 
540         if (ret_uuid)
541                 *ret_uuid = uuid;
542 
543         return 0;
544 }
545 
verify_xbootldr_udev(dev_t devid,bool searching,sd_id128_t * ret_uuid)546 static int verify_xbootldr_udev(
547                 dev_t devid,
548                 bool searching,
549                 sd_id128_t *ret_uuid) {
550 
551         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
552         _cleanup_free_ char *node = NULL;
553         sd_id128_t uuid = SD_ID128_NULL;
554         const char *v;
555         int r;
556 
557         r = device_path_make_major_minor(S_IFBLK, devid, &node);
558         if (r < 0)
559                 return log_error_errno(r, "Failed to format major/minor device path: %m");
560 
561         r = sd_device_new_from_devnum(&d, 'b', devid);
562         if (r < 0)
563                 return log_error_errno(r, "Failed to get device from device number: %m");
564 
565         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
566         if (r < 0)
567                 return log_error_errno(r, "Failed to get device property: %m");
568 
569         if (streq(v, "gpt")) {
570 
571                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
572                 if (r < 0)
573                         return log_error_errno(r, "Failed to get device property: %m");
574                 if (id128_equal_string(v, GPT_XBOOTLDR))
575                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
576                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
577                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
578 
579                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
580                 if (r < 0)
581                         return log_error_errno(r, "Failed to get device property: %m");
582                 r = sd_id128_from_string(v, &uuid);
583                 if (r < 0)
584                         return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
585 
586         } else if (streq(v, "dos")) {
587 
588                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
589                 if (r < 0)
590                         return log_error_errno(r, "Failed to get device property: %m");
591                 if (!streq(v, "0xea"))
592                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
593                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
594                                               "File system \"%s\" has wrong type for extended boot loader partition.", node);
595         } else
596                 return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
597                                       searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
598                                       "File system \"%s\" is not on a GPT or DOS partition table.", node);
599 
600         if (ret_uuid)
601                 *ret_uuid = uuid;
602 
603         return 0;
604 }
605 
verify_xbootldr(const char * p,bool searching,bool unprivileged_mode,sd_id128_t * ret_uuid,dev_t * ret_devid)606 static int verify_xbootldr(
607                 const char *p,
608                 bool searching,
609                 bool unprivileged_mode,
610                 sd_id128_t *ret_uuid,
611                 dev_t *ret_devid) {
612 
613         bool relax_checks;
614         dev_t devid;
615         int r;
616 
617         assert(p);
618 
619         relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
620 
621         r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
622         if (r < 0)
623                 return r;
624 
625         if (detect_container() > 0 || relax_checks)
626                 goto finish;
627 
628         if (unprivileged_mode)
629                 r = verify_xbootldr_udev(devid, searching, ret_uuid);
630         else
631                 r = verify_xbootldr_blkid(devid, searching, ret_uuid);
632         if (r < 0)
633                 return r;
634 
635         if (ret_devid)
636                 *ret_devid = devid;
637 
638         return 0;
639 
640 finish:
641         if (ret_uuid)
642                 *ret_uuid = SD_ID128_NULL;
643         if (ret_devid)
644                 *ret_devid = 0;
645 
646         return 0;
647 }
648 
find_xbootldr_and_warn(const char * path,bool unprivileged_mode,char ** ret_path,sd_id128_t * ret_uuid,dev_t * ret_devid)649 int find_xbootldr_and_warn(
650                 const char *path,
651                 bool unprivileged_mode,
652                 char **ret_path,
653                 sd_id128_t *ret_uuid,
654                 dev_t *ret_devid) {
655 
656         int r;
657 
658         /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
659 
660         if (path) {
661                 r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
662                 if (r < 0)
663                         return r;
664 
665                 goto found;
666         }
667 
668         path = getenv("SYSTEMD_XBOOTLDR_PATH");
669         if (path) {
670                 struct stat st;
671 
672                 if (!path_is_valid(path) || !path_is_absolute(path))
673                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
674                                                "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
675                                                path);
676 
677                 if (stat(path, &st) < 0)
678                         return log_error_errno(errno, "Failed to stat '%s': %m", path);
679                 if (!S_ISDIR(st.st_mode))
680                         return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
681 
682                 if (ret_uuid)
683                         *ret_uuid = SD_ID128_NULL;
684                 if (ret_devid)
685                         *ret_devid = st.st_dev;
686 
687                 goto found;
688         }
689 
690         r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
691         if (r >= 0) {
692                 path = "/boot";
693                 goto found;
694         }
695         if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
696                 return r;
697 
698         return -ENOKEY;
699 
700 found:
701         if (ret_path) {
702                 char *c;
703 
704                 c = strdup(path);
705                 if (!c)
706                         return log_oom();
707 
708                 *ret_path = c;
709         }
710 
711         return 0;
712 }
713