Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88cf75aa authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French
Browse files

CIFS: Fix write after setting a read lock for read oplock files



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: default avatarPavel Shilovsky <piastry@etersoft.ru>
Reviewed-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent ca8aa29c
Loading
Loading
Loading
Loading
+20 −28
Original line number Diff line number Diff line
@@ -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);

#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 *