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>
This commit is contained in:
Josef Bacik 2011-07-18 13:21:38 -04:00 коммит произвёл Al Viro
Родитель c334b1138b
Коммит 06222e491e
7 изменённых файлов: 66 добавлений и 12 удалений

Просмотреть файл

@ -355,20 +355,25 @@ static loff_t block_llseek(struct file *file, loff_t offset, int origin)
mutex_lock(&bd_inode->i_mutex); mutex_lock(&bd_inode->i_mutex);
size = i_size_read(bd_inode); size = i_size_read(bd_inode);
retval = -EINVAL;
switch (origin) { switch (origin) {
case 2: case SEEK_END:
offset += size; offset += size;
break; break;
case 1: case SEEK_CUR:
offset += file->f_pos; offset += file->f_pos;
case SEEK_SET:
break;
default:
goto out;
} }
retval = -EINVAL;
if (offset >= 0 && offset <= size) { if (offset >= 0 && offset <= size) {
if (offset != file->f_pos) { if (offset != file->f_pos) {
file->f_pos = offset; file->f_pos = offset;
} }
retval = offset; retval = offset;
} }
out:
mutex_unlock(&bd_inode->i_mutex); mutex_unlock(&bd_inode->i_mutex);
return retval; return retval;
} }

Просмотреть файл

@ -446,14 +446,19 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
loff_t retval; loff_t retval;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
retval = -EINVAL;
switch (origin) { switch (origin) {
case SEEK_END: case SEEK_END:
offset += inode->i_size + 2; /* FIXME */ offset += inode->i_size + 2; /* FIXME */
break; break;
case SEEK_CUR: case SEEK_CUR:
offset += file->f_pos; offset += file->f_pos;
case SEEK_SET:
break;
default:
goto out;
} }
retval = -EINVAL;
if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
if (offset != file->f_pos) { if (offset != file->f_pos) {
file->f_pos = offset; 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) if (offset > old_offset)
fi->dir_release_count--; fi->dir_release_count--;
} }
out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return retval; return retval;
} }

Просмотреть файл

@ -768,13 +768,16 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int origin)
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
__ceph_do_pending_vmtruncate(inode); __ceph_do_pending_vmtruncate(inode);
switch (origin) { if (origin != SEEK_CUR || origin != SEEK_SET) {
case SEEK_END:
ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE); ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE);
if (ret < 0) { if (ret < 0) {
offset = ret; offset = ret;
goto out; goto out;
} }
}
switch (origin) {
case SEEK_END:
offset += inode->i_size; offset += inode->i_size;
break; break;
case SEEK_CUR: case SEEK_CUR:
@ -790,6 +793,19 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int origin)
} }
offset += file->f_pos; offset += file->f_pos;
break; 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) { if (offset < 0 || offset > inode->i_sb->s_maxbytes) {

Просмотреть файл

@ -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) 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; int rc;
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;

Просмотреть файл

@ -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; struct inode *inode = file->f_path.dentry->d_inode;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
switch (origin) { if (origin != SEEK_CUR || origin != SEEK_SET) {
case SEEK_END:
retval = fuse_update_attributes(inode, NULL, file, NULL); retval = fuse_update_attributes(inode, NULL, file, NULL);
if (retval) if (retval)
goto exit; goto exit;
}
switch (origin) {
case SEEK_END:
offset += i_size_read(inode); offset += i_size_read(inode);
break; break;
case SEEK_CUR: case SEEK_CUR:
offset += file->f_pos; 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; retval = -EINVAL;
if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {

Просмотреть файл

@ -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 hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct super_block *s = i->i_sb; 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); hpfs_lock(s);
/*printk("dir lseek\n");*/ /*printk("dir lseek\n");*/

Просмотреть файл

@ -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, filp->f_path.dentry->d_name.name,
offset, origin); 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; struct inode *inode = filp->f_mapping->host;
int retval = nfs_revalidate_file_size(inode, filp); int retval = nfs_revalidate_file_size(inode, filp);