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