git: 3b6d4c6cdffd - main - nfs: Add support for the NFSv4 archive attribute

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Wed, 22 Oct 2025 21:20:16 UTC
The branch main has been updated by rmacklem:

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

commit 3b6d4c6cdffd6e37b8fa6e0b0fe3067210582c9c
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2025-10-22 21:16:48 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2025-10-22 21:16:48 +0000

    nfs: Add support for the NFSv4 archive attribute
    
    The archive attribute (UF_ARCHIVE) is listed as deprecated
    in RFC8881.  However, the Windows NFSv4.1 client folk disagree
    with this and say they need support for it.
    
    This patch adds support for it, in a manner similar to what
    is done for UF_SYSTEM and UF_HIDDEN.
    
    MFC after:      2 weeks
---
 sys/fs/nfs/nfs_commonsubs.c     | 24 +++++++++++++++++++++---
 sys/fs/nfs/nfsproto.h           |  4 ++++
 sys/fs/nfsclient/nfs_clrpcops.c |  3 +++
 sys/fs/nfsclient/nfs_clvnops.c  |  8 ++++++--
 sys/fs/nfsserver/nfs_nfsdport.c | 11 +++++++----
 sys/fs/nfsserver/nfs_nfsdserv.c | 12 +++++++++++-
 6 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index ec95716ea485..8d506a5643a9 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -641,6 +641,7 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
 		if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL)
 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SIZE);
 		if ((flags & NFSSATTR_FULL) && vap->va_flags != VNOVAL) {
+			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE);
 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
 		}
@@ -1672,9 +1673,17 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
 			attrsum += NFSX_UNSIGNED;
 			break;
 		case NFSATTRBIT_ARCHIVE:
-			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
-			if (compare && !(*retcmpp))
-				*retcmpp = NFSERR_ATTRNOTSUPP;
+			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+			if (compare) {
+				if (!(*retcmpp) && ((*tl == newnfs_true &&
+				    (nap->na_flags & UF_ARCHIVE) == 0) ||
+				    (*tl == newnfs_false &&
+				     (nap->na_flags & UF_ARCHIVE) != 0)))
+					*retcmpp = NFSERR_NOTSAME;
+			} else if (nap != NULL) {
+				if (*tl == newnfs_true)
+					nap->na_flags |= UF_ARCHIVE;
+			}
 			attrsum += NFSX_UNSIGNED;
 			break;
 		case NFSATTRBIT_CANSETTIME:
@@ -2804,6 +2813,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 			if (!has_hiddensystem) {
 			    NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
 			    NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
+			    NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE);
 			}
 			if (clone_blksize == 0)
 			    NFSCLRBIT_ATTRBIT(&attrbits,
@@ -2888,6 +2898,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
 			retnum += NFSX_UNSIGNED;
 			break;
+		case NFSATTRBIT_ARCHIVE:
+			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+			if ((vap->va_flags & UF_ARCHIVE) != 0)
+				*tl = newnfs_true;
+			else
+				*tl = newnfs_false;
+			retnum += NFSX_UNSIGNED;
+			break;
 		case NFSATTRBIT_CANSETTIME:
 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
 			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h
index d628108bdc1a..13fec8a102a3 100644
--- a/sys/fs/nfs/nfsproto.h
+++ b/sys/fs/nfs/nfsproto.h
@@ -1135,6 +1135,7 @@ struct nfsv3_sattr {
  	NFSATTRBM_RDATTRERROR |						\
  	NFSATTRBM_ACL |							\
  	NFSATTRBM_ACLSUPPORT |						\
+	NFSATTRBM_ARCHIVE |						\
  	NFSATTRBM_CANSETTIME |						\
  	NFSATTRBM_CASEINSENSITIVE |					\
  	NFSATTRBM_CASEPRESERVING |					\
@@ -1217,6 +1218,7 @@ struct nfsv3_sattr {
 #define	NFSATTRBIT_SETABLE0						\
 	(NFSATTRBM_SIZE |						\
 	NFSATTRBM_HIDDEN |						\
+	NFSATTRBM_ARCHIVE |						\
 	NFSATTRBM_ACL)
 #define	NFSATTRBIT_SETABLE1						\
  	(NFSATTRBM_MODE |						\
@@ -1262,6 +1264,7 @@ struct nfsv3_sattr {
  	NFSATTRBM_CHANGE |						\
  	NFSATTRBM_SIZE |						\
  	NFSATTRBM_FSID |						\
+	NFSATTRBM_ARCHIVE |						\
  	NFSATTRBM_FILEID |						\
 	NFSATTRBM_HIDDEN |						\
  	NFSATTRBM_MAXREAD)
@@ -1298,6 +1301,7 @@ struct nfsv3_sattr {
  	NFSATTRBM_CHANGE |						\
  	NFSATTRBM_SIZE |						\
  	NFSATTRBM_FSID |						\
+	NFSATTRBM_ARCHIVE |						\
  	NFSATTRBM_FILEID |						\
 	NFSATTRBM_HIDDEN |						\
  	NFSATTRBM_MAXREAD)
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index ad9404a18fc8..d3b83eb8b94b 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -4162,9 +4162,12 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
 		    NFSATTRBIT_TIMECREATE))
 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
 		if (!NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
+		    NFSATTRBIT_ARCHIVE) ||
+		    !NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
 		    NFSATTRBIT_HIDDEN) ||
 		    !NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
 		    NFSATTRBIT_SYSTEM)) {
+			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE);
 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
 		}
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index e9ae91e046e7..f80cf30669ca 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -1081,12 +1081,14 @@ nfs_setattr(struct vop_setattr_args *ap)
 #endif
 
 	/*
-	 * Only setting of UF_HIDDEN and UF_SYSTEM are supported and
+	 * Only setting of UF_ARCHIVE, UF_HIDDEN and UF_SYSTEM are supported and
 	 * only for NFSv4 servers that support them.
 	 */
 	nmp = VFSTONFS(vp->v_mount);
 	if (vap->va_flags != VNOVAL && (!NFSHASNFSV4(nmp) ||
-	    (vap->va_flags & ~(UF_HIDDEN | UF_SYSTEM)) != 0 ||
+	    (vap->va_flags & ~(UF_ARCHIVE | UF_HIDDEN | UF_SYSTEM)) != 0 ||
+	    ((vap->va_flags & UF_ARCHIVE) != 0 &&
+	     !NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, NFSATTRBIT_ARCHIVE)) ||
 	    ((vap->va_flags & UF_HIDDEN) != 0 &&
 	     !NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, NFSATTRBIT_HIDDEN)) ||
 	    ((vap->va_flags & UF_SYSTEM) != 0 &&
@@ -4835,6 +4837,8 @@ nfs_pathconf(struct vop_pathconf_args *ap)
 		break;
 	case _PC_HAS_HIDDENSYSTEM:
 		if (NFS_ISV4(vp) && NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+		    NFSATTRBIT_ARCHIVE) &&
+		    NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
 		    NFSATTRBIT_HIDDEN) &&
 		    NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
 		    NFSATTRBIT_SYSTEM))
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index eb6ba285f8fe..9fe3f4426124 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -3193,7 +3193,8 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
 		bitpos = NFSATTRBIT_MAX;
 	} else {
 		bitpos = 0;
-		if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN) ||
+		if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_ARCHIVE) ||
+		    NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN) ||
 		    NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SYSTEM))
 			nvap->na_flags = 0;
 	}
@@ -3226,9 +3227,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
 			attrsum += aclsize;
 			break;
 		case NFSATTRBIT_ARCHIVE:
-			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
-			if (!nd->nd_repstat)
-				nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+			if (nd->nd_repstat == 0) {
+				if (*tl == newnfs_true)
+					nvap->na_flags |= UF_ARCHIVE;
+			}
 			attrsum += NFSX_UNSIGNED;
 			break;
 		case NFSATTRBIT_HIDDEN:
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 921ea4887af1..6f3447f26620 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -436,6 +436,7 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
 
 	/* For NFSv4, only va_uid and va_flags is used from nva2. */
 	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
+	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ARCHIVE);
 	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
 	NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
 	preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
@@ -569,8 +570,15 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
 		}
 	    }
 	    if (!nd->nd_repstat &&
-		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN) ||
+		(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE) ||
+		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN) ||
 		 NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))) {
+		if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE)) {
+		    if ((nva.na_flags & UF_ARCHIVE) != 0)
+			oldflags |= UF_ARCHIVE;
+		    else
+			oldflags &= ~UF_ARCHIVE;
+		}
 		if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN)) {
 		    if ((nva.na_flags & UF_HIDDEN) != 0)
 			oldflags |= UF_HIDDEN;
@@ -588,6 +596,8 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
 		    exp);
 		if (!nd->nd_repstat) {
+		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ARCHIVE))
+			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ARCHIVE);
 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN))
 			NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
 		    if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))