Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/libblockdev-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ bd_btrfs_add_device
bd_btrfs_remove_device
bd_btrfs_create_subvolume
bd_btrfs_delete_subvolume
bd_btrfs_delete_subvolume_recursive
bd_btrfs_get_default_subvolume_id
bd_btrfs_set_default_subvolume
bd_btrfs_create_snapshot
Expand Down
28 changes: 24 additions & 4 deletions src/lib/plugin_apis/btrfs.api
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,11 @@ typedef enum {
} BDBtrfsTech;

typedef enum {
BD_BTRFS_TECH_MODE_CREATE = 1 << 0,
BD_BTRFS_TECH_MODE_DELETE = 1 << 1,
BD_BTRFS_TECH_MODE_MODIFY = 1 << 2,
BD_BTRFS_TECH_MODE_QUERY = 1 << 3,
BD_BTRFS_TECH_MODE_CREATE = 1 << 0,
BD_BTRFS_TECH_MODE_DELETE = 1 << 1,
BD_BTRFS_TECH_MODE_MODIFY = 1 << 2,
BD_BTRFS_TECH_MODE_QUERY = 1 << 3,
BD_BTRFS_TECH_MODE_DELETE_RECURSIVE = 1 << 4,
} BDBtrfsTechMode;

/**
Expand Down Expand Up @@ -308,6 +309,25 @@ gboolean bd_btrfs_create_subvolume (const gchar *mountpoint, const gchar *name,
*/
gboolean bd_btrfs_delete_subvolume (const gchar *mountpoint, const gchar *name, const BDExtraArg **extra, GError **error);

/**
* bd_btrfs_delete_subvolume_recursive:
* @mountpoint: mountpoint of the btrfs volume to delete subvolume from
* @name: name of the subvolume
* @recursive: whether to delete child subvolumes recursively
* @extra: (nullable) (array zero-terminated=1): extra options for the subvolume deletion (right now
* passed to the 'btrfs' utility)
* @error: (out) (optional): place to store error (if any)
*
* Returns: whether the @mountpoint/@name subvolume was successfully deleted or not
*
* If @recursive is %TRUE, all child subvolumes will be deleted before deleting
* the subvolume itself. This requires btrfs-progs >= 6.12.
Comment thread
vojtechtrefny marked this conversation as resolved.
*
* Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_DELETE if @recursive is %FALSE,
* %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_DELETE_RECURSIVE if @recursive is %TRUE
*/
gboolean bd_btrfs_delete_subvolume_recursive (const gchar *mountpoint, const gchar *name, gboolean recursive, const BDExtraArg **extra, GError **error);

/**
* bd_btrfs_get_default_subvolume_id:
* @mountpoint: mountpoint of the volume to get the default subvolume ID of
Expand Down
53 changes: 47 additions & 6 deletions src/plugins/btrfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,14 @@ static volatile guint avail_module_deps = 0;
static GMutex deps_check_lock;

#define DEPS_BTRFS 0
#define DEPS_BTRFS_612 1
#define DEPS_BTRFS_MASK (1 << DEPS_BTRFS)
#define DEPS_LAST 1
#define DEPS_BTRFS_612_MASK (1 << DEPS_BTRFS_612)
#define DEPS_LAST 2

static const UtilDep deps[DEPS_LAST] = {
{"btrfs", BTRFS_MIN_VERSION, NULL, "[Bb]trfs.* v([\\d\\.]+)"},
{"btrfs", "6.12", NULL, "[Bb]trfs.* v([\\d\\.]+)"},
};

#define MODULE_DEPS_BTRFS 0
Expand Down Expand Up @@ -165,10 +168,16 @@ void bd_btrfs_close (void) {
* Returns: whether the @tech-@mode combination is available -- supported by the
* plugin implementation and having all the runtime dependencies available
*/
gboolean bd_btrfs_is_tech_avail (BDBtrfsTech tech G_GNUC_UNUSED, guint64 mode G_GNUC_UNUSED, GError **error) {
gboolean bd_btrfs_is_tech_avail (BDBtrfsTech tech, guint64 mode, GError **error) {
/* all tech-mode combinations are supported by this implementation of the plugin */
return 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);
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;

if (tech == BD_BTRFS_TECH_SUBVOL && (mode & BD_BTRFS_TECH_MODE_DELETE_RECURSIVE))
return check_deps (&avail_deps, DEPS_BTRFS_612_MASK, deps, DEPS_LAST, &deps_check_lock, error);

return TRUE;
}

static BDBtrfsDeviceInfo* get_device_info_from_match (GMatchInfo *match_info) {
Expand Down Expand Up @@ -423,19 +432,51 @@ gboolean bd_btrfs_create_subvolume (const gchar *mountpoint, const gchar *name,
* Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_DELETE
*/
gboolean bd_btrfs_delete_subvolume (const gchar *mountpoint, const gchar *name, const BDExtraArg **extra, GError **error) {
return bd_btrfs_delete_subvolume_recursive (mountpoint, name, FALSE, extra, error);
}

/**
* bd_btrfs_delete_subvolume_recursive:
* @mountpoint: mountpoint of the btrfs volume to delete subvolume from
* @name: name of the subvolume
* @recursive: whether to delete child subvolumes recursively
* @extra: (nullable) (array zero-terminated=1): extra options for the subvolume deletion (right now
* passed to the 'btrfs' utility)
* @error: (out) (optional): place to store error (if any)
*
* Returns: whether the @mountpoint/@name subvolume was successfully deleted or not
*
* If @recursive is %TRUE, all child subvolumes will be deleted before deleting
* the subvolume itself. This requires btrfs-progs >= 6.12.
*
* Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_DELETE if @recursive is %FALSE,
* %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_DELETE_RECURSIVE if @recursive is %TRUE
*/
gboolean bd_btrfs_delete_subvolume_recursive (const gchar *mountpoint, const gchar *name, gboolean recursive, const BDExtraArg **extra, GError **error) {
gchar *path = NULL;
gboolean success = FALSE;
const gchar *argv[5] = {"btrfs", "subvol", "delete", NULL, NULL};
const gchar *argv[6] = {"btrfs", "subvol", "delete", NULL, NULL, 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 FALSE;

if (recursive) {
if (!check_deps (&avail_deps, DEPS_BTRFS_612_MASK, deps, DEPS_LAST, &deps_check_lock, error))
return FALSE;
}

if (g_str_has_suffix (mountpoint, "/"))
path = g_strdup_printf ("%s%s", mountpoint, name);
else
path = g_strdup_printf ("%s/%s", mountpoint, name);
argv[3] = path;

if (recursive) {
argv[3] = "--recursive";
argv[4] = path;
} else {
argv[3] = path;
}

success = bd_utils_exec_and_report_error (argv, extra, error);
g_free (path);
Expand Down
10 changes: 6 additions & 4 deletions src/plugins/btrfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ typedef enum {
} BDBtrfsTech;

typedef enum {
BD_BTRFS_TECH_MODE_CREATE = 1 << 0,
BD_BTRFS_TECH_MODE_DELETE = 1 << 1,
BD_BTRFS_TECH_MODE_MODIFY = 1 << 2,
BD_BTRFS_TECH_MODE_QUERY = 1 << 3,
BD_BTRFS_TECH_MODE_CREATE = 1 << 0,
BD_BTRFS_TECH_MODE_DELETE = 1 << 1,
BD_BTRFS_TECH_MODE_MODIFY = 1 << 2,
BD_BTRFS_TECH_MODE_QUERY = 1 << 3,
BD_BTRFS_TECH_MODE_DELETE_RECURSIVE = 1 << 4,
} BDBtrfsTechMode;

/*
Expand All @@ -78,6 +79,7 @@ gboolean bd_btrfs_add_device (const gchar *mountpoint, const gchar *device, cons
gboolean bd_btrfs_remove_device (const gchar *mountpoint, const gchar *device, const BDExtraArg **extra, GError **error);
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);
gboolean bd_btrfs_delete_subvolume_recursive (const gchar *mountpoint, const gchar *name, gboolean recursive, 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_create_snapshot (const gchar *source, const gchar *dest, gboolean ro, const BDExtraArg **extra, GError **error);
Expand Down
35 changes: 35 additions & 0 deletions tests/btrfs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,41 @@ def test_create_delete_subvolume(self):
seen.add(subvol)
self.assertTrue(subvol.parent_id == BlockDev.BTRFS_MAIN_VOLUME_ID or any(subvol.parent_id == other.id for other in seen))

class BtrfsTestDeleteSubvolumeRecursive(BtrfsTestCase):
@tag_test(TestTags.CORE)
def test_delete_subvolume_recursive(self):
"""Verify that it is possible to recursively delete subvolumes"""

succ = BlockDev.btrfs_create_volume([self.loop_dev], "myShinyBtrfs", None, None, None)
self.assertTrue(succ)

mount(self.loop_dev, TEST_MNT)

# create a subvolume with nested child subvolumes
succ = BlockDev.btrfs_create_subvolume(TEST_MNT, "subvol1", None)
self.assertTrue(succ)

succ = BlockDev.btrfs_create_subvolume(os.path.join(TEST_MNT, "subvol1"), "subvol1.1", None)
self.assertTrue(succ)

succ = BlockDev.btrfs_create_subvolume(os.path.join(TEST_MNT, "subvol1", "subvol1.1"), "subvol1.1.1", None)
self.assertTrue(succ)

subvols = BlockDev.btrfs_list_subvolumes(TEST_MNT, False)
self.assertEqual(len(subvols), 3)

version = self._get_btrfs_version()
if version < Version("6.12"):
with self.assertRaisesRegex(GLib.GError, "Too low version of btrfs"):
BlockDev.btrfs_delete_subvolume_recursive(TEST_MNT, "subvol1", True, None)
else:
# recursive delete should remove the subvolume and all its children
succ = BlockDev.btrfs_delete_subvolume_recursive(TEST_MNT, "subvol1", True, None)
self.assertTrue(succ)

subvols = BlockDev.btrfs_list_subvolumes(TEST_MNT, False)
self.assertEqual(len(subvols), 0)

class BtrfsTestCreateSnapshot(BtrfsTestCase):
def test_create_snapshot(self):
succ = BlockDev.btrfs_create_volume([self.loop_dev], "myShinyBtrfs", None, None, None)
Expand Down
Loading