git: 4fc11c92d324 - main - nfsd: Fix handling of attributes during Open/Create/Exclusive_41

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Thu, 15 Jan 2026 23:30:47 UTC
The branch main has been updated by rmacklem:

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

commit 4fc11c92d324c9099ecc28f25a96591a2ff6105c
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-01-15 23:27:22 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-01-15 23:27:22 +0000

    nfsd: Fix handling of attributes during Open/Create/Exclusive_41
    
    When an NFSv4.n client specifies settings for attributes other
    mode during a Open/Create/Exclusive_41, these other attributes
    were not being set.
    
    This patch resolves the problem by calling nfsrv_fixsattr()
    after the VOP_CREATE() call in nfsvno_open() for this case.
    
    There is no extant NFSv4.n client that currently does this,
    as far as I know.
    
    MFC after:      2 weeks
---
 sys/fs/nfs/nfs.h                |  5 +++++
 sys/fs/nfs/nfs_var.h            |  2 +-
 sys/fs/nfs/nfsdport.h           |  2 ++
 sys/fs/nfsserver/nfs_nfsdport.c | 46 ++++++++++++++++++++++++++++++++++-------
 sys/fs/nfsserver/nfs_nfsdserv.c | 13 +++++++-----
 sys/fs/nfsserver/nfs_nfsdsubs.c |  6 +++---
 6 files changed, 57 insertions(+), 17 deletions(-)

diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h
index ecff9b8e6849..7903542be91d 100644
--- a/sys/fs/nfs/nfs.h
+++ b/sys/fs/nfs/nfs.h
@@ -872,6 +872,11 @@ typedef enum { UNKNOWN=0, DELETED=1, NLINK_ZERO=2, VALID=3 } nfsremove_status;
 #define	SUPPACL_NFSV4	1
 #define	SUPPACL_POSIX	2
 
+/* Values NFSv4 uses for exclusive_flag. */
+#define	NFSV4_EXCLUSIVE_NONE	0
+#define	NFSV4_EXCLUSIVE		1
+#define	NFSV4_EXCLUSIVE_41	2
+
 #endif	/* _KERNEL */
 
 #endif	/* _NFS_NFS_H */
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 0211acf7f00b..28088c12d7e7 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -410,7 +410,7 @@ int nfsv4_strtogid(struct nfsrv_descript *, u_char *, int, gid_t *);
 int nfsrv_checkuidgid(struct nfsrv_descript *, struct nfsvattr *);
 void nfsrv_fixattr(struct nfsrv_descript *, vnode_t,
     struct nfsvattr *, NFSACL_T *, NFSACL_T *, NFSPROC_T *, nfsattrbit_t *,
-    struct nfsexstuff *);
+    bool);
 int nfsrv_errmoved(int);
 int nfsrv_putreferralattr(struct nfsrv_descript *, nfsattrbit_t *,
     struct nfsreferral *, int, int *);
diff --git a/sys/fs/nfs/nfsdport.h b/sys/fs/nfs/nfsdport.h
index c863741746c5..6439ef921d29 100644
--- a/sys/fs/nfs/nfsdport.h
+++ b/sys/fs/nfs/nfsdport.h
@@ -46,6 +46,8 @@
 #define	NFSVNO_ISSETATIME(n)		((n)->na_atime.tv_sec != VNOVAL)
 #define	NFSVNO_NOTSETMTIME(n)		((n)->na_mtime.tv_sec == VNOVAL)
 #define	NFSVNO_ISSETMTIME(n)		((n)->na_mtime.tv_sec != VNOVAL)
+#define	NFSVNO_NOTSETFLAGS(n)		((n)->na_flags == VNOVAL)
+#define	NFSVNO_ISSETFLAGS(n)		((n)->na_flags != VNOVAL)
 
 /*
  * This structure acts as a "catch-all" for information that
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 1e215b52e835..833203cd86fc 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -1972,6 +1972,7 @@ nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
     NFSACL_T *aclp, NFSACL_T *daclp, nfsattrbit_t *attrbitp, struct ucred *cred,
     bool done_namei, struct nfsexstuff *exp, struct vnode **vpp)
 {
+	struct vattr va;
 	struct vnode *vp = NULL;
 	u_quad_t tempsize;
 	struct nfsexstuff nes;
@@ -2018,23 +2019,52 @@ nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
 			    &ndp->ni_vp : NULL, false);
 			nfsvno_relpathbuf(ndp);
 			if (!nd->nd_repstat) {
-				if (*exclusive_flagp) {
-					*exclusive_flagp = 0;
-					NFSVNO_ATTRINIT(nvap);
-					nvap->na_atime.tv_sec = cverf[0];
-					nvap->na_atime.tv_nsec = cverf[1];
+				if (*exclusive_flagp != NFSV4_EXCLUSIVE_NONE) {
+					VATTR_NULL(&va);
+					va.va_atime.tv_sec = cverf[0];
+					va.va_atime.tv_nsec = cverf[1];
 					nd->nd_repstat = VOP_SETATTR(ndp->ni_vp,
-					    &nvap->na_vattr, cred);
+					    &va, cred);
 					if (nd->nd_repstat != 0) {
 						vput(ndp->ni_vp);
 						ndp->ni_vp = NULL;
 						nd->nd_repstat = NFSERR_NOTSUPP;
-					} else
+					} else {
+						/*
+						 * Few clients set these
+						 * attributes in Open/Create
+						 * Exclusive_41.  If this
+						 * changes, this should include
+						 * setting atime, instead of
+						 * the above.
+						 */
+						if (*exclusive_flagp ==
+						    NFSV4_EXCLUSIVE_41 &&
+						    (NFSISSET_ATTRBIT(attrbitp,
+						     NFSATTRBIT_OWNER) ||
+						     NFSISSET_ATTRBIT(attrbitp,
+						     NFSATTRBIT_OWNERGROUP) ||
+						     NFSISSET_ATTRBIT(attrbitp,
+						     NFSATTRBIT_TIMEMODIFYSET)||
+						     NFSISSET_ATTRBIT(attrbitp,
+						     NFSATTRBIT_ARCHIVE) ||
+						     NFSISSET_ATTRBIT(attrbitp,
+						     NFSATTRBIT_HIDDEN) ||
+						     NFSISSET_ATTRBIT(attrbitp,
+						     NFSATTRBIT_SYSTEM) ||
+						     aclp != NULL ||
+						     daclp != NULL))
+							nfsrv_fixattr(nd,
+							    ndp->ni_vp, nvap,
+							    aclp, daclp, p,
+							    attrbitp, true);
 						NFSSETBIT_ATTRBIT(attrbitp,
 						    NFSATTRBIT_TIMEACCESS);
+					}
+					*exclusive_flagp = NFSV4_EXCLUSIVE_NONE;
 				} else {
 					nfsrv_fixattr(nd, ndp->ni_vp, nvap,
-					    aclp, daclp, p, attrbitp, exp);
+					    aclp, daclp, p, attrbitp, false);
 				}
 			}
 			vp = ndp->ni_vp;
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 3eb3471d9ac9..b5f5b9bec9fc 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -1608,7 +1608,7 @@ nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
 	nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
 	if (!nd->nd_repstat) {
 		vp = named.ni_vp;
-		nfsrv_fixattr(nd, vp, &nva, aclp, daclp, p, &attrbits, exp);
+		nfsrv_fixattr(nd, vp, &nva, aclp, daclp, p, &attrbits, false);
 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
 		if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
 			nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
@@ -2120,7 +2120,7 @@ nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
 	    !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
 	if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
 		nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, NULL, p, attrbitp,
-		    exp);
+		    false);
 		if (nd->nd_flag & ND_NFSV3) {
 			nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
 			if (!nd->nd_repstat)
@@ -2255,7 +2255,7 @@ nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
 	    nd->nd_cred, p, exp);
 	if (!nd->nd_repstat) {
 		vp = ndp->ni_vp;
-		nfsrv_fixattr(nd, vp, nvap, aclp, daclp, p, attrbitp, exp);
+		nfsrv_fixattr(nd, vp, nvap, aclp, daclp, p, attrbitp, false);
 		nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
 		if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
 			nd->nd_repstat = nfsvno_getattr(vp, nvap, nd, p, 1,
@@ -2964,7 +2964,8 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 	u_int32_t *tl;
 	int i, retext;
 	struct nfsstate *stp = NULL;
-	int error = 0, create, claim, exclusive_flag = 0, override;
+	int error = 0, create, claim, override;
+	int exclusive_flag = NFSV4_EXCLUSIVE_NONE;
 	u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
 	int how = NFSCREATE_UNCHECKED;
 	int32_t cverf[2], tverf[2] = { 0, 0 };
@@ -3229,6 +3230,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 		    case NFSCREATE_EXCLUSIVE:
 			if (nd->nd_repstat == 0 && named.ni_vp == NULL)
 				nva.na_mode = 0;
+			exclusive_flag = NFSV4_EXCLUSIVE;
 			/* FALLTHROUGH */
 		    case NFSCREATE_EXCLUSIVE41:
 			if (nd->nd_repstat == 0 && named.ni_vp != NULL) {
@@ -3244,7 +3246,8 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 				if (nd->nd_repstat != 0)
 					done_namei = true;
 			}
-			exclusive_flag = 1;
+			if (how == NFSCREATE_EXCLUSIVE41)
+				exclusive_flag = NFSV4_EXCLUSIVE_41;
 			break;
 		    }
 		}
diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c
index c8c78d98be72..fdedf959f0e5 100644
--- a/sys/fs/nfsserver/nfs_nfsdsubs.c
+++ b/sys/fs/nfsserver/nfs_nfsdsubs.c
@@ -1645,7 +1645,7 @@ out:
 void
 nfsrv_fixattr(struct nfsrv_descript *nd, vnode_t vp,
     struct nfsvattr *nvap, NFSACL_T *aclp, NFSACL_T *daclp, NFSPROC_T *p,
-    nfsattrbit_t *attrbitp, struct nfsexstuff *exp)
+    nfsattrbit_t *attrbitp, bool atime_done)
 {
 	int change = 0;
 	struct nfsvattr nva;
@@ -1675,7 +1675,7 @@ nfsrv_fixattr(struct nfsrv_descript *nd, vnode_t vp,
 		}
 	}
 	if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_TIMEACCESSSET) &&
-	    NFSVNO_ISSETATIME(nvap)) {
+	    !atime_done && NFSVNO_ISSETATIME(nvap)) {
 		nva.na_atime = nvap->na_atime;
 		change++;
 		NFSSETBIT_ATTRBIT(&nattrbits, NFSATTRBIT_TIMEACCESSSET);
@@ -1736,7 +1736,7 @@ nfsrv_fixattr(struct nfsrv_descript *nd, vnode_t vp,
 		}
 	}
 	if (change) {
-		error = nfsvno_setattr(vp, &nva, nd->nd_cred, p, exp);
+		error = nfsvno_setattr(vp, &nva, nd->nd_cred, p, NULL);
 		if (error) {
 			NFSCLRALL_ATTRBIT(attrbitp, &nattrbits);
 		}