git: a6d57f312f18 - main - nfsd: Fix handling of hidden/system during Open/Create

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Fri, 09 Jan 2026 00:33:27 UTC
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=a6d57f312f18bbeeda8a34e99d0a662b0db9a190

commit a6d57f312f18bbeeda8a34e99d0a662b0db9a190
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-01-08 16:27:32 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-01-08 16:27:32 +0000

    nfsd: Fix handling of hidden/system during Open/Create
    
    When an NFSv4.n client specifies settings for the archive,
    hidden and/or system attributes during a Open/Create, the
    Open/Create fails for ZFS.  This is caused by ZFS doing
    a secpolicy_xvattr() call, which fails for non-root.
    If this check is bypassed, ZFS panics.
    
    This patch resolves the problem by disabling va_flags
    for the VOP_CREATE() call in the NFSv4.n server and
    then setting the flags with a subsequent VOP_SETATTR().
    
    This problem only affects FreeBSD-15 and main, since the
    archive, system and hidden attributes are not enabled
    for FreeBSD-14.
    
    I think a similar problem exists for the NFSv4.n
    Open/Create/Exclusive_41, but that will be resolved
    in a future commit.
    
    Note that the Linux, Solaris and FreeBSD clients
    do not set archive, hidden or system for Open/Create,
    so the bug does not affect mounts from those clients.
    
    PR:     292283
    Reported by:    Aurelien Couderc <aurelien.couderc2002@gmail.com>
    Tested by:      Aurelien Couderc <aurelien.couderc2002@gmail.com>
    MFC after:      2 weeks
---
 sys/fs/nfsserver/nfs_nfsdport.c | 11 +++++++++++
 sys/fs/nfsserver/nfs_nfsdsubs.c | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 7d64f211b058..1e215b52e835 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -1977,6 +1977,7 @@ nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
 	struct nfsexstuff nes;
 	struct thread *p = curthread;
 	uint32_t oldrepstat;
+	u_long savflags;
 
 	if (ndp->ni_vp == NULL) {
 		/*
@@ -1991,6 +1992,15 @@ nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
 	}
 	if (!nd->nd_repstat) {
 		if (ndp->ni_vp == NULL) {
+			/*
+			 * Most file systems ignore va_flags for
+			 * VOP_CREATE(), however setting va_flags
+			 * for VOP_CREATE() causes problems for ZFS.
+			 * So disable them and let nfsrv_fixattr()
+			 * do them, as required.
+			 */
+			savflags = nvap->na_flags;
+			nvap->na_flags = VNOVAL;
 			nd->nd_repstat = VOP_CREATE(ndp->ni_dvp,
 			    &ndp->ni_vp, &ndp->ni_cnd, &nvap->na_vattr);
 			/* For a pNFS server, create the data file on a DS. */
@@ -2003,6 +2013,7 @@ nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
 				nfsrv_pnfscreate(ndp->ni_vp, &nvap->na_vattr,
 				    cred, p);
 			}
+			nvap->na_flags = savflags;
 			VOP_VPUT_PAIR(ndp->ni_dvp, nd->nd_repstat == 0 ?
 			    &ndp->ni_vp : NULL, false);
 			nfsvno_relpathbuf(ndp);
diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c
index ea8382e4282a..c8c78d98be72 100644
--- a/sys/fs/nfsserver/nfs_nfsdsubs.c
+++ b/sys/fs/nfsserver/nfs_nfsdsubs.c
@@ -1697,6 +1697,44 @@ nfsrv_fixattr(struct nfsrv_descript *nd, vnode_t vp,
 			NFSCLRBIT_ATTRBIT(attrbitp, NFSATTRBIT_OWNERGROUP);
 		}
 	}
+
+	/*
+	 * For archive, ZFS sets it by default for new files,
+	 * so if specified, it must be set or cleared.
+	 * For hidden and system, no file system sets them
+	 * by default upon creation, so they only need to be
+	 * set and not cleared.
+	 */
+	if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_ARCHIVE)) {
+		if (nva.na_flags == VNOVAL)
+			nva.na_flags = 0;
+		if ((nvap->na_flags & UF_ARCHIVE) != 0)
+			nva.na_flags |= UF_ARCHIVE;
+		change++;
+		NFSSETBIT_ATTRBIT(&nattrbits, NFSATTRBIT_ARCHIVE);
+	}
+	if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN)) {
+		if ((nvap->na_flags & UF_HIDDEN) != 0) {
+			if (nva.na_flags == VNOVAL)
+				nva.na_flags = 0;
+			nva.na_flags |= UF_HIDDEN;
+			change++;
+			NFSSETBIT_ATTRBIT(&nattrbits, NFSATTRBIT_HIDDEN);
+		} else {
+			NFSCLRBIT_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN);
+		}
+	}
+	if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SYSTEM)) {
+		if ((nvap->na_flags & UF_SYSTEM) != 0) {
+			if (nva.na_flags == VNOVAL)
+				nva.na_flags = 0;
+			nva.na_flags |= UF_SYSTEM;
+			change++;
+			NFSSETBIT_ATTRBIT(&nattrbits, NFSATTRBIT_SYSTEM);
+		} else {
+			NFSCLRBIT_ATTRBIT(attrbitp, NFSATTRBIT_SYSTEM);
+		}
+	}
 	if (change) {
 		error = nfsvno_setattr(vp, &nva, nd->nd_cred, p, exp);
 		if (error) {