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); }
