From a4d2d1b498c121ebcaddc800e1d98ee285c1568f Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Thu, 27 Jun 2024 16:21:38 +0100 Subject: [PATCH] btrfs-progs: subvolume delete: add new option for recursive deletion Add new option --recursive 'btrfs subvol delete', causing it to pass the BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE flag through to libbtrfsutil. This can work in two modes, depending on the user: - regular user - this will skip subvolumes that are not accessible - root (CAP_SYS_ADMIN) - no limitations Pull-request: #861 Signed-off-by: Mark Harmstone Co-authored-by: Omar Sandoval Reviewed-by: Qu Wenruo [ Add details to man page, fix indent in the doc. ] Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- Documentation/btrfs-subvolume.rst | 9 +++++++++ cmds/subvolume.c | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Documentation/btrfs-subvolume.rst b/Documentation/btrfs-subvolume.rst index d1e89f15..eed602f9 100644 --- a/Documentation/btrfs-subvolume.rst +++ b/Documentation/btrfs-subvolume.rst @@ -112,6 +112,15 @@ delete [options] [ [...]], delete -i|--subvolid -i|--subvolid subvolume id to be removed instead of the that should point to the filesystem with the subvolume + + -R|--recursive + delete subvolumes beneath each subvolume recursively + + This requires either `CAP_SYS_ADMIN` or the filesystem must be + mounted with `user_subvol_rm_allowed` mount option. + In the unprivileged case, subvolumes which cannot be accessed + are skipped. The deletion is not atomic. + -v|--verbose (deprecated) alias for global *-v* option diff --git a/cmds/subvolume.c b/cmds/subvolume.c index 52bc8850..56108269 100644 --- a/cmds/subvolume.c +++ b/cmds/subvolume.c @@ -347,6 +347,8 @@ static const char * const cmd_subvolume_delete_usage[] = { OPTLINE("-c|--commit-after", "wait for transaction commit at the end of the operation"), OPTLINE("-C|--commit-each", "wait for transaction commit after deleting each subvolume"), OPTLINE("-i|--subvolid", "subvolume id of the to be removed subvolume"), + OPTLINE("-R|--recursive", "delete accessible subvolumes beneath each subvolume recursively, " + "this is not atomic, may need root to delete subvolumes not accessible by the user"), OPTLINE("-v|--verbose", "deprecated, alias for global -v option"), HELPINFO_INSERT_GLOBALS, HELPINFO_INSERT_VERBOSE, @@ -367,6 +369,7 @@ static int cmd_subvolume_delete(const struct cmd_struct *cmd, int argc, char **a char *path = NULL; int commit_mode = 0; bool subvol_path_not_found = false; + int flags = 0; u8 fsid[BTRFS_FSID_SIZE]; u64 subvolid = 0; char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; @@ -383,11 +386,12 @@ static int cmd_subvolume_delete(const struct cmd_struct *cmd, int argc, char **a {"commit-after", no_argument, NULL, 'c'}, {"commit-each", no_argument, NULL, 'C'}, {"subvolid", required_argument, NULL, 'i'}, + {"recursive", no_argument, NULL, 'R'}, {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; - c = getopt_long(argc, argv, "cCi:v", long_options, NULL); + c = getopt_long(argc, argv, "cCi:Rv", long_options, NULL); if (c < 0) break; @@ -401,6 +405,9 @@ static int cmd_subvolume_delete(const struct cmd_struct *cmd, int argc, char **a case 'i': subvolid = arg_strtou64(optarg); break; + case 'R': + flags |= BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE; + break; case 'v': bconf_be_verbose(); break; @@ -416,6 +423,11 @@ static int cmd_subvolume_delete(const struct cmd_struct *cmd, int argc, char **a if (subvolid > 0 && check_argc_exact(argc - optind, 1)) return 1; + if (subvolid > 0 && flags & BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE) { + error("option --recursive is not supported with --subvolid"); + return 1; + } + pr_verbose(LOG_INFO, "Transaction commit: %s\n", !commit_mode ? "none (default)" : commit_mode == COMMIT_AFTER ? "at the end" : "after each"); @@ -528,7 +540,7 @@ again: /* Start deleting. */ if (subvolid == 0) - err = btrfs_util_delete_subvolume_fd(fd, vname, 0); + err = btrfs_util_delete_subvolume_fd(fd, vname, flags); else err = btrfs_util_delete_subvolume_by_id_fd(fd, subvolid); if (err) {