Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <sys/stat.h>
4 : : #include <sys/statfs.h>
5 : :
6 : : #include "alloc-util.h"
7 : : #include "blockdev-util.h"
8 : : #include "btrfs-util.h"
9 : : #include "dirent-util.h"
10 : : #include "fd-util.h"
11 : : #include "fileio.h"
12 : : #include "missing.h"
13 : : #include "parse-util.h"
14 : : #include "stat-util.h"
15 : :
16 : 0 : int block_get_whole_disk(dev_t d, dev_t *ret) {
17 : 0 : char p[SYS_BLOCK_PATH_MAX("/partition")];
18 : 0 : _cleanup_free_ char *s = NULL;
19 : : dev_t devt;
20 : : int r;
21 : :
22 [ # # ]: 0 : assert(ret);
23 : :
24 [ # # ]: 0 : if (major(d) == 0)
25 : 0 : return -ENODEV;
26 : :
27 : : /* If it has a queue this is good enough for us */
28 [ # # ]: 0 : xsprintf_sys_block_path(p, "/queue", d);
29 [ # # ]: 0 : if (access(p, F_OK) >= 0) {
30 : 0 : *ret = d;
31 : 0 : return 0;
32 : : }
33 : :
34 : : /* If it is a partition find the originating device */
35 [ # # ]: 0 : xsprintf_sys_block_path(p, "/partition", d);
36 [ # # ]: 0 : if (access(p, F_OK) < 0)
37 : 0 : return -errno;
38 : :
39 : : /* Get parent dev_t */
40 [ # # ]: 0 : xsprintf_sys_block_path(p, "/../dev", d);
41 : 0 : r = read_one_line_file(p, &s);
42 [ # # ]: 0 : if (r < 0)
43 : 0 : return r;
44 : :
45 : 0 : r = parse_dev(s, &devt);
46 [ # # ]: 0 : if (r < 0)
47 : 0 : return r;
48 : :
49 : : /* Only return this if it is really good enough for us. */
50 [ # # ]: 0 : xsprintf_sys_block_path(p, "/queue", devt);
51 [ # # ]: 0 : if (access(p, F_OK) < 0)
52 : 0 : return -errno;
53 : :
54 : 0 : *ret = devt;
55 : 0 : return 1;
56 : : }
57 : :
58 : 0 : int get_block_device(const char *path, dev_t *dev) {
59 : : struct stat st;
60 : : struct statfs sfs;
61 : :
62 [ # # ]: 0 : assert(path);
63 [ # # ]: 0 : assert(dev);
64 : :
65 : : /* Gets the block device directly backing a file system. If
66 : : * the block device is encrypted, returns the device mapper
67 : : * block device. */
68 : :
69 [ # # ]: 0 : if (lstat(path, &st))
70 : 0 : return -errno;
71 : :
72 [ # # ]: 0 : if (major(st.st_dev) != 0) {
73 : 0 : *dev = st.st_dev;
74 : 0 : return 1;
75 : : }
76 : :
77 [ # # ]: 0 : if (statfs(path, &sfs) < 0)
78 : 0 : return -errno;
79 : :
80 [ # # ]: 0 : if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
81 : 0 : return btrfs_get_block_device(path, dev);
82 : :
83 : 0 : *dev = 0;
84 : 0 : return 0;
85 : : }
86 : :
87 : 0 : int block_get_originating(dev_t dt, dev_t *ret) {
88 : 0 : _cleanup_closedir_ DIR *d = NULL;
89 : 0 : _cleanup_free_ char *t = NULL;
90 : 0 : char p[SYS_BLOCK_PATH_MAX("/slaves")];
91 : 0 : struct dirent *de, *found = NULL;
92 : : const char *q;
93 : : dev_t devt;
94 : : int r;
95 : :
96 : : /* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used,
97 : : * trying to find the next underlying layer. */
98 : :
99 [ # # ]: 0 : xsprintf_sys_block_path(p, "/slaves", dt);
100 : 0 : d = opendir(p);
101 [ # # ]: 0 : if (!d)
102 : 0 : return -errno;
103 : :
104 [ # # # # ]: 0 : FOREACH_DIRENT_ALL(de, d, return -errno) {
105 : :
106 [ # # ]: 0 : if (dot_or_dot_dot(de->d_name))
107 : 0 : continue;
108 : :
109 [ # # # # ]: 0 : if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
110 : 0 : continue;
111 : :
112 [ # # ]: 0 : if (found) {
113 [ # # # # : 0 : _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
# # # # ]
114 : :
115 : : /* We found a device backed by multiple other devices. We don't really support automatic
116 : : * discovery on such setups, with the exception of dm-verity partitions. In this case there are
117 : : * two backing devices: the data partition and the hash partition. We are fine with such
118 : : * setups, however, only if both partitions are on the same physical device. Hence, let's
119 : : * verify this. */
120 : :
121 : 0 : u = path_join(p, de->d_name, "../dev");
122 [ # # ]: 0 : if (!u)
123 : 0 : return -ENOMEM;
124 : :
125 : 0 : v = path_join(p, found->d_name, "../dev");
126 [ # # ]: 0 : if (!v)
127 : 0 : return -ENOMEM;
128 : :
129 : 0 : r = read_one_line_file(u, &a);
130 [ # # ]: 0 : if (r < 0)
131 [ # # ]: 0 : return log_debug_errno(r, "Failed to read %s: %m", u);
132 : :
133 : 0 : r = read_one_line_file(v, &b);
134 [ # # ]: 0 : if (r < 0)
135 [ # # ]: 0 : return log_debug_errno(r, "Failed to read %s: %m", v);
136 : :
137 : : /* Check if the parent device is the same. If not, then the two backing devices are on
138 : : * different physical devices, and we don't support that. */
139 [ # # ]: 0 : if (!streq(a, b))
140 : 0 : return -ENOTUNIQ;
141 : : }
142 : :
143 : 0 : found = de;
144 : : }
145 : :
146 [ # # ]: 0 : if (!found)
147 : 0 : return -ENOENT;
148 : :
149 [ # # # # : 0 : q = strjoina(p, "/", found->d_name, "/dev");
# # # # #
# # # ]
150 : :
151 : 0 : r = read_one_line_file(q, &t);
152 [ # # ]: 0 : if (r < 0)
153 : 0 : return r;
154 : :
155 : 0 : r = parse_dev(t, &devt);
156 [ # # ]: 0 : if (r < 0)
157 : 0 : return -EINVAL;
158 : :
159 [ # # ]: 0 : if (major(devt) == 0)
160 : 0 : return -ENOENT;
161 : :
162 : 0 : *ret = devt;
163 : 0 : return 1;
164 : : }
165 : :
166 : 0 : int get_block_device_harder(const char *path, dev_t *ret) {
167 : : int r;
168 : :
169 [ # # ]: 0 : assert(path);
170 [ # # ]: 0 : assert(ret);
171 : :
172 : : /* Gets the backing block device for a file system, and handles LUKS encrypted file systems, looking for its
173 : : * immediate parent, if there is one. */
174 : :
175 : 0 : r = get_block_device(path, ret);
176 [ # # ]: 0 : if (r <= 0)
177 : 0 : return r;
178 : :
179 : 0 : r = block_get_originating(*ret, ret);
180 [ # # ]: 0 : if (r < 0)
181 [ # # ]: 0 : log_debug_errno(r, "Failed to chase block device '%s', ignoring: %m", path);
182 : :
183 : 0 : return 1;
184 : : }
|