If we have a read oplock and set a read lock in it, we can't write to the
locked area - so, filemap_fdatawrite may fail with a no information for a
userspace application even if we request a write to non-locked area. Fix
this by checking for brlocks, populating the page cache without marking
affected pages dirty and directly writing to the server.

Also remove CONFIG_CIFS_SMB2 ifdefs because it's suitable for both CIFS
and SMB2 protocols.

Signed-off-by: Pavel Shilovsky <[email protected]>
---
 fs/cifs/file.c |   63 +++++++++++++++++++++++++++++---------------------------
 1 file changed, 33 insertions(+), 30 deletions(-)

diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index e7fc39c..5cfed9c 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2109,7 +2109,14 @@ static int cifs_write_end(struct file *file, struct 
address_space *mapping,
        } else {
                rc = copied;
                pos += copied;
-               set_page_dirty(page);
+               /*
+                * If we don't have write oplock and use the strict cache mode,
+                * we can leave pages clean - cifs_strict_writev will send a
+                * data to the server itself.
+                */
+               if (CIFS_I(inode)->clientCanCacheAll ||
+                   !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO))
+                       set_page_dirty(page);
        }
 
        if (rc > 0) {
@@ -2460,8 +2467,8 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct 
iovec *iov,
 }
 
 static ssize_t
-cifs_writev(struct kiocb *iocb, const struct iovec *iov,
-           unsigned long nr_segs, loff_t pos)
+cifs_pagecache_writev(struct kiocb *iocb, const struct iovec *iov,
+                     unsigned long nr_segs, loff_t pos)
 {
        struct file *file = iocb->ki_filp;
        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
@@ -2511,25 +2518,26 @@ cifs_strict_writev(struct kiocb *iocb, const struct 
iovec *iov,
        struct cifsFileInfo *cfile = (struct cifsFileInfo *)
                                                iocb->ki_filp->private_data;
        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+       ssize_t written;
 
-#ifdef CONFIG_CIFS_SMB2
-       /*
-        * If we have an oplock for read and want to write a data to the file
-        * we need to store it in the page cache and then push it to the server
-        * to be sure the next read will get a valid data.
-        */
-       if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead) {
-               ssize_t written;
-               int rc;
-
-               written = generic_file_aio_write(iocb, iov, nr_segs, pos);
-               rc = filemap_fdatawrite(inode->i_mapping);
-               if (rc)
-                       return (ssize_t)rc;
-
-               return written;
+       /* we have a read oplock - need to store a data in the page cache */
+       if (cinode->clientCanCacheRead) {
+               if (cap_unix(tcon->ses) &&
+                   ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0) &&
+                   (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(
+                                               tcon->fsUnixInfo.Capability)))
+                       written = generic_file_aio_write(iocb, iov, nr_segs,
+                                                        pos);
+               else
+                       written = cifs_pagecache_writev(iocb, iov, nr_segs,
+                                                       pos);
+               /*
+                * Errors occured during writing or we have read+write oplock -
+                * no need to flush to the server.
+                */
+               if (written < 0 || cinode->clientCanCacheAll)
+                       return written;
        }
-#endif
 
        /*
         * For non-oplocked files in strict cache mode we need to write the data
@@ -2537,16 +2545,11 @@ cifs_strict_writev(struct kiocb *iocb, const struct 
iovec *iov,
         * affected pages because it may cause a error with mandatory locks on
         * these pages but not on the region from pos to ppos+len-1.
         */
-
-       if (!cinode->clientCanCacheAll)
-               return cifs_user_writev(iocb, iov, nr_segs, pos);
-
-       if (cap_unix(tcon->ses) &&
-           (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
-           ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
-               return generic_file_aio_write(iocb, iov, nr_segs, pos);
-
-       return cifs_writev(iocb, iov, nr_segs, pos);
+       written = cifs_user_writev(iocb, iov, nr_segs, pos);
+       /* need to restore pos if errors occured */
+       if (written < 0)
+               iocb->ki_pos = pos;
+       return written;
 }
 
 static struct cifs_readdata *
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to