LCOV - code coverage report
Current view: top level - plugins - btrfs.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 78.0 % 422 329
Test Date: 2026-01-23 09:12:16 Functions: 75.9 % 29 22
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 <bs_size.h>
      25              : 
      26              : #include "btrfs.h"
      27              : #include "check_deps.h"
      28              : 
      29              : #define BTRFS_MIN_VERSION "3.18.2"
      30              : 
      31              : /**
      32              :  * SECTION: btrfs
      33              :  * @short_description: plugin for operations with BTRFS devices
      34              :  * @title: BTRFS
      35              :  * @include: btrfs.h
      36              :  *
      37              :  * A plugin for operations with btrfs devices.
      38              :  */
      39              : 
      40              : /**
      41              :  * bd_btrfs_error_quark: (skip)
      42              :  */
      43            0 : GQuark bd_btrfs_error_quark (void)
      44              : {
      45            0 :     return g_quark_from_static_string ("g-bd-btrfs-error-quark");
      46              : }
      47              : 
      48            0 : BDBtrfsDeviceInfo* bd_btrfs_device_info_copy (BDBtrfsDeviceInfo *info) {
      49            0 :     if (info == NULL)
      50            0 :         return NULL;
      51              : 
      52            0 :     BDBtrfsDeviceInfo *new_info = g_new0 (BDBtrfsDeviceInfo, 1);
      53              : 
      54            0 :     new_info->id = info->id;
      55            0 :     new_info->path = g_strdup (info->path);
      56            0 :     new_info->size = info->size;
      57            0 :     new_info->used = info->used;
      58              : 
      59            0 :     return new_info;
      60              : }
      61              : 
      62            0 : void bd_btrfs_device_info_free (BDBtrfsDeviceInfo *info) {
      63            0 :     if (info == NULL)
      64            0 :         return;
      65              : 
      66            0 :     g_free (info->path);
      67            0 :     g_free (info);
      68              : }
      69              : 
      70            0 : BDBtrfsSubvolumeInfo* bd_btrfs_subvolume_info_copy (BDBtrfsSubvolumeInfo *info) {
      71            0 :     if  (info == NULL)
      72            0 :         return NULL;
      73              : 
      74            0 :     BDBtrfsSubvolumeInfo *new_info = g_new0 (BDBtrfsSubvolumeInfo, 1);
      75              : 
      76            0 :     new_info->id = info->id;
      77            0 :     new_info->parent_id = info->parent_id;
      78            0 :     new_info->path = g_strdup (info->path);
      79              : 
      80            0 :     return new_info;
      81              : }
      82              : 
      83            0 : void bd_btrfs_subvolume_info_free (BDBtrfsSubvolumeInfo *info) {
      84            0 :     if (info == NULL)
      85            0 :         return;
      86              : 
      87            0 :     g_free (info->path);
      88            0 :     g_free (info);
      89              : }
      90              : 
      91            0 : BDBtrfsFilesystemInfo* bd_btrfs_filesystem_info_copy (BDBtrfsFilesystemInfo *info) {
      92            0 :     if (info == NULL)
      93            0 :         return NULL;
      94              : 
      95            0 :     BDBtrfsFilesystemInfo *new_info = g_new0 (BDBtrfsFilesystemInfo, 1);
      96              : 
      97            0 :     new_info->label = g_strdup (info->label);
      98            0 :     new_info->uuid = g_strdup (info->uuid);
      99            0 :     new_info->num_devices = info->num_devices;
     100            0 :     new_info->used = info->used;
     101              : 
     102            0 :     return new_info;
     103              : }
     104              : 
     105            0 : void bd_btrfs_filesystem_info_free (BDBtrfsFilesystemInfo *info) {
     106            0 :     if (info == NULL)
     107            0 :         return;
     108              : 
     109            0 :     g_free (info->label);
     110            0 :     g_free (info->uuid);
     111            0 :     g_free (info);
     112              : }
     113              : 
     114              : static volatile guint avail_deps = 0;
     115              : static volatile guint avail_module_deps = 0;
     116              : static GMutex deps_check_lock;
     117              : 
     118              : #define DEPS_BTRFS 0
     119              : #define DEPS_BTRFS_MASK (1 << DEPS_BTRFS)
     120              : #define DEPS_LAST 1
     121              : 
     122              : static const UtilDep deps[DEPS_LAST] = {
     123              :     {"btrfs", BTRFS_MIN_VERSION, NULL, "[Bb]trfs.* v([\\d\\.]+)"},
     124              : };
     125              : 
     126              : #define MODULE_DEPS_BTRFS 0
     127              : #define MODULE_DEPS_BTRFS_MASK (1 << MODULE_DEPS_BTRFS)
     128              : #define MODULE_DEPS_LAST 1
     129              : 
     130              : static const gchar*const module_deps[MODULE_DEPS_LAST] = { "btrfs" };
     131              : 
     132              : 
     133              : /**
     134              :  * bd_btrfs_init:
     135              :  *
     136              :  * Initializes the plugin. **This function is called automatically by the
     137              :  * library's initialization functions.**
     138              :  *
     139              :  */
     140           24 : gboolean bd_btrfs_init (void) {
     141              :     /* nothing to do here */
     142           24 :     return TRUE;
     143              : };
     144              : 
     145              : /**
     146              :  * bd_btrfs_close:
     147              :  *
     148              :  * Cleans up after the plugin. **This function is called automatically by the
     149              :  * library's functions that unload it.**
     150              :  *
     151              :  */
     152           24 : void bd_btrfs_close (void) {
     153              :     /* nothing to do here */
     154           24 : }
     155              : 
     156              : 
     157              : 
     158              : /**
     159              :  * bd_btrfs_is_tech_avail:
     160              :  * @tech: the queried tech
     161              :  * @mode: a bit mask of queried modes of operation for @tech
     162              :  * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
     163              :  *
     164              :  * Returns: whether the @tech-@mode combination is available -- supported by the
     165              :  *          plugin implementation and having all the runtime dependencies available
     166              :  */
     167            4 : gboolean bd_btrfs_is_tech_avail (BDBtrfsTech tech G_GNUC_UNUSED, guint64 mode G_GNUC_UNUSED, GError **error) {
     168              :     /* all tech-mode combinations are supported by this implementation of the plugin */
     169            6 :     return check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) &&
     170            2 :            check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error);
     171              : }
     172              : 
     173           18 : static BDBtrfsDeviceInfo* get_device_info_from_match (GMatchInfo *match_info) {
     174           18 :     BDBtrfsDeviceInfo *ret = g_new(BDBtrfsDeviceInfo, 1);
     175           18 :     gchar *item = NULL;
     176           18 :     BSSize size = NULL;
     177           18 :     BSError *error = NULL;
     178              : 
     179           18 :     item = g_match_info_fetch_named (match_info, "id");
     180           18 :     ret->id = g_ascii_strtoull (item, NULL, 0);
     181           18 :     g_free (item);
     182              : 
     183           18 :     ret->path = g_match_info_fetch_named (match_info, "path");
     184              : 
     185           18 :     item = g_match_info_fetch_named (match_info, "size");
     186           18 :     if (item) {
     187           18 :         size = bs_size_new_from_str (item, &error);
     188           18 :         if (size) {
     189           18 :             ret->size = bs_size_get_bytes (size, NULL, &error);
     190           18 :             bs_size_free (size);
     191              :         }
     192           18 :         if (error)
     193            0 :             bd_utils_log_format (BD_UTILS_LOG_WARNING, "%s", error->msg);
     194           18 :         bs_clear_error (&error);
     195           18 :         g_free (item);
     196              :     }
     197              : 
     198           18 :     item = g_match_info_fetch_named (match_info, "used");
     199           18 :     if (item) {
     200           18 :         size = bs_size_new_from_str (item, &error);
     201           18 :         if (size) {
     202           18 :             ret->used = bs_size_get_bytes (size, NULL, &error);
     203           18 :             bs_size_free (size);
     204              :         }
     205           18 :         if (error)
     206            0 :             bd_utils_log_format (BD_UTILS_LOG_WARNING, "%s", error->msg);
     207           18 :         bs_clear_error (&error);
     208           18 :         g_free (item);
     209              :     }
     210              : 
     211           18 :     return ret;
     212              : }
     213              : 
     214          145 : static BDBtrfsSubvolumeInfo* get_subvolume_info_from_match (GMatchInfo *match_info) {
     215          145 :     BDBtrfsSubvolumeInfo *ret = g_new(BDBtrfsSubvolumeInfo, 1);
     216          145 :     gchar *item = NULL;
     217              : 
     218          145 :     item = g_match_info_fetch_named (match_info, "id");
     219          145 :     ret->id = g_ascii_strtoull (item, NULL, 0);
     220          145 :     g_free (item);
     221              : 
     222          145 :     item = g_match_info_fetch_named (match_info, "parent_id");
     223          145 :     ret->parent_id = g_ascii_strtoull (item, NULL, 0);
     224          145 :     g_free (item);
     225              : 
     226          145 :     ret->path = g_match_info_fetch_named (match_info, "path");
     227              : 
     228          145 :     return ret;
     229              : }
     230              : 
     231            3 : static BDBtrfsFilesystemInfo* get_filesystem_info_from_match (GMatchInfo *match_info) {
     232            3 :     BDBtrfsFilesystemInfo *ret = g_new(BDBtrfsFilesystemInfo, 1);
     233            3 :     gchar *item = NULL;
     234            3 :     BSSize size = NULL;
     235            3 :     BSError *error = NULL;
     236              : 
     237            3 :     ret->label = g_match_info_fetch_named (match_info, "label");
     238            3 :     ret->uuid = g_match_info_fetch_named (match_info, "uuid");
     239              : 
     240            3 :     item = g_match_info_fetch_named (match_info, "num_devices");
     241            3 :     ret->num_devices = g_ascii_strtoull (item, NULL, 0);
     242            3 :     g_free (item);
     243              : 
     244            3 :     item = g_match_info_fetch_named (match_info, "used");
     245            3 :     if (item) {
     246            3 :         size = bs_size_new_from_str (item, &error);
     247            3 :         if (size) {
     248            3 :             ret->used = bs_size_get_bytes (size, NULL, &error);
     249            3 :             bs_size_free (size);
     250              :         }
     251            3 :         if (error)
     252            0 :             bd_utils_log_format (BD_UTILS_LOG_WARNING, "%s", error->msg);
     253            3 :         bs_clear_error (&error);
     254            3 :         g_free (item);
     255              :     }
     256              : 
     257            3 :     return ret;
     258              : }
     259              : 
     260              : /**
     261              :  * bd_btrfs_create_volume:
     262              :  * @devices: (array zero-terminated=1): list of devices to create btrfs volume from
     263              :  * @label: (nullable): label for the volume
     264              :  * @data_level: (nullable): RAID level for the data or %NULL to use the default
     265              :  * @md_level: (nullable): RAID level for the metadata or %NULL to use the default
     266              :  * @extra: (nullable) (array zero-terminated=1): extra options for the volume creation (right now
     267              :  *                                                 passed to the 'mkfs.btrfs' utility)
     268              :  * @error: (out) (optional): place to store error (if any)
     269              :  *
     270              :  * Returns: whether the new btrfs volume was created from @devices or not
     271              :  *
     272              :  * See mkfs.btrfs(8) for details about @data_level, @md_level and btrfs in general.
     273              :  *
     274              :  * Tech category: %BD_BTRFS_TECH_MULTI_DEV-%BD_BTRFS_TECH_MODE_CREATE
     275              :  */
     276           34 : gboolean bd_btrfs_create_volume (const gchar **devices, const gchar *label, const gchar *data_level, const gchar *md_level, const BDExtraArg **extra, GError **error) {
     277           34 :     const gchar **device_p = NULL;
     278           34 :     guint8 num_args = 0;
     279           34 :     const gchar **argv = NULL;
     280           34 :     guint8 next_arg = 1;
     281           34 :     gboolean success = FALSE;
     282              : 
     283           68 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     284           34 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     285            0 :         return FALSE;
     286              : 
     287           34 :     if (!devices || (g_strv_length ((gchar **) devices) < 1)) {
     288            2 :         g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_DEVICE, "No devices given");
     289            2 :         return FALSE;
     290              :     }
     291              : 
     292           69 :     for (device_p = devices; *device_p != NULL; device_p++) {
     293           39 :         if (access (*device_p, F_OK) != 0) {
     294            2 :             g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_DEVICE, "Device %s does not exist", *device_p);
     295            2 :             return FALSE;
     296              :         }
     297           37 :         num_args++;
     298              :     }
     299              : 
     300           30 :     if (label)
     301           16 :         num_args += 2;
     302           30 :     if (data_level)
     303            4 :         num_args += 2;
     304           30 :     if (md_level)
     305            4 :         num_args += 2;
     306              : 
     307           30 :     argv = g_new0 (const gchar*, num_args + 2);
     308           30 :     argv[0] = "mkfs.btrfs";
     309           30 :     if (label) {
     310           16 :         argv[next_arg] = "--label";
     311           16 :         next_arg++;
     312           16 :         argv[next_arg] = label;
     313           16 :         next_arg++;
     314              :     }
     315           30 :     if (data_level) {
     316            4 :         argv[next_arg] = "--data";
     317            4 :         next_arg++;
     318            4 :         argv[next_arg] = data_level;
     319            4 :         next_arg++;
     320              :     }
     321           30 :     if (md_level) {
     322            4 :         argv[next_arg] = "--metadata";
     323            4 :         next_arg++;
     324            4 :         argv[next_arg] = md_level;
     325            4 :         next_arg++;
     326              :     }
     327              : 
     328           67 :     for (device_p = devices; next_arg <= num_args; device_p++, next_arg++)
     329           37 :         argv[next_arg] = *device_p;
     330           30 :     argv[next_arg] = NULL;
     331              : 
     332           30 :     success = bd_utils_exec_and_report_error (argv, extra, error);
     333           30 :     g_free (argv);
     334           30 :     return success;
     335              : }
     336              : 
     337              : /**
     338              :  * bd_btrfs_add_device:
     339              :  * @mountpoint: mountpoint of the btrfs volume to add new device to
     340              :  * @device: a device to add to the btrfs volume
     341              :  * @extra: (nullable) (array zero-terminated=1): extra options for the addition (right now
     342              :  *                                                 passed to the 'btrfs' utility)
     343              :  * @error: (out) (optional): place to store error (if any)
     344              :  *
     345              :  * Returns: whether the @device was successfully added to the @mountpoint btrfs volume or not
     346              :  *
     347              :  * Tech category: %BD_BTRFS_TECH_MULTI_DEV-%BD_BTRFS_TECH_MODE_MODIFY
     348              :  */
     349            1 : gboolean bd_btrfs_add_device (const gchar *mountpoint, const gchar *device, const BDExtraArg **extra, GError **error) {
     350            1 :     const gchar *argv[6] = {"btrfs", "device", "add", device, mountpoint, NULL};
     351            2 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     352            1 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     353            0 :         return FALSE;
     354              : 
     355            1 :     return bd_utils_exec_and_report_error (argv, extra, error);
     356              : }
     357              : 
     358              : /**
     359              :  * bd_btrfs_remove_device:
     360              :  * @mountpoint: mountpoint of the btrfs volume to remove device from
     361              :  * @device: a device to remove from the btrfs volume
     362              :  * @extra: (nullable) (array zero-terminated=1): extra options for the removal (right now
     363              :  *                                                 passed to the 'btrfs' utility)
     364              :  * @error: (out) (optional): place to store error (if any)
     365              :  *
     366              :  * Returns: whether the @device was successfully removed from the @mountpoint btrfs volume or not
     367              :  *
     368              :  * Tech category: %BD_BTRFS_TECH_MULTI_DEV-%BD_BTRFS_TECH_MODE_MODIFY
     369              :  */
     370            1 : gboolean bd_btrfs_remove_device (const gchar *mountpoint, const gchar *device, const BDExtraArg **extra, GError **error) {
     371            1 :     const gchar *argv[6] = {"btrfs", "device", "delete", device, mountpoint, NULL};
     372            2 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     373            1 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     374            0 :         return FALSE;
     375              : 
     376            1 :     return bd_utils_exec_and_report_error (argv, extra, error);
     377              : }
     378              : 
     379              : /**
     380              :  * bd_btrfs_create_subvolume:
     381              :  * @mountpoint: mountpoint of the btrfs volume to create subvolume under
     382              :  * @name: name of the subvolume
     383              :  * @extra: (nullable) (array zero-terminated=1): extra options for the subvolume creation (right now
     384              :  *                                                 passed to the 'btrfs' utility)
     385              :  * @error: (out) (optional): place to store error (if any)
     386              :  *
     387              :  * Returns: whether the @mountpoint/@name subvolume was successfully created or not
     388              :  *
     389              :  * Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_CREATE
     390              :  */
     391           11 : gboolean bd_btrfs_create_subvolume (const gchar *mountpoint, const gchar *name, const BDExtraArg **extra, GError **error) {
     392           11 :     gchar *path = NULL;
     393           11 :     gboolean success = FALSE;
     394           11 :     const gchar *argv[5] = {"btrfs", "subvol", "create", NULL, NULL};
     395              : 
     396           22 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     397           11 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     398            0 :         return FALSE;
     399              : 
     400           11 :     if (g_str_has_suffix (mountpoint, "/"))
     401            0 :         path = g_strdup_printf ("%s%s", mountpoint, name);
     402              :     else
     403           11 :         path = g_strdup_printf ("%s/%s", mountpoint, name);
     404           11 :     argv[3] = path;
     405              : 
     406           11 :     success = bd_utils_exec_and_report_error (argv, extra, error);
     407           11 :     g_free (path);
     408              : 
     409           11 :     return success;
     410              : }
     411              : 
     412              : /**
     413              :  * bd_btrfs_delete_subvolume:
     414              :  * @mountpoint: mountpoint of the btrfs volume to delete subvolume from
     415              :  * @name: name of the subvolume
     416              :  * @extra: (nullable) (array zero-terminated=1): extra options for the subvolume deletion (right now
     417              :  *                                                 passed to the 'btrfs' utility)
     418              :  * @error: (out) (optional): place to store error (if any)
     419              :  *
     420              :  * Returns: whether the @mountpoint/@name subvolume was successfully deleted or not
     421              :  *
     422              :  * Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_DELETE
     423              :  */
     424            2 : gboolean bd_btrfs_delete_subvolume (const gchar *mountpoint, const gchar *name, const BDExtraArg **extra, GError **error) {
     425            2 :     gchar *path = NULL;
     426            2 :     gboolean success = FALSE;
     427            2 :     const gchar *argv[5] = {"btrfs", "subvol", "delete", NULL, NULL};
     428              : 
     429            4 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     430            2 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     431            0 :         return FALSE;
     432              : 
     433            2 :     if (g_str_has_suffix (mountpoint, "/"))
     434            0 :         path = g_strdup_printf ("%s%s", mountpoint, name);
     435              :     else
     436            2 :         path = g_strdup_printf ("%s/%s", mountpoint, name);
     437            2 :     argv[3] = path;
     438              : 
     439            2 :     success = bd_utils_exec_and_report_error (argv, extra, error);
     440            2 :     g_free (path);
     441              : 
     442            2 :     return success;
     443              : }
     444              : 
     445              : /**
     446              :  * bd_btrfs_get_default_subvolume_id:
     447              :  * @mountpoint: mountpoint of the volume to get the default subvolume ID of
     448              :  * @error: (out) (optional): place to store error (if any)
     449              :  *
     450              :  * Returns: ID of the @mountpoint volume's default subvolume. If 0,
     451              :  * @error) may be set to indicate error
     452              :  *
     453              :  * Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_QUERY
     454              :  */
     455            5 : guint64 bd_btrfs_get_default_subvolume_id (const gchar *mountpoint, GError **error) {
     456            5 :     GRegex *regex = NULL;
     457            5 :     GMatchInfo *match_info = NULL;
     458            5 :     gboolean success = FALSE;
     459            5 :     gchar *output = NULL;
     460            5 :     gchar *match = NULL;
     461            5 :     guint64 ret = 0;
     462            5 :     const gchar *argv[5] = {"btrfs", "subvol", "get-default", mountpoint, NULL};
     463              : 
     464           10 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     465            5 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     466            0 :         return 0;
     467              : 
     468            5 :     regex = g_regex_new ("ID (\\d+) .*", 0, 0, error);
     469            5 :     if (!regex) {
     470            0 :         bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
     471              :         /* error is already populated */
     472            0 :         return 0;
     473              :     }
     474              : 
     475            5 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
     476            5 :     if (!success) {
     477            1 :         g_regex_unref (regex);
     478            1 :         return 0;
     479              :     }
     480              : 
     481            4 :     success = g_regex_match (regex, output, 0, &match_info);
     482            4 :     if (!success) {
     483            0 :         g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_PARSE, "Failed to parse subvolume's ID");
     484            0 :         g_regex_unref (regex);
     485            0 :         g_match_info_free (match_info);
     486            0 :         g_free (output);
     487            0 :         return 0;
     488              :     }
     489              : 
     490            4 :     match = g_match_info_fetch (match_info, 1);
     491            4 :     ret = g_ascii_strtoull (match, NULL, 0);
     492              : 
     493            4 :     g_free (match);
     494            4 :     g_match_info_free (match_info);
     495            4 :     g_regex_unref (regex);
     496            4 :     g_free (output);
     497              : 
     498            4 :     return ret;
     499              : }
     500              : 
     501              : /**
     502              :  * bd_btrfs_set_default_subvolume:
     503              :  * @mountpoint: mountpoint of the volume to set the default subvolume ID of
     504              :  * @subvol_id: ID of the subvolume to be set as the default subvolume
     505              :  * @extra: (nullable) (array zero-terminated=1): extra options for the setting (right now
     506              :  *                                                 passed to the 'btrfs' utility)
     507              :  * @error: (out) (optional): place to store error (if any)
     508              :  *
     509              :  * Returns: whether the @mountpoint volume's default subvolume was correctly set
     510              :  * to @subvol_id or not
     511              :  *
     512              :  * Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_MODIFY
     513              :  */
     514            2 : gboolean bd_btrfs_set_default_subvolume (const gchar *mountpoint, guint64 subvol_id, const BDExtraArg **extra, GError **error) {
     515            2 :     const gchar *argv[6] = {"btrfs", "subvol", "set-default", NULL, mountpoint, NULL};
     516            2 :     gboolean ret = FALSE;
     517              : 
     518            4 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     519            2 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     520            0 :         return FALSE;
     521              : 
     522            2 :     argv[3] = g_strdup_printf ("%"G_GUINT64_FORMAT, subvol_id);
     523            2 :     ret = bd_utils_exec_and_report_error (argv, extra, error);
     524            2 :     g_free ((gchar *) argv[3]);
     525              : 
     526            2 :     return ret;
     527              : }
     528              : 
     529              : /**
     530              :  * bd_btrfs_create_snapshot:
     531              :  * @source: path to source subvolume
     532              :  * @dest: path to new snapshot volume
     533              :  * @ro: whether the snapshot should be read-only
     534              :  * @extra: (nullable) (array zero-terminated=1): extra options for the snapshot creation (right now
     535              :  *                                                 passed to the 'btrfs' utility)
     536              :  * @error: (out) (optional): place to store error (if any)
     537              :  *
     538              :  * Returns: whether the @dest snapshot of @source was successfully created or not
     539              :  *
     540              :  * Tech category: %BD_BTRFS_TECH_SNAPSHOT-%BD_BTRFS_TECH_MODE_CREATE
     541              :  */
     542            2 : gboolean bd_btrfs_create_snapshot (const gchar *source, const gchar *dest, gboolean ro, const BDExtraArg **extra, GError **error) {
     543            2 :     const gchar *argv[7] = {"btrfs", "subvol", "snapshot", NULL, NULL, NULL, NULL};
     544            2 :     guint next_arg = 3;
     545              : 
     546            4 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     547            2 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     548            0 :         return FALSE;
     549              : 
     550            2 :     if (ro) {
     551            1 :         argv[next_arg] = "-r";
     552            1 :         next_arg++;
     553              :     }
     554            2 :     argv[next_arg] = source;
     555            2 :     next_arg++;
     556            2 :     argv[next_arg] = dest;
     557              : 
     558            2 :     return bd_utils_exec_and_report_error (argv, extra, error);
     559              : }
     560              : 
     561              : /**
     562              :  * bd_btrfs_list_devices:
     563              :  * @device: a device that is part of the queried btrfs volume
     564              :  * @error: (out) (optional): place to store error (if any)
     565              :  *
     566              :  * Returns: (array zero-terminated=1): information about the devices that are part of the btrfs volume
     567              :  * containing @device or %NULL in case of error
     568              :  *
     569              :  * Tech category: %BD_BTRFS_TECH_MULTI_DEV-%BD_BTRFS_TECH_MODE_QUERY
     570              :  */
     571           12 : BDBtrfsDeviceInfo** bd_btrfs_list_devices (const gchar *device, GError **error) {
     572           12 :     const gchar *argv[5] = {"btrfs", "filesystem", "show", device, NULL};
     573           12 :     gchar *output = NULL;
     574           12 :     gboolean success = FALSE;
     575           12 :     gchar **lines = NULL;
     576           12 :     gchar **line_p = NULL;
     577           12 :     gchar const * const pattern = "devid[ \\t]+(?P<id>\\d+)[ \\t]+" \
     578              :                                   "size[ \\t]+(?P<size>\\S+)[ \\t]+" \
     579              :                                   "used[ \\t]+(?P<used>\\S+)[ \\t]+" \
     580              :                                   "path[ \\t]+(?P<path>\\S+)\n";
     581           12 :     GRegex *regex = NULL;
     582           12 :     GMatchInfo *match_info = NULL;
     583              :     GPtrArray *dev_infos;
     584              : 
     585           24 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     586           12 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     587            0 :         return NULL;
     588              : 
     589           12 :     regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, error);
     590           12 :     if (!regex) {
     591            0 :         bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
     592              :         /* error is already populated */
     593            0 :         return NULL;
     594              :     }
     595              : 
     596           12 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
     597           12 :     if (!success) {
     598            0 :         g_regex_unref (regex);
     599              :         /* error is already populated from the previous call */
     600            0 :         return NULL;
     601              :     }
     602              : 
     603           12 :     lines = g_strsplit (output, "\n", 0);
     604           12 :     g_free (output);
     605              : 
     606           12 :     dev_infos = g_ptr_array_new ();
     607           66 :     for (line_p = lines; *line_p; line_p++) {
     608           54 :         success = g_regex_match (regex, *line_p, 0, &match_info);
     609           54 :         if (!success) {
     610           36 :             g_match_info_free (match_info);
     611           36 :             continue;
     612              :         }
     613              : 
     614           18 :         g_ptr_array_add (dev_infos, get_device_info_from_match (match_info));
     615           18 :         g_match_info_free (match_info);
     616              :     }
     617              : 
     618           12 :     g_strfreev (lines);
     619           12 :     g_regex_unref (regex);
     620              : 
     621           12 :     if (dev_infos->len == 0) {
     622            0 :         g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_PARSE, "Failed to parse information about devices");
     623            0 :         g_ptr_array_free (dev_infos, TRUE);
     624            0 :         return NULL;
     625              :     }
     626              : 
     627           12 :     g_ptr_array_add (dev_infos, NULL);
     628           12 :     return (BDBtrfsDeviceInfo **) g_ptr_array_free (dev_infos, FALSE);
     629              : }
     630              : 
     631              : /**
     632              :  * bd_btrfs_list_subvolumes:
     633              :  * @mountpoint: a mountpoint of the queried btrfs volume
     634              :  * @snapshots_only: whether to list only snapshot subvolumes or not
     635              :  * @error: (out) (optional): place to store error (if any)
     636              :  *
     637              :  * Returns: (array zero-terminated=1): information about the subvolumes that are part of the btrfs volume
     638              :  * mounted at @mountpoint or %NULL in case of error
     639              :  *
     640              :  * The subvolumes are sorted in a way that no child subvolume appears in the
     641              :  * list before its parent (sub)volume.
     642              :  *
     643              :  * Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_QUERY
     644              :  */
     645           14 : BDBtrfsSubvolumeInfo** bd_btrfs_list_subvolumes (const gchar *mountpoint, gboolean snapshots_only, GError **error) {
     646           14 :     const gchar *argv[8] = {"btrfs", "subvol", "list", "-a", "-p", NULL, NULL, NULL};
     647           14 :     gchar *output = NULL;
     648           14 :     gboolean success = FALSE;
     649           14 :     gchar **lines = NULL;
     650           14 :     gchar **line_p = NULL;
     651           14 :     gchar const * const pattern = "ID\\s+(?P<id>\\d+)\\s+gen\\s+\\d+\\s+(cgen\\s+\\d+\\s+)?" \
     652              :                                   "parent\\s+(?P<parent_id>\\d+)\\s+top\\s+level\\s+\\d+\\s+" \
     653              :                                   "(otime\\s+(\\d{4}-\\d{2}-\\d{2}\\s+\\d\\d:\\d\\d:\\d\\d|-)\\s+)?"\
     654              :                                   "path\\s+(<FS_TREE>/)?(?P<path>.+)";
     655           14 :     GRegex *regex = NULL;
     656           14 :     GMatchInfo *match_info = NULL;
     657           14 :     guint64 i = 0;
     658           14 :     guint64 y = 0;
     659           14 :     guint64 next_sorted_idx = 0;
     660              :     GPtrArray *subvol_infos;
     661           14 :     BDBtrfsSubvolumeInfo* item = NULL;
     662           14 :     BDBtrfsSubvolumeInfo* swap_item = NULL;
     663           14 :     BDBtrfsSubvolumeInfo** ret = NULL;
     664           14 :     GError *l_error = NULL;
     665              : 
     666           28 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     667           14 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     668            0 :         return NULL;
     669              : 
     670           14 :     if (snapshots_only) {
     671            4 :         argv[5] = "-s";
     672            4 :         argv[6] = mountpoint;
     673              :     } else
     674           10 :         argv[5] = mountpoint;
     675              : 
     676           14 :     regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, error);
     677           14 :     if (!regex) {
     678            0 :         bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
     679              :         /* error is already populated */
     680            0 :         return NULL;
     681              :     }
     682              : 
     683           14 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, &l_error);
     684           14 :     if (!success) {
     685            4 :         g_regex_unref (regex);
     686            4 :         if (g_error_matches (l_error,  BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
     687              :             /* no output -> no subvolumes */
     688            4 :             g_clear_error (&l_error);
     689            4 :             return g_new0 (BDBtrfsSubvolumeInfo*, 1);
     690              :         } else {
     691            0 :             g_propagate_error (error, l_error);
     692            0 :             return NULL;
     693              :         }
     694              :     }
     695              : 
     696           10 :     lines = g_strsplit (output, "\n", 0);
     697           10 :     g_free (output);
     698              : 
     699           10 :     subvol_infos = g_ptr_array_new ();
     700          165 :     for (line_p = lines; *line_p; line_p++) {
     701          155 :         success = g_regex_match (regex, *line_p, 0, &match_info);
     702          155 :         if (!success) {
     703           10 :             g_match_info_free (match_info);
     704           10 :             continue;
     705              :         }
     706              : 
     707          145 :         g_ptr_array_add (subvol_infos, get_subvolume_info_from_match (match_info));
     708          145 :         g_match_info_free (match_info);
     709              :     }
     710              : 
     711           10 :     g_strfreev (lines);
     712           10 :     g_regex_unref (regex);
     713              : 
     714           10 :     if (subvol_infos->len == 0) {
     715            0 :         g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_PARSE, "Failed to parse information about subvolumes");
     716            0 :         g_ptr_array_free (subvol_infos, TRUE);
     717            0 :         return NULL;
     718              :     }
     719              : 
     720              :     /* now we know how much space to allocate for the result (subvols + NULL) */
     721           10 :     ret = g_new0 (BDBtrfsSubvolumeInfo*, subvol_infos->len + 1);
     722              : 
     723              :     /* we need to sort the subvolumes in a way that no child subvolume appears
     724              :        in the list before its parent (sub)volume */
     725              : 
     726              :     /* let's start by moving all top-level (sub)volumes to the beginning */
     727          155 :     for (i=0; i < subvol_infos->len; i++) {
     728          145 :         item = (BDBtrfsSubvolumeInfo*) g_ptr_array_index (subvol_infos, i);
     729          145 :         if (item->parent_id == BD_BTRFS_MAIN_VOLUME_ID)
     730              :             /* top-level (sub)volume */
     731           13 :             ret[next_sorted_idx++] = item;
     732              :     }
     733              :     /* top-level (sub)volumes are now processed */
     734           23 :     for (i=0; i < next_sorted_idx; i++)
     735           13 :         g_ptr_array_remove_fast (subvol_infos, ret[i]);
     736              : 
     737              :     /* now sort the rest in a way that we search for an already sorted parent or sibling */
     738          142 :     for (i=0; i < subvol_infos->len; i++) {
     739          132 :         item = (BDBtrfsSubvolumeInfo*) g_ptr_array_index (subvol_infos, i);
     740          132 :         ret[next_sorted_idx] = item;
     741              :         /* move the item towards beginning of the array checking if some parent
     742              :            or sibling has been already processed/sorted before or we reached the
     743              :            top-level (sub)volumes */
     744          383 :         for (y=next_sorted_idx; (y > 0 && (ret[y-1]->id != item->parent_id) && (ret[y-1]->parent_id != item->parent_id) && (ret[y-1]->parent_id != BD_BTRFS_MAIN_VOLUME_ID)); y--) {
     745          251 :             swap_item = ret[y-1];
     746          251 :             ret[y-1] = ret[y];
     747          251 :             ret[y] = swap_item;
     748              :         }
     749          132 :         next_sorted_idx++;
     750              :     }
     751           10 :     ret[next_sorted_idx] = NULL;
     752              : 
     753              :     /* now just free the pointer array */
     754           10 :     g_ptr_array_free (subvol_infos, TRUE);
     755              : 
     756           10 :     return ret;
     757              : }
     758              : 
     759              : /**
     760              :  * bd_btrfs_filesystem_info:
     761              :  * @device: a device that is part of the queried btrfs volume
     762              :  * @error: (out) (optional): place to store error (if any)
     763              :  *
     764              :  * Returns: information about the @device's volume's filesystem or %NULL in case of error
     765              :  *
     766              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_QUERY
     767              :  */
     768            3 : BDBtrfsFilesystemInfo* bd_btrfs_filesystem_info (const gchar *device, GError **error) {
     769            3 :     const gchar *argv[5] = {"btrfs", "filesystem", "show", device, NULL};
     770            3 :     gchar *output = NULL;
     771            3 :     gboolean success = FALSE;
     772            3 :     gchar const * const pattern = "Label:\\s+(none|'(?P<label>.+)')\\s+" \
     773              :                                   "uuid:\\s+(?P<uuid>\\S+)\\s+" \
     774              :                                   "Total\\sdevices\\s+(?P<num_devices>\\d+)\\s+" \
     775              :                                   "FS\\sbytes\\sused\\s+(?P<used>\\S+)";
     776            3 :     GRegex *regex = NULL;
     777            3 :     GMatchInfo *match_info = NULL;
     778            3 :     BDBtrfsFilesystemInfo *ret = NULL;
     779              : 
     780            6 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     781            3 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     782            0 :         return NULL;
     783              : 
     784            3 :     regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, error);
     785            3 :     if (!regex) {
     786            0 :         bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
     787              :         /* error is already populated */
     788            0 :         return NULL;
     789              :     }
     790              : 
     791            3 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
     792            3 :     if (!success) {
     793              :         /* error is already populated from the call above or just empty
     794              :            output */
     795            0 :         g_regex_unref (regex);
     796            0 :         return NULL;
     797              :     }
     798              : 
     799            3 :     success = g_regex_match (regex, output, 0, &match_info);
     800            3 :     if (!success) {
     801            0 :         g_regex_unref (regex);
     802            0 :         g_match_info_free (match_info);
     803            0 :         g_free (output);
     804            0 :         return NULL;
     805              :     }
     806              : 
     807            3 :     ret = get_filesystem_info_from_match (match_info);
     808            3 :     g_match_info_free (match_info);
     809            3 :     g_regex_unref (regex);
     810              : 
     811            3 :     g_free (output);
     812              : 
     813            3 :     return ret;
     814              : }
     815              : 
     816              : /**
     817              :  * bd_btrfs_mkfs:
     818              :  * @devices: (array zero-terminated=1): list of devices to create btrfs volume from
     819              :  * @label: (nullable): label for the volume
     820              :  * @data_level: (nullable): RAID level for the data or %NULL to use the default
     821              :  * @md_level: (nullable): RAID level for the metadata or %NULL to use the default
     822              :  * @extra: (nullable) (array zero-terminated=1): extra options for the volume creation (right now
     823              :  *                                                 passed to the 'btrfs' utility)
     824              :  * @error: (out) (optional): place to store error (if any)
     825              :  *
     826              :  * Returns: whether the new btrfs volume was created from @devices or not
     827              :  *
     828              :  * See mkfs.btrfs(8) for details about @data_level, @md_level and btrfs in general.
     829              :  *
     830              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_CREATE
     831              :  */
     832            7 : gboolean bd_btrfs_mkfs (const gchar **devices, const gchar *label, const gchar *data_level, const gchar *md_level, const BDExtraArg **extra, GError **error) {
     833            7 :     return bd_btrfs_create_volume (devices, label, data_level, md_level, extra, error);
     834              : }
     835              : 
     836              : /**
     837              :  * bd_btrfs_resize:
     838              :  * @mountpoint: a mountpoint of the to be resized btrfs filesystem
     839              :  * @size: requested new size
     840              :  * @extra: (nullable) (array zero-terminated=1): extra options for the volume resize (right now
     841              :  *                                                 passed to the 'btrfs' utility)
     842              :  * @error: (out) (optional): place to store error (if any)
     843              :  *
     844              :  * Returns: whether the @mountpoint filesystem was successfully resized to @size
     845              :  * or not
     846              :  *
     847              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_MODIFY
     848              :  */
     849            1 : gboolean bd_btrfs_resize (const gchar *mountpoint, guint64 size, const BDExtraArg **extra, GError **error) {
     850            1 :     const gchar *argv[6] = {"btrfs", "filesystem", "resize", NULL, mountpoint, NULL};
     851            1 :     gboolean ret = FALSE;
     852              : 
     853            2 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     854            1 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     855            0 :         return FALSE;
     856              : 
     857            1 :     argv[3] = g_strdup_printf ("%"G_GUINT64_FORMAT, size);
     858            1 :     ret = bd_utils_exec_and_report_error (argv, extra, error);
     859            1 :     g_free ((gchar *) argv[3]);
     860              : 
     861            1 :     return ret;
     862              : }
     863              : 
     864              : /**
     865              :  * bd_btrfs_check:
     866              :  * @device: a device that is part of the checked btrfs volume
     867              :  * @extra: (nullable) (array zero-terminated=1): extra options for the check (right now
     868              :  *                                                 passed to the 'btrfs' utility)
     869              :  * @error: (out) (optional): place to store error (if any)
     870              :  *
     871              :  * Returns: whether the filesystem was successfully checked or not
     872              :  *
     873              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_QUERY
     874              :  */
     875            1 : gboolean bd_btrfs_check (const gchar *device, const BDExtraArg **extra, GError **error) {
     876            1 :     const gchar *argv[4] = {"btrfs", "check", device, NULL};
     877              : 
     878            2 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     879            1 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     880            0 :         return FALSE;
     881              : 
     882            1 :     return bd_utils_exec_and_report_error (argv, extra, error);
     883              : }
     884              : 
     885              : /**
     886              :  * bd_btrfs_repair:
     887              :  * @device: a device that is part of the to be repaired btrfs volume
     888              :  * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
     889              :  *                                                 passed to the 'btrfs' utility)
     890              :  * @error: (out) (optional): place to store error (if any)
     891              :  *
     892              :  * Returns: whether the filesystem was successfully checked and repaired or not
     893              :  *
     894              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_MODIFY
     895              :  */
     896            1 : gboolean bd_btrfs_repair (const gchar *device, const BDExtraArg **extra, GError **error) {
     897            1 :     const gchar *argv[5] = {"btrfs", "check", "--repair", device, NULL};
     898              : 
     899            2 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     900            1 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     901            0 :         return FALSE;
     902              : 
     903            1 :     return bd_utils_exec_and_report_error (argv, extra, error);
     904              : }
     905              : 
     906              : /**
     907              :  * bd_btrfs_change_label:
     908              :  * @mountpoint: a mountpoint of the btrfs filesystem to change label of
     909              :  * @label: new label for the filesystem
     910              :  * @extra: (nullable) (array zero-terminated=1): extra options for the volume creation (right now
     911              :  *                                                 passed to the 'btrfs' utility)
     912              :  * @error: (out) (optional): place to store error (if any)
     913              :  *
     914              :  * Returns: whether the label of the @mountpoint filesystem was successfully set
     915              :  * to @label or not
     916              :  *
     917              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_MODIFY
     918              :  */
     919            1 : gboolean bd_btrfs_change_label (const gchar *mountpoint, const gchar *label, GError **error) {
     920            1 :     const gchar *argv[6] = {"btrfs", "filesystem", "label", mountpoint, label, NULL};
     921              : 
     922            2 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
     923            1 :         !check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
     924            0 :         return FALSE;
     925              : 
     926            1 :     return bd_utils_exec_and_report_error (argv, NULL, error);
     927              : }
        

Generated by: LCOV version 2.0-1