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: David Howells <dhowells@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Родитель
1c23f9e627
Коммит
c919c164fc
|
@ -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;
|
||||
}
|
||||
rc = -EOPNOTSUPP;
|
||||
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
|
||||
goto zero_range_exit;
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче