[RFC PATCH samba] smbd: break directory leases before sending notify events
ChenXiaoSong
chenxiaosong at chenxiaosong.com
Mon Aug 10 08:29:55 UTC 2026
From: ChenXiaoSong <chenxiaosong at kylinos.cn>
Reproducer:
1. samba: `smb.conf`:
[global]
smb3 directory leases = yes
2. samba:
systemctl start smb # fedora
3. Windows 11 File Explorer:
Mount the share and enter the top-level directory of the mount point.
4. samba: touch /export/file
5. Windows 11 File Explorer:
`file` does not appear in the top-level directory of the mount point.
Windows can keep directory data in a cache while it has a directory
lease. A file change made on the server does not use the SMB request
path, so the lease is not broken.
The server sends a notify event, but Windows may still use the old data.
Some new files are then not shown in File Explorer.
Signed-off-by: ChenXiaoSong <chenxiaosong at kylinos.cn>
---
source3/smbd/notify.c | 7 ++++++-
source3/smbd/notifyd/fcn_wait.c | 2 +-
source3/smbd/notifyd/notifyd.c | 2 +-
source3/smbd/proto.h | 1 +
source3/smbd/smb2_oplock.c | 37 +++++++++++++++++++++++++++++++++
5 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index 5970a7a9220..24dcedb9dba 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -262,10 +262,15 @@ static struct files_struct *notify_fsp_cb(struct files_struct *fsp,
void *private_data)
{
struct notify_fsp_state *state = private_data;
+ uint32_t action = state->e->action;
if (fsp == state->notified_fsp) {
DBG_DEBUG("notify_callback called for %s\n", fsp_str_dbg(fsp));
- notify_fsp(fsp, state->when, state->e->action, state->e->path);
+ if (action & NOTIFY_ACTION_DIRLEASE_BREAK) {
+ contend_dirleases_by_fsp(fsp);
+ action &= ~NOTIFY_ACTION_DIRLEASE_BREAK;
+ }
+ notify_fsp(fsp, state->when, action, state->e->path);
return fsp;
}
diff --git a/source3/smbd/notifyd/fcn_wait.c b/source3/smbd/notifyd/fcn_wait.c
index e32240d911a..6b5b6e66d43 100644
--- a/source3/smbd/notifyd/fcn_wait.c
+++ b/source3/smbd/notifyd/fcn_wait.c
@@ -256,7 +256,7 @@ NTSTATUS fcn_wait_recv(
*when = evt->msg.when;
}
if (action != NULL) {
- *action = evt->msg.action;
+ *action = evt->msg.action & ~NOTIFY_ACTION_DIRLEASE_BREAK;
}
DLIST_REMOVE(state->events, evt);
diff --git a/source3/smbd/notifyd/notifyd.c b/source3/smbd/notifyd/notifyd.c
index 0b07ab3e435..a42a7a9162e 100644
--- a/source3/smbd/notifyd/notifyd.c
+++ b/source3/smbd/notifyd/notifyd.c
@@ -579,7 +579,7 @@ static void notifyd_sys_callback(struct sys_notify_context *ctx,
msg = (struct notify_trigger_msg) {
.when = timespec_current(),
- .action = ev->action,
+ .action = ev->action | NOTIFY_ACTION_DIRLEASE_BREAK,
.filter = filter,
};
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 7ced3a3ffd5..012039c7a5f 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -730,6 +730,7 @@ void smbd_contend_level2_oplocks_end(files_struct *fsp,
void contend_dirleases(struct connection_struct *conn,
const struct smb_filename *smb_fname,
const struct smb2_lease *lease);
+void contend_dirleases_by_fsp(struct files_struct *fsp);
bool init_oplocks(struct smbd_server_connection *sconn);
void init_kernel_oplocks(struct smbd_server_connection *sconn);
diff --git a/source3/smbd/smb2_oplock.c b/source3/smbd/smb2_oplock.c
index 2489090af55..4de33889eff 100644
--- a/source3/smbd/smb2_oplock.c
+++ b/source3/smbd/smb2_oplock.c
@@ -1398,6 +1398,43 @@ void contend_dirleases(struct connection_struct *conn,
TALLOC_FREE(lck);
}
+void contend_dirleases_by_fsp(struct files_struct *fsp)
+{
+ struct dirlease_break_state state = {
+ .sconn = fsp->conn->sconn,
+ .file_id = fsp->file_id,
+ };
+ struct share_mode_lock *lck = NULL;
+ uint32_t access_mask, share_mode;
+ bool ok;
+
+ if (!lp_smb3_directory_leases() || !fsp->fsp_flags.is_directory) {
+ return;
+ }
+
+ lck = get_existing_share_mode_lock(talloc_tos(), state.file_id);
+ if (lck == NULL) {
+ return;
+ }
+
+ ok = share_mode_forall_leases(lck, do_dirlease_break_to_none, &state);
+ if (!ok) {
+ DBG_WARNING("share_mode_forall_leases failed\n");
+ }
+
+ share_mode_flags_get(lck,
+ &access_mask,
+ &share_mode,
+ NULL);
+ share_mode_flags_set(lck,
+ access_mask,
+ share_mode,
+ state.total_lease_types,
+ NULL);
+
+ TALLOC_FREE(lck);
+}
+
/****************************************************************************
This function is called on any file modification or lock request. If a file
is level 2 oplocked then it must tell all other level 2 holders to break to
--
2.54.0
More information about the samba-technical
mailing list