* [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-20 5:21 ` Namjae Jeon
2026-07-17 3:39 ` [RFC PATCH v2 2/9] smb: client: support security.xfstests " Ze Tan
` (7 subsequent siblings)
8 siblings, 1 reply; 21+ 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 [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 1/9] smb: client: support security.capability over EAs
2026-07-17 3:39 ` [RFC PATCH v2 1/9] smb: client: support security.capability " Ze Tan
@ 2026-07-20 5:21 ` Namjae Jeon
0 siblings, 0 replies; 21+ messages in thread
From: Namjae Jeon @ 2026-07-20 5:21 UTC (permalink / raw)
To: Ze Tan
Cc: sfrench, pc, sprasad, tom, senozhatsky, chenxiaosong, linux-cifs,
linux-kernel
On Fri, Jul 17, 2026 at 12:40 PM Ze Tan <tanze@kylinos.cn> wrote:
>
> 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>
The client exposes these EA names through security.* regardless of
whether SMB3 POSIX extensions were
negotiated. Is that right?
^ permalink raw reply [flat|nested] 21+ 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; 21+ 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 [flat|nested] 21+ 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; 21+ 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 [flat|nested] 21+ 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; 21+ 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 [flat|nested] 21+ 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; 21+ 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 [flat|nested] 21+ 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-20 6:57 ` Ralph Boehme
2026-07-17 3:39 ` [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs Ze Tan
` (2 subsequent siblings)
8 siblings, 1 reply; 21+ 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 [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 6/9] ksmbd: support security.capability EAs
2026-07-17 3:39 ` [RFC PATCH v2 6/9] ksmbd: support security.capability EAs Ze Tan
@ 2026-07-20 6:57 ` Ralph Boehme
2026-07-20 13:06 ` tanze
0 siblings, 1 reply; 21+ messages in thread
From: Ralph Boehme @ 2026-07-20 6:57 UTC (permalink / raw)
To: Ze Tan, sfrench, linkinjeon, pc, sprasad, tom, senozhatsky,
chenxiaosong, linux-cifs
Cc: linux-kernel
[-- Attachment #1.1: Type: text/plain, Size: 768 bytes --]
On 7/17/26 5:39 AM, Ze Tan wrote:
> +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)) {
why the check for allowed namespaces via ksmbd_is_posix_ea_name() at all
and not just pass through whatever the client sends us, pass it down and
let the xattr subsystem deal with it?
Still wrapping my head around this so I might be missing something
obvious. :)
Thanks!
-slow
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 6/9] ksmbd: support security.capability EAs
2026-07-20 6:57 ` Ralph Boehme
@ 2026-07-20 13:06 ` tanze
2026-07-21 13:38 ` Ralph Boehme
0 siblings, 1 reply; 21+ messages in thread
From: tanze @ 2026-07-20 13:06 UTC (permalink / raw)
To: Ralph Boehme, sfrench, linkinjeon, pc, sprasad, tom, senozhatsky,
chenxiaosong, linux-cifs
Cc: linux-kernel
Thanks for your question. When the CIFS client accesses user.EA, the
name transmitted over the SMB protocol is simply EA instead of user.EA.
ksmbd then prepends user. to convert it back to user.EA. If we pass
every attribute name directly to the VFS unmodified, other types of
extended attributes will return -EOPNOTSUPP due to missing handlers.
The current workflow works as follows:
Client request --user.EA
↓ CIFS client strips off the "user."
prefix SMB EA layer receives: --EA
↓ ksmbd adds back the "user."
prefix Server filesystem layer: --user.EA
After ksmbd finishes mapping to the underlying extended attribute name,
filesystem compatibility checks, value validation and all related
operations should be delegated uniformly to |vfs_setxattr()|. This
mapping logic is not meant to duplicate the full set of xattr namespaces
supported by the VFS. Instead, it filters which SMB extended attribute
names get parsed as native Linux xattr names.
I will revise the patch to enable native namespace mapping only when
both of the following two conditions are satisfied:
1.The request is sent with POSIX extended attribute parameters;
2. The mapped server-side user account is root or an account with
administrative privileges.
Thanks!
在 2026/7/20 14:57, Ralph Boehme 写道:
> On 7/17/26 5:39 AM, Ze Tan wrote:
>> +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)) {
>
> why the check for allowed namespaces via ksmbd_is_posix_ea_name() at
> all and not just pass through whatever the client sends us, pass it
> down and let the xattr subsystem deal with it?
>
> Still wrapping my head around this so I might be missing something
> obvious. :)
>
> Thanks!
> -slow
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 6/9] ksmbd: support security.capability EAs
2026-07-20 13:06 ` tanze
@ 2026-07-21 13:38 ` Ralph Boehme
2026-07-21 16:41 ` Steve French
0 siblings, 1 reply; 21+ messages in thread
From: Ralph Boehme @ 2026-07-21 13:38 UTC (permalink / raw)
To: tanze, sfrench, linkinjeon, pc, sprasad, tom, senozhatsky,
chenxiaosong, linux-cifs
Cc: linux-kernel, Jeremy Allison, Stefan Metzmacher
[-- Attachment #1.1: Type: text/plain, Size: 3537 bytes --]
On 7/20/26 3:06 PM, tanze wrote:
> Thanks for your question. When the CIFS client accesses user.EA, the
> name transmitted over the SMB protocol is simply EA instead of user.EA.
> ksmbd then prepends user. to convert it back to user.EA. If we pass
> every attribute name directly to the VFS unmodified, other types of
> extended attributes will return -EOPNOTSUPP due to missing handlers.
yup, but I guess that's exactly what we want.
If I get that right your proposal is
Client Network Server
-------------------------------------------------
user.foo foo user.foo
system.bar system.bar system.bar
This seems wrong (user namespace not prefixed, others prefixed).
What we might want to do instead is send the namespace over the wire in
all cases, but in a separate protocol field (expressed by splitting the
dotted string into two words seperated by a comma):
Client Network Server
=============================================================
Linux: Linux:
user.foo user, foo user.foo
system.bar system, bar system.bar
FreeBSD: Linux:
*1, foo user, foo user.foo
*2, bar system, bar system.bar
*1: EXTATTR_NAMESPACE_USER
*2: EXTATTR_NAMESPACE_SYSTEM
This way we have a clear way how implementations can map their namespace
to the protocol, and server and client know exactly what to do.
Thoughts?
-slow
>
> The current workflow works as follows:
>
> Client request --user.EA
> ↓ CIFS client strips off the "user."
> prefix SMB EA layer receives: --EA
> ↓ ksmbd adds back the "user."
> prefix Server filesystem layer: --user.EA
>
> After ksmbd finishes mapping to the underlying extended attribute name,
> filesystem compatibility checks, value validation and all related
> operations should be delegated uniformly to |vfs_setxattr()|. This
> mapping logic is not meant to duplicate the full set of xattr namespaces
> supported by the VFS. Instead, it filters which SMB extended attribute
> names get parsed as native Linux xattr names.
>
> I will revise the patch to enable native namespace mapping only when
> both of the following two conditions are satisfied:
>
> 1.The request is sent with POSIX extended attribute parameters;
> 2. The mapped server-side user account is root or an account with
> administrative privileges.
>
> Thanks!
>
>
> 在 2026/7/20 14:57, Ralph Boehme 写道:
>> On 7/17/26 5:39 AM, Ze Tan wrote:
>>> +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)) {
>>
>> why the check for allowed namespaces via ksmbd_is_posix_ea_name() at
>> all and not just pass through whatever the client sends us, pass it
>> down and let the xattr subsystem deal with it?
>>
>> Still wrapping my head around this so I might be missing something
>> obvious. :)
>>
>> Thanks!
>> -slow
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 6/9] ksmbd: support security.capability EAs
2026-07-21 13:38 ` Ralph Boehme
@ 2026-07-21 16:41 ` Steve French
2026-07-21 17:10 ` Ralph Boehme
0 siblings, 1 reply; 21+ messages in thread
From: Steve French @ 2026-07-21 16:41 UTC (permalink / raw)
To: Ralph Boehme
Cc: tanze, sfrench, linkinjeon, pc, sprasad, tom, senozhatsky,
chenxiaosong, linux-cifs, linux-kernel, Jeremy Allison,
Stefan Metzmacher
On Tue, Jul 21, 2026 at 8:48 AM Ralph Boehme <slow@samba.org> wrote:
>
> On 7/20/26 3:06 PM, tanze wrote:
> > Thanks for your question. When the CIFS client accesses user.EA, the
> > name transmitted over the SMB protocol is simply EA instead of user.EA.
> > ksmbd then prepends user. to convert it back to user.EA. If we pass
> > every attribute name directly to the VFS unmodified, other types of
> > extended attributes will return -EOPNOTSUPP due to missing handlers.
>
> yup, but I guess that's exactly what we want.
>
> If I get that right your proposal is
>
> Client Network Server
> -------------------------------------------------
> user.foo foo user.foo
> system.bar system.bar system.bar
>
> This seems wrong (user namespace not prefixed, others prefixed).
This approach at least doesn't require changing the protocol. It
shouldn't regress existing servers and clients who are sending "foo"
across the network (without the prefix) for the most common EAs
> What we might want to do instead is send the namespace over the wire in
> all cases, but in a separate protocol field (expressed by splitting the
> dotted string into two words seperated by a comma):
>
> Client Network Server
> =============================================================
> Linux: Linux:
> user.foo user, foo user.foo
> system.bar system, bar system.bar
>
> FreeBSD: Linux:
> *1, foo user, foo user.foo
> *2, bar system, bar system.bar
>
> *1: EXTATTR_NAMESPACE_USER
> *2: EXTATTR_NAMESPACE_SYSTEM
>
> This way we have a clear way how implementations can map their namespace
> to the protocol, and server and client know exactly what to do.
>
> Thoughts?
> -slow
>
> >
> > The current workflow works as follows:
> >
> > Client request --user.EA
> > ↓ CIFS client strips off the "user."
> > prefix SMB EA layer receives: --EA
> > ↓ ksmbd adds back the "user."
> > prefix Server filesystem layer: --user.EA
> >
> > After ksmbd finishes mapping to the underlying extended attribute name,
> > filesystem compatibility checks, value validation and all related
> > operations should be delegated uniformly to |vfs_setxattr()|. This
> > mapping logic is not meant to duplicate the full set of xattr namespaces
> > supported by the VFS. Instead, it filters which SMB extended attribute
> > names get parsed as native Linux xattr names.
> >
> > I will revise the patch to enable native namespace mapping only when
> > both of the following two conditions are satisfied:
> >
> > 1.The request is sent with POSIX extended attribute parameters;
> > 2. The mapped server-side user account is root or an account with
> > administrative privileges.
> >
> > Thanks!
> >
> >
> > 在 2026/7/20 14:57, Ralph Boehme 写道:
> >> On 7/17/26 5:39 AM, Ze Tan wrote:
> >>> +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)) {
> >>
> >> why the check for allowed namespaces via ksmbd_is_posix_ea_name() at
> >> all and not just pass through whatever the client sends us, pass it
> >> down and let the xattr subsystem deal with it?
> >>
> >> Still wrapping my head around this so I might be missing something
> >> obvious. :)
> >>
> >> Thanks!
> >> -slow
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 6/9] ksmbd: support security.capability EAs
2026-07-21 16:41 ` Steve French
@ 2026-07-21 17:10 ` Ralph Boehme
0 siblings, 0 replies; 21+ messages in thread
From: Ralph Boehme @ 2026-07-21 17:10 UTC (permalink / raw)
To: Steve French
Cc: tanze, sfrench, linkinjeon, pc, sprasad, tom, senozhatsky,
chenxiaosong, linux-cifs, linux-kernel, Jeremy Allison,
Stefan Metzmacher
[-- Attachment #1.1: Type: text/plain, Size: 1894 bytes --]
On 7/21/26 6:41 PM, Steve French wrote:
> On Tue, Jul 21, 2026 at 8:48 AM Ralph Boehme <slow@samba.org> wrote:
>>
>> On 7/20/26 3:06 PM, tanze wrote:
>>> Thanks for your question. When the CIFS client accesses user.EA, the
>>> name transmitted over the SMB protocol is simply EA instead of user.EA.
>>> ksmbd then prepends user. to convert it back to user.EA. If we pass
>>> every attribute name directly to the VFS unmodified, other types of
>>> extended attributes will return -EOPNOTSUPP due to missing handlers.
>>
>> yup, but I guess that's exactly what we want.
>>
>> If I get that right your proposal is
>>
>> Client Network Server
>> -------------------------------------------------
>> user.foo foo user.foo
>> system.bar system.bar system.bar
>>
>> This seems wrong (user namespace not prefixed, others prefixed).
>
> This approach at least doesn't require changing the protocol.
By definition we're defining an SMB3 POSIX protocol here, so I think we
should strive for a protocol that is consistent rather than encoding
historic Linux-specific implementation details.
In the proposal above, user.* is treated differently from every other
namespace:
user.foo -> foo
system.bar -> system.bar
That means the wire format no longer has a uniform representation of an
extended attribute. Whether a namespace is transmitted depends on which
namespace it is, which makes the protocol less self-describing and
introduces a special case that every implementation has to know about.
This is horrible.
If the protocol instead carries namespace and attribute name as separate
fields, every namespace is represented uniformly. Linux, FreeBSD, and
any future implementation simply map their local namespace
representation to the protocol, rather than the protocol inheriting
Linux's conventions.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]
^ permalink raw reply [flat|nested] 21+ 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-20 5:03 ` Namjae Jeon
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, 1 reply; 21+ 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 [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs
2026-07-17 3:39 ` [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs Ze Tan
@ 2026-07-20 5:03 ` Namjae Jeon
2026-07-20 6:33 ` tanze
2026-07-20 6:36 ` tanze
0 siblings, 2 replies; 21+ messages in thread
From: Namjae Jeon @ 2026-07-20 5:03 UTC (permalink / raw)
To: Ze Tan
Cc: sfrench, pc, sprasad, tom, senozhatsky, chenxiaosong, linux-cifs,
linux-kernel
> 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);
Is it appropriate to special-case an xfstests-only xattr name in ksmbd?
Could the security namespace support be implemented using a general,
well-defined policy instead?
Thanks.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs
2026-07-20 5:03 ` Namjae Jeon
@ 2026-07-20 6:33 ` tanze
2026-07-20 6:36 ` tanze
1 sibling, 0 replies; 21+ messages in thread
From: tanze @ 2026-07-20 6:33 UTC (permalink / raw)
To: Namjae Jeon
Cc: sfrench, pc, sprasad, tom, senozhatsky, chenxiaosong, linux-cifs,
linux-kernel
This is how the current version works, but I believe there is an issue
with it. Three days ago, I sent a private email to you and Steve
outlining my proposal for improvement, which leverages the SMB3 POSIX
extensions to implement this feature. However, your email system blocks
messages from me, so I was unable to forward the email to you successfully.
在 2026/7/20 13:03, Namjae Jeon 写道:
>> 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);
> Is it appropriate to special-case an xfstests-only xattr name in ksmbd?
> Could the security namespace support be implemented using a general,
> well-defined policy instead?
> Thanks.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 7/9] ksmbd: support security.xfstests EAs
2026-07-20 5:03 ` Namjae Jeon
2026-07-20 6:33 ` tanze
@ 2026-07-20 6:36 ` tanze
1 sibling, 0 replies; 21+ messages in thread
From: tanze @ 2026-07-20 6:36 UTC (permalink / raw)
To: Namjae Jeon
Cc: sfrench, pc, sprasad, tom, senozhatsky, chenxiaosong, linux-cifs,
linux-kernel
That’s what I came to think later. My plan is to place it under the
security.* extended EA namespace, and we can add more entries if further
requirements arise. I will push a revised code version in a couple of days.
在 2026/7/20 13:03, Namjae Jeon 写道:
> Is it appropriate to special-case an xfstests-only xattr name in ksmbd?
> Could the security namespace support be implemented using a general,
> well-defined policy instead?
> Thanks.
^ permalink raw reply [flat|nested] 21+ 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; 21+ 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 [flat|nested] 21+ 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
2026-07-20 5:34 ` Namjae Jeon
8 siblings, 1 reply; 21+ 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 [flat|nested] 21+ messages in thread* Re: [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens
2026-07-17 3:39 ` [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens Ze Tan
@ 2026-07-20 5:34 ` Namjae Jeon
2026-07-20 8:54 ` tanze
0 siblings, 1 reply; 21+ messages in thread
From: Namjae Jeon @ 2026-07-20 5:34 UTC (permalink / raw)
To: Ze Tan
Cc: sfrench, pc, sprasad, tom, senozhatsky, chenxiaosong, linux-cifs,
linux-kernel
On Fri, Jul 17, 2026 at 12:40 PM Ze Tan <tanze@kylinos.cn> wrote:
>
> 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.
A session can own multiple independent opens. Why is session identity
sufficient to conclude that an EA/metadata-only open may bypass the
existing exclusive or batch oplock break? Oplock state and break
notifications are associated with an individual open, rather
than the session as a whole. Could you point to the SMB specification text ?
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens
2026-07-20 5:34 ` Namjae Jeon
@ 2026-07-20 8:54 ` tanze
0 siblings, 0 replies; 21+ messages in thread
From: tanze @ 2026-07-20 8:54 UTC (permalink / raw)
To: Namjae Jeon
Cc: sfrench, pc, sprasad, tom, senozhatsky, chenxiaosong, linux-cifs,
linux-kernel
You are right, my handling here is flawed. The logic cannot rely solely
on matching sessions, as a single SMB session can hold multiple
independent file open handles.
I will remove the session-based special exception logic on the server
side and rework the complete fix on the client side.
在 2026/7/20 13:34, Namjae Jeon 写道:
> On Fri, Jul 17, 2026 at 12:40 PM Ze Tan <tanze@kylinos.cn> wrote:
>> 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.
> A session can own multiple independent opens. Why is session identity
> sufficient to conclude that an EA/metadata-only open may bypass the
> existing exclusive or batch oplock break? Oplock state and break
> notifications are associated with an individual open, rather
> than the session as a whole. Could you point to the SMB specification text ?
^ permalink raw reply [flat|nested] 21+ messages in thread