The branch main has been updated by rmacklem:

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

commit 4fc11c92d324c9099ecc28f25a96591a2ff6105c
Author:     Rick Macklem <[email protected]>
AuthorDate: 2026-01-15 23:27:22 +0000
Commit:     Rick Macklem <[email protected]>
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);
                }

Reply via email to