Message ID | 755787.1716560616@warthog.procyon.org.uk |
---|---|
State | New |
Headers | show |
Series | cifs: Fix missing set of remote_i_size | expand |
added to cifs-2.6.git for-next pending testing On Fri, May 24, 2024 at 9:23 AM David Howells <dhowells@redhat.com> wrote: > > Occasionally, the generic/001 xfstest will fail indicating corruption in > one of the copy chains when run on cifs against a server that supports > FSCTL_DUPLICATE_EXTENTS_TO_FILE (eg. Samba with a share on btrfs). The > problem is that the remote_i_size value isn't updated by cifs_setsize() > when called by smb2_duplicate_extents(), but i_size *is*. > > This may cause cifs_remap_file_range() to then skip the bit after calling > ->duplicate_extents() that sets sizes. > > Fix this by calling netfs_resize_file() in smb2_duplicate_extents() before > calling cifs_setsize() to set i_size. > > This means we don't then need to call netfs_resize_file() upon return from > ->duplicate_extents(), but we also fix the test to compare against the pre-dup > inode size. > > [Note that this goes back before the addition of remote_i_size with the > netfs_inode struct. It should probably have been setting cifsi->server_eof > previously.] > > Fixes: cfc63fc8126a ("smb3: fix cached file size problems in duplicate extents (reflink)") > Signed-off-by: David Howells <dhowells@redhat.com> > cc: Steve French <sfrench@samba.org> > cc: Paulo Alcantara <pc@manguebit.com> > cc: Shyam Prasad N <nspmangalore@gmail.com> > cc: Rohith Surabattula <rohiths.msft@gmail.com> > cc: Jeff Layton <jlayton@kernel.org> > cc: linux-cifs@vger.kernel.org > cc: netfs@lists.linux.dev > --- > fs/smb/client/cifsfs.c | 6 +++--- > fs/smb/client/smb2ops.c | 1 + > 2 files changed, 4 insertions(+), 3 deletions(-) > > diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c > index 14810ffd15c8..bb86fc0641d8 100644 > --- a/fs/smb/client/cifsfs.c > +++ b/fs/smb/client/cifsfs.c > @@ -1227,7 +1227,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, > struct cifsFileInfo *smb_file_src = src_file->private_data; > struct cifsFileInfo *smb_file_target = dst_file->private_data; > struct cifs_tcon *target_tcon, *src_tcon; > - unsigned long long destend, fstart, fend, new_size; > + unsigned long long destend, fstart, fend, old_size, new_size; > unsigned int xid; > int rc; > > @@ -1294,6 +1294,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, > goto unlock; > if (fend > target_cifsi->netfs.zero_point) > target_cifsi->netfs.zero_point = fend + 1; > + old_size = target_cifsi->netfs.remote_i_size; > > /* Discard all the folios that overlap the destination region. */ > cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend); > @@ -1306,9 +1307,8 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, > if (target_tcon->ses->server->ops->duplicate_extents) { > rc = target_tcon->ses->server->ops->duplicate_extents(xid, > smb_file_src, smb_file_target, off, len, destoff); > - if (rc == 0 && new_size > i_size_read(target_inode)) { > + if (rc == 0 && new_size > old_size) { > truncate_setsize(target_inode, new_size); > - netfs_resize_file(&target_cifsi->netfs, new_size, true); > fscache_resize_cookie(cifs_inode_cookie(target_inode), > new_size); > } > diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c > index b87b70edd0be..4ce6c3121a7e 100644 > --- a/fs/smb/client/smb2ops.c > +++ b/fs/smb/client/smb2ops.c > @@ -2028,6 +2028,7 @@ smb2_duplicate_extents(const unsigned int xid, > * size will be queried on next revalidate, but it is important > * to make sure that file's cached size is updated immediately > */ > + netfs_resize_file(netfs_inode(inode), dest_off + len, true); > cifs_setsize(inode, dest_off + len); > } > rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, > >
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 14810ffd15c8..bb86fc0641d8 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1227,7 +1227,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, struct cifsFileInfo *smb_file_src = src_file->private_data; struct cifsFileInfo *smb_file_target = dst_file->private_data; struct cifs_tcon *target_tcon, *src_tcon; - unsigned long long destend, fstart, fend, new_size; + unsigned long long destend, fstart, fend, old_size, new_size; unsigned int xid; int rc; @@ -1294,6 +1294,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, goto unlock; if (fend > target_cifsi->netfs.zero_point) target_cifsi->netfs.zero_point = fend + 1; + old_size = target_cifsi->netfs.remote_i_size; /* Discard all the folios that overlap the destination region. */ cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend); @@ -1306,9 +1307,8 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, if (target_tcon->ses->server->ops->duplicate_extents) { rc = target_tcon->ses->server->ops->duplicate_extents(xid, smb_file_src, smb_file_target, off, len, destoff); - if (rc == 0 && new_size > i_size_read(target_inode)) { + if (rc == 0 && new_size > old_size) { truncate_setsize(target_inode, new_size); - netfs_resize_file(&target_cifsi->netfs, new_size, true); fscache_resize_cookie(cifs_inode_cookie(target_inode), new_size); } diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index b87b70edd0be..4ce6c3121a7e 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2028,6 +2028,7 @@ smb2_duplicate_extents(const unsigned int xid, * size will be queried on next revalidate, but it is important * to make sure that file's cached size is updated immediately */ + netfs_resize_file(netfs_inode(inode), dest_off + len, true); cifs_setsize(inode, dest_off + len); } rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
Occasionally, the generic/001 xfstest will fail indicating corruption in one of the copy chains when run on cifs against a server that supports FSCTL_DUPLICATE_EXTENTS_TO_FILE (eg. Samba with a share on btrfs). The problem is that the remote_i_size value isn't updated by cifs_setsize() when called by smb2_duplicate_extents(), but i_size *is*. This may cause cifs_remap_file_range() to then skip the bit after calling ->duplicate_extents() that sets sizes. Fix this by calling netfs_resize_file() in smb2_duplicate_extents() before calling cifs_setsize() to set i_size. This means we don't then need to call netfs_resize_file() upon return from ->duplicate_extents(), but we also fix the test to compare against the pre-dup inode size. [Note that this goes back before the addition of remote_i_size with the netfs_inode struct. It should probably have been setting cifsi->server_eof previously.] Fixes: cfc63fc8126a ("smb3: fix cached file size problems in duplicate extents (reflink)") Signed-off-by: David Howells <dhowells@redhat.com> cc: Steve French <sfrench@samba.org> cc: Paulo Alcantara <pc@manguebit.com> cc: Shyam Prasad N <nspmangalore@gmail.com> cc: Rohith Surabattula <rohiths.msft@gmail.com> cc: Jeff Layton <jlayton@kernel.org> cc: linux-cifs@vger.kernel.org cc: netfs@lists.linux.dev --- fs/smb/client/cifsfs.c | 6 +++--- fs/smb/client/smb2ops.c | 1 + 2 files changed, 4 insertions(+), 3 deletions(-)