diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 45 | 
1 files changed, 29 insertions, 16 deletions
| diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a1ee0b775e65..4f4b13830b25 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -704,11 +704,17 @@ static noinline int create_subvol(struct inode *dir,  	btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2);  	ret = btrfs_update_inode(trans, root, dir); -	BUG_ON(ret); +	if (ret) { +		btrfs_abort_transaction(trans, ret); +		goto fail; +	}  	ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid,  				 btrfs_ino(BTRFS_I(dir)), index, name, namelen); -	BUG_ON(ret); +	if (ret) { +		btrfs_abort_transaction(trans, ret); +		goto fail; +	}  	ret = btrfs_uuid_tree_add(trans, root_item->uuid,  				  BTRFS_UUID_KEY_SUBVOL, objectid); @@ -1122,7 +1128,7 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start)  		/* get the big lock and read metadata off disk */  		lock_extent_bits(io_tree, start, end, &cached); -		em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, start, len, 0); +		em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, start, len);  		unlock_extent_cached(io_tree, start, end, &cached);  		if (IS_ERR(em)) @@ -3237,6 +3243,7 @@ static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1,  static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 len,  				   struct inode *dst, u64 dst_loff)  { +	const u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;  	int ret;  	/* @@ -3244,7 +3251,7 @@ static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 len,  	 * source range to serialize with relocation.  	 */  	btrfs_double_extent_lock(src, loff, dst, dst_loff, len); -	ret = btrfs_clone(src, dst, loff, len, len, dst_loff, 1); +	ret = btrfs_clone(src, dst, loff, len, ALIGN(len, bs), dst_loff, 1);  	btrfs_double_extent_unlock(src, loff, dst, dst_loff, len);  	return ret; @@ -3720,24 +3727,18 @@ process_slot:  	ret = 0;  	if (last_dest_end < destoff + len) { -		struct btrfs_clone_extent_info clone_info = { 0 };  		/* -		 * We have an implicit hole (NO_HOLES feature is enabled) that -		 * fully or partially overlaps our cloning range at its end. +		 * We have an implicit hole that fully or partially overlaps our +		 * cloning range at its end. This means that we either have the +		 * NO_HOLES feature enabled or the implicit hole happened due to +		 * mixing buffered and direct IO writes against this file.  		 */  		btrfs_release_path(path);  		path->leave_spinning = 0; -		/* -		 * We are dealing with a hole and our clone_info already has a -		 * disk_offset of 0, we only need to fill the data length and -		 * file offset. -		 */ -		clone_info.data_len = destoff + len - last_dest_end; -		clone_info.file_offset = last_dest_end;  		ret = btrfs_punch_hole_range(inode, path,  					     last_dest_end, destoff + len - 1, -					     &clone_info, &trans); +					     NULL, &trans);  		if (ret)  			goto out; @@ -4252,7 +4253,19 @@ static long btrfs_ioctl_scrub(struct file *file, void __user *arg)  			      &sa->progress, sa->flags & BTRFS_SCRUB_READONLY,  			      0); -	if (ret == 0 && copy_to_user(arg, sa, sizeof(*sa))) +	/* +	 * Copy scrub args to user space even if btrfs_scrub_dev() returned an +	 * error. This is important as it allows user space to know how much +	 * progress scrub has done. For example, if scrub is canceled we get +	 * -ECANCELED from btrfs_scrub_dev() and return that error back to user +	 * space. Later user space can inspect the progress from the structure +	 * btrfs_ioctl_scrub_args and resume scrub from where it left off +	 * previously (btrfs-progs does this). +	 * If we fail to copy the btrfs_ioctl_scrub_args structure to user space +	 * then return -EFAULT to signal the structure was not copied or it may +	 * be corrupt and unreliable due to a partial copy. +	 */ +	if (copy_to_user(arg, sa, sizeof(*sa)))  		ret = -EFAULT;  	if (!(sa->flags & BTRFS_SCRUB_READONLY)) | 
