Skip to content

Commit c919c16

Browse files
dhowellsSteve French
authored andcommitted
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>
1 parent 1c23f9e commit c919c16

File tree

1 file changed

+30
-25
lines changed

1 file changed

+30
-25
lines changed

fs/cifs/smb2ops.c

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3307,53 +3307,56 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
33073307
return pntsd;
33083308
}
33093309

3310+
static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon,
3311+
loff_t offset, loff_t len, unsigned int xid)
3312+
{
3313+
struct cifsFileInfo *cfile = file->private_data;
3314+
struct file_zero_data_information fsctl_buf;
3315+
3316+
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
3317+
3318+
fsctl_buf.FileOffset = cpu_to_le64(offset);
3319+
fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
3320+
3321+
return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
3322+
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
3323+
(char *)&fsctl_buf,
3324+
sizeof(struct file_zero_data_information),
3325+
0, NULL, NULL);
3326+
}
3327+
33103328
static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
33113329
loff_t offset, loff_t len, bool keep_size)
33123330
{
33133331
struct cifs_ses *ses = tcon->ses;
3314-
struct inode *inode;
3315-
struct cifsInodeInfo *cifsi;
3332+
struct inode *inode = file_inode(file);
3333+
struct cifsInodeInfo *cifsi = CIFS_I(inode);
33163334
struct cifsFileInfo *cfile = file->private_data;
3317-
struct file_zero_data_information fsctl_buf;
33183335
long rc;
33193336
unsigned int xid;
33203337
__le64 eof;
33213338

33223339
xid = get_xid();
33233340

3324-
inode = d_inode(cfile->dentry);
3325-
cifsi = CIFS_I(inode);
3326-
33273341
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
33283342
ses->Suid, offset, len);
33293343

3344+
inode_lock(inode);
3345+
filemap_invalidate_lock(inode->i_mapping);
3346+
33303347
/*
33313348
* We zero the range through ioctl, so we need remove the page caches
33323349
* first, otherwise the data may be inconsistent with the server.
33333350
*/
33343351
truncate_pagecache_range(inode, offset, offset + len - 1);
33353352

33363353
/* if file not oplocked can't be sure whether asking to extend size */
3337-
if (!CIFS_CACHE_READ(cifsi))
3338-
if (keep_size == false) {
3339-
rc = -EOPNOTSUPP;
3340-
trace_smb3_zero_err(xid, cfile->fid.persistent_fid,
3341-
tcon->tid, ses->Suid, offset, len, rc);
3342-
free_xid(xid);
3343-
return rc;
3344-
}
3345-
3346-
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
3347-
3348-
fsctl_buf.FileOffset = cpu_to_le64(offset);
3349-
fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
3354+
rc = -EOPNOTSUPP;
3355+
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
3356+
goto zero_range_exit;
33503357

3351-
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
3352-
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
3353-
(char *)&fsctl_buf,
3354-
sizeof(struct file_zero_data_information),
3355-
0, NULL, NULL);
3356-
if (rc)
3358+
rc = smb3_zero_data(file, tcon, offset, len, xid);
3359+
if (rc < 0)
33573360
goto zero_range_exit;
33583361

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

33683371
zero_range_exit:
3372+
filemap_invalidate_unlock(inode->i_mapping);
3373+
inode_unlock(inode);
33693374
free_xid(xid);
33703375
if (rc)
33713376
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,

0 commit comments

Comments
 (0)