Skip to content

Commit 949964c

Browse files
marcospskdave
authored andcommitted
btrfs: add new BTRFS_IOC_SNAP_DESTROY_V2 ioctl
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 and @subvol2, 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. This patch introduces a new ioctl BTRFS_IOC_SNAP_DESTROY_V2 that takes the extended structure with flags to allow to delete subvolume using subvolid. Now, we can use this new ioctl specifying the subvolume id and refer to the same mount point. It doesn't matter which subvolume was mounted, since we can reach to the desired one using the subvolume id, and then delete it. The full path to the subvolume id is resolved internally and access is verified as if the subvolume was accessed by path. The volume args v2 structure is extended to use the existing union for subvolume id specification, that's valid in case the BTRFS_SUBVOL_SPEC_BY_ID is set. Signed-off-by: Marcos Paulo de Souza <[email protected]> Reviewed-by: David Sterba <[email protected]> [ update changelog ] Signed-off-by: David Sterba <[email protected]>
1 parent c0c907a commit 949964c

File tree

2 files changed

+127
-21
lines changed

2 files changed

+127
-21
lines changed

fs/btrfs/ioctl.c

Lines changed: 116 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/iversion.h>
2929
#include "ctree.h"
3030
#include "disk-io.h"
31+
#include "export.h"
3132
#include "transaction.h"
3233
#include "btrfs_inode.h"
3334
#include "print-tree.h"
@@ -2842,7 +2843,8 @@ static int btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp)
28422843
}
28432844

28442845
static noinline int btrfs_ioctl_snap_destroy(struct file *file,
2845-
void __user *arg)
2846+
void __user *arg,
2847+
bool destroy_v2)
28462848
{
28472849
struct dentry *parent = file->f_path.dentry;
28482850
struct btrfs_fs_info *fs_info = btrfs_sb(parent->d_sb);
@@ -2851,34 +2853,120 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
28512853
struct inode *inode;
28522854
struct btrfs_root *root = BTRFS_I(dir)->root;
28532855
struct btrfs_root *dest = NULL;
2854-
struct btrfs_ioctl_vol_args *vol_args;
2855-
int namelen;
2856+
struct btrfs_ioctl_vol_args *vol_args = NULL;
2857+
struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL;
2858+
char *subvol_name, *subvol_name_ptr = NULL;
2859+
int subvol_namelen;
28562860
int err = 0;
2861+
bool destroy_parent = false;
28572862

2858-
if (!S_ISDIR(dir->i_mode))
2859-
return -ENOTDIR;
2863+
if (destroy_v2) {
2864+
vol_args2 = memdup_user(arg, sizeof(*vol_args2));
2865+
if (IS_ERR(vol_args2))
2866+
return PTR_ERR(vol_args2);
28602867

2861-
vol_args = memdup_user(arg, sizeof(*vol_args));
2862-
if (IS_ERR(vol_args))
2863-
return PTR_ERR(vol_args);
2868+
if (vol_args2->flags & ~BTRFS_SUBVOL_DELETE_ARGS_MASK) {
2869+
err = -EOPNOTSUPP;
2870+
goto out;
2871+
}
28642872

2865-
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
2866-
namelen = strlen(vol_args->name);
2867-
if (strchr(vol_args->name, '/') ||
2868-
strncmp(vol_args->name, "..", namelen) == 0) {
2869-
err = -EINVAL;
2870-
goto out;
2873+
/*
2874+
* If SPEC_BY_ID is not set, we are looking for the subvolume by
2875+
* name, same as v1 currently does.
2876+
*/
2877+
if (!(vol_args2->flags & BTRFS_SUBVOL_SPEC_BY_ID)) {
2878+
vol_args2->name[BTRFS_SUBVOL_NAME_MAX] = 0;
2879+
subvol_name = vol_args2->name;
2880+
2881+
err = mnt_want_write_file(file);
2882+
if (err)
2883+
goto out;
2884+
} else {
2885+
if (vol_args2->subvolid < BTRFS_FIRST_FREE_OBJECTID) {
2886+
err = -EINVAL;
2887+
goto out;
2888+
}
2889+
2890+
err = mnt_want_write_file(file);
2891+
if (err)
2892+
goto out;
2893+
2894+
dentry = btrfs_get_dentry(fs_info->sb,
2895+
BTRFS_FIRST_FREE_OBJECTID,
2896+
vol_args2->subvolid, 0, 0);
2897+
if (IS_ERR(dentry)) {
2898+
err = PTR_ERR(dentry);
2899+
goto out_drop_write;
2900+
}
2901+
2902+
/*
2903+
* Change the default parent since the subvolume being
2904+
* deleted can be outside of the current mount point.
2905+
*/
2906+
parent = btrfs_get_parent(dentry);
2907+
2908+
/*
2909+
* At this point dentry->d_name can point to '/' if the
2910+
* subvolume we want to destroy is outsite of the
2911+
* current mount point, so we need to release the
2912+
* current dentry and execute the lookup to return a new
2913+
* one with ->d_name pointing to the
2914+
* <mount point>/subvol_name.
2915+
*/
2916+
dput(dentry);
2917+
if (IS_ERR(parent)) {
2918+
err = PTR_ERR(parent);
2919+
goto out_drop_write;
2920+
}
2921+
dir = d_inode(parent);
2922+
2923+
/*
2924+
* If v2 was used with SPEC_BY_ID, a new parent was
2925+
* allocated since the subvolume can be outside of the
2926+
* current mount point. Later on we need to release this
2927+
* new parent dentry.
2928+
*/
2929+
destroy_parent = true;
2930+
2931+
subvol_name_ptr = btrfs_get_subvol_name_from_objectid(
2932+
fs_info, vol_args2->subvolid);
2933+
if (IS_ERR(subvol_name_ptr)) {
2934+
err = PTR_ERR(subvol_name_ptr);
2935+
goto free_parent;
2936+
}
2937+
/* subvol_name_ptr is already NULL termined */
2938+
subvol_name = (char *)kbasename(subvol_name_ptr);
2939+
}
2940+
} else {
2941+
vol_args = memdup_user(arg, sizeof(*vol_args));
2942+
if (IS_ERR(vol_args))
2943+
return PTR_ERR(vol_args);
2944+
2945+
vol_args->name[BTRFS_PATH_NAME_MAX] = 0;
2946+
subvol_name = vol_args->name;
2947+
2948+
err = mnt_want_write_file(file);
2949+
if (err)
2950+
goto out;
28712951
}
28722952

2873-
err = mnt_want_write_file(file);
2874-
if (err)
2875-
goto out;
2953+
subvol_namelen = strlen(subvol_name);
28762954

2955+
if (strchr(subvol_name, '/') ||
2956+
strncmp(subvol_name, "..", subvol_namelen) == 0) {
2957+
err = -EINVAL;
2958+
goto free_subvol_name;
2959+
}
2960+
2961+
if (!S_ISDIR(dir->i_mode)) {
2962+
err = -ENOTDIR;
2963+
goto free_subvol_name;
2964+
}
28772965

28782966
err = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
28792967
if (err == -EINTR)
2880-
goto out_drop_write;
2881-
dentry = lookup_one_len(vol_args->name, parent, namelen);
2968+
goto free_subvol_name;
2969+
dentry = lookup_one_len(subvol_name, parent, subvol_namelen);
28822970
if (IS_ERR(dentry)) {
28832971
err = PTR_ERR(dentry);
28842972
goto out_unlock_dir;
@@ -2947,9 +3035,15 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
29473035
dput(dentry);
29483036
out_unlock_dir:
29493037
inode_unlock(dir);
3038+
free_subvol_name:
3039+
kfree(subvol_name_ptr);
3040+
free_parent:
3041+
if (destroy_parent)
3042+
dput(parent);
29503043
out_drop_write:
29513044
mnt_drop_write_file(file);
29523045
out:
3046+
kfree(vol_args2);
29533047
kfree(vol_args);
29543048
return err;
29553049
}
@@ -5474,7 +5568,9 @@ long btrfs_ioctl(struct file *file, unsigned int
54745568
case BTRFS_IOC_SUBVOL_CREATE_V2:
54755569
return btrfs_ioctl_snap_create_v2(file, argp, 1);
54765570
case BTRFS_IOC_SNAP_DESTROY:
5477-
return btrfs_ioctl_snap_destroy(file, argp);
5571+
return btrfs_ioctl_snap_destroy(file, argp, false);
5572+
case BTRFS_IOC_SNAP_DESTROY_V2:
5573+
return btrfs_ioctl_snap_destroy(file, argp, true);
54785574
case BTRFS_IOC_SUBVOL_GETFLAGS:
54795575
return btrfs_ioctl_subvol_getflags(file, argp);
54805576
case BTRFS_IOC_SUBVOL_SETFLAGS:

include/uapi/linux/btrfs.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ struct btrfs_ioctl_vol_args {
4242

4343
#define BTRFS_DEVICE_SPEC_BY_ID (1ULL << 3)
4444

45+
#define BTRFS_SUBVOL_SPEC_BY_ID (1ULL << 4)
46+
4547
#define BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED \
4648
(BTRFS_SUBVOL_CREATE_ASYNC | \
4749
BTRFS_SUBVOL_RDONLY | \
4850
BTRFS_SUBVOL_QGROUP_INHERIT | \
49-
BTRFS_DEVICE_SPEC_BY_ID)
51+
BTRFS_DEVICE_SPEC_BY_ID | \
52+
BTRFS_SUBVOL_SPEC_BY_ID)
5053

5154
#define BTRFS_FSID_SIZE 16
5255
#define BTRFS_UUID_SIZE 16
@@ -117,6 +120,10 @@ struct btrfs_ioctl_qgroup_limit_args {
117120
BTRFS_SUBVOL_RDONLY | \
118121
BTRFS_SUBVOL_QGROUP_INHERIT)
119122

123+
/* Supported flags for BTRFS_IOC_SNAP_DESTROY_V2 */
124+
#define BTRFS_SUBVOL_DELETE_ARGS_MASK \
125+
(BTRFS_SUBVOL_SPEC_BY_ID)
126+
120127
struct btrfs_ioctl_vol_args_v2 {
121128
__s64 fd;
122129
__u64 transid;
@@ -131,6 +138,7 @@ struct btrfs_ioctl_vol_args_v2 {
131138
union {
132139
char name[BTRFS_SUBVOL_NAME_MAX + 1];
133140
__u64 devid;
141+
__u64 subvolid;
134142
};
135143
};
136144

@@ -959,5 +967,7 @@ enum btrfs_err_code {
959967
struct btrfs_ioctl_get_subvol_rootref_args)
960968
#define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \
961969
struct btrfs_ioctl_ino_lookup_user_args)
970+
#define BTRFS_IOC_SNAP_DESTROY_V2 _IOW(BTRFS_IOCTL_MAGIC, 63, \
971+
struct btrfs_ioctl_vol_args_v2)
962972

963973
#endif /* _UAPI_LINUX_BTRFS_H */

0 commit comments

Comments
 (0)