CIFS: Fix write after setting a read lock for read oplock files
authorPavel Shilovsky <piastry@etersoft.ru>
Fri, 21 Dec 2012 11:07:52 +0000 (15:07 +0400)
committerSteve French <smfrench@gmail.com>
Wed, 2 Jan 2013 05:04:14 +0000 (23:04 -0600)
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 writing directly to the server and then breaking oplock level from
level2 to None.

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

Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/file.c

index 1b322d041f1ef24bcee43b1206fc3630cf40d4b6..22c37254b64ef3e7624ce0b7ab00d1523dd3df69 100644 (file)
@@ -2505,42 +2505,34 @@ 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;
+       if (cinode->clientCanCacheAll) {
+               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);
        }
-#endif
-
        /*
         * For non-oplocked files in strict cache mode we need to write the data
         * to the server exactly from the pos to pos+len-1 rather than flush all
         * 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);
+       if (written > 0 && cinode->clientCanCacheRead) {
+               /*
+                * Windows 7 server can delay breaking level2 oplock if a write
+                * request comes - break it on the client to prevent reading
+                * an old data.
+                */
+               cifs_invalidate_mapping(inode);
+               cFYI(1, "Set no oplock for inode=%p after a write operation",
+                    inode);
+               cinode->clientCanCacheRead = false;
+       }
+       return written;
 }
 
 static struct cifs_readdata *