mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
cifs: add support for SEEK_DATA and SEEK_HOLE
Add llseek op for SEEK_DATA and SEEK_HOLE. Improves xfstests/285,286,436,445,448 and 490 Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
9ab70ca653
commit
dece44e381
@ -878,6 +878,9 @@ out:
|
||||
|
||||
static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
struct cifs_tcon *tcon;
|
||||
|
||||
/*
|
||||
* whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
|
||||
* the cached file length
|
||||
@ -909,6 +912,12 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
|
||||
if (rc < 0)
|
||||
return (loff_t)rc;
|
||||
}
|
||||
if (cfile && cfile->tlink) {
|
||||
tcon = tlink_tcon(cfile->tlink);
|
||||
if (tcon->ses->server->ops->llseek)
|
||||
return tcon->ses->server->ops->llseek(file, tcon,
|
||||
offset, whence);
|
||||
}
|
||||
return generic_file_llseek(file, offset, whence);
|
||||
}
|
||||
|
||||
|
@ -497,6 +497,8 @@ struct smb_version_operations {
|
||||
/* version specific fiemap implementation */
|
||||
int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *,
|
||||
struct fiemap_extent_info *, u64, u64);
|
||||
/* version specific llseek implementation */
|
||||
loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int);
|
||||
};
|
||||
|
||||
struct smb_version_values {
|
||||
|
@ -2922,6 +2922,90 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence)
|
||||
{
|
||||
struct cifsFileInfo *wrcfile, *cfile = file->private_data;
|
||||
struct cifsInodeInfo *cifsi;
|
||||
struct inode *inode;
|
||||
int rc = 0;
|
||||
struct file_allocated_range_buffer in_data, *out_data = NULL;
|
||||
u32 out_data_len;
|
||||
unsigned int xid;
|
||||
|
||||
if (whence != SEEK_HOLE && whence != SEEK_DATA)
|
||||
return generic_file_llseek(file, offset, whence);
|
||||
|
||||
inode = d_inode(cfile->dentry);
|
||||
cifsi = CIFS_I(inode);
|
||||
|
||||
if (offset < 0 || offset >= i_size_read(inode))
|
||||
return -ENXIO;
|
||||
|
||||
xid = get_xid();
|
||||
/*
|
||||
* We need to be sure that all dirty pages are written as they
|
||||
* might fill holes on the server.
|
||||
* Note that we also MUST flush any written pages since at least
|
||||
* some servers (Windows2016) will not reflect recent writes in
|
||||
* QUERY_ALLOCATED_RANGES until SMB2_flush is called.
|
||||
*/
|
||||
wrcfile = find_writable_file(cifsi, false);
|
||||
if (wrcfile) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
smb2_flush_file(xid, tcon, &wrcfile->fid);
|
||||
cifsFileInfo_put(wrcfile);
|
||||
}
|
||||
|
||||
if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) {
|
||||
if (whence == SEEK_HOLE)
|
||||
offset = i_size_read(inode);
|
||||
goto lseek_exit;
|
||||
}
|
||||
|
||||
in_data.file_offset = cpu_to_le64(offset);
|
||||
in_data.length = cpu_to_le64(i_size_read(inode));
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid,
|
||||
FSCTL_QUERY_ALLOCATED_RANGES, true,
|
||||
(char *)&in_data, sizeof(in_data),
|
||||
sizeof(struct file_allocated_range_buffer),
|
||||
(char **)&out_data, &out_data_len);
|
||||
if (rc == -E2BIG)
|
||||
rc = 0;
|
||||
if (rc)
|
||||
goto lseek_exit;
|
||||
|
||||
if (whence == SEEK_HOLE && out_data_len == 0)
|
||||
goto lseek_exit;
|
||||
|
||||
if (whence == SEEK_DATA && out_data_len == 0) {
|
||||
rc = -ENXIO;
|
||||
goto lseek_exit;
|
||||
}
|
||||
|
||||
if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
|
||||
rc = -EINVAL;
|
||||
goto lseek_exit;
|
||||
}
|
||||
if (whence == SEEK_DATA) {
|
||||
offset = le64_to_cpu(out_data->file_offset);
|
||||
goto lseek_exit;
|
||||
}
|
||||
if (offset < le64_to_cpu(out_data->file_offset))
|
||||
goto lseek_exit;
|
||||
|
||||
offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length);
|
||||
|
||||
lseek_exit:
|
||||
free_xid(xid);
|
||||
kfree(out_data);
|
||||
if (!rc)
|
||||
return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
||||
else
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smb3_fiemap(struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile,
|
||||
struct fiemap_extent_info *fei, u64 start, u64 len)
|
||||
@ -4166,6 +4250,7 @@ struct smb_version_operations smb20_operations = {
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb21_operations = {
|
||||
@ -4266,6 +4351,7 @@ struct smb_version_operations smb21_operations = {
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb30_operations = {
|
||||
@ -4375,6 +4461,7 @@ struct smb_version_operations smb30_operations = {
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb311_operations = {
|
||||
@ -4485,6 +4572,7 @@ struct smb_version_operations smb311_operations = {
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
};
|
||||
|
||||
struct smb_version_values smb20_values = {
|
||||
|
Loading…
Reference in New Issue
Block a user