diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 70 | 
1 files changed, 39 insertions, 31 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5d91776e12a2..0adf5422fce9 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1030,6 +1030,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u32 thresh,  	struct extent_map *em;  	int ret = 1;  	bool next_mergeable = true; +	bool prev_mergeable = true;  	/*  	 * make sure that once we start defragging an extent, we keep on @@ -1050,13 +1051,16 @@ static int should_defrag_range(struct inode *inode, u64 start, u32 thresh,  		goto out;  	} +	if (!*defrag_end) +		prev_mergeable = false; +  	next_mergeable = defrag_check_next_extent(inode, em);  	/*  	 * we hit a real extent, if it is big or the next extent is not a  	 * real extent, don't bother defragging it  	 */  	if (!compress && (*last_len == 0 || *last_len >= thresh) && -	    (em->len >= thresh || !next_mergeable)) +	    (em->len >= thresh || (!next_mergeable && !prev_mergeable)))  		ret = 0;  out:  	/* @@ -1933,6 +1937,7 @@ static noinline int copy_to_sk(struct btrfs_root *root,  	u64 found_transid;  	struct extent_buffer *leaf;  	struct btrfs_ioctl_search_header sh; +	struct btrfs_key test;  	unsigned long item_off;  	unsigned long item_len;  	int nritems; @@ -2016,12 +2021,17 @@ static noinline int copy_to_sk(struct btrfs_root *root,  	}  advance_key:  	ret = 0; -	if (key->offset < (u64)-1 && key->offset < sk->max_offset) +	test.objectid = sk->max_objectid; +	test.type = sk->max_type; +	test.offset = sk->max_offset; +	if (btrfs_comp_cpu_keys(key, &test) >= 0) +		ret = 1; +	else if (key->offset < (u64)-1)  		key->offset++; -	else if (key->type < (u8)-1 && key->type < sk->max_type) { +	else if (key->type < (u8)-1) {  		key->offset = 0;  		key->type++; -	} else if (key->objectid < (u64)-1 && key->objectid < sk->max_objectid) { +	} else if (key->objectid < (u64)-1) {  		key->offset = 0;  		key->type = 0;  		key->objectid++; @@ -2842,8 +2852,7 @@ static void btrfs_double_inode_lock(struct inode *inode1, struct inode *inode2)  		swap(inode1, inode2);  	mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); -	if (inode1 != inode2) -		mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); +	mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD);  }  static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1, @@ -2861,8 +2870,7 @@ static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1,  		swap(loff1, loff2);  	}  	lock_extent_range(inode1, loff1, len); -	if (inode1 != inode2) -		lock_extent_range(inode2, loff2, len); +	lock_extent_range(inode2, loff2, len);  }  struct cmp_pages { @@ -3090,7 +3098,7 @@ out_unlock:  static long btrfs_ioctl_file_extent_same(struct file *file,  			struct btrfs_ioctl_same_args __user *argp)  { -	struct btrfs_ioctl_same_args *same; +	struct btrfs_ioctl_same_args *same = NULL;  	struct btrfs_ioctl_same_extent_info *info;  	struct inode *src = file_inode(file);  	u64 off; @@ -3120,6 +3128,7 @@ static long btrfs_ioctl_file_extent_same(struct file *file,  	if (IS_ERR(same)) {  		ret = PTR_ERR(same); +		same = NULL;  		goto out;  	} @@ -3190,6 +3199,7 @@ static long btrfs_ioctl_file_extent_same(struct file *file,  out:  	mnt_drop_write_file(file); +	kfree(same);  	return ret;  } @@ -3586,6 +3596,20 @@ process_slot:  				u64 trim = 0;  				u64 aligned_end = 0; +				/* +				 * Don't copy an inline extent into an offset +				 * greater than zero. Having an inline extent +				 * at such an offset results in chaos as btrfs +				 * isn't prepared for such cases. Just skip +				 * this case for the same reasons as commented +				 * at btrfs_ioctl_clone(). +				 */ +				if (last_dest_end > 0) { +					ret = -EOPNOTSUPP; +					btrfs_end_transaction(trans, root); +					goto out; +				} +  				if (off > key.offset) {  					skip = off - key.offset;  					new_key.offset += skip; @@ -3771,13 +3795,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  		goto out_fput;  	if (!same_inode) { -		if (inode < src) { -			mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); -			mutex_lock_nested(&src->i_mutex, I_MUTEX_CHILD); -		} else { -			mutex_lock_nested(&src->i_mutex, I_MUTEX_PARENT); -			mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); -		} +		btrfs_double_inode_lock(src, inode);  	} else {  		mutex_lock(&src->i_mutex);  	} @@ -3827,8 +3845,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  		lock_extent_range(src, lock_start, lock_len);  	} else { -		lock_extent_range(src, off, len); -		lock_extent_range(inode, destoff, len); +		btrfs_double_extent_lock(src, off, inode, destoff, len);  	}  	ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); @@ -3839,9 +3856,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  		unlock_extent(&BTRFS_I(src)->io_tree, lock_start, lock_end);  	} else { -		unlock_extent(&BTRFS_I(src)->io_tree, off, off + len - 1); -		unlock_extent(&BTRFS_I(inode)->io_tree, destoff, -			      destoff + len - 1); +		btrfs_double_extent_unlock(src, off, inode, destoff, len);  	}  	/*  	 * Truncate page cache pages so that future reads will see the cloned @@ -3850,17 +3865,10 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	truncate_inode_pages_range(&inode->i_data, destoff,  				   PAGE_CACHE_ALIGN(destoff + len) - 1);  out_unlock: -	if (!same_inode) { -		if (inode < src) { -			mutex_unlock(&src->i_mutex); -			mutex_unlock(&inode->i_mutex); -		} else { -			mutex_unlock(&inode->i_mutex); -			mutex_unlock(&src->i_mutex); -		} -	} else { +	if (!same_inode) +		btrfs_double_inode_unlock(src, inode); +	else  		mutex_unlock(&src->i_mutex); -	}  out_fput:  	fdput(src_file);  out_drop_write:  |