diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 27 | 
1 files changed, 20 insertions, 7 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index bd3511c5ca81..ac45f022b495 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2086,9 +2086,14 @@ static noinline int copy_to_sk(struct btrfs_path *path,  		sh.len = item_len;  		sh.transid = found_transid; -		/* copy search result header */ -		if (copy_to_user(ubuf + *sk_offset, &sh, sizeof(sh))) { -			ret = -EFAULT; +		/* +		 * Copy search result header. If we fault then loop again so we +		 * can fault in the pages and -EFAULT there if there's a +		 * problem. Otherwise we'll fault and then copy the buffer in +		 * properly this next time through +		 */ +		if (copy_to_user_nofault(ubuf + *sk_offset, &sh, sizeof(sh))) { +			ret = 0;  			goto out;  		} @@ -2096,10 +2101,14 @@ static noinline int copy_to_sk(struct btrfs_path *path,  		if (item_len) {  			char __user *up = ubuf + *sk_offset; -			/* copy the item */ -			if (read_extent_buffer_to_user(leaf, up, -						       item_off, item_len)) { -				ret = -EFAULT; +			/* +			 * Copy the item, same behavior as above, but reset the +			 * * sk_offset so we copy the full thing again. +			 */ +			if (read_extent_buffer_to_user_nofault(leaf, up, +						item_off, item_len)) { +				ret = 0; +				*sk_offset -= sizeof(sh);  				goto out;  			} @@ -2184,6 +2193,10 @@ static noinline int search_ioctl(struct inode *inode,  	key.offset = sk->min_offset;  	while (1) { +		ret = fault_in_pages_writeable(ubuf, *buf_size - sk_offset); +		if (ret) +			break; +  		ret = btrfs_search_forward(root, &key, path, sk->min_transid);  		if (ret != 0) {  			if (ret > 0)  | 
