Line data Source code
1 : /*
2 : * Copyright (C) 2014 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Vratislav Podzimek <vpodzime@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <string.h>
22 : #include <unistd.h>
23 : #include <blockdev/utils.h>
24 : #include <libdevmapper.h>
25 : #include <stdarg.h>
26 : #include <syslog.h>
27 :
28 : #include "dm.h"
29 : #include "check_deps.h"
30 : #include "dm_logging.h"
31 :
32 : #define DM_MIN_VERSION "1.02.93"
33 :
34 :
35 : /**
36 : * SECTION: dm
37 : * @short_description: plugin for basic operations with device mapper
38 : * @title: DeviceMapper
39 : * @include: dm.h
40 : *
41 : * A plugin for basic operations with device mapper.
42 : */
43 :
44 : /**
45 : * bd_dm_error_quark: (skip)
46 : */
47 0 : GQuark bd_dm_error_quark (void)
48 : {
49 0 : return g_quark_from_static_string ("g-bd-dm-error-quark");
50 : }
51 :
52 : static volatile guint avail_deps = 0;
53 : static GMutex deps_check_lock;
54 :
55 : #define DEPS_DMSETUP 0
56 : #define DEPS_DMSETUP_MASK (1 << DEPS_DMSETUP)
57 : #define DEPS_LAST 1
58 :
59 : static const UtilDep deps[DEPS_LAST] = {
60 : {"dmsetup", DM_MIN_VERSION, NULL, "Library version:\\s+([\\d\\.]+)"},
61 : };
62 :
63 :
64 : /**
65 : * bd_dm_init:
66 : *
67 : * Initializes the plugin. **This function is called automatically by the
68 : * library's initialization functions.**
69 : *
70 : */
71 18 : gboolean bd_dm_init (void) {
72 18 : dm_log_with_errno_init ((dm_log_with_errno_fn) redirect_dm_log);
73 : #ifdef DEBUG
74 : dm_log_init_verbose (LOG_DEBUG);
75 : #else
76 18 : dm_log_init_verbose (LOG_INFO);
77 : #endif
78 :
79 18 : return TRUE;
80 : }
81 :
82 : /**
83 : * bd_dm_close:
84 : *
85 : * Cleans up after the plugin. **This function is called automatically by the
86 : * library's functions that unload it.**
87 : *
88 : */
89 18 : void bd_dm_close (void) {
90 18 : dm_log_with_errno_init (NULL);
91 18 : dm_log_init_verbose (0);
92 18 : }
93 :
94 : /**
95 : * bd_dm_is_tech_avail:
96 : * @tech: the queried tech
97 : * @mode: a bit mask of queried modes of operation (#BDDMTechMode) for @tech
98 : * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
99 : *
100 : * Returns: whether the @tech-@mode combination is available -- supported by the
101 : * plugin implementation and having all the runtime dependencies available
102 : */
103 2 : gboolean bd_dm_is_tech_avail (BDDMTech tech, guint64 mode G_GNUC_UNUSED, GError **error) {
104 : /* all combinations are supported by this implementation of the plugin, but
105 : BD_DM_TECH_MAP requires the 'dmsetup' utility */
106 2 : switch (tech) {
107 2 : case BD_DM_TECH_MAP:
108 2 : return check_deps (&avail_deps, DEPS_DMSETUP_MASK, deps, DEPS_LAST, &deps_check_lock, error);
109 0 : default:
110 0 : return TRUE;
111 : }
112 : }
113 :
114 : /**
115 : * bd_dm_create_linear:
116 : * @map_name: name of the map
117 : * @device: device to create map for
118 : * @length: length of the mapping in sectors
119 : * @uuid: (nullable): UUID for the new dev mapper device or %NULL if not specified
120 : * @error: (out) (optional): place to store error (if any)
121 : *
122 : * Returns: whether the new linear mapping @map_name was successfully created
123 : * for the @device or not
124 : *
125 : * Tech category: %BD_DM_TECH_MAP-%BD_DM_TECH_MODE_CREATE_ACTIVATE
126 : */
127 5 : gboolean bd_dm_create_linear (const gchar *map_name, const gchar *device, guint64 length, const gchar *uuid, GError **error) {
128 5 : gboolean success = FALSE;
129 5 : const gchar *argv[9] = {"dmsetup", "create", map_name, "--table", NULL, NULL, NULL, NULL, NULL};
130 :
131 5 : if (!check_deps (&avail_deps, DEPS_DMSETUP_MASK, deps, DEPS_LAST, &deps_check_lock, error))
132 0 : return FALSE;
133 :
134 5 : gchar *table = g_strdup_printf ("0 %"G_GUINT64_FORMAT" linear %s 0", length, device);
135 5 : argv[4] = table;
136 :
137 5 : if (uuid) {
138 1 : argv[5] = "-u";
139 1 : argv[6] = uuid;
140 1 : argv[7] = device;
141 : } else
142 4 : argv[5] = device;
143 :
144 5 : success = bd_utils_exec_and_report_error (argv, NULL, error);
145 5 : g_free (table);
146 :
147 5 : return success;
148 : }
149 :
150 : /**
151 : * bd_dm_remove:
152 : * @map_name: name of the map to remove
153 : * @error: (out) (optional): place to store error (if any)
154 : *
155 : * Returns: whether the @map_name map was successfully removed or not
156 : *
157 : * Tech category: %BD_DM_TECH_MAP-%BD_DM_TECH_MODE_REMOVE_DEACTIVATE
158 : */
159 10 : gboolean bd_dm_remove (const gchar *map_name, GError **error) {
160 10 : const gchar *argv[4] = {"dmsetup", "remove", map_name, NULL};
161 :
162 10 : if (!check_deps (&avail_deps, DEPS_DMSETUP_MASK, deps, DEPS_LAST, &deps_check_lock, error))
163 0 : return FALSE;
164 :
165 10 : return bd_utils_exec_and_report_error (argv, NULL, error);
166 : }
167 :
168 : /**
169 : * bd_dm_name_from_node:
170 : * @dm_node: name of the DM node (e.g. "dm-0")
171 : * @error: (out) (optional): place to store error (if any)
172 : *
173 : * Returns: map name of the map providing the @dm_node device or %NULL
174 : * (@error) contains the error in such cases)
175 : *
176 : * Tech category: %BD_DM_TECH_MAP-%BD_DM_TECH_MODE_QUERY
177 : */
178 1 : gchar* bd_dm_name_from_node (const gchar *dm_node, GError **error) {
179 1 : gchar *ret = NULL;
180 1 : gboolean success = FALSE;
181 :
182 1 : gchar *sys_path = g_strdup_printf ("/sys/class/block/%s/dm/name", dm_node);
183 :
184 1 : if (access (sys_path, R_OK) != 0) {
185 0 : g_free (sys_path);
186 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_SYS,
187 : "Failed to access dm node's parameters under /sys");
188 0 : return NULL;
189 : }
190 :
191 1 : success = g_file_get_contents (sys_path, &ret, NULL, error);
192 1 : g_free (sys_path);
193 :
194 1 : if (!success) {
195 : /* error is already populated */
196 0 : g_free (ret);
197 0 : return NULL;
198 : }
199 :
200 1 : return g_strstrip (ret);
201 : }
202 :
203 : /**
204 : * bd_dm_node_from_name:
205 : * @map_name: name of the queried DM map
206 : * @error: (out) (optional): place to store error (if any)
207 : *
208 : * Returns: DM node name for the @map_name map or %NULL (@error) contains
209 : * the error in such cases)
210 : *
211 : * Tech category: %BD_DM_TECH_MAP-%BD_DM_TECH_MODE_QUERY
212 : */
213 1 : gchar* bd_dm_node_from_name (const gchar *map_name, GError **error) {
214 1 : gchar *dev_path = NULL;
215 1 : gchar *ret = NULL;
216 1 : gchar *dev_mapper_path = g_strdup_printf ("/dev/mapper/%s", map_name);
217 :
218 1 : dev_path = bd_utils_resolve_device (dev_mapper_path, error);
219 1 : g_free (dev_mapper_path);
220 1 : if (!dev_path)
221 : /* error is already populated */
222 0 : return NULL;
223 :
224 1 : ret = g_path_get_basename (dev_path);
225 1 : g_free (dev_path);
226 :
227 1 : return ret;
228 : }
229 :
230 : /**
231 : * bd_dm_get_subsystem_from_name:
232 : * @device_name: name of the device
233 : * @error: (out) (optional): place to store error (if any)
234 : *
235 : * Returns: subsystem of the given device
236 : *
237 : * Tech category: %BD_DM_TECH_MAP-%BD_DM_TECH_MODE_QUERY
238 : */
239 5 : gchar* bd_dm_get_subsystem_from_name (const gchar *device_name, GError **error) {
240 5 : struct dm_task *task = NULL;
241 : struct dm_info info;
242 5 : const gchar *uuid = NULL;
243 5 : gchar *subsystem = NULL;
244 5 : gchar *hyphen_pos = NULL;
245 :
246 5 : task = dm_task_create (DM_DEVICE_INFO);
247 5 : if (!task) {
248 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_TASK,
249 : "Failed to create DM task");
250 0 : return NULL;
251 : }
252 :
253 5 : if (!dm_task_set_name (task, device_name)) {
254 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_TASK,
255 : "Failed to set device name for DM task");
256 0 : dm_task_destroy (task);
257 0 : return NULL;
258 : }
259 :
260 5 : if (!dm_task_run (task)) {
261 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_TASK,
262 : "Failed to run DM task");
263 0 : dm_task_destroy (task);
264 0 : return NULL;
265 : }
266 :
267 5 : if (!dm_task_get_info (task, &info)) {
268 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_TASK,
269 : "Failed to get info from DM task");
270 0 : dm_task_destroy (task);
271 0 : return NULL;
272 : }
273 :
274 5 : if (!info.exists) {
275 1 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_DEVICE_NOEXIST,
276 : "DM device %s does not exist", device_name);
277 1 : dm_task_destroy (task);
278 1 : return NULL;
279 : }
280 :
281 4 : uuid = dm_task_get_uuid (task);
282 4 : if (!uuid || !(*uuid)) {
283 1 : dm_task_destroy (task);
284 1 : return g_strdup ("");
285 : }
286 :
287 3 : hyphen_pos = strchr (uuid, '-');
288 3 : if (hyphen_pos)
289 2 : subsystem = g_strndup (uuid, hyphen_pos - uuid);
290 : else
291 1 : subsystem = g_strdup ("");
292 :
293 3 : dm_task_destroy (task);
294 3 : return subsystem;
295 : }
296 :
297 : /**
298 : * bd_dm_map_exists:
299 : * @map_name: name of the queried map
300 : * @live_only: whether to go through the live maps only or not
301 : * @active_only: whether to ignore suspended maps or not
302 : * @error: (out) (optional): place to store error (if any)
303 : *
304 : * Returns: whether the given @map_name exists (and is live if @live_only is
305 : * %TRUE (and is active if @active_only is %TRUE)). If %FALSE is returned,
306 : * @error) indicates whether error appeared (non-%NULL) or not (%NULL).
307 : *
308 : * Tech category: %BD_DM_TECH_MAP-%BD_DM_TECH_MODE_QUERY
309 : */
310 4 : gboolean bd_dm_map_exists (const gchar *map_name, gboolean live_only, gboolean active_only, GError **error) {
311 4 : struct dm_task *task_list = NULL;
312 4 : struct dm_task *task_info = NULL;
313 4 : struct dm_names *names = NULL;
314 : struct dm_info info;
315 4 : guint64 next = 0;
316 4 : gboolean ret = FALSE;
317 :
318 4 : task_list = dm_task_create (DM_DEVICE_LIST);
319 4 : if (!task_list) {
320 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_TASK,
321 : "Failed to create DM task");
322 0 : return FALSE;
323 : }
324 :
325 4 : dm_task_run (task_list);
326 4 : names = dm_task_get_names (task_list);
327 :
328 4 : if (!names || !names->dev)
329 0 : return FALSE;
330 :
331 : do {
332 15 : names = (void *)names + next;
333 15 : next = names->next;
334 : /* we are searching for the particular map_name map */
335 15 : if (g_strcmp0 (map_name, names->name) != 0)
336 : /* not matching, skip */
337 12 : continue;
338 :
339 : /* get device info */
340 3 : task_info = dm_task_create (DM_DEVICE_INFO);
341 3 : if (!task_info) {
342 0 : g_set_error (error, BD_DM_ERROR, BD_DM_ERROR_TASK,
343 : "Failed to create DM task");
344 0 : break;
345 : }
346 :
347 : /* something failed, try next one */
348 3 : if (dm_task_set_name (task_info, names->name) == 0) {
349 0 : dm_task_destroy (task_info);
350 0 : continue;
351 : }
352 3 : if (dm_task_run (task_info) == 0) {
353 0 : dm_task_destroy (task_info);
354 0 : continue;
355 : }
356 3 : if (dm_task_get_info (task_info, &info) == 0) {
357 0 : dm_task_destroy (task_info);
358 0 : continue;
359 : }
360 :
361 3 : if (!info.exists) {
362 : /* doesn't exist, try next one */
363 0 : dm_task_destroy (task_info);
364 0 : continue;
365 : }
366 :
367 : /* found existing name match, let's test the restrictions */
368 3 : ret = TRUE;
369 3 : if (live_only)
370 3 : ret = info.live_table;
371 3 : if (active_only)
372 2 : ret = ret && !info.suspended;
373 :
374 3 : dm_task_destroy (task_info);
375 3 : if (ret)
376 : /* found match according to restrictions */
377 2 : break;
378 13 : } while (next);
379 :
380 4 : dm_task_destroy (task_list);
381 :
382 4 : return ret;
383 : }
|