cifs: add support for fallocate mode 0 for non-sparse files
authorRonnie Sahlberg <lsahlber@redhat.com>
Fri, 17 Jan 2020 01:45:02 +0000 (11:45 +1000)
committerSteve French <stfrench@microsoft.com>
Mon, 27 Jan 2020 01:24:17 +0000 (19:24 -0600)
RHBZ 1336264

When we extend a file we must also force the size to be updated.

This fixes an issue with holetest in xfs-tests which performs the following
sequence :
1, create a new file
2, use fallocate mode==0 to populate the file
3, mmap the file
4, touch each page by reading the mmapped region.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsfs.h
fs/cifs/inode.c
fs/cifs/smb2ops.c

index b59dc74..096a4c1 100644 (file)
@@ -149,6 +149,9 @@ extern ssize_t cifs_file_copychunk_range(unsigned int xid,
                                        size_t len, unsigned int flags);
 
 extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+extern void cifs_setsize(struct inode *inode, loff_t offset);
+extern int cifs_truncate_page(struct address_space *mapping, loff_t from);
+
 #ifdef CONFIG_CIFS_NFSD_EXPORT
 extern const struct export_operations cifs_export_ops;
 #endif /* CONFIG_CIFS_NFSD_EXPORT */
index ca76a92..9b547f7 100644 (file)
@@ -2228,7 +2228,7 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
        return -ENOTSUPP;
 }
 
-static int cifs_truncate_page(struct address_space *mapping, loff_t from)
+int cifs_truncate_page(struct address_space *mapping, loff_t from)
 {
        pgoff_t index = from >> PAGE_SHIFT;
        unsigned offset = from & (PAGE_SIZE - 1);
@@ -2245,7 +2245,7 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from)
        return rc;
 }
 
-static void cifs_setsize(struct inode *inode, loff_t offset)
+void cifs_setsize(struct inode *inode, loff_t offset)
 {
        struct cifsInodeInfo *cifs_i = CIFS_I(inode);
 
index 2e532f0..6787fce 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/uuid.h>
 #include <linux/sort.h>
 #include <crypto/aead.h>
+#include "cifsfs.h"
 #include "cifsglob.h"
 #include "smb2pdu.h"
 #include "smb2proto.h"
@@ -3171,29 +3172,33 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
                        return rc;
                }
 
+       /*
+        * Extending the file
+        */
+       if ((keep_size == false) && i_size_read(inode) < off + len) {
+               if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0)
+                       smb2_set_sparse(xid, tcon, cfile, inode, false);
+
+               eof = cpu_to_le64(off + len);
+               rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+                                 cfile->fid.volatile_fid, cfile->pid, &eof);
+               if (rc == 0) {
+                       cifsi->server_eof = off + len;
+                       cifs_setsize(inode, off + len);
+                       cifs_truncate_page(inode->i_mapping, inode->i_size);
+                       truncate_setsize(inode, off + len);
+               }
+               goto out;
+       }
+
        /*
         * Files are non-sparse by default so falloc may be a no-op
-        * Must check if file sparse. If not sparse, and not extending
-        * then no need to do anything since file already allocated
+        * Must check if file sparse. If not sparse, and since we are not
+        * extending then no need to do anything since file already allocated
         */
        if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
-               if (keep_size == true)
-                       rc = 0;
-               /* check if extending file */
-               else if (i_size_read(inode) >= off + len)
-                       /* not extending file and already not sparse */
-                       rc = 0;
-               /* BB: in future add else clause to extend file */
-               else
-                       rc = -EOPNOTSUPP;
-               if (rc)
-                       trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
-                               tcon->tid, tcon->ses->Suid, off, len, rc);
-               else
-                       trace_smb3_falloc_done(xid, cfile->fid.persistent_fid,
-                               tcon->tid, tcon->ses->Suid, off, len);
-               free_xid(xid);
-               return rc;
+               rc = 0;
+               goto out;
        }
 
        if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
@@ -3207,25 +3212,14 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
                 */
                if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
                        rc = -EOPNOTSUPP;
-                       trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
-                               tcon->tid, tcon->ses->Suid, off, len, rc);
-                       free_xid(xid);
-                       return rc;
-               }
-
-               smb2_set_sparse(xid, tcon, cfile, inode, false);
-               rc = 0;
-       } else {
-               smb2_set_sparse(xid, tcon, cfile, inode, false);
-               rc = 0;
-               if (i_size_read(inode) < off + len) {
-                       eof = cpu_to_le64(off + len);
-                       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
-                                         cfile->fid.volatile_fid, cfile->pid,
-                                         &eof);
+                       goto out;
                }
        }
 
+       smb2_set_sparse(xid, tcon, cfile, inode, false);
+       rc = 0;
+
+out:
        if (rc)
                trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid,
                                tcon->ses->Suid, off, len, rc);