LCOV - code coverage report
Current view: top level - plugins/lvm - lvm-common.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 72.2 % 367 265
Test Date: 2026-01-26 13:19:28 Functions: 100.0 % 31 31
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2014-2025  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              :  */
      18              : 
      19              : #include <glib.h>
      20              : #include <math.h>
      21              : #include <stdio.h>
      22              : #include <libdevmapper.h>
      23              : 
      24              : #include "lvm.h"
      25              : #include "lvm-private.h"
      26              : #include "check_deps.h"
      27              : #include "dm_logging.h"
      28              : #include "vdo_stats.h"
      29              : 
      30              : 
      31              : #define INT_FLOAT_EPS 1e-5
      32              : 
      33              : #define MIN_PE_SIZE (1 KiB)
      34              : #define MAX_PE_SIZE (16 GiB)
      35              : 
      36              : #define VDO_POOL_SUFFIX "vpool"
      37              : 
      38              : #define THPOOL_MD_FACTOR_NEW (0.2)
      39              : #define THPOOL_MD_FACTOR_EXISTS (1 / 6.0)
      40              : 
      41              : #define MIN_THPOOL_MD_SIZE (4 MiB)
      42              : /* DM_THIN_MAX_METADATA_SIZE is in 512 sectors */
      43              : #define MAX_THPOOL_MD_SIZE (DM_THIN_MAX_METADATA_SIZE * 512)
      44              : 
      45              : #define MIN_THPOOL_CHUNK_SIZE (64 KiB)
      46              : #define MAX_THPOOL_CHUNK_SIZE (1 GiB)
      47              : #define DEFAULT_CHUNK_SIZE (64 KiB)
      48              : 
      49              : /* according to lvmcache (7) */
      50              : #define MIN_CACHE_MD_SIZE (8 MiB)
      51              : 
      52              : #ifdef __LP64__
      53              : /* 64bit system */
      54              : #define MAX_LV_SIZE (8 EiB)
      55              : #else
      56              : /* 32bit system */
      57              : #define MAX_LV_SIZE (16 TiB)
      58              : #endif
      59              : 
      60              : GMutex global_config_lock;
      61              : gchar *global_config_str = NULL;
      62              : gchar *global_devices_str = NULL;
      63              : 
      64              : /**
      65              :  * bd_lvm_is_supported_pe_size:
      66              :  * @size: size (in bytes) to test
      67              :  * @error: (out) (optional): place to store error (if any)
      68              :  *
      69              :  * Returns: whether the given size is supported physical extent size or not
      70              :  *
      71              :  * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
      72              :  */
      73           71 : gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error G_GNUC_UNUSED) {
      74           71 :     return (((size % 2) == 0) && (size >= (MIN_PE_SIZE)) && (size <= (MAX_PE_SIZE)));
      75              : }
      76              : 
      77              : /**
      78              :  * bd_lvm_get_supported_pe_sizes:
      79              :  * @error: (out) (optional): place to store error (if any)
      80              :  *
      81              :  * Returns: (transfer full) (array fixed-size=25): list of supported PE sizes
      82              :  *
      83              :  * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
      84              :  */
      85            2 : guint64 *bd_lvm_get_supported_pe_sizes (GError **error G_GNUC_UNUSED) {
      86              :     guint8 i;
      87            2 :     guint64 val = MIN_PE_SIZE;
      88            2 :     guint8 num_items = ((guint8) round (log2 ((double) MAX_PE_SIZE))) - ((guint8) round (log2 ((double) MIN_PE_SIZE))) + 2;
      89            2 :     guint64 *ret = g_new0 (guint64, num_items);
      90              : 
      91           52 :     for (i=0; (val <= MAX_PE_SIZE); i++, val = val * 2)
      92           50 :         ret[i] = val;
      93              : 
      94            2 :     ret[num_items-1] = 0;
      95              : 
      96            2 :     return ret;
      97              : }
      98              : 
      99              : /**
     100              :  * bd_lvm_get_max_lv_size:
     101              :  * @error: (out) (optional): place to store error (if any)
     102              :  *
     103              :  * Returns: maximum LV size in bytes
     104              :  *
     105              :  * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
     106              :  */
     107           13 : guint64 bd_lvm_get_max_lv_size (GError **error G_GNUC_UNUSED) {
     108           13 :     return MAX_LV_SIZE;
     109              : }
     110              : 
     111              : /**
     112              :  * bd_lvm_round_size_to_pe:
     113              :  * @size: size to be rounded
     114              :  * @pe_size: physical extent (PE) size or 0 to use the default
     115              :  * @roundup: whether to round up or down (ceil or floor)
     116              :  * @error: (out) (optional): place to store error (if any)
     117              :  *
     118              :  * Returns: @size rounded to @pe_size according to the @roundup
     119              :  *
     120              :  * Rounds given @size up/down to a multiple of @pe_size according to the value
     121              :  * of the @roundup parameter. If the rounded value is too big to fit in the
     122              :  * return type, the result is rounded down (floored) regardless of the @roundup
     123              :  * parameter.
     124              :  *
     125              :  * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
     126              :  */
     127           47 : guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error G_GNUC_UNUSED) {
     128           47 :     pe_size = RESOLVE_PE_SIZE (pe_size);
     129           47 :     guint64 delta = size % pe_size;
     130           47 :     if (delta == 0)
     131            9 :         return size;
     132              : 
     133           38 :     if (roundup && (((G_MAXUINT64 - (pe_size - delta)) >= size)))
     134           26 :         return size + (pe_size - delta);
     135              :     else
     136           12 :         return size - delta;
     137              : }
     138              : 
     139              : /**
     140              :  * bd_lvm_get_lv_physical_size:
     141              :  * @lv_size: LV size
     142              :  * @pe_size: PE size
     143              :  * @error: (out) (optional): place to store error (if any)
     144              :  *
     145              :  * Returns: space taken on disk(s) by the LV with given @size
     146              :  *
     147              :  * Gives number of bytes needed for an LV with the size @lv_size on an LVM stack
     148              :  * using given @pe_size.
     149              :  *
     150              :  * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
     151              :  */
     152            6 : guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error) {
     153            6 :     pe_size = RESOLVE_PE_SIZE (pe_size);
     154              : 
     155              :     /* the LV just takes space rounded up to the multiple of extent size */
     156            6 :     return bd_lvm_round_size_to_pe (lv_size, pe_size, TRUE, error);
     157              : }
     158              : 
     159              : /**
     160              :  * bd_lvm_get_thpool_padding:
     161              :  * @size: size of the thin pool
     162              :  * @pe_size: PE size or 0 if the default value should be used
     163              :  * @included: if padding is already included in the size
     164              :  * @error: (out) (optional): place to store error (if any)
     165              :  *
     166              :  * Returns: size of the padding needed for a thin pool with the given @size
     167              :  *         according to the @pe_size and @included
     168              :  *
     169              :  * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
     170              :  */
     171            5 : guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error) {
     172              :     guint64 raw_md_size;
     173            5 :     pe_size = RESOLVE_PE_SIZE (pe_size);
     174              : 
     175            5 :     if (included)
     176            2 :         raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_EXISTS);
     177              :     else
     178            3 :         raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_NEW);
     179              : 
     180            5 :     return MIN (bd_lvm_round_size_to_pe (raw_md_size, pe_size, TRUE, error),
     181              :                 bd_lvm_round_size_to_pe (MAX_THPOOL_MD_SIZE, pe_size, TRUE, error));
     182              : }
     183              : 
     184              : /**
     185              :  * bd_lvm_get_thpool_meta_size:
     186              :  * @size: size of the thin pool
     187              :  * @chunk_size: chunk size of the thin pool or 0 to use the default
     188              :  * @n_snapshots: ignored
     189              :  * @error: (out) (optional): place to store error (if any)
     190              :  *
     191              :  * Note: This function will be changed in 3.0: the @n_snapshots parameter
     192              :  *       is currently not used and will be removed.
     193              :  *
     194              :  * Returns: recommended size of the metadata space for the specified pool
     195              :  *
     196              :  * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
     197              :  */
     198            6 : guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) {
     199            6 :     guint64 md_size = 0;
     200              : 
     201              :     /* based on lvcreate metadata size calculation */
     202            6 :     md_size = UINT64_C (64) * size / (chunk_size ? chunk_size : DEFAULT_CHUNK_SIZE);
     203              : 
     204            6 :     if (md_size > MAX_THPOOL_MD_SIZE)
     205            0 :         md_size = MAX_THPOOL_MD_SIZE;
     206            6 :     else if (md_size < MIN_THPOOL_MD_SIZE)
     207            2 :         md_size = MIN_THPOOL_MD_SIZE;
     208              : 
     209            6 :     return md_size;
     210              : }
     211              : 
     212              : /**
     213              :  * bd_lvm_is_valid_thpool_md_size:
     214              :  * @size: the size to be tested
     215              :  * @error: (out) (optional): place to store error (if any)
     216              :  *
     217              :  * Returns: whether the given size is a valid thin pool metadata size or not
     218              :  *
     219              :  * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
     220              :  */
     221           14 : gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error G_GNUC_UNUSED) {
     222           14 :     return ((MIN_THPOOL_MD_SIZE <= size) && (size <= MAX_THPOOL_MD_SIZE));
     223              : }
     224              : 
     225              : /**
     226              :  * bd_lvm_is_valid_thpool_chunk_size:
     227              :  * @size: the size to be tested
     228              :  * @discard: whether discard/TRIM is required to be supported or not
     229              :  * @error: (out) (optional): place to store error (if any)
     230              :  *
     231              :  * Returns: whether the given size is a valid thin pool chunk size or not
     232              :  *
     233              :  * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
     234              :  */
     235           12 : gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error G_GNUC_UNUSED) {
     236           12 :     gdouble size_log2 = 0.0;
     237              : 
     238           12 :     if ((size < MIN_THPOOL_CHUNK_SIZE) || (size > MAX_THPOOL_CHUNK_SIZE))
     239            0 :         return FALSE;
     240              : 
     241              :     /* To support discard, chunk size must be a power of two. Otherwise it must be a
     242              :        multiple of 64 KiB. */
     243           12 :     if (discard) {
     244            6 :         size_log2 = log2 ((double) size);
     245            6 :         return ABS (((int) round (size_log2)) - size_log2) <= INT_FLOAT_EPS;
     246              :     } else
     247            6 :         return (size % (64 KiB)) == 0;
     248              : }
     249              : 
     250              : /**
     251              :  * bd_lvm_cache_get_default_md_size:
     252              :  * @cache_size: size of the cache to determine MD size for
     253              :  * @error: (out) (optional): place to store error (if any)
     254              :  *
     255              :  * Returns: recommended default size of the cache metadata LV or 0 in case of error
     256              :  *
     257              :  * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
     258              :  */
     259           20 : guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error G_GNUC_UNUSED) {
     260           20 :     return MAX ((guint64) cache_size / 1000, MIN_CACHE_MD_SIZE);
     261              : }
     262              : 
     263              : /**
     264              :  * bd_lvm_set_global_config:
     265              :  * @new_config: (nullable): string representation of the new global libblockdev LVM
     266              :  *                          configuration to set or %NULL to reset to default
     267              :  * @error: (out) (optional): place to store error (if any)
     268              :  *
     269              :  *
     270              :  * Note: This function sets configuration options for LVM calls internally
     271              :  *       in libblockdev, it doesn't change the global lvm.conf config file.
     272              :  *       Calling this function with `backup {backup=0 archive=0}` for example
     273              :  *       means `--config=backup {backup=0 archive=0}"` will be added to all
     274              :  *       calls libblockdev makes.
     275              :  *
     276              :  * Returns: whether the new requested global config @new_config was successfully
     277              :  *          set or not
     278              :  *
     279              :  * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
     280              :  */
     281           34 : gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error G_GNUC_UNUSED) {
     282              :     /* XXX: the error attribute will likely be used in the future when
     283              :        some validation comes into the game */
     284              : 
     285           34 :     g_mutex_lock (&global_config_lock);
     286              : 
     287              :     /* first free the old value */
     288           34 :     g_free (global_config_str);
     289              : 
     290              :     /* now store the new one */
     291           34 :     if (!new_config || g_strcmp0 (new_config, "") == 0)
     292           18 :          global_config_str = NULL;
     293              :     else
     294           16 :         global_config_str = g_strdup (new_config);
     295              : 
     296           34 :     g_mutex_unlock (&global_config_lock);
     297           34 :     return TRUE;
     298              : }
     299              : 
     300              : /**
     301              :  * bd_lvm_get_global_config:
     302              :  * @error: (out) (optional): place to store error (if any)
     303              :  *
     304              :  * Returns: (transfer full): a copy of a string representation of the currently
     305              :  *                           set libblockdev LVM global configuration
     306              :  *
     307              :  * Note: This function does not change the global `lvm.conf` config
     308              :  *       file, see %bd_lvm_set_global_config for details.
     309              :  *
     310              :  * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
     311              :  */
     312           10 : gchar* bd_lvm_get_global_config (GError **error G_GNUC_UNUSED) {
     313           10 :     gchar *ret = NULL;
     314              : 
     315           10 :     g_mutex_lock (&global_config_lock);
     316           10 :     ret = g_strdup (global_config_str ? global_config_str : "");
     317           10 :     g_mutex_unlock (&global_config_lock);
     318              : 
     319           10 :     return ret;
     320              : }
     321              : 
     322              : /**
     323              :  * bd_lvm_set_devices_filter:
     324              :  * @devices: (nullable) (array zero-terminated=1): list of devices for lvm commands to work on
     325              :  * @error: (out) (optional): place to store error (if any)
     326              :  *
     327              :  * Returns: whether the devices filter was successfully set or not
     328              :  *
     329              :  * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
     330              :  */
     331           12 : gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
     332           12 :     if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
     333            0 :         return FALSE;
     334              : 
     335           12 :     g_mutex_lock (&global_config_lock);
     336              : 
     337              :     /* first free the old value */
     338           12 :     g_free (global_devices_str);
     339              : 
     340              :     /* now store the new one */
     341           12 :     if (!devices || !(*devices))
     342            4 :         global_devices_str = NULL;
     343              :     else
     344            8 :         global_devices_str = g_strjoinv (",", (gchar **) devices);
     345              : 
     346           12 :     g_mutex_unlock (&global_config_lock);
     347           12 :     return TRUE;
     348              : }
     349              : 
     350              : /**
     351              :  * bd_lvm_get_devices_filter:
     352              :  * @error: (out) (optional): place to store error (if any)
     353              :  *
     354              :  * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
     355              :  *                                                     the currently set LVM devices filter
     356              :  *
     357              :  * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
     358              :  */
     359            8 : gchar** bd_lvm_get_devices_filter (GError **error G_GNUC_UNUSED) {
     360            8 :     gchar **ret = NULL;
     361              : 
     362            8 :     g_mutex_lock (&global_config_lock);
     363              : 
     364            8 :     if (global_devices_str)
     365            4 :         ret = g_strsplit (global_devices_str, ",", -1);
     366              :     else
     367            4 :         ret = NULL;
     368              : 
     369            8 :     g_mutex_unlock (&global_config_lock);
     370              : 
     371            8 :     return ret;
     372              : }
     373              : 
     374              : /**
     375              :  * bd_lvm_cache_get_mode_str:
     376              :  * @mode: mode to get the string representation for
     377              :  * @error: (out) (optional): place to store error (if any)
     378              :  *
     379              :  * Returns: string representation of @mode or %NULL in case of error
     380              :  *
     381              :  * Tech category: always provided/supported
     382              :  */
     383           22 : const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error) {
     384           22 :     if (mode == BD_LVM_CACHE_MODE_WRITETHROUGH)
     385           14 :         return "writethrough";
     386            8 :     else if (mode == BD_LVM_CACHE_MODE_WRITEBACK)
     387            6 :         return "writeback";
     388            2 :     else if (mode == BD_LVM_CACHE_MODE_UNKNOWN)
     389            2 :         return "unknown";
     390              :     else {
     391            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
     392              :                      "Invalid mode given: %d", mode);
     393            0 :         return NULL;
     394              :     }
     395              : }
     396              : 
     397              : /**
     398              :  * bd_lvm_cache_get_mode_from_str:
     399              :  * @mode_str: string representation of a cache mode
     400              :  * @error: (out) (optional): place to store error (if any)
     401              :  *
     402              :  * Returns: cache mode for the @mode_str or %BD_LVM_CACHE_MODE_UNKNOWN if
     403              :  *          failed to determine
     404              :  *
     405              :  * Tech category: always provided/supported
     406              :  */
     407            8 : BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error) {
     408            8 :     if (g_strcmp0 (mode_str, "writethrough") == 0)
     409            2 :         return BD_LVM_CACHE_MODE_WRITETHROUGH;
     410            6 :     else if (g_strcmp0 (mode_str, "writeback") == 0)
     411            2 :         return BD_LVM_CACHE_MODE_WRITEBACK;
     412            4 :     else if (g_strcmp0 (mode_str, "unknown") == 0)
     413            2 :         return BD_LVM_CACHE_MODE_UNKNOWN;
     414              :     else {
     415            2 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
     416              :                      "Invalid mode given: %s", mode_str);
     417            2 :         return BD_LVM_CACHE_MODE_UNKNOWN;
     418              :     }
     419              : }
     420              : 
     421              : /**
     422              :  * bd_lvm_get_vdo_operating_mode_str:
     423              :  * @mode: mode to get the string representation for
     424              :  * @error: (out) (optional): place to store error (if any)
     425              :  *
     426              :  * Returns: string representation of @mode or %NULL in case of error
     427              :  *
     428              :  * Tech category: always provided/supported
     429              :  */
     430            2 : const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error) {
     431            2 :     switch (mode) {
     432            0 :     case BD_LVM_VDO_MODE_RECOVERING:
     433            0 :         return "recovering";
     434            0 :     case BD_LVM_VDO_MODE_READ_ONLY:
     435            0 :         return "read-only";
     436            2 :     case BD_LVM_VDO_MODE_NORMAL:
     437            2 :         return "normal";
     438            0 :     case BD_LVM_VDO_MODE_UNKNOWN:
     439            0 :         return "unknown";
     440            0 :     default:
     441            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
     442              :                      "Invalid LVM VDO operating mode.");
     443            0 :         return NULL;
     444              :     }
     445              : }
     446              : 
     447              : /**
     448              :  * bd_lvm_get_vdo_compression_state_str:
     449              :  * @state: state to get the string representation for
     450              :  * @error: (out) (optional): place to store error (if any)
     451              :  *
     452              :  * Returns: string representation of @state or %NULL in case of error
     453              :  *
     454              :  * Tech category: always provided/supported
     455              :  */
     456            4 : const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error) {
     457            4 :     switch (state) {
     458            4 :     case BD_LVM_VDO_COMPRESSION_ONLINE:
     459            4 :         return "online";
     460            0 :     case BD_LVM_VDO_COMPRESSION_OFFLINE:
     461            0 :         return "offline";
     462            0 :     case BD_LVM_VDO_COMPRESSION_UNKNOWN:
     463            0 :         return "unknown";
     464            0 :     default:
     465            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
     466              :                      "Invalid LVM VDO compression state.");
     467            0 :         return NULL;
     468              :     }
     469              : }
     470              : 
     471              : /**
     472              :  * bd_lvm_get_vdo_index_state_str:
     473              :  * @state: state to get the string representation for
     474              :  * @error: (out) (optional): place to store error (if any)
     475              :  *
     476              :  * Returns: string representation of @state or %NULL in case of error
     477              :  *
     478              :  * Tech category: always provided/supported
     479              :  */
     480            2 : const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error) {
     481            2 :     switch (state) {
     482            0 :     case BD_LVM_VDO_INDEX_ERROR:
     483            0 :         return "error";
     484            0 :     case BD_LVM_VDO_INDEX_CLOSED:
     485            0 :         return "closed";
     486            1 :     case BD_LVM_VDO_INDEX_OPENING:
     487            1 :         return "opening";
     488            0 :     case BD_LVM_VDO_INDEX_CLOSING:
     489            0 :         return "closing";
     490            0 :     case BD_LVM_VDO_INDEX_OFFLINE:
     491            0 :         return "offline";
     492            1 :     case BD_LVM_VDO_INDEX_ONLINE:
     493            1 :         return "online";
     494            0 :     case BD_LVM_VDO_INDEX_UNKNOWN:
     495            0 :         return "unknown";
     496            0 :     default:
     497            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
     498              :                      "Invalid LVM VDO index state.");
     499            0 :         return NULL;
     500              :     }
     501              : }
     502              : 
     503              : /**
     504              :  * bd_lvm_get_vdo_write_policy_str:
     505              :  * @policy: policy to get the string representation for
     506              :  * @error: (out) (optional): place to store error (if any)
     507              :  *
     508              :  * Returns: string representation of @policy or %NULL in case of error
     509              :  *
     510              :  * Tech category: always provided/supported
     511              :  */
     512           20 : const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error) {
     513           20 :     switch (policy) {
     514           16 :     case BD_LVM_VDO_WRITE_POLICY_AUTO:
     515           16 :         return "auto";
     516            4 :     case BD_LVM_VDO_WRITE_POLICY_SYNC:
     517            4 :         return "sync";
     518            0 :     case BD_LVM_VDO_WRITE_POLICY_ASYNC:
     519            0 :         return "async";
     520            0 :     case BD_LVM_VDO_WRITE_POLICY_UNKNOWN:
     521            0 :         return "unknown";
     522            0 :     default:
     523            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
     524              :                      "Invalid LVM VDO write policy.");
     525            0 :         return NULL;
     526              :     }
     527              : }
     528              : 
     529              : /**
     530              :  * bd_lvm_get_vdo_write_policy_from_str:
     531              :  * @policy_str: string representation of a policy
     532              :  * @error: (out) (optional): place to store error (if any)
     533              :  *
     534              :  * Returns: write policy for the @policy_str or %BD_LVM_VDO_WRITE_POLICY_UNKNOWN if
     535              :  *          failed to determine
     536              :  *
     537              :  * Tech category: always provided/supported
     538              :  */
     539            2 : BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error) {
     540            2 :     if (g_strcmp0 (policy_str, "auto") == 0)
     541            0 :         return BD_LVM_VDO_WRITE_POLICY_AUTO;
     542            2 :     else if (g_strcmp0 (policy_str, "sync") == 0)
     543            2 :         return BD_LVM_VDO_WRITE_POLICY_SYNC;
     544            0 :     else if (g_strcmp0 (policy_str, "async") == 0)
     545            0 :         return BD_LVM_VDO_WRITE_POLICY_ASYNC;
     546              :     else {
     547            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_VDO_POLICY_INVAL,
     548              :                      "Invalid policy given: %s", policy_str);
     549            0 :         return BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
     550              :     }
     551              : }
     552              : 
     553              : /**
     554              :  * bd_lvm_vdo_get_stats_full:
     555              :  * @vg_name: name of the VG that contains @pool_name VDO pool
     556              :  * @pool_name: name of the VDO pool to get statistics for
     557              :  * @error: (out) (optional): place to store error (if any)
     558              :  *
     559              :  * Returns: (transfer full) (element-type utf8 utf8): hashtable of type string - string of available
     560              :  *                                                    statistics or %NULL in case of error
     561              :  *                                                    (@error gets populated in those cases)
     562              :  *
     563              :  * Statistics are collected from the values exposed by the kernel `dm-vdo` module.
     564              :  *
     565              :  * Some of the keys are computed to mimic the information produced by the vdo tools.
     566              :  * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version.
     567              :  *
     568              :  * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
     569              :  */
     570            4 : GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error) {
     571            8 :     g_autofree gchar *kvdo_name = g_strdup_printf ("%s-%s-%s", vg_name, pool_name, VDO_POOL_SUFFIX);
     572            4 :     return vdo_get_stats_full (kvdo_name, error);
     573              : }
     574              : 
     575              : /**
     576              :  * bd_lvm_vdo_get_stats:
     577              :  * @vg_name: name of the VG that contains @pool_name VDO pool
     578              :  * @pool_name: name of the VDO pool to get statistics for
     579              :  * @error: (out) (optional): place to store error (if any)
     580              :  *
     581              :  * Returns: (transfer full): a structure containing selected statistics or %NULL in case of error
     582              :  *                           (@error gets populated in those cases)
     583              :  *
     584              :  * In contrast to @bd_lvm_vdo_get_stats_full this function will only return selected statistics
     585              :  * in a fixed structure. In case a value is not available, -1 would be returned.
     586              :  *
     587              :  * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
     588              :  */
     589            2 : BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error) {
     590            2 :     GHashTable *full_stats = NULL;
     591            2 :     BDLVMVDOStats *stats = NULL;
     592              : 
     593            2 :     full_stats = bd_lvm_vdo_get_stats_full (vg_name, pool_name, error);
     594            2 :     if (!full_stats)
     595            0 :         return NULL;
     596              : 
     597            2 :     stats = g_new0 (BDLVMVDOStats, 1);
     598            2 :     get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1);
     599            2 :     get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1);
     600            2 :     get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1);
     601            2 :     get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1);
     602            2 :     get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1);
     603            2 :     get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1);
     604            2 :     get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1);
     605            2 :     get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1);
     606            2 :     if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio))
     607            0 :         stats->write_amplification_ratio = -1;
     608              : 
     609            2 :     g_hash_table_destroy (full_stats);
     610              : 
     611            2 :     return stats;
     612              : }
     613              : 
     614              : /* check whether the LVM devices file is enabled by LVM
     615              :  * we use the existence of the "lvmdevices" command to check whether the feature is available
     616              :  * or not, but this can still be disabled either in LVM or in lvm.conf
     617              :  */
     618          286 : static gboolean _lvm_devices_enabled () {
     619          286 :     const gchar *args[6] = {"lvmconfig", "--typeconfig", NULL, "devices/use_devicesfile", NULL, NULL};
     620          286 :     gboolean ret = FALSE;
     621          286 :     GError *loc_error = NULL;
     622          286 :     gchar *output = NULL;
     623          286 :     gboolean enabled = FALSE;
     624          286 :     gint scanned = 0;
     625          286 :     g_autofree gchar *config_arg = NULL;
     626              : 
     627              :     /* try full config first -- if we get something from this it means the feature is
     628              :        explicitly enabled or disabled by system lvm.conf or using the --config option */
     629          286 :     args[2] = "full";
     630              : 
     631              :     /* make sure to include the global config from us when getting the current config value */
     632          286 :     g_mutex_lock (&global_config_lock);
     633          286 :     if (global_config_str) {
     634           10 :         config_arg = g_strdup_printf ("--config=%s", global_config_str);
     635           10 :         args[4] = config_arg;
     636              :     }
     637              : 
     638          286 :     ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
     639          286 :     g_mutex_unlock (&global_config_lock);
     640          286 :     if (ret) {
     641          286 :         scanned = sscanf (output, "use_devicesfile=%u", &enabled);
     642          286 :         g_free (output);
     643          286 :         if (scanned != 1)
     644            0 :             return FALSE;
     645              : 
     646          286 :         return enabled;
     647              :     } else {
     648            0 :         g_clear_error (&loc_error);
     649            0 :         g_free (output);
     650              :     }
     651              : 
     652            0 :     output = NULL;
     653              : 
     654              :     /* now try default */
     655            0 :     args[2] = "default";
     656            0 :     ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
     657            0 :     if (ret) {
     658            0 :         scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
     659            0 :         g_free (output);
     660            0 :         if (scanned != 1)
     661            0 :             return FALSE;
     662              : 
     663            0 :         return enabled;
     664              :     } else {
     665            0 :         g_clear_error (&loc_error);
     666            0 :         g_free (output);
     667              :     }
     668              : 
     669            0 :     return FALSE;
     670              : }
     671              : 
     672              : /**
     673              :  * bd_lvm_devices_add:
     674              :  * @device: device (PV) to add to the devices file
     675              :  * @devices_file: (nullable): LVM devices file or %NULL for default
     676              :  * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
     677              :  * @error: (out) (optional): place to store error (if any)
     678              :  *
     679              :  * Returns: whether the @device was successfully added to @devices_file or not
     680              :  *
     681              :  * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
     682              :  */
     683            6 : gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
     684            6 :     const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
     685            6 :     g_autofree gchar *devfile = NULL;
     686              : 
     687            6 :     if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
     688            0 :         return FALSE;
     689              : 
     690            6 :     if (!_lvm_devices_enabled ()) {
     691            2 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
     692              :                      "LVM devices file not enabled.");
     693            2 :         return FALSE;
     694              :     }
     695              : 
     696            4 :     if (devices_file) {
     697            4 :         devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
     698            4 :         args[3] = devfile;
     699              :     }
     700              : 
     701            4 :     return bd_utils_exec_and_report_error (args, extra, error);
     702              : }
     703              : 
     704              : /**
     705              :  * bd_lvm_devices_delete:
     706              :  * @device: device (PV) to delete from the devices file
     707              :  * @devices_file: (nullable): LVM devices file or %NULL for default
     708              :  * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
     709              :  * @error: (out) (optional): place to store error (if any)
     710              :  *
     711              :  * Returns: whether the @device was successfully removed from @devices_file or not
     712              :  *
     713              :  * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
     714              :  */
     715          280 : gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
     716          280 :     const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
     717          280 :     g_autofree gchar *devfile = NULL;
     718              : 
     719          280 :     if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
     720            0 :         return FALSE;
     721              : 
     722          280 :     if (!_lvm_devices_enabled ()) {
     723            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
     724              :                      "LVM devices file not enabled.");
     725            0 :         return FALSE;
     726              :     }
     727              : 
     728          280 :     if (devices_file) {
     729            4 :         devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
     730            4 :         args[3] = devfile;
     731              :     }
     732              : 
     733          280 :     return bd_utils_exec_and_report_error (args, extra, error);
     734              : }
     735              : 
     736              : /**
     737              :  * bd_lvm_config_get:
     738              :  * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config
     739              :  * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section
     740              :  * @type: type of the config, e.g. 'full' or 'current'
     741              :  * @values_only: whether to include only values without keys in the output
     742              :  * @global_config: whether to include our internal global config in the call or not
     743              :  * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command
     744              :  *                                               (just passed to LVM as is)
     745              :  * @error: (out) (optional): place to store error (if any)
     746              :  *
     747              :  * Returns: (transfer full): Requested LVM config @section and @setting configuration or %NULL in case of error.
     748              :  *
     749              :  * Tech category: %BD_LVM_TECH_CONFIG no mode (it is ignored)
     750              :  */
     751           18 : gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) {
     752           18 :     g_autofree gchar *conf_spec = NULL;
     753           18 :     g_autofree gchar *config_arg = NULL;
     754           18 :     const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL};
     755           18 :     guint next_arg = 2;
     756           18 :     gchar *output = NULL;
     757           18 :     gboolean success = FALSE;
     758              : 
     759           18 :     if (!section && setting) {
     760            2 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
     761              :                      "Specifying setting without section is not supported.");
     762            2 :         return NULL;
     763              :     }
     764              : 
     765           16 :     if (section)
     766           14 :         if (setting)
     767           12 :             conf_spec = g_strdup_printf ("%s/%s", section, setting);
     768              :         else
     769            2 :             conf_spec = g_strdup (section);
     770              :     else
     771            2 :         conf_spec = NULL;
     772              : 
     773           16 :     args[next_arg++] = type;
     774           16 :     args[next_arg++] = conf_spec;
     775           16 :     if (values_only)
     776           14 :         args[next_arg++] = "--valuesonly";
     777              : 
     778           16 :     g_mutex_lock (&global_config_lock);
     779           16 :     if (global_config && global_config_str) {
     780            4 :         config_arg = g_strdup_printf ("--config=%s", global_config_str);
     781            4 :         args[next_arg++] = config_arg;
     782              :     }
     783           16 :     g_mutex_unlock (&global_config_lock);
     784              : 
     785           16 :     success = bd_utils_exec_and_capture_output (args, extra, &output, error);
     786           16 :     if (!success)
     787            0 :         return NULL;
     788           16 :     return g_strchomp (output);
     789              : }
     790              : 
     791            8 : gboolean _vgcfgbackup_restore (const gchar *command, const gchar *vg_name, const gchar *file, const BDExtraArg **extra, GError **error) {
     792            8 :     const gchar *args[6] = {"lvm", NULL, NULL, NULL, NULL, NULL};
     793            8 :     guint next_arg = 1;
     794            8 :     gchar *output = NULL;
     795            8 :     g_autofree gchar *config_arg = NULL;
     796              : 
     797            8 :     args[next_arg++] = command;
     798            8 :     if (file) {
     799            4 :         args[next_arg++] = "-f";
     800            4 :         args[next_arg++] = file;
     801              :     }
     802            8 :     args[next_arg++] = vg_name;
     803              : 
     804            8 :     g_mutex_lock (&global_config_lock);
     805            8 :     if (global_config_str) {
     806            0 :         config_arg = g_strdup_printf ("--config=%s", global_config_str);
     807            0 :         args[next_arg++] = config_arg;
     808              :     }
     809            8 :     g_mutex_unlock (&global_config_lock);
     810              : 
     811            8 :     return bd_utils_exec_and_capture_output (args, extra, &output, error);
     812              : }
     813              : 
     814              : /**
     815              :  * bd_lvm_vgcfgbackup:
     816              :  * @vg_name: name of the VG to backup configuration
     817              :  * @backup_file: (nullable): file to save the backup to or %NULL for using the default backup file
     818              :  *                           in /etc/lvm/backup
     819              :  * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgbackup command
     820              :  *                                               (just passed to LVM as is)
     821              :  * @error: (out) (optional): place to store error (if any)
     822              :  *
     823              :  * Note: This function does not back up the data content of LVs. See `vgcfbackup(8)` man page
     824              :  *       for more information.
     825              :  *
     826              :  * Returns: Whether the backup was successfully created or not.
     827              :  *
     828              :  * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
     829              :  */
     830            4 : gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) {
     831            4 :     return _vgcfgbackup_restore ("vgcfgbackup", vg_name, backup_file, extra, error);
     832              : }
     833              : 
     834              : /**
     835              :  * bd_lvm_vgcfgrestore:
     836              :  * @vg_name: name of the VG to restore configuration
     837              :  * @backup_file: (nullable): file to restore VG configuration from to or %NULL for using the
     838              :  *                           latest backup in /etc/lvm/backup
     839              :  * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgrestore command
     840              :  *                                               (just passed to LVM as is)
     841              :  * @error: (out) (optional): place to store error (if any)
     842              :  *
     843              :  * Note: This function restores VG configuration created by %bd_lvm_vgcfgbackup from given
     844              :  *       @backup_file or from the latest backup in /etc/lvm/backup.
     845              :  *
     846              :  * Returns: Whether the configuration was successfully restored or not.
     847              :  *
     848              :  * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
     849              :  */
     850            4 : gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) {
     851            4 :     return _vgcfgbackup_restore ("vgcfgrestore", vg_name, backup_file, extra, error);
     852              : }
     853              : 
     854              : /**
     855              :  * bd_lvm_cache_stats:
     856              :  * @vg_name: name of the VG containing the @cached_lv
     857              :  * @cached_lv: cached LV to get stats for
     858              :  * @error: (out) (optional): place to store error (if any)
     859              :  *
     860              :  * Returns: stats for the @cached_lv or %NULL in case of error
     861              :  *
     862              :  * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
     863              :  */
     864            6 : BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
     865            6 :     struct dm_pool *pool = NULL;
     866            6 :     struct dm_task *task = NULL;
     867              :     struct dm_info info;
     868            6 :     struct dm_status_cache *status = NULL;
     869            6 :     gchar *map_name = NULL;
     870            6 :     guint64 start = 0;
     871            6 :     guint64 length = 0;
     872            6 :     gchar *type = NULL;
     873            6 :     gchar *params = NULL;
     874            6 :     BDLVMCacheStats *ret = NULL;
     875            6 :     BDLVMLVdata *lvdata = NULL;
     876              : 
     877            6 :     lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
     878            6 :     if (!lvdata)
     879            0 :         return NULL;
     880              : 
     881            6 :     pool = dm_pool_create ("bd-pool", 20);
     882              : 
     883            6 :     if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
     884            2 :         map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
     885              :     else
     886              :         /* translate the VG+LV name into the DM map name */
     887            4 :         map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
     888              : 
     889            6 :     bd_lvm_lvdata_free (lvdata);
     890              : 
     891            6 :     task = dm_task_create (DM_DEVICE_STATUS);
     892            6 :     if (!task) {
     893            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     894              :                      "Failed to create DM task for the cache map '%s': ", map_name);
     895            0 :         dm_pool_destroy (pool);
     896            0 :         return NULL;
     897              :     }
     898              : 
     899            6 :     if (dm_task_set_name (task, map_name) == 0) {
     900            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     901              :                      "Failed to create DM task for the cache map '%s': ", map_name);
     902            0 :         dm_task_destroy (task);
     903            0 :         dm_pool_destroy (pool);
     904            0 :         return NULL;
     905              :     }
     906              : 
     907            6 :     if (dm_task_run (task) == 0) {
     908            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     909              :                      "Failed to run the DM task for the cache map '%s': ", map_name);
     910            0 :         dm_task_destroy (task);
     911            0 :         dm_pool_destroy (pool);
     912            0 :         return NULL;
     913              :     }
     914              : 
     915            6 :     if (dm_task_get_info (task, &info) == 0) {
     916            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     917              :                      "Failed to get task info for the cache map '%s': ", map_name);
     918            0 :         dm_task_destroy (task);
     919            0 :         dm_pool_destroy (pool);
     920            0 :         return NULL;
     921              :     }
     922              : 
     923            6 :     if (!info.exists) {
     924            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
     925              :                      "The cache map '%s' doesn't exist: ", map_name);
     926            0 :         dm_task_destroy (task);
     927            0 :         dm_pool_destroy (pool);
     928            0 :         return NULL;
     929              :     }
     930              : 
     931            6 :     dm_get_next_target (task, NULL, &start, &length, &type, &params);
     932              : 
     933            6 :     if (dm_get_status_cache (pool, params, &status) == 0) {
     934            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
     935              :                      "Failed to get status of the cache map '%s': ", map_name);
     936            0 :         dm_task_destroy (task);
     937            0 :         dm_pool_destroy (pool);
     938            0 :         return NULL;
     939              :     }
     940              : 
     941            6 :     ret = g_new0 (BDLVMCacheStats, 1);
     942            6 :     ret->block_size = status->block_size * SECTOR_SIZE;
     943            6 :     ret->cache_size = status->total_blocks * ret->block_size;
     944            6 :     ret->cache_used = status->used_blocks * ret->block_size;
     945              : 
     946            6 :     ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
     947            6 :     ret->md_size = status->metadata_total_blocks * ret->md_block_size;
     948            6 :     ret->md_used = status->metadata_used_blocks * ret->md_block_size;
     949              : 
     950            6 :     ret->read_hits = status->read_hits;
     951            6 :     ret->read_misses = status->read_misses;
     952            6 :     ret->write_hits = status->write_hits;
     953            6 :     ret->write_misses = status->write_misses;
     954              : 
     955            6 :     if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
     956            6 :         ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
     957            0 :     else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
     958            0 :         ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
     959              :     else {
     960            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
     961              :                       "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
     962            0 :                       status->feature_flags);
     963            0 :         dm_task_destroy (task);
     964            0 :         dm_pool_destroy (pool);
     965            0 :         bd_lvm_cache_stats_free (ret);
     966            0 :         return NULL;
     967              :     }
     968              : 
     969            6 :     dm_task_destroy (task);
     970            6 :     dm_pool_destroy (pool);
     971              : 
     972            6 :     return ret;
     973              : }
        

Generated by: LCOV version 2.0-1