public inbox for linux-nfs@vger.kernel.org
 help / color / mirror / Atom feed
From: rick.macklem@gmail.com
To: linux-nfs@vger.kernel.org
Cc: Rick Macklem <rmacklem@uoguelph.ca>
Subject: [PATCH v1 04/17] Add support for encoding/decoding POSIX draft ACLs
Date: Tue, 30 Dec 2025 18:21:06 -0800	[thread overview]
Message-ID: <20251231022119.1714-5-rick.macklem@gmail.com> (raw)
In-Reply-To: <20251231022119.1714-1-rick.macklem@gmail.com>

From: Rick Macklem <rmacklem@uoguelph.ca>

This patch adds encoding/decoding of the new attributes described
by the internet draft "POSIX Draft ACL support for Network
File System Version 4, Minor Version 2".

Signed-off-by: Rick Macklem <rmacklem@uoguelph.ca>
---
 fs/nfsd/nfs4xdr.c | 292 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 287 insertions(+), 5 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5065727204b9..5f996b3a4ce4 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -43,6 +43,7 @@
 #include <linux/sunrpc/addr.h>
 #include <linux/xattr.h>
 #include <linux/vmalloc.h>
+#include <linux/nfsacl.h>
 
 #include <uapi/linux/xattr.h>
 
@@ -377,16 +378,123 @@ nfsd4_decode_security_label(struct nfsd4_compoundargs *argp,
 	return nfs_ok;
 }
 
+static __be32
+nfsacl4_posix_xdrtotag(struct xdr_stream *xdr, u32 *tag)
+{
+	u32 type;
+
+	if (xdr_stream_decode_u32(xdr, &type) < 0)
+		return nfserr_bad_xdr;
+	switch(type) {
+	case POSIXACE4_TAG_USER_OBJ:
+		*tag = ACL_USER_OBJ;
+		break;
+	case POSIXACE4_TAG_GROUP_OBJ:
+		*tag = ACL_GROUP_OBJ;
+		break;
+	case POSIXACE4_TAG_USER:
+		*tag = ACL_USER;
+		break;
+	case POSIXACE4_TAG_GROUP:
+		*tag = ACL_GROUP;
+		break;
+	case POSIXACE4_TAG_MASK:
+		*tag = ACL_MASK;
+		break;
+	case POSIXACE4_TAG_OTHER:
+		*tag = ACL_OTHER;
+		break;
+	default:
+		return nfserr_bad_xdr;
+	}
+	return nfs_ok;
+}
+
+static __be32
+nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp,
+			struct posix_acl_entry *ace)
+{
+	u32 val;
+	__be32 *p, status;
+
+	status = nfsacl4_posix_xdrtotag(argp->xdr, &val);
+	if (status != nfs_ok)
+		return status;
+	ace->e_tag = val;
+	if (xdr_stream_decode_u32(argp->xdr, &val) < 0)
+		return nfserr_bad_xdr;
+	if (val & ~S_IRWXO)
+		return nfserr_bad_xdr;
+	ace->e_perm = val;
+
+	if (xdr_stream_decode_u32(argp->xdr, &val) < 0)
+		return nfserr_bad_xdr;
+	p = xdr_inline_decode(argp->xdr, val);
+	if (!p)
+		return nfserr_bad_xdr;
+	switch(ace->e_tag) {
+	case ACL_USER:
+		status = nfsd_map_name_to_uid(argp->rqstp,
+				(char *)p, val, &ace->e_uid);
+		break;
+	case ACL_GROUP:
+		status = nfsd_map_name_to_gid(argp->rqstp,
+				(char *)p, val, &ace->e_gid);
+	}
+
+	return status;
+}
+
+static noinline __be32
+nfsd4_decode_posix_acl(struct nfsd4_compoundargs *argp, struct posix_acl **acl)
+{
+	struct posix_acl_entry *ace;
+	__be32 status;
+	u32 count;
+
+	if (xdr_stream_decode_u32(argp->xdr, &count) < 0)
+		return nfserr_bad_xdr;
+
+	if (count > xdr_stream_remaining(argp->xdr) / 16)
+		/*
+		 * Even with 4-byte names there wouldn't be
+		 * space for that many aces; something fishy is
+		 * going on:
+		 */
+		return nfserr_fbig;
+
+	*acl = posix_acl_alloc(count, GFP_NOFS);
+	if (*acl == NULL)
+		return nfserr_resource;
+
+	(*acl)->a_count = count;
+	for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) {
+		status = nfsd4_decode_posixace4(argp, ace);
+		if (status) {
+			posix_acl_release(*acl);
+			*acl = NULL;
+			return status;
+		}
+	}
+
+	return nfs_ok;
+}
+
 static __be32
 nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
 		    struct iattr *iattr, struct nfs4_acl **acl,
-		    struct xdr_netobj *label, int *umask)
+		    struct xdr_netobj *label, int *umask,
+		    struct posix_acl **dpaclp, struct posix_acl **paclp)
 {
 	unsigned int starting_pos;
 	u32 attrlist4_count;
 	__be32 *p, status;
 
 	iattr->ia_valid = 0;
+	if (dpaclp)
+		*dpaclp = NULL;
+	if (paclp)
+		*paclp = NULL;
 	status = nfsd4_decode_bitmap4(argp, bmval, bmlen);
 	if (status)
 		return nfserr_bad_xdr;
@@ -542,6 +650,28 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen,
 		iattr->ia_valid |= ATTR_CTIME | ATTR_CTIME_SET |
 				   ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG;
 	}
+	if (bmval[2] & FATTR4_WORD2_POSIX_DEFAULT_ACL) {
+		struct posix_acl *dpacl;
+
+		status = nfsd4_decode_posix_acl(argp, &dpacl);
+		if (status)
+			return status;
+		if (dpaclp)
+			*dpaclp = dpacl;
+		else
+			posix_acl_release(dpacl);
+	}
+	if (bmval[2] & FATTR4_WORD2_POSIX_ACCESS_ACL) {
+		struct posix_acl *pacl;
+
+		status = nfsd4_decode_posix_acl(argp, &pacl);
+		if (status)
+			return status;
+		if (paclp)
+			*paclp = pacl;
+		else
+			posix_acl_release(pacl);
+	}
 
 	/* request sanity: did attrlist4 contain the expected number of words? */
 	if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
@@ -849,7 +979,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
 	status = nfsd4_decode_fattr4(argp, create->cr_bmval,
 				    ARRAY_SIZE(create->cr_bmval),
 				    &create->cr_iattr, &create->cr_acl,
-				    &create->cr_label, &create->cr_umask);
+				    &create->cr_label, &create->cr_umask,
+				    NULL, NULL);
 	if (status)
 		return status;
 
@@ -1000,7 +1131,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open
 		status = nfsd4_decode_fattr4(argp, open->op_bmval,
 					     ARRAY_SIZE(open->op_bmval),
 					     &open->op_iattr, &open->op_acl,
-					     &open->op_label, &open->op_umask);
+					     &open->op_label, &open->op_umask,
+					     NULL, NULL);
 		if (status)
 			return status;
 		break;
@@ -1018,7 +1150,8 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open
 		status = nfsd4_decode_fattr4(argp, open->op_bmval,
 					     ARRAY_SIZE(open->op_bmval),
 					     &open->op_iattr, &open->op_acl,
-					     &open->op_label, &open->op_umask);
+					     &open->op_label, &open->op_umask,
+					     NULL, NULL);
 		if (status)
 			return status;
 		break;
@@ -1345,7 +1478,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
 	return nfsd4_decode_fattr4(argp, setattr->sa_bmval,
 				   ARRAY_SIZE(setattr->sa_bmval),
 				   &setattr->sa_iattr, &setattr->sa_acl,
-				   &setattr->sa_label, NULL);
+				   &setattr->sa_label, NULL, &setattr->sa_dpacl,
+				   &setattr->sa_pacl);
 }
 
 static __be32
@@ -2930,6 +3064,8 @@ struct nfsd4_fattr_args {
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 	struct lsm_context	context;
 #endif
+	struct posix_acl	*dpacl;
+	struct posix_acl	*pacl;
 	u32			rdattr_err;
 	bool			contextsupport;
 	bool			ignore_crossmnt;
@@ -3470,6 +3606,128 @@ static __be32 nfsd4_encode_fattr4_open_arguments(struct xdr_stream *xdr,
 	return nfs_ok;
 }
 
+static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr,
+					const struct nfsd4_fattr_args *args)
+{
+
+	return nfsd4_encode_uint32_t(xdr, ACL_MODEL_POSIX_DRAFT);
+}
+
+static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr,
+					const struct nfsd4_fattr_args *args)
+{
+
+	return nfsd4_encode_uint32_t(xdr, ACL_SCOPE_FILE_SYSTEM);
+}
+
+static int nfsacl4_posix_tagtotype(u32 tag)
+{
+	int type;
+
+	switch(tag) {
+	case ACL_USER_OBJ:
+		type = POSIXACE4_TAG_USER_OBJ;
+		break;
+	case ACL_GROUP_OBJ:
+		type = POSIXACE4_TAG_GROUP_OBJ;
+		break;
+	case ACL_USER:
+		type = POSIXACE4_TAG_USER;
+		break;
+	case ACL_GROUP:
+		type = POSIXACE4_TAG_GROUP;
+		break;
+	case ACL_MASK:
+		type = POSIXACE4_TAG_MASK;
+		break;
+	case ACL_OTHER:
+		type = POSIXACE4_TAG_OTHER;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return type;
+}
+
+static __be32 xdr_nfs4ace_stream_encode(struct xdr_stream *xdr,
+				struct svc_rqst *rqstp,
+				struct posix_acl_entry *acep)
+{
+	__be32 status;
+	int type;
+
+	type = nfsacl4_posix_tagtotype(acep->e_tag);
+	if (type < 0)
+		return nfserr_resource;
+	if (xdr_stream_encode_u32(xdr, type) != XDR_UNIT)
+		return nfserr_resource;
+	if (xdr_stream_encode_u32(xdr, acep->e_perm) != XDR_UNIT)
+		return nfserr_resource;
+	switch(acep->e_tag) {
+	case ACL_USER_OBJ:
+	case ACL_GROUP_OBJ:
+	case ACL_MASK:
+	case ACL_OTHER:
+		if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
+			return nfserr_resource;
+		break;
+	case ACL_USER:
+		status = nfsd4_encode_user(xdr, rqstp, acep->e_uid);
+		if (status != nfs_ok)
+			return status;
+		break;
+	case ACL_GROUP:
+		status = nfsd4_encode_group(xdr, rqstp, acep->e_gid);
+		if (status != nfs_ok)
+			return status;
+		break;
+	default:
+		return nfserr_resource;
+	}
+	return nfs_ok;
+}
+
+static __be32 encode_stream_posixacl(struct xdr_stream *xdr,
+				struct posix_acl *acl,
+				struct svc_rqst *rqstp)
+{
+	__be32 status;
+	int cnt;
+
+	if (acl == NULL) {
+		if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
+			return nfserr_resource;
+		return nfs_ok;
+	}
+	if (acl->a_count > NFS_ACL_MAX_ENTRIES)
+		return nfserr_resource;
+	if (xdr_stream_encode_u32(xdr, acl->a_count) != XDR_UNIT)
+		return nfserr_resource;
+
+	for (cnt = 0; cnt < acl->a_count; cnt++) {
+		status = xdr_nfs4ace_stream_encode(xdr, rqstp,
+						&acl->a_entries[cnt]);
+		if (status != nfs_ok)
+			return status;
+	}
+
+	return nfs_ok;
+}
+
+static __be32 nfsd4_encode_fattr4_posix_default_acl(struct xdr_stream *xdr,
+				      const struct nfsd4_fattr_args *args)
+{
+
+	return encode_stream_posixacl(xdr, args->dpacl, args->rqstp);
+}
+
+static __be32 nfsd4_encode_fattr4_posix_access_acl(struct xdr_stream *xdr,
+				      const struct nfsd4_fattr_args *args)
+{
+
+	return encode_stream_posixacl(xdr, args->pacl, args->rqstp);
+}
+
 static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
 	[FATTR4_SUPPORTED_ATTRS]	= nfsd4_encode_fattr4_supported_attrs,
 	[FATTR4_TYPE]			= nfsd4_encode_fattr4_type,
@@ -3573,6 +3831,10 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
 	[FATTR4_TIME_DELEG_ACCESS]	= nfsd4_encode_fattr4__inval,
 	[FATTR4_TIME_DELEG_MODIFY]	= nfsd4_encode_fattr4__inval,
 	[FATTR4_OPEN_ARGUMENTS]		= nfsd4_encode_fattr4_open_arguments,
+	[FATTR4_ACL_TRUEFORM]		= nfsd4_encode_fattr4_acl_trueform,
+	[FATTR4_ACL_TRUEFORM_SCOPE]	= nfsd4_encode_fattr4_acl_trueform_scope,
+	[FATTR4_POSIX_DEFAULT_ACL]	= nfsd4_encode_fattr4_posix_default_acl,
+	[FATTR4_POSIX_ACCESS_ACL]	= nfsd4_encode_fattr4_posix_access_acl,
 };
 
 /*
@@ -3610,6 +3872,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 	args.dentry = dentry;
 	args.ignore_crossmnt = (ignore_crossmnt != 0);
 	args.acl = NULL;
+	args.pacl = NULL;
+	args.dpacl = NULL;
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 	args.context.context = NULL;
 #endif
@@ -3699,6 +3963,20 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 			goto out_nfserr;
 	}
 
+	if (attrmask[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL |
+					FATTR4_WORD2_POSIX_ACCESS_ACL)) {
+		err = nfsd4_get_posix_acl(rqstp, dentry, &args.pacl,
+					&args.dpacl);
+		if (err == -EOPNOTSUPP)
+			attrmask[2] &= ~(FATTR4_WORD2_POSIX_DEFAULT_ACL |
+						FATTR4_WORD2_POSIX_ACCESS_ACL);
+		else if (err == -EINVAL) {
+			status = nfserr_attrnotsupp;
+			goto out;
+		} else if (err != 0)
+			goto out_nfserr;
+	}
+
 	args.contextsupport = false;
 
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
@@ -3747,6 +4025,10 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
 		security_release_secctx(&args.context);
 #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
 	kfree(args.acl);
+	if (args.pacl)
+		posix_acl_release(args.pacl);
+	if (args.dpacl)
+		posix_acl_release(args.dpacl);
 	if (tempfh) {
 		fh_put(tempfh);
 		kfree(tempfh);
-- 
2.49.0


  parent reply	other threads:[~2025-12-31  2:22 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-31  2:21 [PATCH v1 00/17] Add NFSv4.2 POSIX ACL support rick.macklem
2025-12-31  2:21 ` [PATCH v1 01/17] Add definitions for the POSIX draft ACL attributes rick.macklem
2025-12-31  2:21 ` [PATCH v1 02/17] Add a new function to acquire the POSIX draft ACLs rick.macklem
2025-12-31  2:21 ` [PATCH v1 03/17] Add a function to set POSIX ACLs rick.macklem
2025-12-31  2:21 ` rick.macklem [this message]
2025-12-31  2:21 ` [PATCH v1 05/17] Add a check for both POSIX and NFSv4 ACLs being set rick.macklem
2025-12-31  2:21 ` [PATCH v1 06/17] Add na_dpaclerr and na_paclerr for file creation rick.macklem
2025-12-31  2:21 ` [PATCH v1 07/17] Add support for POSIX draft ACLs " rick.macklem
2025-12-31  2:21 ` [PATCH v1 08/17] Add the arguments for decoding of POSIX ACLs rick.macklem
2025-12-31  2:21 ` [PATCH v1 09/17] Fix a couple of bugs in POSIX ACL decoding rick.macklem
2025-12-31  2:21 ` [PATCH v1 10/17] Improve correctness for the ACL_TRUEFORM attribute rick.macklem
2025-12-31  2:21 ` [PATCH v1 11/17] Make sort_pacl_range() global rick.macklem
2025-12-31  2:21 ` [PATCH v1 12/17] Call sort_pacl_range() for decoded POSIX draft ACLs rick.macklem
2025-12-31  2:21 ` [PATCH v1 13/17] Fix handling of POSIX ACLs with zero ACEs rick.macklem
2025-12-31  2:21 ` [PATCH v1 14/17] Fix handling of zero length ACLs for file creation rick.macklem
2025-12-31  2:21 ` [PATCH v1 15/17] Do not allow (N)VERIFY to check POSIX ACL attributes rick.macklem
2025-12-31  2:21 ` [PATCH v1 16/17] Set the POSIX ACL attributes supported rick.macklem
2025-12-31  2:21 ` [PATCH v1 17/17] Change a bunch of function prefixes to nfsd42_ rick.macklem
2025-12-31  2:47 ` [PATCH v1 00/17] Add NFSv4.2 POSIX ACL support Chuck Lever
2025-12-31  2:49   ` Rick Macklem
2025-12-31  2:57   ` Rick Macklem

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251231022119.1714-5-rick.macklem@gmail.com \
    --to=rick.macklem@gmail.com \
    --cc=linux-nfs@vger.kernel.org \
    --cc=rmacklem@uoguelph.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox