Skip to content

Commit

Permalink
smb3: fix temporary data corruption in collapse range
Browse files Browse the repository at this point in the history
collapse range doesn't discard the affected cached region
so can risk temporarily corrupting the file data. This
fixes xfstest generic/031

I also decided to merge a minor cleanup to this into the same patch
(avoiding rereading inode size repeatedly unnecessarily) to make it
clearer.

Cc: stable@vger.kernel.org
Fixes: 5476b5d ("cifs: add support for FALLOC_FL_COLLAPSE_RANGE")
Reported-by: David Howells <dhowells@redhat.com>
Tested-by: David Howells <dhowells@redhat.com>
Reviewed-by: David Howells <dhowells@redhat.com>
cc: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
Steve French committed Aug 29, 2022
1 parent c3a72bb commit fa30a81
Showing 1 changed file with 16 additions and 10 deletions.
26 changes: 16 additions & 10 deletions fs/cifs/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -3669,41 +3669,47 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
{
int rc;
unsigned int xid;
struct inode *inode;
struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data;
struct cifsInodeInfo *cifsi;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
__le64 eof;
loff_t old_eof;

xid = get_xid();

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

if (off >= i_size_read(inode) ||
off + len >= i_size_read(inode)) {
old_eof = i_size_read(inode);
if ((off >= old_eof) ||
off + len >= old_eof) {
rc = -EINVAL;
goto out;
}

filemap_invalidate_lock(inode->i_mapping);
filemap_write_and_wait(inode->i_mapping);
truncate_pagecache_range(inode, off, old_eof);

rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
i_size_read(inode) - off - len, off);
old_eof - off - len, off);
if (rc < 0)
goto out;
goto out_2;

eof = cpu_to_le64(i_size_read(inode) - len);
eof = cpu_to_le64(old_eof - len);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, &eof);
if (rc < 0)
goto out;
goto out_2;

rc = 0;

cifsi->server_eof = i_size_read(inode) - len;
truncate_setsize(inode, cifsi->server_eof);
fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof);
out_2:
filemap_invalidate_unlock(inode->i_mapping);
out:
inode_unlock(inode);
free_xid(xid);
return rc;
}
Expand Down

0 comments on commit fa30a81

Please sign in to comment.