chiark / gitweb /
Prep v236 : Add missing SPDX-License-Identifier (2/9) src/basic
[elogind.git] / src / basic / util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <alloca.h>
22 //#include <errno.h>
23 //#include <fcntl.h>
24 #include <sched.h>
25 //#include <signal.h>
26 //#include <stdarg.h>
27 //#include <stdio.h>
28 #include <stdlib.h>
29 //#include <string.h>
30 //#include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/statfs.h>
33 #include <sys/sysmacros.h>
34 //#include <sys/types.h>
35 //#include <unistd.h>
36
37 #include "alloc-util.h"
38 //#include "btrfs-util.h"
39 #include "build.h"
40 #include "cgroup-util.h"
41 //#include "def.h"
42 //#include "device-nodes.h"
43 #include "dirent-util.h"
44 #include "fd-util.h"
45 #include "fileio.h"
46 //#include "format-util.h"
47 #include "hashmap.h"
48 #include "hostname-util.h"
49 //#include "log.h"
50 #include "macro.h"
51 //#include "missing.h"
52 #include "parse-util.h"
53 //#include "path-util.h"
54 #include "process-util.h"
55 #include "set.h"
56 #include "signal-util.h"
57 #include "stat-util.h"
58 #include "string-util.h"
59 #include "strv.h"
60 #include "time-util.h"
61 #include "umask-util.h"
62 #include "user-util.h"
63 #include "util.h"
64
65 int saved_argc = 0;
66 char **saved_argv = NULL;
67 static int saved_in_initrd = -1;
68
69 size_t page_size(void) {
70         static thread_local size_t pgsz = 0;
71         long r;
72
73         if (_likely_(pgsz > 0))
74                 return pgsz;
75
76         r = sysconf(_SC_PAGESIZE);
77         assert(r > 0);
78
79         pgsz = (size_t) r;
80         return pgsz;
81 }
82
83 #if 0 /// UNNEEDED by elogind
84 bool plymouth_running(void) {
85         return access("/run/plymouth/pid", F_OK) >= 0;
86 }
87 #endif // 0
88
89 bool display_is_local(const char *display) {
90         assert(display);
91
92         return
93                 display[0] == ':' &&
94                 display[1] >= '0' &&
95                 display[1] <= '9';
96 }
97
98 int socket_from_display(const char *display, char **path) {
99         size_t k;
100         char *f, *c;
101
102         assert(display);
103         assert(path);
104
105         if (!display_is_local(display))
106                 return -EINVAL;
107
108         k = strspn(display+1, "0123456789");
109
110         f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
111         if (!f)
112                 return -ENOMEM;
113
114         c = stpcpy(f, "/tmp/.X11-unix/X");
115         memcpy(c, display+1, k);
116         c[k] = 0;
117
118         *path = f;
119
120         return 0;
121 }
122
123 #if 0 /// UNNEEDED by elogind
124 int block_get_whole_disk(dev_t d, dev_t *ret) {
125         char p[SYS_BLOCK_PATH_MAX("/partition")];
126         _cleanup_free_ char *s = NULL;
127         int r;
128         unsigned n, m;
129
130         assert(ret);
131
132         /* If it has a queue this is good enough for us */
133         xsprintf_sys_block_path(p, "/queue", d);
134         if (access(p, F_OK) >= 0) {
135                 *ret = d;
136                 return 0;
137         }
138
139         /* If it is a partition find the originating device */
140         xsprintf_sys_block_path(p, "/partition", d);
141         if (access(p, F_OK) < 0)
142                 return -ENOENT;
143
144         /* Get parent dev_t */
145         xsprintf_sys_block_path(p, "/../dev", d);
146         r = read_one_line_file(p, &s);
147         if (r < 0)
148                 return r;
149
150         r = sscanf(s, "%u:%u", &m, &n);
151         if (r != 2)
152                 return -EINVAL;
153
154         /* Only return this if it is really good enough for us. */
155         xsprintf_sys_block_path(p, "/queue", makedev(m, n));
156         if (access(p, F_OK) < 0)
157                 return -ENOENT;
158
159         *ret = makedev(m, n);
160         return 0;
161 }
162
163 bool kexec_loaded(void) {
164        _cleanup_free_ char *s = NULL;
165
166        if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0)
167                return false;
168
169        return s[0] == '1';
170 }
171
172 int prot_from_flags(int flags) {
173
174         switch (flags & O_ACCMODE) {
175
176         case O_RDONLY:
177                 return PROT_READ;
178
179         case O_WRONLY:
180                 return PROT_WRITE;
181
182         case O_RDWR:
183                 return PROT_READ|PROT_WRITE;
184
185         default:
186                 return -EINVAL;
187         }
188 }
189 #endif // 0
190
191 int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
192         bool stdout_is_tty, stderr_is_tty;
193         pid_t parent_pid, agent_pid;
194         sigset_t ss, saved_ss;
195         unsigned n, i;
196         va_list ap;
197         char **l;
198
199         assert(pid);
200         assert(path);
201
202         /* Spawns a temporary TTY agent, making sure it goes away when
203          * we go away */
204
205         parent_pid = getpid_cached();
206
207         /* First we temporarily block all signals, so that the new
208          * child has them blocked initially. This way, we can be sure
209          * that SIGTERMs are not lost we might send to the agent. */
210         assert_se(sigfillset(&ss) >= 0);
211         assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
212
213         agent_pid = fork();
214         if (agent_pid < 0) {
215                 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
216                 return -errno;
217         }
218
219         if (agent_pid != 0) {
220                 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
221                 *pid = agent_pid;
222                 return 0;
223         }
224
225         /* In the child:
226          *
227          * Make sure the agent goes away when the parent dies */
228         if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
229                 _exit(EXIT_FAILURE);
230
231         /* Make sure we actually can kill the agent, if we need to, in
232          * case somebody invoked us from a shell script that trapped
233          * SIGTERM or so... */
234         (void) reset_all_signal_handlers();
235         (void) reset_signal_mask();
236
237         /* Check whether our parent died before we were able
238          * to set the death signal and unblock the signals */
239         if (getppid() != parent_pid)
240                 _exit(EXIT_SUCCESS);
241
242         /* Don't leak fds to the agent */
243         close_all_fds(except, n_except);
244
245         stdout_is_tty = isatty(STDOUT_FILENO);
246         stderr_is_tty = isatty(STDERR_FILENO);
247
248         if (!stdout_is_tty || !stderr_is_tty) {
249                 int fd;
250
251                 /* Detach from stdout/stderr. and reopen
252                  * /dev/tty for them. This is important to
253                  * ensure that when systemctl is started via
254                  * popen() or a similar call that expects to
255                  * read EOF we actually do generate EOF and
256                  * not delay this indefinitely by because we
257                  * keep an unused copy of stdin around. */
258                 fd = open("/dev/tty", O_WRONLY);
259                 if (fd < 0) {
260                         log_error_errno(errno, "Failed to open /dev/tty: %m");
261                         _exit(EXIT_FAILURE);
262                 }
263
264                 if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
265                         log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
266                         _exit(EXIT_FAILURE);
267                 }
268
269                 if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
270                         log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
271                         _exit(EXIT_FAILURE);
272                 }
273
274                 if (fd > STDERR_FILENO)
275                         close(fd);
276         }
277
278         /* Count arguments */
279         va_start(ap, path);
280         for (n = 0; va_arg(ap, char*); n++)
281                 ;
282         va_end(ap);
283
284         /* Allocate strv */
285         l = alloca(sizeof(char *) * (n + 1));
286
287         /* Fill in arguments */
288         va_start(ap, path);
289         for (i = 0; i <= n; i++)
290                 l[i] = va_arg(ap, char*);
291         va_end(ap);
292
293         execv(path, l);
294         _exit(EXIT_FAILURE);
295 }
296
297 bool in_initrd(void) {
298         struct statfs s;
299
300         if (saved_in_initrd >= 0)
301                 return saved_in_initrd;
302
303         /* We make two checks here:
304          *
305          * 1. the flag file /etc/initrd-release must exist
306          * 2. the root file system must be a memory file system
307          *
308          * The second check is extra paranoia, since misdetecting an
309          * initrd can have bad consequences due the initrd
310          * emptying when transititioning to the main systemd.
311          */
312
313         saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
314                           statfs("/", &s) >= 0 &&
315                           is_temporary_fs(&s);
316
317         return saved_in_initrd;
318 }
319
320 void in_initrd_force(bool value) {
321         saved_in_initrd = value;
322 }
323
324 #if 0 /// UNNEEDED by elogind
325 /* hey glibc, APIs with callbacks without a user pointer are so useless */
326 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
327                  int (*compar) (const void *, const void *, void *), void *arg) {
328         size_t l, u, idx;
329         const void *p;
330         int comparison;
331
332         l = 0;
333         u = nmemb;
334         while (l < u) {
335                 idx = (l + u) / 2;
336                 p = (const char *) base + idx * size;
337                 comparison = compar(key, p, arg);
338                 if (comparison < 0)
339                         u = idx;
340                 else if (comparison > 0)
341                         l = idx + 1;
342                 else
343                         return (void *)p;
344         }
345         return NULL;
346 }
347
348 int on_ac_power(void) {
349         bool found_offline = false, found_online = false;
350         _cleanup_closedir_ DIR *d = NULL;
351         struct dirent *de;
352
353         d = opendir("/sys/class/power_supply");
354         if (!d)
355                 return errno == ENOENT ? true : -errno;
356
357         FOREACH_DIRENT(de, d, return -errno) {
358                 _cleanup_close_ int fd = -1, device = -1;
359                 char contents[6];
360                 ssize_t n;
361
362                 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
363                 if (device < 0) {
364                         if (IN_SET(errno, ENOENT, ENOTDIR))
365                                 continue;
366
367                         return -errno;
368                 }
369
370                 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
371                 if (fd < 0) {
372                         if (errno == ENOENT)
373                                 continue;
374
375                         return -errno;
376                 }
377
378                 n = read(fd, contents, sizeof(contents));
379                 if (n < 0)
380                         return -errno;
381
382                 if (n != 6 || memcmp(contents, "Mains\n", 6))
383                         continue;
384
385                 safe_close(fd);
386                 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
387                 if (fd < 0) {
388                         if (errno == ENOENT)
389                                 continue;
390
391                         return -errno;
392                 }
393
394                 n = read(fd, contents, sizeof(contents));
395                 if (n < 0)
396                         return -errno;
397
398                 if (n != 2 || contents[1] != '\n')
399                         return -EIO;
400
401                 if (contents[0] == '1') {
402                         found_online = true;
403                         break;
404                 } else if (contents[0] == '0')
405                         found_offline = true;
406                 else
407                         return -EIO;
408         }
409
410         return found_online || !found_offline;
411 }
412
413 #endif // 0
414 int container_get_leader(const char *machine, pid_t *pid) {
415         _cleanup_free_ char *s = NULL, *class = NULL;
416         const char *p;
417         pid_t leader;
418         int r;
419
420         assert(machine);
421         assert(pid);
422
423         if (!machine_name_is_valid(machine))
424                 return -EINVAL;
425
426         p = strjoina("/run/systemd/machines/", machine);
427         r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
428         if (r == -ENOENT)
429                 return -EHOSTDOWN;
430         if (r < 0)
431                 return r;
432         if (!s)
433                 return -EIO;
434
435         if (!streq_ptr(class, "container"))
436                 return -EIO;
437
438         r = parse_pid(s, &leader);
439         if (r < 0)
440                 return r;
441         if (leader <= 1)
442                 return -EIO;
443
444         *pid = leader;
445         return 0;
446 }
447
448 int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
449         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
450         int rfd = -1;
451
452         assert(pid >= 0);
453
454         if (mntns_fd) {
455                 const char *mntns;
456
457                 mntns = procfs_file_alloca(pid, "ns/mnt");
458                 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
459                 if (mntnsfd < 0)
460                         return -errno;
461         }
462
463         if (pidns_fd) {
464                 const char *pidns;
465
466                 pidns = procfs_file_alloca(pid, "ns/pid");
467                 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
468                 if (pidnsfd < 0)
469                         return -errno;
470         }
471
472         if (netns_fd) {
473                 const char *netns;
474
475                 netns = procfs_file_alloca(pid, "ns/net");
476                 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
477                 if (netnsfd < 0)
478                         return -errno;
479         }
480
481         if (userns_fd) {
482                 const char *userns;
483
484                 userns = procfs_file_alloca(pid, "ns/user");
485                 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
486                 if (usernsfd < 0 && errno != ENOENT)
487                         return -errno;
488         }
489
490         if (root_fd) {
491                 const char *root;
492
493                 root = procfs_file_alloca(pid, "root");
494                 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
495                 if (rfd < 0)
496                         return -errno;
497         }
498
499         if (pidns_fd)
500                 *pidns_fd = pidnsfd;
501
502         if (mntns_fd)
503                 *mntns_fd = mntnsfd;
504
505         if (netns_fd)
506                 *netns_fd = netnsfd;
507
508         if (userns_fd)
509                 *userns_fd = usernsfd;
510
511         if (root_fd)
512                 *root_fd = rfd;
513
514         pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
515
516         return 0;
517 }
518
519 int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
520         if (userns_fd >= 0) {
521                 /* Can't setns to your own userns, since then you could
522                  * escalate from non-root to root in your own namespace, so
523                  * check if namespaces equal before attempting to enter. */
524                 _cleanup_free_ char *userns_fd_path = NULL;
525                 int r;
526                 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
527                         return -ENOMEM;
528
529                 r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
530                 if (r < 0)
531                         return r;
532                 if (r)
533                         userns_fd = -1;
534         }
535
536         if (pidns_fd >= 0)
537                 if (setns(pidns_fd, CLONE_NEWPID) < 0)
538                         return -errno;
539
540         if (mntns_fd >= 0)
541                 if (setns(mntns_fd, CLONE_NEWNS) < 0)
542                         return -errno;
543
544         if (netns_fd >= 0)
545                 if (setns(netns_fd, CLONE_NEWNET) < 0)
546                         return -errno;
547
548         if (userns_fd >= 0)
549                 if (setns(userns_fd, CLONE_NEWUSER) < 0)
550                         return -errno;
551
552         if (root_fd >= 0) {
553                 if (fchdir(root_fd) < 0)
554                         return -errno;
555
556                 if (chroot(".") < 0)
557                         return -errno;
558         }
559
560         return reset_uid_gid();
561 }
562
563 uint64_t physical_memory(void) {
564         _cleanup_free_ char *root = NULL, *value = NULL;
565         uint64_t mem, lim;
566         size_t ps;
567         long sc;
568
569         /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
570          * memory.
571          *
572          * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
573          * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
574
575         sc = sysconf(_SC_PHYS_PAGES);
576         assert(sc > 0);
577
578         ps = page_size();
579         mem = (uint64_t) sc * (uint64_t) ps;
580
581         if (cg_get_root_path(&root) < 0)
582                 return mem;
583
584         if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value))
585                 return mem;
586
587         if (safe_atou64(value, &lim) < 0)
588                 return mem;
589
590         /* Make sure the limit is a multiple of our own page size */
591         lim /= ps;
592         lim *= ps;
593
594         return MIN(mem, lim);
595 }
596
597 uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
598         uint64_t p, m, ps, r;
599
600         assert(max > 0);
601
602         /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
603          * the result is a multiple of the page size (rounds down). */
604
605         ps = page_size();
606         assert(ps > 0);
607
608         p = physical_memory() / ps;
609         assert(p > 0);
610
611         m = p * v;
612         if (m / p != v)
613                 return UINT64_MAX;
614
615         m /= max;
616
617         r = m * ps;
618         if (r / ps != m)
619                 return UINT64_MAX;
620
621         return r;
622 }
623
624 uint64_t system_tasks_max(void) {
625
626 #if SIZEOF_PID_T == 4
627 #define TASKS_MAX ((uint64_t) (INT32_MAX-1))
628 #elif SIZEOF_PID_T == 2
629 #define TASKS_MAX ((uint64_t) (INT16_MAX-1))
630 #else
631 #error "Unknown pid_t size"
632 #endif
633
634         _cleanup_free_ char *value = NULL, *root = NULL;
635         uint64_t a = TASKS_MAX, b = TASKS_MAX;
636
637         /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
638          * limit:
639          *
640          * a) the maximum value for the pid_t type
641          * b) the cgroups pids_max attribute for the system
642          * c) the kernel's configure maximum PID value
643          *
644          * And then pick the smallest of the three */
645
646         if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
647                 (void) safe_atou64(value, &a);
648
649         if (cg_get_root_path(&root) >= 0) {
650                 value = mfree(value);
651
652                 if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
653                         (void) safe_atou64(value, &b);
654         }
655
656         return MIN3(TASKS_MAX,
657                     a <= 0 ? TASKS_MAX : a,
658                     b <= 0 ? TASKS_MAX : b);
659 }
660
661 uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
662         uint64_t t, m;
663
664         assert(max > 0);
665
666         /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
667          * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
668
669         t = system_tasks_max();
670         assert(t > 0);
671
672         m = t * v;
673         if (m / t != v) /* overflow? */
674                 return UINT64_MAX;
675
676         return m / max;
677 }
678
679 #if 0 /// UNNEEDED by elogind
680 int update_reboot_parameter_and_warn(const char *param) {
681         int r;
682
683         if (isempty(param)) {
684                 if (unlink("/run/systemd/reboot-param") < 0) {
685                         if (errno == ENOENT)
686                                 return 0;
687
688                         return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
689                 }
690
691                 return 0;
692         }
693
694         RUN_WITH_UMASK(0022) {
695                 r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
696                 if (r < 0)
697                         return log_warning_errno(r, "Failed to write reboot parameter file: %m");
698         }
699
700         return 0;
701 }
702 #endif // 0
703
704 int version(void) {
705         puts(PACKAGE_STRING "\n"
706              SYSTEMD_FEATURES);
707         return 0;
708 }
709
710 #if 0 /// UNNEEDED by elogind
711 int get_block_device(const char *path, dev_t *dev) {
712         struct stat st;
713         struct statfs sfs;
714
715         assert(path);
716         assert(dev);
717
718         /* Get's the block device directly backing a file system. If
719          * the block device is encrypted, returns the device mapper
720          * block device. */
721
722         if (lstat(path, &st))
723                 return -errno;
724
725         if (major(st.st_dev) != 0) {
726                 *dev = st.st_dev;
727                 return 1;
728         }
729
730         if (statfs(path, &sfs) < 0)
731                 return -errno;
732
733         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
734                 return btrfs_get_block_device(path, dev);
735
736         return 0;
737 }
738
739 int get_block_device_harder(const char *path, dev_t *dev) {
740         _cleanup_closedir_ DIR *d = NULL;
741         _cleanup_free_ char *t = NULL;
742         char p[SYS_BLOCK_PATH_MAX("/slaves")];
743         struct dirent *de, *found = NULL;
744         const char *q;
745         unsigned maj, min;
746         dev_t dt;
747         int r;
748
749         assert(path);
750         assert(dev);
751
752         /* Gets the backing block device for a file system, and
753          * handles LUKS encrypted file systems, looking for its
754          * immediate parent, if there is one. */
755
756         r = get_block_device(path, &dt);
757         if (r <= 0)
758                 return r;
759
760         xsprintf_sys_block_path(p, "/slaves", dt);
761         d = opendir(p);
762         if (!d) {
763                 if (errno == ENOENT)
764                         goto fallback;
765
766                 return -errno;
767         }
768
769         FOREACH_DIRENT_ALL(de, d, return -errno) {
770
771                 if (dot_or_dot_dot(de->d_name))
772                         continue;
773
774                 if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
775                         continue;
776
777                 if (found) {
778                         _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
779
780                         /* We found a device backed by multiple other devices. We don't really support automatic
781                          * discovery on such setups, with the exception of dm-verity partitions. In this case there are
782                          * two backing devices: the data partition and the hash partition. We are fine with such
783                          * setups, however, only if both partitions are on the same physical device. Hence, let's
784                          * verify this. */
785
786                         u = strjoin(p, "/", de->d_name, "/../dev");
787                         if (!u)
788                                 return -ENOMEM;
789
790                         v = strjoin(p, "/", found->d_name, "/../dev");
791                         if (!v)
792                                 return -ENOMEM;
793
794                         r = read_one_line_file(u, &a);
795                         if (r < 0) {
796                                 log_debug_errno(r, "Failed to read %s: %m", u);
797                                 goto fallback;
798                         }
799
800                         r = read_one_line_file(v, &b);
801                         if (r < 0) {
802                                 log_debug_errno(r, "Failed to read %s: %m", v);
803                                 goto fallback;
804                         }
805
806                         /* Check if the parent device is the same. If not, then the two backing devices are on
807                          * different physical devices, and we don't support that. */
808                         if (!streq(a, b))
809                                 goto fallback;
810                 }
811
812                 found = de;
813         }
814
815         if (!found)
816                 goto fallback;
817
818         q = strjoina(p, "/", found->d_name, "/dev");
819
820         r = read_one_line_file(q, &t);
821         if (r == -ENOENT)
822                 goto fallback;
823         if (r < 0)
824                 return r;
825
826         if (sscanf(t, "%u:%u", &maj, &min) != 2)
827                 return -EINVAL;
828
829         if (maj == 0)
830                 goto fallback;
831
832         *dev = makedev(maj, min);
833         return 1;
834
835 fallback:
836         *dev = dt;
837         return 1;
838 }
839 #endif // 0