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 <math.h>
22 : #include <string.h>
23 : #include <unistd.h>
24 : #include <blockdev/utils.h>
25 : #include <libdevmapper.h>
26 :
27 : #include "lvm.h"
28 : #include "lvm-private.h"
29 : #include "check_deps.h"
30 : #include "dm_logging.h"
31 : #include "vdo_stats.h"
32 :
33 : /**
34 : * SECTION: lvm
35 : * @short_description: plugin for operations with LVM
36 : * @title: LVM
37 : * @include: lvm.h
38 : *
39 : * A plugin for operations with LVM. All sizes passed in/out to/from
40 : * the functions are in bytes.
41 : */
42 :
43 : /**
44 : * bd_lvm_error_quark: (skip)
45 : */
46 0 : GQuark bd_lvm_error_quark (void)
47 : {
48 0 : return g_quark_from_static_string ("g-bd-lvm-error-quark");
49 : }
50 :
51 0 : BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data) {
52 0 : if (data == NULL)
53 0 : return NULL;
54 :
55 0 : BDLVMPVdata *new_data = g_new0 (BDLVMPVdata, 1);
56 :
57 0 : new_data->pv_name = g_strdup (data->pv_name);
58 0 : new_data->pv_uuid = g_strdup (data->pv_uuid);
59 0 : new_data->pv_free = data->pv_free;
60 0 : new_data->pv_size = data->pv_size;
61 0 : new_data->pe_start = data->pe_start;
62 0 : new_data->vg_name = g_strdup (data->vg_name);
63 0 : new_data->vg_uuid = g_strdup (data->vg_uuid);
64 0 : new_data->vg_size = data->vg_size;
65 0 : new_data->vg_free = data->vg_free;
66 0 : new_data->vg_extent_size = data->vg_extent_size;
67 0 : new_data->vg_extent_count = data->vg_extent_count;
68 0 : new_data->vg_free_count = data->vg_free_count;
69 0 : new_data->vg_pv_count = data->vg_pv_count;
70 0 : new_data->pv_tags = g_strdupv (data->pv_tags);
71 0 : new_data->missing = data->missing;
72 :
73 0 : return new_data;
74 : }
75 :
76 0 : void bd_lvm_pvdata_free (BDLVMPVdata *data) {
77 0 : if (data == NULL)
78 0 : return;
79 :
80 0 : g_free (data->pv_name);
81 0 : g_free (data->pv_uuid);
82 0 : g_free (data->vg_name);
83 0 : g_free (data->vg_uuid);
84 0 : g_strfreev (data->pv_tags);
85 0 : g_free (data);
86 : }
87 :
88 0 : BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data) {
89 0 : if (data == NULL)
90 0 : return NULL;
91 :
92 0 : BDLVMVGdata *new_data = g_new0 (BDLVMVGdata, 1);
93 :
94 0 : new_data->name = g_strdup (data->name);
95 0 : new_data->uuid = g_strdup (data->uuid);
96 0 : new_data->size = data->size;
97 0 : new_data->free = data->free;
98 0 : new_data->extent_size = data->extent_size;
99 0 : new_data->extent_count = data->extent_count;
100 0 : new_data->free_count = data->free_count;
101 0 : new_data->pv_count = data->pv_count;
102 0 : new_data->vg_tags = g_strdupv (data->vg_tags);
103 0 : return new_data;
104 : }
105 :
106 0 : void bd_lvm_vgdata_free (BDLVMVGdata *data) {
107 0 : if (data == NULL)
108 0 : return;
109 :
110 0 : g_free (data->name);
111 0 : g_free (data->uuid);
112 0 : g_strfreev (data->vg_tags);
113 0 : g_free (data);
114 : }
115 :
116 0 : BDLVMSEGdata* bd_lvm_segdata_copy (BDLVMSEGdata *data) {
117 0 : if (data == NULL)
118 0 : return NULL;
119 :
120 0 : BDLVMSEGdata *new_data = g_new0 (BDLVMSEGdata, 1);
121 :
122 0 : new_data->size_pe = data->size_pe;
123 0 : new_data->pv_start_pe = data->pv_start_pe;
124 0 : new_data->pvdev = g_strdup (data->pvdev);
125 0 : return new_data;
126 : }
127 :
128 0 : void bd_lvm_segdata_free (BDLVMSEGdata *data) {
129 0 : if (data == NULL)
130 0 : return;
131 :
132 0 : g_free (data->pvdev);
133 0 : g_free (data);
134 : }
135 :
136 0 : static BDLVMSEGdata **copy_segs (BDLVMSEGdata **segs) {
137 : int len;
138 : BDLVMSEGdata **new_segs;
139 :
140 0 : if (segs == NULL)
141 0 : return NULL;
142 :
143 0 : for (len = 0; segs[len]; len++)
144 : ;
145 :
146 0 : new_segs = g_new0 (BDLVMSEGdata *, len+1);
147 0 : for (int i = 0; i < len; i++)
148 0 : new_segs[i] = bd_lvm_segdata_copy (segs[i]);
149 :
150 0 : return new_segs;
151 : }
152 :
153 0 : static void free_segs (BDLVMSEGdata **segs) {
154 0 : if (segs == NULL)
155 0 : return;
156 :
157 0 : for (int i = 0; segs[i]; i++)
158 0 : bd_lvm_segdata_free (segs[i]);
159 0 : (g_free) (segs);
160 : }
161 :
162 0 : BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data) {
163 0 : if (data == NULL)
164 0 : return NULL;
165 :
166 0 : BDLVMLVdata *new_data = g_new0 (BDLVMLVdata, 1);
167 :
168 0 : new_data->lv_name = g_strdup (data->lv_name);
169 0 : new_data->vg_name = g_strdup (data->vg_name);
170 0 : new_data->uuid = g_strdup (data->uuid);
171 0 : new_data->size = data->size;
172 0 : new_data->attr = g_strdup (data->attr);
173 0 : new_data->segtype = g_strdup (data->segtype);
174 0 : new_data->origin = g_strdup (data->origin);
175 0 : new_data->pool_lv = g_strdup (data->pool_lv);
176 0 : new_data->data_lv = g_strdup (data->data_lv);
177 0 : new_data->metadata_lv = g_strdup (data->metadata_lv);
178 0 : new_data->roles = g_strdup (data->roles);
179 0 : new_data->move_pv = g_strdup (data->move_pv);
180 0 : new_data->data_percent = data->data_percent;
181 0 : new_data->metadata_percent = data->metadata_percent;
182 0 : new_data->copy_percent = data->copy_percent;
183 0 : new_data->lv_tags = g_strdupv (data->lv_tags);
184 0 : new_data->data_lvs = g_strdupv (data->data_lvs);
185 0 : new_data->metadata_lvs = g_strdupv (data->metadata_lvs);
186 0 : new_data->segs = copy_segs (data->segs);
187 0 : return new_data;
188 : }
189 :
190 0 : void bd_lvm_lvdata_free (BDLVMLVdata *data) {
191 0 : if (data == NULL)
192 0 : return;
193 :
194 0 : g_free (data->lv_name);
195 0 : g_free (data->vg_name);
196 0 : g_free (data->uuid);
197 0 : g_free (data->attr);
198 0 : g_free (data->segtype);
199 0 : g_free (data->origin);
200 0 : g_free (data->pool_lv);
201 0 : g_free (data->data_lv);
202 0 : g_free (data->metadata_lv);
203 0 : g_free (data->roles);
204 0 : g_free (data->move_pv);
205 0 : g_strfreev (data->lv_tags);
206 0 : g_strfreev (data->data_lvs);
207 0 : g_strfreev (data->metadata_lvs);
208 0 : free_segs (data->segs);
209 0 : g_free (data);
210 : }
211 :
212 0 : BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data) {
213 0 : if (data == NULL)
214 0 : return NULL;
215 :
216 0 : BDLVMVDOPooldata *new_data = g_new0 (BDLVMVDOPooldata, 1);
217 :
218 0 : new_data->operating_mode = data->operating_mode;
219 0 : new_data->compression_state = data->compression_state;
220 0 : new_data->index_state = data->index_state;
221 0 : new_data->write_policy = data->write_policy;
222 0 : new_data->used_size = data->used_size;
223 0 : new_data->saving_percent = data->saving_percent;
224 0 : new_data->index_memory_size = data->index_memory_size;
225 0 : new_data->deduplication = data->deduplication;
226 0 : new_data->compression = data->compression;
227 0 : return new_data;
228 : }
229 :
230 0 : void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data) {
231 0 : if (data == NULL)
232 0 : return;
233 :
234 0 : g_free (data);
235 : }
236 :
237 0 : BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data) {
238 0 : if (data == NULL)
239 0 : return NULL;
240 :
241 0 : BDLVMCacheStats *new = g_new0 (BDLVMCacheStats, 1);
242 :
243 0 : new->block_size = data->block_size;
244 0 : new->cache_size = data->cache_size;
245 0 : new->cache_used = data->cache_used;
246 0 : new->md_block_size = data->md_block_size;
247 0 : new->md_size = data->md_size;
248 0 : new->md_used = data->md_used;
249 0 : new->read_hits = data->read_hits;
250 0 : new->read_misses = data->read_misses;
251 0 : new->write_hits = data->write_hits;
252 0 : new->write_misses = data->write_misses;
253 0 : new->mode = data->mode;
254 :
255 0 : return new;
256 : }
257 :
258 0 : void bd_lvm_cache_stats_free (BDLVMCacheStats *data) {
259 0 : g_free (data);
260 0 : }
261 :
262 :
263 : static volatile guint avail_deps = 0;
264 : static volatile guint avail_features = 0;
265 : static volatile guint avail_module_deps = 0;
266 : static GMutex deps_check_lock;
267 :
268 : #define DEPS_LVM 0
269 : #define DEPS_LVM_MASK (1 << DEPS_LVM)
270 : #define DEPS_LVMDEVICES 1
271 : #define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
272 : #define DEPS_LVMCONFIG 2
273 : #define DEPS_LVMCONFIG_MASK (1 << DEPS_LVMCONFIG)
274 : #define DEPS_LAST 3
275 :
276 : static const UtilDep deps[DEPS_LAST] = {
277 : {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
278 : {"lvmdevices", NULL, NULL, NULL},
279 : {"lvmconfig", "2.03.17", "--version", "LVM version:\\s+([\\d\\.]+)"},
280 : };
281 :
282 : #define FEATURES_VDO 0
283 : #define FEATURES_VDO_MASK (1 << FEATURES_VDO)
284 : #define FEATURES_WRITECACHE 0
285 : #define FEATURES_WRITECACHE_MASK (1 << FEATURES_WRITECACHE)
286 : #define FEATURES_LAST 2
287 :
288 : static const UtilFeatureDep features[FEATURES_LAST] = {
289 : {"lvm", "vdo", "segtypes", NULL},
290 : {"lvm", "writecache", "segtypes", NULL},
291 : };
292 :
293 : #define MODULE_DEPS_VDO 0
294 : #define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO)
295 : #define MODULE_DEPS_LAST 1
296 :
297 : static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" };
298 :
299 :
300 : /**
301 : * bd_lvm_init:
302 : *
303 : * Initializes the plugin. **This function is called automatically by the
304 : * library's initialization functions.**
305 : *
306 : */
307 24 : gboolean bd_lvm_init (void) {
308 24 : dm_log_with_errno_init ((dm_log_with_errno_fn) redirect_dm_log);
309 : #ifdef DEBUG
310 : dm_log_init_verbose (LOG_DEBUG);
311 : #else
312 24 : dm_log_init_verbose (LOG_INFO);
313 : #endif
314 :
315 24 : return TRUE;
316 : };
317 :
318 : /**
319 : * bd_lvm_close:
320 : *
321 : * Cleans up after the plugin. **This function is called automatically by the
322 : * library's functions that unload it.**
323 : *
324 : */
325 24 : void bd_lvm_close (void) {
326 24 : dm_log_with_errno_init (NULL);
327 24 : dm_log_init_verbose (0);
328 24 : }
329 :
330 : /**
331 : * bd_lvm_is_tech_avail:
332 : * @tech: the queried tech
333 : * @mode: a bit mask of queried modes of operation (#BDLVMTechMode) for @tech
334 : * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
335 : *
336 : * Returns: whether the @tech-@mode combination is available -- supported by the
337 : * plugin implementation and having all the runtime dependencies available
338 : */
339 160 : gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
340 160 : switch (tech) {
341 1 : case BD_LVM_TECH_THIN_CALCS:
342 1 : if (mode & ~BD_LVM_TECH_MODE_QUERY) {
343 1 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
344 : "Only 'query' supported for thin calculations");
345 1 : return FALSE;
346 : } else
347 0 : return TRUE;
348 0 : case BD_LVM_TECH_CALCS:
349 0 : if (mode & ~BD_LVM_TECH_MODE_QUERY) {
350 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
351 : "Only 'query' supported for calculations");
352 0 : return FALSE;
353 : } else
354 0 : return TRUE;
355 0 : case BD_LVM_TECH_VDO:
356 0 : return check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
357 0 : check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error) &&
358 0 : check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
359 0 : case BD_LVM_TECH_WRITECACHE:
360 0 : return check_features (&avail_features, FEATURES_WRITECACHE_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
361 0 : check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
362 157 : case BD_LVM_TECH_DEVICES:
363 157 : return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
364 0 : case BD_LVM_TECH_CONFIG:
365 0 : return check_deps (&avail_deps, DEPS_LVMCONFIG_MASK, deps, DEPS_LAST, &deps_check_lock, error);
366 2 : default:
367 : /* everything is supported by this implementation of the plugin */
368 2 : return check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
369 : }
370 : }
371 :
372 546 : static gboolean call_lvm_and_report_error (const gchar **args, const BDExtraArg **extra, gboolean lock_config, GError **error) {
373 546 : gboolean success = FALSE;
374 546 : guint i = 0;
375 546 : guint args_length = g_strv_length ((gchar **) args);
376 546 : g_autofree gchar *config_arg = NULL;
377 546 : g_autofree gchar *devices_arg = NULL;
378 :
379 546 : if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
380 0 : return FALSE;
381 :
382 : /* don't allow global config string changes during the run */
383 546 : if (lock_config)
384 538 : g_mutex_lock (&global_config_lock);
385 :
386 : /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
387 546 : const gchar **argv = g_new0 (const gchar*, args_length + 4);
388 :
389 : /* construct argv from args with "lvm" prepended */
390 546 : argv[0] = "lvm";
391 3145 : for (i=0; i < args_length; i++)
392 2599 : argv[i+1] = args[i];
393 546 : if (global_config_str) {
394 15 : config_arg = g_strdup_printf ("--config=%s", global_config_str);
395 15 : argv[++args_length] = config_arg;
396 : }
397 546 : if (global_devices_str) {
398 1 : devices_arg = g_strdup_printf ("--devices=%s", global_devices_str);
399 1 : argv[++args_length] = devices_arg;
400 : }
401 546 : argv[++args_length] = NULL;
402 :
403 546 : success = bd_utils_exec_and_report_error (argv, extra, error);
404 546 : if (lock_config)
405 538 : g_mutex_unlock (&global_config_lock);
406 546 : g_free (argv);
407 :
408 546 : return success;
409 : }
410 :
411 95 : static gboolean call_lvm_and_capture_output (const gchar **args, const BDExtraArg **extra, gchar **output, GError **error) {
412 95 : gboolean success = FALSE;
413 95 : guint i = 0;
414 95 : guint args_length = g_strv_length ((gchar **) args);
415 95 : g_autofree gchar *config_arg = NULL;
416 95 : g_autofree gchar *devices_arg = NULL;
417 :
418 95 : if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
419 0 : return FALSE;
420 :
421 : /* don't allow global config string changes during the run */
422 95 : g_mutex_lock (&global_config_lock);
423 :
424 : /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
425 95 : const gchar **argv = g_new0 (const gchar*, args_length + 4);
426 :
427 : /* construct argv from args with "lvm" prepended */
428 95 : argv[0] = "lvm";
429 991 : for (i=0; i < args_length; i++)
430 896 : argv[i+1] = args[i];
431 95 : if (global_config_str) {
432 0 : config_arg = g_strdup_printf ("--config=%s", global_config_str);
433 0 : argv[++args_length] = config_arg;
434 : }
435 95 : if (global_devices_str) {
436 0 : devices_arg = g_strdup_printf ("--devices=%s", global_devices_str);
437 0 : argv[++args_length] = devices_arg;
438 : }
439 95 : argv[++args_length] = NULL;
440 :
441 95 : success = bd_utils_exec_and_capture_output (argv, extra, output, error);
442 95 : g_mutex_unlock (&global_config_lock);
443 95 : g_free (argv);
444 :
445 95 : return success;
446 : }
447 :
448 : /**
449 : * parse_lvm_vars:
450 : * @str: string to parse
451 : * @num_items: (out): number of parsed items
452 : *
453 : * Returns: (transfer full): a GHashTable containing key-value items parsed from the @string
454 : */
455 139 : static GHashTable* parse_lvm_vars (const gchar *str, guint *num_items) {
456 139 : GHashTable *table = NULL;
457 139 : gchar **items = NULL;
458 139 : gchar **item_p = NULL;
459 139 : gchar **key_val = NULL;
460 :
461 139 : table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
462 139 : *num_items = 0;
463 :
464 139 : items = g_strsplit_set (str, " \t\n", 0);
465 2204 : for (item_p=items; *item_p; item_p++) {
466 2065 : key_val = g_strsplit (*item_p, "=", 2);
467 2065 : if (g_strv_length (key_val) == 2) {
468 : /* we only want to process valid lines (with the '=' character) */
469 1829 : g_hash_table_insert (table, key_val[0], key_val[1]);
470 1829 : g_free (key_val);
471 1829 : (*num_items)++;
472 : } else
473 : /* invalid line, just free key_val */
474 236 : g_strfreev (key_val);
475 : }
476 :
477 139 : g_strfreev (items);
478 139 : return table;
479 : }
480 :
481 15 : static BDLVMPVdata* get_pv_data_from_table (GHashTable *table, gboolean free_table) {
482 15 : BDLVMPVdata *data = g_new0 (BDLVMPVdata, 1);
483 15 : gchar *value = NULL;
484 :
485 15 : data->pv_name = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_PV_NAME"));
486 15 : data->pv_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_PV_UUID"));
487 :
488 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_FREE");
489 15 : if (value)
490 15 : data->pv_free = g_ascii_strtoull (value, NULL, 0);
491 : else
492 0 : data->pv_free = 0;
493 :
494 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_SIZE");
495 15 : if (value)
496 15 : data->pv_size = g_ascii_strtoull (value, NULL, 0);
497 : else
498 0 : data->pv_size = 0;
499 :
500 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PE_START");
501 15 : if (value)
502 15 : data->pe_start = g_ascii_strtoull (value, NULL, 0);
503 : else
504 0 : data->pe_start = 0;
505 :
506 15 : data->vg_name = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_VG_NAME"));
507 15 : data->vg_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_VG_UUID"));
508 :
509 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_SIZE");
510 15 : if (value)
511 15 : data->vg_size = g_ascii_strtoull (value, NULL, 0);
512 : else
513 0 : data->vg_size = 0;
514 :
515 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE");
516 15 : if (value)
517 15 : data->vg_free = g_ascii_strtoull (value, NULL, 0);
518 : else
519 0 : data->vg_free = 0;
520 :
521 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_SIZE");
522 15 : if (value)
523 15 : data->vg_extent_size = g_ascii_strtoull (value, NULL, 0);
524 : else
525 0 : data->vg_extent_size = 0;
526 :
527 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_COUNT");
528 15 : if (value)
529 15 : data->vg_extent_count = g_ascii_strtoull (value, NULL, 0);
530 : else
531 0 : data->vg_extent_count = 0;
532 :
533 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE_COUNT");
534 15 : if (value)
535 15 : data->vg_free_count = g_ascii_strtoull (value, NULL, 0);
536 : else
537 0 : data->vg_free_count = 0;
538 :
539 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_COUNT");
540 15 : if (value)
541 15 : data->vg_pv_count = g_ascii_strtoull (value, NULL, 0);
542 : else
543 0 : data->vg_pv_count = 0;
544 :
545 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_TAGS");
546 15 : if (value)
547 15 : data->pv_tags = g_strsplit (value, ",", -1);
548 : else
549 0 : data->pv_tags = NULL;
550 :
551 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_MISSING");
552 15 : data->missing = (g_strcmp0 (value, "missing") == 0);
553 :
554 15 : if (free_table)
555 15 : g_hash_table_destroy (table);
556 :
557 15 : return data;
558 : }
559 :
560 7 : static BDLVMVGdata* get_vg_data_from_table (GHashTable *table, gboolean free_table) {
561 7 : BDLVMVGdata *data = g_new0 (BDLVMVGdata, 1);
562 7 : gchar *value = NULL;
563 :
564 7 : data->name = g_strdup (g_hash_table_lookup (table, "LVM2_VG_NAME"));
565 7 : data->uuid = g_strdup (g_hash_table_lookup (table, "LVM2_VG_UUID"));
566 :
567 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_SIZE");
568 7 : if (value)
569 7 : data->size = g_ascii_strtoull (value, NULL, 0);
570 : else
571 0 : data->size = 0;
572 :
573 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE");
574 7 : if (value)
575 7 : data->free = g_ascii_strtoull (value, NULL, 0);
576 : else
577 0 : data->free= 0;
578 :
579 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_SIZE");
580 7 : if (value)
581 7 : data->extent_size = g_ascii_strtoull (value, NULL, 0);
582 : else
583 0 : data->extent_size = 0;
584 :
585 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_COUNT");
586 7 : if (value)
587 7 : data->extent_count = g_ascii_strtoull (value, NULL, 0);
588 : else
589 0 : data->extent_count = 0;
590 :
591 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE_COUNT");
592 7 : if (value)
593 7 : data->free_count = g_ascii_strtoull (value, NULL, 0);
594 : else
595 0 : data->free_count = 0;
596 :
597 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_COUNT");
598 7 : if (value)
599 7 : data->pv_count = g_ascii_strtoull (value, NULL, 0);
600 : else
601 0 : data->pv_count = 0;
602 :
603 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXPORTED");
604 7 : if (value && g_strcmp0 (value, "exported") == 0)
605 0 : data->exported = TRUE;
606 : else
607 7 : data->exported = FALSE;
608 :
609 7 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_TAGS");
610 7 : if (value)
611 7 : data->vg_tags = g_strsplit (value, ",", -1);
612 : else
613 0 : data->vg_tags = NULL;
614 :
615 7 : if (free_table)
616 7 : g_hash_table_destroy (table);
617 :
618 7 : return data;
619 : }
620 :
621 8 : static gchar **prepare_sublvs (gchar **values, gchar *extra_value) {
622 : /* LVM2 guarantees: No "/dev/" prefixes or "[unknown]" in a list of sub-lvs. */
623 8 : gboolean found_extra = FALSE;
624 24 : for (int i = 0; values[i]; i++) {
625 16 : gchar *paren = strrchr (values[i], '(');
626 16 : if (paren) {
627 : /* LVM2 guarantees: start offsets of sub-lvs are always zero. */
628 16 : *paren = '\0';
629 : }
630 16 : if (g_strcmp0 (extra_value, values[i]) == 0)
631 0 : found_extra = TRUE;
632 : }
633 8 : if (extra_value && *extra_value && !found_extra) {
634 0 : int len = g_strv_length (values);
635 0 : gchar **new_values = g_new0 (gchar *, len+2);
636 0 : for (int j = 0; j < len; j++)
637 0 : new_values[j] = values[j];
638 0 : new_values[len] = g_strdup (extra_value);
639 0 : g_free (values);
640 0 : values = new_values;
641 : }
642 8 : return values;
643 : }
644 :
645 86 : static BDLVMLVdata* get_lv_data_from_table (GHashTable *table, gboolean free_table) {
646 86 : BDLVMLVdata *data = g_new0 (BDLVMLVdata, 1);
647 86 : gchar *value = NULL;
648 :
649 86 : data->lv_name = g_strdup (g_hash_table_lookup (table, "LVM2_LV_NAME"));
650 86 : data->vg_name = g_strdup (g_hash_table_lookup (table, "LVM2_VG_NAME"));
651 86 : data->uuid = g_strdup (g_hash_table_lookup (table, "LVM2_LV_UUID"));
652 :
653 86 : value = (gchar*) g_hash_table_lookup (table, "LVM2_LV_SIZE");
654 86 : if (value)
655 86 : data->size = g_ascii_strtoull (value, NULL, 0);
656 : else
657 0 : data->size = 0;
658 :
659 86 : data->attr = g_strdup (g_hash_table_lookup (table, "LVM2_LV_ATTR"));
660 :
661 86 : value = g_hash_table_lookup (table, "LVM2_SEGTYPE");
662 86 : if (g_strcmp0 (value, "error") == 0) {
663 : /* A segment type "error" appears when "vgreduce
664 : * --removemissing" replaces a missing PV with a device mapper
665 : * "error" target. It very likely was a "linear" segment before that
666 : * and will again be "linear" after repair. Let's not expose
667 : * this implementation detail.
668 : */
669 2 : value = "linear";
670 : }
671 86 : data->segtype = g_strdup (value);
672 :
673 86 : data->origin = g_strdup (g_hash_table_lookup (table, "LVM2_ORIGIN"));
674 86 : data->pool_lv = g_strdup (g_hash_table_lookup (table, "LVM2_POOL_LV"));
675 86 : data->data_lv = g_strdup (g_hash_table_lookup (table, "LVM2_DATA_LV"));
676 86 : data->metadata_lv = g_strdup (g_hash_table_lookup (table, "LVM2_METADATA_LV"));
677 86 : data->roles = g_strdup (g_hash_table_lookup (table, "LVM2_LV_ROLE"));
678 :
679 86 : data->move_pv = g_strdup (g_hash_table_lookup (table, "LVM2_MOVE_PV"));
680 :
681 86 : value = (gchar*) g_hash_table_lookup (table, "LVM2_DATA_PERCENT");
682 86 : if (value)
683 86 : data->data_percent = g_ascii_strtoull (value, NULL, 0);
684 : else
685 0 : data->data_percent = 0;
686 :
687 86 : value = (gchar*) g_hash_table_lookup (table, "LVM2_METADATA_PERCENT");
688 86 : if (value)
689 86 : data->metadata_percent = g_ascii_strtoull (value, NULL, 0);
690 : else
691 0 : data->metadata_percent = 0;
692 :
693 86 : value = (gchar*) g_hash_table_lookup (table, "LVM2_COPY_PERCENT");
694 86 : if (value)
695 86 : data->copy_percent = g_ascii_strtoull (value, NULL, 0);
696 : else
697 0 : data->copy_percent = 0;
698 :
699 86 : value = (gchar*) g_hash_table_lookup (table, "LVM2_LV_TAGS");
700 86 : if (value)
701 86 : data->lv_tags = g_strsplit (value, ",", -1);
702 : else
703 0 : data->lv_tags = NULL;
704 :
705 : /* replace '[' and ']' (marking LVs as internal) with spaces and then
706 : remove all the leading and trailing whitespace */
707 86 : g_strstrip (g_strdelimit (data->pool_lv, "[]", ' '));
708 86 : g_strstrip (g_strdelimit (data->data_lv, "[]", ' '));
709 86 : g_strstrip (g_strdelimit (data->metadata_lv, "[]", ' '));
710 :
711 86 : value = (gchar*) g_hash_table_lookup (table, "LVM2_DEVICES");
712 86 : if (value) {
713 23 : gchar **values = g_strsplit (value, ",", -1);
714 :
715 : /* If values starts with "/dev/", we have a single PV.
716 :
717 : If the list is empty, this is probably an "error" segment
718 : resulting from a "vgreduce --removemissing" operation.
719 :
720 : If the value starts with "[unknown]", it is a segment with a
721 : missing PV that hasn't been converted to an "error" segment
722 : yet.
723 :
724 : Otherwise it is a list of sub-lvs.
725 :
726 : LVM2 guarantees: only one entry if the first is a PV
727 : Additional segments are added in merge_lv_data below.
728 : */
729 23 : if (!values[0] || g_str_has_prefix (values[0], "[unknown]")) {
730 4 : data->segs = g_new0 (BDLVMSEGdata *, 1);
731 4 : data->segs[0] = NULL;
732 4 : g_strfreev (values);
733 19 : } else if (g_str_has_prefix (values[0], "/dev/")) {
734 15 : data->segs = g_new0 (BDLVMSEGdata *, 2);
735 15 : data->segs[0] = g_new0 (BDLVMSEGdata, 1);
736 15 : data->segs[1] = NULL;
737 :
738 15 : gchar *paren = strrchr (values[0], '(');
739 15 : if (paren) {
740 15 : data->segs[0]->pv_start_pe = atoi (paren+1);
741 15 : *paren = '\0';
742 : }
743 15 : data->segs[0]->pvdev = g_strdup (values[0]);
744 15 : value = (gchar*) g_hash_table_lookup (table, "LVM2_SEG_SIZE_PE");
745 15 : if (value)
746 15 : data->segs[0]->size_pe = g_ascii_strtoull (value, NULL, 0);
747 15 : g_strfreev (values);
748 : } else {
749 4 : data->data_lvs = prepare_sublvs (values, data->data_lv);
750 4 : value = (gchar*) g_hash_table_lookup (table, "LVM2_METADATA_DEVICES");
751 4 : data->metadata_lvs = prepare_sublvs (g_strsplit (value ?: "", ",", -1), data->metadata_lv);
752 : }
753 : }
754 :
755 86 : if (free_table)
756 86 : g_hash_table_destroy (table);
757 :
758 86 : return data;
759 : }
760 :
761 1 : static void merge_lv_data (BDLVMLVdata *data, BDLVMLVdata *more_data) {
762 : /* LVM2 guarantees:
763 : - more_data->data_lvs is NULL
764 : - more_data->metadata_lvs is NULL
765 : - more_data->segs has zero or one entry
766 : - more_data->seg_type is the same as data->seg_type (after mapping "error" to "linear")
767 : */
768 :
769 1 : if (more_data->segs && more_data->segs[0]) {
770 : int i;
771 2 : for (i = 0; data->segs && data->segs[i]; i++)
772 : ;
773 :
774 1 : BDLVMSEGdata **new_segs = g_new0 (BDLVMSEGdata *, i+2);
775 2 : for (i = 0; data->segs && data->segs[i]; i++)
776 1 : new_segs[i] = data->segs[i];
777 1 : new_segs[i] = more_data->segs[0];
778 1 : g_free (data->segs);
779 1 : data->segs = new_segs;
780 1 : more_data->segs[0] = NULL;
781 : }
782 1 : }
783 :
784 10 : static BDLVMVDOPooldata* get_vdo_data_from_table (GHashTable *table, gboolean free_table) {
785 10 : BDLVMVDOPooldata *data = g_new0 (BDLVMVDOPooldata, 1);
786 10 : gchar *value = NULL;
787 :
788 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_OPERATING_MODE");
789 10 : if (g_strcmp0 (value, "recovering") == 0)
790 0 : data->operating_mode = BD_LVM_VDO_MODE_RECOVERING;
791 10 : else if (g_strcmp0 (value, "read-only") == 0)
792 0 : data->operating_mode = BD_LVM_VDO_MODE_READ_ONLY;
793 10 : else if (g_strcmp0 (value, "normal") == 0)
794 10 : data->operating_mode = BD_LVM_VDO_MODE_NORMAL;
795 : else {
796 0 : bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO operating mode: %s", value);
797 0 : data->operating_mode = BD_LVM_VDO_MODE_UNKNOWN;
798 : }
799 :
800 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_COMPRESSION_STATE");
801 10 : if (g_strcmp0 (value, "online") == 0)
802 8 : data->compression_state = BD_LVM_VDO_COMPRESSION_ONLINE;
803 2 : else if (g_strcmp0 (value, "offline") == 0)
804 2 : data->compression_state = BD_LVM_VDO_COMPRESSION_OFFLINE;
805 : else {
806 0 : bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO compression state: %s", value);
807 0 : data->compression_state = BD_LVM_VDO_COMPRESSION_UNKNOWN;
808 : }
809 :
810 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_INDEX_STATE");
811 10 : if (g_strcmp0 (value, "error") == 0)
812 0 : data->index_state = BD_LVM_VDO_INDEX_ERROR;
813 10 : else if (g_strcmp0 (value, "closed") == 0)
814 0 : data->index_state = BD_LVM_VDO_INDEX_CLOSED;
815 10 : else if (g_strcmp0 (value, "opening") == 0)
816 4 : data->index_state = BD_LVM_VDO_INDEX_OPENING;
817 6 : else if (g_strcmp0 (value, "closing") == 0)
818 1 : data->index_state = BD_LVM_VDO_INDEX_CLOSING;
819 5 : else if (g_strcmp0 (value, "offline") == 0)
820 0 : data->index_state = BD_LVM_VDO_INDEX_OFFLINE;
821 5 : else if (g_strcmp0 (value, "online") == 0)
822 5 : data->index_state = BD_LVM_VDO_INDEX_ONLINE;
823 : else {
824 0 : bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO index state: %s", value);
825 0 : data->index_state = BD_LVM_VDO_INDEX_UNKNOWN;
826 : }
827 :
828 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_WRITE_POLICY");
829 10 : if (g_strcmp0 (value, "auto") == 0)
830 9 : data->write_policy = BD_LVM_VDO_WRITE_POLICY_AUTO;
831 1 : else if (g_strcmp0 (value, "sync") == 0)
832 1 : data->write_policy = BD_LVM_VDO_WRITE_POLICY_SYNC;
833 0 : else if (g_strcmp0 (value, "async") == 0)
834 0 : data->write_policy = BD_LVM_VDO_WRITE_POLICY_ASYNC;
835 : else {
836 0 : bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO write policy: %s", value);
837 0 : data->write_policy = BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
838 : }
839 :
840 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_INDEX_MEMORY_SIZE");
841 10 : if (value)
842 10 : data->index_memory_size = g_ascii_strtoull (value, NULL, 0);
843 : else
844 0 : data->index_memory_size = 0;
845 :
846 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_USED_SIZE");
847 10 : if (value)
848 10 : data->used_size = g_ascii_strtoull (value, NULL, 0);
849 : else
850 0 : data->used_size = 0;
851 :
852 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_SAVING_PERCENT");
853 10 : if (value)
854 10 : data->saving_percent = g_ascii_strtoull (value, NULL, 0);
855 : else
856 0 : data->saving_percent = 0;
857 :
858 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_COMPRESSION");
859 10 : if (value && g_strcmp0 (value, "enabled") == 0)
860 8 : data->compression = TRUE;
861 : else
862 2 : data->compression = FALSE;
863 :
864 10 : value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_DEDUPLICATION");
865 10 : if (value && g_strcmp0 (value, "enabled") == 0)
866 9 : data->deduplication = TRUE;
867 : else
868 1 : data->deduplication = FALSE;
869 :
870 10 : if (free_table)
871 10 : g_hash_table_destroy (table);
872 :
873 10 : return data;
874 : }
875 :
876 : /**
877 : * bd_lvm_pvcreate:
878 : * @device: the device to make PV from
879 : * @data_alignment: data (first PE) alignment or 0 to use the default
880 : * @metadata_size: size of the area reserved for metadata or 0 to use the default
881 : * @extra: (nullable) (array zero-terminated=1): extra options for the PV creation
882 : * (just passed to LVM as is)
883 : * @error: (out) (optional): place to store error (if any)
884 : *
885 : * Returns: whether the PV was successfully created or not
886 : *
887 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
888 : */
889 81 : gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error) {
890 81 : const gchar *args[5] = {"pvcreate", device, NULL, NULL, NULL};
891 81 : guint next_arg = 2;
892 81 : gchar *dataalign_str = NULL;
893 81 : gchar *metadata_str = NULL;
894 81 : gboolean ret = FALSE;
895 :
896 81 : if (data_alignment != 0) {
897 1 : dataalign_str = g_strdup_printf ("--dataalignment=%"G_GUINT64_FORMAT"K", data_alignment / 1024);
898 1 : args[next_arg++] = dataalign_str;
899 : }
900 :
901 81 : if (metadata_size != 0) {
902 1 : metadata_str = g_strdup_printf ("--metadatasize=%"G_GUINT64_FORMAT"K", metadata_size / 1024);
903 1 : args[next_arg++] = metadata_str;
904 : }
905 :
906 81 : ret = call_lvm_and_report_error (args, extra, TRUE, error);
907 81 : g_free (dataalign_str);
908 81 : g_free (metadata_str);
909 :
910 81 : return ret;
911 : }
912 :
913 : /**
914 : * bd_lvm_pvresize:
915 : * @device: the device to resize
916 : * @size: the new requested size of the PV or 0 if it should be adjusted to device's size
917 : * @extra: (nullable) (array zero-terminated=1): extra options for the PV resize
918 : * (just passed to LVM as is)
919 : * @error: (out) (optional): place to store error (if any)
920 : *
921 : * Returns: whether the PV's size was successfully changed or not
922 : *
923 : * If given @size different from 0, sets the PV's size to the given value (see
924 : * pvresize(8)). If given @size 0, adjusts the PV's size to the underlying
925 : * block device's size.
926 : *
927 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
928 : */
929 4 : gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error) {
930 4 : gchar *size_str = NULL;
931 4 : const gchar *args[6] = {"pvresize", "-y", NULL, NULL, NULL, NULL};
932 4 : guint8 next_pos = 2;
933 4 : guint8 to_free_pos = 0;
934 4 : gboolean ret = FALSE;
935 :
936 4 : if (size != 0) {
937 4 : size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
938 4 : args[next_pos] = "--setphysicalvolumesize";
939 4 : next_pos++;
940 4 : args[next_pos] = size_str;
941 4 : to_free_pos = next_pos;
942 4 : next_pos++;
943 : }
944 :
945 4 : args[next_pos] = device;
946 :
947 4 : ret = call_lvm_and_report_error (args, extra, TRUE, error);
948 4 : if (to_free_pos > 0)
949 4 : g_free ((gchar *) args[to_free_pos]);
950 :
951 4 : return ret;
952 : }
953 :
954 : /**
955 : * bd_lvm_pvremove:
956 : * @device: the PV device to be removed/destroyed
957 : * @extra: (nullable) (array zero-terminated=1): extra options for the PV removal
958 : * (just passed to LVM as is)
959 : * @error: (out) (optional): place to store error (if any)
960 : *
961 : * Returns: whether the PV was successfully removed/destroyed or not
962 : *
963 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
964 : */
965 151 : gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error) {
966 : /* one has to be really persuasive to remove a PV (the double --force is not
967 : bug, at least not in this code) */
968 151 : const gchar *args[6] = {"pvremove", "--force", "--force", "--yes", device, NULL};
969 :
970 151 : return call_lvm_and_report_error (args, extra, TRUE, error);
971 : }
972 :
973 0 : static gboolean extract_pvmove_progress (const gchar *line, guint8 *completion) {
974 0 : gchar *num_start = NULL;
975 0 : gint n_scanned = 0;
976 0 : guint8 try_completion = 0;
977 :
978 0 : num_start = strrchr (line, ' ');
979 0 : if (!num_start)
980 0 : return FALSE;
981 0 : num_start++;
982 0 : n_scanned = sscanf (num_start, "%hhu", &try_completion);
983 0 : if (n_scanned == 1) {
984 0 : *completion = try_completion;
985 0 : return TRUE;
986 : }
987 0 : return FALSE;
988 : }
989 :
990 : /**
991 : * bd_lvm_pvmove:
992 : * @src: the PV device to move extents off of
993 : * @dest: (nullable): the PV device to move extents onto or %NULL
994 : * @extra: (nullable) (array zero-terminated=1): extra options for the PV move
995 : * (just passed to LVM as is)
996 : * @error: (out) (optional): place to store error (if any)
997 : *
998 : * Returns: whether the extents from the @src PV where successfully moved or not
999 : *
1000 : * If @dest is %NULL, VG allocation rules are used for the extents from the @src
1001 : * PV (see pvmove(8)).
1002 : *
1003 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1004 : */
1005 0 : gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error) {
1006 0 : const gchar *args[6] = {"pvmove", "-i", "1", src, NULL, NULL};
1007 0 : gint status = 0;
1008 0 : if (dest)
1009 0 : args[4] = dest;
1010 :
1011 0 : return bd_utils_exec_and_report_progress (args, extra, extract_pvmove_progress, &status, error);
1012 : }
1013 :
1014 : /**
1015 : * bd_lvm_pvscan:
1016 : * @device: (nullable): the device to scan for PVs or %NULL
1017 : * @update_cache: whether to update the lvmetad cache or not
1018 : * @extra: (nullable) (array zero-terminated=1): extra options for the PV scan
1019 : * (just passed to LVM as is)
1020 : * @error: (out) (optional): place to store error (if any)
1021 : *
1022 : * Returns: whether the system or @device was successfully scanned for PVs or not
1023 : *
1024 : * The @device argument is used only if @update_cache is %TRUE. Otherwise the
1025 : * whole system is scanned for PVs.
1026 : *
1027 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1028 : */
1029 5 : gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error) {
1030 5 : const gchar *args[4] = {"pvscan", NULL, NULL, NULL};
1031 5 : if (update_cache) {
1032 3 : args[1] = "--cache";
1033 3 : args[2] = device;
1034 : }
1035 : else
1036 2 : if (device)
1037 0 : bd_utils_log_format (BD_UTILS_LOG_WARNING, "Ignoring the device argument in pvscan (cache update not requested)");
1038 :
1039 5 : return call_lvm_and_report_error (args, extra, TRUE, error);
1040 : }
1041 :
1042 9 : static gboolean _manage_lvm_tags (const gchar *devspec, const gchar **tags, const gchar *action, const gchar *cmd, GError **error) {
1043 9 : guint tags_len = g_strv_length ((gchar **) tags);
1044 9 : const gchar **argv = g_new0 (const gchar*, 2 * tags_len + 3);
1045 9 : guint next_arg = 0;
1046 9 : gboolean success = FALSE;
1047 :
1048 9 : argv[next_arg++] = cmd;
1049 27 : for (guint i = 0; i < tags_len; i++) {
1050 18 : argv[next_arg++] = action;
1051 18 : argv[next_arg++] = tags[i];
1052 : }
1053 9 : argv[next_arg++] = devspec;
1054 9 : argv[next_arg] = NULL;
1055 :
1056 9 : success = call_lvm_and_report_error (argv, NULL, TRUE, error);
1057 9 : g_free (argv);
1058 9 : return success;
1059 : }
1060 :
1061 : /**
1062 : * bd_lvm_add_pv_tags:
1063 : * @device: the device to set PV tags for
1064 : * @tags: (array zero-terminated=1): list of tags to add
1065 : * @error: (out) (optional): place to store error (if any)
1066 : *
1067 : * Returns: whether the tags were successfully added to @device or not
1068 : *
1069 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1070 : */
1071 2 : gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error) {
1072 2 : return _manage_lvm_tags (device, tags, "--addtag", "pvchange", error);
1073 : }
1074 :
1075 : /**
1076 : * bd_lvm_delete_pv_tags:
1077 : * @device: the device to set PV tags for
1078 : * @tags: (array zero-terminated=1): list of tags to remove
1079 : * @error: (out) (optional): place to store error (if any)
1080 : *
1081 : * Returns: whether the tags were successfully removed from @device or not
1082 : *
1083 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1084 : */
1085 1 : gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error) {
1086 1 : return _manage_lvm_tags (device, tags, "--deltag", "pvchange", error);
1087 : }
1088 :
1089 : /**
1090 : * bd_lvm_pvinfo:
1091 : * @device: a PV to get information about or %NULL
1092 : * @error: (out) (optional): place to store error (if any)
1093 : *
1094 : * Returns: (transfer full): information about the PV on the given @device or
1095 : * %NULL in case of error (the @error) gets populated in those cases)
1096 : *
1097 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1098 : */
1099 9 : BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error) {
1100 9 : const gchar *args[10] = {"pvs", "--unit=b", "--nosuffix", "--nameprefixes",
1101 : "--unquoted", "--noheadings",
1102 : "-o", "pv_name,pv_uuid,pv_free,pv_size,pe_start,vg_name,vg_uuid,vg_size," \
1103 : "vg_free,vg_extent_size,vg_extent_count,vg_free_count,pv_count,pv_tags,pv_missing",
1104 : device, NULL};
1105 9 : GHashTable *table = NULL;
1106 9 : gboolean success = FALSE;
1107 9 : gchar *output = NULL;
1108 9 : gchar **lines = NULL;
1109 9 : gchar **lines_p = NULL;
1110 : guint num_items;
1111 :
1112 9 : success = call_lvm_and_capture_output (args, NULL, &output, error);
1113 9 : if (!success)
1114 : /* the error is already populated from the call */
1115 0 : return NULL;
1116 :
1117 9 : lines = g_strsplit (output, "\n", 0);
1118 9 : g_free (output);
1119 :
1120 9 : for (lines_p = lines; *lines_p; lines_p++) {
1121 9 : table = parse_lvm_vars ((*lines_p), &num_items);
1122 9 : if (table && (num_items == 15)) {
1123 9 : g_clear_error (error);
1124 9 : g_strfreev (lines);
1125 9 : return get_pv_data_from_table (table, TRUE);
1126 : } else
1127 0 : if (table)
1128 0 : g_hash_table_destroy (table);
1129 : }
1130 0 : g_strfreev (lines);
1131 :
1132 : /* getting here means no usable info was found */
1133 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
1134 : "Failed to parse information about the PV");
1135 0 : return NULL;
1136 : }
1137 :
1138 : /**
1139 : * bd_lvm_pvs:
1140 : * @error: (out) (optional): place to store error (if any)
1141 : *
1142 : * Returns: (array zero-terminated=1): information about PVs found in the system
1143 : *
1144 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1145 : */
1146 4 : BDLVMPVdata** bd_lvm_pvs (GError **error) {
1147 4 : const gchar *args[9] = {"pvs", "--unit=b", "--nosuffix", "--nameprefixes",
1148 : "--unquoted", "--noheadings",
1149 : "-o", "pv_name,pv_uuid,pv_free,pv_size,pe_start,vg_name,vg_uuid,vg_size," \
1150 : "vg_free,vg_extent_size,vg_extent_count,vg_free_count,pv_count,pv_tags,pv_missing",
1151 : NULL};
1152 4 : GHashTable *table = NULL;
1153 4 : gboolean success = FALSE;
1154 4 : gchar *output = NULL;
1155 4 : gchar **lines = NULL;
1156 4 : gchar **lines_p = NULL;
1157 : guint num_items;
1158 : GPtrArray *pvs;
1159 4 : BDLVMPVdata *pvdata = NULL;
1160 :
1161 4 : pvs = g_ptr_array_new ();
1162 :
1163 4 : success = call_lvm_and_capture_output (args, NULL, &output, error);
1164 4 : if (!success) {
1165 1 : if (g_error_matches (*error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
1166 : /* no output => no VGs, not an error */
1167 1 : g_clear_error (error);
1168 : /* return an empty list */
1169 1 : g_ptr_array_add (pvs, NULL);
1170 1 : return (BDLVMPVdata **) g_ptr_array_free (pvs, FALSE);
1171 : }
1172 : else {
1173 : /* the error is already populated from the call */
1174 0 : g_ptr_array_free (pvs, TRUE);
1175 0 : return NULL;
1176 : }
1177 : }
1178 :
1179 3 : lines = g_strsplit (output, "\n", 0);
1180 3 : g_free (output);
1181 :
1182 12 : for (lines_p = lines; *lines_p; lines_p++) {
1183 9 : table = parse_lvm_vars ((*lines_p), &num_items);
1184 9 : if (table && (num_items == 15)) {
1185 : /* valid line, try to parse and record it */
1186 6 : pvdata = get_pv_data_from_table (table, TRUE);
1187 6 : if (pvdata)
1188 6 : g_ptr_array_add (pvs, pvdata);
1189 : } else
1190 3 : if (table)
1191 3 : g_hash_table_destroy (table);
1192 : }
1193 :
1194 3 : g_strfreev (lines);
1195 :
1196 3 : if (pvs->len == 0) {
1197 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
1198 : "Failed to parse information about PVs");
1199 0 : g_ptr_array_free (pvs, TRUE);
1200 0 : return NULL;
1201 : }
1202 :
1203 : /* returning NULL-terminated array of BDLVMPVdata */
1204 3 : g_ptr_array_add (pvs, NULL);
1205 3 : return (BDLVMPVdata **) g_ptr_array_free (pvs, FALSE);
1206 : }
1207 :
1208 : /**
1209 : * bd_lvm_vgcreate:
1210 : * @name: name of the newly created VG
1211 : * @pv_list: (array zero-terminated=1): list of PVs the newly created VG should use
1212 : * @pe_size: PE size or 0 if the default value should be used
1213 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG creation
1214 : * (just passed to LVM as is)
1215 : * @error: (out) (optional): place to store error (if any)
1216 : *
1217 : * Returns: whether the VG @name was successfully created or not
1218 : *
1219 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
1220 : */
1221 46 : gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error) {
1222 46 : guint i = 0;
1223 46 : guint pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
1224 46 : const gchar **argv = g_new0 (const gchar*, pv_list_len + 5);
1225 46 : pe_size = RESOLVE_PE_SIZE (pe_size);
1226 46 : gboolean success = FALSE;
1227 :
1228 46 : argv[0] = "vgcreate";
1229 46 : argv[1] = "-s";
1230 46 : argv[2] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", pe_size / 1024);
1231 46 : argv[3] = name;
1232 121 : for (i=4; i < (pv_list_len + 4); i++) {
1233 75 : argv[i] = pv_list[i-4];
1234 : }
1235 46 : argv[i] = NULL;
1236 :
1237 46 : success = call_lvm_and_report_error (argv, extra, TRUE, error);
1238 46 : g_free ((gchar *) argv[2]);
1239 46 : g_free (argv);
1240 :
1241 46 : return success;
1242 : }
1243 :
1244 : /**
1245 : * bd_lvm_vgremove:
1246 : * @vg_name: name of the to be removed VG
1247 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG removal
1248 : * (just passed to LVM as is)
1249 : * @error: (out) (optional): place to store error (if any)
1250 : *
1251 : * Returns: whether the VG was successfully removed or not
1252 : *
1253 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
1254 : */
1255 49 : gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
1256 49 : const gchar *args[4] = {"vgremove", "--force", vg_name, NULL};
1257 :
1258 49 : return call_lvm_and_report_error (args, extra, TRUE, error);
1259 : }
1260 :
1261 : /**
1262 : * bd_lvm_vgrename:
1263 : * @old_vg_name: old name of the VG to rename
1264 : * @new_vg_name: new name for the @old_vg_name VG
1265 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG rename
1266 : * (just passed to LVM as is)
1267 : * @error: (out) (optional): place to store error (if any)
1268 : *
1269 : * Returns: whether the VG was successfully renamed or not
1270 : *
1271 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1272 : */
1273 3 : gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error) {
1274 3 : const gchar *args[4] = {"vgrename", old_vg_name, new_vg_name, NULL};
1275 :
1276 3 : return call_lvm_and_report_error (args, extra, TRUE, error);
1277 : }
1278 :
1279 : /**
1280 : * bd_lvm_vgactivate:
1281 : * @vg_name: name of the to be activated VG
1282 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG activation
1283 : * (just passed to LVM as is)
1284 : * @error: (out) (optional): place to store error (if any)
1285 : *
1286 : * Returns: whether the VG was successfully activated or not
1287 : *
1288 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1289 : */
1290 3 : gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
1291 3 : const gchar *args[4] = {"vgchange", "-ay", vg_name, NULL};
1292 :
1293 3 : return call_lvm_and_report_error (args, extra, TRUE, error);
1294 : }
1295 :
1296 : /**
1297 : * bd_lvm_vgdeactivate:
1298 : * @vg_name: name of the to be deactivated VG
1299 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG deactivation
1300 : * (just passed to LVM as is)
1301 : * @error: (out) (optional): place to store error (if any)
1302 : *
1303 : * Returns: whether the VG was successfully deactivated or not
1304 : *
1305 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1306 : */
1307 3 : gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
1308 3 : const gchar *args[4] = {"vgchange", "-an", vg_name, NULL};
1309 :
1310 3 : return call_lvm_and_report_error (args, extra, TRUE, error);
1311 : }
1312 :
1313 : /**
1314 : * bd_lvm_vgextend:
1315 : * @vg_name: name of the to be extended VG
1316 : * @device: PV device to extend the @vg_name VG with
1317 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG extension
1318 : * (just passed to LVM as is)
1319 : * @error: (out) (optional): place to store error (if any)
1320 : *
1321 : * Returns: whether the VG @vg_name was successfully extended with the given @device or not.
1322 : *
1323 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1324 : */
1325 4 : gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
1326 4 : const gchar *args[4] = {"vgextend", vg_name, device, NULL};
1327 :
1328 4 : return call_lvm_and_report_error (args, extra, TRUE, error);
1329 : }
1330 :
1331 : /**
1332 : * bd_lvm_vgreduce:
1333 : * @vg_name: name of the to be reduced VG
1334 : * @device: (nullable): PV device the @vg_name VG should be reduced of or %NULL
1335 : * if the VG should be reduced of the missing PVs
1336 : * @extra: (nullable) (array zero-terminated=1): extra options for the VG reduction
1337 : * (just passed to LVM as is)
1338 : * @error: (out) (optional): place to store error (if any)
1339 : *
1340 : * Returns: whether the VG @vg_name was successfully reduced of the given @device or not
1341 : *
1342 : * Note: This function does not move extents off of the PV before removing
1343 : * it from the VG. You must do that first by calling #bd_lvm_pvmove.
1344 : *
1345 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1346 : */
1347 4 : gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
1348 4 : const gchar *args[5] = {"vgreduce", NULL, NULL, NULL, NULL};
1349 :
1350 4 : if (!device) {
1351 2 : args[1] = "--removemissing";
1352 2 : args[2] = "--force";
1353 2 : args[3] = vg_name;
1354 : } else {
1355 2 : args[1] = vg_name;
1356 2 : args[2] = device;
1357 : }
1358 :
1359 4 : return call_lvm_and_report_error (args, extra, TRUE, error);
1360 : }
1361 :
1362 : /**
1363 : * bd_lvm_add_vg_tags:
1364 : * @vg_name: the VG to set tags on
1365 : * @tags: (array zero-terminated=1): list of tags to add
1366 : * @error: (out) (optional): place to store error (if any)
1367 : *
1368 : * Returns: whether the tags were successfully added to @vg_name or not
1369 : *
1370 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1371 : */
1372 2 : gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
1373 2 : return _manage_lvm_tags (vg_name, tags, "--addtag", "vgchange", error);
1374 : }
1375 :
1376 : /**
1377 : * bd_lvm_delete_vg_tags:
1378 : * @vg_name: the VG to set tags on
1379 : * @tags: (array zero-terminated=1): list of tags to remove
1380 : * @error: (out) (optional): place to store error (if any)
1381 : *
1382 : * Returns: whether the tags were successfully removed from @vg_name or not
1383 : *
1384 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1385 : */
1386 1 : gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
1387 1 : return _manage_lvm_tags (vg_name, tags, "--deltag", "vgchange", error);
1388 : }
1389 :
1390 0 : static gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) {
1391 0 : const gchar *args[4] = {"vgchange", NULL, vg_name, NULL};
1392 :
1393 0 : if (start)
1394 0 : args[1] = "--lockstart";
1395 : else
1396 0 : args[1] = "--lockstop";
1397 :
1398 0 : return call_lvm_and_report_error (args, extra, TRUE, error);
1399 : }
1400 :
1401 : /**
1402 : * bd_lvm_vglock_start:
1403 : * @vg_name: a shared VG to start the lockspace in lvmlockd
1404 : * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
1405 : * (just passed to LVM as is)
1406 : * @error: (out) (optional): place to store error (if any)
1407 : *
1408 : * Returns: whether the lock was successfully started for @vg_name or not
1409 : *
1410 : * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY
1411 : */
1412 0 : gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
1413 0 : return _vglock_start_stop (vg_name, TRUE, extra, error);
1414 : }
1415 :
1416 : /**
1417 : * bd_lvm_vglock_stop:
1418 : * @vg_name: a shared VG to stop the lockspace in lvmlockd
1419 : * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
1420 : * (just passed to LVM as is)
1421 : * @error: (out) (optional): place to store error (if any)
1422 : *
1423 : * Returns: whether the lock was successfully stopped for @vg_name or not
1424 : *
1425 : * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY
1426 : */
1427 0 : gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
1428 0 : return _vglock_start_stop (vg_name, FALSE, extra, error);
1429 : }
1430 :
1431 : /**
1432 : * bd_lvm_vginfo:
1433 : * @vg_name: a VG to get information about
1434 : * @error: (out) (optional): place to store error (if any)
1435 : *
1436 : * Returns: (transfer full): information about the @vg_name VG or %NULL in case
1437 : * of error (the @error) gets populated in those cases)
1438 : *
1439 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1440 : */
1441 6 : BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error) {
1442 6 : const gchar *args[10] = {"vgs", "--noheadings", "--nosuffix", "--nameprefixes",
1443 : "--unquoted", "--units=b",
1444 : "-o", "name,uuid,size,free,extent_size,extent_count,free_count,pv_count,vg_exported,vg_tags",
1445 : vg_name, NULL};
1446 :
1447 6 : GHashTable *table = NULL;
1448 6 : gboolean success = FALSE;
1449 6 : gchar *output = NULL;
1450 6 : gchar **lines = NULL;
1451 6 : gchar **lines_p = NULL;
1452 : guint num_items;
1453 :
1454 6 : success = call_lvm_and_capture_output (args, NULL, &output, error);
1455 6 : if (!success)
1456 : /* the error is already populated from the call */
1457 0 : return NULL;
1458 :
1459 6 : lines = g_strsplit (output, "\n", 0);
1460 6 : g_free (output);
1461 :
1462 6 : for (lines_p = lines; *lines_p; lines_p++) {
1463 6 : table = parse_lvm_vars ((*lines_p), &num_items);
1464 6 : if (table && (num_items == 10)) {
1465 6 : g_strfreev (lines);
1466 6 : return get_vg_data_from_table (table, TRUE);
1467 : } else
1468 0 : if (table)
1469 0 : g_hash_table_destroy (table);
1470 : }
1471 0 : g_strfreev (lines);
1472 :
1473 : /* getting here means no usable info was found */
1474 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
1475 : "Failed to parse information about the VG");
1476 0 : return NULL;
1477 : }
1478 :
1479 : /**
1480 : * bd_lvm_vgs:
1481 : * @error: (out) (optional): place to store error (if any)
1482 : *
1483 : * Returns: (array zero-terminated=1): information about VGs found in the system
1484 : *
1485 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1486 : */
1487 2 : BDLVMVGdata** bd_lvm_vgs (GError **error) {
1488 2 : const gchar *args[9] = {"vgs", "--noheadings", "--nosuffix", "--nameprefixes",
1489 : "--unquoted", "--units=b",
1490 : "-o", "name,uuid,size,free,extent_size,extent_count,free_count,pv_count,vg_tags",
1491 : NULL};
1492 2 : GHashTable *table = NULL;
1493 2 : gboolean success = FALSE;
1494 2 : gchar *output = NULL;
1495 2 : gchar **lines = NULL;
1496 2 : gchar **lines_p = NULL;
1497 : guint num_items;
1498 : GPtrArray *vgs;
1499 2 : BDLVMVGdata *vgdata = NULL;
1500 2 : GError *l_error = NULL;
1501 :
1502 2 : vgs = g_ptr_array_new ();
1503 :
1504 2 : success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
1505 2 : if (!success) {
1506 1 : if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
1507 : /* no output => no VGs, not an error */
1508 1 : g_clear_error (&l_error);
1509 : /* return an empty list */
1510 1 : g_ptr_array_add (vgs, NULL);
1511 1 : return (BDLVMVGdata **) g_ptr_array_free (vgs, FALSE);
1512 : } else {
1513 : /* the error is already populated from the call */
1514 0 : g_ptr_array_free (vgs, TRUE);
1515 0 : g_propagate_error (error, l_error);
1516 0 : return NULL;
1517 : }
1518 : }
1519 :
1520 1 : lines = g_strsplit (output, "\n", 0);
1521 1 : g_free (output);
1522 :
1523 3 : for (lines_p = lines; *lines_p; lines_p++) {
1524 2 : table = parse_lvm_vars ((*lines_p), &num_items);
1525 2 : if (table && (num_items == 9)) {
1526 : /* valid line, try to parse and record it */
1527 1 : vgdata = get_vg_data_from_table (table, TRUE);
1528 1 : if (vgdata)
1529 1 : g_ptr_array_add (vgs, vgdata);
1530 : } else
1531 1 : if (table)
1532 1 : g_hash_table_destroy (table);
1533 : }
1534 :
1535 1 : g_strfreev (lines);
1536 :
1537 1 : if (vgs->len == 0) {
1538 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
1539 : "Failed to parse information about VGs");
1540 0 : g_ptr_array_free (vgs, TRUE);
1541 0 : return NULL;
1542 : }
1543 :
1544 : /* returning NULL-terminated array of BDLVMVGdata */
1545 1 : g_ptr_array_add (vgs, NULL);
1546 1 : return (BDLVMVGdata **) g_ptr_array_free (vgs, FALSE);
1547 : }
1548 :
1549 : /**
1550 : * bd_lvm_lvorigin:
1551 : * @vg_name: name of the VG containing the queried LV
1552 : * @lv_name: name of the queried LV
1553 : * @error: (out) (optional): place to store error (if any)
1554 : *
1555 : * Returns: (transfer full): the origin volume for the @vg_name/@lv_name LV or
1556 : * %NULL if failed to determine (@error) is set in those cases)
1557 : *
1558 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1559 : */
1560 1 : gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error) {
1561 1 : gboolean success = FALSE;
1562 1 : gchar *output = NULL;
1563 1 : const gchar *args[6] = {"lvs", "--noheadings", "-o", "origin", NULL, NULL};
1564 1 : args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1565 :
1566 1 : success = call_lvm_and_capture_output (args, NULL, &output, error);
1567 1 : g_free ((gchar *) args[4]);
1568 :
1569 1 : if (!success)
1570 : /* the error is already populated from the call */
1571 0 : return NULL;
1572 :
1573 1 : return g_strstrip (output);
1574 : }
1575 :
1576 : /**
1577 : * bd_lvm_lvcreate:
1578 : * @vg_name: name of the VG to create a new LV in
1579 : * @lv_name: name of the to-be-created LV
1580 : * @size: requested size of the new LV
1581 : * @type: (nullable): type of the new LV ("striped", "raid1",..., see lvcreate (8))
1582 : * @pv_list: (nullable) (array zero-terminated=1): list of PVs the newly created LV should use or %NULL
1583 : * if not specified
1584 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV creation
1585 : * (just passed to LVM as is)
1586 : * @error: (out) (optional): place to store error (if any)
1587 : *
1588 : * Returns: whether the given @vg_name/@lv_name LV was successfully created or not
1589 : *
1590 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
1591 : */
1592 47 : gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
1593 47 : guint8 pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
1594 47 : const gchar **args = g_new0 (const gchar*, pv_list_len + 10);
1595 47 : gboolean success = FALSE;
1596 47 : guint64 i = 0;
1597 47 : guint64 j = 0;
1598 47 : gchar *size_str = NULL;
1599 47 : gchar *type_str = NULL;
1600 :
1601 47 : args[i++] = "lvcreate";
1602 47 : args[i++] = "-n";
1603 47 : args[i++] = lv_name;
1604 47 : args[i++] = "-L";
1605 47 : size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
1606 47 : args[i++] = size_str;
1607 47 : args[i++] = "-y";
1608 47 : if (type) {
1609 3 : if (g_strcmp0 (type, "striped") == 0) {
1610 1 : args[i++] = "--stripes";
1611 1 : type_str = g_strdup_printf ("%d", pv_list_len);
1612 1 : args[i++] = type_str;
1613 : } else {
1614 2 : args[i++] = "--type";
1615 2 : args[i++] = type;
1616 : }
1617 : }
1618 47 : args[i++] = vg_name;
1619 :
1620 94 : for (j=0; j < pv_list_len; j++)
1621 47 : args[i++] = pv_list[j];
1622 :
1623 47 : args[i] = NULL;
1624 :
1625 47 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1626 47 : g_free (size_str);
1627 47 : g_free (type_str);
1628 47 : g_free (args);
1629 :
1630 47 : return success;
1631 : }
1632 :
1633 : /**
1634 : * bd_lvm_lvremove:
1635 : * @vg_name: name of the VG containing the to-be-removed LV
1636 : * @lv_name: name of the to-be-removed LV
1637 : * @force: whether to force removal or not
1638 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV removal
1639 : * (just passed to LVM as is)
1640 : * @error: (out) (optional): place to store error (if any)
1641 : *
1642 : * Returns: whether the @vg_name/@lv_name LV was successfully removed or not
1643 : *
1644 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
1645 : */
1646 54 : gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error) {
1647 : /* '--yes' is needed if DISCARD is enabled */
1648 54 : const gchar *args[5] = {"lvremove", "--yes", NULL, NULL, NULL};
1649 54 : guint8 next_arg = 2;
1650 54 : gboolean success = FALSE;
1651 :
1652 54 : if (force) {
1653 52 : args[next_arg] = "--force";
1654 52 : next_arg++;
1655 : }
1656 :
1657 54 : args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1658 :
1659 54 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1660 54 : g_free ((gchar *) args[next_arg]);
1661 :
1662 54 : return success;
1663 : }
1664 :
1665 : /**
1666 : * bd_lvm_lvrename:
1667 : * @vg_name: name of the VG containing the to-be-renamed LV
1668 : * @lv_name: name of the to-be-renamed LV
1669 : * @new_name: new name for the @vg_name/@lv_name LV
1670 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV rename
1671 : * (just passed to LVM as is)
1672 : * @error: (out) (optional): place to store error (if any)
1673 : *
1674 : * Returns: whether the @vg_name/@lv_name LV was successfully renamed to
1675 : * @vg_name/@new_name or not
1676 : *
1677 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1678 : */
1679 7 : gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error) {
1680 7 : const gchar *args[5] = {"lvrename", vg_name, lv_name, new_name, NULL};
1681 7 : return call_lvm_and_report_error (args, extra, TRUE, error);
1682 : }
1683 :
1684 :
1685 : /**
1686 : * bd_lvm_lvresize:
1687 : * @vg_name: name of the VG containing the to-be-resized LV
1688 : * @lv_name: name of the to-be-resized LV
1689 : * @size: the requested new size of the LV
1690 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV resize
1691 : * (just passed to LVM as is)
1692 : * @error: (out) (optional): place to store error (if any)
1693 : *
1694 : * Returns: whether the @vg_name/@lv_name LV was successfully resized or not
1695 : *
1696 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1697 : */
1698 10 : gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
1699 10 : const gchar *args[8] = {"lvresize", "--force", "-L", NULL, NULL, NULL, NULL, NULL};
1700 10 : gboolean success = FALSE;
1701 10 : guint8 next_arg = 4;
1702 20 : g_autofree gchar *lvspec = NULL;
1703 :
1704 10 : args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
1705 :
1706 : /* Starting with 2.03.19 we need to add an extra option to avoid
1707 : any filesystem related checks by lvresize.
1708 : */
1709 10 : success = bd_utils_check_util_version (deps[DEPS_LVM].name, LVM_VERSION_FSRESIZE,
1710 10 : deps[DEPS_LVM].ver_arg, deps[DEPS_LVM].ver_regexp, NULL);
1711 10 : if (success) {
1712 10 : args[next_arg++] = "--fs";
1713 10 : args[next_arg++] = "ignore";
1714 : }
1715 :
1716 10 : lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
1717 10 : args[next_arg++] = lvspec;
1718 :
1719 10 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1720 10 : g_free ((gchar *) args[3]);
1721 :
1722 10 : return success;
1723 : }
1724 :
1725 : /**
1726 : * bd_lvm_lvrepair:
1727 : * @vg_name: name of the VG containing the to-be-repaired LV
1728 : * @lv_name: name of the to-be-repaired LV
1729 : * @pv_list: (array zero-terminated=1): list of PVs to be used for the repair
1730 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV repair
1731 : * (just passed to LVM as is)
1732 : * @error: (out) (optional): place to store error (if any)
1733 : *
1734 : * Returns: whether the @vg_name/@lv_name LV was successfully repaired or not
1735 : *
1736 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1737 : */
1738 1 : gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
1739 1 : guint i = 0;
1740 1 : guint pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
1741 1 : const gchar **argv = g_new0 (const gchar*, pv_list_len + 5);
1742 1 : gboolean success = FALSE;
1743 :
1744 1 : argv[0] = "lvconvert";
1745 1 : argv[1] = "--repair";
1746 1 : argv[2] = "--yes";
1747 1 : argv[3] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1748 2 : for (i=4; i < (pv_list_len + 4); i++) {
1749 1 : argv[i] = pv_list[i-4];
1750 : }
1751 1 : argv[i] = NULL;
1752 :
1753 1 : success = call_lvm_and_report_error (argv, extra, TRUE, error);
1754 1 : g_free ((gchar *) argv[3]);
1755 1 : g_free (argv);
1756 :
1757 1 : return success;
1758 : }
1759 :
1760 : /**
1761 : * bd_lvm_lvactivate:
1762 : * @vg_name: name of the VG containing the to-be-activated LV
1763 : * @lv_name: name of the to-be-activated LV
1764 : * @ignore_skip: whether to ignore the skip flag or not
1765 : * @shared: whether to activate the LV in shared mode (used for shared LVM setups with lvmlockd,
1766 : * use %FALSE if not sure)
1767 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV activation
1768 : * (just passed to LVM as is)
1769 : * @error: (out) (optional): place to store error (if any)
1770 : *
1771 : * Returns: whether the @vg_name/@lv_name LV was successfully activated or not
1772 : *
1773 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1774 : */
1775 6 : gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) {
1776 6 : const gchar *args[5] = {"lvchange", NULL, NULL, NULL, NULL};
1777 6 : guint8 next_arg = 2;
1778 6 : gboolean success = FALSE;
1779 :
1780 6 : if (shared)
1781 1 : args[1] = "-asy";
1782 : else
1783 5 : args[1] = "-ay";
1784 :
1785 6 : if (ignore_skip) {
1786 6 : args[next_arg] = "-K";
1787 6 : next_arg++;
1788 : }
1789 6 : args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1790 :
1791 6 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1792 6 : g_free ((gchar *) args[next_arg]);
1793 :
1794 6 : return success;
1795 : }
1796 :
1797 : /**
1798 : * bd_lvm_lvdeactivate:
1799 : * @vg_name: name of the VG containing the to-be-deactivated LV
1800 : * @lv_name: name of the to-be-deactivated LV
1801 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV deactivation
1802 : * (just passed to LVM as is)
1803 : * @error: (out) (optional): place to store error (if any)
1804 : *
1805 : * Returns: whether the @vg_name/@lv_name LV was successfully deactivated or not
1806 : *
1807 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
1808 : */
1809 13 : gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error) {
1810 13 : const gchar *args[4] = {"lvchange", "-an", NULL, NULL};
1811 13 : gboolean success = FALSE;
1812 :
1813 13 : args[2] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1814 :
1815 13 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1816 13 : g_free ((gchar *) args[2]);
1817 :
1818 13 : return success;
1819 : }
1820 :
1821 : /**
1822 : * bd_lvm_lvsnapshotcreate:
1823 : * @vg_name: name of the VG containing the LV a new snapshot should be created of
1824 : * @origin_name: name of the LV a new snapshot should be created of
1825 : * @snapshot_name: name of the to-be-created snapshot
1826 : * @size: requested size for the snapshot
1827 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot creation
1828 : * (just passed to LVM as is)
1829 : * @error: (out) (optional): place to store error (if any)
1830 : *
1831 : * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name LV
1832 : * was successfully created or not.
1833 : *
1834 : * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_CREATE
1835 : */
1836 1 : gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error) {
1837 1 : const gchar *args[8] = {"lvcreate", "-s", "-L", NULL, "-n", snapshot_name, NULL, NULL};
1838 1 : gboolean success = FALSE;
1839 :
1840 1 : args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
1841 1 : args[6] = g_strdup_printf ("%s/%s", vg_name, origin_name);
1842 :
1843 1 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1844 1 : g_free ((gchar *) args[3]);
1845 1 : g_free ((gchar *) args[6]);
1846 :
1847 1 : return success;
1848 : }
1849 :
1850 : /**
1851 : * bd_lvm_lvsnapshotmerge:
1852 : * @vg_name: name of the VG containing the to-be-merged LV snapshot
1853 : * @snapshot_name: name of the to-be-merged LV snapshot
1854 : * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot merge
1855 : * (just passed to LVM as is)
1856 : * @error: (out) (optional): place to store error (if any)
1857 : *
1858 : * Returns: whether the @vg_name/@snapshot_name LV snapshot was successfully merged or not
1859 : *
1860 : * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_MODIFY
1861 : */
1862 1 : gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error) {
1863 1 : const gchar *args[4] = {"lvconvert", "--merge", NULL, NULL};
1864 1 : gboolean success = FALSE;
1865 :
1866 1 : args[2] = g_strdup_printf ("%s/%s", vg_name, snapshot_name);
1867 :
1868 1 : success = call_lvm_and_report_error (args, extra, TRUE, error);
1869 1 : g_free ((gchar *) args[2]);
1870 :
1871 1 : return success;
1872 : }
1873 :
1874 : /**
1875 : * bd_lvm_add_lv_tags:
1876 : * @vg_name: name of the VG that contains the LV to set tags on
1877 : * @lv_name: name of the LV to set tags on
1878 : * @tags: (array zero-terminated=1): list of tags to add
1879 : * @error: (out) (optional): place to store error (if any)
1880 : *
1881 : * Returns: whether the tags were successfully added to @device or not
1882 : *
1883 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1884 : */
1885 2 : gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
1886 4 : g_autofree gchar *lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
1887 2 : return _manage_lvm_tags (lvspec, tags, "--addtag", "lvchange", error);
1888 : }
1889 :
1890 : /**
1891 : * bd_lvm_delete_lv_tags:
1892 : * @vg_name: name of the VG that contains the LV to set tags on
1893 : * @lv_name: name of the LV to set tags on
1894 : * @tags: (array zero-terminated=1): list of tags to remove
1895 : * @error: (out) (optional): place to store error (if any)
1896 : *
1897 : * Returns: whether the tags were successfully removed from @device or not
1898 : *
1899 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1900 : */
1901 1 : gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
1902 2 : g_autofree gchar *lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
1903 1 : return _manage_lvm_tags (lvspec, tags, "--deltag", "lvchange", error);
1904 : }
1905 :
1906 : /**
1907 : * bd_lvm_lvinfo:
1908 : * @vg_name: name of the VG that contains the LV to get information about
1909 : * @lv_name: name of the LV to get information about
1910 : * @error: (out) (optional): place to store error (if any)
1911 : *
1912 : * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
1913 : * of error (the @error) gets populated in those cases)
1914 : *
1915 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
1916 : */
1917 39 : BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error) {
1918 39 : const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
1919 : "--unquoted", "--units=b", "-a",
1920 : "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags",
1921 : NULL, NULL};
1922 :
1923 39 : GHashTable *table = NULL;
1924 39 : gboolean success = FALSE;
1925 39 : gchar *output = NULL;
1926 39 : gchar **lines = NULL;
1927 39 : gchar **lines_p = NULL;
1928 : guint num_items;
1929 :
1930 39 : args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1931 :
1932 39 : success = call_lvm_and_capture_output (args, NULL, &output, error);
1933 39 : g_free ((gchar *) args[9]);
1934 :
1935 39 : if (!success)
1936 : /* the error is already populated from the call */
1937 0 : return NULL;
1938 :
1939 39 : lines = g_strsplit (output, "\n", 0);
1940 39 : g_free (output);
1941 :
1942 39 : for (lines_p = lines; *lines_p; lines_p++) {
1943 39 : table = parse_lvm_vars ((*lines_p), &num_items);
1944 39 : if (table && (num_items == 16)) {
1945 39 : g_strfreev (lines);
1946 39 : return get_lv_data_from_table (table, TRUE);
1947 : } else
1948 0 : if (table)
1949 0 : g_hash_table_destroy (table);
1950 : }
1951 0 : g_strfreev (lines);
1952 :
1953 : /* getting here means no usable info was found */
1954 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
1955 : "Failed to parse information about the LV");
1956 0 : return NULL;
1957 : }
1958 :
1959 2 : BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error) {
1960 2 : const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
1961 : "--unquoted", "--units=b", "-a",
1962 : "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags,devices,metadata_devices,seg_size_pe",
1963 : NULL, NULL};
1964 :
1965 2 : GHashTable *table = NULL;
1966 2 : gboolean success = FALSE;
1967 2 : gchar *output = NULL;
1968 2 : gchar **lines = NULL;
1969 2 : gchar **lines_p = NULL;
1970 : guint num_items;
1971 2 : BDLVMLVdata *result = NULL;
1972 :
1973 2 : args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
1974 :
1975 2 : success = call_lvm_and_capture_output (args, NULL, &output, error);
1976 2 : g_free ((gchar *) args[9]);
1977 :
1978 2 : if (!success)
1979 : /* the error is already populated from the call */
1980 0 : return NULL;
1981 :
1982 2 : lines = g_strsplit (output, "\n", 0);
1983 2 : g_free (output);
1984 :
1985 7 : for (lines_p = lines; *lines_p; lines_p++) {
1986 5 : table = parse_lvm_vars ((*lines_p), &num_items);
1987 8 : if (table && (num_items == 19)) {
1988 3 : BDLVMLVdata *lvdata = get_lv_data_from_table (table, TRUE);
1989 3 : if (result) {
1990 1 : merge_lv_data (result, lvdata);
1991 1 : bd_lvm_lvdata_free (lvdata);
1992 : } else
1993 2 : result = lvdata;
1994 : } else {
1995 2 : if (table)
1996 2 : g_hash_table_destroy (table);
1997 : }
1998 : }
1999 2 : g_strfreev (lines);
2000 :
2001 2 : if (result == NULL)
2002 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
2003 : "Failed to parse information about the LV");
2004 2 : return result;
2005 : }
2006 :
2007 : /**
2008 : * bd_lvm_lvs:
2009 : * @vg_name: (nullable): name of the VG to get information about LVs from
2010 : * @error: (out) (optional): place to store error (if any)
2011 : *
2012 : * Returns: (array zero-terminated=1): information about LVs found in the given
2013 : * @vg_name VG or in system if @vg_name is %NULL
2014 : *
2015 : * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
2016 : */
2017 14 : BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error) {
2018 14 : const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
2019 : "--unquoted", "--units=b", "-a",
2020 : "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags",
2021 : NULL, NULL};
2022 :
2023 14 : GHashTable *table = NULL;
2024 14 : gboolean success = FALSE;
2025 14 : gchar *output = NULL;
2026 14 : gchar **lines = NULL;
2027 14 : gchar **lines_p = NULL;
2028 : guint num_items;
2029 : GPtrArray *lvs;
2030 14 : BDLVMLVdata *lvdata = NULL;
2031 14 : GError *l_error = NULL;
2032 :
2033 14 : lvs = g_ptr_array_new ();
2034 :
2035 14 : if (vg_name)
2036 12 : args[9] = vg_name;
2037 :
2038 14 : success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
2039 14 : if (!success) {
2040 3 : if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
2041 : /* no output => no LVs, not an error */
2042 3 : g_clear_error (&l_error);
2043 : /* return an empty list */
2044 3 : g_ptr_array_add (lvs, NULL);
2045 3 : return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
2046 : }
2047 : else {
2048 : /* the error is already populated from the call */
2049 0 : g_ptr_array_free (lvs, TRUE);
2050 0 : g_propagate_error (error, l_error);
2051 0 : return NULL;
2052 : }
2053 : }
2054 :
2055 11 : lines = g_strsplit (output, "\n", 0);
2056 11 : g_free (output);
2057 :
2058 46 : for (lines_p = lines; *lines_p; lines_p++) {
2059 35 : table = parse_lvm_vars ((*lines_p), &num_items);
2060 35 : if (table && (num_items == 16)) {
2061 : /* valid line, try to parse and record it */
2062 24 : lvdata = get_lv_data_from_table (table, TRUE);
2063 24 : if (lvdata) {
2064 : /* ignore duplicate entries in lvs output, these are caused by multi segments LVs */
2065 46 : for (gsize i = 0; i < lvs->len; i++) {
2066 23 : if (g_strcmp0 (((BDLVMLVdata *) g_ptr_array_index (lvs, i))->lv_name, lvdata->lv_name) == 0) {
2067 1 : bd_utils_log_format (BD_UTILS_LOG_DEBUG,
2068 : "Duplicate LV entry for '%s' found in lvs output",
2069 : lvdata->lv_name);
2070 1 : bd_lvm_lvdata_free (lvdata);
2071 1 : lvdata = NULL;
2072 1 : break;
2073 : }
2074 : }
2075 :
2076 24 : if (lvdata)
2077 23 : g_ptr_array_add (lvs, lvdata);
2078 : }
2079 : } else
2080 11 : if (table)
2081 11 : g_hash_table_destroy (table);
2082 : }
2083 :
2084 11 : g_strfreev (lines);
2085 :
2086 11 : if (lvs->len == 0) {
2087 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
2088 : "Failed to parse information about LVs");
2089 0 : g_ptr_array_free (lvs, TRUE);
2090 0 : return NULL;
2091 : }
2092 :
2093 : /* returning NULL-terminated array of BDLVMLVdata */
2094 11 : g_ptr_array_add (lvs, NULL);
2095 11 : return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
2096 : }
2097 :
2098 4 : BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error) {
2099 4 : const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
2100 : "--unquoted", "--units=b", "-a",
2101 : "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags,devices,metadata_devices,seg_size_pe",
2102 : NULL, NULL};
2103 :
2104 4 : GHashTable *table = NULL;
2105 4 : gboolean success = FALSE;
2106 4 : gchar *output = NULL;
2107 4 : gchar **lines = NULL;
2108 4 : gchar **lines_p = NULL;
2109 : guint num_items;
2110 : GPtrArray *lvs;
2111 4 : BDLVMLVdata *lvdata = NULL;
2112 4 : GError *l_error = NULL;
2113 :
2114 4 : lvs = g_ptr_array_new ();
2115 :
2116 4 : if (vg_name)
2117 4 : args[9] = vg_name;
2118 :
2119 4 : success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
2120 4 : if (!success) {
2121 0 : if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
2122 : /* no output => no LVs, not an error */
2123 0 : g_clear_error (&l_error);
2124 : /* return an empty list */
2125 0 : g_ptr_array_add (lvs, NULL);
2126 0 : return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
2127 : }
2128 : else {
2129 : /* the error is already populated from the call */
2130 0 : g_ptr_array_free (lvs, TRUE);
2131 0 : g_propagate_error (error, l_error);
2132 0 : return NULL;
2133 : }
2134 : }
2135 :
2136 4 : lines = g_strsplit (output, "\n", 0);
2137 4 : g_free (output);
2138 :
2139 28 : for (lines_p = lines; *lines_p; lines_p++) {
2140 24 : table = parse_lvm_vars ((*lines_p), &num_items);
2141 24 : if (table && (num_items == 19)) {
2142 : /* valid line, try to parse and record it */
2143 20 : lvdata = get_lv_data_from_table (table, TRUE);
2144 20 : if (lvdata) {
2145 60 : for (gsize i = 0; i < lvs->len; i++) {
2146 40 : BDLVMLVdata *other = (BDLVMLVdata *) g_ptr_array_index (lvs, i);
2147 40 : if (g_strcmp0 (other->lv_name, lvdata->lv_name) == 0) {
2148 0 : merge_lv_data (other, lvdata);
2149 0 : bd_lvm_lvdata_free (lvdata);
2150 0 : lvdata = NULL;
2151 0 : break;
2152 : }
2153 : }
2154 :
2155 20 : if (lvdata)
2156 20 : g_ptr_array_add (lvs, lvdata);
2157 : }
2158 : } else
2159 4 : if (table)
2160 4 : g_hash_table_destroy (table);
2161 : }
2162 :
2163 4 : g_strfreev (lines);
2164 :
2165 4 : if (lvs->len == 0) {
2166 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
2167 : "Failed to parse information about LVs");
2168 0 : g_ptr_array_free (lvs, TRUE);
2169 0 : return NULL;
2170 : }
2171 :
2172 : /* returning NULL-terminated array of BDLVMLVdata */
2173 4 : g_ptr_array_add (lvs, NULL);
2174 4 : return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
2175 : }
2176 :
2177 : /**
2178 : * bd_lvm_thpoolcreate:
2179 : * @vg_name: name of the VG to create a thin pool in
2180 : * @lv_name: name of the to-be-created pool LV
2181 : * @size: requested size of the to-be-created pool
2182 : * @md_size: requested metadata size or 0 to use the default
2183 : * @chunk_size: requested chunk size or 0 to use the default
2184 : * @profile: (nullable): profile to use (see lvm(8) for more information) or %NULL to use
2185 : * the default
2186 : * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
2187 : * (just passed to LVM as is)
2188 : * @error: (out) (optional): place to store error (if any)
2189 : *
2190 : * Returns: whether the @vg_name/@lv_name thin pool was successfully created or not
2191 : *
2192 : * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
2193 : */
2194 6 : gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error) {
2195 6 : const gchar *args[9] = {"lvcreate", "-T", "-L", NULL, NULL, NULL, NULL, NULL, NULL};
2196 6 : guint8 next_arg = 4;
2197 6 : gboolean success = FALSE;
2198 :
2199 6 : args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
2200 :
2201 6 : if (md_size != 0) {
2202 6 : args[next_arg] = g_strdup_printf ("--poolmetadatasize=%"G_GUINT64_FORMAT"K", md_size / 1024);
2203 6 : next_arg++;
2204 : }
2205 :
2206 6 : if (chunk_size != 0) {
2207 6 : args[next_arg] = g_strdup_printf ("--chunksize=%"G_GUINT64_FORMAT"K", chunk_size / 1024);
2208 6 : next_arg++;
2209 : }
2210 :
2211 6 : if (profile) {
2212 4 : args[next_arg] = g_strdup_printf ("--profile=%s", profile);
2213 4 : next_arg++;
2214 : }
2215 :
2216 6 : args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
2217 :
2218 6 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2219 6 : g_free ((gchar *) args[3]);
2220 6 : g_free ((gchar *) args[4]);
2221 6 : g_free ((gchar *) args[5]);
2222 6 : g_free ((gchar *) args[6]);
2223 6 : g_free ((gchar *) args[7]);
2224 :
2225 6 : return success;
2226 : }
2227 :
2228 : /**
2229 : * bd_lvm_thlvcreate:
2230 : * @vg_name: name of the VG containing the thin pool providing extents for the to-be-created thin LV
2231 : * @pool_name: name of the pool LV providing extents for the to-be-created thin LV
2232 : * @lv_name: name of the to-be-created thin LV
2233 : * @size: requested virtual size of the to-be-created thin LV
2234 : * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV creation
2235 : * (just passed to LVM as is)
2236 : * @error: (out) (optional): place to store error (if any)
2237 : *
2238 : * Returns: whether the @vg_name/@lv_name thin LV was successfully created or not
2239 : *
2240 : * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
2241 : */
2242 2 : gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
2243 2 : const gchar *args[8] = {"lvcreate", "-T", NULL, "-V", NULL, "-n", lv_name, NULL};
2244 : gboolean success;
2245 :
2246 2 : args[2] = g_strdup_printf ("%s/%s", vg_name, pool_name);
2247 2 : args[4] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
2248 :
2249 2 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2250 2 : g_free ((gchar *) args[2]);
2251 2 : g_free ((gchar *) args[4]);
2252 :
2253 2 : return success;
2254 : }
2255 :
2256 : /**
2257 : * bd_lvm_thlvpoolname:
2258 : * @vg_name: name of the VG containing the queried thin LV
2259 : * @lv_name: name of the queried thin LV
2260 : * @error: (out) (optional): place to store error (if any)
2261 : *
2262 : * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
2263 : * thin LV or %NULL if failed to determine (@error) is set in those cases)
2264 : *
2265 : * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_QUERY
2266 : */
2267 2 : gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
2268 2 : gboolean success = FALSE;
2269 2 : gchar *output = NULL;
2270 2 : const gchar *args[6] = {"lvs", "--noheadings", "-o", "pool_lv", NULL, NULL};
2271 2 : args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
2272 :
2273 2 : success = call_lvm_and_capture_output (args, NULL, &output, error);
2274 2 : g_free ((gchar *) args[4]);
2275 :
2276 2 : if (!success)
2277 : /* the error is already populated from the call */
2278 0 : return NULL;
2279 :
2280 2 : return g_strstrip (output);
2281 : }
2282 :
2283 : /**
2284 : * bd_lvm_thsnapshotcreate:
2285 : * @vg_name: name of the VG containing the thin LV a new snapshot should be created of
2286 : * @origin_name: name of the thin LV a new snapshot should be created of
2287 : * @snapshot_name: name of the to-be-created snapshot
2288 : * @pool_name: (nullable): name of the thin pool to create the snapshot in or %NULL if not specified
2289 : * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV snapshot creation
2290 : * (just passed to LVM as is)
2291 : * @error: (out) (optional): place to store error (if any)
2292 : *
2293 : * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name
2294 : * thin LV was successfully created or not.
2295 : *
2296 : * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
2297 : */
2298 1 : gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
2299 1 : const gchar *args[8] = {"lvcreate", "-s", "-n", snapshot_name, NULL, NULL, NULL, NULL};
2300 1 : guint next_arg = 4;
2301 1 : gboolean success = FALSE;
2302 :
2303 1 : if (pool_name) {
2304 1 : args[next_arg] = "--thinpool";
2305 1 : next_arg++;
2306 1 : args[next_arg] = pool_name;
2307 1 : next_arg++;
2308 : }
2309 :
2310 1 : args[next_arg] = g_strdup_printf ("%s/%s", vg_name, origin_name);
2311 :
2312 1 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2313 1 : g_free ((gchar *) args[next_arg]);
2314 :
2315 1 : return success;
2316 : }
2317 :
2318 : /**
2319 : * get_lv_type_from_flags: (skip)
2320 : * @meta: getting type for a (future) metadata LV
2321 : *
2322 : * Get LV type string from flags.
2323 : */
2324 16 : static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error G_GNUC_UNUSED) {
2325 16 : if (!meta) {
2326 8 : if (flags & BD_LVM_CACHE_POOL_STRIPED)
2327 1 : return "striped";
2328 7 : else if (flags & BD_LVM_CACHE_POOL_RAID1)
2329 0 : return "raid1";
2330 7 : else if (flags & BD_LVM_CACHE_POOL_RAID5)
2331 0 : return "raid5";
2332 7 : else if (flags & BD_LVM_CACHE_POOL_RAID6)
2333 0 : return "raid6";
2334 7 : else if (flags & BD_LVM_CACHE_POOL_RAID10)
2335 0 : return "raid10";
2336 : else
2337 7 : return NULL;
2338 : } else {
2339 8 : if (flags & BD_LVM_CACHE_POOL_META_STRIPED)
2340 0 : return "striped";
2341 8 : else if (flags & BD_LVM_CACHE_POOL_META_RAID1)
2342 1 : return "raid1";
2343 7 : else if (flags & BD_LVM_CACHE_POOL_META_RAID5)
2344 0 : return "raid5";
2345 7 : else if (flags & BD_LVM_CACHE_POOL_META_RAID6)
2346 0 : return "raid6";
2347 7 : else if (flags & BD_LVM_CACHE_POOL_META_RAID10)
2348 0 : return "raid10";
2349 : else
2350 7 : return NULL;
2351 : }
2352 : }
2353 :
2354 : /**
2355 : * bd_lvm_cache_create_pool:
2356 : * @vg_name: name of the VG to create @pool_name in
2357 : * @pool_name: name of the cache pool LV to create
2358 : * @pool_size: desired size of the cache pool @pool_name
2359 : * @md_size: desired size of the @pool_name cache pool's metadata LV or 0 to
2360 : * use the default
2361 : * @mode: cache mode of the @pool_name cache pool
2362 : * @flags: a combination of (ORed) #BDLVMCachePoolFlags
2363 : * @fast_pvs: (array zero-terminated=1): list of (fast) PVs to create the @pool_name
2364 : * cache pool (and the metadata LV)
2365 : * @error: (out) (optional): place to store error (if any)
2366 : *
2367 : * Returns: whether the cache pool @vg_name/@pool_name was successfully created or not
2368 : *
2369 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
2370 : */
2371 8 : gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error) {
2372 8 : gboolean success = FALSE;
2373 8 : const gchar *type = NULL;
2374 8 : gchar *name = NULL;
2375 8 : gchar *msg = NULL;
2376 8 : guint64 progress_id = 0;
2377 8 : const gchar *args[10] = {"lvconvert", "-y", "--type", "cache-pool", "--poolmetadata", NULL, "--cachemode", NULL, NULL, NULL};
2378 8 : GError *l_error = NULL;
2379 :
2380 8 : msg = g_strdup_printf ("Started 'create cache pool %s/%s'", vg_name, pool_name);
2381 8 : progress_id = bd_utils_report_started (msg);
2382 8 : g_free (msg);
2383 :
2384 : /* create an LV for the pool */
2385 8 : type = get_lv_type_from_flags (flags, FALSE, NULL);
2386 8 : success = bd_lvm_lvcreate (vg_name, pool_name, pool_size, type, fast_pvs, NULL, &l_error);
2387 8 : if (!success) {
2388 0 : g_prefix_error (&l_error, "Failed to create the pool LV: ");
2389 0 : bd_utils_report_finished (progress_id, l_error->message);
2390 0 : g_propagate_error (error, l_error);
2391 0 : return FALSE;
2392 : }
2393 :
2394 : /* 1/3 steps done */
2395 8 : bd_utils_report_progress (progress_id, 33, "Created the data LV");
2396 :
2397 : /* determine the size of the metadata LV */
2398 8 : type = get_lv_type_from_flags (flags, TRUE, NULL);
2399 8 : if (md_size == 0)
2400 7 : md_size = bd_lvm_cache_get_default_md_size (pool_size, &l_error);
2401 8 : if (l_error) {
2402 0 : g_prefix_error (&l_error, "Failed to determine size for the pool metadata LV: ");
2403 0 : bd_utils_report_finished (progress_id, l_error->message);
2404 0 : g_propagate_error (error, l_error);
2405 0 : return FALSE;
2406 : }
2407 8 : name = g_strdup_printf ("%s_meta", pool_name);
2408 :
2409 : /* create the metadata LV */
2410 8 : success = bd_lvm_lvcreate (vg_name, name, md_size, type, fast_pvs, NULL, &l_error);
2411 8 : if (!success) {
2412 0 : g_free (name);
2413 0 : g_prefix_error (&l_error, "Failed to create the pool metadata LV: ");
2414 0 : bd_utils_report_finished (progress_id, l_error->message);
2415 0 : g_propagate_error (error, l_error);
2416 0 : return FALSE;
2417 : }
2418 :
2419 : /* 2/3 steps done */
2420 8 : bd_utils_report_progress (progress_id, 66, "Created the metadata LV");
2421 :
2422 : /* create the cache pool from the two LVs */
2423 8 : args[5] = name;
2424 8 : args[7] = (const gchar *) bd_lvm_cache_get_mode_str (mode, &l_error);
2425 8 : if (!args[7]) {
2426 0 : g_free ((gchar *) args[5]);
2427 0 : bd_utils_report_finished (progress_id, l_error->message);
2428 0 : g_propagate_error (error, l_error);
2429 0 : return FALSE;
2430 : }
2431 8 : name = g_strdup_printf ("%s/%s", vg_name, pool_name);
2432 8 : args[8] = name;
2433 8 : success = call_lvm_and_report_error (args, NULL, TRUE, &l_error);
2434 8 : g_free ((gchar *) args[5]);
2435 8 : g_free ((gchar *) args[8]);
2436 :
2437 8 : if (!success) {
2438 0 : if (l_error)
2439 0 : bd_utils_report_finished (progress_id, l_error->message);
2440 : else
2441 0 : bd_utils_report_finished (progress_id, "Completed");
2442 0 : g_propagate_error (error, l_error);
2443 : } else
2444 8 : bd_utils_report_finished (progress_id, "Completed");
2445 :
2446 : /* just return the result of the last step (it sets error on fail) */
2447 8 : return success;
2448 : }
2449 :
2450 : /**
2451 : * bd_lvm_cache_attach:
2452 : * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
2453 : * @data_lv: data LV to attach the @cache_pool_lv to
2454 : * @cache_pool_lv: cache pool LV to attach to the @data_lv
2455 : * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
2456 : * (just passed to LVM as is)
2457 : * @error: (out) (optional): place to store error (if any)
2458 : *
2459 : * Returns: whether the @cache_pool_lv was successfully attached to the @data_lv or not
2460 : *
2461 : * Note: Both @data_lv and @cache_lv will be deactivated before the operation.
2462 : *
2463 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
2464 : */
2465 6 : gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error) {
2466 6 : const gchar *args[8] = {"lvconvert", "-y", "--type", "cache", "--cachepool", NULL, NULL, NULL};
2467 6 : gboolean success = FALSE;
2468 :
2469 6 : args[5] = g_strdup_printf ("%s/%s", vg_name, cache_pool_lv);
2470 6 : args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
2471 6 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2472 :
2473 6 : g_free ((gchar *) args[5]);
2474 6 : g_free ((gchar *) args[6]);
2475 6 : return success;
2476 : }
2477 :
2478 : /**
2479 : * bd_lvm_cache_detach:
2480 : * @vg_name: name of the VG containing the @cached_lv
2481 : * @cached_lv: name of the cached LV to detach its cache from
2482 : * @destroy: whether to destroy the cache after detach or not
2483 : * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
2484 : * (just passed to LVM as is)
2485 : * @error: (out) (optional): place to store error (if any)
2486 : *
2487 : * Returns: whether the cache was successfully detached from the @cached_lv or not
2488 : *
2489 : * Note: synces the cache first
2490 : *
2491 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
2492 : */
2493 4 : gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
2494 : /* need to both "assume yes" and "force" to get rid of the interactive
2495 : questions in case of "--uncache" */
2496 4 : const gchar *args[6] = {"lvconvert", "-y", "-f", NULL, NULL, NULL};
2497 4 : gboolean success = FALSE;
2498 :
2499 4 : args[3] = destroy ? "--uncache" : "--splitcache";
2500 4 : args[4] = g_strdup_printf ("%s/%s", vg_name, cached_lv);
2501 4 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2502 :
2503 4 : g_free ((gchar *) args[4]);
2504 4 : return success;
2505 : }
2506 :
2507 : /**
2508 : * bd_lvm_cache_create_cached_lv:
2509 : * @vg_name: name of the VG to create a cached LV in
2510 : * @lv_name: name of the cached LV to create
2511 : * @data_size: size of the data LV
2512 : * @cache_size: size of the cache (or cached LV more precisely)
2513 : * @md_size: size of the cache metadata LV or 0 to use the default
2514 : * @mode: cache mode for the cached LV
2515 : * @flags: a combination of (ORed) #BDLVMCachePoolFlags
2516 : * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
2517 : * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
2518 : * @error: (out) (optional): place to store error (if any)
2519 : *
2520 : * Returns: whether the cached LV @lv_name was successfully created or not
2521 : *
2522 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
2523 : */
2524 1 : gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
2525 : const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
2526 1 : gboolean success = FALSE;
2527 1 : gchar *name = NULL;
2528 1 : gchar *msg = NULL;
2529 1 : guint64 progress_id = 0;
2530 1 : GError *l_error = NULL;
2531 :
2532 1 : msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
2533 1 : progress_id = bd_utils_report_started (msg);
2534 1 : g_free (msg);
2535 :
2536 1 : name = g_strdup_printf ("%s_cache", lv_name);
2537 1 : success = bd_lvm_cache_create_pool (vg_name, name, cache_size, md_size, mode, flags, fast_pvs, &l_error);
2538 1 : if (!success) {
2539 0 : g_prefix_error (&l_error, "Failed to create the cache pool '%s': ", name);
2540 0 : g_free (name);
2541 0 : bd_utils_report_finished (progress_id, l_error->message);
2542 0 : g_propagate_error (error, l_error);
2543 0 : return FALSE;
2544 : }
2545 :
2546 : /* 3/5 steps (cache pool creation has 3 steps) done */
2547 1 : bd_utils_report_progress (progress_id, 60, "Cache pool created");
2548 :
2549 1 : success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
2550 1 : if (!success) {
2551 0 : g_free (name);
2552 0 : g_prefix_error (&l_error, "Failed to create the data LV: ");
2553 0 : bd_utils_report_finished (progress_id, l_error->message);
2554 0 : g_propagate_error (error, l_error);
2555 0 : return FALSE;
2556 : }
2557 :
2558 : /* 4/5 steps (cache pool creation has 3 steps) done */
2559 1 : bd_utils_report_progress (progress_id, 80, "Data LV created");
2560 :
2561 1 : success = bd_lvm_cache_attach (vg_name, lv_name, name, NULL, &l_error);
2562 1 : if (!success) {
2563 0 : g_prefix_error (error, "Failed to attach the cache pool '%s' to the data LV: ", name);
2564 0 : g_free (name);
2565 0 : bd_utils_report_finished (progress_id, l_error->message);
2566 0 : g_propagate_error (error, l_error);
2567 0 : return FALSE;
2568 : }
2569 :
2570 1 : bd_utils_report_finished (progress_id, "Completed");
2571 1 : g_free (name);
2572 1 : return TRUE;
2573 : }
2574 :
2575 : /**
2576 : * bd_lvm_writecache_attach:
2577 : * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
2578 : * @data_lv: data LV to attach the @cache_lv to
2579 : * @cache_lv: cache (fast) LV to attach to the @data_lv
2580 : * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
2581 : * (just passed to LVM as is)
2582 : * @error: (out) (optional): place to store error (if any)
2583 : *
2584 : * Returns: whether the @cache_lv was successfully attached to the @data_lv or not
2585 : *
2586 : * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
2587 : */
2588 3 : gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error) {
2589 3 : const gchar *args[8] = {"lvconvert", "-y", "--type", "writecache", "--cachevol", NULL, NULL, NULL};
2590 3 : gboolean success = FALSE;
2591 :
2592 : /* both LVs need to be inactive for the writecache convert to work */
2593 3 : success = bd_lvm_lvdeactivate (vg_name, data_lv, NULL, error);
2594 3 : if (!success)
2595 0 : return FALSE;
2596 :
2597 3 : success = bd_lvm_lvdeactivate (vg_name, cache_lv, NULL, error);
2598 3 : if (!success)
2599 0 : return FALSE;
2600 :
2601 3 : args[5] = g_strdup_printf ("%s/%s", vg_name, cache_lv);
2602 3 : args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
2603 3 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2604 :
2605 3 : g_free ((gchar *) args[5]);
2606 3 : g_free ((gchar *) args[6]);
2607 3 : return success;
2608 : }
2609 :
2610 : /**
2611 : * bd_lvm_writecache_detach:
2612 : * @vg_name: name of the VG containing the @cached_lv
2613 : * @cached_lv: name of the cached LV to detach its cache from
2614 : * @destroy: whether to destroy the cache after detach or not
2615 : * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
2616 : * (just passed to LVM as is)
2617 : * @error: (out) (optional): place to store error (if any)
2618 : *
2619 : * Returns: whether the cache was successfully detached from the @cached_lv or not
2620 : *
2621 : * Note: synces the cache first
2622 : *
2623 : * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
2624 : */
2625 2 : gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
2626 2 : return bd_lvm_cache_detach (vg_name, cached_lv, destroy, extra, error);
2627 : }
2628 :
2629 : /**
2630 : * bd_lvm_writecache_create_cached_lv:
2631 : * @vg_name: name of the VG to create a cached LV in
2632 : * @lv_name: name of the cached LV to create
2633 : * @data_size: size of the data LV
2634 : * @cache_size: size of the cache (or cached LV more precisely)
2635 : * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
2636 : * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
2637 : * @error: (out) (optional): place to store error (if any)
2638 : *
2639 : * Returns: whether the cached LV @lv_name was successfully created or not
2640 : *
2641 : * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_CREATE
2642 : */
2643 1 : gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size,
2644 : const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
2645 1 : gboolean success = FALSE;
2646 1 : gchar *name = NULL;
2647 1 : gchar *msg = NULL;
2648 1 : guint64 progress_id = 0;
2649 1 : GError *l_error = NULL;
2650 :
2651 1 : msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
2652 1 : progress_id = bd_utils_report_started (msg);
2653 1 : g_free (msg);
2654 :
2655 1 : name = g_strdup_printf ("%s_writecache", lv_name);
2656 1 : success = bd_lvm_lvcreate (vg_name, name, cache_size, NULL, fast_pvs, NULL, &l_error);
2657 1 : if (!success) {
2658 0 : g_prefix_error (&l_error, "Failed to create the cache LV '%s': ", name);
2659 0 : g_free (name);
2660 0 : bd_utils_report_finished (progress_id, l_error->message);
2661 0 : g_propagate_error (error, l_error);
2662 0 : return FALSE;
2663 : }
2664 :
2665 : /* 1/3 steps done */
2666 1 : bd_utils_report_progress (progress_id, 33, "Cache LV created");
2667 :
2668 1 : success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
2669 1 : if (!success) {
2670 0 : g_free (name);
2671 0 : g_prefix_error (&l_error, "Failed to create the data LV: ");
2672 0 : bd_utils_report_finished (progress_id, l_error->message);
2673 0 : g_propagate_error (error, l_error);
2674 0 : return FALSE;
2675 : }
2676 :
2677 : /* 2/3 steps done */
2678 1 : bd_utils_report_progress (progress_id, 66, "Data LV created");
2679 :
2680 1 : success = bd_lvm_writecache_attach (vg_name, lv_name, name, NULL, &l_error);
2681 1 : if (!success) {
2682 0 : g_prefix_error (&l_error, "Failed to attach the cache LV '%s' to the data LV: ", name);
2683 0 : g_free (name);
2684 0 : bd_utils_report_finished (progress_id, l_error->message);
2685 0 : g_propagate_error (error, l_error);
2686 0 : return FALSE;
2687 : }
2688 :
2689 1 : bd_utils_report_finished (progress_id, "Completed");
2690 1 : g_free (name);
2691 1 : return TRUE;
2692 : }
2693 :
2694 : /**
2695 : * bd_lvm_cache_pool_name:
2696 : * @vg_name: name of the VG containing the @cached_lv
2697 : * @cached_lv: cached LV to get the name of the its pool LV for
2698 : * @error: (out) (optional): place to store error (if any)
2699 : *
2700 : * Returns: name of the cache pool LV used by the @cached_lv or %NULL in case of error
2701 : *
2702 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
2703 : */
2704 1 : gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error) {
2705 1 : gchar *ret = NULL;
2706 1 : gchar *name_start = NULL;
2707 1 : gchar *name_end = NULL;
2708 1 : gchar *pool_name = NULL;
2709 :
2710 : /* same as for a thin LV, but with square brackets */
2711 1 : ret = bd_lvm_thlvpoolname (vg_name, cached_lv, error);
2712 1 : if (!ret)
2713 0 : return NULL;
2714 :
2715 1 : name_start = strchr (ret, '[');
2716 1 : if (!name_start) {
2717 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
2718 : "Failed to determine cache pool name from: '%s'", ret);
2719 0 : g_free (ret);
2720 0 : return NULL;
2721 : }
2722 1 : name_start++;
2723 :
2724 1 : name_end = strchr (ret, ']');
2725 1 : if (!name_end) {
2726 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
2727 : "Failed to determine cache pool name from: '%s'", ret);
2728 0 : g_free (ret);
2729 0 : return NULL;
2730 : }
2731 :
2732 1 : pool_name = g_strndup (name_start, name_end - name_start);
2733 1 : g_free (ret);
2734 :
2735 1 : return pool_name;
2736 : }
2737 :
2738 : /**
2739 : * bd_lvm_cache_stats:
2740 : * @vg_name: name of the VG containing the @cached_lv
2741 : * @cached_lv: cached LV to get stats for
2742 : * @error: (out) (optional): place to store error (if any)
2743 : *
2744 : * Returns: stats for the @cached_lv or %NULL in case of error
2745 : *
2746 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
2747 : */
2748 3 : BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
2749 3 : struct dm_pool *pool = NULL;
2750 3 : struct dm_task *task = NULL;
2751 : struct dm_info info;
2752 3 : struct dm_status_cache *status = NULL;
2753 3 : gchar *map_name = NULL;
2754 3 : guint64 start = 0;
2755 3 : guint64 length = 0;
2756 3 : gchar *type = NULL;
2757 3 : gchar *params = NULL;
2758 3 : BDLVMCacheStats *ret = NULL;
2759 3 : BDLVMLVdata *lvdata = NULL;
2760 :
2761 3 : if (geteuid () != 0) {
2762 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_ROOT,
2763 : "Not running as root, cannot query DM maps");
2764 0 : return NULL;
2765 : }
2766 :
2767 3 : lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
2768 3 : if (!lvdata)
2769 0 : return NULL;
2770 :
2771 3 : pool = dm_pool_create ("bd-pool", 20);
2772 :
2773 3 : if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
2774 1 : map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
2775 : else
2776 : /* translate the VG+LV name into the DM map name */
2777 2 : map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
2778 :
2779 3 : bd_lvm_lvdata_free (lvdata);
2780 :
2781 3 : task = dm_task_create (DM_DEVICE_STATUS);
2782 3 : if (!task) {
2783 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
2784 : "Failed to create DM task for the cache map '%s': ", map_name);
2785 0 : dm_pool_destroy (pool);
2786 0 : return NULL;
2787 : }
2788 :
2789 3 : if (dm_task_set_name (task, map_name) == 0) {
2790 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
2791 : "Failed to create DM task for the cache map '%s': ", map_name);
2792 0 : dm_task_destroy (task);
2793 0 : dm_pool_destroy (pool);
2794 0 : return NULL;
2795 : }
2796 :
2797 3 : if (dm_task_run (task) == 0) {
2798 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
2799 : "Failed to run the DM task for the cache map '%s': ", map_name);
2800 0 : dm_task_destroy (task);
2801 0 : dm_pool_destroy (pool);
2802 0 : return NULL;
2803 : }
2804 :
2805 3 : if (dm_task_get_info (task, &info) == 0) {
2806 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
2807 : "Failed to get task info for the cache map '%s': ", map_name);
2808 0 : dm_task_destroy (task);
2809 0 : dm_pool_destroy (pool);
2810 0 : return NULL;
2811 : }
2812 :
2813 3 : if (!info.exists) {
2814 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
2815 : "The cache map '%s' doesn't exist: ", map_name);
2816 0 : dm_task_destroy (task);
2817 0 : dm_pool_destroy (pool);
2818 0 : return NULL;
2819 : }
2820 :
2821 3 : dm_get_next_target (task, NULL, &start, &length, &type, ¶ms);
2822 :
2823 3 : if (dm_get_status_cache (pool, params, &status) == 0) {
2824 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
2825 : "Failed to get status of the cache map '%s': ", map_name);
2826 0 : dm_task_destroy (task);
2827 0 : dm_pool_destroy (pool);
2828 0 : return NULL;
2829 : }
2830 :
2831 3 : ret = g_new0 (BDLVMCacheStats, 1);
2832 3 : ret->block_size = status->block_size * SECTOR_SIZE;
2833 3 : ret->cache_size = status->total_blocks * ret->block_size;
2834 3 : ret->cache_used = status->used_blocks * ret->block_size;
2835 :
2836 3 : ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
2837 3 : ret->md_size = status->metadata_total_blocks * ret->md_block_size;
2838 3 : ret->md_used = status->metadata_used_blocks * ret->md_block_size;
2839 :
2840 3 : ret->read_hits = status->read_hits;
2841 3 : ret->read_misses = status->read_misses;
2842 3 : ret->write_hits = status->write_hits;
2843 3 : ret->write_misses = status->write_misses;
2844 :
2845 3 : if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
2846 3 : ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
2847 0 : else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
2848 0 : ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
2849 : else {
2850 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
2851 : "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
2852 0 : status->feature_flags);
2853 0 : dm_task_destroy (task);
2854 0 : dm_pool_destroy (pool);
2855 0 : bd_lvm_cache_stats_free (ret);
2856 0 : return NULL;
2857 : }
2858 :
2859 3 : dm_task_destroy (task);
2860 3 : dm_pool_destroy (pool);
2861 :
2862 3 : return ret;
2863 : }
2864 :
2865 : /**
2866 : * bd_lvm_thpool_convert:
2867 : * @vg_name: name of the VG to create the new thin pool in
2868 : * @data_lv: name of the LV that should become the data part of the new pool
2869 : * @metadata_lv: name of the LV that should become the metadata part of the new pool
2870 : * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
2871 : * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
2872 : * (just passed to LVM as is)
2873 : * @error: (out) (optional): place to store error (if any)
2874 : *
2875 : * Converts the @data_lv and @metadata_lv into a new thin pool in the @vg_name
2876 : * VG.
2877 : *
2878 : * Returns: whether the new thin pool was successfully created from @data_lv and
2879 : * @metadata_lv or not
2880 : *
2881 : * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
2882 : */
2883 1 : gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
2884 1 : const gchar *args[8] = {"lvconvert", "--yes", "--type", "thin-pool", "--poolmetadata", metadata_lv, NULL, NULL};
2885 1 : gboolean success = FALSE;
2886 :
2887 1 : args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
2888 :
2889 1 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2890 1 : g_free ((gchar *) args[6]);
2891 :
2892 1 : if (success && name)
2893 1 : success = bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
2894 :
2895 1 : return success;
2896 : }
2897 :
2898 : /**
2899 : * bd_lvm_cache_pool_convert:
2900 : * @vg_name: name of the VG to create the new thin pool in
2901 : * @data_lv: name of the LV that should become the data part of the new pool
2902 : * @metadata_lv: name of the LV that should become the metadata part of the new pool
2903 : * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
2904 : * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
2905 : * (just passed to LVM as is)
2906 : * @error: (out) (optional): place to store error (if any)
2907 : *
2908 : * Converts the @data_lv and @metadata_lv into a new cache pool in the @vg_name
2909 : * VG.
2910 : *
2911 : * Returns: whether the new cache pool was successfully created from @data_lv and
2912 : * @metadata_lv or not
2913 : *
2914 : * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
2915 : */
2916 1 : gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
2917 1 : const gchar *args[8] = {"lvconvert", "--yes", "--type", "cache-pool", "--poolmetadata", metadata_lv, NULL, NULL};
2918 1 : gboolean success = FALSE;
2919 :
2920 1 : args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
2921 :
2922 1 : success = call_lvm_and_report_error (args, extra, TRUE, error);
2923 1 : g_free ((gchar *) args[6]);
2924 :
2925 1 : if (success && name)
2926 1 : success = bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
2927 :
2928 1 : return success;
2929 : }
2930 :
2931 : /**
2932 : * bd_lvm_vdo_pool_create:
2933 : * @vg_name: name of the VG to create a new LV in
2934 : * @lv_name: name of the to-be-created VDO LV
2935 : * @pool_name: (nullable): name of the to-be-created VDO pool LV or %NULL for default name
2936 : * @data_size: requested size of the data VDO LV (physical size of the @pool_name VDO pool LV)
2937 : * @virtual_size: requested virtual_size of the @lv_name VDO LV
2938 : * @index_memory: amount of index memory (in bytes) or 0 for default
2939 : * @compression: whether to enable compression or not
2940 : * @deduplication: whether to enable deduplication or not
2941 : * @write_policy: write policy for the volume
2942 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV creation
2943 : * (just passed to LVM as is)
2944 : * @error: (out) (optional): place to store error (if any)
2945 : *
2946 : * Returns: whether the given @vg_name/@lv_name VDO LV was successfully created or not
2947 : *
2948 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE
2949 : */
2950 7 : gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
2951 7 : const gchar *args[16] = {"lvcreate", "--type", "vdo", "-n", lv_name, "-L", NULL, "-V", NULL,
2952 : "--compression", compression ? "y" : "n",
2953 : "--deduplication", deduplication ? "y" : "n",
2954 : "-y", NULL, NULL};
2955 7 : gboolean success = FALSE;
2956 7 : gchar *old_config = NULL;
2957 7 : const gchar *write_policy_str = NULL;
2958 :
2959 7 : write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
2960 7 : if (!write_policy_str)
2961 0 : return FALSE;
2962 :
2963 7 : args[6] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", data_size / 1024);
2964 7 : args[8] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", virtual_size / 1024);
2965 :
2966 7 : if (pool_name) {
2967 6 : args[14] = g_strdup_printf ("%s/%s", vg_name, pool_name);
2968 : } else
2969 1 : args[14] = vg_name;
2970 :
2971 : /* index_memory and write_policy can be specified only using the config */
2972 7 : g_mutex_lock (&global_config_lock);
2973 7 : old_config = global_config_str;
2974 7 : if (index_memory != 0)
2975 3 : global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
2976 : index_memory / (1024 * 1024),
2977 : write_policy_str);
2978 : else
2979 4 : global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
2980 : write_policy_str);
2981 :
2982 7 : success = call_lvm_and_report_error (args, extra, FALSE, error);
2983 :
2984 7 : g_free (global_config_str);
2985 7 : global_config_str = old_config;
2986 7 : g_mutex_unlock (&global_config_lock);
2987 :
2988 7 : g_free ((gchar *) args[6]);
2989 7 : g_free ((gchar *) args[8]);
2990 :
2991 7 : if (pool_name)
2992 6 : g_free ((gchar *) args[14]);
2993 :
2994 7 : return success;
2995 : }
2996 :
2997 4 : static gboolean _vdo_set_compression_deduplication (const gchar *vg_name, const gchar *pool_name, const gchar *op, gboolean enable, const BDExtraArg **extra, GError **error) {
2998 4 : const gchar *args[5] = {"lvchange", op, enable ? "y" : "n", NULL, NULL};
2999 4 : gboolean success = FALSE;
3000 :
3001 4 : args[3] = g_strdup_printf ("%s/%s", vg_name, pool_name);
3002 :
3003 4 : success = call_lvm_and_report_error (args, extra, TRUE, error);
3004 4 : g_free ((gchar *) args[3]);
3005 :
3006 4 : return success;
3007 : }
3008 :
3009 : /**
3010 : * bd_lvm_vdo_enable_compression:
3011 : * @vg_name: name of the VG containing the to-be-changed VDO pool LV
3012 : * @pool_name: name of the VDO pool LV to enable compression on
3013 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
3014 : * (just passed to LVM as is)
3015 : * @error: (out) (optional): place to store error (if any)
3016 : *
3017 : * Returns: whether compression was successfully enabled on @vg_name/@pool_name LV or not
3018 : *
3019 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
3020 : */
3021 1 : gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
3022 1 : return _vdo_set_compression_deduplication (vg_name, pool_name, "--compression", TRUE, extra, error);
3023 : }
3024 :
3025 : /**
3026 : * bd_lvm_vdo_disable_compression:
3027 : * @vg_name: name of the VG containing the to-be-changed VDO pool LV
3028 : * @pool_name: name of the VDO pool LV to disable compression on
3029 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
3030 : * (just passed to LVM as is)
3031 : * @error: (out) (optional): place to store error (if any)
3032 : *
3033 : * Returns: whether compression was successfully disabled on @vg_name/@pool_name LV or not
3034 : *
3035 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
3036 : */
3037 1 : gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
3038 1 : return _vdo_set_compression_deduplication (vg_name, pool_name, "--compression", FALSE, extra, error);
3039 : }
3040 :
3041 : /**
3042 : * bd_lvm_vdo_enable_deduplication:
3043 : * @vg_name: name of the VG containing the to-be-changed VDO pool LV
3044 : * @pool_name: name of the VDO pool LV to enable deduplication on
3045 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
3046 : * (just passed to LVM as is)
3047 : * @error: (out) (optional): place to store error (if any)
3048 : *
3049 : * Returns: whether deduplication was successfully enabled on @vg_name/@pool_name LV or not
3050 : *
3051 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
3052 : */
3053 1 : gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
3054 1 : return _vdo_set_compression_deduplication (vg_name, pool_name, "--deduplication", TRUE, extra, error);
3055 : }
3056 :
3057 : /**
3058 : * bd_lvm_vdo_enable_deduplication:
3059 : * @vg_name: name of the VG containing the to-be-changed VDO pool LV
3060 : * @pool_name: name of the VDO pool LV to disable deduplication on
3061 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
3062 : * (just passed to LVM as is)
3063 : * @error: (out) (optional): place to store error (if any)
3064 : *
3065 : * Returns: whether deduplication was successfully disabled on @vg_name/@pool_name LV or not
3066 : *
3067 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
3068 : */
3069 1 : gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
3070 1 : return _vdo_set_compression_deduplication (vg_name, pool_name, "--deduplication", FALSE, extra, error);
3071 : }
3072 :
3073 : /**
3074 : * bd_lvm_vdo_info:
3075 : * @vg_name: name of the VG that contains the LV to get information about
3076 : * @lv_name: name of the LV to get information about
3077 : * @error: (out) (optional): place to store error (if any)
3078 : *
3079 : * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
3080 : * of error (the @error) gets populated in those cases)
3081 : *
3082 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
3083 : */
3084 10 : BDLVMVDOPooldata* bd_lvm_vdo_info (const gchar *vg_name, const gchar *lv_name, GError **error) {
3085 10 : const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
3086 : "--unquoted", "--units=b", "-a",
3087 : "-o", "vdo_operating_mode,vdo_compression_state,vdo_index_state,vdo_write_policy,vdo_index_memory_size,vdo_used_size,vdo_saving_percent,vdo_compression,vdo_deduplication",
3088 : NULL, NULL};
3089 :
3090 10 : GHashTable *table = NULL;
3091 10 : gboolean success = FALSE;
3092 10 : gchar *output = NULL;
3093 10 : gchar **lines = NULL;
3094 10 : gchar **lines_p = NULL;
3095 : guint num_items;
3096 :
3097 10 : args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
3098 :
3099 10 : success = call_lvm_and_capture_output (args, NULL, &output, error);
3100 10 : g_free ((gchar *) args[9]);
3101 :
3102 10 : if (!success)
3103 : /* the error is already populated from the call */
3104 0 : return NULL;
3105 :
3106 10 : lines = g_strsplit (output, "\n", 0);
3107 10 : g_free (output);
3108 :
3109 10 : for (lines_p = lines; *lines_p; lines_p++) {
3110 10 : table = parse_lvm_vars ((*lines_p), &num_items);
3111 10 : if (table && (num_items == 9)) {
3112 10 : g_strfreev (lines);
3113 10 : return get_vdo_data_from_table (table, TRUE);
3114 0 : } else if (table)
3115 0 : g_hash_table_destroy (table);
3116 : }
3117 0 : g_strfreev (lines);
3118 :
3119 : /* getting here means no usable info was found */
3120 0 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
3121 : "Failed to parse information about the VDO LV");
3122 0 : return NULL;
3123 : }
3124 :
3125 : /**
3126 : * bd_lvm_vdo_resize:
3127 : * @vg_name: name of the VG containing the to-be-resized VDO LV
3128 : * @lv_name: name of the to-be-resized VDO LV
3129 : * @size: the requested new size of the VDO LV
3130 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV resize
3131 : * (just passed to LVM as is)
3132 : * @error: (out) (optional): place to store error (if any)
3133 : *
3134 : * Returns: whether the @vg_name/@lv_name VDO LV was successfully resized or not
3135 : *
3136 : * Note: Reduction needs to process TRIM for reduced disk area to unmap used data blocks
3137 : * from the VDO pool LV and it may take a long time.
3138 : *
3139 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
3140 : */
3141 1 : gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
3142 1 : return bd_lvm_lvresize (vg_name, lv_name, size, extra, error);
3143 : }
3144 :
3145 : /**
3146 : * bd_lvm_vdo_pool_resize:
3147 : * @vg_name: name of the VG containing the to-be-resized VDO pool LV
3148 : * @pool_name: name of the to-be-resized VDO pool LV
3149 : * @size: the requested new size of the VDO pool LV
3150 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool LV resize
3151 : * (just passed to LVM as is)
3152 : * @error: (out) (optional): place to store error (if any)
3153 : *
3154 : * Returns: whether the @vg_name/@pool_name VDO pool LV was successfully resized or not
3155 : *
3156 : * Note: Size of the VDO pool LV can be only extended, not reduced.
3157 : *
3158 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
3159 : */
3160 2 : gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error) {
3161 2 : BDLVMLVdata *info = NULL;
3162 :
3163 2 : info = bd_lvm_lvinfo (vg_name, pool_name, error);
3164 2 : if (!info)
3165 0 : return FALSE;
3166 :
3167 2 : if (info->size >= size) {
3168 1 : g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_SUPPORTED,
3169 : "Reducing physical size of the VDO pool LV is not supported.");
3170 1 : bd_lvm_lvdata_free (info);
3171 1 : return FALSE;
3172 : }
3173 :
3174 1 : bd_lvm_lvdata_free (info);
3175 :
3176 1 : return bd_lvm_lvresize (vg_name, pool_name, size, extra, error);
3177 : }
3178 :
3179 : /**
3180 : * bd_lvm_vdo_pool_convert:
3181 : * @vg_name: name of the VG that contains @pool_lv
3182 : * @pool_lv: name of the LV that should become the new VDO pool LV
3183 : * @name: (nullable): name for the VDO LV or %NULL for default name
3184 : * @virtual_size: virtual size for the new VDO LV
3185 : * @index_memory: amount of index memory (in bytes) or 0 for default
3186 : * @compression: whether to enable compression or not
3187 : * @deduplication: whether to enable deduplication or not
3188 : * @write_policy: write policy for the volume
3189 : * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool creation
3190 : * (just passed to LVM as is)
3191 : * @error: (out) (optional): place to store error (if any)
3192 : *
3193 : * Converts the @pool_lv into a new VDO pool LV in the @vg_name VG and creates a new
3194 : * @name VDO LV with size @virtual_size.
3195 : *
3196 : * Note: All data on @pool_lv will be irreversibly destroyed.
3197 : *
3198 : * Returns: whether the new VDO pool LV was successfully created from @pool_lv and or not
3199 : *
3200 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE&%BD_LVM_TECH_MODE_MODIFY
3201 : */
3202 1 : gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name, const gchar *pool_lv, const gchar *name, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
3203 1 : const gchar *args[14] = {"lvconvert", "--yes", "--type", "vdo-pool",
3204 : "--compression", compression ? "y" : "n",
3205 : "--deduplication", deduplication ? "y" : "n",
3206 : NULL, NULL, NULL, NULL, NULL, NULL};
3207 1 : gboolean success = FALSE;
3208 1 : guint next_arg = 4;
3209 1 : gchar *size_str = NULL;
3210 1 : gchar *lv_spec = NULL;
3211 1 : gchar *old_config = NULL;
3212 1 : const gchar *write_policy_str = NULL;
3213 :
3214 1 : write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
3215 1 : if (!write_policy_str)
3216 0 : return FALSE;
3217 :
3218 1 : if (name) {
3219 1 : args[next_arg++] = "-n";
3220 1 : args[next_arg++] = name;
3221 : }
3222 :
3223 1 : args[next_arg++] = "-V";
3224 1 : size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", virtual_size / 1024);
3225 1 : args[next_arg++] = size_str;
3226 1 : lv_spec = g_strdup_printf ("%s/%s", vg_name, pool_lv);
3227 1 : args[next_arg++] = lv_spec;
3228 :
3229 : /* index_memory and write_policy can be specified only using the config */
3230 1 : g_mutex_lock (&global_config_lock);
3231 1 : old_config = global_config_str;
3232 1 : if (index_memory != 0)
3233 0 : global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
3234 : index_memory / (1024 * 1024),
3235 : write_policy_str);
3236 : else
3237 1 : global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
3238 : write_policy_str);
3239 :
3240 1 : success = call_lvm_and_report_error (args, extra, FALSE, error);
3241 :
3242 1 : g_free (global_config_str);
3243 1 : global_config_str = old_config;
3244 1 : g_mutex_unlock (&global_config_lock);
3245 :
3246 1 : g_free (size_str);
3247 1 : g_free (lv_spec);
3248 :
3249 1 : return success;
3250 : }
3251 :
3252 : /**
3253 : * bd_lvm_vdolvpoolname:
3254 : * @vg_name: name of the VG containing the queried VDO LV
3255 : * @lv_name: name of the queried VDO LV
3256 : * @error: (out) (optional): place to store error (if any)
3257 : *
3258 : * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
3259 : * VDO LV or %NULL if failed to determine (@error) is set in those cases)
3260 : *
3261 : * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
3262 : */
3263 2 : gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
3264 2 : gboolean success = FALSE;
3265 2 : gchar *output = NULL;
3266 2 : const gchar *args[6] = {"lvs", "--noheadings", "-o", "pool_lv", NULL, NULL};
3267 2 : args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
3268 :
3269 2 : success = call_lvm_and_capture_output (args, NULL, &output, error);
3270 2 : g_free ((gchar *) args[4]);
3271 :
3272 2 : if (!success)
3273 : /* the error is already populated from the call */
3274 0 : return NULL;
3275 :
3276 2 : return g_strstrip (output);
3277 : }
|