[RFC PATCH samba] smbd: break directory leases before sending notify events
Ralph Boehme
slow at samba.org
Mon Aug 17 08:00:53 UTC 2026
Hi!
Thanks for looking into this and trying to address the issue!
Unfortunately, iirc it won't be as simple due to the way Samba
internally integrates inotify with changes coming in over SMB. I have
this sitting on my todo list for the next months, the plan is add a new
change-notify backend based on fanotify, where, in contrast to inotify,
change notifications contain the pid of the triggering process. We need
that information as a differentiator in notifyd to correctly drive the
machinery.
As an example, iirc with just your patch, we would break the dirlease
for changes done by the client owning it and the change correctly
referencing it via the parent dirlease key in the handle triggering the
change.
Thanks!
-slow
On 8/10/26 10:29 AM, ChenXiaoSong wrote:
> 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 840 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20260817/78e7a897/OpenPGP_signature.sig>
More information about the samba-technical
mailing list