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