* [PATCH 1/4] ksmbd: advertise SMB2_GLOBAL_CAP_PERSISTENT_HANDLES
2026-08-24 0:56 [PATCH 0/4] ksmbd: implement Continuously Available (CA) share support Yunseong Kim
@ 2026-08-24 0:56 ` Yunseong Kim
2026-08-24 0:56 ` [PATCH 2/4] ksmbd: set SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY in tree connect Yunseong Kim
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Yunseong Kim @ 2026-08-24 0:56 UTC (permalink / raw)
To: Namjae Jeon, Sergey Senozhatsky, Tom Talpey, ChenXiaoSong
Cc: linux-cifs, linux-kernel, Yunseong Kim, ysk
Advertise SMB2_GLOBAL_CAP_PERSISTENT_HANDLES in the SMB2 negotiate
response when durable handles are enabled globally. This tells clients
that the server supports persistent handles, which is a prerequisite
for Continuously Available (CA) share functionality.
The capability is gated by KSMBD_GLOBAL_FLAG_DURABLE_HANDLE (controlled
by 'durable handles = yes' in ksmbd.conf) so administrators must
explicitly enable it.
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
---
fs/smb/server/smb2ops.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
index 4578291fb172..414153fa2a68 100644
--- a/fs/smb/server/smb2ops.c
+++ b/fs/smb/server/smb2ops.c
@@ -270,10 +270,8 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
- /*
- * Durable handles are in-memory only. Do not advertise persistent
- * handles until CA recovery and fencing are implemented.
- */
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
}
/**
@@ -296,7 +294,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
- /* See init_smb3_02_server(): persistent handles require CA recovery. */
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+
return 0;
}
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/4] ksmbd: set SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY in tree connect
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 ` Yunseong Kim
2026-08-24 0:56 ` [PATCH 3/4] ksmbd: add fencing for disconnected persistent handles Yunseong Kim
2026-08-24 0:56 ` [PATCH 4/4] ksmbd: enforce write-through on CA share opens Yunseong Kim
3 siblings, 0 replies; 5+ messages in thread
From: Yunseong Kim @ 2026-08-24 0:56 UTC (permalink / raw)
To: Namjae Jeon, Sergey Senozhatsky, Tom Talpey, ChenXiaoSong
Cc: linux-cifs, linux-kernel, Yunseong Kim, ysk
Set the SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY capability in the tree
connect response for shares configured with the 'continuous availability'
option (KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY).
This capability is only advertised when persistent handles are also
enabled globally (SMB2_GLOBAL_CAP_PERSISTENT_HANDLES). It informs
clients that the share supports persistent handles, allowing them to
request SMB2_DHANDLE_FLAG_PERSISTENT in DH2Q create contexts.
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
---
fs/smb/server/smb2pdu.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index bd74b45ce0e7..b4bf36fb7183 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2858,10 +2858,15 @@ int smb2_tree_connect(struct ksmbd_work *work)
rsp->StructureSize = cpu_to_le16(16);
out_err1:
/*
- * A configured CA share is not continuously available until persistent
- * open recovery, ownership fencing, and failover are implemented.
+ * Advertise Continuous Availability on shares configured with
+ * the CA flag, when persistent handles are enabled globally.
*/
- rsp->Capabilities = 0;
+ if (share && test_share_config_flag(share,
+ KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY) &&
+ conn->vals->req_capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
+ rsp->Capabilities = cpu_to_le32(SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY);
+ else
+ rsp->Capabilities = 0;
rsp->Reserved = 0;
/* default manual caching */
rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING;
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/4] ksmbd: add fencing for disconnected persistent handles
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
2026-08-24 0:56 ` [PATCH 4/4] ksmbd: enforce write-through on CA share opens Yunseong Kim
3 siblings, 0 replies; 5+ messages in thread
From: Yunseong Kim @ 2026-08-24 0:56 UTC (permalink / raw)
To: Namjae Jeon, Sergey Senozhatsky, Tom Talpey, ChenXiaoSong
Cc: linux-cifs, linux-kernel, Yunseong Kim, ysk
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
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 4/4] ksmbd: enforce write-through on CA share opens
2026-08-24 0:56 [PATCH 0/4] ksmbd: implement Continuously Available (CA) share support Yunseong Kim
` (2 preceding siblings ...)
2026-08-24 0:56 ` [PATCH 3/4] ksmbd: add fencing for disconnected persistent handles Yunseong Kim
@ 2026-08-24 0:56 ` Yunseong Kim
3 siblings, 0 replies; 5+ messages in thread
From: Yunseong Kim @ 2026-08-24 0:56 UTC (permalink / raw)
To: Namjae Jeon, Sergey Senozhatsky, Tom Talpey, ChenXiaoSong
Cc: linux-cifs, linux-kernel, Yunseong Kim, ysk
Per MS-SMB2 3.3.5.9, opens on Continuously Available shares MUST have
FILE_WRITE_THROUGH semantics to ensure data is committed to stable
storage before the operation completes. This is required because
persistent handles may need to be recovered after a server failure, and
any data not flushed to disk would be lost.
Force FILE_WRITE_THROUGH_LE in the CreateOptions passed to
ksmbd_vfs_set_fadvise() for files opened on CA shares, which sets
O_SYNC on the underlying file descriptor.
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
---
fs/smb/server/smb2pdu.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 36efc3f0bf15..beb5120013ed 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -4681,7 +4681,14 @@ int smb2_open(struct ksmbd_work *work)
file_info = FILE_CREATED;
}
- ksmbd_vfs_set_fadvise(filp, req->CreateOptions);
+ /*
+ * MS-SMB2 3.3.5.9: Opens on CA shares MUST have FILE_WRITE_THROUGH
+ * semantics to ensure data is committed to stable storage.
+ */
+ if (test_share_config_flag(share, KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY))
+ ksmbd_vfs_set_fadvise(filp, req->CreateOptions | FILE_WRITE_THROUGH_LE);
+ else
+ ksmbd_vfs_set_fadvise(filp, req->CreateOptions);
/* Obtain Volatile-ID */
fp = ksmbd_open_fd(work, filp);
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread