diff options
Diffstat (limited to 'fs/ioctl.c')
| -rw-r--r-- | fs/ioctl.c | 92 | 
1 files changed, 53 insertions, 39 deletions
diff --git a/fs/ioctl.c b/fs/ioctl.c index 6c751106c2e..7faefb4da93 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -228,14 +228,23 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)  #ifdef CONFIG_BLOCK -#define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits) -#define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits); +static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) +{ +	return (offset >> inode->i_blkbits); +} + +static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) +{ +	return (blk << inode->i_blkbits); +}  /**   * __generic_block_fiemap - FIEMAP for block based inodes (no locking) - * @inode - the inode to map - * @arg - the pointer to userspace where we copy everything to - * @get_block - the fs's get_block function + * @inode: the inode to map + * @fieinfo: the fiemap info struct that will be passed back to userspace + * @start: where to start mapping in the inode + * @len: how much space to map + * @get_block: the fs's get_block function   *   * This does FIEMAP for block based inodes.  Basically it will just loop   * through get_block until we hit the number of extents we want to map, or we @@ -250,58 +259,63 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)   */  int __generic_block_fiemap(struct inode *inode, -			   struct fiemap_extent_info *fieinfo, u64 start, -			   u64 len, get_block_t *get_block) +			   struct fiemap_extent_info *fieinfo, loff_t start, +			   loff_t len, get_block_t *get_block)  { -	struct buffer_head tmp; -	unsigned long long start_blk; -	long long length = 0, map_len = 0; +	struct buffer_head map_bh; +	sector_t start_blk, last_blk; +	loff_t isize = i_size_read(inode);  	u64 logical = 0, phys = 0, size = 0;  	u32 flags = FIEMAP_EXTENT_MERGED; -	int ret = 0, past_eof = 0, whole_file = 0; +	bool past_eof = false, whole_file = false; +	int ret = 0; -	if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC))) +	ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); +	if (ret)  		return ret; -	start_blk = logical_to_blk(inode, start); - -	length = (long long)min_t(u64, len, i_size_read(inode)); -	if (length < len) -		whole_file = 1; +	/* +	 * Either the i_mutex or other appropriate locking needs to be held +	 * since we expect isize to not change at all through the duration of +	 * this call. +	 */ +	if (len >= isize) { +		whole_file = true; +		len = isize; +	} -	map_len = length; +	start_blk = logical_to_blk(inode, start); +	last_blk = logical_to_blk(inode, start + len - 1);  	do {  		/*  		 * we set b_size to the total size we want so it will map as  		 * many contiguous blocks as possible at once  		 */ -		memset(&tmp, 0, sizeof(struct buffer_head)); -		tmp.b_size = map_len; +		memset(&map_bh, 0, sizeof(struct buffer_head)); +		map_bh.b_size = len; -		ret = get_block(inode, start_blk, &tmp, 0); +		ret = get_block(inode, start_blk, &map_bh, 0);  		if (ret)  			break;  		/* HOLE */ -		if (!buffer_mapped(&tmp)) { -			length -= blk_to_logical(inode, 1); +		if (!buffer_mapped(&map_bh)) {  			start_blk++;  			/* -			 * we want to handle the case where there is an +			 * We want to handle the case where there is an  			 * allocated block at the front of the file, and then  			 * nothing but holes up to the end of the file properly,  			 * to make sure that extent at the front gets properly  			 * marked with FIEMAP_EXTENT_LAST  			 */  			if (!past_eof && -			    blk_to_logical(inode, start_blk) >= -			    blk_to_logical(inode, 0)+i_size_read(inode)) +			    blk_to_logical(inode, start_blk) >= isize)  				past_eof = 1;  			/* -			 * first hole after going past the EOF, this is our +			 * First hole after going past the EOF, this is our  			 * last extent  			 */  			if (past_eof && size) { @@ -309,15 +323,18 @@ int __generic_block_fiemap(struct inode *inode,  				ret = fiemap_fill_next_extent(fieinfo, logical,  							      phys, size,  							      flags); -				break; +			} else if (size) { +				ret = fiemap_fill_next_extent(fieinfo, logical, +							      phys, size, flags); +				size = 0;  			}  			/* if we have holes up to/past EOF then we're done */ -			if (length <= 0 || past_eof) +			if (start_blk > last_blk || past_eof || ret)  				break;  		} else {  			/* -			 * we have gone over the length of what we wanted to +			 * We have gone over the length of what we wanted to  			 * map, and it wasn't the entire file, so add the extent  			 * we got last time and exit.  			 * @@ -331,7 +348,7 @@ int __generic_block_fiemap(struct inode *inode,  			 * are good to go, just add the extent to the fieinfo  			 * and break  			 */ -			if (length <= 0 && !whole_file) { +			if (start_blk > last_blk && !whole_file) {  				ret = fiemap_fill_next_extent(fieinfo, logical,  							      phys, size,  							      flags); @@ -351,11 +368,10 @@ int __generic_block_fiemap(struct inode *inode,  			}  			logical = blk_to_logical(inode, start_blk); -			phys = blk_to_logical(inode, tmp.b_blocknr); -			size = tmp.b_size; +			phys = blk_to_logical(inode, map_bh.b_blocknr); +			size = map_bh.b_size;  			flags = FIEMAP_EXTENT_MERGED; -			length -= tmp.b_size;  			start_blk += logical_to_blk(inode, size);  			/* @@ -363,15 +379,13 @@ int __generic_block_fiemap(struct inode *inode,  			 * soon as we find a hole that the last extent we found  			 * is marked with FIEMAP_EXTENT_LAST  			 */ -			if (!past_eof && -			    logical+size >= -			    blk_to_logical(inode, 0)+i_size_read(inode)) -				past_eof = 1; +			if (!past_eof && logical + size >= isize) +				past_eof = true;  		}  		cond_resched();  	} while (1); -	/* if ret is 1 then we just hit the end of the extent array */ +	/* If ret is 1 then we just hit the end of the extent array */  	if (ret == 1)  		ret = 0;  |