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:
parent
f06c961ee3
commit
38e3ea69d4
@ -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 @@ again:
|
||||
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 @@ again:
|
||||
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 @@ again:
|
||||
* 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 @@ again:
|
||||
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 @@ again:
|
||||
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 @@ again:
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user