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 : #define _XOPEN_SOURCE /* needed for time.h */
21 :
22 : #include <glib.h>
23 : #include <unistd.h>
24 : #include <blockdev/utils.h>
25 : #include <string.h>
26 : #include <glob.h>
27 : #include <time.h>
28 : #include <bs_size.h>
29 :
30 : #include "mdraid.h"
31 : #include "check_deps.h"
32 :
33 : #define MDADM_MIN_VERSION "3.3.2"
34 :
35 : /**
36 : * SECTION: mdraid
37 : * @short_description: plugin for basic operations with MD RAID
38 : * @title: MD RAID
39 : * @include: mdraid.h
40 : *
41 : * A plugin for basic operations with MD RAID. Also sizes are in
42 : * bytes unless specified otherwise.
43 : */
44 :
45 : /**
46 : * bd_md_error_quark: (skip)
47 : */
48 0 : GQuark bd_md_error_quark (void)
49 : {
50 0 : return g_quark_from_static_string ("g-bd-md-error-quark");
51 : }
52 :
53 : /**
54 : * bd_md_examine_data_copy: (skip)
55 : *
56 : * Creates a new copy of @data.
57 : */
58 0 : BDMDExamineData* bd_md_examine_data_copy (BDMDExamineData *data) {
59 0 : if (data == NULL)
60 0 : return NULL;
61 :
62 0 : BDMDExamineData *new_data = g_new0 (BDMDExamineData, 1);
63 :
64 0 : new_data->device = g_strdup (data->device);
65 0 : new_data->level = g_strdup (data->level);
66 0 : new_data->num_devices = data->num_devices;
67 0 : new_data->name = g_strdup (data->name);
68 0 : new_data->size = data->size;
69 0 : new_data->uuid = g_strdup (data->uuid);
70 0 : new_data->update_time = data->update_time;
71 0 : new_data->dev_uuid = g_strdup (data->dev_uuid);
72 0 : new_data->events = data->events;
73 0 : new_data->metadata = g_strdup (data->metadata);
74 0 : new_data->chunk_size = data->chunk_size;
75 0 : return new_data;
76 : }
77 :
78 : /**
79 : * bd_md_examine_data_free: (skip)
80 : *
81 : * Frees @data.
82 : */
83 0 : void bd_md_examine_data_free (BDMDExamineData *data) {
84 0 : if (data == NULL)
85 0 : return;
86 :
87 0 : g_free (data->device);
88 0 : g_free (data->level);
89 0 : g_free (data->name);
90 0 : g_free (data->uuid);
91 0 : g_free (data->dev_uuid);
92 0 : g_free (data->metadata);
93 0 : g_free (data);
94 : }
95 :
96 : /**
97 : * bd_md_detail_data_copy: (skip)
98 : *
99 : * Creates a new copy of @data.
100 : */
101 0 : BDMDDetailData* bd_md_detail_data_copy (BDMDDetailData *data) {
102 0 : if (data == NULL)
103 0 : return NULL;
104 :
105 0 : BDMDDetailData *new_data = g_new0 (BDMDDetailData, 1);
106 :
107 0 : new_data->device = g_strdup (data->device);
108 0 : new_data->name = g_strdup (data->name);
109 0 : new_data->metadata = g_strdup (data->metadata);
110 0 : new_data->creation_time = g_strdup (data->creation_time);
111 0 : new_data->level = g_strdup (data->level);
112 0 : new_data->array_size = data->array_size;
113 0 : new_data->use_dev_size = data->use_dev_size;
114 0 : new_data->raid_devices = data->raid_devices;
115 0 : new_data->active_devices = data->active_devices;
116 0 : new_data->working_devices = data->working_devices;
117 0 : new_data->failed_devices = data->failed_devices;
118 0 : new_data->spare_devices = data->spare_devices;
119 0 : new_data->clean = data->clean;
120 0 : new_data->uuid = g_strdup (data->uuid);
121 0 : new_data->container = g_strdup (data->container);
122 :
123 0 : return new_data;
124 : }
125 :
126 : /**
127 : * bd_md_detail_data_free: (skip)
128 : *
129 : * Frees @data.
130 : */
131 0 : void bd_md_detail_data_free (BDMDDetailData *data) {
132 0 : if (data == NULL)
133 0 : return;
134 :
135 0 : g_free (data->device);
136 0 : g_free (data->name);
137 0 : g_free (data->metadata);
138 0 : g_free (data->creation_time);
139 0 : g_free (data->level);
140 0 : g_free (data->uuid);
141 0 : g_free (data->container);
142 :
143 0 : g_free (data);
144 : }
145 :
146 :
147 : static volatile guint avail_deps = 0;
148 : static GMutex deps_check_lock;
149 :
150 : #define DEPS_MDADM 0
151 : #define DEPS_MDADM_MASK (1 << DEPS_MDADM)
152 : #define DEPS_LAST 1
153 :
154 : static const UtilDep deps[DEPS_LAST] = {
155 : {"mdadm", MDADM_MIN_VERSION, NULL, "mdadm - v([\\d\\.]+)"},
156 : };
157 :
158 :
159 : /**
160 : * bd_md_init:
161 : *
162 : * Initializes the plugin. **This function is called automatically by the
163 : * library's initialization functions.**
164 : *
165 : */
166 25 : gboolean bd_md_init (void) {
167 : /* nothing to do here */
168 25 : return TRUE;
169 : };
170 :
171 : /**
172 : * bd_md_close:
173 : *
174 : * Cleans up after the plugin. **This function is called automatically by the
175 : * library's functions that unload it.**
176 : *
177 : */
178 25 : void bd_md_close (void) {
179 : /* nothing to do here */
180 25 : }
181 :
182 :
183 : /**
184 : * bd_md_is_tech_avail:
185 : * @tech: the queried tech
186 : * @mode: a bit mask of queried modes of operation for @tech
187 : * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
188 : *
189 : * Returns: whether the @tech-@mode combination is available -- supported by the
190 : * plugin implementation and having all the runtime dependencies available
191 : */
192 6 : gboolean bd_md_is_tech_avail (BDMDTech tech G_GNUC_UNUSED, guint64 mode G_GNUC_UNUSED, GError **error) {
193 : /* all tech-mode combinations are supported by this implementation of the
194 : plugin, but it requires the 'mdadm' utility */
195 6 : return check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
196 : }
197 :
198 : /**
199 : * parse_mdadm_vars: (skip)
200 : * @str: string to parse
201 : * @item_sep: item separator(s) (key-value pairs separator)
202 : * @key_val_sep: key-value separator(s) (typically ":" or "=")
203 : * @num_items: (out): number of parsed items (key-value pairs)
204 : *
205 : * Returns: (transfer full): GHashTable containing the key-value pairs parsed
206 : * from the @str.
207 : */
208 28 : static GHashTable* parse_mdadm_vars (const gchar *str, const gchar *item_sep, const gchar *key_val_sep, guint *num_items) {
209 28 : GHashTable *table = NULL;
210 28 : gchar **items = NULL;
211 28 : gchar **item_p = NULL;
212 28 : gchar **key_val = NULL;
213 28 : gchar **vals = NULL;
214 :
215 28 : table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
216 28 : *num_items = 0;
217 :
218 28 : items = g_strsplit_set (str, item_sep, 0);
219 632 : for (item_p=items; *item_p; item_p++) {
220 604 : key_val = g_strsplit (*item_p, key_val_sep, 2);
221 604 : if (g_strv_length ((gchar **) key_val) == 2) {
222 : /* we only want to process valid lines (with the separator) */
223 : /* only use the first value for the given key */
224 433 : if (!g_hash_table_contains (table, g_strstrip (key_val[0]))) {
225 418 : if (strstr (key_val[1], "<--")) {
226 : /* mdadm --examine output for a set being migrated */
227 5 : vals = g_strsplit (key_val[1], "<--", 2);
228 5 : g_hash_table_insert (table, g_strstrip (key_val[0]), g_strstrip (vals[0]));
229 5 : g_free (key_val[1]);
230 5 : g_free (vals[1]);
231 5 : g_free (vals);
232 : } else {
233 413 : g_hash_table_insert (table, g_strstrip (key_val[0]), g_strstrip (key_val[1]));
234 : }
235 418 : g_free (key_val);
236 : } else {
237 15 : g_strfreev (key_val);
238 : }
239 433 : (*num_items)++;
240 : } else
241 : /* invalid line, just free key_val */
242 171 : g_strfreev (key_val);
243 : }
244 :
245 28 : g_strfreev (items);
246 28 : return table;
247 : }
248 :
249 8 : static BDMDExamineData* get_examine_data_from_table (GHashTable *table, gboolean free_table) {
250 8 : BDMDExamineData *data = g_new0 (BDMDExamineData, 1);
251 8 : gchar *value = NULL;
252 8 : gchar *first_space = NULL;
253 8 : BSSize size = NULL;
254 8 : BSError *bs_error = NULL;
255 : struct tm tm;
256 : char time_str[20];
257 8 : gchar *name_str = NULL;
258 :
259 8 : data->level = g_strdup ((gchar*) g_hash_table_lookup (table, "Raid Level"));
260 8 : if (!(data->level))
261 : /* BUG: mdadm outputs "RAID Level" for some metadata formats (rhbz#1380034) */
262 8 : data->level = g_strdup ((gchar*) g_hash_table_lookup (table, "RAID Level"));
263 :
264 8 : value = (gchar*) g_hash_table_lookup (table, "Raid Devices");
265 8 : if (!value)
266 : /* BUG: mdadm outputs "RAID Devices" for some metadata formats (rhbz#1380034) */
267 4 : value = (gchar*) g_hash_table_lookup (table, "RAID Devices");
268 8 : if (value)
269 6 : data->num_devices = g_ascii_strtoull (value, NULL, 0);
270 : else
271 2 : data->num_devices = 0;
272 :
273 8 : name_str = ((gchar*) g_hash_table_lookup (table, "Name"));
274 8 : if (name_str) {
275 4 : g_strstrip (name_str);
276 4 : first_space = strchr (name_str, ' ');
277 4 : if (first_space)
278 4 : *first_space = '\0';
279 4 : data->name = g_strdup (name_str);
280 : }
281 :
282 8 : value = (gchar*) g_hash_table_lookup (table, "Array Size");
283 8 : if (value) {
284 4 : first_space = strchr (value, ' ');
285 4 : if (first_space)
286 4 : *first_space = '\0';
287 4 : if (value && first_space)
288 : /* Array Size is in KiB */
289 4 : data->size = g_ascii_strtoull (value, NULL, 0) * 1024;
290 : } else
291 4 : data->size = 0;
292 :
293 8 : data->uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "Array UUID"));
294 8 : if (!data->uuid)
295 : /* also try just "UUID" which may be reported e.g for IMSM FW RAID */
296 8 : data->uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "UUID"));
297 :
298 8 : value = (gchar*) g_hash_table_lookup (table, "Update Time");
299 8 : if (value) {
300 4 : memset (&tm, 0, sizeof (struct tm));
301 4 : strptime (value, "%a %b %e %H:%M:%S %Y", &tm);
302 4 : strftime (time_str, sizeof (time_str), "%s" , &tm);
303 :
304 4 : data->update_time = g_ascii_strtoull (time_str, NULL, 0);
305 : } else
306 4 : data->update_time = 0;
307 :
308 8 : data->dev_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "Device UUID"));
309 :
310 8 : value = (gchar*) g_hash_table_lookup (table, "Events");
311 8 : if (value)
312 4 : data->events = g_ascii_strtoull (value, NULL, 0);
313 : else
314 4 : data->events = 0;
315 :
316 8 : value = (gchar*) g_hash_table_lookup (table, "Version");
317 8 : if (value)
318 7 : data->metadata = g_strdup (value);
319 : else
320 1 : data->metadata = NULL;
321 :
322 8 : value = (gchar*) g_hash_table_lookup (table, "Chunk Size");
323 8 : if (value) {
324 4 : size = bs_size_new_from_str (value, &bs_error);
325 4 : if (size) {
326 4 : data->chunk_size = bs_size_get_bytes (size, NULL, &bs_error);
327 4 : bs_size_free (size);
328 : }
329 :
330 4 : if (bs_error) {
331 0 : bd_utils_log_format (BD_UTILS_LOG_WARNING, "get_examine_data_from_table(): Failed to parse chunk size "
332 0 : "from mdexamine data: %s", bs_error->msg);
333 0 : bs_clear_error (&bs_error);
334 : }
335 : } else
336 4 : data->chunk_size = 0;
337 :
338 8 : if (free_table)
339 8 : g_hash_table_destroy (table);
340 :
341 8 : return data;
342 : }
343 :
344 12 : static BDMDDetailData* get_detail_data_from_table (GHashTable *table, gboolean free_table) {
345 12 : BDMDDetailData *data = g_new0 (BDMDDetailData, 1);
346 12 : gchar *value = NULL;
347 12 : gchar *name_str = NULL;
348 12 : gchar *first_space = NULL;
349 :
350 12 : data->metadata = g_strdup ((gchar*) g_hash_table_lookup (table, "Version"));
351 12 : data->creation_time = g_strdup ((gchar*) g_hash_table_lookup (table, "Creation Time"));
352 12 : data->level = g_strdup ((gchar*) g_hash_table_lookup (table, "Raid Level"));
353 12 : data->uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "UUID"));
354 :
355 12 : name_str = ((gchar*) g_hash_table_lookup (table, "Name"));
356 12 : if (name_str) {
357 10 : g_strstrip (name_str);
358 10 : first_space = strchr (name_str, ' ');
359 10 : if (first_space)
360 10 : *first_space = '\0';
361 10 : data->name = g_strdup (name_str);
362 : }
363 :
364 12 : value = (gchar*) g_hash_table_lookup (table, "Array Size");
365 12 : if (value) {
366 11 : first_space = strchr (value, ' ');
367 11 : if (first_space)
368 11 : *first_space = '\0';
369 11 : if (value && first_space)
370 11 : data->array_size = g_ascii_strtoull (value, NULL, 0);
371 : }
372 : else
373 1 : data->array_size = 0;
374 :
375 12 : value = (gchar*) g_hash_table_lookup (table, "Used Dev Size");
376 12 : if (value) {
377 9 : first_space = strchr (value, ' ');
378 9 : if (first_space)
379 9 : *first_space = '\0';
380 9 : if (value && first_space)
381 9 : data->use_dev_size = g_ascii_strtoull (value, NULL, 0);
382 : }
383 : else
384 3 : data->use_dev_size = 0;
385 :
386 12 : value = (gchar*) g_hash_table_lookup (table, "Raid Devices");
387 12 : if (value)
388 11 : data->raid_devices = g_ascii_strtoull (value, NULL, 0);
389 : else
390 1 : data->raid_devices = 0;
391 :
392 12 : value = (gchar*) g_hash_table_lookup (table, "Total Devices");
393 12 : if (value)
394 12 : data->total_devices = g_ascii_strtoull (value, NULL, 0);
395 : else
396 0 : data->total_devices = 0;
397 :
398 12 : value = (gchar*) g_hash_table_lookup (table, "Active Devices");
399 12 : if (value)
400 11 : data->active_devices = g_ascii_strtoull (value, NULL, 0);
401 : else
402 1 : data->active_devices = 0;
403 :
404 12 : value = (gchar*) g_hash_table_lookup (table, "Working Devices");
405 12 : if (value)
406 12 : data->working_devices = g_ascii_strtoull (value, NULL, 0);
407 : else
408 0 : data->working_devices = 0;
409 :
410 12 : value = (gchar*) g_hash_table_lookup (table, "Failed Devices");
411 12 : if (value)
412 11 : data->failed_devices = g_ascii_strtoull (value, NULL, 0);
413 : else
414 1 : data->failed_devices = 0;
415 :
416 12 : value = (gchar*) g_hash_table_lookup (table, "Spare Devices");
417 12 : if (value)
418 10 : data->spare_devices = g_ascii_strtoull (value, NULL, 0);
419 : else
420 2 : data->spare_devices = 0;
421 :
422 12 : value = (gchar*) g_hash_table_lookup (table, "State");
423 12 : if (value)
424 11 : data->clean = (g_strcmp0 (value, "clean") == 0);
425 : else
426 1 : data->clean = FALSE;
427 :
428 12 : if (free_table)
429 12 : g_hash_table_destroy (table);
430 :
431 12 : return data;
432 : }
433 :
434 : /**
435 : * get_sysfs_name_from_input: (skip)
436 : * @input: either RAID name or node name
437 : * @error: (out) (optional): place to store error (if any)
438 : *
439 : * Returns: (transfer full): RAID node name
440 : */
441 9 : static gchar* get_sysfs_name_from_input (const gchar *input, GError **error) {
442 9 : gchar* sysfs_name = NULL;
443 9 : gchar* path = NULL;
444 :
445 : /* get rid of the "/dev/" or "/dev/md/" prefix (if any) */
446 9 : if (g_str_has_prefix (input, "/dev/md/"))
447 1 : input = input + 8;
448 8 : else if (g_str_has_prefix (input, "/dev/"))
449 1 : input = input + 5;
450 :
451 9 : path = g_strdup_printf ("/sys/class/block/%s/md", input);
452 9 : if (access (path, F_OK) == 0)
453 2 : sysfs_name = g_strdup (input);
454 : else
455 7 : sysfs_name = bd_md_node_from_name (input, error);
456 :
457 9 : g_free (path);
458 :
459 9 : return sysfs_name;
460 : }
461 :
462 : /**
463 : * get_mdadm_spec_from_input: (skip)
464 : * @input: RAID specification from user
465 : * @error: (out) (optional): place to store error (if any)
466 : *
467 : * Returns: (transfer full): RAID specification for mdadm
468 : *
469 : * Takes some RAID specification (raid name, node name, path or name symlink)
470 : * and returns a new specification suitable for mdadm command.
471 : */
472 73 : static gchar* get_mdadm_spec_from_input (const gchar *input, GError **error) {
473 73 : gchar* md_path_str = NULL;
474 73 : gchar* name_path_str = NULL;
475 73 : gchar* mdadm_spec = NULL;
476 :
477 73 : if (g_str_has_prefix (input, "/dev/")) {
478 5 : if (access (input, F_OK) == 0)
479 4 : mdadm_spec = g_strdup (input);
480 : else {
481 1 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_INVAL,
482 : "Device %s doesn't exist.", input);
483 1 : mdadm_spec = NULL;
484 : }
485 : } else {
486 68 : md_path_str = g_strdup_printf ("/dev/%s", input);
487 68 : name_path_str = g_strdup_printf ("/dev/md/%s", input);
488 68 : if (access (name_path_str, F_OK) == 0)
489 34 : mdadm_spec = g_strdup (name_path_str);
490 34 : else if (access (md_path_str, F_OK) == 0)
491 7 : mdadm_spec = g_strdup (md_path_str);
492 : else
493 27 : mdadm_spec = g_strdup (input);
494 : }
495 :
496 73 : g_free (md_path_str);
497 73 : g_free (name_path_str);
498 :
499 73 : return mdadm_spec;
500 : }
501 :
502 : /**
503 : * bd_md_get_superblock_size:
504 : * @member_size: size of an array member
505 : * @version: (nullable): metadata version or %NULL to use the current default version
506 : * @error: (out) (optional): place to store error (if any)
507 : *
508 : * Returns: Calculated superblock size for an array with a given @member_size
509 : * and metadata @version or default if unsupported @version is used.
510 : *
511 : * Tech category: always available
512 : */
513 11 : guint64 bd_md_get_superblock_size (guint64 member_size, const gchar *version, GError **error G_GNUC_UNUSED) {
514 11 : guint64 headroom = BD_MD_SUPERBLOCK_SIZE;
515 11 : guint64 min_headroom = (1 MiB);
516 :
517 : /* mdadm 3.2.4 made a major change in the amount of space used for 1.1 and
518 : * 1.2 in order to reserve space for reshaping. See commit 508a7f16 in the
519 : * upstream mdadm repository. */
520 18 : if (!version || (g_strcmp0 (version, "1.1") == 0) ||
521 12 : (g_strcmp0 (version, "1.2") == 0) || (g_strcmp0 (version, "default") == 0)) {
522 : /* MDADM: We try to leave 0.1% at the start for reshape
523 : * MDADM: operations, but limit this to 128Meg (0.1% of 10Gig)
524 : * MDADM: which is plenty for efficient reshapes
525 : * NOTE: In the mdadm code this is in 512b sectors. Converted to use MiB */
526 8 : headroom = (128 MiB);
527 36 : while (((headroom << 10) > member_size) && (headroom > min_headroom))
528 28 : headroom >>= 1;
529 : }
530 :
531 11 : return headroom;
532 : }
533 :
534 : /**
535 : * bd_md_create:
536 : * @device_name: name of the device to create
537 : * @level: RAID level (as understood by mdadm, see mdadm(8))
538 : * @disks: (array zero-terminated=1): disks to use for the new RAID (including spares)
539 : * @spares: number of spare devices
540 : * @version: (nullable): metadata version
541 : * @bitmap: (nullable): write-intent bitmap location ('none', 'internal') or %NULL to let mdadm decide (i.e. internal > 100GB)
542 : * @chunk_size: chunk size of the device to create
543 : * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
544 : * passed to the 'mdadm' utility)
545 : * @error: (out) (optional): place to store error (if any)
546 : *
547 : * Returns: whether the new MD RAID device @device_name was successfully created or not
548 : *
549 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_CREATE
550 : */
551 13 : gboolean bd_md_create (const gchar *device_name, const gchar *level, const gchar **disks, guint64 spares, const gchar *version, const gchar *bitmap, guint64 chunk_size, const BDExtraArg **extra, GError **error) {
552 13 : const gchar **argv = NULL;
553 : /* {"mdadm", "create", device, "--run", "level", "raid-devices",...} */
554 13 : guint argv_len = 6;
555 13 : guint argv_top = 0;
556 13 : guint i = 0;
557 13 : guint num_disks = 0;
558 13 : gchar *level_str = NULL;
559 13 : gchar *rdevices_str = NULL;
560 13 : gchar *spares_str = NULL;
561 13 : gchar *version_str = NULL;
562 13 : gchar *chunk_str = NULL;
563 13 : gchar *bitmap_str = NULL;
564 13 : gboolean ret = FALSE;
565 :
566 13 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
567 0 : return FALSE;
568 :
569 13 : if (spares != 0)
570 2 : argv_len++;
571 13 : if (version)
572 1 : argv_len++;
573 13 : if (bitmap)
574 2 : argv_len++;
575 13 : if (chunk_size != 0)
576 1 : argv_len++;
577 :
578 13 : num_disks = g_strv_length ((gchar **) disks);
579 13 : argv_len += num_disks;
580 :
581 13 : argv = g_new0 (const gchar*, argv_len + 1);
582 :
583 13 : level_str = g_strdup_printf ("--level=%s", level);
584 13 : rdevices_str = g_strdup_printf ("--raid-devices=%"G_GUINT64_FORMAT, (num_disks - spares));
585 :
586 13 : argv[argv_top++] = "mdadm";
587 13 : argv[argv_top++] = "--create";
588 13 : argv[argv_top++] = device_name;
589 13 : argv[argv_top++] = "--run";
590 13 : argv[argv_top++] = level_str;
591 13 : argv[argv_top++] = rdevices_str;
592 :
593 13 : if (spares != 0) {
594 2 : spares_str = g_strdup_printf ("--spare-devices=%"G_GUINT64_FORMAT, spares);
595 2 : argv[argv_top++] = spares_str;
596 : }
597 13 : if (version) {
598 1 : version_str = g_strdup_printf ("--metadata=%s", version);
599 1 : argv[argv_top++] = version_str;
600 : }
601 13 : if (bitmap) {
602 2 : bitmap_str = g_strdup_printf ("--bitmap=%s", bitmap);
603 2 : argv[argv_top++] = bitmap_str;
604 : }
605 13 : if (chunk_size != 0) {
606 1 : chunk_str = g_strdup_printf ("--chunk=%"G_GUINT64_FORMAT, chunk_size/1024);
607 1 : argv[argv_top++] = chunk_str;
608 : }
609 :
610 41 : for (i=0; i < num_disks; i++)
611 28 : argv[argv_top++] = disks[i];
612 13 : argv[argv_top] = NULL;
613 :
614 13 : ret = bd_utils_exec_and_report_error (argv, extra, error);
615 :
616 13 : g_free (level_str);
617 13 : g_free (rdevices_str);
618 13 : g_free (spares_str);
619 13 : g_free (version_str);
620 13 : g_free (chunk_str);
621 13 : g_free (bitmap_str);
622 13 : g_free (argv);
623 :
624 13 : return ret;
625 : }
626 :
627 : /**
628 : * bd_md_destroy:
629 : * @device: device to destroy MD RAID metadata on
630 : * @error: (out) (optional): place to store error (if any)
631 : *
632 : * Returns: whether the MD RAID metadata was successfully destroyed on @device or not
633 : *
634 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_DELETE
635 : */
636 31 : gboolean bd_md_destroy (const gchar *device, GError **error) {
637 31 : const gchar *argv[] = {"mdadm", "--zero-superblock", device, NULL};
638 :
639 31 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
640 0 : return FALSE;
641 :
642 31 : return bd_utils_exec_and_report_error (argv, NULL, error);
643 : }
644 :
645 : /**
646 : * bd_md_deactivate:
647 : * @raid_spec: specification of the RAID device (name, node or path)
648 : * @error: (out) (optional): place to store error (if any)
649 : *
650 : * Returns: whether the RAID device @raid_spec was successfully deactivated or not
651 : *
652 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
653 : */
654 44 : gboolean bd_md_deactivate (const gchar *raid_spec, GError **error) {
655 44 : const gchar *argv[] = {"mdadm", "--stop", NULL, NULL};
656 44 : gchar *mdadm_spec = NULL;
657 44 : gboolean ret = FALSE;
658 :
659 44 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
660 0 : return FALSE;
661 :
662 44 : mdadm_spec = get_mdadm_spec_from_input (raid_spec, error);
663 44 : if (!mdadm_spec)
664 : /* error is already populated */
665 0 : return FALSE;
666 :
667 44 : argv[2] = mdadm_spec;
668 :
669 44 : ret = bd_utils_exec_and_report_error (argv, NULL, error);
670 44 : g_free (mdadm_spec);
671 :
672 44 : return ret;
673 : }
674 :
675 : /**
676 : * bd_md_activate:
677 : * @raid_spec: (nullable): specification of the RAID device (name, node or path) to activate (if not given "--scan" is implied and @members is ignored)
678 : * @members: (nullable) (array zero-terminated=1): member devices to be considered for @device activation
679 : * @uuid: (nullable): UUID (in the MD RAID format!) of the MD RAID to activate
680 : * @start_degraded: whether to start the array even if it's degraded
681 : * @extra: (nullable) (array zero-terminated=1): extra options for the activation (right now
682 : * passed to the 'mdadm' utility)
683 : * @error: (out) (optional): place to store error (if any)
684 : *
685 : * Returns: whether the MD RAID @device was successfully activated or not
686 : *
687 : * Note: either @members or @uuid (or both) have to be specified.
688 : *
689 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
690 : */
691 8 : gboolean bd_md_activate (const gchar *raid_spec, const gchar **members, const gchar *uuid, gboolean start_degraded, const BDExtraArg **extra, GError **error) {
692 8 : guint64 num_members = (raid_spec && members) ? g_strv_length ((gchar **) members) : 0;
693 8 : const gchar **argv = NULL;
694 8 : gchar *uuid_str = NULL;
695 8 : guint argv_top = 0;
696 8 : guint i = 0;
697 8 : gboolean ret = FALSE;
698 8 : BDMDDetailData *data = NULL;
699 :
700 8 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
701 0 : return FALSE;
702 :
703 8 : if (raid_spec) {
704 6 : data = bd_md_detail (raid_spec, NULL);
705 6 : if (data) {
706 1 : bd_utils_log_format (BD_UTILS_LOG_INFO,
707 : "RAID array '%s' is already active with %"G_GUINT64_FORMAT" devices"
708 : " (%"G_GUINT64_FORMAT" active, %"G_GUINT64_FORMAT" spare)",
709 : raid_spec, data->total_devices,
710 : data->active_devices, data->spare_devices);
711 1 : bd_md_detail_data_free (data);
712 1 : return TRUE;
713 : }
714 : }
715 :
716 : /* mdadm, --assemble, raid_spec/--scan, --run, --uuid=uuid, member1, member2,..., NULL*/
717 7 : argv = g_new0 (const gchar*, num_members + 6);
718 :
719 7 : argv[argv_top++] = "mdadm";
720 7 : argv[argv_top++] = "--assemble";
721 7 : if (raid_spec)
722 5 : argv[argv_top++] = raid_spec;
723 : else
724 2 : argv[argv_top++] = "--scan";
725 7 : if (start_degraded)
726 7 : argv[argv_top++] = "--run";
727 7 : if (uuid) {
728 3 : uuid_str = g_strdup_printf ("--uuid=%s", uuid);
729 3 : argv[argv_top++] = uuid_str;
730 : }
731 : /* only add member device if device_name given (a combination of --scan with
732 : a list of members doesn't work) */
733 7 : if (raid_spec && members)
734 15 : for (i=0; i < num_members; i++)
735 10 : argv[argv_top++] = members[i];
736 7 : argv[argv_top] = NULL;
737 :
738 7 : ret = bd_utils_exec_and_report_error (argv, extra, error);
739 :
740 7 : g_free (uuid_str);
741 7 : g_free (argv);
742 :
743 7 : return ret;
744 : }
745 :
746 : /**
747 : * bd_md_run:
748 : * @raid_spec: specification of the (possibly degraded) RAID device (name, node or path) to be started
749 : * @error: (out) (optional): place to store error (if any)
750 : *
751 : * Returns: whether the @raid_spec was successfully started or not
752 : *
753 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
754 : */
755 0 : gboolean bd_md_run (const gchar *raid_spec, GError **error) {
756 0 : const gchar *argv[] = {"mdadm", "--run", NULL, NULL};
757 0 : gchar *mdadm_spec = NULL;
758 0 : gboolean ret = FALSE;
759 :
760 0 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
761 0 : return FALSE;
762 :
763 0 : mdadm_spec = get_mdadm_spec_from_input (raid_spec, error);
764 0 : if (!mdadm_spec)
765 : /* error is already populated */
766 0 : return FALSE;
767 :
768 0 : argv[2] = mdadm_spec;
769 :
770 0 : ret = bd_utils_exec_and_report_error (argv, NULL, error);
771 0 : g_free (mdadm_spec);
772 :
773 0 : return ret;
774 : }
775 :
776 : /**
777 : * bd_md_nominate:
778 : * @device: device to nominate (add to its appropriate RAID) as a MD RAID device
779 : * @error: (out) (optional): place to store error (if any)
780 : *
781 : * Returns: whether the @device was successfully nominated (added to its
782 : * appropriate RAID) or not
783 : *
784 : * Note: may start the MD RAID if it becomes ready by adding @device.
785 : *
786 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
787 : */
788 2 : gboolean bd_md_nominate (const gchar *device, GError **error) {
789 2 : const gchar *argv[] = {"mdadm", "--incremental", "--quiet", "--run", device, NULL};
790 :
791 2 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
792 0 : return FALSE;
793 :
794 2 : return bd_utils_exec_and_report_error (argv, NULL, error);
795 : }
796 :
797 : /**
798 : * bd_md_denominate:
799 : * @device: device to denominate (remove from its appropriate RAID) as a MD RAID device
800 : * @error: (out) (optional): place to store error (if any)
801 : *
802 : * Returns: whether the @device was successfully denominated (added to its
803 : * appropriate RAID) or not
804 : *
805 : * Note: may start the MD RAID if it becomes ready by adding @device.
806 : *
807 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
808 : */
809 2 : gboolean bd_md_denominate (const gchar *device, GError **error) {
810 2 : const gchar *argv[] = {"mdadm", "--incremental", "--fail", device, NULL};
811 :
812 : /* XXX: stupid mdadm! --incremental --fail requires "sda1" instead of "/dev/sda1" */
813 2 : if (g_str_has_prefix (device, "/dev/"))
814 2 : argv[3] = (device + 5);
815 :
816 2 : return bd_utils_exec_and_report_error (argv, NULL, error);
817 : }
818 :
819 : /**
820 : * bd_md_add:
821 : * @raid_spec: specification of the RAID device (name, node or path) to add @device into
822 : * @device: name of the device to add to the @raid_spec RAID device
823 : * @raid_devs: number of devices the @raid_spec RAID should actively use or 0
824 : * to leave unspecified (see below)
825 : * @extra: (nullable) (array zero-terminated=1): extra options for the addition (right now
826 : * passed to the 'mdadm' utility)
827 : * @error: (out) (optional): place to store error (if any)
828 : *
829 : * Returns: whether the @device was successfully added to the @raid_spec RAID or
830 : * not
831 : *
832 : * The @raid_devs parameter is used when adding devices to a raid array that has
833 : * no actual redundancy. In this case it is necessary to explicitly grow the
834 : * array all at once rather than manage it in the sense of adding spares.
835 : *
836 : * Whether the new device will be added as a spare or an active member is
837 : * decided by mdadm.
838 : *
839 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
840 : */
841 5 : gboolean bd_md_add (const gchar *raid_spec, const gchar *device, guint64 raid_devs, const BDExtraArg **extra, GError **error) {
842 5 : const gchar *argv[7] = {"mdadm", NULL, NULL, NULL, NULL, NULL, NULL};
843 5 : guint argv_top = 1;
844 5 : gchar *mdadm_spec = NULL;
845 5 : gchar *raid_devs_str = NULL;
846 5 : gboolean ret = FALSE;
847 :
848 5 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
849 0 : return FALSE;
850 :
851 5 : mdadm_spec = get_mdadm_spec_from_input (raid_spec, error);
852 5 : if (!mdadm_spec)
853 : /* error is already populated */
854 0 : return FALSE;
855 :
856 5 : if (raid_devs != 0) {
857 0 : raid_devs_str = g_strdup_printf ("--raid-devices=%"G_GUINT64_FORMAT, raid_devs);
858 0 : argv[argv_top++] = "--grow";
859 0 : argv[argv_top++] = mdadm_spec;
860 0 : argv[argv_top++] = raid_devs_str;
861 : } else
862 5 : argv[argv_top++] = mdadm_spec;
863 :
864 5 : argv[argv_top++] = "--add";
865 5 : argv[argv_top] = device;
866 :
867 5 : ret = bd_utils_exec_and_report_error (argv, extra, error);
868 5 : g_free (mdadm_spec);
869 5 : g_free (raid_devs_str);
870 :
871 5 : return ret;
872 : }
873 :
874 : /**
875 : * bd_md_remove:
876 : * @raid_spec: specification of the RAID device (name, node or path) to remove @device from
877 : * @device: device to remove from the @raid_spec RAID
878 : * @fail: whether to mark the @device as failed before removing
879 : * @extra: (nullable) (array zero-terminated=1): extra options for the removal (right now
880 : * passed to the 'mdadm' utility)
881 : * @error: (out) (optional): place to store error (if any)
882 : *
883 : * Returns: whether the @device was successfully removed from the @raid_spec
884 : * RAID or not.
885 : *
886 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
887 : */
888 2 : gboolean bd_md_remove (const gchar *raid_spec, const gchar *device, gboolean fail, const BDExtraArg **extra, GError **error) {
889 2 : const gchar *argv[] = {"mdadm", NULL, NULL, NULL, NULL, NULL, NULL};
890 2 : guint argv_top = 2;
891 2 : gchar *mdadm_spec = NULL;
892 2 : gboolean ret = FALSE;
893 2 : gchar *dev_path = NULL;
894 :
895 2 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
896 0 : return FALSE;
897 :
898 2 : mdadm_spec = get_mdadm_spec_from_input (raid_spec, error);
899 2 : if (!mdadm_spec)
900 : /* error is already populated */
901 0 : return FALSE;
902 :
903 2 : argv[1] = mdadm_spec;
904 :
905 2 : dev_path = bd_utils_resolve_device (device, error);
906 2 : if (!dev_path) {
907 : /* error is populated */
908 0 : g_free (mdadm_spec);
909 0 : return FALSE;
910 : }
911 :
912 2 : if (fail) {
913 1 : argv[argv_top++] = "--fail";
914 1 : argv[argv_top++] = dev_path;
915 : }
916 :
917 2 : argv[argv_top++] = "--remove";
918 2 : argv[argv_top++] = dev_path;
919 :
920 2 : ret = bd_utils_exec_and_report_error (argv, extra, error);
921 2 : g_free (dev_path);
922 2 : g_free (mdadm_spec);
923 :
924 2 : return ret;
925 : }
926 :
927 : /**
928 : * bd_md_examine:
929 : * @device: name of the device (a member of an MD RAID) to examine
930 : * @error: (out) (optional): place to store error (if any)
931 : *
932 : * Returns: information about the MD RAID extracted from the @device
933 : *
934 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_QUERY
935 : */
936 8 : BDMDExamineData* bd_md_examine (const gchar *device, GError **error) {
937 8 : const gchar *argv[] = {"mdadm", "--examine", "-E", device, NULL};
938 8 : gchar *output = NULL;
939 8 : gboolean success = FALSE;
940 8 : GHashTable *table = NULL;
941 8 : guint num_items = 0;
942 8 : BDMDExamineData *ret = NULL;
943 8 : gchar *value = NULL;
944 8 : gchar **output_fields = NULL;
945 8 : gchar *orig_data = NULL;
946 8 : guint i = 0;
947 8 : gboolean found_array_line = FALSE;
948 :
949 8 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
950 0 : return NULL;
951 :
952 8 : success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
953 8 : if (!success)
954 : /* error is already populated */
955 0 : return NULL;
956 :
957 8 : table = parse_mdadm_vars (output, "\n", ":", &num_items);
958 8 : g_free (output);
959 8 : if (!table || (num_items == 0)) {
960 : /* something bad happened */
961 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_PARSE, "Failed to parse mdexamine data");
962 0 : if (table)
963 0 : g_hash_table_destroy (table);
964 0 : return NULL;
965 : }
966 :
967 8 : ret = get_examine_data_from_table (table, TRUE);
968 8 : if (!ret) {
969 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_PARSE, "Failed to get mdexamine data");
970 0 : if (table)
971 0 : g_hash_table_destroy (table);
972 0 : return NULL;
973 : }
974 :
975 : /* canonicalize UUIDs (as long as we got them) */
976 8 : orig_data = ret->uuid;
977 8 : if (orig_data) {
978 6 : ret->uuid = bd_md_canonicalize_uuid (orig_data, error);
979 6 : if (!ret->uuid) {
980 0 : g_prefix_error (error, "Failed to canonicalize MD UUID '%s': ", orig_data);
981 0 : g_free (orig_data);
982 0 : bd_md_examine_data_free (ret);
983 0 : return NULL;
984 : }
985 6 : g_free (orig_data);
986 : }
987 :
988 8 : orig_data = ret->dev_uuid;
989 8 : if (orig_data) {
990 4 : ret->dev_uuid = bd_md_canonicalize_uuid (orig_data, error);
991 4 : if (!ret->dev_uuid) {
992 0 : g_prefix_error (error, "Failed to canonicalize MD UUID '%s': ", orig_data);
993 0 : g_free (orig_data);
994 0 : bd_md_examine_data_free (ret);
995 0 : return NULL;
996 : }
997 4 : g_free (orig_data);
998 : }
999 :
1000 8 : argv[2] = "--export";
1001 8 : success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
1002 8 : if (!success) {
1003 : /* error is already populated */
1004 0 : bd_md_examine_data_free (ret);
1005 0 : return NULL;
1006 : }
1007 :
1008 : /* try to get a better information about RAID level because it may be
1009 : misleading in the output without --export */
1010 8 : output_fields = g_strsplit (output, "\n", 0);
1011 8 : g_free (output);
1012 8 : output = NULL;
1013 55 : for (i=0; (i < g_strv_length (output_fields) - 1); i++)
1014 47 : if (g_str_has_prefix (output_fields[i], "MD_LEVEL=")) {
1015 8 : value = strchr (output_fields[i], '=');
1016 8 : value++;
1017 8 : g_free (ret->level);
1018 8 : ret->level = g_strdup (value);
1019 39 : } else if (!ret->uuid && g_str_has_prefix (output_fields[i], "MD_UUID=")) {
1020 2 : value = strchr (output_fields[i], '=');
1021 2 : value++;
1022 2 : ret->uuid = bd_md_canonicalize_uuid (value, error);
1023 2 : if (!ret->uuid) {
1024 0 : g_prefix_error (error, "Failed to canonicalize MD UUID '%s': ", value);
1025 0 : bd_md_examine_data_free (ret);
1026 0 : g_strfreev (output_fields);
1027 0 : return NULL;
1028 : }
1029 : }
1030 8 : g_strfreev (output_fields);
1031 :
1032 8 : argv[2] = "--brief";
1033 8 : success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
1034 8 : if (!success) {
1035 : /* error is already populated */
1036 0 : bd_md_examine_data_free (ret);
1037 0 : return NULL;
1038 : }
1039 :
1040 : /* try to find the "ARRAY /dev/md/something" pair in the output */
1041 8 : output_fields = g_strsplit_set (output, " \n", 0);
1042 16 : for (i=0; !found_array_line && (i < g_strv_length (output_fields) - 1); i++)
1043 8 : if (g_strcmp0 (output_fields[i], "ARRAY") == 0) {
1044 8 : found_array_line = TRUE;
1045 8 : if (g_str_has_prefix (output_fields[i+1], "/dev/md/")) {
1046 8 : ret->device = g_strdup (output_fields[i+1]);
1047 : } else {
1048 4 : ret->device = NULL;
1049 : }
1050 : }
1051 8 : if (!found_array_line)
1052 0 : ret->device = NULL;
1053 8 : g_strfreev (output_fields);
1054 :
1055 8 : table = parse_mdadm_vars (output, " ", "=", &num_items);
1056 8 : g_free (output);
1057 8 : if (!table) {
1058 : /* something bad happened or some expected items were missing */
1059 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_PARSE,
1060 : "Failed to parse mdexamine metadata");
1061 0 : g_hash_table_destroy (table);
1062 0 : bd_md_examine_data_free (ret);
1063 0 : return NULL;
1064 : }
1065 :
1066 : /* try to get metadata version from the output (may be missing) */
1067 8 : g_free (ret->metadata);
1068 8 : value = (gchar*) g_hash_table_lookup (table, "metadata");
1069 8 : if (value)
1070 7 : ret->metadata = g_strdup (value);
1071 : else
1072 1 : ret->metadata = NULL;
1073 8 : g_hash_table_destroy (table);
1074 :
1075 8 : return ret;
1076 : }
1077 :
1078 : /**
1079 : * bd_md_detail:
1080 : * @raid_spec: specification of the RAID device (name, node or path) to examine
1081 : * @error: (out) (optional): place to store error (if any)
1082 : *
1083 : * Returns: information about the MD RAID @raid_spec
1084 : *
1085 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_QUERY
1086 : */
1087 17 : BDMDDetailData* bd_md_detail (const gchar *raid_spec, GError **error) {
1088 17 : const gchar *argv[] = {"mdadm", "--detail", NULL, NULL, NULL};
1089 17 : gchar *output = NULL;
1090 17 : gboolean success = FALSE;
1091 17 : GHashTable *table = NULL;
1092 17 : guint num_items = 0;
1093 17 : gchar *orig_uuid = NULL;
1094 17 : g_autofree gchar *mdadm_spec = NULL;
1095 17 : gchar *value = NULL;
1096 17 : gchar **output_fields = NULL;
1097 17 : guint i = 0;
1098 17 : BDMDDetailData *ret = NULL;
1099 :
1100 17 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
1101 0 : return NULL;
1102 :
1103 17 : mdadm_spec = get_mdadm_spec_from_input (raid_spec, error);
1104 17 : if (!mdadm_spec)
1105 : /* error is already populated */
1106 1 : return NULL;
1107 :
1108 16 : argv[2] = mdadm_spec;
1109 :
1110 16 : success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
1111 16 : if (!success)
1112 : /* error is already populated */
1113 4 : return NULL;
1114 :
1115 12 : table = parse_mdadm_vars (output, "\n", ":", &num_items);
1116 12 : g_free (output);
1117 12 : if (!table || (num_items == 0)) {
1118 : /* something bad happened or some expected items were missing */
1119 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_PARSE, "Failed to parse mddetail data");
1120 0 : if (table)
1121 0 : g_hash_table_destroy (table);
1122 0 : return NULL;
1123 : }
1124 :
1125 12 : ret = get_detail_data_from_table (table, TRUE);
1126 12 : if (!ret) {
1127 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_PARSE, "Failed to get mddetail data");
1128 0 : return NULL;
1129 : }
1130 :
1131 12 : ret->device = g_strdup (argv[2]);
1132 :
1133 12 : orig_uuid = ret->uuid;
1134 12 : if (orig_uuid) {
1135 10 : ret->uuid = bd_md_canonicalize_uuid (orig_uuid, error);
1136 10 : g_free (orig_uuid);
1137 : }
1138 :
1139 12 : argv[2] = "--export";
1140 12 : argv[3] = mdadm_spec;
1141 12 : success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
1142 12 : if (!success) {
1143 : /* error is already populated */
1144 0 : bd_md_detail_data_free (ret);
1145 0 : return NULL;
1146 : }
1147 :
1148 : /* try to get a better information about RAID level because it may be
1149 : missing in the output without --export */
1150 12 : output_fields = g_strsplit (output, "\n", 0);
1151 12 : g_free (output);
1152 12 : output = NULL;
1153 156 : for (i = 0; (i < g_strv_length (output_fields) - 1); i++) {
1154 144 : if (!ret->uuid && g_str_has_prefix (output_fields[i], "MD_UUID=")) {
1155 2 : value = strchr (output_fields[i], '=');
1156 2 : value++;
1157 2 : ret->uuid = bd_md_canonicalize_uuid (value, error);
1158 2 : if (!ret->uuid) {
1159 0 : g_prefix_error (error, "Failed to canonicalize MD UUID '%s': ", value);
1160 0 : bd_md_detail_data_free (ret);
1161 0 : g_strfreev (output_fields);
1162 0 : return NULL;
1163 : }
1164 142 : } else if (g_str_has_prefix (output_fields[i], "MD_CONTAINER=")) {
1165 1 : value = strchr (output_fields[i], '=');
1166 1 : value++;
1167 1 : ret->container = g_strdup (value);
1168 : }
1169 : }
1170 12 : g_strfreev (output_fields);
1171 :
1172 12 : return ret;
1173 : }
1174 :
1175 : /**
1176 : * bd_md_canonicalize_uuid:
1177 : * @uuid: UUID to canonicalize
1178 : * @error: (out) (optional): place to store error (if any)
1179 : *
1180 : * Returns: (transfer full): canonicalized form of @uuid or %NULL in case of error
1181 : *
1182 : * This function expects a UUID in the form that mdadm returns. The change is as
1183 : * follows: 3386ff85:f5012621:4a435f06:1eb47236 -> 3386ff85-f501-2621-4a43-5f061eb47236
1184 : *
1185 : * Tech category: always available
1186 : */
1187 28 : gchar* bd_md_canonicalize_uuid (const gchar *uuid, GError **error) {
1188 28 : const gchar *next_set = uuid;
1189 28 : gchar *ret = g_new0 (gchar, 37);
1190 28 : gchar *dest = ret;
1191 28 : GRegex *regex = NULL;
1192 :
1193 28 : regex = g_regex_new ("[0-9a-f]{8}:[0-9a-f]{8}:[0-9a-f]{8}:[0-9a-f]{8}", 0, 0, error);
1194 28 : if (!regex) {
1195 : /* error is already populated */
1196 0 : g_free (ret);
1197 0 : return NULL;
1198 : }
1199 :
1200 28 : if (!g_regex_match (regex, uuid, 0, NULL)) {
1201 1 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_BAD_FORMAT,
1202 : "malformed or invalid UUID: %s", uuid);
1203 1 : g_regex_unref (regex);
1204 1 : g_free (ret);
1205 1 : return NULL;
1206 : }
1207 27 : g_regex_unref (regex);
1208 :
1209 : /* first 8 symbols */
1210 27 : memcpy (dest, next_set, 8);
1211 27 : next_set += 9;
1212 27 : dest += 8;
1213 27 : *dest = '-';
1214 27 : dest++;
1215 :
1216 : /* 4 symbols from the second 8 */
1217 27 : memcpy (dest, next_set, 4);
1218 27 : next_set += 4;
1219 27 : dest += 4;
1220 27 : *dest = '-';
1221 27 : dest++;
1222 :
1223 : /* 4 symbols from the second 8 */
1224 27 : memcpy (dest, next_set, 4);
1225 27 : next_set += 5;
1226 27 : dest += 4;
1227 27 : *dest = '-';
1228 27 : dest++;
1229 :
1230 : /* 4 symbols from the third 8 */
1231 27 : memcpy (dest, next_set, 4);
1232 27 : next_set += 4;
1233 27 : dest += 4;
1234 27 : *dest = '-';
1235 27 : dest++;
1236 :
1237 : /* 4 symbols from the third 8 with no trailing - */
1238 27 : memcpy (dest, next_set, 4);
1239 27 : next_set += 5;
1240 27 : dest += 4;
1241 :
1242 : /* 9 symbols (8 + \0) from the fourth 8 */
1243 27 : memcpy (dest, next_set, 9);
1244 :
1245 27 : return ret;
1246 : }
1247 :
1248 : /**
1249 : * bd_md_get_md_uuid:
1250 : * @uuid: UUID to transform into format used by MD RAID
1251 : * @error: (out) (optional): place to store error (if any)
1252 : *
1253 : * Returns: (transfer full): transformed form of @uuid or %NULL in case of error
1254 : *
1255 : * This function expects a UUID in the canonical (traditional format) and
1256 : * returns a UUID in the format used by MD RAID and is thus reverse to
1257 : * bd_md_canonicalize_uuid(). The change is as follows:
1258 : * 3386ff85-f501-2621-4a43-5f061eb47236 -> 3386ff85:f5012621:4a435f06:1eb47236
1259 : *
1260 : * Tech category: always available
1261 : */
1262 3 : gchar* bd_md_get_md_uuid (const gchar *uuid, GError **error) {
1263 3 : const gchar *next_set = uuid;
1264 3 : gchar *ret = g_new0 (gchar, 37);
1265 3 : gchar *dest = ret;
1266 3 : GRegex *regex = NULL;
1267 :
1268 3 : regex = g_regex_new ("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", 0, 0, error);
1269 3 : if (!regex) {
1270 : /* error is already populated */
1271 0 : g_free (ret);
1272 0 : return NULL;
1273 : }
1274 :
1275 3 : if (!g_regex_match (regex, uuid, 0, NULL)) {
1276 1 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_BAD_FORMAT,
1277 : "malformed or invalid UUID: %s", uuid);
1278 1 : g_regex_unref (regex);
1279 1 : g_free (ret);
1280 1 : return NULL;
1281 : }
1282 2 : g_regex_unref (regex);
1283 :
1284 : /* first 8 symbols */
1285 2 : memcpy (dest, next_set, 8);
1286 2 : next_set += 9;
1287 2 : dest += 8;
1288 2 : *dest = ':';
1289 2 : dest++;
1290 :
1291 : /* 4 symbols from the 4 */
1292 2 : memcpy (dest, next_set, 4);
1293 2 : next_set += 5;
1294 2 : dest += 4;
1295 :
1296 : /* 4 symbols from the second 4 plus :*/
1297 2 : memcpy (dest, next_set, 4);
1298 2 : next_set += 5;
1299 2 : dest += 4;
1300 2 : *dest = ':';
1301 2 : dest++;
1302 :
1303 : /* 4 symbols from the third 4 */
1304 2 : memcpy (dest, next_set, 4);
1305 2 : next_set += 5;
1306 2 : dest += 4;
1307 :
1308 : /* 4 symbols from the 12 */
1309 2 : memcpy (dest, next_set, 4);
1310 2 : next_set += 4;
1311 2 : dest += 4;
1312 2 : *dest = ':';
1313 2 : dest++;
1314 :
1315 : /* 9 symbols (8 + \0) from the 12 */
1316 2 : memcpy (dest, next_set, 9);
1317 :
1318 2 : return ret;
1319 : }
1320 :
1321 : /**
1322 : * bd_md_node_from_name:
1323 : * @name: name of the MD RAID
1324 : * @error: (out) (optional): place to store error (if any)
1325 : *
1326 : * Returns: device node of the @name MD RAID or %NULL in case of error
1327 : *
1328 : * Tech category: always available
1329 : */
1330 37 : gchar* bd_md_node_from_name (const gchar *name, GError **error) {
1331 37 : gchar *dev_path = NULL;
1332 37 : gchar *ret = NULL;
1333 37 : gchar *md_path = g_strdup_printf ("/dev/md/%s", name);
1334 :
1335 37 : dev_path = bd_utils_resolve_device (md_path, error);
1336 37 : g_free (md_path);
1337 37 : if (!dev_path)
1338 : /* error is already populated */
1339 17 : return NULL;
1340 :
1341 20 : ret = g_path_get_basename (dev_path);
1342 20 : g_free (dev_path);
1343 :
1344 20 : return ret;
1345 : }
1346 :
1347 : /**
1348 : * bd_md_name_from_node:
1349 : * @node: path of the MD RAID's device node
1350 : * @error: (out) (optional): place to store error (if any)
1351 : *
1352 : * Returns: @name of the MD RAID the device node belongs to or %NULL in case of error
1353 : *
1354 : * Tech category: always available
1355 : */
1356 3 : gchar* bd_md_name_from_node (const gchar *node, GError **error) {
1357 : glob_t glob_buf;
1358 : gchar **path_p;
1359 3 : gboolean found = FALSE;
1360 3 : gchar *dev_path = NULL;
1361 3 : gchar *name = NULL;
1362 3 : gchar *node_name = NULL;
1363 :
1364 : /* get rid of the "/dev/" prefix (if any) */
1365 3 : if (g_str_has_prefix (node, "/dev/"))
1366 1 : node = node + 5;
1367 :
1368 3 : if (glob ("/dev/md/*", GLOB_NOSORT, NULL, &glob_buf) != 0) {
1369 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_NO_MATCH,
1370 : "No name found for the node '%s'", node);
1371 0 : return NULL;
1372 : }
1373 6 : for (path_p = glob_buf.gl_pathv; *path_p && !found; path_p++) {
1374 3 : dev_path = bd_utils_resolve_device (*path_p, NULL);
1375 3 : if (!dev_path)
1376 0 : continue;
1377 3 : node_name = g_path_get_basename (dev_path);
1378 3 : g_free (dev_path);
1379 3 : if (g_strcmp0 (node_name, node) == 0) {
1380 2 : found = TRUE;
1381 2 : name = g_path_get_basename (*path_p);
1382 : }
1383 3 : g_free (node_name);
1384 : }
1385 3 : globfree (&glob_buf);
1386 :
1387 3 : if (!found)
1388 1 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_NO_MATCH,
1389 : "No name found for the node '%s'", node);
1390 3 : return name;
1391 : }
1392 :
1393 : /**
1394 : * bd_md_get_status
1395 : * @raid_spec: specification of the RAID device (name, node or path) to get status
1396 : * @error: (out) (optional): place to store error (if any)
1397 : *
1398 : * Returns: (transfer full): status of the @raid_spec RAID.
1399 : *
1400 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_QUERY
1401 : */
1402 1 : gchar* bd_md_get_status (const gchar *raid_spec, GError **error) {
1403 1 : gboolean success = FALSE;
1404 1 : gchar *ret = NULL;
1405 1 : gchar *raid_node = NULL;
1406 1 : gchar *sys_path = NULL;
1407 :
1408 1 : raid_node = get_sysfs_name_from_input (raid_spec, error);
1409 1 : if (!raid_node)
1410 : /* error is already populated */
1411 0 : return NULL;
1412 :
1413 1 : sys_path = g_strdup_printf ("/sys/class/block/%s/md/array_state", raid_node);
1414 1 : g_free (raid_node);
1415 :
1416 1 : success = g_file_get_contents (sys_path, &ret, NULL, error);
1417 1 : if (!success) {
1418 : /* error is already populated */
1419 0 : g_free (sys_path);
1420 0 : return NULL;
1421 : }
1422 :
1423 1 : g_free (sys_path);
1424 :
1425 1 : return g_strstrip (ret);
1426 : }
1427 :
1428 : /**
1429 : * bd_md_set_bitmap_location:
1430 : * @raid_spec: specification of the RAID device (name, node or path) to set the bitmap location
1431 : * @location: bitmap location (none, internal or path)
1432 : * @error: (out) (optional): place to store error (if any)
1433 : *
1434 : * Returns: whether @location was successfully set for @raid_spec
1435 : *
1436 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
1437 : */
1438 5 : gboolean bd_md_set_bitmap_location (const gchar *raid_spec, const gchar *location, GError **error) {
1439 5 : const gchar *argv[] = {"mdadm", "--grow", NULL, "--bitmap", location, NULL};
1440 5 : gchar* mdadm_spec = NULL;
1441 5 : gboolean ret = FALSE;
1442 :
1443 5 : if (!check_deps (&avail_deps, DEPS_MDADM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
1444 0 : return FALSE;
1445 :
1446 5 : mdadm_spec = get_mdadm_spec_from_input (raid_spec, error);
1447 5 : if (!mdadm_spec)
1448 : /* error is already populated */
1449 0 : return FALSE;
1450 :
1451 5 : argv[2] = mdadm_spec;
1452 :
1453 5 : if ((g_strcmp0 (location, "none") != 0) && (g_strcmp0 (location, "internal") != 0) &&
1454 0 : !g_str_has_prefix (location , "/")) {
1455 :
1456 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_INVAL,
1457 : "Bitmap location must start with '/' or be 'internal' or 'none'.");
1458 0 : g_free (mdadm_spec);
1459 0 : return FALSE;
1460 : }
1461 :
1462 5 : ret = bd_utils_exec_and_report_error (argv, NULL, error);
1463 :
1464 5 : g_free (mdadm_spec);
1465 :
1466 5 : return ret;
1467 : }
1468 :
1469 : /**
1470 : * bd_md_get_bitmap_location:
1471 : * @raid_spec: specification of the RAID device (name, node or path) to get the bitmap location
1472 : * @error: (out) (optional): place to store error (if any)
1473 : *
1474 : * Returns: (transfer full): bitmap location for @raid_spec
1475 : *
1476 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_QUERY
1477 : */
1478 7 : gchar* bd_md_get_bitmap_location (const gchar *raid_spec, GError **error) {
1479 7 : g_autofree gchar *raid_node = NULL;
1480 7 : g_autofree gchar *sys_path = NULL;
1481 7 : gchar *ret = NULL;
1482 7 : gboolean success = FALSE;
1483 7 : GError *l_error = NULL;
1484 :
1485 7 : raid_node = get_sysfs_name_from_input (raid_spec, error);
1486 7 : if (!raid_node)
1487 : /* error is already populated */
1488 0 : return NULL;
1489 :
1490 7 : sys_path = g_strdup_printf ("/sys/class/block/%s/md/bitmap/location", raid_node);
1491 :
1492 7 : success = g_file_get_contents (sys_path, &ret, NULL, &l_error);
1493 7 : if (!success) {
1494 0 : if (g_error_matches (l_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
1495 0 : g_clear_error (&l_error);
1496 0 : return g_strdup ("none");
1497 : } else {
1498 0 : g_propagate_prefixed_error (error, l_error, "Failed to get bitmap location: ");
1499 0 : return NULL;
1500 : }
1501 : }
1502 :
1503 7 : return g_strstrip (ret);
1504 : }
1505 :
1506 : /**
1507 : * bd_md_request_sync_action:
1508 : * @raid_spec: specification of the RAID device (name, node or path) to request sync action on
1509 : * @action: requested sync action (resync, recovery, check, repair or idle)
1510 : * @error: (out) (optional): place to store error (if any)
1511 : *
1512 : * Returns: whether the @action was successfully requested for the @raid_spec
1513 : * RAID or not.
1514 : *
1515 : * Tech category: %BD_MD_TECH_MDRAID-%BD_MD_TECH_MODE_MODIFY
1516 : */
1517 1 : gboolean bd_md_request_sync_action (const gchar *raid_spec, const gchar *action, GError **error) {
1518 1 : gchar *sys_path = NULL;
1519 1 : gchar *raid_node = NULL;
1520 1 : gboolean success = FALSE;
1521 :
1522 2 : if ((g_strcmp0 (action, "resync") != 0) && (g_strcmp0 (action, "recovery") != 0) &&
1523 1 : (g_strcmp0 (action, "check") != 0) && (g_strcmp0 (action, "repair") != 0) &&
1524 0 : (g_strcmp0 (action, "idle") != 0)) {
1525 :
1526 0 : g_set_error (error, BD_MD_ERROR, BD_MD_ERROR_INVAL,
1527 : "Action must be one of resync, recovery, check, repair or idle.");
1528 0 : return FALSE;
1529 : }
1530 :
1531 1 : raid_node = get_sysfs_name_from_input (raid_spec, error);
1532 1 : if (!raid_node)
1533 : /* error is already populated */
1534 0 : return FALSE;
1535 :
1536 1 : sys_path = g_strdup_printf ("/sys/class/block/%s/md/sync_action", raid_node);
1537 1 : g_free (raid_node);
1538 :
1539 1 : success = bd_utils_echo_str_to_file (action, sys_path, error);
1540 1 : g_free (sys_path);
1541 1 : if (!success) {
1542 0 : g_prefix_error (error, "Failed to set requested sync action.");
1543 0 : return FALSE;
1544 : }
1545 :
1546 1 : return TRUE;
1547 : }
|