Linux CIFS filesystem development
 help / color / mirror / Atom feed
From: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
To: linkinjeon@kernel.org, tom@talpey.com, senozhatsky@chromium.org,
	chenxiaosong@chenxiaosong.com
Cc: linux-cifs@vger.kernel.org, ChenXiaoSong <chenxiaosong@kylinos.cn>
Subject: [PATCH 01/12] smb/server: move change notify handling into notify.c
Date: Sat, 26 Sep 2026 09:05:07 +0000	[thread overview]
Message-ID: <20260926090518.78547-2-chenxiaosong@chenxiaosong.com> (raw)
In-Reply-To: <20260926090518.78547-1-chenxiaosong@chenxiaosong.com>

From: ChenXiaoSong <chenxiaosong@kylinos.cn>

Move the SMB2 CHANGE_NOTIFY implementation out of smb2pdu.c into a
dedicated notify.c file.

Factor out two helper functions ksmbd_notify_validate_req() and
ksmbd_notify_wait().

No functional change.

Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/server/Makefile  |   3 +-
 fs/smb/server/notify.c  | 183 ++++++++++++++++++++++++++++++++++++++++
 fs/smb/server/notify.h  |  24 ++++++
 fs/smb/server/smb2pdu.c | 110 +-----------------------
 4 files changed, 213 insertions(+), 107 deletions(-)
 create mode 100644 fs/smb/server/notify.c
 create mode 100644 fs/smb/server/notify.h

diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index 9bc87695a53c..5daae1727b32 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -10,7 +10,8 @@ ksmbd-y :=	unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
 		mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
 		transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
 		smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
-		ksmbd_spnego_negtokentarg.asn1.o asn1.o compress.o
+		ksmbd_spnego_negtokentarg.asn1.o asn1.o compress.o \
+		notify.o
 
 $(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h
 
diff --git a/fs/smb/server/notify.c b/fs/smb/server/notify.c
new file mode 100644
index 000000000000..7e324af36b47
--- /dev/null
+++ b/fs/smb/server/notify.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *   SMB2 CHANGE_NOTIFY
+ *
+ *   Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ *
+ *   Author(s): ChenXiaoSong <chenxiaosong@kylinos.cn>
+ *              Gael Blivet <gael.blivet@gmail.com>
+ *
+ */
+
+#include "glob.h"
+#include "../common/smb2status.h"
+#include "connection.h"
+#include "ksmbd_work.h"
+#include "notify.h"
+#include "smb_common.h"
+#include "smb2pdu.h"
+#include "vfs_cache.h"
+#include "mgmt/user_session.h"
+
+struct ksmbd_notify_req {
+	wait_queue_head_t wait;
+};
+
+/*
+ * Cancel handler for a pending CHANGE_NOTIFY. Called either by
+ * smb2_cancel() (conn->request_lock held, work->state already set to
+ * KSMBD_WORK_CANCELLED by the caller) or by
+ * set_close_state_blocked_works() (vfs_cache.c, fp->f_lock held,
+ * work->state already set to KSMBD_WORK_CLOSED by the caller) -- both
+ * callers hold a spinlock across this call, so it must not sleep.
+ * wake_up() only wakes the waiter in smb2_notify(); it does not touch
+ * fp->blocked_works itself, matching smb2_remove_blocked_lock()'s same
+ * non-mutating style for the equivalent byte-range-lock wait.
+ */
+static void smb2_notify_cancel(void **argv)
+{
+	struct ksmbd_notify_req *notify_req = argv[0];
+
+	wake_up(&notify_req->wait);
+}
+
+static struct ksmbd_file *
+ksmbd_notify_validate_req(struct ksmbd_work *work,
+			  struct smb2_change_notify_req *req,
+			  struct smb2_change_notify_rsp *rsp)
+{
+	struct ksmbd_file *fp;
+
+	if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
+		pr_err("Notify request is not the last compound command\n");
+		rsp->hdr.Status = STATUS_INTERNAL_ERROR;
+		return ERR_PTR(-EIO);
+	}
+
+	fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId,
+				  req->PersistentFileId);
+	if (!fp) {
+		pr_err("Invalid file id for notify, fid %llu:%llu\n",
+		       le64_to_cpu(req->PersistentFileId),
+		       le64_to_cpu(req->VolatileFileId));
+		rsp->hdr.Status = STATUS_FILE_CLOSED;
+		return ERR_PTR(-ENOENT);
+	}
+
+	return fp;
+}
+
+static int ksmbd_notify_wait(struct ksmbd_work *work,
+			     struct ksmbd_file *fp,
+			     struct ksmbd_notify_req *notify_req)
+{
+	int err;
+
+	/*
+	 * Handle close holds the file-table write lock while it marks the
+	 * handle closed and walks blocked_works.  Hold the matching read lock
+	 * across the state check and registration so close cannot finish its
+	 * walk between the lookup above and this list insertion.
+	 */
+	read_lock(&work->sess->file_table.lock);
+	if (fp->f_state != FP_INITED) {
+		read_unlock(&work->sess->file_table.lock);
+		return -ENOENT;
+	}
+	spin_lock(&fp->f_lock);
+	list_add_tail(&work->fp_entry, &fp->blocked_works);
+	spin_unlock(&fp->f_lock);
+	read_unlock(&work->sess->file_table.lock);
+
+	smb2_send_interim_resp(work, STATUS_PENDING);
+
+	err = wait_event_interruptible(notify_req->wait,
+				       READ_ONCE(work->state) !=
+					       KSMBD_WORK_ACTIVE);
+	if (err && READ_ONCE(work->state) == KSMBD_WORK_ACTIVE) {
+		pr_err("Notify wait interrupted, async id %d: %d\n",
+		       work->async_id, err);
+		/*
+		 * Woken by a signal, not a real cancel/close. There is no
+		 * notification backend yet to report anything else against,
+		 * so treat this the same as a client-side cancel.
+		 */
+		WRITE_ONCE(work->state, KSMBD_WORK_CANCELLED);
+	}
+
+	spin_lock(&fp->f_lock);
+	list_del_init(&work->fp_entry);
+	spin_unlock(&fp->f_lock);
+
+	return err;
+}
+
+/**
+ * ksmbd_handle_notify() - handle an SMB2 change notify request
+ * @work: smb work containing notify command buffer
+ * @req: SMB2 change notify request
+ * @rsp: SMB2 change notify response
+ *
+ * Return: 0 on success, otherwise error
+ */
+int ksmbd_handle_notify(struct ksmbd_work *work,
+			struct smb2_change_notify_req *req,
+			struct smb2_change_notify_rsp *rsp)
+{
+	struct ksmbd_notify_req notify_req = {};
+	struct ksmbd_file *fp = NULL;
+	void **argv = NULL;
+	bool async_work = false;
+	int err = 0;
+
+	fp = ksmbd_notify_validate_req(work, req, rsp);
+	if (IS_ERR(fp)) {
+		err = PTR_ERR(fp);
+		fp = NULL;
+		goto out;
+	}
+
+	argv = kmalloc_obj(*argv, KSMBD_DEFAULT_GFP);
+	if (!argv) {
+		pr_err("Failed to allocate notify cancel arguments\n");
+		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+		err = -ENOMEM;
+		goto out;
+	}
+	init_waitqueue_head(&notify_req.wait);
+	argv[0] = &notify_req;
+
+	err = setup_async_work(work, smb2_notify_cancel, argv);
+	if (err) {
+		pr_err("Failed to set up asynchronous notify work: %d\n", err);
+		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+		goto out;
+	}
+	async_work = true;
+
+	err = ksmbd_notify_wait(work, fp, &notify_req);
+	if (err == -ENOENT) {
+		rsp->hdr.Status = STATUS_NOTIFY_CLEANUP;
+		goto out;
+	}
+
+	if (work->state == KSMBD_WORK_CLOSED) {
+		rsp->hdr.Status = STATUS_NOTIFY_CLEANUP;
+	} else {
+		rsp->hdr.Status = STATUS_CANCELLED;
+	}
+	smb2_send_interim_resp(work, rsp->hdr.Status);
+	work->send_no_response = 1;
+
+out:
+	if (rsp->hdr.Status != STATUS_SUCCESS && !work->send_no_response)
+		smb2_set_err_rsp(work);
+	if (async_work)
+		release_async_work(work);
+	else
+		kfree(argv);
+	if (fp)
+		ksmbd_fd_put(work, fp);
+	return err;
+}
diff --git a/fs/smb/server/notify.h b/fs/smb/server/notify.h
new file mode 100644
index 000000000000..8de46e07b02e
--- /dev/null
+++ b/fs/smb/server/notify.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *   SMB2 CHANGE_NOTIFY
+ *
+ *   Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ *
+ *   Author(s): ChenXiaoSong <chenxiaosong@kylinos.cn>
+ *              Gael Blivet <gael.blivet@gmail.com>
+ *
+ */
+
+#ifndef __SMB_SERVER_NOTIFY_H__
+#define __SMB_SERVER_NOTIFY_H__
+
+struct ksmbd_work;
+struct smb2_change_notify_req;
+struct smb2_change_notify_rsp;
+
+int ksmbd_handle_notify(struct ksmbd_work *work,
+			struct smb2_change_notify_req *req,
+			struct smb2_change_notify_rsp *rsp);
+
+#endif /* __SMB_SERVER_NOTIFY_H__ */
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 7b9080508a3f..1f45a0836fe9 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -33,6 +33,7 @@
 #include "vfs.h"
 #include "vfs_cache.h"
 #include "misc.h"
+#include "notify.h"
 
 #include "server.h"
 #include "smb_common.h"
@@ -11818,28 +11819,6 @@ int smb2_oplock_break(struct ksmbd_work *work)
 	return 0;
 }
 
-struct ksmbd_notify_req {
-	wait_queue_head_t wait;
-};
-
-/*
- * Cancel handler for a pending CHANGE_NOTIFY. Called either by
- * smb2_cancel() (conn->request_lock held, work->state already set to
- * KSMBD_WORK_CANCELLED by the caller) or by
- * set_close_state_blocked_works() (vfs_cache.c, fp->f_lock held,
- * work->state already set to KSMBD_WORK_CLOSED by the caller) -- both
- * callers hold a spinlock across this call, so it must not sleep.
- * wake_up() only wakes the waiter in smb2_notify(); it does not touch
- * fp->blocked_works itself, matching smb2_remove_blocked_lock()'s same
- * non-mutating style for the equivalent byte-range-lock wait.
- */
-static void smb2_notify_cancel(void **argv)
-{
-	struct ksmbd_notify_req *notify_req = argv[0];
-
-	wake_up(&notify_req->wait);
-}
-
 /**
  * smb2_notify() - handler for smb2 notify request
  * @work:   smb work containing notify command buffer
@@ -11850,98 +11829,17 @@ int smb2_notify(struct ksmbd_work *work)
 {
 	struct smb2_change_notify_req *req;
 	struct smb2_change_notify_rsp *rsp;
-	struct ksmbd_notify_req notify_req;
-	struct ksmbd_file *fp = NULL;
-	void **argv = NULL;
-	bool async_work = false;
-	int err = 0;
 
 	ksmbd_debug(SMB, "Received smb2 notify\n");
 
 	WORK_BUFFERS(work, req, rsp);
 
-	if (smb2_compound_has_failed(work, &rsp->hdr))
+	if (smb2_compound_has_failed(work, &rsp->hdr)) {
+		pr_err("Failed compound notify request\n");
 		return -EACCES;
-
-	if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
-		rsp->hdr.Status = STATUS_INTERNAL_ERROR;
-		err = -EIO;
-		goto out;
-	}
-
-	fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId);
-	if (!fp) {
-		rsp->hdr.Status = STATUS_FILE_CLOSED;
-		err = -ENOENT;
-		goto out;
-	}
-
-	argv = kmalloc(sizeof(void *), KSMBD_DEFAULT_GFP);
-	if (!argv) {
-		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
-		err = -ENOMEM;
-		goto out;
-	}
-	init_waitqueue_head(&notify_req.wait);
-	argv[0] = &notify_req;
-
-	err = setup_async_work(work, smb2_notify_cancel, argv);
-	if (err) {
-		rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
-		goto out;
-	}
-	async_work = true;
-
-	/*
-	 * Handle close holds the file-table write lock while it marks the
-	 * handle closed and walks blocked_works.  Hold the matching read lock
-	 * across the state check and registration so close cannot finish its
-	 * walk between the lookup above and this list insertion.
-	 */
-	read_lock(&work->sess->file_table.lock);
-	if (fp->f_state != FP_INITED) {
-		read_unlock(&work->sess->file_table.lock);
-		rsp->hdr.Status = STATUS_NOTIFY_CLEANUP;
-		err = -ENOENT;
-		goto out;
 	}
-	spin_lock(&fp->f_lock);
-	list_add_tail(&work->fp_entry, &fp->blocked_works);
-	spin_unlock(&fp->f_lock);
-	read_unlock(&work->sess->file_table.lock);
 
-	smb2_send_interim_resp(work, STATUS_PENDING);
-
-	err = wait_event_interruptible(notify_req.wait,
-				       READ_ONCE(work->state) != KSMBD_WORK_ACTIVE);
-	if (err && READ_ONCE(work->state) == KSMBD_WORK_ACTIVE) {
-		/*
-		 * Woken by a signal, not a real cancel/close. There is no
-		 * notification backend yet to report anything else against,
-		 * so treat this the same as a client-side cancel.
-		 */
-		WRITE_ONCE(work->state, KSMBD_WORK_CANCELLED);
-	}
-
-	spin_lock(&fp->f_lock);
-	list_del_init(&work->fp_entry);
-	spin_unlock(&fp->f_lock);
-
-	rsp->hdr.Status = work->state == KSMBD_WORK_CLOSED ?
-			  STATUS_NOTIFY_CLEANUP : STATUS_CANCELLED;
-	smb2_send_interim_resp(work, rsp->hdr.Status);
-	work->send_no_response = 1;
-
-out:
-	if (rsp->hdr.Status != STATUS_SUCCESS && !work->send_no_response)
-		smb2_set_err_rsp(work);
-	if (async_work)
-		release_async_work(work);
-	else
-		kfree(argv);
-	if (fp)
-		ksmbd_fd_put(work, fp);
-	return err;
+	return ksmbd_handle_notify(work, req, rsp);
 }
 
 /**
-- 
2.55.0


  reply	other threads:[~2026-09-26  9:06 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-26  9:05 [PATCH 00/12] smb/server: change notify support ChenXiaoSong
2026-09-26  9:05 ` ChenXiaoSong [this message]
2026-09-26  9:05 ` [PATCH 02/12] smb/server: add more validation for change notify requests ChenXiaoSong
2026-09-26  9:05 ` [PATCH 03/12] smb/server: add debug type for change notify ChenXiaoSong
2026-09-26  9:05 ` [PATCH 04/12] smb/server: support non-recursive directory change watches ChenXiaoSong
2026-09-27 10:51   ` Namjae Jeon
2026-09-28  0:38     ` ChenXiaoSong
2026-09-26  9:05 ` [PATCH 05/12] smb/server: support recursive " ChenXiaoSong
2026-09-26  9:05 ` [PATCH 06/12] smb/server: keep notify watches on file handles ChenXiaoSong
2026-09-26  9:05 ` [PATCH 07/12] smb/server: save simple notify events ChenXiaoSong
2026-09-27 10:31   ` Namjae Jeon
2026-09-28  1:27     ` ChenXiaoSong
2026-09-26  9:05 ` [PATCH 08/12] smb/server: save old names for rename " ChenXiaoSong
2026-09-26  9:05 ` [PATCH 09/12] smb/server: match " ChenXiaoSong
2026-09-26  9:05 ` [PATCH 10/12] smb/server: encode " ChenXiaoSong
2026-09-26  9:05 ` [PATCH 11/12] smb/server: send notify events to the client ChenXiaoSong
2026-09-26  9:05 ` [PATCH 12/12] smb/server: break directory leases before sending notify events ChenXiaoSong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260926090518.78547-2-chenxiaosong@chenxiaosong.com \
    --to=chenxiaosong@chenxiaosong.com \
    --cc=chenxiaosong@kylinos.cn \
    --cc=linkinjeon@kernel.org \
    --cc=linux-cifs@vger.kernel.org \
    --cc=senozhatsky@chromium.org \
    --cc=tom@talpey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox