diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 395 | 
1 files changed, 286 insertions, 109 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4b4516770f0..c83086fdda0 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -42,12 +42,12 @@  #include <linux/slab.h>  #include <linux/blkdev.h>  #include <linux/uuid.h> +#include <linux/btrfs.h>  #include "compat.h"  #include "ctree.h"  #include "disk-io.h"  #include "transaction.h"  #include "btrfs_inode.h" -#include "ioctl.h"  #include "print-tree.h"  #include "volumes.h"  #include "locking.h" @@ -152,7 +152,7 @@ void btrfs_inherit_iflags(struct inode *inode, struct inode *dir)  static int btrfs_ioctl_getflags(struct file *file, void __user *arg)  { -	struct btrfs_inode *ip = BTRFS_I(file->f_path.dentry->d_inode); +	struct btrfs_inode *ip = BTRFS_I(file_inode(file));  	unsigned int flags = btrfs_flags_to_ioctl(ip->flags);  	if (copy_to_user(arg, &flags, sizeof(flags))) @@ -177,7 +177,7 @@ static int check_flags(unsigned int flags)  static int btrfs_ioctl_setflags(struct file *file, void __user *arg)  { -	struct inode *inode = file->f_path.dentry->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_inode *ip = BTRFS_I(inode);  	struct btrfs_root *root = ip->root;  	struct btrfs_trans_handle *trans; @@ -310,7 +310,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)  static int btrfs_ioctl_getversion(struct file *file, int __user *arg)  { -	struct inode *inode = file->f_path.dentry->d_inode; +	struct inode *inode = file_inode(file);  	return put_user(inode->i_generation, arg);  } @@ -363,46 +363,52 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)  	return 0;  } -static noinline int create_subvol(struct btrfs_root *root, +static noinline int create_subvol(struct inode *dir,  				  struct dentry *dentry,  				  char *name, int namelen,  				  u64 *async_transid, -				  struct btrfs_qgroup_inherit **inherit) +				  struct btrfs_qgroup_inherit *inherit)  {  	struct btrfs_trans_handle *trans;  	struct btrfs_key key;  	struct btrfs_root_item root_item;  	struct btrfs_inode_item *inode_item;  	struct extent_buffer *leaf; +	struct btrfs_root *root = BTRFS_I(dir)->root;  	struct btrfs_root *new_root; -	struct dentry *parent = dentry->d_parent; -	struct inode *dir; +	struct btrfs_block_rsv block_rsv;  	struct timespec cur_time = CURRENT_TIME;  	int ret;  	int err;  	u64 objectid;  	u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;  	u64 index = 0; +	u64 qgroup_reserved;  	uuid_le new_uuid;  	ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);  	if (ret)  		return ret; -	dir = parent->d_inode; - +	btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP);  	/* -	 * 1 - inode item -	 * 2 - refs -	 * 1 - root item -	 * 2 - dir items +	 * The same as the snapshot creation, please see the comment +	 * of create_snapshot().  	 */ -	trans = btrfs_start_transaction(root, 6); -	if (IS_ERR(trans)) -		return PTR_ERR(trans); +	ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, +					       7, &qgroup_reserved); +	if (ret) +		return ret; -	ret = btrfs_qgroup_inherit(trans, root->fs_info, 0, objectid, -				   inherit ? *inherit : NULL); +	trans = btrfs_start_transaction(root, 0); +	if (IS_ERR(trans)) { +		ret = PTR_ERR(trans); +		goto out; +	} +	trans->block_rsv = &block_rsv; +	trans->bytes_reserved = block_rsv.size; + +	ret = btrfs_qgroup_inherit(trans, root->fs_info, 0, objectid, inherit);  	if (ret)  		goto fail; @@ -515,8 +521,9 @@ static noinline int create_subvol(struct btrfs_root *root,  	BUG_ON(ret); -	d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));  fail: +	trans->block_rsv = NULL; +	trans->bytes_reserved = 0;  	if (async_transid) {  		*async_transid = trans->transid;  		err = btrfs_commit_transaction_async(trans, root, 1); @@ -525,12 +532,18 @@ fail:  	}  	if (err && !ret)  		ret = err; + +	if (!ret) +		d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); +out: +	btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);  	return ret;  } -static int create_snapshot(struct btrfs_root *root, struct dentry *dentry, -			   char *name, int namelen, u64 *async_transid, -			   bool readonly, struct btrfs_qgroup_inherit **inherit) +static int create_snapshot(struct btrfs_root *root, struct inode *dir, +			   struct dentry *dentry, char *name, int namelen, +			   u64 *async_transid, bool readonly, +			   struct btrfs_qgroup_inherit *inherit)  {  	struct inode *inode;  	struct btrfs_pending_snapshot *pending_snapshot; @@ -546,23 +559,31 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,  	btrfs_init_block_rsv(&pending_snapshot->block_rsv,  			     BTRFS_BLOCK_RSV_TEMP); +	/* +	 * 1 - parent dir inode +	 * 2 - dir entries +	 * 1 - root item +	 * 2 - root ref/backref +	 * 1 - root of snapshot +	 */ +	ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root, +					&pending_snapshot->block_rsv, 7, +					&pending_snapshot->qgroup_reserved); +	if (ret) +		goto out; +  	pending_snapshot->dentry = dentry;  	pending_snapshot->root = root;  	pending_snapshot->readonly = readonly; -	if (inherit) { -		pending_snapshot->inherit = *inherit; -		*inherit = NULL;	/* take responsibility to free it */ -	} +	pending_snapshot->dir = dir; +	pending_snapshot->inherit = inherit; -	trans = btrfs_start_transaction(root->fs_info->extent_root, 6); +	trans = btrfs_start_transaction(root, 0);  	if (IS_ERR(trans)) {  		ret = PTR_ERR(trans);  		goto fail;  	} -	ret = btrfs_snap_reserve_metadata(trans, pending_snapshot); -	BUG_ON(ret); -  	spin_lock(&root->fs_info->trans_lock);  	list_add(&pending_snapshot->list,  		 &trans->transaction->pending_snapshots); @@ -599,6 +620,10 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,  	d_instantiate(dentry, inode);  	ret = 0;  fail: +	btrfs_subvolume_release_metadata(BTRFS_I(dir)->root, +					 &pending_snapshot->block_rsv, +					 pending_snapshot->qgroup_reserved); +out:  	kfree(pending_snapshot);  	return ret;  } @@ -692,7 +717,7 @@ static noinline int btrfs_mksubvol(struct path *parent,  				   char *name, int namelen,  				   struct btrfs_root *snap_src,  				   u64 *async_transid, bool readonly, -				   struct btrfs_qgroup_inherit **inherit) +				   struct btrfs_qgroup_inherit *inherit)  {  	struct inode *dir  = parent->dentry->d_inode;  	struct dentry *dentry; @@ -729,11 +754,11 @@ static noinline int btrfs_mksubvol(struct path *parent,  		goto out_up_read;  	if (snap_src) { -		error = create_snapshot(snap_src, dentry, name, namelen, +		error = create_snapshot(snap_src, dir, dentry, name, namelen,  					async_transid, readonly, inherit);  	} else { -		error = create_subvol(BTRFS_I(dir)->root, dentry, -				      name, namelen, async_transid, inherit); +		error = create_subvol(dir, dentry, name, namelen, +				      async_transid, inherit);  	}  	if (!error)  		fsnotify_mkdir(dir, dentry); @@ -815,7 +840,7 @@ static int find_new_extents(struct btrfs_root *root,  	while(1) {  		ret = btrfs_search_forward(root, &min_key, &max_key, -					   path, 0, newer_than); +					   path, newer_than);  		if (ret != 0)  			goto none;  		if (min_key.objectid != ino) @@ -1203,6 +1228,12 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,  		if (!(inode->i_sb->s_flags & MS_ACTIVE))  			break; +		if (btrfs_defrag_cancelled(root->fs_info)) { +			printk(KERN_DEBUG "btrfs: defrag_file cancelled\n"); +			ret = -EAGAIN; +			break; +		} +  		if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,  					 extent_thresh, &last_len, &skip,  					 &defrag_end, range->flags & @@ -1317,7 +1348,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,  	u64 new_size;  	u64 old_size;  	u64 devid = 1; -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_vol_args *vol_args;  	struct btrfs_trans_handle *trans;  	struct btrfs_device *device = NULL; @@ -1326,9 +1357,6 @@ static noinline int btrfs_ioctl_resize(struct file *file,  	int ret = 0;  	int mod = 0; -	if (root->fs_info->sb->s_flags & MS_RDONLY) -		return -EROFS; -  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; @@ -1339,7 +1367,8 @@ static noinline int btrfs_ioctl_resize(struct file *file,  	if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,  			1)) {  		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); -		return -EINPROGRESS; +		mnt_drop_write_file(file); +		return -EINVAL;  	}  	mutex_lock(&root->fs_info->volume_mutex); @@ -1359,21 +1388,27 @@ static noinline int btrfs_ioctl_resize(struct file *file,  		*devstr = '\0';  		devstr = vol_args->name;  		devid = simple_strtoull(devstr, &end, 10); +		if (!devid) { +			ret = -EINVAL; +			goto out_free; +		}  		printk(KERN_INFO "btrfs: resizing devid %llu\n",  		       (unsigned long long)devid);  	} +  	device = btrfs_find_device(root->fs_info, devid, NULL, NULL);  	if (!device) {  		printk(KERN_INFO "btrfs: resizer unable to find device %llu\n",  		       (unsigned long long)devid); -		ret = -EINVAL; +		ret = -ENODEV;  		goto out_free;  	} -	if (device->fs_devices && device->fs_devices->seeding) { + +	if (!device->writeable) {  		printk(KERN_INFO "btrfs: resizer unable to apply on " -		       "seeding device %llu\n", +		       "readonly device %llu\n",  		       (unsigned long long)devid); -		ret = -EINVAL; +		ret = -EPERM;  		goto out_free;  	} @@ -1395,7 +1430,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,  	}  	if (device->is_tgtdev_for_dev_replace) { -		ret = -EINVAL; +		ret = -EPERM;  		goto out_free;  	} @@ -1443,15 +1478,15 @@ out_free:  	kfree(vol_args);  out:  	mutex_unlock(&root->fs_info->volume_mutex); -	mnt_drop_write_file(file);  	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  }  static noinline int btrfs_ioctl_snap_create_transid(struct file *file,  				char *name, unsigned long fd, int subvol,  				u64 *transid, bool readonly, -				struct btrfs_qgroup_inherit **inherit) +				struct btrfs_qgroup_inherit *inherit)  {  	int namelen;  	int ret = 0; @@ -1483,8 +1518,8 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,  			goto out_drop_write;  		} -		src_inode = src.file->f_path.dentry->d_inode; -		if (src_inode->i_sb != file->f_path.dentry->d_inode->i_sb) { +		src_inode = file_inode(src.file); +		if (src_inode->i_sb != file_inode(file)->i_sb) {  			printk(KERN_INFO "btrfs: Snapshot src from "  			       "another FS\n");  			ret = -EINVAL; @@ -1560,7 +1595,7 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,  	ret = btrfs_ioctl_snap_create_transid(file, vol_args->name,  					      vol_args->fd, subvol, ptr, -					      readonly, &inherit); +					      readonly, inherit);  	if (ret == 0 && ptr &&  	    copy_to_user(arg + @@ -1576,7 +1611,7 @@ out:  static noinline int btrfs_ioctl_subvol_getflags(struct file *file,  						void __user *arg)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	int ret = 0;  	u64 flags = 0; @@ -1598,7 +1633,7 @@ static noinline int btrfs_ioctl_subvol_getflags(struct file *file,  static noinline int btrfs_ioctl_subvol_setflags(struct file *file,  					      void __user *arg)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_trans_handle *trans;  	u64 root_flags; @@ -1857,7 +1892,7 @@ static noinline int search_ioctl(struct inode *inode,  	path->keep_locks = 1;  	while(1) { -		ret = btrfs_search_forward(root, &key, &max_key, path, 0, +		ret = btrfs_search_forward(root, &key, &max_key, path,  					   sk->min_transid);  		if (ret != 0) {  			if (ret > 0) @@ -1892,7 +1927,7 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,  	if (IS_ERR(args))  		return PTR_ERR(args); -	inode = fdentry(file)->d_inode; +	inode = file_inode(file);  	ret = search_ioctl(inode, args);  	if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))  		ret = -EFAULT; @@ -2002,7 +2037,7 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,  	if (IS_ERR(args))  		return PTR_ERR(args); -	inode = fdentry(file)->d_inode; +	inode = file_inode(file);  	if (args->treeid == 0)  		args->treeid = BTRFS_I(inode)->root->root_key.objectid; @@ -2029,6 +2064,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  	struct btrfs_root *dest = NULL;  	struct btrfs_ioctl_vol_args *vol_args;  	struct btrfs_trans_handle *trans; +	struct btrfs_block_rsv block_rsv; +	u64 qgroup_reserved;  	int namelen;  	int ret;  	int err = 0; @@ -2095,13 +2132,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  		err = inode_permission(inode, MAY_WRITE | MAY_EXEC);  		if (err)  			goto out_dput; - -		/* check if subvolume may be deleted by a non-root user */ -		err = btrfs_may_delete(dir, dentry, 1); -		if (err) -			goto out_dput;  	} +	/* check if subvolume may be deleted by a user */ +	err = btrfs_may_delete(dir, dentry, 1); +	if (err) +		goto out_dput; +  	if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {  		err = -EINVAL;  		goto out_dput; @@ -2118,12 +2155,23 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  	if (err)  		goto out_up_write; +	btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP); +	/* +	 * One for dir inode, two for dir entries, two for root +	 * ref/backref. +	 */ +	err = btrfs_subvolume_reserve_metadata(root, &block_rsv, +					       5, &qgroup_reserved); +	if (err) +		goto out_up_write; +  	trans = btrfs_start_transaction(root, 0);  	if (IS_ERR(trans)) {  		err = PTR_ERR(trans); -		goto out_up_write; +		goto out_release;  	} -	trans->block_rsv = &root->fs_info->global_block_rsv; +	trans->block_rsv = &block_rsv; +	trans->bytes_reserved = block_rsv.size;  	ret = btrfs_unlink_subvol(trans, root, dir,  				dest->root_key.objectid, @@ -2153,10 +2201,14 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  		}  	}  out_end_trans: +	trans->block_rsv = NULL; +	trans->bytes_reserved = 0;  	ret = btrfs_end_transaction(trans, root);  	if (ret && !err)  		err = ret;  	inode->i_flags |= S_DEAD; +out_release: +	btrfs_subvolume_release_metadata(root, &block_rsv, qgroup_reserved);  out_up_write:  	up_write(&root->fs_info->subvol_sem);  out_unlock: @@ -2165,6 +2217,12 @@ out_unlock:  		shrink_dcache_sb(root->fs_info->sb);  		btrfs_invalidate_inodes(dest);  		d_delete(dentry); + +		/* the last ref */ +		if (dest->cache_inode) { +			iput(dest->cache_inode); +			dest->cache_inode = NULL; +		}  	}  out_dput:  	dput(dentry); @@ -2178,24 +2236,25 @@ out:  static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_ioctl_defrag_range_args *range;  	int ret; -	if (btrfs_root_readonly(root)) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret;  	if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,  			1)) {  		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); -		return -EINPROGRESS; +		mnt_drop_write_file(file); +		return -EINVAL;  	} -	ret = mnt_want_write_file(file); -	if (ret) { -		atomic_set(&root->fs_info->mutually_exclusive_operation_running, -			   0); -		return ret; + +	if (btrfs_root_readonly(root)) { +		ret = -EROFS; +		goto out;  	}  	switch (inode->i_mode & S_IFMT) { @@ -2204,10 +2263,10 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  			ret = -EPERM;  			goto out;  		} -		ret = btrfs_defrag_root(root, 0); +		ret = btrfs_defrag_root(root);  		if (ret)  			goto out; -		ret = btrfs_defrag_root(root->fs_info->extent_root, 0); +		ret = btrfs_defrag_root(root->fs_info->extent_root);  		break;  	case S_IFREG:  		if (!(file->f_mode & FMODE_WRITE)) { @@ -2237,7 +2296,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  			/* the rest are all set to zero by kzalloc */  			range->len = (u64)-1;  		} -		ret = btrfs_defrag_file(fdentry(file)->d_inode, file, +		ret = btrfs_defrag_file(file_inode(file), file,  					range, 0, 0);  		if (ret > 0)  			ret = 0; @@ -2247,8 +2306,8 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  		ret = -EINVAL;  	}  out: -	mnt_drop_write_file(file);  	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  } @@ -2263,7 +2322,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)  	if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,  			1)) {  		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); -		return -EINPROGRESS; +		return -EINVAL;  	}  	mutex_lock(&root->fs_info->volume_mutex); @@ -2285,7 +2344,7 @@ out:  static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_vol_args *vol_args;  	int ret; @@ -2300,7 +2359,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)  			1)) {  		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");  		mnt_drop_write_file(file); -		return -EINPROGRESS; +		return -EINVAL;  	}  	mutex_lock(&root->fs_info->volume_mutex); @@ -2316,8 +2375,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)  	kfree(vol_args);  out:  	mutex_unlock(&root->fs_info->volume_mutex); -	mnt_drop_write_file(file);  	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  } @@ -2408,7 +2467,7 @@ out:  static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  				       u64 off, u64 olen, u64 destoff)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct fd src_file;  	struct inode *src; @@ -2454,7 +2513,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	if (src_file.file->f_path.mnt != file->f_path.mnt)  		goto out_fput; -	src = src_file.file->f_dentry->d_inode; +	src = file_inode(src_file.file);  	ret = -EINVAL;  	if (src == inode) @@ -2816,7 +2875,7 @@ static long btrfs_ioctl_clone_range(struct file *file, void __user *argp)   */  static long btrfs_ioctl_trans_start(struct file *file)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_trans_handle *trans;  	int ret; @@ -2856,7 +2915,7 @@ out:  static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_root *new_root;  	struct btrfs_dir_item *di; @@ -3080,7 +3139,7 @@ out:   */  long btrfs_ioctl_trans_end(struct file *file)  { -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_trans_handle *trans; @@ -3104,7 +3163,7 @@ static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root,  	u64 transid;  	int ret; -	trans = btrfs_attach_transaction(root); +	trans = btrfs_attach_transaction_barrier(root);  	if (IS_ERR(trans)) {  		if (PTR_ERR(trans) != -ENOENT)  			return PTR_ERR(trans); @@ -3142,7 +3201,7 @@ static noinline long btrfs_ioctl_wait_sync(struct btrfs_root *root,  static long btrfs_ioctl_scrub(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_scrub_args *sa;  	int ret; @@ -3282,7 +3341,7 @@ static long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg)  	struct inode_fs_paths *ipath = NULL;  	struct btrfs_path *path; -	if (!capable(CAP_SYS_ADMIN)) +	if (!capable(CAP_DAC_READ_SEARCH))  		return -EPERM;  	path = btrfs_alloc_path(); @@ -3433,12 +3492,12 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,  static long btrfs_ioctl_balance(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_fs_info *fs_info = root->fs_info;  	struct btrfs_ioctl_balance_args *bargs;  	struct btrfs_balance_control *bctl; +	bool need_unlock; /* for mut. excl. ops lock */  	int ret; -	int need_to_clear_lock = 0;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; @@ -3447,14 +3506,61 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  	if (ret)  		return ret; -	mutex_lock(&fs_info->volume_mutex); +again: +	if (!atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1)) { +		mutex_lock(&fs_info->volume_mutex); +		mutex_lock(&fs_info->balance_mutex); +		need_unlock = true; +		goto locked; +	} + +	/* +	 * mut. excl. ops lock is locked.  Three possibilites: +	 *   (1) some other op is running +	 *   (2) balance is running +	 *   (3) balance is paused -- special case (think resume) +	 */  	mutex_lock(&fs_info->balance_mutex); +	if (fs_info->balance_ctl) { +		/* this is either (2) or (3) */ +		if (!atomic_read(&fs_info->balance_running)) { +			mutex_unlock(&fs_info->balance_mutex); +			if (!mutex_trylock(&fs_info->volume_mutex)) +				goto again; +			mutex_lock(&fs_info->balance_mutex); + +			if (fs_info->balance_ctl && +			    !atomic_read(&fs_info->balance_running)) { +				/* this is (3) */ +				need_unlock = false; +				goto locked; +			} + +			mutex_unlock(&fs_info->balance_mutex); +			mutex_unlock(&fs_info->volume_mutex); +			goto again; +		} else { +			/* this is (2) */ +			mutex_unlock(&fs_info->balance_mutex); +			ret = -EINPROGRESS; +			goto out; +		} +	} else { +		/* this is (1) */ +		mutex_unlock(&fs_info->balance_mutex); +		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); +		ret = -EINVAL; +		goto out; +	} + +locked: +	BUG_ON(!atomic_read(&fs_info->mutually_exclusive_operation_running));  	if (arg) {  		bargs = memdup_user(arg, sizeof(*bargs));  		if (IS_ERR(bargs)) {  			ret = PTR_ERR(bargs); -			goto out; +			goto out_unlock;  		}  		if (bargs->flags & BTRFS_BALANCE_RESUME) { @@ -3474,13 +3580,10 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  		bargs = NULL;  	} -	if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, -			1)) { -		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); +	if (fs_info->balance_ctl) {  		ret = -EINPROGRESS;  		goto out_bargs;  	} -	need_to_clear_lock = 1;  	bctl = kzalloc(sizeof(*bctl), GFP_NOFS);  	if (!bctl) { @@ -3501,11 +3604,17 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  	}  do_balance: -	ret = btrfs_balance(bctl, bargs);  	/* -	 * bctl is freed in __cancel_balance or in free_fs_info if -	 * restriper was paused all the way until unmount +	 * Ownership of bctl and mutually_exclusive_operation_running +	 * goes to to btrfs_balance.  bctl is freed in __cancel_balance, +	 * or, if restriper was paused all the way until unmount, in +	 * free_fs_info.  mutually_exclusive_operation_running is +	 * cleared in __cancel_balance.  	 */ +	need_unlock = false; + +	ret = btrfs_balance(bctl, bargs); +  	if (arg) {  		if (copy_to_user(arg, bargs, sizeof(*bargs)))  			ret = -EFAULT; @@ -3513,12 +3622,12 @@ do_balance:  out_bargs:  	kfree(bargs); -out: -	if (need_to_clear_lock) -		atomic_set(&root->fs_info->mutually_exclusive_operation_running, -			   0); +out_unlock:  	mutex_unlock(&fs_info->balance_mutex);  	mutex_unlock(&fs_info->volume_mutex); +	if (need_unlock) +		atomic_set(&fs_info->mutually_exclusive_operation_running, 0); +out:  	mnt_drop_write_file(file);  	return ret;  } @@ -3573,7 +3682,7 @@ out:  static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_quota_ctl_args *sa;  	struct btrfs_trans_handle *trans = NULL;  	int ret; @@ -3632,7 +3741,7 @@ drop_write:  static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_qgroup_assign_args *sa;  	struct btrfs_trans_handle *trans;  	int ret; @@ -3679,7 +3788,7 @@ drop_write:  static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_qgroup_create_args *sa;  	struct btrfs_trans_handle *trans;  	int ret; @@ -3698,6 +3807,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)  		goto drop_write;  	} +	if (!sa->qgroupid) { +		ret = -EINVAL; +		goto out; +	} +  	trans = btrfs_join_transaction(root);  	if (IS_ERR(trans)) {  		ret = PTR_ERR(trans); @@ -3725,7 +3839,7 @@ drop_write:  static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	struct btrfs_ioctl_qgroup_limit_args *sa;  	struct btrfs_trans_handle *trans;  	int ret; @@ -3775,7 +3889,7 @@ static long btrfs_ioctl_set_received_subvol(struct file *file,  					    void __user *arg)  {  	struct btrfs_ioctl_received_subvol_args *sa = NULL; -	struct inode *inode = fdentry(file)->d_inode; +	struct inode *inode = file_inode(file);  	struct btrfs_root *root = BTRFS_I(inode)->root;  	struct btrfs_root_item *root_item = &root->root_item;  	struct btrfs_trans_handle *trans; @@ -3852,10 +3966,69 @@ out:  	return ret;  } +static int btrfs_ioctl_get_fslabel(struct file *file, void __user *arg) +{ +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	const char *label = root->fs_info->super_copy->label; +	size_t len = strnlen(label, BTRFS_LABEL_SIZE); +	int ret; + +	if (len == BTRFS_LABEL_SIZE) { +		pr_warn("btrfs: label is too long, return the first %zu bytes\n", +			--len); +	} + +	mutex_lock(&root->fs_info->volume_mutex); +	ret = copy_to_user(arg, label, len); +	mutex_unlock(&root->fs_info->volume_mutex); + +	return ret ? -EFAULT : 0; +} + +static int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg) +{ +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_super_block *super_block = root->fs_info->super_copy; +	struct btrfs_trans_handle *trans; +	char label[BTRFS_LABEL_SIZE]; +	int ret; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	if (copy_from_user(label, arg, sizeof(label))) +		return -EFAULT; + +	if (strnlen(label, BTRFS_LABEL_SIZE) == BTRFS_LABEL_SIZE) { +		pr_err("btrfs: unable to set label with more than %d bytes\n", +		       BTRFS_LABEL_SIZE - 1); +		return -EINVAL; +	} + +	ret = mnt_want_write_file(file); +	if (ret) +		return ret; + +	mutex_lock(&root->fs_info->volume_mutex); +	trans = btrfs_start_transaction(root, 0); +	if (IS_ERR(trans)) { +		ret = PTR_ERR(trans); +		goto out_unlock; +	} + +	strcpy(super_block->label, label); +	ret = btrfs_end_transaction(trans, root); + +out_unlock: +	mutex_unlock(&root->fs_info->volume_mutex); +	mnt_drop_write_file(file); +	return ret; +} +  long btrfs_ioctl(struct file *file, unsigned int  		cmd, unsigned long arg)  { -	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; +	struct btrfs_root *root = BTRFS_I(file_inode(file))->root;  	void __user *argp = (void __user *)arg;  	switch (cmd) { @@ -3952,6 +4125,10 @@ long btrfs_ioctl(struct file *file, unsigned int  		return btrfs_ioctl_qgroup_limit(file, argp);  	case BTRFS_IOC_DEV_REPLACE:  		return btrfs_ioctl_dev_replace(root, argp); +	case BTRFS_IOC_GET_FSLABEL: +		return btrfs_ioctl_get_fslabel(file, argp); +	case BTRFS_IOC_SET_FSLABEL: +		return btrfs_ioctl_set_fslabel(file, argp);  	}  	return -ENOTTY;  |