nfsd: Avoid relocking vnode for NFSv4 Readdir

Without this patch, nfsv4_fillattr() relocks the vnode to
test to see if extended attributes are supported.
This is inefficient and could cause deadlocks if Readdir
ever asks for this attribute.

At this time, no extant NFSv4 client asks for this attribute
for Readdir, but this patch fixes the problem in case a
future client does so, by moving the test for extended attribute
support to before the nfsv4_fillattr() call where the vnode
is still locked.

MFC after:	2 weeks
This commit is contained in:
Rick Macklem 2025-07-19 15:45:40 -07:00
parent b153804256
commit c00baac0ab
6 changed files with 40 additions and 29 deletions

View file

@ -647,7 +647,8 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
NFSATTRBIT_TIMECREATE))
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
(void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0,
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL);
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL,
false);
break;
}
}
@ -2646,7 +2647,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno,
struct statfs *pnfssf)
struct statfs *pnfssf, bool xattrsupp)
{
int bitpos, retnum = 0;
u_int32_t *tl;
@ -2660,8 +2661,6 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
struct nfsfsinfo fsinf;
struct timespec temptime;
NFSACL_T *aclp, *naclp = NULL;
size_t atsiz;
bool xattrsupp;
short irflag;
long has_pathconf;
#ifdef QUOTA
@ -2747,18 +2746,6 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
}
}
/* Check to see if Extended Attributes are supported. */
xattrsupp = false;
if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_XATTRSUPPORT)) {
if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER,
"xxx", NULL, &atsiz, cred, p);
NFSVOPUNLOCK(vp);
if (error != EOPNOTSUPP)
xattrsupp = true;
}
}
/*
* Put out the attribute bitmap for the ones being filled in
* and get the field for the number of attributes returned.

View file

@ -395,8 +395,8 @@ int nfsrv_putopbit(struct nfsrv_descript *, nfsopbit_t *);
void nfsrv_wcc(struct nfsrv_descript *, int, struct nfsvattr *, int,
struct nfsvattr *);
int nfsv4_fillattr(struct nfsrv_descript *, struct mount *, vnode_t, NFSACL_T *,
struct vattr *, fhandle_t *, int, nfsattrbit_t *,
struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t, struct statfs *);
struct vattr *, fhandle_t *, int, nfsattrbit_t *, struct ucred *,
NFSPROC_T *, int, int, int, int, uint64_t, struct statfs *, bool);
void nfsrv_fillattr(struct nfsrv_descript *, struct nfsvattr *);
struct mbuf *nfsrv_adj(struct mbuf *, int, int);
void nfsrv_postopattr(struct nfsrv_descript *, int, struct nfsvattr *);
@ -735,7 +735,7 @@ int nfsvno_updfilerev(vnode_t, struct nfsvattr *, struct nfsrv_descript *,
NFSPROC_T *);
int nfsvno_fillattr(struct nfsrv_descript *, struct mount *, vnode_t,
struct nfsvattr *, fhandle_t *, int, nfsattrbit_t *,
struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t);
struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t, bool);
int nfsrv_sattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, nfsattrbit_t *,
NFSACL_T *, NFSPROC_T *);
int nfsv4_sattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, nfsattrbit_t *,

View file

@ -5436,7 +5436,7 @@ nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
NFSZERO_ATTRBIT(&attrbits);
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
(void) nfsv4_fillattr(nd, vp->v_mount, vp, aclp, NULL, NULL, 0,
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL);
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL, false);
error = nfscl_request(nd, vp, p, cred);
if (error)
return (error);

View file

@ -3701,7 +3701,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
if (!error)
(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
(uint64_t)0, NULL);
(uint64_t)0, NULL, false);
break;
case NFSV4OP_CBRECALL:
NFSCL_DEBUG(4, "cbrecall\n");

View file

@ -2112,7 +2112,8 @@ int
nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
struct nfsvattr *nvap, fhandle_t *fhp, int rderror, nfsattrbit_t *attrbitp,
struct ucred *cred, struct thread *p, int isdgram, int reterr,
int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno,
bool xattrsupp)
{
struct statfs *sf;
int error;
@ -2131,7 +2132,7 @@ nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
}
error = nfsv4_fillattr(nd, mp, vp, NULL, &nvap->na_vattr, fhp, rderror,
attrbitp, cred, p, isdgram, reterr, supports_nfsv4acls, at_root,
mounted_on_fileno, sf);
mounted_on_fileno, sf, xattrsupp);
free(sf, M_TEMP);
NFSEXITCODE2(0, nd);
return (error);
@ -2448,7 +2449,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
struct nfsvattr nva, at, *nvap = &nva;
struct mbuf *mb0, *mb1;
struct nfsreferral *refp;
int nlen, r, error = 0, getret = 1, usevget = 1;
int nlen, r, error = 0, getret = 1, ret, usevget = 1;
int siz, cnt, fullsiz, eofflag, ncookies, entrycnt;
caddr_t bpos0, bpos1;
u_int64_t off, toff, verf __unused;
@ -2462,6 +2463,8 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
uint64_t mounted_on_fileno;
struct thread *p = curthread;
int bextpg0, bextpg1, bextpgsiz0, bextpgsiz1;
size_t atsiz;
bool xattrsupp;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
@ -2936,9 +2939,18 @@ again:
*tl++ = newnfs_true;
txdr_hyper(*cookiep, tl);
dirlen += nfsm_strtom(nd, dp->d_name, nlen);
xattrsupp = false;
if (nvp != NULL) {
supports_nfsv4acls =
nfs_supportsnfsv4acls(nvp);
if (NFSISSET_ATTRBIT(&attrbits,
NFSATTRBIT_XATTRSUPPORT)) {
ret = VOP_GETEXTATTR(nvp,
EXTATTR_NAMESPACE_USER,
"xxx", NULL, &atsiz,
nd->nd_cred, p);
xattrsupp = ret != EOPNOTSUPP;
}
NFSVOPUNLOCK(nvp);
} else
supports_nfsv4acls = 0;
@ -2958,13 +2970,13 @@ again:
nvp, nvap, &nfh, r, &rderrbits,
nd->nd_cred, p, isdgram, 0,
supports_nfsv4acls, at_root,
mounted_on_fileno);
mounted_on_fileno, xattrsupp);
} else {
dirlen += nfsvno_fillattr(nd, new_mp,
nvp, nvap, &nfh, r, &attrbits,
nd->nd_cred, p, isdgram, 0,
supports_nfsv4acls, at_root,
mounted_on_fileno);
mounted_on_fileno, xattrsupp);
}
if (nvp != NULL)
vrele(nvp);
@ -6356,7 +6368,7 @@ nfsrv_setacldsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
* the same type (VREG).
*/
nfsv4_fillattr(nd, NULL, vp, aclp, NULL, NULL, 0, &attrbits, NULL,
NULL, 0, 0, 0, 0, 0, NULL);
NULL, 0, 0, 0, 0, 0, NULL, false);
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
if (error != 0) {

View file

@ -241,7 +241,7 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
{
struct nfsvattr nva;
fhandle_t fh;
int at_root = 0, error = 0, supports_nfsv4acls;
int at_root = 0, error = 0, ret, supports_nfsv4acls;
struct nfsreferral *refp;
nfsattrbit_t attrbits, tmpbits;
struct mount *mp;
@ -250,6 +250,8 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
uint64_t mounted_on_fileno = 0;
accmode_t accmode;
struct thread *p = curthread;
size_t atsiz;
bool xattrsupp;
if (nd->nd_repstat)
goto out;
@ -307,6 +309,15 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
&nva, &attrbits, p);
if (nd->nd_repstat == 0) {
supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
xattrsupp = false;
if (NFSISSET_ATTRBIT(&attrbits,
NFSATTRBIT_XATTRSUPPORT)) {
ret = VOP_GETEXTATTR(vp,
EXTATTR_NAMESPACE_USER,
"xxx", NULL, &atsiz, nd->nd_cred,
p);
xattrsupp = ret != EOPNOTSUPP;
}
mp = vp->v_mount;
if (nfsrv_enable_crossmntpt != 0 &&
vp->v_type == VDIR &&
@ -340,7 +351,8 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
(void)nfsvno_fillattr(nd, mp, vp, &nva,
&fh, 0, &attrbits, nd->nd_cred, p,
isdgram, 1, supports_nfsv4acls,
at_root, mounted_on_fileno);
at_root, mounted_on_fileno,
xattrsupp);
vfs_unbusy(mp);
}
vrele(vp);