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