Modify the experimental nfs server so that it falls back to

using VOP_LOOKUP() when VFS_VGET() returns EOPNOTSUPP in the
ReaddirPlus RPC. This patch is based upon one by pjd@ for the
regular nfs server which has not yet been committed. It is needed
when a ZFS volume is exported and ReaddirPlus (which almost
always happens for NFSv4) is performed by a client. The patch
also simplifies vnode lock handling somewhat.

MFC after:	2 weeks
This commit is contained in:
Rick Macklem 2009-11-23 16:08:15 +00:00
parent f06c961ee3
commit 38e3ea69d4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=199715

View File

@ -1675,7 +1675,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, vgetret;
int nlen, r, error = 0, getret = 1, usevget = 1;
int siz, cnt, fullsiz, eofflag, ncookies, entrycnt;
caddr_t bpos0, bpos1;
u_int64_t off, toff, verf;
@ -1683,6 +1683,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
nfsattrbit_t attrbits, rderrbits, savbits;
struct uio io;
struct iovec iv;
struct componentname cn;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
@ -1761,8 +1762,6 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
return (0);
}
NFSVOPUNLOCK(vp, 0, p);
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
again:
eofflag = 0;
@ -1780,10 +1779,8 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
io.uio_segflg = UIO_SYSSPACE;
io.uio_rw = UIO_READ;
io.uio_td = NULL;
NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY, p);
nd->nd_repstat = VOP_READDIR(vp, &io, nd->nd_cred, &eofflag, &ncookies,
&cookies);
NFSVOPUNLOCK(vp, 0, p);
off = (u_int64_t)io.uio_offset;
if (io.uio_resid)
siz -= io.uio_resid;
@ -1795,7 +1792,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
if (!nd->nd_repstat)
nd->nd_repstat = getret;
if (nd->nd_repstat) {
vrele(vp);
vput(vp);
if (cookies)
free((caddr_t)cookies, M_TEMP);
free((caddr_t)rbuf, M_TEMP);
@ -1808,7 +1805,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
* rpc reply
*/
if (siz == 0) {
vrele(vp);
vput(vp);
if (nd->nd_flag & ND_NFSV3)
nfsrv_postopattr(nd, getret, &at);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
@ -1853,33 +1850,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
toff = off;
goto again;
}
/*
* Probe one of the directory entries to see if the filesystem
* supports VGET for NFSv3. For NFSv4, it will return an
* error later, if attributes are required.
* (To be honest, most if not all NFSv4 clients will require
* attributes, but??)
*/
if ((nd->nd_flag & ND_NFSV3)) {
vgetret = VFS_VGET(vp->v_mount, dp->d_fileno, LK_EXCLUSIVE,
&nvp);
if (vgetret != 0) {
if (vgetret == EOPNOTSUPP)
nd->nd_repstat = NFSERR_NOTSUPP;
else
nd->nd_repstat = NFSERR_SERVERFAULT;
vrele(vp);
if (cookies)
free((caddr_t)cookies, M_TEMP);
free((caddr_t)rbuf, M_TEMP);
nfsrv_postopattr(nd, getret, &at);
return (0);
}
if (!vgetret)
vput(nvp);
nvp = NULL;
}
NFSVOPUNLOCK(vp, 0, p);
/*
* Save this position, in case there is an error before one entry
@ -1937,9 +1908,41 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
if (nd->nd_flag & ND_NFSV4)
refp = nfsv4root_getreferral(NULL,
vp, dp->d_fileno);
if (refp == NULL)
r = VFS_VGET(vp->v_mount, dp->d_fileno,
LK_EXCLUSIVE, &nvp);
if (refp == NULL) {
if (usevget)
r = VFS_VGET(vp->v_mount,
dp->d_fileno, LK_EXCLUSIVE,
&nvp);
else
r = EOPNOTSUPP;
if (r == EOPNOTSUPP) {
if (usevget) {
usevget = 0;
cn.cn_nameiop = LOOKUP;
cn.cn_lkflags =
LK_EXCLUSIVE |
LK_RETRY;
cn.cn_cred =
nd->nd_cred;
cn.cn_thread = p;
}
cn.cn_nameptr = dp->d_name;
cn.cn_namelen = nlen;
cn.cn_flags = ISLASTCN |
NOFOLLOW | LOCKLEAF |
MPSAFE;
if (nlen == 2 &&
dp->d_name[0] == '.' &&
dp->d_name[1] == '.')
cn.cn_flags |=
ISDOTDOT;
if (!VOP_ISLOCKED(vp))
vn_lock(vp,
LK_EXCLUSIVE |
LK_RETRY);
r = VOP_LOOKUP(vp, &nvp, &cn);
}
}
if (!r) {
if (refp == NULL &&
((nd->nd_flag & ND_NFSV3) ||
@ -2018,7 +2021,10 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
cookiep++;
ncookies--;
}
vrele(vp);
if (!usevget && VOP_ISLOCKED(vp))
vput(vp);
else
vrele(vp);
/*
* If dirlen > cnt, we must strip off the last entry. If that