Skip to content

Commit 110fee6

Browse files
pcacjrsmfrench
authored andcommitted
smb: client: fix missing timestamp updates with O_TRUNC
Don't call ->set_file_info() on open handle to prevent the server from stopping [cm]time updates automatically as per MS-FSA 2.1.4.17. Fix this by checking for ATTR_OPEN bit earlier in cifs_setattr() to prevent ->set_file_info() from being called when opening a file with O_TRUNC. Do the truncation in ->open() instead. This also saves two roundtrips when opening a file with O_TRUNC and there are currently no open handles to be reused. Before patch: $ mount.cifs //srv/share /mnt -o ... $ cd /mnt $ exec 3>foo; stat -c 'old: %z %y' foo; sleep 2; echo test >&3; exec 3>&-; sleep 2; stat -c 'new: %z %y' foo old: 2025-10-03 13:26:23.151030500 -0300 2025-10-03 13:26:23.151030500 -0300 new: 2025-10-03 13:26:23.151030500 -0300 2025-10-03 13:26:23.151030500 -0300 After patch: $ mount.cifs //srv/share /mnt -o ... $ cd /mnt $ exec 3>foo; stat -c 'old: %z %y' foo; sleep 2; echo test >&3; exec 3>&-; sleep 2; stat -c 'new: %z %y' foo $ exec 3>foo; stat -c 'old: %z %y' foo; sleep 2; echo test >&3; exec 3>&-; sleep 2; stat -c 'new: %z %y' foo old: 2025-10-03 13:28:13.911933800 -0300 2025-10-03 13:28:13.911933800 -0300 new: 2025-10-03 13:28:26.647492700 -0300 2025-10-03 13:28:26.647492700 -0300 Reported-by: Frank Sorenson <sorenson@redhat.com> Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Reviewed-by: David Howells <dhowells@redhat.com> Cc: linux-cifs@vger.kernel.org Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 0cc380d commit 110fee6

3 files changed

Lines changed: 159 additions & 92 deletions

File tree

fs/smb/client/cifsglob.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,11 @@ struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
15661566
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr,
15671567
bool offload);
15681568
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
1569+
int cifs_file_flush(const unsigned int xid, struct inode *inode,
1570+
struct cifsFileInfo *cfile);
1571+
int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
1572+
const char *full_path, struct cifsFileInfo *open_file,
1573+
loff_t size);
15691574

15701575
#define CIFS_CACHE_READ_FLG 1
15711576
#define CIFS_CACHE_HANDLE_FLG 2

fs/smb/client/file.c

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,66 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
952952
}
953953
}
954954

955+
int cifs_file_flush(const unsigned int xid, struct inode *inode,
956+
struct cifsFileInfo *cfile)
957+
{
958+
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
959+
struct cifs_tcon *tcon;
960+
int rc;
961+
962+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)
963+
return 0;
964+
965+
if (cfile && (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE)) {
966+
tcon = tlink_tcon(cfile->tlink);
967+
return tcon->ses->server->ops->flush(xid, tcon,
968+
&cfile->fid);
969+
}
970+
rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
971+
if (!rc) {
972+
tcon = tlink_tcon(cfile->tlink);
973+
rc = tcon->ses->server->ops->flush(xid, tcon, &cfile->fid);
974+
cifsFileInfo_put(cfile);
975+
} else if (rc == -EBADF) {
976+
rc = 0;
977+
}
978+
return rc;
979+
}
980+
981+
static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
982+
{
983+
struct cifsInodeInfo *cinode = CIFS_I(d_inode(dentry));
984+
struct inode *inode = d_inode(dentry);
985+
struct cifsFileInfo *cfile = NULL;
986+
struct TCP_Server_Info *server;
987+
struct cifs_tcon *tcon;
988+
int rc;
989+
990+
rc = filemap_write_and_wait(inode->i_mapping);
991+
if (is_interrupt_error(rc))
992+
return -ERESTARTSYS;
993+
mapping_set_error(inode->i_mapping, rc);
994+
995+
cfile = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
996+
rc = cifs_file_flush(xid, inode, cfile);
997+
if (!rc) {
998+
if (cfile) {
999+
tcon = tlink_tcon(cfile->tlink);
1000+
server = tcon->ses->server;
1001+
rc = server->ops->set_file_size(xid, tcon,
1002+
cfile, 0, false);
1003+
}
1004+
if (!rc) {
1005+
netfs_resize_file(&cinode->netfs, 0, true);
1006+
cifs_setsize(inode, 0);
1007+
inode->i_blocks = 0;
1008+
}
1009+
}
1010+
if (cfile)
1011+
cifsFileInfo_put(cfile);
1012+
return rc;
1013+
}
1014+
9551015
int cifs_open(struct inode *inode, struct file *file)
9561016

9571017
{
@@ -1004,6 +1064,12 @@ int cifs_open(struct inode *inode, struct file *file)
10041064
file->f_op = &cifs_file_direct_ops;
10051065
}
10061066

1067+
if (file->f_flags & O_TRUNC) {
1068+
rc = cifs_do_truncate(xid, file_dentry(file));
1069+
if (rc)
1070+
goto out;
1071+
}
1072+
10071073
/* Get the cached handle as SMB2 close is deferred */
10081074
if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) {
10091075
rc = cifs_get_writable_path(tcon, full_path,
@@ -2685,13 +2751,10 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
26852751
int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
26862752
int datasync)
26872753
{
2688-
unsigned int xid;
2689-
int rc = 0;
2690-
struct cifs_tcon *tcon;
2691-
struct TCP_Server_Info *server;
26922754
struct cifsFileInfo *smbfile = file->private_data;
26932755
struct inode *inode = file_inode(file);
2694-
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
2756+
unsigned int xid;
2757+
int rc = 0;
26952758

26962759
rc = file_write_and_wait_range(file, start, end);
26972760
if (rc) {
@@ -2712,26 +2775,7 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
27122775
}
27132776
}
27142777

2715-
tcon = tlink_tcon(smbfile->tlink);
2716-
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
2717-
server = tcon->ses->server;
2718-
if (server->ops->flush == NULL) {
2719-
rc = -ENOSYS;
2720-
goto strict_fsync_exit;
2721-
}
2722-
2723-
if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
2724-
smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
2725-
if (smbfile) {
2726-
rc = server->ops->flush(xid, tcon, &smbfile->fid);
2727-
cifsFileInfo_put(smbfile);
2728-
} else
2729-
cifs_dbg(FYI, "ignore fsync for file not open for write\n");
2730-
} else
2731-
rc = server->ops->flush(xid, tcon, &smbfile->fid);
2732-
}
2733-
2734-
strict_fsync_exit:
2778+
rc = cifs_file_flush(xid, inode, smbfile);
27352779
free_xid(xid);
27362780
return rc;
27372781
}

0 commit comments

Comments
 (0)