aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifsfs.c9
-rw-r--r--fs/cifs/cifsglob.h2
-rw-r--r--fs/cifs/smb2ops.c88
3 files changed, 99 insertions, 0 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index d0cb042732cb..f5fcd6360056 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -878,6 +878,9 @@ out:
878 878
879static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) 879static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
880{ 880{
881 struct cifsFileInfo *cfile = file->private_data;
882 struct cifs_tcon *tcon;
883
881 /* 884 /*
882 * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate 885 * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
883 * the cached file length 886 * the cached file length
@@ -909,6 +912,12 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
909 if (rc < 0) 912 if (rc < 0)
910 return (loff_t)rc; 913 return (loff_t)rc;
911 } 914 }
915 if (cfile && cfile->tlink) {
916 tcon = tlink_tcon(cfile->tlink);
917 if (tcon->ses->server->ops->llseek)
918 return tcon->ses->server->ops->llseek(file, tcon,
919 offset, whence);
920 }
912 return generic_file_llseek(file, offset, whence); 921 return generic_file_llseek(file, offset, whence);
913} 922}
914 923
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 33c251b408aa..334ff5f9c3f3 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -497,6 +497,8 @@ struct smb_version_operations {
497 /* version specific fiemap implementation */ 497 /* version specific fiemap implementation */
498 int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *, 498 int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *,
499 struct fiemap_extent_info *, u64, u64); 499 struct fiemap_extent_info *, u64, u64);
500 /* version specific llseek implementation */
501 loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int);
500}; 502};
501 503
502struct smb_version_values { 504struct smb_version_values {
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 542b50c0b292..e921e6511728 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2922,6 +2922,90 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
2922 return rc; 2922 return rc;
2923} 2923}
2924 2924
2925static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence)
2926{
2927 struct cifsFileInfo *wrcfile, *cfile = file->private_data;
2928 struct cifsInodeInfo *cifsi;
2929 struct inode *inode;
2930 int rc = 0;
2931 struct file_allocated_range_buffer in_data, *out_data = NULL;
2932 u32 out_data_len;
2933 unsigned int xid;
2934
2935 if (whence != SEEK_HOLE && whence != SEEK_DATA)
2936 return generic_file_llseek(file, offset, whence);
2937
2938 inode = d_inode(cfile->dentry);
2939 cifsi = CIFS_I(inode);
2940
2941 if (offset < 0 || offset >= i_size_read(inode))
2942 return -ENXIO;
2943
2944 xid = get_xid();
2945 /*
2946 * We need to be sure that all dirty pages are written as they
2947 * might fill holes on the server.
2948 * Note that we also MUST flush any written pages since at least
2949 * some servers (Windows2016) will not reflect recent writes in
2950 * QUERY_ALLOCATED_RANGES until SMB2_flush is called.
2951 */
2952 wrcfile = find_writable_file(cifsi, false);
2953 if (wrcfile) {
2954 filemap_write_and_wait(inode->i_mapping);
2955 smb2_flush_file(xid, tcon, &wrcfile->fid);
2956 cifsFileInfo_put(wrcfile);
2957 }
2958
2959 if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) {
2960 if (whence == SEEK_HOLE)
2961 offset = i_size_read(inode);
2962 goto lseek_exit;
2963 }
2964
2965 in_data.file_offset = cpu_to_le64(offset);
2966 in_data.length = cpu_to_le64(i_size_read(inode));
2967
2968 rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
2969 cfile->fid.volatile_fid,
2970 FSCTL_QUERY_ALLOCATED_RANGES, true,
2971 (char *)&in_data, sizeof(in_data),
2972 sizeof(struct file_allocated_range_buffer),
2973 (char **)&out_data, &out_data_len);
2974 if (rc == -E2BIG)
2975 rc = 0;
2976 if (rc)
2977 goto lseek_exit;
2978
2979 if (whence == SEEK_HOLE && out_data_len == 0)
2980 goto lseek_exit;
2981
2982 if (whence == SEEK_DATA && out_data_len == 0) {
2983 rc = -ENXIO;
2984 goto lseek_exit;
2985 }
2986
2987 if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
2988 rc = -EINVAL;
2989 goto lseek_exit;
2990 }
2991 if (whence == SEEK_DATA) {
2992 offset = le64_to_cpu(out_data->file_offset);
2993 goto lseek_exit;
2994 }
2995 if (offset < le64_to_cpu(out_data->file_offset))
2996 goto lseek_exit;
2997
2998 offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length);
2999
3000 lseek_exit:
3001 free_xid(xid);
3002 kfree(out_data);
3003 if (!rc)
3004 return vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
3005 else
3006 return rc;
3007}
3008
2925static int smb3_fiemap(struct cifs_tcon *tcon, 3009static int smb3_fiemap(struct cifs_tcon *tcon,
2926 struct cifsFileInfo *cfile, 3010 struct cifsFileInfo *cfile,
2927 struct fiemap_extent_info *fei, u64 start, u64 len) 3011 struct fiemap_extent_info *fei, u64 start, u64 len)
@@ -4166,6 +4250,7 @@ struct smb_version_operations smb20_operations = {
4166 .ioctl_query_info = smb2_ioctl_query_info, 4250 .ioctl_query_info = smb2_ioctl_query_info,
4167 .make_node = smb2_make_node, 4251 .make_node = smb2_make_node,
4168 .fiemap = smb3_fiemap, 4252 .fiemap = smb3_fiemap,
4253 .llseek = smb3_llseek,
4169}; 4254};
4170 4255
4171struct smb_version_operations smb21_operations = { 4256struct smb_version_operations smb21_operations = {
@@ -4266,6 +4351,7 @@ struct smb_version_operations smb21_operations = {
4266 .ioctl_query_info = smb2_ioctl_query_info, 4351 .ioctl_query_info = smb2_ioctl_query_info,
4267 .make_node = smb2_make_node, 4352 .make_node = smb2_make_node,
4268 .fiemap = smb3_fiemap, 4353 .fiemap = smb3_fiemap,
4354 .llseek = smb3_llseek,
4269}; 4355};
4270 4356
4271struct smb_version_operations smb30_operations = { 4357struct smb_version_operations smb30_operations = {
@@ -4375,6 +4461,7 @@ struct smb_version_operations smb30_operations = {
4375 .ioctl_query_info = smb2_ioctl_query_info, 4461 .ioctl_query_info = smb2_ioctl_query_info,
4376 .make_node = smb2_make_node, 4462 .make_node = smb2_make_node,
4377 .fiemap = smb3_fiemap, 4463 .fiemap = smb3_fiemap,
4464 .llseek = smb3_llseek,
4378}; 4465};
4379 4466
4380struct smb_version_operations smb311_operations = { 4467struct smb_version_operations smb311_operations = {
@@ -4485,6 +4572,7 @@ struct smb_version_operations smb311_operations = {
4485 .ioctl_query_info = smb2_ioctl_query_info, 4572 .ioctl_query_info = smb2_ioctl_query_info,
4486 .make_node = smb2_make_node, 4573 .make_node = smb2_make_node,
4487 .fiemap = smb3_fiemap, 4574 .fiemap = smb3_fiemap,
4575 .llseek = smb3_llseek,
4488}; 4576};
4489 4577
4490struct smb_version_values smb20_values = { 4578struct smb_version_values smb20_values = {