LCOV - code coverage report
Current view: top level - plugins/fs - xfs.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 70.2 % 188 132
Test Date: 2026-01-26 13:19:28 Functions: 84.6 % 13 11
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2017  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 <blockdev/utils.h>
      21              : #include <check_deps.h>
      22              : #include <string.h>
      23              : #include <ctype.h>
      24              : 
      25              : #include "xfs.h"
      26              : #include "fs.h"
      27              : #include "common.h"
      28              : 
      29              : static volatile guint avail_deps = 0;
      30              : static GMutex deps_check_lock;
      31              : 
      32              : #define DEPS_MKFSXFS 0
      33              : #define DEPS_MKFSXFS_MASK (1 << DEPS_MKFSXFS)
      34              : #define DEPS_XFS_DB 1
      35              : #define DEPS_XFS_DB_MASK (1 << DEPS_XFS_DB)
      36              : #define DEPS_XFS_REPAIR 2
      37              : #define DEPS_XFS_REPAIR_MASK (1 << DEPS_XFS_REPAIR)
      38              : #define DEPS_XFS_ADMIN 3
      39              : #define DEPS_XFS_ADMIN_MASK (1 << DEPS_XFS_ADMIN)
      40              : #define DEPS_XFS_GROWFS 4
      41              : #define DEPS_XFS_GROWFS_MASK (1 << DEPS_XFS_GROWFS)
      42              : 
      43              : #define DEPS_LAST 5
      44              : 
      45              : static const UtilDep deps[DEPS_LAST] = {
      46              :     {"mkfs.xfs", NULL, NULL, NULL},
      47              :     {"xfs_db", NULL, NULL, NULL},
      48              :     {"xfs_repair", NULL, NULL, NULL},
      49              :     {"xfs_admin", NULL, NULL, NULL},
      50              :     {"xfs_growfs", NULL, NULL, NULL},
      51              : };
      52              : 
      53              : static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
      54              :     DEPS_MKFSXFS_MASK,      /* mkfs */
      55              :     0,                      /* wipe */
      56              :     DEPS_XFS_DB_MASK,       /* check */
      57              :     DEPS_XFS_REPAIR_MASK,   /* repair */
      58              :     DEPS_XFS_ADMIN_MASK,    /* set-label */
      59              :     DEPS_XFS_ADMIN_MASK,    /* query */
      60              :     DEPS_XFS_GROWFS_MASK,   /* resize */
      61              :     DEPS_XFS_ADMIN_MASK     /* set-uuid */
      62              : };
      63              : 
      64              : 
      65              : #ifdef __clang__
      66              : #define ZERO_INIT {}
      67              : #else
      68              : #define ZERO_INIT {0}
      69              : #endif
      70              : 
      71              : /**
      72              :  * bd_fs_xfs_is_tech_avail:
      73              :  * @tech: the queried tech
      74              :  * @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
      75              :  * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
      76              :  *
      77              :  * Returns: whether the @tech-@mode combination is available -- supported by the
      78              :  *          plugin implementation and having all the runtime dependencies available
      79              :  */
      80              : G_GNUC_INTERNAL gboolean
      81            8 : bd_fs_xfs_is_tech_avail (BDFSTech tech G_GNUC_UNUSED, guint64 mode, GError **error) {
      82            8 :     guint32 required = 0;
      83            8 :     guint i = 0;
      84           72 :     for (i = 0; i <= BD_FS_MODE_LAST; i++)
      85           64 :         if (mode & (1 << i))
      86           14 :             required |= fs_mode_util[i];
      87              : 
      88            8 :     return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
      89              : }
      90              : 
      91              : /**
      92              :  * bd_fs_xfs_info_copy: (skip)
      93              :  *
      94              :  * Creates a new copy of @data.
      95              :  */
      96            0 : BDFSXfsInfo* bd_fs_xfs_info_copy (BDFSXfsInfo *data) {
      97            0 :     if (data == NULL)
      98            0 :         return NULL;
      99              : 
     100            0 :     BDFSXfsInfo *ret = g_new0 (BDFSXfsInfo, 1);
     101              : 
     102            0 :     ret->label = g_strdup (data->label);
     103            0 :     ret->uuid = g_strdup (data->uuid);
     104            0 :     ret->block_size = data->block_size;
     105            0 :     ret->block_count = data->block_count;
     106              : 
     107            0 :     return ret;
     108              : }
     109              : 
     110              : /**
     111              :  * bd_fs_xfs_info_free: (skip)
     112              :  *
     113              :  * Frees @data.
     114              :  */
     115            0 : void bd_fs_xfs_info_free (BDFSXfsInfo *data) {
     116            0 :     if (data == NULL)
     117            0 :         return;
     118              : 
     119            0 :     g_free (data->label);
     120            0 :     g_free (data->uuid);
     121            0 :     g_free (data);
     122              : }
     123              : 
     124              : G_GNUC_INTERNAL BDExtraArg **
     125            8 : bd_fs_xfs_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
     126            8 :     GPtrArray *options_array = g_ptr_array_new ();
     127            8 :     const BDExtraArg **extra_p = NULL;
     128            8 :     gchar *uuid_option = NULL;
     129              : 
     130            8 :     if (options->label && g_strcmp0 (options->label, "") != 0)
     131            1 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-L", options->label));
     132              : 
     133            8 :     if (options->uuid && g_strcmp0 (options->uuid, "") != 0) {
     134            1 :         uuid_option = g_strdup_printf ("uuid=%s", options->uuid);
     135            1 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-m", uuid_option));
     136            1 :         g_free (uuid_option);
     137              :     }
     138              : 
     139            8 :     if (options->dry_run)
     140            2 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-N", ""));
     141              : 
     142            8 :     if (options->no_discard)
     143            0 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-K", ""));
     144              : 
     145            8 :     if (options->force)
     146            2 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-f", ""));
     147              : 
     148            8 :     if (extra) {
     149            0 :         for (extra_p = extra; *extra_p; extra_p++)
     150            0 :             g_ptr_array_add (options_array, bd_extra_arg_copy ((BDExtraArg *) *extra_p));
     151              :     }
     152              : 
     153            8 :     g_ptr_array_add (options_array, NULL);
     154              : 
     155            8 :     return (BDExtraArg **) g_ptr_array_free (options_array, FALSE);
     156              : }
     157              : 
     158              : 
     159              : /**
     160              :  * bd_fs_xfs_mkfs:
     161              :  * @device: the device to create a new xfs fs on
     162              :  * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
     163              :  *                                                 passed to the 'mkfs.xfs' utility)
     164              :  * @error: (out) (optional): place to store error (if any)
     165              :  *
     166              :  * Returns: whether a new xfs fs was successfully created on @device or not
     167              :  *
     168              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_MKFS
     169              :  */
     170           24 : gboolean bd_fs_xfs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
     171           24 :     const gchar *args[3] = {"mkfs.xfs", device, NULL};
     172              : 
     173           24 :     if (!check_deps (&avail_deps, DEPS_MKFSXFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     174            0 :         return FALSE;
     175              : 
     176           24 :     return bd_utils_exec_and_report_error (args, extra, error);
     177              : }
     178              : 
     179              : /**
     180              :  * bd_fs_xfs_check:
     181              :  * @device: the device containing the file system to check
     182              :  * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
     183              :  *                                               passed to the 'xfs_repair' utility)
     184              :  * @error: (out) (optional): place to store error (if any)
     185              :  *
     186              :  * Returns: whether an xfs file system on the @device is clean or not
     187              :  *
     188              :  * Note: If the file system is mounted RW, it will always be reported as not
     189              :  *       clean!
     190              :  *
     191              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_CHECK
     192              :  */
     193            5 : gboolean bd_fs_xfs_check (const gchar *device, const BDExtraArg **extra, GError **error) {
     194            5 :     const gchar *args[4] = {"xfs_repair", "-n", device, NULL};
     195            5 :     gboolean ret = FALSE;
     196            5 :     GError *l_error = NULL;
     197              : 
     198            5 :     if (!check_deps (&avail_deps, DEPS_XFS_REPAIR_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     199            0 :         return FALSE;
     200              : 
     201            5 :     ret = bd_utils_exec_and_report_error (args, extra, &l_error);
     202            5 :     if (!ret) {
     203            0 :         if (l_error && g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED)) {
     204              :             /* non-zero exit status -> the fs is not clean, but not an error */
     205              :             /* TODO: should we check that the device exists and contains an XFS FS beforehand? */
     206            0 :             g_clear_error (&l_error);
     207              :         } else
     208            0 :             g_propagate_error (error, l_error);
     209              :     }
     210            5 :     return ret;
     211              : }
     212              : 
     213              : /**
     214              :  * bd_fs_xfs_repair:
     215              :  * @device: the device containing the file system to repair
     216              :  * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
     217              :  *                                                 passed to the 'xfs_repair' utility)
     218              :  * @error: (out) (optional): place to store error (if any)
     219              :  *
     220              :  * Returns: whether an xfs file system on the @device was successfully repaired
     221              :  *          (if needed) or not (error is set in that case)
     222              :  *
     223              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_REPAIR
     224              :  */
     225            5 : gboolean bd_fs_xfs_repair (const gchar *device, const BDExtraArg **extra, GError **error) {
     226            5 :     const gchar *args[3] = {"xfs_repair", device, NULL};
     227              : 
     228            5 :     if (!check_deps (&avail_deps, DEPS_XFS_REPAIR_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     229            0 :         return FALSE;
     230              : 
     231            5 :     return bd_utils_exec_and_report_error (args, extra, error);
     232              : }
     233              : 
     234              : /**
     235              :  * bd_fs_xfs_set_label:
     236              :  * @device: the device containing the file system to set label for
     237              :  * @label: label to set
     238              :  * @error: (out) (optional): place to store error (if any)
     239              :  *
     240              :  * Returns: whether the label of xfs file system on the @device was
     241              :  *          successfully set or not
     242              :  *
     243              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_SET_LABEL
     244              :  */
     245            5 : gboolean bd_fs_xfs_set_label (const gchar *device, const gchar *label, GError **error) {
     246            5 :     const gchar *args[5] = {"xfs_admin", "-L", label, device, NULL};
     247            5 :     if (!label || (strncmp (label, "", 1) == 0))
     248            1 :         args[2] = "--";
     249              : 
     250            5 :     if (!check_deps (&avail_deps, DEPS_XFS_ADMIN_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     251            0 :         return FALSE;
     252              : 
     253            5 :     return bd_utils_exec_and_report_error (args, NULL, error);
     254              : }
     255              : 
     256              : /**
     257              :  * bd_fs_xfs_check_label:
     258              :  * @label: label to check
     259              :  * @error: (out) (optional): place to store error
     260              :  *
     261              :  * Returns: whether @label is a valid label for the xfs file system or not
     262              :  *          (reason is provided in @error)
     263              :  *
     264              :  * Tech category: always available
     265              :  */
     266            5 : gboolean bd_fs_xfs_check_label (const gchar *label, GError **error) {
     267            5 :     size_t len = 0;
     268              : 
     269            5 :     len = strlen (label);
     270            5 :     if (len > 12) {
     271            2 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
     272              :                      "Label for XFS filesystem must be at most 12 characters long.");
     273            2 :         return FALSE;
     274              :     }
     275              : 
     276            3 :     if (strchr (label, ' ') != NULL) {
     277            1 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
     278              :                      "Label for XFS filesystem cannot contain spaces.");
     279            1 :         return FALSE;
     280              :     }
     281              : 
     282            2 :     return TRUE;
     283              : }
     284              : 
     285              : /**
     286              :  * bd_fs_xfs_set_uuid:
     287              :  * @device: the device containing the file system to set uuid for
     288              :  * @uuid: (nullable): UUID to set %NULL to generate a new one
     289              :  *                      UUID can also be one of "nil" and "generate" to clear or
     290              :  *                      generate a new UUID
     291              :  * @error: (out) (optional): place to store error (if any)
     292              :  *
     293              :  * Returns: whether the UUID of xfs file system on the @device was
     294              :  *          successfully set or not
     295              :  *
     296              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_SET_UUID
     297              :  */
     298            6 : gboolean bd_fs_xfs_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
     299            6 :     const gchar *args[5] = {"xfs_admin", "-U", uuid, device, NULL};
     300            6 :     if (!uuid)
     301            2 :         args[2] = "generate";
     302              : 
     303            6 :     if (!check_deps (&avail_deps, DEPS_XFS_ADMIN_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     304            0 :         return FALSE;
     305              : 
     306            6 :     return bd_utils_exec_and_report_error (args, NULL, error);
     307              : }
     308              : 
     309              : /**
     310              :  * bd_fs_xfs_check_uuid:
     311              :  * @uuid: UUID to check
     312              :  * @error: (out) (optional): place to store error
     313              :  *
     314              :  * Returns: whether @uuid is a valid UUID for the xfs file system or not
     315              :  *          (reason is provided in @error)
     316              :  *
     317              :  * Tech category: always available
     318              :  */
     319            4 : gboolean bd_fs_xfs_check_uuid (const gchar *uuid, GError **error) {
     320            4 :     return check_uuid (uuid, error);
     321              : }
     322              : 
     323              : /**
     324              :  * bd_fs_xfs_get_info:
     325              :  * @device: the device containing the file system to get info for
     326              :  * @error: (out) (optional): place to store error (if any)
     327              :  *
     328              :  * Returns: (transfer full): information about the file system on @device or
     329              :  *                           %NULL in case of error
     330              :  *
     331              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_QUERY
     332              :  */
     333           41 : BDFSXfsInfo* bd_fs_xfs_get_info (const gchar *device, GError **error) {
     334           41 :     const gchar *args[6] = ZERO_INIT;
     335           41 :     gboolean success = FALSE;
     336           41 :     gchar *output = NULL;
     337           41 :     BDFSXfsInfo *ret = NULL;
     338           41 :     gchar **lines = NULL;
     339           41 :     gchar **line_p = NULL;
     340           41 :     gchar *val_start = NULL;
     341           41 :     g_autofree gchar* mountpoint = NULL;
     342              : 
     343           41 :     if (!check_deps (&avail_deps, DEPS_XFS_ADMIN_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     344            0 :         return NULL;
     345              : 
     346           41 :     ret = g_new0 (BDFSXfsInfo, 1);
     347              : 
     348           41 :     success = get_uuid_label (device, &(ret->uuid), &(ret->label), error);
     349           41 :     if (!success) {
     350              :         /* error is already populated */
     351            0 :         bd_fs_xfs_info_free (ret);
     352            0 :         return NULL;
     353              :     }
     354              : 
     355              :     /* It is important to use xfs_spaceman for a mounted filesystem
     356              :        since xfs_db might return old information.  xfs_info would be
     357              :        able to do the job for us (running xfs_spaceman or xfs_db
     358              :        depending on whether the fs is mounted), but it doesn't pass
     359              :        "-r" to xfs_db, which is important to avoid spurious udev
     360              :        events just for reading information.
     361              :     */
     362              : 
     363           41 :     mountpoint = bd_fs_get_mountpoint (device, NULL);
     364           41 :     if (mountpoint) {
     365           25 :       args[0] = "xfs_spaceman";
     366           25 :       args[1] = "-c";
     367           25 :       args[2] = "info";
     368           25 :       args[3] = mountpoint;
     369           25 :       args[4] = NULL;
     370              :     } else {
     371           16 :       args[0] = "xfs_db";
     372           16 :       args[1] = "-r";
     373           16 :       args[2] = "-c";
     374           16 :       args[3] = "info";
     375           16 :       args[4] = device;
     376           16 :       args[5] = NULL;
     377              :     }
     378              : 
     379           41 :     success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
     380           41 :     if (!success) {
     381              :         /* error is already populated */
     382            0 :         bd_fs_xfs_info_free (ret);
     383            0 :         return NULL;
     384              :     }
     385              : 
     386           41 :     lines = g_strsplit (output, "\n", 0);
     387           41 :     g_free (output);
     388           41 :     line_p = lines;
     389              :     /* find the beginning of the (data) section we are interested in */
     390          246 :     while (line_p && *line_p && !g_str_has_prefix (*line_p, "data"))
     391          205 :         line_p++;
     392           41 :     if (!line_p || !(*line_p)) {
     393            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
     394            0 :         g_strfreev (lines);
     395            0 :         bd_fs_xfs_info_free (ret);
     396            0 :         return NULL;
     397              :     }
     398              : 
     399              :     /* extract data from something like this: "data     =      bsize=4096   blocks=262400, imaxpct=25" */
     400           41 :     val_start = strchr (*line_p, '=');
     401           41 :     if (!val_start) {
     402            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
     403            0 :         g_strfreev (lines);
     404            0 :         bd_fs_xfs_info_free (ret);
     405            0 :         return NULL;
     406              :     }
     407           41 :     val_start++;
     408          984 :     while (isspace (*val_start))
     409          943 :         val_start++;
     410           41 :     if (g_str_has_prefix (val_start, "bsize")) {
     411           41 :         val_start = strchr (val_start, '=');
     412           41 :         if (!val_start) {
     413            0 :             g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
     414            0 :             g_strfreev (lines);
     415            0 :             bd_fs_xfs_info_free (ret);
     416            0 :             return NULL;
     417              :         }
     418           41 :         val_start++;
     419           41 :         ret->block_size = g_ascii_strtoull (val_start, NULL, 0);
     420              :     } else {
     421            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
     422            0 :         g_strfreev (lines);
     423            0 :         bd_fs_xfs_info_free (ret);
     424            0 :         return NULL;
     425              :     }
     426          328 :     while (isdigit (*val_start) || isspace(*val_start))
     427          287 :         val_start++;
     428           41 :     if (g_str_has_prefix (val_start, "blocks")) {
     429           41 :         val_start = strchr (val_start, '=');
     430           41 :         if (!val_start) {
     431            0 :             g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
     432            0 :             g_strfreev (lines);
     433            0 :             bd_fs_xfs_info_free (ret);
     434            0 :             return NULL;
     435              :         }
     436           41 :         val_start++;
     437           41 :         ret->block_count = g_ascii_strtoull (val_start, NULL, 0);
     438              :     } else {
     439            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
     440            0 :         g_strfreev (lines);
     441            0 :         bd_fs_xfs_info_free (ret);
     442            0 :         return NULL;
     443              :     }
     444           41 :     g_strfreev (lines);
     445              : 
     446           41 :     return ret;
     447              : }
     448              : 
     449              : /**
     450              :  * bd_fs_xfs_resize:
     451              :  * @mpoint: the mount point of the file system to resize
     452              :  * @new_size: new requested size for the file system *in file system blocks* (see bd_fs_xfs_get_info())
     453              :  *            (if 0, the file system is adapted to the underlying block device)
     454              :  * @extra: (nullable) (array zero-terminated=1): extra options for the resize (right now
     455              :  *                                                 passed to the 'xfs_growfs' utility)
     456              :  * @error: (out) (optional): place to store error (if any)
     457              :  *
     458              :  * Returns: whether the file system mounted on @mpoint was successfully resized or not
     459              :  *
     460              :  * Tech category: %BD_FS_TECH_XFS-%BD_FS_TECH_MODE_RESIZE
     461              :  */
     462           16 : gboolean bd_fs_xfs_resize (const gchar *mpoint, guint64 new_size, const BDExtraArg **extra, GError **error) {
     463           16 :     const gchar *args[5] = {"xfs_growfs", NULL, NULL, NULL, NULL};
     464           16 :     gchar *size_str = NULL;
     465           16 :     gboolean ret = FALSE;
     466              : 
     467           16 :     if (!check_deps (&avail_deps, DEPS_XFS_GROWFS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     468            0 :         return FALSE;
     469              : 
     470           16 :     if (new_size != 0) {
     471            3 :         args[1] = "-D";
     472              :         /* xfs_growfs doesn't understand bytes, just a number of blocks */
     473            3 :         size_str = g_strdup_printf ("%"G_GUINT64_FORMAT, new_size);
     474            3 :         args[2] = size_str;
     475            3 :         args[3] = mpoint;
     476              :     } else
     477           13 :         args[1] = mpoint;
     478              : 
     479           16 :     ret = bd_utils_exec_and_report_error (args, extra, error);
     480              : 
     481           16 :     g_free (size_str);
     482           16 :     return ret;
     483              : }
        

Generated by: LCOV version 2.0-1