nfsv4: Add support for the NFSv4 hidden and system attributes

There now appears to be a use for the NFSv4 hidden and system
attributes for the Windows ms-nfs41 client.  As such, this
patch implements these using the UF_HIDDEN and UF_SYSTEM
flags.  Commit afd5bc6309 added support for _PC_HAS_HIDDENSYSTEM,
to VOP_PATHCONF(), which is used by the server to check for
support of the UF_HIDDEN and UF_SYSTEM flags.

This patch only affects NFSv4 and only when the client/server
on the other end supports the hidden and system attributes.
This commit is contained in:
Rick Macklem 2025-07-06 15:49:53 -07:00
parent 0d011ec653
commit c5d72d29fe
6 changed files with 152 additions and 27 deletions

View file

@ -630,6 +630,10 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP);
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_HIDDEN);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
}
if (vap->va_atime.tv_sec != VNOVAL)
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
if (vap->va_mtime.tv_sec != VNOVAL)
@ -1314,6 +1318,7 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
u_int32_t freenum = 0, tuint;
u_int64_t uquad = 0, thyp, thyp2;
uint16_t tui16;
long has_pathconf;
#ifdef QUOTA
struct dqblk dqb;
uid_t savuid;
@ -1421,6 +1426,16 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACL);
NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACLSUPPORT);
}
/* Some filesystems do not support uf_hidden */
if (vp == NULL || VOP_PATHCONF(vp,
_PC_HAS_HIDDENSYSTEM, &has_pathconf) != 0)
has_pathconf = 0;
if (has_pathconf == 0) {
NFSCLRBIT_ATTRBIT(&checkattrbits,
NFSATTRBIT_HIDDEN);
NFSCLRBIT_ATTRBIT(&checkattrbits,
NFSATTRBIT_SYSTEM);
}
if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
|| retnotsup)
*retcmpp = NFSERR_NOTSAME;
@ -1521,15 +1536,13 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
if (compare) {
if (!(*retcmpp)) {
long has_named_attr;
if (vp == NULL || VOP_PATHCONF(vp,
_PC_HAS_NAMEDATTR, &has_named_attr)
_PC_HAS_NAMEDATTR, &has_pathconf)
!= 0)
has_named_attr = 0;
if ((has_named_attr != 0 &&
has_pathconf = 0;
if ((has_pathconf != 0 &&
*tl != newnfs_true) ||
(has_named_attr == 0 &&
(has_pathconf == 0 &&
*tl != newnfs_false))
*retcmpp = NFSERR_NOTSAME;
}
@ -1792,9 +1805,17 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
free(cp2, M_NFSSTRING);
break;
case NFSATTRBIT_HIDDEN:
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_HIDDEN) == 0) ||
(*tl == newnfs_false &&
(nap->na_flags & UF_HIDDEN) != 0)))
*retcmpp = NFSERR_NOTSAME;
} else if (nap != NULL) {
if (*tl == newnfs_true)
nap->na_flags |= UF_HIDDEN;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HOMOGENEOUS:
@ -2166,9 +2187,17 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_SYSTEM:
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_SYSTEM) == 0) ||
(*tl == newnfs_false &&
(nap->na_flags & UF_SYSTEM) != 0)))
*retcmpp = NFSERR_NOTSAME;
} else if (nap != NULL) {
if (*tl == newnfs_true)
nap->na_flags |= UF_SYSTEM;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESS:
@ -2634,7 +2663,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
size_t atsiz;
bool xattrsupp;
short irflag;
long has_named_attr;
long has_pathconf;
#ifdef QUOTA
struct dqblk dqb;
uid_t savuid;
@ -2751,6 +2780,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
}
if (cred == NULL || p == NULL || vp == NULL ||
VOP_PATHCONF(vp, _PC_HAS_HIDDENSYSTEM,
&has_pathconf) != 0)
has_pathconf = 0;
if (has_pathconf == 0) {
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
}
retnum += nfsrv_putattrbit(nd, &attrbits);
break;
case NFSATTRBIT_TYPE:
@ -2791,10 +2828,10 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
break;
case NFSATTRBIT_NAMEDATTR:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (VOP_PATHCONF(vp, _PC_HAS_NAMEDATTR, &has_named_attr)
!= 0)
has_named_attr = 0;
if (has_named_attr != 0)
if (VOP_PATHCONF(vp, _PC_HAS_NAMEDATTR,
&has_pathconf) != 0)
has_pathconf = 0;
if (has_pathconf != 0)
*tl = newnfs_true;
else
*tl = newnfs_false;
@ -2899,6 +2936,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
*tl = 0;
retnum += 2 * NFSX_UNSIGNED;
break;
case NFSATTRBIT_HIDDEN:
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
if ((vap->va_flags & UF_HIDDEN) != 0)
*tl = newnfs_true;
else
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HOMOGENEOUS:
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
@ -3088,6 +3133,14 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
txdr_hyper(vap->va_bytes, tl);
retnum += NFSX_HYPER;
break;
case NFSATTRBIT_SYSTEM:
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
if ((vap->va_flags & UF_SYSTEM) != 0)
*tl = newnfs_true;
else
*tl = newnfs_false;
retnum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESS:
NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
txdr_nfsv4time(&vap->va_atime, tl);

View file

@ -1142,6 +1142,7 @@ struct nfsv3_sattr {
NFSATTRBM_FILESFREE | \
NFSATTRBM_FILESTOTAL | \
NFSATTRBM_FSLOCATIONS | \
NFSATTRBM_HIDDEN | \
NFSATTRBM_HOMOGENEOUS | \
NFSATTRBM_MAXFILESIZE | \
NFSATTRBM_MAXLINK | \
@ -1163,6 +1164,7 @@ struct nfsv3_sattr {
NFSATTRBM_SPACEFREE | \
NFSATTRBM_SPACETOTAL | \
NFSATTRBM_SPACEUSED | \
NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMEACCESS | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEDELTA | \
@ -1210,11 +1212,13 @@ struct nfsv3_sattr {
*/
#define NFSATTRBIT_SETABLE0 \
(NFSATTRBM_SIZE | \
NFSATTRBM_HIDDEN | \
NFSATTRBM_ACL)
#define NFSATTRBIT_SETABLE1 \
(NFSATTRBM_MODE | \
NFSATTRBM_OWNER | \
NFSATTRBM_OWNERGROUP | \
NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEACCESSSET | \
NFSATTRBM_TIMEMODIFYSET)
@ -1254,6 +1258,7 @@ struct nfsv3_sattr {
NFSATTRBM_SIZE | \
NFSATTRBM_FSID | \
NFSATTRBM_FILEID | \
NFSATTRBM_HIDDEN | \
NFSATTRBM_MAXREAD)
/*
@ -1266,6 +1271,7 @@ struct nfsv3_sattr {
NFSATTRBM_OWNERGROUP | \
NFSATTRBM_RAWDEV | \
NFSATTRBM_SPACEUSED | \
NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMEACCESS | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEMETADATA | \
@ -1288,6 +1294,7 @@ struct nfsv3_sattr {
NFSATTRBM_SIZE | \
NFSATTRBM_FSID | \
NFSATTRBM_FILEID | \
NFSATTRBM_HIDDEN | \
NFSATTRBM_MAXREAD)
/*
@ -1298,6 +1305,7 @@ struct nfsv3_sattr {
NFSATTRBM_NUMLINKS | \
NFSATTRBM_RAWDEV | \
NFSATTRBM_SPACEUSED | \
NFSATTRBM_SYSTEM | \
NFSATTRBM_TIMEACCESS | \
NFSATTRBM_TIMECREATE | \
NFSATTRBM_TIMEMETADATA | \

View file

@ -4158,6 +4158,13 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
if (!NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
NFSATTRBIT_TIMECREATE))
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
if (!NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
NFSATTRBIT_HIDDEN) ||
!NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
NFSATTRBIT_SYSTEM)) {
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
}
}
/*

View file

@ -1074,15 +1074,23 @@ nfs_setattr(struct vop_setattr_args *ap)
int error = 0;
u_quad_t tsize;
struct timespec ts;
struct nfsmount *nmp;
#ifndef nolint
tsize = (u_quad_t)0;
#endif
/*
* Setting of flags and marking of atimes are not supported.
* Only setting of UF_HIDDEN and UF_SYSTEM are supported and
* only for NFSv4 servers that support them.
*/
if (vap->va_flags != VNOVAL)
nmp = VFSTONFS(vp->v_mount);
if (vap->va_flags != VNOVAL && (!NFSHASNFSV4(nmp) ||
(vap->va_flags & ~(UF_HIDDEN | UF_SYSTEM)) != 0 ||
((vap->va_flags & UF_HIDDEN) != 0 &&
!NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, NFSATTRBIT_HIDDEN)) ||
((vap->va_flags & UF_SYSTEM) != 0 &&
!NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, NFSATTRBIT_SYSTEM))))
return (EOPNOTSUPP);
/*
@ -1092,7 +1100,8 @@ nfs_setattr(struct vop_setattr_args *ap)
vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
vap->va_mtime.tv_sec != VNOVAL ||
vap->va_birthtime.tv_sec != VNOVAL ||
vap->va_mode != (mode_t)VNOVAL) &&
vap->va_mode != (mode_t)VNOVAL ||
vap->va_flags != (u_long)VNOVAL) &&
(vp->v_mount->mnt_flag & MNT_RDONLY))
return (EROFS);
if (vap->va_size != VNOVAL) {
@ -4754,6 +4763,15 @@ nfs_pathconf(struct vop_pathconf_args *ap)
else
*ap->a_retval = 0;
break;
case _PC_HAS_HIDDENSYSTEM:
if (NFS_ISV4(vp) && NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
NFSATTRBIT_HIDDEN) &&
NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
NFSATTRBIT_SYSTEM))
*ap->a_retval = 1;
else
*ap->a_retval = 0;
break;
default:
error = vop_stdpathconf(ap);

View file

@ -449,6 +449,7 @@ nfsvno_getattr(struct vnode *vp, struct nfsvattr *nvap,
}
nvap->na_bsdflags = 0;
nvap->na_flags = 0;
error = VOP_GETATTR(vp, &nvap->na_vattr, nd->nd_cred);
if (lockedit != 0)
NFSVOPUNLOCK(vp);
@ -3127,6 +3128,9 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
bitpos = NFSATTRBIT_MAX;
} else {
bitpos = 0;
if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN) ||
NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SYSTEM))
nvap->na_flags = 0;
}
moderet = 0;
for (; bitpos < NFSATTRBIT_MAX; bitpos++) {
@ -3163,9 +3167,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HIDDEN:
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_HIDDEN;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MIMETYPE:
@ -3240,9 +3246,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
break;
case NFSATTRBIT_SYSTEM:
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_SYSTEM;
}
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESSSET:

View file

@ -403,8 +403,10 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
if (error)
goto nfsmout;
/* For NFSv4, only va_uid is used from nva2. */
/* For NFSv4, only va_uid and va_flags is used from nva2. */
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
if (!nd->nd_repstat)
nd->nd_repstat = preat_ret;
@ -463,6 +465,9 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
&nva, &attrbits, exp, p);
if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
u_long oldflags;
oldflags = nva2.na_flags;
/*
* For V4, try setting the attributes in sets, so that the
* reply bitmap will be correct for an error case.
@ -532,6 +537,32 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
}
}
if (!nd->nd_repstat &&
(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN) ||
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))) {
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN)) {
if ((nva.na_flags & UF_HIDDEN) != 0)
oldflags |= UF_HIDDEN;
else
oldflags &= ~UF_HIDDEN;
}
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM)) {
if ((nva.na_flags & UF_SYSTEM) != 0)
oldflags |= UF_SYSTEM;
else
oldflags &= ~UF_SYSTEM;
}
NFSVNO_ATTRINIT(&nva2);
NFSVNO_SETATTRVAL(&nva2, flags, oldflags);
nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
exp);
if (!nd->nd_repstat) {
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN))
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_HIDDEN);
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM))
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SYSTEM);
}
}
#ifdef NFS4_ACL_EXTATTR_NAME
if (!nd->nd_repstat && aclp->acl_cnt > 0 &&