Re: [PATCHv3] btrfs: Introduce new BTRFS_IOC_SNAP_DESTROY_V2 ioctl

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 2020-02-10 at 16:41 -0700, Nathan Chancellor wrote:
> On Fri, Feb 07, 2020 at 10:05:46AM -0300, Marcos Paulo de Souza
> wrote:
> > From: Marcos Paulo de Souza <mpdesouza@xxxxxxxx>
> > 
> > This ioctl will be responsible for deleting a subvolume using it's
> 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 handler 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 flag to allow BTRFS_IOC_SNAP_DESTROY_V2
> > 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.
> > 
> > Also in this patch:
> > * export get_subvol_name_from_objectid, adding btrfs suffix
> > * add BTRFS_SUBVOL_SPEC_BY_ID flag
> > * add subvolid as a union member in struct btrfs_ioctl_vol_args_v2.
> > 
> > Signed-off-by: Marcos Paulo de Souza <mpdesouza@xxxxxxxx>
> > ---
> > 
> >  Changes from v2:
> >  * Commit message improved, explaining how to use the new ioctl
> (David)
> >  * Moved subvolid member to the union which already contained devid
> and name
> >    (David)
> >  * Changed name_ptr to subvol_name_ptr, since it'll point to the
> "full"
> >    subvolume name, but we need the basename of this char, which was
> also renamed
> >    to subvol_name (David).
> >  * Change the check for a valid subvolid to be >=
> BTRFS_FIRST_FREE_OBJECTID
> >    (David)
> >  * Now BTRFS_IOC_SNAP_DESTROY_V2 can handle both cases where the
> user uses the
> >    subvolid and just the subvolume name (David)
> >  * Changed BTRFS_SUBVOL_DELETE_BY_ID to BTRFS_SUBVOL_SPEC_BY_ID,
> since this flag
> >    can be used for other actions rather than deleting a subvolume
> (David, Christoph)
> >  * Rewritten comment about the getting/releasing the dentry before
> doing the
> >    lookup, explaining why this dentry can be released in order to
> get a new one
> >    from lookup (David)
> >  * Moved mnt_want_write_file call sites right after the flag
> validation (David)
> > 
> >  Changes from v1:
> >  * make btrfs_ioctl_snap_destroy handle both SNAP_DESTROY and
> SNAP_DESTROY_V2
> >    (suggested by Josef)
> >  * Change BTRFS_SUBVOL_DELETE_BY_ID to BTRFS_SUBVOL_BY_ID (David)
> >  * Send patches for btrfs-progs and xfstests along this change
> > 
> >  fs/btrfs/ctree.h           |   2 +
> >  fs/btrfs/export.c          |   4 +-
> >  fs/btrfs/export.h          |   5 ++
> >  fs/btrfs/ioctl.c           | 128 +++++++++++++++++++++++++++++++
> ------
> >  fs/btrfs/super.c           |   4 +-
> >  include/uapi/linux/btrfs.h |   8 ++-
> >  6 files changed, 127 insertions(+), 24 deletions(-)
> > 
> > diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> > index 54efb21c2727..2d56517c4bca 100644
> > --- a/fs/btrfs/ctree.h
> > +++ b/fs/btrfs/ctree.h
> > @@ -2963,6 +2963,8 @@ int btrfs_defrag_leaves(struct
> btrfs_trans_handle *trans,
> >  int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
> >  			unsigned long new_flags);
> >  int btrfs_sync_fs(struct super_block *sb, int wait);
> > +char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info
> *fs_info,
> > +					   u64 subvol_objectid);
> >  
> >  static inline __printf(2, 3) __cold
> >  void btrfs_no_printk(const struct btrfs_fs_info *fs_info, const
> char *fmt, ...)
> > diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c
> > index 72e312cae69d..027411cdbae7 100644
> > --- a/fs/btrfs/export.c
> > +++ b/fs/btrfs/export.c
> > @@ -57,7 +57,7 @@ static int btrfs_encode_fh(struct inode *inode,
> u32 *fh, int *max_len,
> >  	return type;
> >  }
> >  
> > -static struct dentry *btrfs_get_dentry(struct super_block *sb, u64
> objectid,
> > +struct dentry *btrfs_get_dentry(struct super_block *sb, u64
> objectid,
> >  				       u64 root_objectid, u32
> generation,
> >  				       int check_generation)
> >  {
> > @@ -152,7 +152,7 @@ static struct dentry *btrfs_fh_to_dentry(struct
> super_block *sb, struct fid *fh,
> >  	return btrfs_get_dentry(sb, objectid, root_objectid,
> generation, 1);
> >  }
> >  
> > -static struct dentry *btrfs_get_parent(struct dentry *child)
> > +struct dentry *btrfs_get_parent(struct dentry *child)
> >  {
> >  	struct inode *dir = d_inode(child);
> >  	struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
> > diff --git a/fs/btrfs/export.h b/fs/btrfs/export.h
> > index 57488ecd7d4e..f981e8103d64 100644
> > --- a/fs/btrfs/export.h
> > +++ b/fs/btrfs/export.h
> > @@ -18,4 +18,9 @@ struct btrfs_fid {
> >  	u64 parent_root_objectid;
> >  } __attribute__ ((packed));
> >  
> > +struct dentry *btrfs_get_dentry(struct super_block *sb, u64
> objectid,
> > +				       u64 root_objectid, u32
> generation,
> > +				       int check_generation);
> > +struct dentry *btrfs_get_parent(struct dentry *child);
> > +
> >  #endif
> > diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> > index 12ae31e1813e..be5350582955 100644
> > --- a/fs/btrfs/ioctl.c
> > +++ b/fs/btrfs/ioctl.c
> > @@ -28,6 +28,7 @@
> >  #include <linux/iversion.h>
> >  #include "ctree.h"
> >  #include "disk-io.h"
> > +#include "export.h"
> >  #include "transaction.h"
> >  #include "btrfs_inode.h"
> >  #include "print-tree.h"
> > @@ -2836,7 +2837,8 @@ static int
> btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp)
> >  }
> >  
> >  static noinline int btrfs_ioctl_snap_destroy(struct file *file,
> > -					     void __user *arg)
> > +					     void __user *arg,
> > +					     bool destroy_v2)
> >  {
> >  	struct dentry *parent = file->f_path.dentry;
> >  	struct btrfs_fs_info *fs_info = btrfs_sb(parent->d_sb);
> > @@ -2845,34 +2847,114 @@ static noinline int
> btrfs_ioctl_snap_destroy(struct file *file,
> >  	struct inode *inode;
> >  	struct btrfs_root *root = BTRFS_I(dir)->root;
> >  	struct btrfs_root *dest = NULL;
> > -	struct btrfs_ioctl_vol_args *vol_args;
> > -	int namelen;
> > +	struct btrfs_ioctl_vol_args *vol_args = NULL;
> > +	struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL;
> > +	char *subvol_name, *subvol_name_ptr = NULL;
> > +	int subvol_namelen;
> >  	int err = 0;
> > +	bool destroy_parent = false;
> >  
> > -	if (!S_ISDIR(dir->i_mode))
> > -		return -ENOTDIR;
> > +	if (destroy_v2) {
> > +		vol_args2 = memdup_user(arg, sizeof(*vol_args2));
> > +		if (IS_ERR(vol_args2))
> > +			return PTR_ERR(vol_args2);
> >  
> > -	vol_args = memdup_user(arg, sizeof(*vol_args));
> > -	if (IS_ERR(vol_args))
> > -		return PTR_ERR(vol_args);
> > +		/*
> > +		 * If SPEC_BY_ID is not set, we are looking for the
> subvolume by
> > +		 * name, same as v1 currently does.
> > +		 */
> > +		if (!(vol_args2->flags & BTRFS_SUBVOL_SPEC_BY_ID)) {
> > +			vol_args2->name[BTRFS_PATH_NAME_MAX] = '\0';
> > +			subvol_name = vol_args2->name;
> >  
> > -	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
> > -	namelen = strlen(vol_args->name);
> > -	if (strchr(vol_args->name, '/') ||
> > -	    strncmp(vol_args->name, "..", namelen) == 0) {
> > -		err = -EINVAL;
> > -		goto out;
> > +			err = mnt_want_write_file(file);
> > +			if (err)
> > +				goto out;
> > +		} else {
> > +			if (vol_args2->subvolid <
> BTRFS_FIRST_FREE_OBJECTID) {
> > +				err = -EINVAL;
> > +				goto out;
> > +			}
> > +
> > +			err = mnt_want_write_file(file);
> > +			if (err)
> > +				goto out;
> > +
> > +			dentry = btrfs_get_dentry(fs_info->sb,
> > +					BTRFS_FIRST_FREE_OBJECTID,
> > +					vol_args2->subvolid, 0, 0);
> > +			if (IS_ERR(dentry)) {
> > +				err = PTR_ERR(dentry);
> > +				goto out_drop_write;
> > +			}
> > +
> > +			/*
> > +			 * Change the default parent since the
> subvolume being
> > +			 * deleted can be outside of the current mount
> point.
> > +			 */
> > +			parent = btrfs_get_parent(dentry);
> > +
> > +			/*
> > +			 * At this point dentry->d_name can point to
> '/' if the
> > +			 * subvolume we want to destroy is outsite of
> the
> > +			 * current mount point, so we need to released
> the
> > +			 * current dentry and execute the lookup to
> return a new
> > +			 * one with ->d_name pointing to the
> > +			 * <mount point>/subvol_name.
> > +			 */
> > +			dput(dentry);
> > +			if (IS_ERR(parent)) {
> > +				err = PTR_ERR(parent);
> > +				goto out_drop_write;
> > +			}
> > +			dir = d_inode(parent);
> > +
> > +			/* If v2 was used with SPEC_BY_ID, a new parent
> was
> > +			 * allocated since the subvolume can be outside
> of the
> > +			 * current moutn point. Later on we need to
> release this
> > +			 * new parent dentry.
> > +			 */
> > +			destroy_parent = true;
> > +
> > +			subvol_name_ptr =
> btrfs_get_subvol_name_from_objectid(fs_info,
> > +					vol_args2->subvolid);
> > +			if (IS_ERR(subvol_name_ptr)) {
> > +				err = PTR_ERR(subvol_name_ptr);
> > +				goto free_parent;
> > +			}
> > +			/* subvol_name_ptr is already NULL termined */
> > +			subvol_name = (char
> *)kbasename(subvol_name_ptr);
> > +		}
> > +	} else {
> > +		vol_args = memdup_user(arg, sizeof(*vol_args));
> > +		if (IS_ERR(vol_args))
> > +			return PTR_ERR(vol_args);
> > +
> > +		vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
> > +		subvol_name = vol_args->name;
> > +
> > +		err = mnt_want_write_file(file);
> > +		if (err)
> > +			goto out;
> >  	}
> >  
> > -	err = mnt_want_write_file(file);
> > -	if (err)
> > -		goto out;
> > +	subvol_namelen = strlen(subvol_name);
> >  
> > +	if (strchr(subvol_name, '/') ||
> > +	    strncmp(subvol_name, "..", subvol_namelen) == 0) {
> > +		err = -EINVAL;
> > +		goto free_subvol_name;
> > +	}
> > +
> > +	if (!S_ISDIR(dir->i_mode)) {
> > +		err = -ENOTDIR;
> > +		goto free_subvol_name;
> > +	}
> >  
> >  	err = down_write_killable_nested(&dir->i_rwsem,
> I_MUTEX_PARENT);
> >  	if (err == -EINTR)
> >  		goto out_drop_write;
> > -	dentry = lookup_one_len(vol_args->name, parent, namelen);
> > +	dentry = lookup_one_len(subvol_name, parent, subvol_namelen);
> >  	if (IS_ERR(dentry)) {
> >  		err = PTR_ERR(dentry);
> >  		goto out_unlock_dir;
> > @@ -2941,9 +3023,15 @@ static noinline int
> btrfs_ioctl_snap_destroy(struct file *file,
> >  	dput(dentry);
> >  out_unlock_dir:
> >  	inode_unlock(dir);
> > +free_subvol_name:
> > +	kfree(subvol_name_ptr);
> > +free_parent:
> > +	if (destroy_parent)
> > +		dput(parent);
> >  out_drop_write:
> >  	mnt_drop_write_file(file);
> >  out:
> > +	kfree(vol_args2);
> >  	kfree(vol_args);
> >  	return err;
> >  }
> > @@ -5464,7 +5552,9 @@ long btrfs_ioctl(struct file *file, unsigned
> int
> >  	case BTRFS_IOC_SUBVOL_CREATE_V2:
> >  		return btrfs_ioctl_snap_create_v2(file, argp, 1);
> >  	case BTRFS_IOC_SNAP_DESTROY:
> > -		return btrfs_ioctl_snap_destroy(file, argp);
> > +		return btrfs_ioctl_snap_destroy(file, argp, false);
> > +	case BTRFS_IOC_SNAP_DESTROY_V2:
> > +		return btrfs_ioctl_snap_destroy(file, argp, true);
> >  	case BTRFS_IOC_SUBVOL_GETFLAGS:
> >  		return btrfs_ioctl_subvol_getflags(file, argp);
> >  	case BTRFS_IOC_SUBVOL_SETFLAGS:
> > diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> > index f452a94abdc3..649531e92a1d 100644
> > --- a/fs/btrfs/super.c
> > +++ b/fs/btrfs/super.c
> > @@ -1005,7 +1005,7 @@ static int btrfs_parse_subvol_options(const
> char *options, char **subvol_name,
> >  	return error;
> >  }
> >  
> > -static char *get_subvol_name_from_objectid(struct btrfs_fs_info
> *fs_info,
> > +char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info
> *fs_info,
> >  					   u64 subvol_objectid)
> >  {
> >  	struct btrfs_root *root = fs_info->tree_root;
> > @@ -1417,7 +1417,7 @@ static struct dentry *mount_subvol(const char
> *subvol_name, u64 subvol_objectid,
> >  				goto out;
> >  			}
> >  		}
> > -		subvol_name =
> get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb),
> > +		subvol_name =
> btrfs_get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb),
> >  							    subvol_obje
> ctid);
> >  		if (IS_ERR(subvol_name)) {
> >  			root = ERR_CAST(subvol_name);
> > diff --git a/include/uapi/linux/btrfs.h
> b/include/uapi/linux/btrfs.h
> > index 7a8bc8b920f5..280f6ded2104 100644
> > --- a/include/uapi/linux/btrfs.h
> > +++ b/include/uapi/linux/btrfs.h
> > @@ -42,11 +42,14 @@ struct btrfs_ioctl_vol_args {
> >  
> >  #define BTRFS_DEVICE_SPEC_BY_ID		(1ULL << 3)
> >  
> > +#define BTRFS_SUBVOL_SPEC_BY_ID	(1ULL << 4)
> > +
> >  #define BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED		\
> >  			(BTRFS_SUBVOL_CREATE_ASYNC |	\
> >  			BTRFS_SUBVOL_RDONLY |		\
> >  			BTRFS_SUBVOL_QGROUP_INHERIT |	\
> > -			BTRFS_DEVICE_SPEC_BY_ID)
> > +			BTRFS_DEVICE_SPEC_BY_ID |	\
> > +			BTRFS_SUBVOL_SPEC_BY_ID)
> >  
> >  #define BTRFS_FSID_SIZE 16
> >  #define BTRFS_UUID_SIZE 16
> > @@ -120,6 +123,7 @@ struct btrfs_ioctl_vol_args_v2 {
> >  	};
> >  	union {
> >  		char name[BTRFS_SUBVOL_NAME_MAX + 1];
> > +		__u64 subvolid;
> >  		__u64 devid;
> >  	};
> >  };
> > @@ -949,5 +953,7 @@ enum btrfs_err_code {
> >  				struct
> btrfs_ioctl_get_subvol_rootref_args)
> >  #define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \
> >  				struct
> btrfs_ioctl_ino_lookup_user_args)
> > +#define BTRFS_IOC_SNAP_DESTROY_V2 _IOW(BTRFS_IOCTL_MAGIC, 63, \
> > +				struct btrfs_ioctl_vol_args_v2)
> >  
> >  #endif /* _UAPI_LINUX_BTRFS_H */
> > -- 
> > 2.24.0
> > 
> 
> Hi Marcos,
> 
> We received a build report from the 0day bot when building with clang
> that appears legitimate if I am reading everything correctly.
> 
> ../fs/btrfs/ioctl.c:2867:4: warning: array index 4087 is past the end
> of the array (which contains 4040 elements) [-Warray-bounds]
>                         vol_args2->name[BTRFS_PATH_NAME_MAX] = '\0';
>                         ^               ~~~~~~~~~~~~~~~~~~~
> ../include/uapi/linux/btrfs.h:125:3: note: array 'name' declared here
>                 char name[BTRFS_SUBVOL_NAME_MAX + 1];
>                 ^
> 1 warning generated.

Sure, I will send a new patch to address this warning after this one
gets merged, since this problem existed before this change. Thanks for
the report!

> 
> The full report can be viewed here:
> 
> https://groups.google.com/d/msg/clang-built-linux/YFcXVkPdkTY/EhB6grZ2BQAJ
> 
> Mind taking a look at it?
> 
> Cheers,
> Nathan




[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux