btrfs-progs: subvol delete: add --subvolid argument to deletee by id

This ioctl will be responsible for deleting a subvolume using its id.
This can be used when a system has a file system mounted from a
subvolume, rather than the root file system, like below:

/
@subvol1/
@subvol2/
@subvol_default/

If only @subvol_default is mounted, we have no path to reach @subvol1
(id 256) and @subvol2 (id 257), thus no way to delete them. Current
subvolume delete ioctl takes a file handle point as argument, and if
@subvol_default is mounted, we can't reach @subvol1 and @subvol2 from
the same mount point.

  $ mount -o subvol=subvol_default /mnt
  $ btrfs subvolume delete -i 257 /mnt

This will delete @subvol2 although it's path is hidden.

Fixes: #152
Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Marcos Paulo de Souza 2020-02-07 10:10:28 -03:00 committed by David Sterba
parent 672e398eed
commit 6e85994e80
2 changed files with 59 additions and 9 deletions

View File

@ -59,12 +59,16 @@ directory.
Add the newly created subvolume to a qgroup. This option can be given multiple
times.
*delete* [options] <subvolume> [<subvolume>...]::
*delete* [options] <[<subvolume> [<subvolume>...]]::
*delete* -i|--subvolid <subvolid> <path>>::
Delete the subvolume(s) from the filesystem.
+
If <subvolume> is not a subvolume, btrfs returns an error but continues if
there are more arguments to process.
+
If --subvolid is used, <path> 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 <subvolid>::::
subvolume id to be removed instead of the <path> that should point to the
filesystem with the subvolume
*find-new* <subvolume> <last_gen>::
List the recently modified files in a subvolume, after <last_gen> generation.

View File

@ -222,10 +222,12 @@ static int wait_for_commit(int fd)
}
static const char * const cmd_subvol_delete_usage[] = {
"btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
"btrfs subvolume delete [options] <subvolume> [<subvolume>...]\n"
"btrfs subvolume delete [options] -i|--subvolid <subvolid> <path>",
"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 <subvolid> <path> */
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, <path> 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;