From: Namjae Jeon <linkinjeon@kernel.org>
To: linux-cifs@vger.kernel.org
Cc: smfrench@gmail.com, senozhatsky@chromium.org, tom@talpey.com,
atteh.mailbox@gmail.com, Namjae Jeon <linkinjeon@kernel.org>
Subject: [PATCH 02/29] ksmbd: supersede disconnected delete-on-close durable handle
Date: Sun, 21 Jun 2026 21:48:17 +0900 [thread overview]
Message-ID: <20260621124844.6235-2-linkinjeon@kernel.org> (raw)
In-Reply-To: <20260621124844.6235-1-linkinjeon@kernel.org>
A durable handle opened with FILE_DELETE_ON_CLOSE is preserved across a
disconnect so it can be reclaimed by a durable reconnect.
smb2.durable-open.delete_on_close2 disconnects such a handle and then
reconnects it, expecting the reconnect to succeed.
When the client does not reconnect but instead opens the same name with a
new delete-on-close create, the preserved handle keeps the file present
with delete-on-close set. ksmbd then rejects the new open with
STATUS_ACCESS_DENIED on the file_present + FILE_DELETE_ON_CLOSE +
OPEN_IF/OVERWRITE_IF path. smb2.durable-open.delete_on_close1 expects this
open to create a fresh, empty file instead, i.e. the disconnected handle's
delete-on-close must take effect first.
Add ksmbd_close_disconnected_durable_delete_on_close(), which closes
disconnected (conn == NULL) durable handles that keep a delete-on-close
file present. The final close promotes S_DEL_ON_CLS to S_DEL_PENDING and
unlinks the file, so a re-resolved path is absent and the new open creates
it fresh. Call it from smb2_open() before the delete-on-close conflict
check, only for the conflicting open shapes. A live (connected) handle
still keeps the file and blocks the open as before.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
---
fs/smb/server/smb2pdu.c | 17 ++++++++++++
fs/smb/server/vfs_cache.c | 57 +++++++++++++++++++++++++++++++++++++++
fs/smb/server/vfs_cache.h | 1 +
3 files changed, 75 insertions(+)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 2bc275ed450a..37a20c5740cd 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3272,6 +3272,23 @@ int smb2_open(struct ksmbd_work *work)
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS,
&path, 1);
+
+ /*
+ * A durable handle opened with delete-on-close is preserved across a
+ * disconnect so it can be reclaimed by a durable reconnect. When a new
+ * delete-on-close open for the same name arrives instead, the
+ * disconnected handle must give way: close it so its delete-on-close
+ * removes the file, then re-resolve so this open can create a fresh one.
+ */
+ if (!rc && (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) &&
+ (req->CreateDisposition == FILE_OVERWRITE_IF_LE ||
+ req->CreateDisposition == FILE_OPEN_IF_LE) &&
+ ksmbd_close_disconnected_durable_delete_on_close(path.dentry)) {
+ path_put(&path);
+ rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS,
+ &path, 1);
+ }
+
if (!rc) {
file_present = true;
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 39c56942ae44..96fa3f160d5b 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -517,6 +517,63 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
kmem_cache_free(filp_cache, fp);
}
+/**
+ * ksmbd_close_disconnected_durable_delete_on_close() - drop a delete-on-close
+ * file kept present only by disconnected durable handles
+ * @dentry: dentry of the file being opened
+ *
+ * A durable handle opened with delete-on-close is preserved across a
+ * disconnect so it can be reclaimed by a durable reconnect. When a new
+ * (non-reconnect) open arrives for the same name instead, the disconnected
+ * handle has to give way. Close such handles so their delete-on-close is
+ * applied and the file is removed once the last handle is gone, letting the
+ * new open create a fresh file.
+ *
+ * The caller's inode reference is dropped before closing so that the final
+ * close can promote S_DEL_ON_CLS to S_DEL_PENDING and unlink the file.
+ *
+ * Return: true if a disconnected durable handle was closed.
+ */
+bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
+{
+ struct ksmbd_inode *ci;
+ struct ksmbd_file *fp, *tmp;
+ LIST_HEAD(dispose);
+ bool closed = false;
+
+ ci = ksmbd_inode_lookup_lock(dentry);
+ if (!ci)
+ return false;
+
+ down_write(&ci->m_lock);
+ if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_ON_CLS_STREAM | S_DEL_PENDING)) {
+ list_for_each_entry_safe(fp, tmp, &ci->m_fp_list, node) {
+ if (fp->conn || !fp->is_durable ||
+ fp->f_state != FP_INITED)
+ continue;
+ list_move_tail(&fp->node, &dispose);
+ }
+ }
+ up_write(&ci->m_lock);
+
+ /*
+ * Drop our lookup reference before closing so the last __ksmbd_close_fd()
+ * can drop m_count to zero and unlink the delete-on-close file. The
+ * collected handles still hold references, so ci stays valid until they
+ * are closed below.
+ */
+ ksmbd_inode_put(ci);
+
+ while (!list_empty(&dispose)) {
+ fp = list_first_entry(&dispose, struct ksmbd_file, node);
+ list_del_init(&fp->node);
+ __ksmbd_close_fd(NULL, fp);
+ closed = true;
+ }
+
+ return closed;
+}
+
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 a3a9fda6de91..21c24956c7c2 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -160,6 +160,7 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
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);
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.25.1
next prev parent reply other threads:[~2026-06-21 12:48 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-21 12:48 [PATCH 01/29] ksmbd: handle missing create contexts for lease opens Namjae Jeon
2026-06-21 12:48 ` Namjae Jeon [this message]
2026-06-21 12:48 ` [PATCH 03/29] ksmbd: invalidate durable handles on oplock break Namjae Jeon
2026-06-21 12:48 ` [PATCH 04/29] ksmbd: fix durable reconnect context parsing Namjae Jeon
2026-06-21 12:48 ` [PATCH 05/29] ksmbd: handle durable v2 app instance id Namjae Jeon
2026-06-21 12:48 ` [PATCH 06/29] ksmbd: preserve open change time across rename Namjae Jeon
2026-06-21 12:48 ` [PATCH 07/29] ksmbd: check parent directory sharing conflicts on rename Namjae Jeon
2026-06-21 12:48 ` [PATCH 08/29] ksmbd: deny renaming directory with open children Namjae Jeon
2026-06-21 12:48 ` [PATCH 09/29] ksmbd: propagate failed command status in related compounds Namjae Jeon
2026-06-21 12:48 ` [PATCH 10/29] ksmbd: validate handle for create or get object id Namjae Jeon
2026-06-21 12:48 ` [PATCH 11/29] ksmbd: preserve compound responses for chained errors Namjae Jeon
2026-06-21 12:48 ` [PATCH 12/29] ksmbd: return success for deferred final close Namjae Jeon
2026-06-21 12:48 ` [PATCH 13/29] ksmbd: send pending interim for last compound I/O Namjae Jeon
2026-06-21 12:48 ` [PATCH 14/29] ksmbd: honor stream delete sharing for base file Namjae Jeon
2026-06-21 12:48 ` [PATCH 15/29] ksmbd: reject empty-attribute synchronize-only create Namjae Jeon
2026-06-21 12:48 ` [PATCH 16/29] ksmbd: tighten create file attribute validation Namjae Jeon
2026-06-21 12:48 ` [PATCH 17/29] ksmbd: return requested create allocation size Namjae Jeon
2026-06-22 23:01 ` Nathan Chancellor
2026-06-23 1:28 ` Namjae Jeon
2026-06-21 12:48 ` [PATCH 18/29] ksmbd: apply create security descriptor first Namjae Jeon
2026-06-21 12:48 ` [PATCH 19/29] ksmbd: downgrade oplock after break timeout Namjae Jeon
2026-06-21 12:48 ` [PATCH 20/29] ksmbd: avoid level II oplock break notification on unlink Namjae Jeon
2026-06-21 12:48 ` [PATCH 21/29] ksmbd: return oplock protocol error for level II ack Namjae Jeon
2026-06-21 12:48 ` [PATCH 22/29] ksmbd: normalize ungrantable lease states Namjae Jeon
2026-06-21 12:48 ` [PATCH 23/29] ksmbd: break handle caching for share conflicts Namjae Jeon
2026-06-21 12:48 ` [PATCH 24/29] ksmbd: break conflicting-open leases only as far as needed Namjae Jeon
2026-06-21 12:48 ` [PATCH 25/29] ksmbd: validate :: stream type against directory create Namjae Jeon
2026-06-21 12:48 ` [PATCH 26/29] ksmbd: treat read-control opens as stat opens only for leases Namjae Jeon
2026-06-21 12:48 ` [PATCH 27/29] ksmbd: start file id allocation at 1 Namjae Jeon
2026-06-21 12:48 ` [PATCH 28/29] ksmbd: sleep interruptibly in the durable handle scavenger Namjae Jeon
2026-06-21 12:48 ` [PATCH 29/29] ksmbd: fix UBSAN array-index-out-of-bounds in decode_compress_ctxt() Namjae Jeon
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=20260621124844.6235-2-linkinjeon@kernel.org \
--to=linkinjeon@kernel.org \
--cc=atteh.mailbox@gmail.com \
--cc=linux-cifs@vger.kernel.org \
--cc=senozhatsky@chromium.org \
--cc=smfrench@gmail.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.