diff options
| -rw-r--r-- | fs/cifs/cifsfs.c | 9 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
| -rw-r--r-- | fs/cifs/smb2ops.c | 88 |
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 | ||
| 879 | static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) | 879 | static 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 | ||
| 502 | struct smb_version_values { | 504 | struct 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 | ||
| 2925 | static 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 | |||
| 2925 | static int smb3_fiemap(struct cifs_tcon *tcon, | 3009 | static 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 | ||
| 4171 | struct smb_version_operations smb21_operations = { | 4256 | struct 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 | ||
| 4271 | struct smb_version_operations smb30_operations = { | 4357 | struct 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 | ||
| 4380 | struct smb_version_operations smb311_operations = { | 4467 | struct 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 | ||
| 4490 | struct smb_version_values smb20_values = { | 4578 | struct smb_version_values smb20_values = { |
