1 /*
2 * virdevmapper.c: Functions for handling device mapper
3 *
4 * Copyright (C) 2018 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but 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
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include "virdevmapper.h"
24 #include "internal.h"
25
26 #ifdef __linux__
27 # include <sys/sysmacros.h>
28 # include <linux/dm-ioctl.h>
29 # include <sys/ioctl.h>
30 # include <sys/types.h>
31 # include <sys/stat.h>
32 # include <fcntl.h>
33
34 # include "virthread.h"
35 # include "viralloc.h"
36 # include "virstring.h"
37 # include "virfile.h"
38 # include "virlog.h"
39 # include "virglibutil.h"
40
41 # define VIR_FROM_THIS VIR_FROM_STORAGE
42
43 VIR_LOG_INIT("util.virdevmapper");
44
45 # define PROC_DEVICES "/proc/devices"
46 # define PROC_DEVICES_BUF_SIZE (16 * 1024)
47 # define DM_NAME "device-mapper"
48 # define DEV_DM_DIR "/dev/" DM_DIR
49 # define CONTROL_PATH DEV_DM_DIR "/" DM_CONTROL_NODE
50
51 # define VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT (16 * 1024)
52 G_STATIC_ASSERT(VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT > sizeof(struct dm_ioctl));
53
54
55 static int
virDevMapperGetMajor(unsigned int * major)56 virDevMapperGetMajor(unsigned int *major)
57 {
58 g_autofree char *buf = NULL;
59 g_auto(GStrv) lines = NULL;
60 size_t i;
61
62 if (!virFileExists(CONTROL_PATH))
63 return -2;
64
65 if (virFileReadAll(PROC_DEVICES, PROC_DEVICES_BUF_SIZE, &buf) < 0)
66 return -1;
67
68 lines = g_strsplit(buf, "\n", 0);
69 if (!lines)
70 return -1;
71
72 for (i = 0; lines[i]; i++) {
73 g_autofree char *dev = NULL;
74 unsigned int maj;
75
76 if (sscanf(lines[i], "%u %ms\n", &maj, &dev) == 2 &&
77 STREQ(dev, DM_NAME)) {
78 *major = maj;
79 break;
80 }
81 }
82
83 if (!lines[i]) {
84 virReportError(VIR_ERR_INTERNAL_ERROR,
85 _("Unable to find major for %s"),
86 DM_NAME);
87 return -1;
88 }
89
90 return 0;
91 }
92
93
94 static void *
virDMIoctl(int controlFD,int cmd,struct dm_ioctl * dm,char ** buf)95 virDMIoctl(int controlFD, int cmd, struct dm_ioctl *dm, char **buf)
96 {
97 size_t bufsize = VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT;
98
99 reread:
100 *buf = g_new0(char, bufsize);
101
102 dm->version[0] = DM_VERSION_MAJOR;
103 dm->version[1] = 0;
104 dm->version[2] = 0;
105 dm->data_size = bufsize;
106 dm->data_start = sizeof(struct dm_ioctl);
107
108 memcpy(*buf, dm, sizeof(struct dm_ioctl));
109
110 if (ioctl(controlFD, cmd, *buf) < 0) {
111 VIR_FREE(*buf);
112 return NULL;
113 }
114
115 memcpy(dm, *buf, sizeof(struct dm_ioctl));
116
117 if (dm->flags & DM_BUFFER_FULL_FLAG) {
118 bufsize += VIR_DEVMAPPER_IOCTL_BUF_SIZE_INCREMENT;
119 VIR_FREE(*buf);
120 goto reread;
121 }
122
123 return *buf + dm->data_start;
124 }
125
126
127 static int
virDMOpen(void)128 virDMOpen(void)
129 {
130 VIR_AUTOCLOSE controlFD = -1;
131 struct dm_ioctl dm;
132 g_autofree char *tmp = NULL;
133 int ret;
134
135 memset(&dm, 0, sizeof(dm));
136
137 if ((controlFD = open(CONTROL_PATH, O_RDWR)) < 0) {
138 /* We can't talk to devmapper. Produce a warning and let
139 * the caller decide what to do next. */
140 if (errno == ENOENT) {
141 VIR_DEBUG("device mapper not available");
142 } else {
143 VIR_WARN("unable to open %s: %s",
144 CONTROL_PATH, g_strerror(errno));
145 }
146 return -2;
147 }
148
149 if (!virDMIoctl(controlFD, DM_VERSION, &dm, &tmp)) {
150 virReportSystemError(errno, "%s",
151 _("Unable to get device-mapper version"));
152 return -1;
153 }
154
155 if (dm.version[0] != DM_VERSION_MAJOR) {
156 virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
157 _("Unsupported device-mapper version. Expected %d got %d"),
158 DM_VERSION_MAJOR, dm.version[0]);
159 return -1;
160 }
161
162 ret = controlFD;
163 controlFD = -1;
164 return ret;
165 }
166
167
168 static char *
virDMSanitizepath(const char * path)169 virDMSanitizepath(const char *path)
170 {
171 g_autofree char *dmDirPath = NULL;
172 struct dirent *ent = NULL;
173 struct stat sb[2];
174 g_autoptr(DIR) dh = NULL;
175 const char *p;
176
177 /* If a path is NOT provided then assume it's DM name */
178 p = strrchr(path, '/');
179
180 if (!p)
181 return g_strdup(path);
182 else
183 p++;
184
185 /* It's a path. Check if the last component is DM name */
186 if (stat(path, &sb[0]) < 0) {
187 virReportError(errno,
188 _("Unable to stat %p"),
189 path);
190 return NULL;
191 }
192
193 dmDirPath = g_strdup_printf(DEV_DM_DIR "/%s", p);
194
195 if (stat(dmDirPath, &sb[1]) == 0 &&
196 sb[0].st_rdev == sb[1].st_rdev) {
197 return g_strdup(p);
198 }
199
200 /* The last component of @path wasn't DM name. Let's check if
201 * there's a device under /dev/mapper/ with the same rdev. */
202 if (virDirOpen(&dh, DEV_DM_DIR) < 0)
203 return NULL;
204
205 while (virDirRead(dh, &ent, DEV_DM_DIR) > 0) {
206 g_autofree char *tmp = g_strdup_printf(DEV_DM_DIR "/%s", ent->d_name);
207
208 if (stat(tmp, &sb[1]) == 0 &&
209 sb[0].st_rdev == sb[1].st_rdev) {
210 return g_steal_pointer(&tmp);
211 }
212 }
213
214 return NULL;
215 }
216
217
218 static int
virDevMapperGetTargetsImpl(int controlFD,const char * path,GSList ** devPaths,unsigned int ttl)219 virDevMapperGetTargetsImpl(int controlFD,
220 const char *path,
221 GSList **devPaths,
222 unsigned int ttl)
223 {
224 g_autofree char *sanitizedPath = NULL;
225 g_autofree char *buf = NULL;
226 struct dm_ioctl dm;
227 struct dm_target_deps *deps = NULL;
228 size_t i;
229
230 memset(&dm, 0, sizeof(dm));
231
232 if (ttl == 0) {
233 errno = ELOOP;
234 return -1;
235 }
236
237 if (!virIsDevMapperDevice(path))
238 return 0;
239
240 if (!(sanitizedPath = virDMSanitizepath(path)))
241 return 0;
242
243 if (virStrcpy(dm.name, sanitizedPath, DM_NAME_LEN) < 0) {
244 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
245 _("Resolved device mapper name too long"));
246 return -1;
247 }
248
249 deps = virDMIoctl(controlFD, DM_TABLE_DEPS, &dm, &buf);
250 if (!deps) {
251 if (errno == ENXIO)
252 return 0;
253
254 virReportSystemError(errno,
255 _("Unable to query dependencies for %s"),
256 path);
257 return -1;
258 }
259
260 for (i = 0; i < deps->count; i++) {
261 char *curpath = g_strdup_printf("/dev/block/%u:%u",
262 major(deps->dev[i]),
263 minor(deps->dev[i]));
264
265 *devPaths = g_slist_prepend(*devPaths, curpath);
266
267 if (virDevMapperGetTargetsImpl(controlFD, curpath, devPaths, ttl - 1) < 0)
268 return -1;
269 }
270
271 return 0;
272 }
273
274
275 /**
276 * virDevMapperGetTargets:
277 * @path: devmapper target
278 * @devPaths: filled in by a GSList containing the paths
279 *
280 * For given @path figure out its targets, and store them in
281 * @devPaths.
282 *
283 * If @path is not a devmapper device, @devPaths is set to NULL and
284 * success is returned.
285 *
286 * If @path consists of yet another devmapper targets these are
287 * consulted recursively.
288 *
289 * Returns 0 on success,
290 * -1 otherwise (with errno set, no libvirt error is
291 * reported)
292 */
293 int
virDevMapperGetTargets(const char * path,GSList ** devPaths)294 virDevMapperGetTargets(const char *path,
295 GSList **devPaths)
296 {
297 VIR_AUTOCLOSE controlFD = -1;
298 const unsigned int ttl = 32;
299 g_autoptr(virGSListString) paths = NULL;
300
301 /* Arbitrary limit on recursion level. A devmapper target can
302 * consist of devices or yet another targets. If that's the
303 * case, we have to stop recursion somewhere. */
304
305 if ((controlFD = virDMOpen()) < 0) {
306 if (controlFD == -2) {
307 /* The CONTROL_PATH doesn't exist or is unusable.
308 * Probably the module isn't loaded, yet. Don't error
309 * out, just exit. */
310 return 0;
311 }
312
313 return -1;
314 }
315
316 if (virDevMapperGetTargetsImpl(controlFD, path, &paths, ttl) < 0)
317 return -1;
318
319 *devPaths = g_slist_reverse(g_steal_pointer(&paths));
320 return 0;
321 }
322
323
324 bool
virIsDevMapperDevice(const char * dev_name)325 virIsDevMapperDevice(const char *dev_name)
326 {
327 struct stat buf;
328 unsigned int major;
329
330 if (virDevMapperGetMajor(&major) < 0)
331 return false;
332
333 if (!stat(dev_name, &buf) &&
334 S_ISBLK(buf.st_mode) &&
335 major(buf.st_rdev) == major)
336 return true;
337
338 return false;
339 }
340
341 #else /* !defined(__linux__) */
342
343 int
virDevMapperGetTargets(const char * path G_GNUC_UNUSED,GSList ** devPaths G_GNUC_UNUSED)344 virDevMapperGetTargets(const char *path G_GNUC_UNUSED,
345 GSList **devPaths G_GNUC_UNUSED)
346 {
347 errno = ENOSYS;
348 return -1;
349 }
350
351
352 bool
virIsDevMapperDevice(const char * dev_name G_GNUC_UNUSED)353 virIsDevMapperDevice(const char *dev_name G_GNUC_UNUSED)
354 {
355 return false;
356 }
357 #endif /* ! defined(__linux__) */
358