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