diff options
| author | Josef Bacik <josef@redhat.com> | 2011-07-18 13:21:38 -0400 | 
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-07-20 20:47:58 -0400 | 
| commit | 06222e491e663dac939f04b125c9dc52126a75c4 (patch) | |
| tree | 99636fd666c8148a5bf58ea4844263d4b3a36310 | |
| parent | c334b1138bd44bea578eab7971c59bd9212a1093 (diff) | |
| download | olio-linux-3.10-06222e491e663dac939f04b125c9dc52126a75c4.tar.xz olio-linux-3.10-06222e491e663dac939f04b125c9dc52126a75c4.zip  | |
fs: handle SEEK_HOLE/SEEK_DATA properly in all fs's that define their own llseek
This converts everybody to handle SEEK_HOLE/SEEK_DATA properly.  In some cases
we just return -EINVAL, in others we do the normal generic thing, and in others
we're simply making sure that the properly due-dilligence is done.  For example
in NFS/CIFS we need to make sure the file size is update properly for the
SEEK_HOLE and SEEK_DATA case, but since it calls the generic llseek stuff itself
that is all we have to do.  Thanks,
Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | fs/block_dev.c | 11 | ||||
| -rw-r--r-- | fs/ceph/dir.c | 8 | ||||
| -rw-r--r-- | fs/ceph/file.c | 20 | ||||
| -rw-r--r-- | fs/cifs/cifsfs.c | 7 | ||||
| -rw-r--r-- | fs/fuse/file.c | 21 | ||||
| -rw-r--r-- | fs/hpfs/dir.c | 4 | ||||
| -rw-r--r-- | fs/nfs/file.c | 7 | 
7 files changed, 66 insertions, 12 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index 610e8e0b04b..966617a422d 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -355,20 +355,25 @@ static loff_t block_llseek(struct file *file, loff_t offset, int origin)  	mutex_lock(&bd_inode->i_mutex);  	size = i_size_read(bd_inode); +	retval = -EINVAL;  	switch (origin) { -		case 2: +		case SEEK_END:  			offset += size;  			break; -		case 1: +		case SEEK_CUR:  			offset += file->f_pos; +		case SEEK_SET: +			break; +		default: +			goto out;  	} -	retval = -EINVAL;  	if (offset >= 0 && offset <= size) {  		if (offset != file->f_pos) {  			file->f_pos = offset;  		}  		retval = offset;  	} +out:  	mutex_unlock(&bd_inode->i_mutex);  	return retval;  } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index e8477dc51b4..0972b457a03 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -446,14 +446,19 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)  	loff_t retval;  	mutex_lock(&inode->i_mutex); +	retval = -EINVAL;  	switch (origin) {  	case SEEK_END:  		offset += inode->i_size + 2;   /* FIXME */  		break;  	case SEEK_CUR:  		offset += file->f_pos; +	case SEEK_SET: +		break; +	default: +		goto out;  	} -	retval = -EINVAL; +  	if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {  		if (offset != file->f_pos) {  			file->f_pos = offset; @@ -477,6 +482,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)  		if (offset > old_offset)  			fi->dir_release_count--;  	} +out:  	mutex_unlock(&inode->i_mutex);  	return retval;  } diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 0a292459c89..0d0eae05598 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -768,13 +768,16 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int origin)  	mutex_lock(&inode->i_mutex);  	__ceph_do_pending_vmtruncate(inode); -	switch (origin) { -	case SEEK_END: +	if (origin != SEEK_CUR || origin != SEEK_SET) {  		ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE);  		if (ret < 0) {  			offset = ret;  			goto out;  		} +	} + +	switch (origin) { +	case SEEK_END:  		offset += inode->i_size;  		break;  	case SEEK_CUR: @@ -790,6 +793,19 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int origin)  		}  		offset += file->f_pos;  		break; +	case SEEK_DATA: +		if (offset >= inode->i_size) { +			ret = -ENXIO; +			goto out; +		} +		break; +	case SEEK_HOLE: +		if (offset >= inode->i_size) { +			ret = -ENXIO; +			goto out; +		} +		offset = inode->i_size; +		break;  	}  	if (offset < 0 || offset > inode->i_sb->s_maxbytes) { diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index cbbb55e781d..86551747096 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -704,8 +704,11 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,  static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)  { -	/* origin == SEEK_END => we must revalidate the cached file length */ -	if (origin == SEEK_END) { +	/* +	 * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate +	 * the cached file length +	 */ +	if (origin != SEEK_SET || origin != SEEK_CUR) {  		int rc;  		struct inode *inode = file->f_path.dentry->d_inode; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 82a66466a24..73b89df2085 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1600,15 +1600,32 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)  	struct inode *inode = file->f_path.dentry->d_inode;  	mutex_lock(&inode->i_mutex); -	switch (origin) { -	case SEEK_END: +	if (origin != SEEK_CUR || origin != SEEK_SET) {  		retval = fuse_update_attributes(inode, NULL, file, NULL);  		if (retval)  			goto exit; +	} + +	switch (origin) { +	case SEEK_END:  		offset += i_size_read(inode);  		break;  	case SEEK_CUR:  		offset += file->f_pos; +		break; +	case SEEK_DATA: +		if (offset >= i_size_read(inode)) { +			retval = -ENXIO; +			goto exit; +		} +		break; +	case SEEK_HOLE: +		if (offset >= i_size_read(inode)) { +			retval = -ENXIO; +			goto exit; +		} +		offset = i_size_read(inode); +		break;  	}  	retval = -EINVAL;  	if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index f46ae025bfb..96a8ed91ced 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -29,6 +29,10 @@ static loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence)  	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);  	struct super_block *s = i->i_sb; +	/* Somebody else will have to figure out what to do here */ +	if (whence == SEEK_DATA || whence == SEEK_HOLE) +		return -EINVAL; +  	hpfs_lock(s);  	/*printk("dir lseek\n");*/ diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 2f093ed1698..2c1705b6acd 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -187,8 +187,11 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)  			filp->f_path.dentry->d_name.name,  			offset, origin); -	/* origin == SEEK_END => we must revalidate the cached file length */ -	if (origin == SEEK_END) { +	/* +	 * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate +	 * the cached file length +	 */ +	if (origin != SEEK_SET || origin != SEEK_CUR) {  		struct inode *inode = filp->f_mapping->host;  		int retval = nfs_revalidate_file_size(inode, filp);  |