Commit c919c164 authored by David Howells's avatar David Howells Committed by Steve French
Browse files

smb3: missing inode locks in zero range



smb3 fallocate zero range was not grabbing the inode or filemap_invalidate
locks so could have race with pagemap reinstantiating the page.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 1c23f9e6
Loading
Loading
Loading
Loading
+30 −25
Original line number Diff line number Diff line
@@ -3307,26 +3307,43 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
	return pntsd;
}

static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon,
			     loff_t offset, loff_t len, unsigned int xid)
{
	struct cifsFileInfo *cfile = file->private_data;
	struct file_zero_data_information fsctl_buf;

	cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);

	fsctl_buf.FileOffset = cpu_to_le64(offset);
	fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);

	return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
			  cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
			  (char *)&fsctl_buf,
			  sizeof(struct file_zero_data_information),
			  0, NULL, NULL);
}

static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
			    loff_t offset, loff_t len, bool keep_size)
{
	struct cifs_ses *ses = tcon->ses;
	struct inode *inode;
	struct cifsInodeInfo *cifsi;
	struct inode *inode = file_inode(file);
	struct cifsInodeInfo *cifsi = CIFS_I(inode);
	struct cifsFileInfo *cfile = file->private_data;
	struct file_zero_data_information fsctl_buf;
	long rc;
	unsigned int xid;
	__le64 eof;

	xid = get_xid();

	inode = d_inode(cfile->dentry);
	cifsi = CIFS_I(inode);

	trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
			      ses->Suid, offset, len);

	inode_lock(inode);
	filemap_invalidate_lock(inode->i_mapping);

	/*
	 * We zero the range through ioctl, so we need remove the page caches
	 * first, otherwise the data may be inconsistent with the server.
@@ -3334,26 +3351,12 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
	truncate_pagecache_range(inode, offset, offset + len - 1);

	/* if file not oplocked can't be sure whether asking to extend size */
	if (!CIFS_CACHE_READ(cifsi))
		if (keep_size == false) {
	rc = -EOPNOTSUPP;
			trace_smb3_zero_err(xid, cfile->fid.persistent_fid,
				tcon->tid, ses->Suid, offset, len, rc);
			free_xid(xid);
			return rc;
		}

	cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);

	fsctl_buf.FileOffset = cpu_to_le64(offset);
	fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
	if (keep_size == false && !CIFS_CACHE_READ(cifsi))
		goto zero_range_exit;

	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
			cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
			(char *)&fsctl_buf,
			sizeof(struct file_zero_data_information),
			0, NULL, NULL);
	if (rc)
	rc = smb3_zero_data(file, tcon, offset, len, xid);
	if (rc < 0)
		goto zero_range_exit;

	/*
@@ -3366,6 +3369,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
	}

 zero_range_exit:
	filemap_invalidate_unlock(inode->i_mapping);
	inode_unlock(inode);
	free_xid(xid);
	if (rc)
		trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,