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

Generated by: LCOV version 2.0-1