diff --git a/Documentation/btrfs-subvolume.asciidoc b/Documentation/btrfs-subvolume.asciidoc index 6c0af2f8..1de6a6fe 100644 --- a/Documentation/btrfs-subvolume.asciidoc +++ b/Documentation/btrfs-subvolume.asciidoc @@ -59,12 +59,16 @@ directory. Add the newly created subvolume to a qgroup. This option can be given multiple times. -*delete* [options] [...]:: +*delete* [options] <[ [...]]:: +*delete* -i|--subvolid >:: Delete the subvolume(s) from the filesystem. + If is not a subvolume, btrfs returns an error but continues if there are more arguments to process. + +If --subvolid is used, must point to a btrfs filesystem. See `btrfs +subvolume list` or `btrfs inspect-internal rootid` how to get the subvolume id. ++ The corresponding directory is removed instantly but the data blocks are removed later in the background. The command returns immediately. See `btrfs subvolume sync` how to wait until the subvolume gets completely removed. @@ -84,6 +88,10 @@ wait for transaction commit after deleting each subvolume. + -v|--verbose:::: verbose output of operations. ++ +-i|--subvolid :::: +subvolume id to be removed instead of the that should point to the +filesystem with the subvolume *find-new* :: List the recently modified files in a subvolume, after generation. diff --git a/cmds/subvolume.c b/cmds/subvolume.c index 7a5fd79b..6f1d9035 100644 --- a/cmds/subvolume.c +++ b/cmds/subvolume.c @@ -222,10 +222,12 @@ static int wait_for_commit(int fd) } static const char * const cmd_subvol_delete_usage[] = { - "btrfs subvolume delete [options] [...]", + "btrfs subvolume delete [options] [...]\n" + "btrfs subvolume delete [options] -i|--subvolid ", "Delete subvolume(s)", - "Delete subvolumes from the filesystem. The corresponding directory", - "is removed instantly but the data blocks are removed later.", + "Delete subvolumes from the filesystem, specified by a path or id. The", + "corresponding directory is removed instantly but the data blocks are", + "removed later.", "The deletion does not involve full commit by default due to", "performance reasons (as a consequence, the subvolume may appear again", "after a crash). Use one of the --commit options to wait until the", @@ -233,6 +235,7 @@ static const char * const cmd_subvol_delete_usage[] = { "", "-c|--commit-after wait for transaction commit at the end of the operation", "-C|--commit-each wait for transaction commit after deleting each subvolume", + "-i|--subvolid subvolume id of the to be removed subvolume", "-v|--verbose verbose output of operations", NULL }; @@ -246,12 +249,14 @@ static int cmd_subvol_delete(const struct cmd_struct *cmd, char *dname, *vname, *cpath; char *dupdname = NULL; char *dupvname = NULL; - char *path; + char *path = NULL; DIR *dirstream = NULL; int verbose = 0; int commit_mode = 0; u8 fsid[BTRFS_FSID_SIZE]; + u64 subvolid = 0; char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; + char full_subvolpath[BTRFS_SUBVOL_NAME_MAX]; struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, }; enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 }; enum btrfs_util_error err; @@ -262,11 +267,12 @@ static int cmd_subvol_delete(const struct cmd_struct *cmd, static const struct option long_options[] = { {"commit-after", no_argument, NULL, 'c'}, {"commit-each", no_argument, NULL, 'C'}, + {"subvolid", required_argument, NULL, 'i'}, {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; - c = getopt_long(argc, argv, "cCv", long_options, NULL); + c = getopt_long(argc, argv, "cCi:v", long_options, NULL); if (c < 0) break; @@ -277,6 +283,9 @@ static int cmd_subvol_delete(const struct cmd_struct *cmd, case 'C': commit_mode = COMMIT_EACH; break; + case 'i': + subvolid = arg_strtou64(optarg); + break; case 'v': verbose++; break; @@ -288,6 +297,10 @@ static int cmd_subvol_delete(const struct cmd_struct *cmd, if (check_argc_min(argc - optind, 1)) return 1; + /* When using --subvolid, ensure that we have only one argument */ + if (subvolid > 0 && check_argc_exact(argc - optind, 1)) + return 1; + if (verbose > 0) { printf("Transaction commit: %s\n", !commit_mode ? "none (default)" : @@ -296,6 +309,23 @@ static int cmd_subvol_delete(const struct cmd_struct *cmd, cnt = optind; + /* Check the following syntax: subvolume delete --subvolid */ + if (subvolid > 0) { + char *subvol; + + path = argv[cnt]; + err = btrfs_util_subvolume_path(path, subvolid, &subvol); + if (err) { + error_btrfs_util(err); + ret = 1; + goto out; + } + + /* Build new path using the volume name found */ + sprintf(full_subvolpath, "%s/%s", path, subvol); + free(subvol); + } + again: path = argv[cnt]; @@ -318,17 +348,29 @@ again: vname = basename(dupvname); free(cpath); + /* When subvolid is passed, will point to the mount point */ + if (subvolid > 0) + dname = dupvname; + fd = btrfs_open_dir(dname, &dirstream, 1); if (fd < 0) { ret = 1; goto out; } - printf("Delete subvolume (%s): '%s/%s'\n", + printf("Delete subvolume (%s): ", commit_mode == COMMIT_EACH || (commit_mode == COMMIT_AFTER && cnt + 1 == argc) - ? "commit" : "no-commit", dname, vname); + ? "commit" : "no-commit"); - err = btrfs_util_delete_subvolume_fd(fd, vname, 0); + if (subvolid == 0) + printf("'%s/%s'\n", dname, vname); + else + printf("'%s'\n", full_subvolpath); + + if (subvolid == 0) + err = btrfs_util_delete_subvolume_fd(fd, vname, 0); + else + err = btrfs_util_delete_subvolume_by_id_fd(fd, subvolid); if (err) { error_btrfs_util(err); ret = 1;