diff options
| author | Frank Sorenson <sorenson@redhat.com> | 2026-07-21 18:55:51 -0500 |
|---|---|---|
| committer | Steve French <stfrench@microsoft.com> | 2026-07-21 20:19:03 -0500 |
| commit | e8a8d54c2d508891c142a928fc7d298c4c8bd0dd (patch) | |
| tree | ccef485932aea5821a5cd6b5cb4e8804b5c5d784 /include | |
| parent | 2eb74eef4b7eda8df593d22fb48e94ef959ec8a5 (diff) | |
| download | linux-e8a8d54c2d508891c142a928fc7d298c4c8bd0dd.tar.gz linux-e8a8d54c2d508891c142a928fc7d298c4c8bd0dd.zip | |
cifs: prevent readdir from changing file size due to stale directory metadata
Windows Server's directory enumeration metadata lags behind the actual
file size after a write+close or rename. A concurrent readdir() in the
window between close() returning to userspace and stat() being called
overwrites the correct cached i_size with the stale server value, causing
stat() to return the wrong size.
Once _cifsFileInfo_put() removes the last writable handle from
openFileList, is_size_safe_to_change() permits readdir to overwrite
i_size. smb2_close_getattr() then stamps cifs_i->time = jiffies, making
the corrupt cached value appear fresh to the next stat().
The existing check (see Fixes:) only blocked stale size updates while
an active RW lease was held, not after the last writable handle closes.
Add cifsInodeInfo->time_last_write, written via smp_store_release() at
writable close and on setattr/truncate. is_size_safe_to_change() checks
is_inode_writable() first (acquiring open_file_lock), then rejects a
readdir size update if time_last_write falls within acregmax jiffies.
The spinlock release in _cifsFileInfo_put() forms a store-release barrier
that pairs with the spin_lock() (load-acquire) in is_inode_writable(),
ensuring the subsequent smp_load_acquire() on time_last_write observes
any update from a concurrent close(). When a size update is rejected and
the server value differs from the cached one, cifs_i->time is cleared to
force a fresh QUERY_INFO on the next stat().
readdir is also blocked from changing i_size while writable handles are
open or an RW lease is held, even on direct-IO mounts.
For deferred close (closetimeo > 0), time_last_write is refreshed at the
actual server close in smb2_deferred_work_close() and in the
cifs_close_deferred_file*() drain paths invoked by lease/oplock breaks
and tcon teardown, anchoring the protection window to the real close time
rather than the earlier userspace close.
time_last_write == 0 skips the time_before() check to avoid false
positives near boot on 32-bit systems where jiffies starts close to
INITIAL_JIFFIES.
Does not reproduce against Samba or with actimeo=0.
Fixes: e4b61f3b1c67 ("cifs: prevent updating file size from server if we have a read/write lease")
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'include')
0 files changed, 0 insertions, 0 deletions
