]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
cifs: add support for SEEK_DATA and SEEK_HOLE
authorRonnie Sahlberg <lsahlber@redhat.com>
Tue, 14 May 2019 21:17:02 +0000 (07:17 +1000)
committerSteve French <stfrench@microsoft.com>
Thu, 16 May 2019 03:27:53 +0000 (22:27 -0500)
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>
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/smb2ops.c

index d0cb042732cb0f5e7f322d1121a3bb568b510220..f5fcd6360056500772b1249cf462c834991df268 100644 (file)
@@ -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);
 }
 
index 33c251b408aa6bdb84a7d472364ffaad0cfe9365..334ff5f9c3f3374197e5b05f75429ca3ce0b03bc 100644 (file)
@@ -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 {
index 542b50c0b29265423eb59db46ec46755b9a6f35e..e921e65117285996653e25e010d929e98489dd90 100644 (file)
@@ -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 = {