From: Yunseong Kim <yunseong.kim@est.tech>
To: Namjae Jeon <linkinjeon@kernel.org>,
Sergey Senozhatsky <senozhatsky@chromium.org>,
Tom Talpey <tom@talpey.com>,
ChenXiaoSong <chenxiaosong@chenxiaosong.com>
Cc: linux-cifs@vger.kernel.org, linux-kernel@vger.kernel.org,
Yunseong Kim <yunseong.kim@est.tech>,
ysk@kzalloc.com
Subject: [PATCH 3/4] ksmbd: add fencing for disconnected persistent handles
Date: Mon, 24 Aug 2026 02:56:30 +0200 [thread overview]
Message-ID: <20260824-b4-ca-v1-3-79d6ae0ad2c3@est.tech> (raw)
In-Reply-To: <20260824-b4-ca-v1-0-79d6ae0ad2c3@est.tech>
Per MS-SMB2 3.3.5.9, when a non-reconnect CREATE request arrives for a
file that has a disconnected persistent handle from a different client,
the server SHOULD fail the request with STATUS_FILE_NOT_AVAILABLE.
Add ksmbd_has_disconnected_persistent_handle() which checks the inode's
open list for any persistent handle that is disconnected (fp->conn is
NULL) and belongs to a different client (different ClientGUID). The same
client is allowed through so it can reconnect its own handle via DH2C.
This fencing prevents data corruption by blocking conflicting opens
while the original client's persistent handle is preserved for
reconnection.
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
---
fs/smb/server/smb2pdu.c | 13 +++++++++++++
fs/smb/server/vfs_cache.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
fs/smb/server/vfs_cache.h | 2 ++
3 files changed, 60 insertions(+)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b4bf36fb7183..36efc3f0bf15 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -4427,6 +4427,19 @@ int smb2_open(struct ksmbd_work *work)
if (!rc) {
file_present = true;
+ /*
+ * MS-SMB2 3.3.5.9: If a non-reconnect CREATE arrives for a
+ * file with a disconnected persistent handle from a different
+ * client, fail with STATUS_FILE_NOT_AVAILABLE.
+ */
+ if (!dh_info.reconnected && !dh_info.replay &&
+ ksmbd_has_disconnected_persistent_handle(path.dentry,
+ conn->ClientGUID)) {
+ rsp->hdr.Status = STATUS_FILE_NOT_AVAILABLE;
+ rc = -EAGAIN;
+ goto err_out;
+ }
+
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
struct xattr_dos_attrib da;
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 81626d204249..48762f743789 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -788,6 +788,51 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
return closed;
}
+/**
+ * ksmbd_has_disconnected_persistent_handle() - check if a file has a
+ * disconnected persistent handle that should block new opens (fencing)
+ * @dentry: dentry of the file being opened
+ * @client_guid: ClientGUID of the requesting client (same client may reconnect)
+ *
+ * Per MS-SMB2 3.3.5.9: when a non-reconnect CREATE arrives for a file with
+ * a disconnected persistent handle, the server SHOULD fail with
+ * STATUS_FILE_NOT_AVAILABLE. Exception: same ClientGuid with handle-caching
+ * lease or batch oplock may attempt to break the lease.
+ *
+ * Return: true if a disconnected persistent handle exists from a
+ * different client.
+ */
+bool ksmbd_has_disconnected_persistent_handle(struct dentry *dentry,
+ const char *client_guid)
+{
+ struct ksmbd_inode *ci;
+ struct ksmbd_file *fp;
+ bool found = false;
+
+ ci = ksmbd_inode_lookup_lock(dentry);
+ if (!ci)
+ return false;
+
+ down_read(&ci->m_lock);
+ list_for_each_entry(fp, &ci->m_fp_list, node) {
+ if (!fp->is_persistent)
+ continue;
+ if (fp->conn)
+ continue;
+ if (fp->f_state != FP_INITED)
+ continue;
+ /* Same client can reconnect — don't fence it */
+ if (!memcmp(fp->client_guid, client_guid,
+ SMB2_CLIENT_GUID_SIZE))
+ continue;
+ found = true;
+ break;
+ }
+ up_read(&ci->m_lock);
+ ksmbd_inode_put(ci);
+ return found;
+}
+
static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
{
if (fp->f_state != FP_INITED)
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index 502efb16f05f..c562ed1c9ee8 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -207,6 +207,8 @@ void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
void ksmbd_inode_put(struct ksmbd_inode *ci);
bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry);
+bool ksmbd_has_disconnected_persistent_handle(struct dentry *dentry,
+ const char *client_guid);
struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id);
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
void ksmbd_put_durable_fd(struct ksmbd_file *fp);
--
2.47.3
next prev parent reply other threads:[~2026-08-24 0:56 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 0:56 [PATCH 0/4] ksmbd: implement Continuously Available (CA) share support Yunseong Kim
2026-08-24 0:56 ` [PATCH 1/4] ksmbd: advertise SMB2_GLOBAL_CAP_PERSISTENT_HANDLES Yunseong Kim
2026-08-24 0:56 ` [PATCH 2/4] ksmbd: set SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY in tree connect Yunseong Kim
2026-08-24 0:56 ` Yunseong Kim [this message]
2026-08-24 0:56 ` [PATCH 4/4] ksmbd: enforce write-through on CA share opens Yunseong Kim
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=20260824-b4-ca-v1-3-79d6ae0ad2c3@est.tech \
--to=yunseong.kim@est.tech \
--cc=chenxiaosong@chenxiaosong.com \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=senozhatsky@chromium.org \
--cc=tom@talpey.com \
--cc=ysk@kzalloc.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
all inboxes | Powered by JetHome®