All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs
@ 2026-07-17  3:39 Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 1/9] smb: client: support security.capability " Ze Tan
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

The CIFS client and ksmbd currently expose SMB extended attributes
mainly through the Linux user.* namespace. This causes security and
trusted xattr names carried through SMB EAs to be stored with an
additional user. prefix on the server.

For example, security.xfstests can be stored as
user.security.xfstests instead of security.xfstests. This also prevents
security.capability from being handled as a file capability.

generic/093 exercises these namespaces through several commands:

  - setcap and getcap use security.capability;
  - _require_attrs security uses security.xfstests;
  - setfattr and getfattr use trusted.name.

This RFC adds end-to-end handling for the xattrs required by those
operations. The client passes security.capability,
security.xfstests and trusted.* names through SMB EAs without adding
the user. prefix. When SMB3 POSIX extensions are available, ksmbd maps
the same names to their native backing filesystem xattr namespaces for
set, get and list operations.

The security namespace is deliberately restricted to
security.capability and security.xfstests. Other security.* xattrs are
not exposed by this series. trusted.* names are supported while
internal POSIX ACL xattrs remain filtered.

The series also avoids an oplock self-deadlock found while running
generic/093. An EA or metadata-only reopen from the same SMB session
can otherwise break an oplock held by that session and wait for the
same client. Oplock breaks from other sessions and opens with share
conflicts continue to use the normal break path.

Patches 1-3 add the CIFS client support. Patches 4-5 refactor the ksmbd
EA name conversion paths without changing behavior. Patches 6-8 add
the corresponding ksmbd mappings. Patch 9 handles the same-session
oplock case.

The series touches both the CIFS client and ksmbd. It is sent as one
RFC so that the end-to-end name mapping and protocol behavior can be
reviewed together. It can be split into client and server patchsets
for merging after the approach is agreed.

Testing:

generic/093 was run with the CIFS test mount mapped to the fsgqa uid
and gid. Both xfstests mount option variables were configured as
follows:

  export CIFS_OPTS="-o username=fsgqa,password=<password>"
  export CIFS_OPTS="${CIFS_OPTS},uid=1001,gid=1001"
  export TEST_FS_MOUNT_OPTS="${CIFS_OPTS}"
  export MOUNT_OPTIONS="${CIFS_OPTS}"
  cd ~/xfstests/
  ./check -d generic/093

generic/093 passed.

Changelog
---------------

rfc v2: Rebase the patch series onto the for-next branch and resubmit it to avoid "Failed to apply" errors.

rfc: https://lore.kernel.org/all/20260715074908.641940-1-tanze@kylinos.cn/

Ze Tan (9):
  smb: client: support security.capability over EAs
  smb: client: support security.xfstests over EAs
  smb: client: support trusted xattrs over EAs
  ksmbd: extract SMB EA backing xattr name mapping
  ksmbd: extract SMB EA response name handling
  ksmbd: support security.capability EAs
  ksmbd: support security.xfstests EAs
  ksmbd: support trusted EAs
  ksmbd: avoid self oplock breaks for EA opens

 fs/smb/client/smb2ops.c |  34 ++++++++-
 fs/smb/client/xattr.c   | 109 ++++++++++++++++++++++++--
 fs/smb/server/oplock.c  |  49 ++++++++++++
 fs/smb/server/smb2pdu.c | 165 +++++++++++++++++++++++++++++++---------
 4 files changed, 311 insertions(+), 46 deletions(-)


base-commit: fce2dfa773ced15f27dd27cd0b482a7473cdcf2a
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 1/9] smb: client: support security.capability over EAs
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 2/9] smb: client: support security.xfstests " Ze Tan
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

generic/093 uses setcap and getcap to check that a write clears the file
capability. The CIFS client does not have a security xattr handler, so
these commands cannot set or read security.capability through SMB EAs.

Register a security xattr handler and build the complete EA name for
SET_INFO and QUERY_INFO. Keep security.capability unchanged in EA lists.
Reject other security xattrs.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/client/smb2ops.c | 26 ++++++++++++---
 fs/smb/client/xattr.c   | 70 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 87 insertions(+), 9 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index cbd51a08e97e..320742ec3ed8 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -14,6 +14,7 @@
 #include <crypto/aead.h>
 #include <linux/fiemap.h>
 #include <linux/folio_queue.h>
+#include <linux/xattr.h>
 #include <uapi/linux/magic.h>
 #include "cifsfs.h"
 #include "cifsglob.h"
@@ -1043,6 +1044,15 @@ static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 #ifdef CONFIG_CIFS_XATTR
+static bool cifs_passthrough(const char *name, size_t name_len)
+{
+	if (name_len == sizeof(XATTR_NAME_CAPS) - 1 &&
+	    !memcmp(name, XATTR_NAME_CAPS, name_len))
+		return true;
+
+	return false;
+}
+
 static ssize_t
 move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 		     struct smb2_file_full_ea_info *src, size_t src_size,
@@ -1085,16 +1095,24 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 				goto out;
 			}
 		} else {
-			/* 'user.' plus a terminating null */
-			user_name_len = 5 + 1 + name_len;
+			bool passthrough_name;
+
+			passthrough_name = cifs_passthrough(name, name_len);
+			if (passthrough_name)
+				user_name_len = name_len + 1;
+			else
+				/* 'user.' plus a terminating null */
+				user_name_len = 5 + 1 + name_len;
 
 			if (buf_size == 0) {
 				/* skip copy - calc size only */
 				rc += user_name_len;
 			} else if (dst_size >= user_name_len) {
 				dst_size -= user_name_len;
-				memcpy(dst, "user.", 5);
-				dst += 5;
+				if (!passthrough_name) {
+					memcpy(dst, "user.", 5);
+					dst += 5;
+				}
 				memcpy(dst, src->ea_data, name_len);
 				dst += name_len;
 				*dst = 0;
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 5091f6c0d7fe..8fa40593bc93 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -36,12 +36,38 @@
 #define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */
 #define SMB3_XATTR_ATTRIB "smb3.dosattrib"  /* full name: user.smb3.dosattrib */
 #define SMB3_XATTR_CREATETIME "smb3.creationtime"  /* user.smb3.creationtime */
-/* BB need to add server (Samba e.g) support for security and trusted prefix */
-
-enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
+enum { XATTR_USER, XATTR_SECURITY,
+	XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
 	XATTR_CIFS_NTSD_SACL, XATTR_CIFS_NTSD_OWNER,
 	XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL };
 
+static int cifs_build_ea_name(int xattr_flag, const char *name, char *ea_name,
+			      size_t ea_name_size)
+{
+	size_t name_len;
+
+	switch (xattr_flag) {
+	case XATTR_SECURITY:
+		/*
+		 * Only security.capability has native Linux security xattr
+		 * semantics on POSIX-capable servers.
+		 */
+		if (strcmp(name, XATTR_CAPS_SUFFIX))
+			return -EOPNOTSUPP;
+		name_len = strlen(name);
+		if (ea_name_size <= XATTR_SECURITY_PREFIX_LEN ||
+		    name_len > ea_name_size - XATTR_SECURITY_PREFIX_LEN - 1)
+			return -ERANGE;
+		memcpy(ea_name, XATTR_SECURITY_PREFIX,
+		       XATTR_SECURITY_PREFIX_LEN);
+		memcpy(ea_name + XATTR_SECURITY_PREFIX_LEN, name,
+		       name_len + 1);
+		return XATTR_SECURITY_PREFIX_LEN + name_len;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon,
 			   struct inode *inode, const char *full_path,
 			   const void *value, size_t size)
@@ -104,6 +130,8 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 	struct cifs_tcon *pTcon;
 	const char *full_path;
 	void *page;
+	char ea_name[XATTR_NAME_MAX + 1];
+	const char *server_ea_name = name;
 
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
@@ -131,6 +159,16 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 	}
 
 	switch (handler->flags) {
+	case XATTR_SECURITY:
+		rc = cifs_build_ea_name(handler->flags, name, ea_name,
+					sizeof(ea_name));
+		if (rc < 0)
+			goto out;
+		server_ea_name = ea_name;
+		cifs_dbg(FYI, "%s: setting security xattr %s\n",
+			 __func__, name);
+		goto set_ea;
+
 	case XATTR_USER:
 		cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name);
 		if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) ||
@@ -149,12 +187,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 			break;
 		}
 
+set_ea:
 		if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NO_XATTR)
 			goto out;
 
 		if (pTcon->ses->server->ops->set_EA) {
 			rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
-				full_path, name, value, (__u16)size,
+				full_path, server_ea_name, value, (__u16)size,
 				cifs_sb->local_nls, cifs_sb);
 			if (rc == 0)
 				inode_set_ctime_current(inode);
@@ -280,6 +319,8 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 	struct cifs_tcon *pTcon;
 	const char *full_path;
 	void *page;
+	char ea_name[XATTR_NAME_MAX + 1];
+	const char *server_ea_name = name;
 
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
@@ -297,6 +338,16 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 
 	/* return alt name if available as pseudo attr */
 	switch (handler->flags) {
+	case XATTR_SECURITY:
+		rc = cifs_build_ea_name(handler->flags, name, ea_name,
+					sizeof(ea_name));
+		if (rc < 0)
+			goto out;
+		server_ea_name = ea_name;
+		cifs_dbg(FYI, "%s: querying security xattr %s\n",
+			 __func__, name);
+		goto query_ea;
+
 	case XATTR_USER:
 		cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
 		if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) ||
@@ -309,12 +360,13 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 			break;
 		}
 
+query_ea:
 		if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NO_XATTR)
 			goto out;
 
 		if (pTcon->ses->server->ops->query_all_EAs)
 			rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
-				full_path, name, value, size, cifs_sb);
+				full_path, server_ea_name, value, size, cifs_sb);
 		break;
 
 	case XATTR_CIFS_ACL:
@@ -438,6 +490,13 @@ static const struct xattr_handler cifs_user_xattr_handler = {
 	.set = cifs_xattr_set,
 };
 
+static const struct xattr_handler cifs_security_xattr_handler = {
+	.prefix = XATTR_SECURITY_PREFIX,
+	.flags = XATTR_SECURITY,
+	.get = cifs_xattr_get,
+	.set = cifs_xattr_set,
+};
+
 /* os2.* attributes are treated like user.* attributes */
 static const struct xattr_handler cifs_os2_xattr_handler = {
 	.prefix = XATTR_OS2_PREFIX,
@@ -522,6 +581,7 @@ static const struct xattr_handler smb3_ntsd_full_xattr_handler = {
 
 const struct xattr_handler * const cifs_xattr_handlers[] = {
 	&cifs_user_xattr_handler,
+	&cifs_security_xattr_handler,
 	&cifs_os2_xattr_handler,
 	&cifs_cifs_acl_xattr_handler,
 	&smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 2/9] smb: client: support security.xfstests over EAs
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 1/9] smb: client: support security.capability " Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 3/9] smb: client: support trusted xattrs " Ze Tan
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

Before checking file capabilities, generic/093 calls _require_attrs
security. This helper runs "setfattr -n security.xfstests" to check the
security xattr namespace. The security handler supports only
security.capability, so this command returns "Operation not supported".

Add security.xfstests as another exact security EA name. Keep this name
unchanged in EA lists. Continue to reject other security xattrs.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/client/smb2ops.c | 4 ++++
 fs/smb/client/xattr.c   | 7 ++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 320742ec3ed8..9638fdad93cc 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1050,6 +1050,10 @@ static bool cifs_passthrough(const char *name, size_t name_len)
 	    !memcmp(name, XATTR_NAME_CAPS, name_len))
 		return true;
 
+	if (name_len == sizeof(XATTR_SECURITY_PREFIX "xfstests") - 1 &&
+	    !memcmp(name, XATTR_SECURITY_PREFIX "xfstests", name_len))
+		return true;
+
 	return false;
 }
 
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 8fa40593bc93..42db6920901e 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -49,10 +49,11 @@ static int cifs_build_ea_name(int xattr_flag, const char *name, char *ea_name,
 	switch (xattr_flag) {
 	case XATTR_SECURITY:
 		/*
-		 * Only security.capability has native Linux security xattr
-		 * semantics on POSIX-capable servers.
+		 * Only security.capability and the xfstests security namespace
+		 * probe are supported on POSIX-capable servers.
 		 */
-		if (strcmp(name, XATTR_CAPS_SUFFIX))
+		if (strcmp(name, XATTR_CAPS_SUFFIX) &&
+		    strcmp(name, "xfstests"))
 			return -EOPNOTSUPP;
 		name_len = strlen(name);
 		if (ea_name_size <= XATTR_SECURITY_PREFIX_LEN ||
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 3/9] smb: client: support trusted xattrs over EAs
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 1/9] smb: client: support security.capability " Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 2/9] smb: client: support security.xfstests " Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 4/9] ksmbd: extract SMB EA backing xattr name mapping Ze Tan
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

generic/093 uses setfattr and getfattr to check that a write does not
clear trusted.name. The CIFS client does not have a trusted xattr handler,
so these commands cannot set or read trusted xattrs through SMB EAs.

Register a trusted xattr handler and build the complete trusted EA name
for SET_INFO and QUERY_INFO. Keep the trusted prefix in EA lists.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/client/smb2ops.c |  4 ++++
 fs/smb/client/xattr.c   | 40 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 9638fdad93cc..c3a18fafbfa0 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1046,6 +1046,10 @@ static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
 #ifdef CONFIG_CIFS_XATTR
 static bool cifs_passthrough(const char *name, size_t name_len)
 {
+	if (name_len > XATTR_TRUSTED_PREFIX_LEN &&
+	    !memcmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
+		return true;
+
 	if (name_len == sizeof(XATTR_NAME_CAPS) - 1 &&
 	    !memcmp(name, XATTR_NAME_CAPS, name_len))
 		return true;
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 42db6920901e..3dae8b23aca7 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -36,7 +36,7 @@
 #define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */
 #define SMB3_XATTR_ATTRIB "smb3.dosattrib"  /* full name: user.smb3.dosattrib */
 #define SMB3_XATTR_CREATETIME "smb3.creationtime"  /* user.smb3.creationtime */
-enum { XATTR_USER, XATTR_SECURITY,
+enum { XATTR_USER, XATTR_TRUSTED, XATTR_SECURITY,
 	XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
 	XATTR_CIFS_NTSD_SACL, XATTR_CIFS_NTSD_OWNER,
 	XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL };
@@ -64,6 +64,16 @@ static int cifs_build_ea_name(int xattr_flag, const char *name, char *ea_name,
 		memcpy(ea_name + XATTR_SECURITY_PREFIX_LEN, name,
 		       name_len + 1);
 		return XATTR_SECURITY_PREFIX_LEN + name_len;
+	case XATTR_TRUSTED:
+		name_len = strlen(name);
+		if (ea_name_size <= XATTR_TRUSTED_PREFIX_LEN ||
+		    name_len > ea_name_size - XATTR_TRUSTED_PREFIX_LEN - 1)
+			return -ERANGE;
+		memcpy(ea_name, XATTR_TRUSTED_PREFIX,
+		       XATTR_TRUSTED_PREFIX_LEN);
+		memcpy(ea_name + XATTR_TRUSTED_PREFIX_LEN, name,
+		       name_len + 1);
+		return XATTR_TRUSTED_PREFIX_LEN + name_len;
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -160,6 +170,16 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 	}
 
 	switch (handler->flags) {
+	case XATTR_TRUSTED:
+		rc = cifs_build_ea_name(handler->flags, name, ea_name,
+					sizeof(ea_name));
+		if (rc < 0)
+			goto out;
+		server_ea_name = ea_name;
+		cifs_dbg(FYI, "%s: setting trusted xattr %s\n",
+			 __func__, name);
+		goto set_ea;
+
 	case XATTR_SECURITY:
 		rc = cifs_build_ea_name(handler->flags, name, ea_name,
 					sizeof(ea_name));
@@ -339,6 +359,16 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 
 	/* return alt name if available as pseudo attr */
 	switch (handler->flags) {
+	case XATTR_TRUSTED:
+		rc = cifs_build_ea_name(handler->flags, name, ea_name,
+					sizeof(ea_name));
+		if (rc < 0)
+			goto out;
+		server_ea_name = ea_name;
+		cifs_dbg(FYI, "%s: querying trusted xattr %s\n",
+			 __func__, name);
+		goto query_ea;
+
 	case XATTR_SECURITY:
 		rc = cifs_build_ea_name(handler->flags, name, ea_name,
 					sizeof(ea_name));
@@ -491,6 +521,13 @@ static const struct xattr_handler cifs_user_xattr_handler = {
 	.set = cifs_xattr_set,
 };
 
+static const struct xattr_handler cifs_trusted_xattr_handler = {
+	.prefix = XATTR_TRUSTED_PREFIX,
+	.flags = XATTR_TRUSTED,
+	.get = cifs_xattr_get,
+	.set = cifs_xattr_set,
+};
+
 static const struct xattr_handler cifs_security_xattr_handler = {
 	.prefix = XATTR_SECURITY_PREFIX,
 	.flags = XATTR_SECURITY,
@@ -582,6 +619,7 @@ static const struct xattr_handler smb3_ntsd_full_xattr_handler = {
 
 const struct xattr_handler * const cifs_xattr_handlers[] = {
 	&cifs_user_xattr_handler,
+	&cifs_trusted_xattr_handler,
 	&cifs_security_xattr_handler,
 	&cifs_os2_xattr_handler,
 	&cifs_cifs_acl_xattr_handler,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 4/9] ksmbd: extract SMB EA backing xattr name mapping
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
                   ` (2 preceding siblings ...)
  2026-07-17  3:39 ` [RFC PATCH v2 3/9] smb: client: support trusted xattrs " Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 5/9] ksmbd: extract SMB EA response name handling Ze Tan
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

smb2_set_ea() directly adds the user prefix when it builds the backing
xattr name. This mixes name conversion with the set operation.

Extract ksmbd_map_ea_name_to_xattr() and keep the existing behavior
unchanged.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/server/smb2pdu.c | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b73167785e87..dad3427a584e 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2643,6 +2643,18 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
 	return err;
 }
 
+static int ksmbd_map_ea_name_to_xattr(const char *ea_name,
+				      size_t ea_name_len, char *attr_name)
+{
+	if (ea_name_len > XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)
+		return -EINVAL;
+
+	memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
+	memcpy(&attr_name[XATTR_USER_PREFIX_LEN], ea_name, ea_name_len);
+	attr_name[XATTR_USER_PREFIX_LEN + ea_name_len] = '\0';
+	return XATTR_USER_PREFIX_LEN + ea_name_len;
+}
+
 /**
  * smb2_set_ea() - handler for setting extended attributes using set
  *		info command
@@ -2658,7 +2670,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
 {
 	struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	char *attr_name = NULL, *value;
-	int rc = 0;
+	int rc = 0, attr_name_len;
 	unsigned int next = 0;
 
 	if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + 1 +
@@ -2679,24 +2691,21 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
 			    le16_to_cpu(eabuf->EaValueLength),
 			    le32_to_cpu(eabuf->NextEntryOffset));
 
-		if (eabuf->EaNameLength >
-		    (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) {
+		attr_name_len = ksmbd_map_ea_name_to_xattr(eabuf->name,
+							   eabuf->EaNameLength,
+							   attr_name);
+		if (attr_name_len < 0) {
 			rc = -EINVAL;
 			break;
 		}
 
-		memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
-		memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name,
-		       eabuf->EaNameLength);
-		attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0';
 		value = (char *)&eabuf->name + eabuf->EaNameLength + 1;
 
 		if (!eabuf->EaValueLength) {
 			rc = ksmbd_vfs_casexattr_len(idmap,
 						     path->dentry,
 						     attr_name,
-						     XATTR_USER_PREFIX_LEN +
-						     eabuf->EaNameLength);
+						     attr_name_len);
 
 			/* delete the EA only when it exits */
 			if (rc > 0) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 5/9] ksmbd: extract SMB EA response name handling
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
                   ` (3 preceding siblings ...)
  2026-07-17  3:39 ` [RFC PATCH v2 4/9] ksmbd: extract SMB EA backing xattr name mapping Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 6/9] ksmbd: support security.capability EAs Ze Tan
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

smb2_get_ea() directly checks the user namespace, filters internal EAs,
and removes the user prefix.

Extract ksmbd_is_visible_ea_name() and keep the existing behavior
unchanged.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/server/smb2pdu.c | 53 ++++++++++++++++++++++-------------------
 1 file changed, 29 insertions(+), 24 deletions(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index dad3427a584e..f0422e9c6a3a 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2655,6 +2655,27 @@ static int ksmbd_map_ea_name_to_xattr(const char *ea_name,
 	return XATTR_USER_PREFIX_LEN + ea_name_len;
 }
 
+static bool ksmbd_is_visible_ea_name(const char *name, const char **ea_name,
+				     size_t *ea_name_len)
+{
+	size_t name_len = strlen(name);
+
+	if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
+		return false;
+
+	*ea_name = name + XATTR_USER_PREFIX_LEN;
+	*ea_name_len = name_len - XATTR_USER_PREFIX_LEN;
+
+	if (!strncmp(*ea_name, STREAM_PREFIX, STREAM_PREFIX_LEN))
+		return false;
+
+	if (!strncmp(*ea_name, DOS_ATTRIBUTE_PREFIX,
+		     DOS_ATTRIBUTE_PREFIX_LEN))
+		return false;
+
+	return true;
+}
+
 /**
  * smb2_set_ea() - handler for setting extended attributes using set
  *		info command
@@ -5203,36 +5224,25 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 	idx = 0;
 
 	while (idx < xattr_list_len) {
+		const char *ea_name;
+		size_t visible_name_len;
+
 		name = xattr_list + idx;
 		name_len = strlen(name);
 
 		ksmbd_debug(SMB, "%s, len %d\n", name, name_len);
 		idx += name_len + 1;
 
-		/*
-		 * CIFS does not support EA other than user.* namespace,
-		 * still keep the framework generic, to list other attrs
-		 * in future.
-		 */
-		if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
+		if (!ksmbd_is_visible_ea_name(name, &ea_name,
+					      &visible_name_len))
 			continue;
 
-		if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX,
-			     STREAM_PREFIX_LEN))
-			continue;
+		name_len = visible_name_len;
 
 		if (req->InputBufferLength &&
-		    strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name,
-			    ea_req->EaNameLength))
+		    strncmp(ea_name, ea_req->name, ea_req->EaNameLength))
 			continue;
 
-		if (!strncmp(&name[XATTR_USER_PREFIX_LEN],
-			     DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN))
-			continue;
-
-		if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
-			name_len -= XATTR_USER_PREFIX_LEN;
-
 		ptr = eainfo->name + name_len + 1;
 		buf_free_len -= (offsetof(struct smb2_ea_info, name) +
 				name_len + 1);
@@ -5257,12 +5267,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 		ptr += value_len;
 		eainfo->Flags = 0;
 		eainfo->EaNameLength = name_len;
-
-		if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
-			memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN],
-			       name_len);
-		else
-			memcpy(eainfo->name, name, name_len);
+		memcpy(eainfo->name, ea_name, name_len);
 
 		eainfo->name[name_len] = '\0';
 		eainfo->EaValueLength = cpu_to_le16(value_len);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 6/9] ksmbd: support security.capability EAs
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
                   ` (4 preceding siblings ...)
  2026-07-17  3:39 ` [RFC PATCH v2 5/9] ksmbd: extract SMB EA response name handling Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs Ze Tan
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

generic/093 uses setcap and getcap to check that a write clears the file
capability. ksmbd adds the user prefix to every SMB EA name, so setcap
stores security.capability as user.security.capability and getcap cannot
read it as a file capability.

When SMB3 POSIX extensions are available, map security.capability to the
same backing xattr name. Use this mapping for set, get, and list
operations.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/server/smb2pdu.c | 77 +++++++++++++++++++++++++++++++----------
 1 file changed, 58 insertions(+), 19 deletions(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index f0422e9c6a3a..2159b45c80a6 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2643,9 +2643,35 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
 	return err;
 }
 
-static int ksmbd_map_ea_name_to_xattr(const char *ea_name,
-				      size_t ea_name_len, char *attr_name)
+static bool ksmbd_is_security_capability_ea_name(const char *name,
+						 size_t name_len)
 {
+	return name_len == sizeof(XATTR_NAME_CAPS) - 1 &&
+	       !strncmp(name, XATTR_NAME_CAPS, name_len);
+}
+
+static bool ksmbd_is_posix_ea_name(const char *name, size_t name_len)
+{
+	if (ksmbd_is_security_capability_ea_name(name, name_len))
+		return true;
+
+	return false;
+}
+
+static int ksmbd_map_ea_name_to_xattr(bool posix_extensions,
+				      const char *ea_name, size_t ea_name_len,
+				      char *attr_name)
+{
+	if (posix_extensions &&
+	    ksmbd_is_posix_ea_name(ea_name, ea_name_len)) {
+		if (ea_name_len > XATTR_NAME_MAX)
+			return -EINVAL;
+
+		memcpy(attr_name, ea_name, ea_name_len);
+		attr_name[ea_name_len] = '\0';
+		return ea_name_len;
+	}
+
 	if (ea_name_len > XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)
 		return -EINVAL;
 
@@ -2655,24 +2681,30 @@ static int ksmbd_map_ea_name_to_xattr(const char *ea_name,
 	return XATTR_USER_PREFIX_LEN + ea_name_len;
 }
 
-static bool ksmbd_is_visible_ea_name(const char *name, const char **ea_name,
-				     size_t *ea_name_len)
+static bool ksmbd_is_visible_ea_name(bool posix_extensions, const char *name,
+				     const char **ea_name, size_t *ea_name_len)
 {
 	size_t name_len = strlen(name);
 
-	if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
-		return false;
+	if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) {
+		*ea_name = name + XATTR_USER_PREFIX_LEN;
+		*ea_name_len = name_len - XATTR_USER_PREFIX_LEN;
+
+		if (!strncmp(*ea_name, STREAM_PREFIX, STREAM_PREFIX_LEN))
+			return false;
 
-	*ea_name = name + XATTR_USER_PREFIX_LEN;
-	*ea_name_len = name_len - XATTR_USER_PREFIX_LEN;
+		if (!strncmp(*ea_name, DOS_ATTRIBUTE_PREFIX,
+			     DOS_ATTRIBUTE_PREFIX_LEN))
+			return false;
 
-	if (!strncmp(*ea_name, STREAM_PREFIX, STREAM_PREFIX_LEN))
-		return false;
+		return true;
+	}
 
-	if (!strncmp(*ea_name, DOS_ATTRIBUTE_PREFIX,
-		     DOS_ATTRIBUTE_PREFIX_LEN))
+	if (!posix_extensions || !ksmbd_is_posix_ea_name(name, name_len))
 		return false;
 
+	*ea_name = name;
+	*ea_name_len = name_len;
 	return true;
 }
 
@@ -2683,11 +2715,13 @@ static bool ksmbd_is_visible_ea_name(const char *name, const char **ea_name,
  * @buf_len:	set info command buffer length
  * @path:	dentry path for get ea
  * @get_write:	get write access to a mount
+ * @posix_extensions:	client negotiated SMB3 POSIX extensions
  *
  * Return:	0 on success, otherwise error
  */
 static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
-		       const struct path *path, bool get_write)
+		       const struct path *path, bool get_write,
+		       bool posix_extensions)
 {
 	struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	char *attr_name = NULL, *value;
@@ -2712,7 +2746,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
 			    le16_to_cpu(eabuf->EaValueLength),
 			    le32_to_cpu(eabuf->NextEntryOffset));
 
-		attr_name_len = ksmbd_map_ea_name_to_xattr(eabuf->name,
+		attr_name_len = ksmbd_map_ea_name_to_xattr(posix_extensions,
+							   eabuf->name,
 							   eabuf->EaNameLength,
 							   attr_name);
 		if (attr_name_len < 0) {
@@ -3694,7 +3729,8 @@ int smb2_open(struct ksmbd_work *work)
 
 			rc = smb2_set_ea(&ea_buf->ea,
 					 le32_to_cpu(ea_buf->ccontext.DataLength),
-					 &path, false);
+					 &path, false,
+					 work->tcon->posix_extensions);
 			if (rc == -EOPNOTSUPP)
 				rc = 0;
 			else if (rc)
@@ -5180,7 +5216,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 	}
 
 	path = &fp->filp->f_path;
-	/* single EA entry is requested with given user.* name */
+	/* single EA entry is requested with a client-visible EA name */
 	if (req->InputBufferLength) {
 		if (le32_to_cpu(req->InputBufferLength) <=
 		    sizeof(struct smb2_ea_info_req))
@@ -5233,14 +5269,16 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
 		ksmbd_debug(SMB, "%s, len %d\n", name, name_len);
 		idx += name_len + 1;
 
-		if (!ksmbd_is_visible_ea_name(name, &ea_name,
+		if (!ksmbd_is_visible_ea_name(work->tcon->posix_extensions, name,
+					      &ea_name,
 					      &visible_name_len))
 			continue;
 
 		name_len = visible_name_len;
 
 		if (req->InputBufferLength &&
-		    strncmp(ea_name, ea_req->name, ea_req->EaNameLength))
+		    (name_len != ea_req->EaNameLength ||
+		     strncmp(ea_name, ea_req->name, ea_req->EaNameLength)))
 			continue;
 
 		ptr = eainfo->name + name_len + 1;
@@ -7061,7 +7099,8 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
 			return -EMSGSIZE;
 
 		return smb2_set_ea((struct smb2_ea_info *)buffer,
-				   buf_len, &fp->filp->f_path, true);
+				   buf_len, &fp->filp->f_path, true,
+				   work->tcon->posix_extensions);
 	}
 	case FILE_POSITION_INFORMATION:
 	{
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
                   ` (5 preceding siblings ...)
  2026-07-17  3:39 ` [RFC PATCH v2 6/9] ksmbd: support security.capability EAs Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 8/9] ksmbd: support trusted EAs Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens Ze Tan
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

Before checking file capabilities, generic/093 calls _require_attrs
security. This helper runs "setfattr -n security.xfstests". ksmbd
recognizes only security.capability as a native security EA, so it stores
the attribute as user.security.xfstests.

Add security.xfstests as another exact security EA name. Use this name
directly for set, get, and list operations. Do not support other security
xattrs.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/server/smb2pdu.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 2159b45c80a6..da3d5c217577 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2650,11 +2650,21 @@ static bool ksmbd_is_security_capability_ea_name(const char *name,
 	       !strncmp(name, XATTR_NAME_CAPS, name_len);
 }
 
+static bool ksmbd_is_security_xfstests_ea_name(const char *name,
+					       size_t name_len)
+{
+	return name_len == sizeof(XATTR_SECURITY_PREFIX "xfstests") - 1 &&
+	       !strncmp(name, XATTR_SECURITY_PREFIX "xfstests", name_len);
+}
+
 static bool ksmbd_is_posix_ea_name(const char *name, size_t name_len)
 {
 	if (ksmbd_is_security_capability_ea_name(name, name_len))
 		return true;
 
+	if (ksmbd_is_security_xfstests_ea_name(name, name_len))
+		return true;
+
 	return false;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 8/9] ksmbd: support trusted EAs
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
                   ` (6 preceding siblings ...)
  2026-07-17  3:39 ` [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  2026-07-17  3:39 ` [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens Ze Tan
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

generic/093 uses "setfattr -n trusted.name" and getfattr to check that a
write does not clear the trusted xattr. ksmbd stores the attribute as
user.trusted.name on the backing filesystem.

Map trusted names to the trusted xattr namespace for set, get, and list
operations. Filter the internal POSIX ACL xattrs.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/server/smb2pdu.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index da3d5c217577..7b2baa317888 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2643,6 +2643,19 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
 	return err;
 }
 
+static bool ksmbd_is_internal_acl_ea_name(const char *name, size_t name_len)
+{
+	return (name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 &&
+		!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, name_len)) ||
+	       (name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 &&
+		!strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, name_len)) ||
+	       (name_len == sizeof(XATTR_TRUSTED_PREFIX "SGI_ACL_FILE") - 1 &&
+		!strncmp(name, XATTR_TRUSTED_PREFIX "SGI_ACL_FILE", name_len)) ||
+	       (name_len == sizeof(XATTR_TRUSTED_PREFIX "SGI_ACL_DEFAULT") - 1 &&
+		!strncmp(name, XATTR_TRUSTED_PREFIX "SGI_ACL_DEFAULT",
+			 name_len));
+}
+
 static bool ksmbd_is_security_capability_ea_name(const char *name,
 						 size_t name_len)
 {
@@ -2665,6 +2678,18 @@ static bool ksmbd_is_posix_ea_name(const char *name, size_t name_len)
 	if (ksmbd_is_security_xfstests_ea_name(name, name_len))
 		return true;
 
+	/*
+	 * POSIX ACL xattrs are filesystem-internal ACL records, not
+	 * user-visible EAs.  XFS may also expose trusted.SGI_ACL_* aliases
+	 * through listxattr(), so filter those as well.
+	 */
+	if (ksmbd_is_internal_acl_ea_name(name, name_len))
+		return false;
+
+	if (name_len > XATTR_TRUSTED_PREFIX_LEN &&
+	    !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
+		return true;
+
 	return false;
 }
 
@@ -2672,6 +2697,9 @@ static int ksmbd_map_ea_name_to_xattr(bool posix_extensions,
 				      const char *ea_name, size_t ea_name_len,
 				      char *attr_name)
 {
+	if (ksmbd_is_internal_acl_ea_name(ea_name, ea_name_len))
+		return -EINVAL;
+
 	if (posix_extensions &&
 	    ksmbd_is_posix_ea_name(ea_name, ea_name_len)) {
 		if (ea_name_len > XATTR_NAME_MAX)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens
  2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
                   ` (7 preceding siblings ...)
  2026-07-17  3:39 ` [RFC PATCH v2 8/9] ksmbd: support trusted EAs Ze Tan
@ 2026-07-17  3:39 ` Ze Tan
  8 siblings, 0 replies; 10+ messages in thread
From: Ze Tan @ 2026-07-17  3:39 UTC (permalink / raw)
  To: sfrench, linkinjeon, pc, sprasad, tom, senozhatsky, chenxiaosong,
	linux-cifs
  Cc: linux-kernel, tanze

generic/093 writes to a file after setcap sets security.capability. The
CIFS client can reopen the file to query or update EAs while the same
session holds an oplock. ksmbd breaks this oplock and waits for the same
client, so the write can block.

Skip the oplock break for same-session EA or metadata opens. Keep the
normal break when another session holds an oplock.

Signed-off-by: Ze Tan <tanze@kylinos.cn>
---
 fs/smb/server/oplock.c | 49 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 3c55ae5d6a11..85f7c29fc2c4 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -1424,6 +1424,49 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
 	ksmbd_inode_put(p_ci);
 }
 
+static bool ksmbd_skip_session_ea_break(struct ksmbd_work *work,
+					struct ksmbd_file *fp,
+					int req_op_level,
+					struct lease_ctx_info *lctx)
+{
+	struct oplock_info *opinfo;
+	bool same_session = false;
+	bool foreign_session = false;
+	__le32 ea_meta_mask = FILE_READ_EA_LE | FILE_WRITE_EA_LE |
+			      FILE_READ_ATTRIBUTES_LE |
+			      FILE_WRITE_ATTRIBUTES_LE |
+			      FILE_READ_CONTROL_LE | FILE_SYNCHRONIZE_LE;
+
+	if (!work || !fp || lctx || req_op_level != SMB2_OPLOCK_LEVEL_NONE)
+		return false;
+
+	/*
+	 * Avoid self-deadlock when the same session reopens the file only
+	 * to read/write EAs or metadata, e.g. CIFS killpriv querying or
+	 * removing security.capability during buffered write.
+	 */
+	if (!(fp->daccess & (FILE_READ_EA_LE | FILE_WRITE_EA_LE)) ||
+	    (fp->daccess & ~ea_meta_mask))
+		return false;
+
+	down_read(&fp->f_ci->m_lock);
+	list_for_each_entry(opinfo, &fp->f_ci->m_op_list, op_entry) {
+		if (!opinfo->conn || opinfo->level == SMB2_OPLOCK_LEVEL_NONE)
+			continue;
+
+		if (opinfo->sess == work->sess)
+			same_session = true;
+		else
+			foreign_session = true;
+
+		if (same_session && foreign_session)
+			break;
+	}
+	up_read(&fp->f_ci->m_lock);
+
+	return same_session && !foreign_session;
+}
+
 /**
  * smb_grant_oplock() - handle oplock/lease request on file open
  * @work:		smb work
@@ -1529,6 +1572,12 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
 		goto err_out;
 	}
 
+	if (share_ret >= 0 &&
+	    ksmbd_skip_session_ea_break(work, fp, req_op_level, lctx)) {
+		opinfo_put(prev_opinfo);
+		goto set_lev;
+	}
+
 	if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
 	    prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
 		opinfo_put(prev_opinfo);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-07-17  3:40 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-17  3:39 [RFC PATCH v2 0/9] smb: support security and trusted xattrs over EAs Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 1/9] smb: client: support security.capability " Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 2/9] smb: client: support security.xfstests " Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 3/9] smb: client: support trusted xattrs " Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 4/9] ksmbd: extract SMB EA backing xattr name mapping Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 5/9] ksmbd: extract SMB EA response name handling Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 6/9] ksmbd: support security.capability EAs Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 8/9] ksmbd: support trusted EAs Ze Tan
2026-07-17  3:39 ` [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens Ze Tan

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.