The branch main has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=50e733f19b37421a64045e131766ad79d08497a4
commit 50e733f19b37421a64045e131766ad79d08497a4 Author: Rick Macklem <rmack...@freebsd.org> AuthorDate: 2025-06-30 14:51:01 +0000 Commit: Rick Macklem <rmack...@freebsd.org> CommitDate: 2025-06-30 14:51:01 +0000 nfscl: Use delegation ACE when mounted with nocto For NFSv4.1/4.2, there is an ACE in the delegation reply. Without this patch, this ACE is ignored by the NFSv4 client. This patch enables use of the ACE to avoid the need for Access RPCs when the "nocto" option is specified. This requires a NFSv4.1/4.2 server that does not reply with a bogus ACE that is too generous w.r.t. access permissions. Note that the recent commit 0d51adee3072 added use of the NFSv4 ACL for generation of the ACE in the reply. This patch might be needed for this client change to work correctly if NFSv4 ACLs are being used on the NFSv4.1/4.2 exported file systems. This only affects NFSv4 mounts with the "nocto" mount option and only if NFSv4 servers are issuing delegations with ACEs that specify access. Some NFSv4 servers, such as the Linux knfsd reply with ACEs that do not allow any access, so this patch has no effect for them. --- sys/fs/nfs/nfs_commonacl.c | 2 +- sys/fs/nfs/nfs_var.h | 1 + sys/fs/nfsclient/nfs_clstate.c | 51 ++++++++++++++++++++++++++++++++++++++++++ sys/fs/nfsclient/nfs_clvnops.c | 12 ++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/sys/fs/nfs/nfs_commonacl.c b/sys/fs/nfs/nfs_commonacl.c index 69afa8d41712..bba1d8821a9b 100644 --- a/sys/fs/nfs/nfs_commonacl.c +++ b/sys/fs/nfs/nfs_commonacl.c @@ -65,7 +65,7 @@ nfsrv_dissectace(struct nfsrv_descript *nd, struct acl_entry *acep, goto nfsmout; } else if (len == 0) { /* Netapp filers return a 0 length who for nil users */ - acep->ae_tag = ACL_UNDEFINED_TAG; + acep->ae_tag = ACL_EVERYONE; /* Avoid panics. */ acep->ae_id = ACL_UNDEFINED_ID; acep->ae_perm = (acl_perm_t)0; acep->ae_entry_type = ACL_ENTRY_TYPE_DENY; diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index f59a898369e8..3b6c1ec90c06 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -658,6 +658,7 @@ void nfscl_freelayout(struct nfscllayout *); void nfscl_freeflayout(struct nfsclflayout *); void nfscl_freedevinfo(struct nfscldevinfo *); int nfscl_layoutcommit(vnode_t, NFSPROC_T *); +int nfscl_delegacecheck(struct vnode *, accmode_t, struct ucred *); void nfscl_startdelegrecall(struct nfsclclient *, struct nfsfh *); /* nfs_clport.c */ diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index fe3682ae437e..1ae5ed1a75ca 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -5953,6 +5953,57 @@ tryagain: return (0); } +/* + * Check access against a delegation ace. + * Return EINVAL for any case where the check cannot be completed. + */ +int +nfscl_delegacecheck(struct vnode *vp, accmode_t accmode, struct ucred *cred) +{ + struct nfsclclient *clp; + struct nfscldeleg *dp; + struct nfsnode *np; + struct nfsmount *nmp; + struct acl *aclp; + int error; + + np = VTONFS(vp); + nmp = VFSTONFS(vp->v_mount); + if (!NFSHASNFSV4(nmp) || !NFSHASNFSV4N(nmp) || vp->v_type != VREG) + return (EINVAL); + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) { + NFSUNLOCKMNT(nmp); + return (EINVAL); + } + NFSUNLOCKMNT(nmp); + aclp = acl_alloc(M_WAITOK); + NFSLOCKCLSTATE(); + clp = nfscl_findcl(nmp); + if (clp == NULL) { + NFSUNLOCKCLSTATE(); + acl_free(aclp); + return (EINVAL); + } + dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); + if (dp != NULL && (dp->nfsdl_flags & (NFSCLDL_RECALL | + NFSCLDL_DELEGRET)) == 0) { + memcpy(&aclp->acl_entry[0], &dp->nfsdl_ace, + sizeof(struct acl_entry)); + NFSUNLOCKCLSTATE(); + aclp->acl_cnt = 1; + error = vaccess_acl_nfs4(vp->v_type, np->n_vattr.na_uid, + np->n_vattr.na_gid, aclp, accmode, cred); + acl_free(aclp); + if (error == 0 || error == EACCES) + return (error); + } else { + NFSUNLOCKCLSTATE(); + acl_free(aclp); + } + return (EINVAL); +} + /* * Start the recall of a delegation. Called for CB_RECALL and REMOVE * when nlink == 0 after the REMOVE. diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index 84046cdb503e..0049d7edca33 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -480,6 +480,18 @@ nfs_access(struct vop_access_args *ap) break; } } + + /* + * For NFSv4, check for a delegation with an Allow ACE, to see + * if that permits access. + */ + if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOCTO) != 0) { + error = nfscl_delegacecheck(vp, ap->a_accmode, ap->a_cred); + if (error == 0) + return (error); + error = 0; + } + /* * For nfs v3 or v4, check to see if we have done this recently, and if * so return our cached result instead of making an ACCESS call.