Skip to content

Commit

Permalink
btrfs: use libbtrfsutil for get/set default subvolume
Browse files Browse the repository at this point in the history
libbtrfsutil provides it's own error enum for libbtrfsutil functions and
as we no longer parse output this introduces a BD_BTRFS_ERROR_NOT_FOUND
error type. As the mount point not being found is the most likely
failure scenario.
  • Loading branch information
jelly committed Jan 22, 2024
1 parent 7cadfa1 commit 65eac48
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 51 deletions.
66 changes: 17 additions & 49 deletions src/plugins/btrfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
*/

#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <blockdev/utils.h>
#include <bs_size.h>
#include <btrfsutil.h>

#include "btrfs.h"
#include "check_deps.h"
Expand Down Expand Up @@ -258,6 +260,10 @@ static BDBtrfsFilesystemInfo* get_filesystem_info_from_match (GMatchInfo *match_
return ret;
}

static const char* format_btrfs_error (enum btrfs_util_error err) {
return g_strdup_printf ("%s: %m", btrfs_util_strerror (err));
}

/**
* bd_btrfs_create_volume:
* @devices: (array zero-terminated=1): list of devices to create btrfs volume from
Expand Down Expand Up @@ -454,75 +460,37 @@ gboolean bd_btrfs_delete_subvolume (const gchar *mountpoint, const gchar *name,
* Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_QUERY
*/
guint64 bd_btrfs_get_default_subvolume_id (const gchar *mountpoint, GError **error) {
GRegex *regex = NULL;
GMatchInfo *match_info = NULL;
gboolean success = FALSE;
gchar *output = NULL;
gchar *match = NULL;
enum btrfs_util_error err;
guint64 ret = 0;
const gchar *argv[5] = {"btrfs", "subvol", "get-default", mountpoint, NULL};

if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
!check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
return 0;

regex = g_regex_new ("ID (\\d+) .*", 0, 0, error);
if (!regex) {
bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
/* error is already populated */
return 0;
err = btrfs_util_get_default_subvolume (mountpoint, &ret);
if (err) {
g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_NOT_FOUND, format_btrfs_error (err));
}

success = bd_utils_exec_and_capture_output (argv, NULL, &output, error);
if (!success) {
g_regex_unref (regex);
return 0;
}

success = g_regex_match (regex, output, 0, &match_info);
if (!success) {
g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_PARSE, "Failed to parse subvolume's ID");
g_regex_unref (regex);
g_match_info_free (match_info);
g_free (output);
return 0;
}

match = g_match_info_fetch (match_info, 1);
ret = g_ascii_strtoull (match, NULL, 0);

g_free (match);
g_match_info_free (match_info);
g_regex_unref (regex);
g_free (output);

return ret;
}

/**
* bd_btrfs_set_default_subvolume:
* @mountpoint: mountpoint of the volume to set the default subvolume ID of
* @subvol_id: ID of the subvolume to be set as the default subvolume
* @extra: (nullable) (array zero-terminated=1): extra options for the setting (right now
* passed to the 'btrfs' utility)
* @error: (out) (optional): place to store error (if any)
*
* Returns: whether the @mountpoint volume's default subvolume was correctly set
* to @subvol_id or not
*
* Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_MODIFY
*/
gboolean bd_btrfs_set_default_subvolume (const gchar *mountpoint, guint64 subvol_id, const BDExtraArg **extra, GError **error) {
const gchar *argv[6] = {"btrfs", "subvol", "set-default", NULL, mountpoint, NULL};
gboolean bd_btrfs_set_default_subvolume (const gchar *mountpoint, guint64 subvol_id, GError **error) {
enum btrfs_util_error err;
gboolean ret = FALSE;

if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
!check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
return FALSE;

argv[3] = g_strdup_printf ("%"G_GUINT64_FORMAT, subvol_id);
ret = bd_utils_exec_and_report_error (argv, extra, error);
g_free ((gchar *) argv[3]);
err = btrfs_util_set_default_subvolume (mountpoint, subvol_id);
if (err)
g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_NOT_FOUND, format_btrfs_error (err));
else
ret = TRUE;

return ret;
}
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/btrfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ typedef enum {
BD_BTRFS_ERROR_TECH_UNAVAIL,
BD_BTRFS_ERROR_DEVICE,
BD_BTRFS_ERROR_PARSE,
BD_BTRFS_ERROR_NOT_FOUND,
} BDBtrfsError;

typedef struct BDBtrfsDeviceInfo {
Expand Down Expand Up @@ -79,7 +80,7 @@ gboolean bd_btrfs_remove_device (const gchar *mountpoint, const gchar *device, c
gboolean bd_btrfs_create_subvolume (const gchar *mountpoint, const gchar *name, const BDExtraArg **extra, GError **error);
gboolean bd_btrfs_delete_subvolume (const gchar *mountpoint, const gchar *name, const BDExtraArg **extra, GError **error);
guint64 bd_btrfs_get_default_subvolume_id (const gchar *mountpoint, GError **error);
gboolean bd_btrfs_set_default_subvolume (const gchar *mountpoint, guint64 subvol_id, const BDExtraArg **extra, GError **error);
gboolean bd_btrfs_set_default_subvolume (const gchar *mountpoint, guint64 subvol_id, GError **error);
gboolean bd_btrfs_create_snapshot (const gchar *source, const gchar *dest, gboolean ro, const BDExtraArg **extra, GError **error);
BDBtrfsDeviceInfo** bd_btrfs_list_devices (const gchar *device, GError **error);
BDBtrfsSubvolumeInfo** bd_btrfs_list_subvolumes (const gchar *mountpoint, gboolean snapshots_only, GError **error);
Expand Down
6 changes: 5 additions & 1 deletion tests/btrfs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def test_get_default_subvolume_id(self):
self.assertTrue(succ)

# not mounted yet, should fail
with self.assertRaisesRegex(GLib.GError, r".*(can't|cannot) access.*"):
with self.assertRaisesRegex(GLib.GError, r".*Could not open: No such file or directory.*"):
ret = BlockDev.btrfs_get_default_subvolume_id(TEST_MNT)

mount(self.loop_dev, TEST_MNT)
Expand All @@ -292,6 +292,10 @@ def test_set_default_subvolume(self):
succ = BlockDev.btrfs_create_volume([self.loop_dev], "myShinyBtrfs", None, None, None)
self.assertTrue(succ)

# not mounted yet, should fail
with self.assertRaisesRegex(GLib.GError, r".*Could not open: No such file or directory.*"):
ret = BlockDev.btrfs_get_default_subvolume_id(TEST_MNT)

mount(self.loop_dev, TEST_MNT)

ret = BlockDev.btrfs_get_default_subvolume_id(TEST_MNT)
Expand Down

0 comments on commit 65eac48

Please sign in to comment.