LCOV - code coverage report
Current view: top level - plugins/fs - f2fs.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 74.0 % 169 125
Test Date: 2026-01-26 13:19:28 Functions: 81.8 % 11 9
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2019  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              : 
      23              : #include "f2fs.h"
      24              : #include "fs.h"
      25              : #include "common.h"
      26              : 
      27              : static volatile guint avail_deps = 0;
      28              : static volatile guint avail_shrink_deps = 0;
      29              : static GMutex deps_check_lock;
      30              : 
      31              : #define DEPS_MKFSF2FS 0
      32              : #define DEPS_MKFSF2FS_MASK (1 << DEPS_MKFSF2FS)
      33              : #define DEPS_CHECKF2FS 1
      34              : #define DEPS_CHECKF2FS_MASK (1 << DEPS_CHECKF2FS)
      35              : #define DEPS_FSCKF2FS 2
      36              : #define DEPS_FSCKF2FS_MASK (1 << DEPS_FSCKF2FS)
      37              : #define DEPS_DUMPF2FS 3
      38              : #define DEPS_DUMPF2FS_MASK (1 << DEPS_DUMPF2FS)
      39              : #define DEPS_RESIZEF2FS 4
      40              : #define DEPS_RESIZEF2FS_MASK (1 << DEPS_RESIZEF2FS)
      41              : 
      42              : #define DEPS_LAST 5
      43              : 
      44              : static const UtilDep deps[DEPS_LAST] = {
      45              :     {"mkfs.f2fs", NULL, NULL, NULL},
      46              :     {"fsck.f2fs", "1.11.0", "-V", "fsck.f2fs\\s+([\\d\\.]+).+"},
      47              :     {"fsck.f2fs", NULL, NULL, NULL},
      48              :     {"dump.f2fs", NULL, NULL, NULL},
      49              :     {"resize.f2fs", NULL, NULL, NULL}
      50              : };
      51              : 
      52              : /* shrinking needs newer version of f2fs */
      53              : #define SHRINK_DEPS_RESIZEF2FS 0
      54              : #define SHRINK_DEPS_RESIZEF2FS_MASK (1 << SHRINK_DEPS_RESIZEF2FS)
      55              : 
      56              : #define SHRINK_DEPS_LAST 1
      57              : 
      58              : static const UtilDep shrink_deps[SHRINK_DEPS_LAST] = {
      59              :     {"resize.f2fs", "1.12.0", "-V", "resize.f2fs\\s+([\\d\\.]+).+"}
      60              : };
      61              : 
      62              : static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
      63              :     DEPS_MKFSF2FS_MASK,     /* mkfs */
      64              :     0,                      /* wipe */
      65              :     DEPS_CHECKF2FS_MASK,    /* check */
      66              :     DEPS_FSCKF2FS_MASK,     /* repair */
      67              :     0,                      /* set-label */
      68              :     DEPS_DUMPF2FS_MASK,     /* query */
      69              :     DEPS_RESIZEF2FS_MASK,   /* resize */
      70              :     0                       /* set-uuid */
      71              : };
      72              : 
      73              : 
      74              : /* option to get version was added in 1.11.0 so we need to cover situation
      75              :    where the version is too old to check the version */
      76            7 : static gboolean can_check_f2fs_version (UtilDep dep, GError **error) {
      77            7 :     gboolean available = FALSE;
      78            7 :     GError *loc_error = NULL;
      79              : 
      80            7 :     available = bd_utils_check_util_version (dep.name, dep.version,
      81              :                                              dep.ver_arg, dep.ver_regexp,
      82              :                                              &loc_error);
      83            7 :     if (!available) {
      84            2 :         if (g_error_matches (loc_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_UNKNOWN_VER)) {
      85              :             /* assuming version of f2fs is too low to check version of f2fs */
      86            1 :             g_clear_error (&loc_error);
      87            1 :             g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_LOW_VER,
      88              :                          "Too low version of %s. At least %s required.",
      89              :                          dep.name, dep.version);
      90            1 :             return FALSE;
      91              :         }
      92              :     } else {
      93              :         /* just ignore other errors (e.g. version was detected but is still
      94              :            too low) -- check_deps call below will cover this and create a
      95              :            better error message for these cases */
      96            5 :         g_clear_error (&loc_error);
      97              :     }
      98              : 
      99            6 :     return TRUE;
     100              : }
     101              : 
     102              : /**
     103              :  * bd_fs_f2fs_is_tech_avail:
     104              :  * @tech: the queried tech
     105              :  * @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
     106              :  * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
     107              :  *
     108              :  * Returns: whether the @tech-@mode combination is available -- supported by the
     109              :  *          plugin implementation and having all the runtime dependencies available
     110              :  */
     111              : G_GNUC_INTERNAL gboolean
     112          106 : bd_fs_f2fs_is_tech_avail (BDFSTech tech G_GNUC_UNUSED, guint64 mode, GError **error) {
     113          106 :     guint32 required = 0;
     114          106 :     guint i = 0;
     115              : 
     116          106 :     if (mode & BD_FS_TECH_MODE_SET_LABEL) {
     117            1 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_TECH_UNAVAIL,
     118              :                      "F2FS doesn't support setting label for an existing device.");
     119            1 :         return FALSE;
     120              :     }
     121              : 
     122          105 :     if (mode & BD_FS_TECH_MODE_SET_UUID) {
     123            1 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_TECH_UNAVAIL,
     124              :                      "F2FS doesn't support setting UUID for an existing device.");
     125            1 :         return FALSE;
     126              :     }
     127              : 
     128          104 :     if (mode & BD_FS_TECH_MODE_CHECK) {
     129            6 :         if (!can_check_f2fs_version (deps[DEPS_CHECKF2FS], error))
     130            1 :             return FALSE;
     131              :     }
     132              : 
     133          927 :     for (i = 0; i <= BD_FS_MODE_LAST; i++)
     134          824 :         if (mode & (1 << i))
     135          105 :             required |= fs_mode_util[i];
     136              : 
     137          103 :     return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
     138              : }
     139              : 
     140              : /**
     141              :  * bd_fs_f2fs_info_copy: (skip)
     142              :  *
     143              :  * Creates a new copy of @data.
     144              :  */
     145            0 : BDFSF2FSInfo* bd_fs_f2fs_info_copy (BDFSF2FSInfo *data) {
     146            0 :     if (data == NULL)
     147            0 :         return NULL;
     148              : 
     149            0 :     BDFSF2FSInfo *ret = g_new0 (BDFSF2FSInfo, 1);
     150              : 
     151            0 :     ret->label = g_strdup (data->label);
     152            0 :     ret->uuid = g_strdup (data->uuid);
     153            0 :     ret->sector_size = data->sector_size;
     154            0 :     ret->sector_count = data->sector_count;
     155            0 :     ret->features = data->features;
     156              : 
     157            0 :     return ret;
     158              : }
     159              : 
     160              : /**
     161              :  * bd_fs_f2fs_info_free: (skip)
     162              :  *
     163              :  * Frees @data.
     164              :  */
     165            0 : void bd_fs_f2fs_info_free (BDFSF2FSInfo *data) {
     166            0 :     if (data == NULL)
     167            0 :         return;
     168              : 
     169            0 :     g_free (data->label);
     170            0 :     g_free (data->uuid);
     171            0 :     g_free (data);
     172              : }
     173              : 
     174              : G_GNUC_INTERNAL BDExtraArg **
     175            6 : bd_fs_f2fs_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
     176            6 :     GPtrArray *options_array = g_ptr_array_new ();
     177            6 :     const BDExtraArg **extra_p = NULL;
     178              : 
     179            6 :     if (options->label && g_strcmp0 (options->label, "") != 0)
     180            1 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-l", options->label));
     181              : 
     182            6 :     if (options->no_discard)
     183            0 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-t", "nodiscard"));
     184              : 
     185            6 :     if (options->force)
     186            2 :         g_ptr_array_add (options_array, bd_extra_arg_new ("-f", ""));
     187              : 
     188            6 :     if (extra) {
     189            0 :         for (extra_p = extra; *extra_p; extra_p++)
     190            0 :             g_ptr_array_add (options_array, bd_extra_arg_copy ((BDExtraArg *) *extra_p));
     191              :     }
     192              : 
     193            6 :     g_ptr_array_add (options_array, NULL);
     194              : 
     195            6 :     return (BDExtraArg **) g_ptr_array_free (options_array, FALSE);
     196              : }
     197              : 
     198              : 
     199              : /**
     200              :  * bd_fs_f2fs_mkfs:
     201              :  * @device: the device to create a new f2fs fs on
     202              :  * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
     203              :  *                                                 passed to the 'mkfs.f2fs' utility)
     204              :  * @error: (out) (optional): place to store error (if any)
     205              :  *
     206              :  * Returns: whether a new f2fs fs was successfully created on @device or not
     207              :  *
     208              :  * Tech category: %BD_FS_TECH_F2FS-%BD_FS_TECH_MODE_MKFS
     209              :  */
     210           18 : gboolean bd_fs_f2fs_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
     211           18 :     const gchar *args[3] = {"mkfs.f2fs", device, NULL};
     212              : 
     213           18 :     if (!check_deps (&avail_deps, DEPS_MKFSF2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     214            0 :         return FALSE;
     215              : 
     216           18 :     return bd_utils_exec_and_report_error (args, extra, error);
     217              : }
     218              : 
     219              : /**
     220              :  * bd_fs_f2fs_check:
     221              :  * @device: the device containing the file system to check
     222              :  * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
     223              :  *                                                 passed to the 'fsck.f2fs' utility)
     224              :  * @error: (out) (optional): place to store error (if any)
     225              :  *
     226              :  * Returns: whether an f2fs file system on the @device is clean or not
     227              :  *
     228              :  * Tech category: %BD_FS_TECH_F2FS-%BD_FS_TECH_MODE_CHECK
     229              :  */
     230            3 : gboolean bd_fs_f2fs_check (const gchar *device, const BDExtraArg **extra, GError **error) {
     231            3 :     const gchar *args[4] = {"fsck.f2fs", "--dry-run", device, NULL};
     232            3 :     gint status = 0;
     233            3 :     gboolean ret = FALSE;
     234              : 
     235            3 :     if (!bd_fs_f2fs_is_tech_avail (BD_FS_TECH_F2FS, BD_FS_TECH_MODE_CHECK, error))
     236            0 :         return FALSE;
     237              : 
     238            3 :     ret = bd_utils_exec_and_report_status_error (args, extra, &status, error);
     239            3 :     if (!ret && (status == 255)) {
     240              :         /* no error should be reported for exit code 255 -- there are errors on the filesystem */
     241            0 :         g_clear_error (error);
     242              :     }
     243            3 :     return ret;
     244              : }
     245              : 
     246              : /**
     247              :  * bd_fs_f2fs_repair:
     248              :  * @device: the device containing the file system to repair
     249              :  * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
     250              :  *                                                 passed to the 'fsck.f2fs' utility)
     251              :  * @error: (out) (optional): place to store error (if any)
     252              :  *
     253              :  * Returns: whether an f2fs file system on the @device was successfully repaired
     254              :  *          (if needed) or not (error is set in that case)
     255              :  *
     256              :  * Tech category: %BD_FS_TECH_F2FS-%BD_FS_TECH_MODE_REPAIR
     257              :  */
     258            3 : gboolean bd_fs_f2fs_repair (const gchar *device, const BDExtraArg **extra, GError **error) {
     259            3 :     const gchar *args[4] = {"fsck.f2fs", "-a", device, NULL};
     260              : 
     261            3 :     if (!check_deps (&avail_deps, DEPS_FSCKF2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     262            0 :         return FALSE;
     263              : 
     264            3 :     return bd_utils_exec_and_report_error (args, extra, error);
     265              : }
     266              : 
     267              : /**
     268              :  * bd_fs_f2fs_get_info:
     269              :  * @device: the device containing the file system to get info for
     270              :  * @error: (out) (optional): place to store error (if any)
     271              :  *
     272              :  * Returns: (transfer full): information about the file system on @device or
     273              :  *                           %NULL in case of error
     274              :  *
     275              :  * Tech category: %BD_FS_TECH_F2FS-%BD_FS_TECH_MODE_QUERY
     276              :  */
     277            8 : BDFSF2FSInfo* bd_fs_f2fs_get_info (const gchar *device, GError **error) {
     278            8 :     const gchar *argv[3] = {"dump.f2fs", device, NULL};
     279            8 :     gchar *output = NULL;
     280            8 :     gboolean success = FALSE;
     281            8 :     BDFSF2FSInfo*ret = NULL;
     282            8 :     gchar **lines = NULL;
     283            8 :     gchar **line_p = NULL;
     284            8 :     gchar *val_start = NULL;
     285              : 
     286            8 :     if (!check_deps (&avail_deps, DEPS_DUMPF2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     287            0 :         return NULL;
     288              : 
     289            8 :     success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
     290            8 :     if (!success) {
     291              :         /* error is already populated from the call above or just empty
     292              :            output */
     293            0 :         return NULL;
     294              :     }
     295              : 
     296            8 :     ret = g_new0 (BDFSF2FSInfo, 1);
     297              : 
     298            8 :     success = get_uuid_label (device, &(ret->uuid), &(ret->label), error);
     299            8 :     if (!success) {
     300              :         /* error is already populated */
     301            0 :         bd_fs_f2fs_info_free (ret);
     302            0 :         g_free (output);
     303            0 :         return NULL;
     304              :     }
     305              : 
     306            8 :     lines = g_strsplit (output, "\n", 0);
     307            8 :     g_free (output);
     308            8 :     line_p = lines;
     309              : 
     310          137 :     while (line_p && *line_p && !g_str_has_prefix (*line_p, "Info: sector size"))
     311          129 :         line_p++;
     312            8 :     if (!line_p || !(*line_p)) {
     313              :         /* Sector size is not printed with dump.f2fs 1.15 */
     314            8 :         ret->sector_size = 0;
     315              :     } else {
     316              :         /* extract data from something like this: "Info: sector size = 4096" */
     317            0 :         val_start = strchr (*line_p, '=');
     318            0 :         val_start++;
     319            0 :         ret->sector_size = g_ascii_strtoull (val_start, NULL, 0);
     320              :     }
     321              : 
     322            8 :     line_p = lines;
     323           88 :     while (line_p && *line_p && !g_str_has_prefix (*line_p, "Info: total FS sectors"))
     324           80 :         line_p++;
     325            8 :     if (!line_p || !(*line_p)) {
     326            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse F2FS file system information");
     327            0 :         g_strfreev (lines);
     328            0 :         bd_fs_f2fs_info_free (ret);
     329            0 :         return NULL;
     330              :     }
     331              : 
     332              :     /* extract data from something like this: "Info: total sectors = 3932160 (15360 MB)" */
     333            8 :     val_start = strchr (*line_p, '=');
     334            8 :     val_start++;
     335            8 :     ret->sector_count = g_ascii_strtoull (val_start, NULL, 0);
     336              : 
     337            8 :     line_p = lines;
     338           56 :     while (line_p && *line_p && !g_str_has_prefix (*line_p, "Info: superblock features"))
     339           48 :         line_p++;
     340            8 :     if (!line_p || !(*line_p)) {
     341            0 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse F2FS file system information");
     342            0 :         g_strfreev (lines);
     343            0 :         bd_fs_f2fs_info_free (ret);
     344            0 :         return NULL;
     345              :     }
     346              : 
     347              :     /* extract data from something like this: "Info: superblock features = 0" */
     348            8 :     val_start = strchr (*line_p, '=');
     349            8 :     val_start++;
     350            8 :     ret->features = g_ascii_strtoull (val_start, NULL, 16);
     351              : 
     352            8 :     g_strfreev (lines);
     353            8 :     return ret;
     354              : }
     355              : 
     356              : /**
     357              :  * bd_fs_f2fs_resize:
     358              :  * @device: the device containing the file system to resize
     359              :  * @new_size: new requested size for the file system *in file system sectors* (see bd_fs_f2fs_get_info())
     360              :  *            (if 0, the file system is adapted to the underlying block device)
     361              :  * @safe: whether to perform safe resize or not (does not resize metadata)
     362              :  * @extra: (nullable) (array zero-terminated=1): extra options for the resize (right now
     363              :  *                                                 passed to the 'resize.f2fs' utility)
     364              :  * @error: (out) (optional): place to store error (if any)
     365              :  *
     366              :  * Returns: whether the file system on @device was successfully resized or not
     367              :  *
     368              :  * Tech category: %BD_FS_TECH_F2FS-%BD_FS_TECH_MODE_RESIZE
     369              :  */
     370            2 : gboolean bd_fs_f2fs_resize (const gchar *device, guint64 new_size, gboolean safe, const BDExtraArg **extra, GError **error) {
     371            2 :     const gchar *args[6] = {"resize.f2fs", NULL, NULL, NULL, NULL, NULL};
     372            2 :     gchar *size_str = NULL;
     373            2 :     gboolean ret = FALSE;
     374            2 :     guint next_arg = 1;
     375            2 :     BDFSF2FSInfo *info = NULL;
     376              : 
     377            2 :     if (!check_deps (&avail_deps, DEPS_RESIZEF2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
     378            0 :         return FALSE;
     379              : 
     380            2 :     if (safe) {
     381            2 :         if (!can_check_f2fs_version (shrink_deps[SHRINK_DEPS_RESIZEF2FS], error) ||
     382            1 :             !check_deps (&avail_shrink_deps, SHRINK_DEPS_RESIZEF2FS_MASK, shrink_deps, SHRINK_DEPS_LAST, &deps_check_lock, error)) {
     383              :             /* f2fs version is too low to either even check its version or
     384              :                to perform safe resize (shrink) */
     385            0 :             g_prefix_error (error, "Can't perform safe resize: ");
     386            0 :             return FALSE;
     387              :         }
     388              :     }
     389              : 
     390            2 :     info = bd_fs_f2fs_get_info (device, error);
     391            2 :     if (!info) {
     392              :       /* error is already populated */
     393            0 :       return FALSE;
     394              :     }
     395              : 
     396            2 :     if (new_size != 0 && new_size < info->sector_count && !safe) {
     397              :         /* resize.f2fs prints error and returns 0 in this case */
     398            1 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_INVAL,
     399              :                      "F2FS filesystem doesn't support shrinking without using the 'safe' option");
     400            1 :         bd_fs_f2fs_info_free (info);
     401            1 :         return FALSE;
     402              :     }
     403            1 :     bd_fs_f2fs_info_free (info);
     404              : 
     405            1 :     if (safe) {
     406            1 :         args[next_arg++] = "-s";
     407              :     }
     408              : 
     409            1 :     if (new_size != 0) {
     410            1 :         args[next_arg++] = "-t";
     411            1 :         size_str = g_strdup_printf ("%"G_GUINT64_FORMAT, new_size);
     412            1 :         args[next_arg++] = size_str;
     413            1 :         args[next_arg++] = device;
     414              :     } else
     415            0 :         args[next_arg++] = device;
     416              : 
     417            1 :     ret = bd_utils_exec_and_report_error (args, extra, error);
     418              : 
     419            1 :     g_free (size_str);
     420            1 :     return ret;
     421              : }
     422              : 
     423              : /**
     424              :  * bd_fs_f2fs_check_label:
     425              :  * @label: label to check
     426              :  * @error: (out) (optional): place to store error
     427              :  *
     428              :  * Returns: whether @label is a valid label for the f2fs file system or not
     429              :  *          (reason is provided in @error)
     430              :  *
     431              :  * Tech category: always available
     432              :  */
     433            4 : gboolean bd_fs_f2fs_check_label (const gchar *label, GError **error) {
     434            4 :     size_t len = 0;
     435              : 
     436            4 :     len = strlen (label);
     437            4 :     if (len > 512) {
     438            2 :         g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
     439              :                      "Label for F2FS filesystem must be at most 512 characters long.");
     440            2 :         return FALSE;
     441              :     }
     442              : 
     443            2 :     return TRUE;
     444              : }
        

Generated by: LCOV version 2.0-1