LCOV - code coverage report
Current view: top level - plugins/fs - btrfs.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 75.7 % 152 115
Test Date: 2026-01-23 09:12:16 Functions: 84.6 % 13 11
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2020  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: Vojtech Trefny <vtrefny@redhat.com>
      18              :  */
      19              : 
      20              : #include <blockdev/utils.h>
      21              : #include <check_deps.h>
      22              : #include <stdio.h>
      23              : 
      24              : #include "btrfs.h"
      25              : #include "fs.h"
      26              : #include "common.h"
      27              : 
      28              : static volatile guint avail_deps = 0;
      29              : static GMutex deps_check_lock;
      30              : 
      31              : #define DEPS_MKFSBTRFS 0
      32              : #define DEPS_MKFSBTRFS_MASK (1 << DEPS_MKFSBTRFS)
      33              : #define DEPS_BTRFSCK 1
      34              : #define DEPS_BTRFSCK_MASK (1 <<  DEPS_BTRFSCK)
      35              : #define DEPS_BTRFS 2
      36              : #define DEPS_BTRFS_MASK (1 <<  DEPS_BTRFS)
      37              : #define DEPS_BTRFSTUNE 3
      38              : #define DEPS_BTRFSTUNE_MASK (1 <<  DEPS_BTRFSTUNE)
      39              : 
      40              : #define DEPS_LAST 4
      41              : 
      42              : static const UtilDep deps[DEPS_LAST] = {
      43              :     {"mkfs.btrfs", NULL, NULL, NULL},
      44              :     {"btrfsck", NULL, NULL, NULL},
      45              :     {"btrfs", NULL, NULL, NULL},
      46              :     {"btrfstune", NULL, NULL, NULL},
      47              : };
      48              : 
      49              : static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
      50              :     DEPS_MKFSBTRFS_MASK,    /* mkfs */
      51              :     0,                      /* wipe */
      52              :     DEPS_BTRFSCK_MASK,      /* check */
      53              :     DEPS_BTRFSCK_MASK,      /* repair */
      54              :     DEPS_BTRFS_MASK,        /* set-label */
      55              :     DEPS_BTRFS_MASK,        /* query */
      56              :     DEPS_BTRFS_MASK,        /* resize */
      57              :     DEPS_BTRFSTUNE_MASK,    /* set-uuid */
      58              : };
      59              : 
      60              : 
      61              : /**
      62              :  * bd_fs_btrfs_is_tech_avail:
      63              :  * @tech: the queried tech
      64              :  * @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
      65              :  * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
      66              :  *
      67              :  * Returns: whether the @tech-@mode combination is available -- supported by the
      68              :  *          plugin implementation and having all the runtime dependencies available
      69              :  */
      70              : G_GNUC_INTERNAL gboolean
      71           92 : bd_fs_btrfs_is_tech_avail (BDFSTech tech G_GNUC_UNUSED, guint64 mode, GError **error) {
      72           92 :     guint32 required = 0;
      73           92 :     guint i = 0;
      74              : 
      75          828 :     for (i = 0; i <= BD_FS_MODE_LAST; i++)
      76          736 :         if (mode & (1 << i))
      77          460 :             required |= fs_mode_util[i];
      78              : 
      79           92 :     return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
      80              : }
      81              : 
      82              : /**
      83              :  * bd_fs_btrfs_info_copy: (skip)
      84              :  *
      85              :  * Creates a new copy of @data.
      86              :  */
      87            0 : BDFSBtrfsInfo* bd_fs_btrfs_info_copy (BDFSBtrfsInfo *data) {
      88            0 :     if (data == NULL)
      89            0 :         return NULL;
      90              : 
      91            0 :     BDFSBtrfsInfo *ret = g_new0 (BDFSBtrfsInfo, 1);
      92              : 
      93            0 :     ret->label = g_strdup (data->label);
      94            0 :     ret->uuid = g_strdup (data->uuid);
      95            0 :     ret->size = data->size;
      96            0 :     ret->free_space = data->free_space;
      97              : 
      98            0 :     return ret;
      99              : }
     100              : 
     101              : /**
     102              :  * bd_fs_btrfs_info_free: (skip)
     103              :  *
     104              :  * Frees @data.
     105              :  */
     106            0 : void bd_fs_btrfs_info_free (BDFSBtrfsInfo *data) {
     107            0 :     if (data == NULL)
     108            0 :         return;
     109              : 
     110            0 :     g_free (data->label);
     111            0 :     g_free (data->uuid);
     112            0 :     g_free (data);
     113              : }
     114              : 
     115              : G_GNUC_INTERNAL BDExtraArg **
     116            6 : bd_fs_btrfs_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
     117            6 :     GPtrArray *options_array = g_ptr_array_new ();
     118            6 :     const BDExtraArg **extra_p = NULL;
     119              : 
     120            6 :     if (options->label && g_strcmp0 (options->label, "") != 0)
     121            1 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-L", options->label));
     122              : 
     123            6 :     if (options->uuid && g_strcmp0 (options->uuid, "") != 0)
     124            1 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-U", options->uuid));
     125              : 
     126            6 :     if (options->no_discard)
     127            0 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-K", ""));
     128              : 
     129            6 :     if (options->force)
     130            2 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-f", ""));
     131              : 
     132            6 :     if (extra) {
     133            0 :         for (extra_p = extra; *extra_p; extra_p++)
     134            0 :             g_ptr_array_add (options_array, bd_extra_arg_copy ((BDExtraArg *) *extra_p));
     135              :     }
     136              : 
     137            6 :     g_ptr_array_add (options_array, NULL);
     138              : 
     139            6 :     return (BDExtraArg **) g_ptr_array_free (options_array, FALSE);
     140              : }
     141              : 
     142              : /**
     143              :  * bd_fs_btrfs_mkfs:
     144              :  * @device: the device to create a new btrfs fs on
     145              :  * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
     146              :  *                                                 passed to the 'mkfs.btrfs' utility)
     147              :  * @error: (out) (optional): place to store error (if any)
     148              :  *
     149              :  * Returns: whether a new btrfs fs was successfully created on @device or not
     150              :  *
     151              :  * Tech category: %BD_FS_TECH_BTRFS-%BD_FS_TECH_MODE_MKFS
     152              :  *
     153              :  */
     154           22 : gboolean bd_fs_btrfs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
     155           22 :     const gchar *args[3] = {"mkfs.btrfs", device, NULL};
     156              : 
     157           22 :     if (!check_deps (&avail_deps, DEPS_MKFSBTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     158            0 :         return FALSE;
     159              : 
     160           22 :     return bd_utils_exec_and_report_error (args, extra, error);
     161              : }
     162              : 
     163              : /**
     164              :  * bd_fs_btrfs_check:
     165              :  * @device: the device containing the file system to check
     166              :  * @extra: (nullable) (array zero-terminated=1): extra options for the check (right now
     167              :  *                                                 passed to the 'btrfsck' utility)
     168              :  * @error: (out) (optional): place to store error (if any)
     169              :  *
     170              :  * Returns: whether the filesystem was successfully checked or not
     171              :  *
     172              :  * Tech category: %BD_FS_TECH_BTRFS-%BD_FS_TECH_MODE_CHECK
     173              :  */
     174            3 : gboolean bd_fs_btrfs_check (const gchar *device, const BDExtraArg **extra, GError **error) {
     175            3 :     const gchar *argv[4] = {"btrfsck", device, NULL};
     176              : 
     177            3 :     if (!check_deps (&avail_deps, DEPS_BTRFSCK_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     178            0 :         return FALSE;
     179              : 
     180            3 :     return bd_utils_exec_and_report_error (argv, extra, error);
     181              : }
     182              : 
     183              : /**
     184              :  * bd_fs_btrfs_repair:
     185              :  * @device: the device containing the file system to repair
     186              :  * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
     187              :  *                                                 passed to the 'btrfsck' utility)
     188              :  * @error: (out) (optional): place to store error (if any)
     189              :  *
     190              :  * Returns: whether the filesystem was successfully checked and repaired or not
     191              :  *
     192              :  * Tech category: %BD_FS_TECH_BTRFS-%BD_FS_TECH_MODE_REPAIR
     193              :  */
     194            3 : gboolean bd_fs_btrfs_repair (const gchar *device, const BDExtraArg **extra, GError **error) {
     195            3 :     const gchar *argv[5] = {"btrfsck", "--repair", device, NULL};
     196              : 
     197            3 :     if (!check_deps (&avail_deps, DEPS_BTRFSCK_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     198            0 :         return FALSE;
     199              : 
     200            3 :     return bd_utils_exec_and_report_error (argv, extra, error);
     201              : }
     202              : 
     203              : /**
     204              :  * bd_fs_btrfs_set_label:
     205              :  * @mpoint: the mount point of the file system to set label for
     206              :  * @label: label to set
     207              :  * @error: (out) (optional): place to store error (if any)
     208              :  *
     209              :  * Returns: whether the label of btrfs file system on the @mpoint was
     210              :  *          successfully set or not
     211              :  *
     212              :  * Note: This function is intended to be used for btrfs filesystem on a single device,
     213              :  *       for more complicated setups use the btrfs plugin instead.
     214              :  *
     215              :  * Tech category: %BD_FS_TECH_BTRFS-%BD_FS_TECH_MODE_SET_LABEL
     216              :  */
     217            5 : gboolean bd_fs_btrfs_set_label (const gchar *mpoint, const gchar *label, GError **error) {
     218            5 :     const gchar *argv[6] = {"btrfs", "filesystem", "label", mpoint, label, NULL};
     219              : 
     220            5 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     221            0 :         return FALSE;
     222              : 
     223            5 :     return bd_utils_exec_and_report_error (argv, NULL, error);
     224              : }
     225              : 
     226              : /**
     227              :  * bd_fs_btrfs_check_label:
     228              :  * @label: label to check
     229              :  * @error: (out) (optional): place to store error
     230              :  *
     231              :  * Returns: whether @label is a valid label for the btrfs file system or not
     232              :  *          (reason is provided in @error)
     233              :  *
     234              :  * Note: This function is intended to be used for btrfs filesystem on a single device,
     235              :  *       for more complicated setups use the btrfs plugin instead.
     236              :  *
     237              :  * Tech category: always available
     238              :  */
     239            4 : gboolean bd_fs_btrfs_check_label (const gchar *label, GError **error) {
     240            4 :     if (strlen (label) > 256) {
     241            1 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
     242              :                      "Label for btrfs filesystem must be at most 256 characters long.");
     243            1 :         return FALSE;
     244              :     }
     245              : 
     246            3 :     if (strchr (label, '\n') != NULL) {
     247            1 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
     248              :                      "Label for btrfs filesystem cannot contain new lines.");
     249            1 :         return FALSE;
     250              :     }
     251              : 
     252            2 :     return TRUE;
     253              : }
     254              : 
     255              : /**
     256              :  * bd_fs_btrfs_set_uuid:
     257              :  * @device: the device containing the file system to set the UUID (serial number) for
     258              :  * @uuid: (nullable): UUID to set or %NULL to generate a new one
     259              :  * @error: (out) (optional): place to store error (if any)
     260              :  *
     261              :  * Returns: whether the UUID of the btrfs file system on the @device was
     262              :  *          successfully set or not
     263              :  *
     264              :  * Note: This function is intended to be used for btrfs filesystem on a single device,
     265              :  *       for more complicated setups use the btrfs plugin instead.
     266              :  *
     267              :  * Tech category: %BD_FS_TECH_BTRFS-%BD_FS_TECH_MODE_SET_UUID
     268              :  */
     269            4 : gboolean bd_fs_btrfs_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
     270            4 :     const gchar *args[5] = {"btrfstune", NULL, NULL, NULL, NULL};
     271              : 
     272            4 :     if (!check_deps (&avail_deps, DEPS_BTRFSTUNE_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     273            0 :         return FALSE;
     274              : 
     275            4 :     if (!uuid) {
     276            2 :         args[1] = "-u";
     277            2 :         args[2] = device;
     278              :     } else {
     279            2 :         args[1] = "-U";
     280            2 :         args[2] = uuid;
     281            2 :         args[3] = device;
     282              :     }
     283              : 
     284            4 :     return bd_utils_exec_with_input (args, "y\n", NULL, error);
     285              : }
     286              : 
     287              : /**
     288              :  * bd_fs_btrfs_check_uuid:
     289              :  * @uuid: UUID to check
     290              :  * @error: (out) (optional): place to store error
     291              :  *
     292              :  * Returns: whether @uuid is a valid UUID for the btrfs file system or not
     293              :  *          (reason is provided in @error)
     294              :  *
     295              :  * Note: This function is intended to be used for btrfs filesystem on a single device,
     296              :  *       for more complicated setups use the btrfs plugin instead.
     297              :  *
     298              :  * Tech category: always available
     299              :  */
     300            3 : gboolean bd_fs_btrfs_check_uuid (const gchar *uuid, GError **error) {
     301            3 :     return check_uuid (uuid, error);
     302              : }
     303              : 
     304              : /**
     305              :  * bd_fs_btrfs_get_info:
     306              :  * @mpoint: a mountpoint of the btrfs filesystem to get information about
     307              :  * @error: (out) (optional): place to store error (if any)
     308              :  *
     309              :  * Returns: (transfer full): information about the file system on @device or
     310              :  *                           %NULL in case of error
     311              :  *
     312              :  * Note: This function WON'T WORK for multi device btrfs filesystems,
     313              :  *       for more complicated setups use the btrfs plugin instead.
     314              :  *
     315              :  * Tech category: %BD_FS_TECH_BTRFS-%BD_FS_TECH_MODE_QUERY
     316              :  */
     317           34 : BDFSBtrfsInfo* bd_fs_btrfs_get_info (const gchar *mpoint, GError **error) {
     318           34 :     const gchar *argv[6] = {"btrfs", "filesystem", "show", "--raw", mpoint, NULL};
     319           34 :     g_autofree gchar *output = NULL;
     320           34 :     gboolean success = FALSE;
     321           34 :     gchar const * const pattern = "Label:\\s+(none|'(?P<label>.+)')\\s+" \
     322              :                                   "uuid:\\s+(?P<uuid>\\S+)\\s+" \
     323              :                                   "Total\\sdevices\\s+(?P<num_devices>\\d+)\\s+" \
     324              :                                   "FS\\sbytes\\sused\\s+(?P<used>\\S+)\\s+" \
     325              :                                   "devid\\s+1\\s+size\\s+(?P<size>\\S+)\\s+\\S+";
     326           34 :     GRegex *regex = NULL;
     327           34 :     GMatchInfo *match_info = NULL;
     328           34 :     BDFSBtrfsInfo *ret = NULL;
     329           34 :     g_autofree gchar *item = NULL;
     330           34 :     guint64 num_devices = 0;
     331           34 :     guint64 min_size = 0;
     332           34 :     gint scanned = 0;
     333              : 
     334           34 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     335            0 :         return NULL;
     336              : 
     337           34 :     regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, error);
     338           34 :     if (!regex) {
     339            0 :         bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
     340              :         /* error is already populated */
     341            0 :         return NULL;
     342              :     }
     343              : 
     344           34 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
     345           34 :     if (!success) {
     346              :         /* error is already populated from the call above or just empty
     347              :            output */
     348            0 :         g_regex_unref (regex);
     349            0 :         return NULL;
     350              :     }
     351              : 
     352           34 :     success = g_regex_match (regex, output, 0, &match_info);
     353           34 :     if (!success) {
     354            0 :         g_regex_unref (regex);
     355            0 :         g_match_info_free (match_info);
     356            0 :         return NULL;
     357              :     }
     358              : 
     359           34 :     ret = g_new (BDFSBtrfsInfo, 1);
     360              : 
     361           34 :     ret->label = g_match_info_fetch_named (match_info, "label");
     362           34 :     ret->uuid = g_match_info_fetch_named (match_info, "uuid");
     363              : 
     364           34 :     item = g_match_info_fetch_named (match_info, "num_devices");
     365           34 :     num_devices = g_ascii_strtoull (item, NULL, 0);
     366           34 :     if (num_devices != 1) {
     367            2 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
     368              :                      "Btrfs filesystem mounted on %s spans multiple devices (%"G_GUINT64_FORMAT")." \
     369              :                      "Filesystem plugin is not suitable for multidevice Btrfs volumes, please use " \
     370              :                      "Btrfs plugin instead.", mpoint, num_devices);
     371            2 :         g_match_info_free (match_info);
     372            2 :         g_regex_unref (regex);
     373            2 :         bd_fs_btrfs_info_free (ret);
     374            2 :         return NULL;
     375              :     }
     376              : 
     377           32 :     item = g_match_info_fetch_named (match_info, "size");
     378           32 :     ret->size = g_ascii_strtoull (item, NULL, 0);
     379              : 
     380           32 :     g_match_info_free (match_info);
     381           32 :     g_regex_unref (regex);
     382              : 
     383           32 :     argv[1] = "inspect-internal";
     384           32 :     argv[2] = "min-dev-size";
     385           32 :     argv[3] = mpoint;
     386           32 :     argv[4] = NULL;
     387              : 
     388           32 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
     389           32 :     if (!success) {
     390              :         /* error is already populated from the call above or just empty
     391              :            output */
     392            0 :         bd_fs_btrfs_info_free (ret);
     393            0 :         return NULL;
     394              :     }
     395              : 
     396              :     /* 114032640 bytes (108.75MiB) */
     397           32 :     scanned = sscanf (output, " %" G_GUINT64_FORMAT " bytes", &min_size);
     398           32 :     if (scanned != 1) {
     399            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE,
     400              :                      "Failed to parse btrfs filesystem min size.");
     401            0 :         bd_fs_btrfs_info_free (ret);
     402            0 :         return NULL;
     403              :     }
     404              : 
     405           32 :     ret->free_space = ret->size - min_size;
     406              : 
     407           32 :     return ret;
     408              : }
     409              : 
     410              : /**
     411              :  * bd_fs_btrfs_resize:
     412              :  * @mpoint: a mountpoint of the to be resized btrfs filesystem
     413              :  * @new_size: requested new size
     414              :  * @extra: (nullable) (array zero-terminated=1): extra options for the volume resize (right now
     415              :  *                                                 passed to the 'btrfs' utility)
     416              :  * @error: (out) (optional): place to store error (if any)
     417              :  *
     418              :  * Returns: whether the @mpoint filesystem was successfully resized to @new_size
     419              :  * or not
     420              :  *
     421              :  * Note: This function WON'T WORK for multi device btrfs filesystems,
     422              :  *       for more complicated setups use the btrfs plugin instead.
     423              :  *
     424              :  * Tech category: %BD_BTRFS_TECH_FS-%BD_BTRFS_TECH_MODE_MODIFY
     425              :  */
     426            9 : gboolean bd_fs_btrfs_resize (const gchar *mpoint, guint64 new_size, const BDExtraArg **extra, GError **error) {
     427            9 :     const gchar *argv[6] = {"btrfs", "filesystem", "resize", NULL, mpoint, NULL};
     428            9 :     gboolean ret = FALSE;
     429            9 :     BDFSBtrfsInfo *info = NULL;
     430              : 
     431            9 :     if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     432            0 :         return FALSE;
     433              : 
     434              :     /* we don't want to allow resizing multidevice btrfs volumes and get_info
     435              :        returns error for these so just try to get the info here */
     436            9 :     info = bd_fs_btrfs_get_info (mpoint, error);
     437            9 :     if (!info)
     438            1 :         return FALSE;
     439            8 :     bd_fs_btrfs_info_free (info);
     440              : 
     441            8 :     if (new_size == 0)
     442            3 :         argv[3] = g_strdup ("max");
     443              :     else
     444            5 :         argv[3] = g_strdup_printf ("%"G_GUINT64_FORMAT, new_size);
     445              : 
     446            8 :     ret = bd_utils_exec_and_report_error (argv, extra, error);
     447            8 :     g_free ((gchar *) argv[3]);
     448              : 
     449            8 :     return ret;
     450              : }
        

Generated by: LCOV version 2.0-1