From 500fb7072c940fa3c33500c654fa65e32f0fa147 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:38:25 -0700 Subject: [PATCH 01/22] Add a new ACL function to get a POSIX ACL This patch adds a new function called nfsd4_get_posix_acl() to get a POSIX draft NFSv4.2 ACL. It is similar to nfsd4_get_nfs4_acl(). Signed-off-by: Rick Macklem --- fs/nfsd/acl.h | 2 ++ fs/nfsd/nfs4acl.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index 4b7324458a94..213774cebeeb 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -47,6 +47,8 @@ __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who); int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl); +int nfsd4_get_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry, + struct posix_acl **pacl_ret, struct posix_acl **dpacl_ret); __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, struct nfsd_attrs *attr); diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 96e786b5e544..b57a575da46d 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -176,6 +176,39 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, return error; } +int +nfsd4_get_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry, + struct posix_acl **pacl_ret, struct posix_acl **dpacl_ret) +{ + struct inode *inode = d_inode(dentry); + int error = 0; + struct posix_acl *pacl = NULL, *dpacl = NULL; + + *pacl_ret = NULL; + *dpacl_ret = NULL; + pacl = get_inode_acl(inode, ACL_TYPE_ACCESS); + if (!pacl) + pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); + + if (IS_ERR(pacl)) + return PTR_ERR(pacl); + + *pacl_ret = pacl; + + if (S_ISDIR(inode->i_mode)) { + dpacl = get_inode_acl(inode, ACL_TYPE_DEFAULT); + if (IS_ERR(dpacl)) { + error = PTR_ERR(dpacl); + goto out; + } + + *dpacl_ret = dpacl; + } + +out: + return error; +} + struct posix_acl_summary { unsigned short owner; unsigned short users; -- 2.34.1 From 5932407b367624e02d16a37298a6af28df84c1a5 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:40:33 -0700 Subject: [PATCH 02/22] Add a new function to set a POSIX draft ACL This patch adds the function that sets a POSIX draft ACL called nfsd4_proc_setacl() and calls it from nfsd4_setattr() to set the POSIX draft ACLs when a SETATTR of the new POSIX draft ACL attributes is done by a NFSv4.2 client. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index b5a6bf4f459f..d959f8267c01 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1124,6 +1124,31 @@ nfsd4_secinfo_no_name_release(union nfsd4_op_u *u) exp_put(u->secinfo_no_name.sin_exp); } +/* + * Set the Access and/or Default ACL of a file. + */ +static __be32 +nfsd4_proc_setacl(struct svc_rqst *rqstp, svc_fh *fh, struct inode *inode, + struct posix_acl *dpacl, struct posix_acl *pacl) +{ + int error = 0; + + if (dpacl == NULL && pacl == NULL) + return nfs_ok; + + inode_lock(inode); + + if (dpacl != NULL) + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_DEFAULT, dpacl); + if (!error && pacl != NULL) + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_ACCESS, pacl); + + inode_unlock(inode); + return nfserrno(error); +} + static __be32 nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) @@ -1165,11 +1190,18 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cstate->current_fh.fh_no_wcc = true; status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs, NULL); cstate->current_fh.fh_no_wcc = save_no_wcc; + if (!status && (setattr->sa_dpacl != NULL || setattr->sa_pacl != NULL)) + status = nfsd4_proc_setacl(rqstp, &cstate->current_fh, inode, + setattr->sa_dpacl, setattr->sa_pacl); if (!status) status = nfserrno(attrs.na_labelerr); if (!status) status = nfserrno(attrs.na_aclerr); out: + if (setattr->sa_dpacl != NULL) + posix_acl_release(setattr->sa_dpacl); + if (setattr->sa_pacl != NULL) + posix_acl_release(setattr->sa_pacl); nfsd_attrs_free(&attrs); fh_drop_write(&cstate->current_fh); return status; -- 2.34.1 From 00f266397b85b819cce0a4701afb6c0085ae77f7 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:42:51 -0700 Subject: [PATCH 03/22] Add the FATTR4_xxx bits for the POSIX draft ACL attributes Signed-off-by: Rick Macklem --- fs/nfsd/nfsd.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 4b56ba1e8e48..1630897068af 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -458,7 +458,11 @@ enum { (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \ FATTR4_WORD2_MODE_UMASK | \ NFSD4_2_SECURITY_ATTRS | \ - FATTR4_WORD2_XATTR_SUPPORT) + FATTR4_WORD2_XATTR_SUPPORT | \ + FATTR4_WORD2_ACL_TRUEFORM | \ + FATTR4_WORD2_ACL_TRUEFORM_SCOPE | \ + FATTR4_WORD2_POSIX_DEFAULT_ACL | \ + FATTR4_WORD2_POSIX_ACCESS_ACL) extern const u32 nfsd_suppattrs[3][3]; @@ -528,7 +532,9 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval) #endif #define NFSD_WRITEABLE_ATTRS_WORD2 \ (FATTR4_WORD2_MODE_UMASK \ - | MAYBE_FATTR4_WORD2_SECURITY_LABEL) + | MAYBE_FATTR4_WORD2_SECURITY_LABEL \ + | FATTR4_WORD2_POSIX_DEFAULT_ACL \ + | FATTR4_WORD2_POSIX_ACCESS_ACL) #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ NFSD_WRITEABLE_ATTRS_WORD0 -- 2.34.1 From e1b319802b50d2f5706c149b7a3a0d5db89607ce Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:44:39 -0700 Subject: [PATCH 04/22] Add fields for the default and access POSIX ACLs The sa_dpacl and sa_pacl fields are used to pass the POSIX draft ACLs from a SETATTR request to nfsd4_proc_setattr() to set the acl. These ACLs are allocated by nfsd4_decode_posix_acl() once the ace count is known and must be posix_acl_release()'d when done with them. Signed-off-by: Rick Macklem --- fs/nfsd/xdr4.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 2a21a7662e03..99e0ad2a8235 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -483,6 +483,8 @@ struct nfsd4_setattr { struct iattr sa_iattr; /* request */ struct nfs4_acl *sa_acl; struct xdr_netobj sa_label; + struct posix_acl *sa_dpacl; + struct posix_acl *sa_pacl; }; struct nfsd4_setclientid { -- 2.34.1 From f8f80d0507f02dd1b986e3881156c876250f6ad1 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:50:11 -0700 Subject: [PATCH 05/22] Add handling of the XDR for the POSIX draft ACL attributes This patch adds encoding/decoding of the new attributes proposed by the internet draft "POSIX Draft ACL support for Network File System Version 4, Minor Version 2". Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 299 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 294 insertions(+), 5 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f118921250c3..cfff3da2362e 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -376,16 +377,124 @@ 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; +dprintk("perm=%d\n", val); + 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; @@ -520,6 +629,28 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, *umask = mask & S_IRWXUGO; iattr->ia_valid |= ATTR_MODE; } + 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) @@ -840,7 +971,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; @@ -991,7 +1123,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; @@ -1009,7 +1142,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; @@ -1336,7 +1470,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 @@ -2933,6 +3068,8 @@ struct nfsd4_fattr_args { void *context; int contextlen; #endif + struct posix_acl *dpacl; + struct posix_acl *pacl; u32 rdattr_err; bool contextsupport; bool ignore_crossmnt; @@ -3399,6 +3536,134 @@ static __be32 nfsd4_encode_fattr4_xattr_support(struct xdr_stream *xdr, return nfsd4_encode_bool(xdr, err == 0); } +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; + +dprintk("in xdr_nfs4ace_encode tag=%d\n", acep->e_tag); + 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: +dprintk("at encode 0\n"); + 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); +dprintk("encode_user %d\n", status); + 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; + } +dprintk("encode_ace ret nfs_ok\n"); + 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) { +dprintk("in encode_posixacl NULL acl\n"); + 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]); +dprintk("aft xdr_nfs4ace_encode=%d\n", status); + 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, @@ -3499,6 +3764,10 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = { [FATTR4_MODE_UMASK] = nfsd4_encode_fattr4__noop, [FATTR4_XATTR_SUPPORT] = nfsd4_encode_fattr4_xattr_support, + [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, }; /* @@ -3537,6 +3806,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 = NULL; #endif @@ -3609,6 +3880,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 @@ -3657,6 +3942,10 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr, security_release_secctx(args.context, args.contextlen); #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.34.1 From b80c1ee859c3415b13968901c31bf5edf2e41d80 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:52:56 -0700 Subject: [PATCH 06/22] Add a check to ensure POSIX and NFSv4 ACLs are not both being set While here, fix posix_acl_release() for a couple of error cases. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index d959f8267c01..c3340312ab77 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1168,11 +1168,13 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, &cstate->current_fh, &setattr->sa_stateid, WR_STATE, NULL, NULL); if (status) - return status; + goto out_err; } err = fh_want_write(&cstate->current_fh); - if (err) - return nfserrno(err); + if (err) { + status = nfserrno(err); + goto out_err; + } status = nfs_ok; status = check_attr_support(rqstp, cstate, setattr->sa_bmval, @@ -1180,6 +1182,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) goto out; + if (setattr->sa_acl && (setattr->sa_dpacl || setattr->sa_pacl)) { + status = nfserr_inval; + goto out; + } + inode = cstate->current_fh.fh_dentry->d_inode; status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG, setattr->sa_acl, &attrs); @@ -1198,12 +1205,13 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!status) status = nfserrno(attrs.na_aclerr); out: - if (setattr->sa_dpacl != NULL) - posix_acl_release(setattr->sa_dpacl); - if (setattr->sa_pacl != NULL) - posix_acl_release(setattr->sa_pacl); nfsd_attrs_free(&attrs); fh_drop_write(&cstate->current_fh); +out_err: + if (setattr->sa_dpacl) + posix_acl_release(setattr->sa_dpacl); + if (setattr->sa_pacl) + posix_acl_release(setattr->sa_pacl); return status; } -- 2.34.1 From a22a1d708199f73127eec6b0197815e7d3fdac7d Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:54:43 -0700 Subject: [PATCH 07/22] No need to check for a NULL acl pointer Since posix_acl_release() checks for a NULL pointer argument, there is no need to check here. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index c3340312ab77..ca0d872831fb 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1208,10 +1208,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd_attrs_free(&attrs); fh_drop_write(&cstate->current_fh); out_err: - if (setattr->sa_dpacl) - posix_acl_release(setattr->sa_dpacl); - if (setattr->sa_pacl) - posix_acl_release(setattr->sa_pacl); + posix_acl_release(setattr->sa_dpacl); + posix_acl_release(setattr->sa_pacl); return status; } -- 2.34.1 From 7371b38832f14d2f95f58db6ab0d400d72d33d2d Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:56:34 -0700 Subject: [PATCH 08/22] Add na_dpaclerr and na_paclerr for file creation When new file objects are created, a client might choose to specify POSIX draft ACLs for it. If the set_posix_acl() fails, note the failure in these fields. The file object has already been created, so the creation cannot now fail, but the attribute bits can be cleared in the reply. Signed-off-by: Rick Macklem --- fs/nfsd/vfs.c | 6 +++--- fs/nfsd/vfs.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 22325b590e17..022dedb0be0c 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -583,12 +583,12 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, attr->na_labelerr = security_inode_setsecctx(dentry, attr->na_seclabel->data, attr->na_seclabel->len); if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) - attr->na_aclerr = set_posix_acl(&nop_mnt_idmap, + attr->na_paclerr = set_posix_acl(&nop_mnt_idmap, dentry, ACL_TYPE_ACCESS, attr->na_pacl); if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && - !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode)) - attr->na_aclerr = set_posix_acl(&nop_mnt_idmap, + attr->na_dpacl && S_ISDIR(inode->i_mode)) + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, dentry, ACL_TYPE_DEFAULT, attr->na_dpacl); out_fill_attrs: diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 3ff146522556..a3a52fb3552a 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -54,6 +54,8 @@ struct nfsd_attrs { int na_labelerr; /* output */ int na_aclerr; /* output */ + int na_dpaclerr; /* output */ + int na_paclerr; /* output */ }; static inline void nfsd_attrs_free(struct nfsd_attrs *attrs) @@ -67,7 +69,7 @@ static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs) struct iattr *iap = attrs->na_iattr; return (iap->ia_valid || (attrs->na_seclabel && - attrs->na_seclabel->len)); + attrs->na_seclabel->len) || attrs->na_dpacl || attrs->na_pacl); } __be32 nfserrno (int errno); -- 2.34.1 From 6dbd9390db6f5fc2b3b165923daadc57249bc6d8 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 14:59:06 -0700 Subject: [PATCH 09/22] Add support for POSIX draft ACLs for file object creation For NFSv4.2 file object creation, a client may specify POSIX draft ACLs for the file object. This patch adds support to handle these POSIX draft ACLs. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 57 ++++++++++++++++++++++++++++++++++++++-------- fs/nfsd/xdr4.h | 4 ++++ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index ca0d872831fb..8d3c60850696 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -89,6 +89,10 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfserr_attrnotsupp; if ((bmval[0] & FATTR4_WORD0_ACL) && !IS_POSIXACL(d_inode(dentry))) return nfserr_attrnotsupp; + if ((bmval[2] & (FATTR4_WORD2_POSIX_DEFAULT_ACL | + FATTR4_WORD2_POSIX_ACCESS_ACL)) && + !IS_POSIXACL(d_inode(dentry))) + return nfserr_attrnotsupp; if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) && !(exp->ex_flags & NFSEXP_SECURITY_LABEL)) return nfserr_attrnotsupp; @@ -259,8 +263,18 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (host_err) return nfserrno(host_err); - if (is_create_with_attrs(open)) - nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); + if (open->op_acl) { + if (open->op_dpacl || open->op_pacl) + return nfserr_inval; + if (is_create_with_attrs(open)) + nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs); + } else if (is_create_with_attrs(open)) { + /* The dpacl and pacl will get released by nfsd_attrs_free(). */ + attrs.na_dpacl = open->op_dpacl; + attrs.na_pacl = open->op_pacl; + open->op_dpacl = NULL; + open->op_pacl = NULL; + } inode_lock_nested(inode, I_MUTEX_PARENT); @@ -374,6 +388,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; if (attrs.na_aclerr) open->op_bmval[0] &= ~FATTR4_WORD0_ACL; + if (attrs.na_dpaclerr) + open->op_bmval[0] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + if (attrs.na_paclerr) + open->op_bmval[0] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; out: inode_unlock(inode); nfsd_attrs_free(&attrs); @@ -542,8 +560,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, open->op_rqstp = rqstp; /* This check required by spec. */ - if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) - return nfserr_inval; + if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) { + status = nfserr_inval; + goto out_err; + } open->op_created = false; /* @@ -552,8 +572,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, */ if (nfsd4_has_session(cstate) && !test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags) && - open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) - return nfserr_grace; + open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) { + status = nfserr_grace; + goto out_err; + } if (nfsd4_has_session(cstate)) copy_clientid(&open->op_clientid, cstate->session); @@ -640,6 +662,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } nfsd4_cleanup_open_state(cstate, open); nfsd4_bump_seqid(cstate, status); +out_err: + posix_release(open->op_dpacl); + posix_release(open->op_pacl); return status; } @@ -780,6 +805,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd_attrs attrs = { .na_iattr = &create->cr_iattr, .na_seclabel = &create->cr_label, + .na_dpacl = create->cr_dpacl, + .na_pacl = create->cr_pacl, }; struct svc_fh resfh; __be32 status; @@ -789,14 +816,21 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_NOP); if (status) - return status; + goto out_aftermask; status = check_attr_support(rqstp, cstate, create->cr_bmval, nfsd_attrmask); if (status) - return status; + goto out_aftermask; - status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs); + if (create->cr_acl) { + if (create->cr_dpacl || create->cr_pacl) { + status = nfserr_inval; + goto out_aftermask; + } + status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, + &attrs); + } current->fs->umask = create->cr_umask; switch (create->cr_type) { case NF4LNK: @@ -857,12 +891,17 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, create->cr_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; if (attrs.na_aclerr) create->cr_bmval[0] &= ~FATTR4_WORD0_ACL; + if (attrs.na_dpaclerr) + create->cr_bmval[0] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + if (attrs.na_paclerr) + create->cr_bmval[0] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; set_change_info(&create->cr_cinfo, &cstate->current_fh); fh_dup2(&cstate->current_fh, &resfh); out: fh_put(&resfh); out_umask: current->fs->umask = 0; +out_aftermask: nfsd_attrs_free(&attrs); return status; } diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 99e0ad2a8235..ac7e335120cb 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -245,6 +245,8 @@ struct nfsd4_create { int cr_umask; /* request */ struct nfsd4_change_info cr_cinfo; /* response */ struct nfs4_acl *cr_acl; + struct posix_acl *cr_dpacl; + struct posix_acl *cr_pacl; struct xdr_netobj cr_label; }; #define cr_datalen u.link.datalen @@ -397,6 +399,8 @@ struct nfsd4_open { struct nfs4_ol_stateid *op_stp; /* used during processing */ struct nfs4_clnt_odstate *op_odstate; /* used during processing */ struct nfs4_acl *op_acl; + struct posix_acl *op_dpacl; + struct posix_acl *op_pacl; struct xdr_netobj op_label; struct svc_rqst *op_rqstp; }; -- 2.34.1 From da2a4bd9b5306dc27d94ae1efb13529f8a2d8a98 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:00:49 -0700 Subject: [PATCH 10/22] Decode the POSIX draft ACLs for file object creation Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index cfff3da2362e..a628f89d2150 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -429,6 +429,7 @@ dprintk("perm=%d\n", val); if (xdr_stream_decode_u32(argp->xdr, &val) < 0) return nfserr_bad_xdr; +dprintk("val=%d scratchlen=%d scratchbuf=%p\n", val, argp->xdr->scratch.iov_len, argp->xdr->scratch.iov_base); p = xdr_inline_decode(argp->xdr, val); if (!p) return nfserr_bad_xdr; @@ -972,7 +973,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) ARRAY_SIZE(create->cr_bmval), &create->cr_iattr, &create->cr_acl, &create->cr_label, &create->cr_umask, - NULL, NULL); + &create->cr_dpacl, &create->cr_pacl); if (status) return status; @@ -1124,7 +1125,7 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open ARRAY_SIZE(open->op_bmval), &open->op_iattr, &open->op_acl, &open->op_label, &open->op_umask, - NULL, NULL); + &open->op_dpacl, &open->op_pacl); if (status) return status; break; @@ -1143,7 +1144,7 @@ nfsd4_decode_createhow4(struct nfsd4_compoundargs *argp, struct nfsd4_open *open ARRAY_SIZE(open->op_bmval), &open->op_iattr, &open->op_acl, &open->op_label, &open->op_umask, - NULL, NULL); + &open->op_dpacl, &open->op_pacl); if (status) return status; break; -- 2.34.1 From 9921dd29742d3c0dee2d531b1079fb7fb9c84d98 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:02:37 -0700 Subject: [PATCH 11/22] Fix the posix acl release function names Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 8d3c60850696..f83ecd6f134f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -663,8 +663,8 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_cleanup_open_state(cstate, open); nfsd4_bump_seqid(cstate, status); out_err: - posix_release(open->op_dpacl); - posix_release(open->op_pacl); + posix_acl_release(open->op_dpacl); + posix_acl_release(open->op_pacl); return status; } -- 2.34.1 From 8999dfb9b511f7292337d2683b91773e4008238a Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:04:32 -0700 Subject: [PATCH 12/22] Fix a couple of bugs in POSIX ACL decoding 1 - Handle the case where the who is of zero length for ACL_USER or ACL_GROUP. 2 - Correctly calculate the minimum XDR size of the ACL. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index a628f89d2150..e9d2c2f38263 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -417,6 +417,7 @@ nfsd4_decode_posixace4(struct nfsd4_compoundargs *argp, __be32 *p, status; status = nfsacl4_posix_xdrtotag(argp->xdr, &val); +dprintk("decode_posixace val=%d stat=%d\n", val, status); if (status != nfs_ok) return status; ace->e_tag = val; @@ -435,12 +436,18 @@ dprintk("val=%d scratchlen=%d scratchbuf=%p\n", val, argp->xdr->scratch.iov_len, 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); + if (val > 0) + status = nfsd_map_name_to_uid(argp->rqstp, + (char *)p, val, &ace->e_uid); + else + status = nfserr_bad_xdr; break; case ACL_GROUP: - status = nfsd_map_name_to_gid(argp->rqstp, - (char *)p, val, &ace->e_gid); + if (val > 0) + status = nfsd_map_name_to_gid(argp->rqstp, + (char *)p, val, &ace->e_gid); + else + status = nfserr_bad_xdr; } return status; @@ -453,16 +460,18 @@ nfsd4_decode_posix_acl(struct nfsd4_compoundargs *argp, struct posix_acl **acl) __be32 status; u32 count; +dprintk("in nfsd4_decode_posix_acl\n"); if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr; +dprintk("count=%d rem=%d\n", count, xdr_stream_remaining(argp->xdr)); - if (count > xdr_stream_remaining(argp->xdr) / 16) + if (count > xdr_stream_remaining(argp->xdr) / 12) /* - * Even with 4-byte names there wouldn't be + * Even with 0-byte names there wouldn't be * space for that many aces; something fishy is * going on: */ - return nfserr_fbig; + return nfserr_bad_xdr; *acl = posix_acl_alloc(count, GFP_NOFS); if (*acl == NULL) @@ -471,6 +480,7 @@ nfsd4_decode_posix_acl(struct nfsd4_compoundargs *argp, struct posix_acl **acl) (*acl)->a_count = count; for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) { status = nfsd4_decode_posixace4(argp, ace); +dprintk("decode_posixace4=%d\n", status); if (status) { posix_acl_release(*acl); *acl = NULL; -- 2.34.1 From 742550ef4a1c6111fc6eb35aaa24d2975c5b1a8c Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:06:54 -0700 Subject: [PATCH 13/22] Clarify the comment and use 3 * XDR_UNIT instead of 12. No semantics change. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index e9d2c2f38263..69ae395a7bdb 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -465,9 +465,9 @@ dprintk("in nfsd4_decode_posix_acl\n"); return nfserr_bad_xdr; dprintk("count=%d rem=%d\n", count, xdr_stream_remaining(argp->xdr)); - if (count > xdr_stream_remaining(argp->xdr) / 12) + if (count > xdr_stream_remaining(argp->xdr) / (3 * XDR_UNIT)) /* - * Even with 0-byte names there wouldn't be + * Even with 0-byte who strings there wouldn't be * space for that many aces; something fishy is * going on: */ -- 2.34.1 From 1df8385a4e0234a591c91d3c9ea66f96fe460b5d Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:08:28 -0700 Subject: [PATCH 14/22] Fix indentation of switch statements No semantics change. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 78 +++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 69ae395a7bdb..1e65b4764843 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3566,26 +3566,26 @@ 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; + 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; } @@ -3606,27 +3606,27 @@ dprintk("in xdr_nfs4ace_encode tag=%d\n", acep->e_tag); 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: + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: dprintk("at encode 0\n"); - 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); -dprintk("encode_user %d\n", status); - 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: + 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); +dprintk("encode_user %d\n", status); + 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; } dprintk("encode_ace ret nfs_ok\n"); return nfs_ok; -- 2.34.1 From 49992cb210b85172423c315ad6a994c960f4cdc8 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:10:09 -0700 Subject: [PATCH 15/22] Fix the array index for word2 Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index f83ecd6f134f..1411c90860d0 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -389,9 +389,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (attrs.na_aclerr) open->op_bmval[0] &= ~FATTR4_WORD0_ACL; if (attrs.na_dpaclerr) - open->op_bmval[0] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; if (attrs.na_paclerr) - open->op_bmval[0] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; + open->op_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; out: inode_unlock(inode); nfsd_attrs_free(&attrs); @@ -892,9 +892,9 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (attrs.na_aclerr) create->cr_bmval[0] &= ~FATTR4_WORD0_ACL; if (attrs.na_dpaclerr) - create->cr_bmval[0] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; + create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_DEFAULT_ACL; if (attrs.na_paclerr) - create->cr_bmval[0] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; + create->cr_bmval[2] &= ~FATTR4_WORD2_POSIX_ACCESS_ACL; set_change_info(&create->cr_cinfo, &cstate->current_fh); fh_dup2(&cstate->current_fh, &resfh); out: -- 2.34.1 From 1a501887820c4322d539b79dd4ebaf22be312b16 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Mon, 28 Oct 2024 15:12:32 -0700 Subject: [PATCH 16/22] Improve correctness for the ACL_TRUEFORM attribute reply Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 1e65b4764843..70dbe7bd2d73 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3550,8 +3550,12 @@ static __be32 nfsd4_encode_fattr4_xattr_support(struct xdr_stream *xdr, static __be32 nfsd4_encode_fattr4_acl_trueform(struct xdr_stream *xdr, const struct nfsd4_fattr_args *args) { + u32 trueform; - return nfsd4_encode_uint32_t(xdr, ACL_MODEL_POSIX_DRAFT); + trueform = ACL_MODEL_NONE; + if (IS_POSIXACL(d_inode(args->dentry))) + trueform = ACL_MODEL_POSIX_DRAFT; + return nfsd4_encode_uint32_t(xdr, trueform); } static __be32 nfsd4_encode_fattr4_acl_trueform_scope(struct xdr_stream *xdr, -- 2.34.1 From 53c8abfe17dcb9b4774b3069ec7a9075bedc909d Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Tue, 29 Oct 2024 14:51:29 -0700 Subject: [PATCH 17/22] Make sort_pacl_range() global nfsd4_decode_posix_acl() needs to call sort_pacl_range() to sort the ACEs in a decoded POSIX draft ACL attribute. Signed-off-by: Rick Macklem --- fs/nfsd/acl.h | 1 + fs/nfsd/nfs4acl.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index 213774cebeeb..7e061fee2eea 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -51,5 +51,6 @@ int nfsd4_get_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct posix_acl **pacl_ret, struct posix_acl **dpacl_ret); __be32 nfsd4_acl_to_attr(enum nfs_ftype4 type, struct nfs4_acl *acl, struct nfsd_attrs *attr); +void sort_pacl_range(struct posix_acl *pacl, int start, int end); #endif /* LINUX_NFS4_ACL_H */ diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index b57a575da46d..c0eeadc3de74 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -404,7 +404,7 @@ pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2) return false; } -static void +void sort_pacl_range(struct posix_acl *pacl, int start, int end) { int sorted = 0, i; -- 2.34.1 From ea51b4683a086223f74fda42874dc51ad4a97c7f Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Tue, 29 Oct 2024 14:58:31 -0700 Subject: [PATCH 18/22] Call sort_pacl_range() for decoded POSIX draft ACLs posix_valid_check() expects the ACEs in a POSIX draft ACL to be sorted, so sort them. Since sort_pacl_range() uses bubble sort, it will only make a single pass through the ACEs if they are already sorted, which will often be the case. Signed-off-by: Rick Macklem --- fs/nfsd/nfs4xdr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 70dbe7bd2d73..0601558e12cf 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -488,6 +488,16 @@ dprintk("decode_posixace4=%d\n", status); } } + /* + * posix_acl_valid() requires the ACEs to be sorted. + * If they are already sorted, sort_pacl_range() will return + * after one pass through the ACEs, since it implements bubble sort. + * Note that a count == 0 is used to delete a POSIX ACL and a count + * of 1 or 2 will always be found invalid by posix_acl_valid(). + */ + if (count >= 3) + sort_pacl_range(*acl, 0, count - 1); + return nfs_ok; } -- 2.34.1 From 660e8b3e47b574c261ee9ded9c92c8fb79bd6c9c Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Tue, 29 Oct 2024 16:34:02 -0700 Subject: [PATCH 19/22] Fix handling of POSIX draft default ACLs The NFSv4.2 code does a SETATTR with a POSIX draft default ACL of zero ACEs to delete the default ACL. This patch fixes handling of this case. Note that the server code does not handle the case where a file system is of ACL_SCOPE_FILE OBJECT (which means that NFS4 and POSIX draft ACLs are mixed as the "true form" ACL for different file objects in the file system). Signed-off-by: Rick Macklem --- fs/nfsd/nfs4proc.c | 50 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 1411c90860d0..1552b5a1363f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -274,6 +274,7 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, attrs.na_pacl = open->op_pacl; open->op_dpacl = NULL; open->op_pacl = NULL; +dprintk("in open pacl=%p dpacl=%p\n", attrs.na_pacl, attrs.na_dpacl); } inode_lock_nested(inode, I_MUTEX_PARENT); @@ -382,7 +383,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, } set_attr: +dprintk("at nfsd_create_setattr\n"); status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs); +dprintk("aft nfsd_create_setattr=%d paclerr=%d\n", status, attrs.na_paclerr); if (attrs.na_labelerr) open->op_bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; @@ -463,7 +466,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru */ current->fs->umask = open->op_umask; +dprintk("openacl=%p\n", open->op_pacl); status = nfsd4_create_file(rqstp, current_fh, *resfh, open); +dprintk("aft create_file=%d\n", status); current->fs->umask = 0; /* @@ -559,6 +564,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, open->op_filp = NULL; open->op_rqstp = rqstp; +dprintk("nfsd4_open pacl=%p\n", open->op_pacl); /* This check required by spec. */ if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) { status = nfserr_inval; @@ -602,6 +608,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } status = nfsd4_check_open_attributes(rqstp, cstate, open); +dprintk("check_open_attrs=%d\n", status); if (status) goto out; @@ -618,6 +625,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, case NFS4_OPEN_CLAIM_DELEGATE_CUR: case NFS4_OPEN_CLAIM_NULL: status = do_open_lookup(rqstp, cstate, open, &resfh); +dprintk("aft do_open_lookup=%d\n", status); if (status) goto out; break; @@ -1177,14 +1185,35 @@ nfsd4_proc_setacl(struct svc_rqst *rqstp, svc_fh *fh, struct inode *inode, inode_lock(inode); - if (dpacl != NULL) - error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, - ACL_TYPE_DEFAULT, dpacl); - if (!error && pacl != NULL) - error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, - ACL_TYPE_ACCESS, pacl); + if (dpacl != NULL) { +dprintk("proc_setacl dpacl cnt=%d\n", dpacl->a_count); + /* a_count == 0 means delete the ACL. */ + if (dpacl->a_count > 0) + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_DEFAULT, dpacl); + else + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_DEFAULT, NULL); + } + if (!error && pacl != NULL) { +dprintk("proc_setacl pacl cnt=%d\n", pacl->a_count); + /* + * For any file system that is not ACL_SCOPE_FILE_OBJECT, + * a_count == 0 MUST reply nfserr_inval. + * For a file system that is ACL_SCOPE_FILE_OBJECT, + * a_count == 0 means delete the ACL. + * XXX File systems that are ACL_SCOPE_FILE_OBJECT + * are not yet supported. + */ + if (pacl->a_count > 0) + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_ACCESS, pacl); + else + error = -EINVAL; + } inode_unlock(inode); +dprintk("eo proc_setacl=%d\n", error); return nfserrno(error); } @@ -1202,47 +1231,56 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, bool save_no_wcc; int err; +dprintk("nfsd4_setattr iattr=0x%x\n", setattr->sa_iattr.ia_valid); if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, &setattr->sa_stateid, WR_STATE, NULL, NULL); +dprintk("preproc=%d\n", status); if (status) goto out_err; } err = fh_want_write(&cstate->current_fh); if (err) { status = nfserrno(err); +dprintk("want_write=%d\n", status); goto out_err; } status = nfs_ok; status = check_attr_support(rqstp, cstate, setattr->sa_bmval, nfsd_attrmask); +dprintk("checkattr=%d\n", status); if (status) goto out; if (setattr->sa_acl && (setattr->sa_dpacl || setattr->sa_pacl)) { status = nfserr_inval; +dprintk("err_inval\n"); goto out; } inode = cstate->current_fh.fh_dentry->d_inode; status = nfsd4_acl_to_attr(S_ISDIR(inode->i_mode) ? NF4DIR : NF4REG, setattr->sa_acl, &attrs); +dprintk("acltoattr=%d\n", status); if (status) goto out; save_no_wcc = cstate->current_fh.fh_no_wcc; cstate->current_fh.fh_no_wcc = true; status = nfsd_setattr(rqstp, &cstate->current_fh, &attrs, NULL); +dprintk("nfsd_setattr=%d\n", status); cstate->current_fh.fh_no_wcc = save_no_wcc; if (!status && (setattr->sa_dpacl != NULL || setattr->sa_pacl != NULL)) status = nfsd4_proc_setacl(rqstp, &cstate->current_fh, inode, setattr->sa_dpacl, setattr->sa_pacl); +dprintk("proc_setacl=%d\n", status); if (!status) status = nfserrno(attrs.na_labelerr); if (!status) status = nfserrno(attrs.na_aclerr); +dprintk("afterr=%d\n", status); out: nfsd_attrs_free(&attrs); fh_drop_write(&cstate->current_fh); -- 2.34.1 From 0a8f3ee2543fa97882e8df3f28b16eee561b9bd5 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Tue, 29 Oct 2024 16:40:16 -0700 Subject: [PATCH 20/22] Fix handling of zero length ACLs for file object creation Although it seems unlikely that a NFSv4.2 client would specify deletion of an ACL for file object creation, this patch applies the changes needed to support that for a POSIX draft default ACL. Signed-off-by: Rick Macklem --- fs/nfsd/vfs.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 022dedb0be0c..a06a73e725be 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -501,6 +501,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, bool size_change = (iap->ia_valid & ATTR_SIZE); int retries; +dprintk("nfsd_setattr pacl=%p valid=0x%x\n", attr->na_pacl, iap->ia_valid); if (iap->ia_valid & ATTR_SIZE) { accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; ftype = S_IFREG; @@ -582,15 +583,37 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, if (attr->na_seclabel && attr->na_seclabel->len) attr->na_labelerr = security_inode_setsecctx(dentry, attr->na_seclabel->data, attr->na_seclabel->len); - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) - attr->na_paclerr = set_posix_acl(&nop_mnt_idmap, - dentry, ACL_TYPE_ACCESS, - attr->na_pacl); - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && - attr->na_dpacl && S_ISDIR(inode->i_mode)) - attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_dpacl) { + if (!S_ISDIR(inode->i_mode)) + attr->na_dpaclerr = -EINVAL; + else if (attr->na_dpacl->a_count > 0) + /* a_count == 0 means delete the ACL. */ + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, dentry, ACL_TYPE_DEFAULT, attr->na_dpacl); + else + attr->na_dpaclerr = set_posix_acl(&nop_mnt_idmap, + dentry, ACL_TYPE_DEFAULT, + NULL); + } + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl) { +dprintk("at set_posix_acl\n"); + /* + * For any file system that is not ACL_SCOPE_FILE_OBJECT, + * a_count == 0 MUST reply nfserr_inval. + * For a file system that is ACL_SCOPE_FILE_OBJECT, + * a_count == 0 deletes the ACL. + * XXX File systems that are ACL_SCOPE_FILE_OBJECT + * are not yet supported. + */ + if (attr->na_pacl->a_count > 0) + attr->na_paclerr = set_posix_acl(&nop_mnt_idmap, + dentry, ACL_TYPE_ACCESS, + attr->na_pacl); + else + attr->na_paclerr = -EINVAL; +dprintk("set_posix_acl=%d\n", attr->na_paclerr); + } out_fill_attrs: /* * RFC 1813 Section 3.3.2 does not mandate that an NFS server @@ -1418,6 +1441,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, */ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) iap->ia_valid &= ~(ATTR_UID|ATTR_GID); +dprintk("in create_set pacl=%p valid=0x%x\n", attrs->na_pacl, iap->ia_valid); /* * Callers expect new file metadata to be committed even @@ -1427,6 +1451,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, status = nfsd_setattr(rqstp, resfhp, attrs, NULL); else status = nfserrno(commit_metadata(resfhp)); +dprintk("aft nfsd_setattr=%d\n", status); /* * Transactional filesystems had a chance to commit changes -- 2.34.1