LCOV - code coverage report
Current view: top level - plugins - mdraid.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 79.1 % 789 624
Test Date: 2026-01-26 13:19:28 Functions: 81.8 % 33 27
Legend: Lines: hit not hit

            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           26 : gboolean bd_md_init (void) {
     167              :     /* nothing to do here */
     168           26 :     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           26 : void bd_md_close (void) {
     179              :     /* nothing to do here */
     180           26 : }
     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           33 :           mdadm_spec = g_strdup (name_path_str);
     490           35 :       else if (access (md_path_str, F_OK) == 0)
     491            7 :           mdadm_spec = g_strdup (md_path_str);
     492              :       else
     493           28 :           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              : }
        

Generated by: LCOV version 2.0-1