summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/nfsserver/nfs.h3
-rw-r--r--sys/nfsserver/nfs_serv.c298
-rw-r--r--sys/nfsserver/nfs_srvsubs.c16
3 files changed, 203 insertions, 114 deletions
diff --git a/sys/nfsserver/nfs.h b/sys/nfsserver/nfs.h
index 1bbc1f4..07c74bf 100644
--- a/sys/nfsserver/nfs.h
+++ b/sys/nfsserver/nfs.h
@@ -332,7 +332,8 @@ void nfsm_srvpostopattr(struct nfsrv_descript *, int, struct vattr *,
int netaddr_match(int, union nethostaddr *, struct sockaddr *);
int nfs_namei(struct nameidata *, fhandle_t *, int,
struct nfssvc_sock *, struct sockaddr *, struct mbuf **,
- caddr_t *, struct vnode **, struct thread *, int);
+ caddr_t *, struct vnode **, int, struct vattr *, int *,
+ struct thread *, int);
void nfsm_adj(struct mbuf *, int, int);
int nfsm_mbuftouio(struct mbuf **, struct uio *, int, caddr_t *);
void nfsrv_initcache(void);
diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c
index 7573444..ae3f944 100644
--- a/sys/nfsserver/nfs_serv.c
+++ b/sys/nfsserver/nfs_serv.c
@@ -467,7 +467,7 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART;
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, pubflag);
+ &dirp, v3, &dirattr, &dirattr_ret, td, pubflag);
/*
* namei failure, only dirp to cleanup. Clear out garbarge from
@@ -476,9 +476,6 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
if (error) {
if (dirp) {
- if (v3)
- dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred,
- td);
vrele(dirp);
dirp = NULL;
}
@@ -551,9 +548,6 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
}
if (dirp) {
- if (v3)
- dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred,
- td);
vrele(dirp);
dirp = NULL;
}
@@ -1630,15 +1624,10 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* prior to calling nfsm_reply ( which might goto nfsmout ).
*/
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp) {
- if (v3) {
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
- td);
- } else {
- vrele(dirp);
- dirp = NULL;
- }
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
+ if (dirp && !v3) {
+ vrele(dirp);
+ dirp = NULL;
}
if (error) {
nfsm_reply(NFSX_WCCDATA(v3));
@@ -1809,9 +1798,25 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
if (exclusive_flag && !error &&
bcmp(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF))
error = EEXIST;
- diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
- vrele(dirp);
- dirp = NULL;
+ if (dirp == nd.ni_dvp)
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ else {
+ /* Drop the other locks to avoid deadlock. */
+ if (nd.ni_dvp) {
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ }
+ if (nd.ni_vp)
+ vput(nd.ni_vp);
+ nd.ni_dvp = NULL;
+ nd.ni_vp = NULL;
+
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ VOP_UNLOCK(dirp, 0, td);
+ }
}
ereply:
nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3));
@@ -1906,9 +1911,7 @@ nfsrv_mknod(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
*/
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp)
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, td);
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
if (error) {
nfsm_reply(NFSX_WCCDATA(1));
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
@@ -2006,10 +2009,10 @@ out:
vp = NULL;
nd.ni_vp = NULL;
}
- diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
if (dirp) {
- vrele(dirp);
- dirp = NULL;
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ VOP_UNLOCK(dirp, 0, td);
}
ereply:
nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1));
@@ -2085,15 +2088,10 @@ nfsrv_remove(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp) {
- if (v3) {
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
- td);
- } else {
- vrele(dirp);
- dirp = NULL;
- }
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
+ if (dirp && !v3) {
+ vrele(dirp);
+ dirp = NULL;
}
if (error == 0) {
if (nd.ni_vp->v_type == VDIR) {
@@ -2114,7 +2112,25 @@ out:
}
}
if (dirp && v3) {
- diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ if (dirp == nd.ni_dvp)
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ else {
+ /* Drop the other locks to avoid deadlock. */
+ if (nd.ni_dvp) {
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ }
+ if (nd.ni_vp)
+ vput(nd.ni_vp);
+ nd.ni_dvp = NULL;
+ nd.ni_vp = NULL;
+
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ VOP_UNLOCK(dirp, 0, td);
+ }
vrele(dirp);
dirp = NULL;
}
@@ -2200,15 +2216,10 @@ nfsrv_rename(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
fromnd.ni_cnd.cn_nameiop = DELETE;
fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md,
- &dpos, &fdirp, td, FALSE);
- if (fdirp) {
- if (v3) {
- fdirfor_ret = VOP_GETATTR(fdirp, &fdirfor, cred,
- td);
- } else {
- vrele(fdirp);
- fdirp = NULL;
- }
+ &dpos, &fdirp, v3, &fdirfor, &fdirfor_ret, td, FALSE);
+ if (fdirp && !v3) {
+ vrele(fdirp);
+ fdirp = NULL;
}
if (error) {
nfsm_reply(2 * NFSX_WCCDATA(v3));
@@ -2227,15 +2238,10 @@ nfsrv_rename(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
tond.ni_cnd.cn_nameiop = RENAME;
tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
error = nfs_namei(&tond, tfhp, len2, slp, nam, &md,
- &dpos, &tdirp, td, FALSE);
- if (tdirp) {
- if (v3) {
- tdirfor_ret = VOP_GETATTR(tdirp, &tdirfor, cred,
- td);
- } else {
- vrele(tdirp);
- tdirp = NULL;
- }
+ &dpos, &tdirp, v3, &tdirfor, &tdirfor_ret, td, FALSE);
+ if (tdirp && !v3) {
+ vrele(tdirp);
+ tdirp = NULL;
}
if (error)
goto out1;
@@ -2318,12 +2324,30 @@ out:
/* fall through */
out1:
- if (fdirp)
- fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, td);
- if (tdirp)
- tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, td);
nfsm_reply(2 * NFSX_WCCDATA(v3));
if (v3) {
+ /* Release existing locks to prevent deadlock. */
+ if (tond.ni_dvp) {
+ if (tond.ni_dvp == tond.ni_vp)
+ vrele(tond.ni_dvp);
+ else
+ vput(tond.ni_dvp);
+ }
+ if (tond.ni_vp)
+ vput(tond.ni_vp);
+ tond.ni_dvp = NULL;
+ tond.ni_vp = NULL;
+
+ if (fdirp) {
+ vn_lock(fdirp, LK_EXCLUSIVE | LK_RETRY, td);
+ fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, td);
+ VOP_UNLOCK(fdirp, 0, td);
+ }
+ if (tdirp) {
+ vn_lock(tdirp, LK_EXCLUSIVE | LK_RETRY, td);
+ tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, td);
+ VOP_UNLOCK(tdirp, 0, td);
+ }
nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
}
@@ -2407,7 +2431,7 @@ nfsrv_link(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nfsm_srvmtofh(dfhp);
nfsm_srvnamesiz(len);
- error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam, &rdonly, TRUE);
+ error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, slp, nam, &rdonly, TRUE);
if (error) {
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
@@ -2418,47 +2442,71 @@ nfsrv_link(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
error = 0;
goto nfsmout;
}
+ if (v3)
+ getret = VOP_GETATTR(vp, &at, cred, td);
if (vp->v_type == VDIR) {
error = EPERM; /* POSIX */
goto out1;
}
+ VOP_UNLOCK(vp, 0, td);
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
error = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp) {
- if (v3) {
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
- td);
- } else {
- vrele(dirp);
- dirp = NULL;
- }
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
+ if (dirp && !v3) {
+ vrele(dirp);
+ dirp = NULL;
+ }
+ if (error) {
+ vrele(vp);
+ vp = NULL;
+ goto out2;
}
- if (error)
- goto out1;
-
xp = nd.ni_vp;
if (xp != NULL) {
error = EEXIST;
- goto out;
+ vrele(vp);
+ vp = NULL;
+ goto out2;
}
xp = nd.ni_dvp;
- if (vp->v_mount != xp->v_mount)
+ if (vp->v_mount != xp->v_mount) {
error = EXDEV;
-out:
- if (!error) {
- error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
- NDFREE(&nd, NDF_ONLY_PNBUF);
+ vrele(vp);
+ vp = NULL;
+ goto out2;
}
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
/* fall through */
out1:
if (v3)
getret = VOP_GETATTR(vp, &at, cred, td);
- if (dirp)
- diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+out2:
+ if (dirp) {
+ if (dirp == nd.ni_dvp)
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ else {
+ /* Release existing locks to prevent deadlock. */
+ if (nd.ni_dvp) {
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ }
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ nd.ni_dvp = NULL;
+ nd.ni_vp = NULL;
+
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ VOP_UNLOCK(dirp, 0, td);
+ }
+ }
ereply:
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
@@ -2473,7 +2521,7 @@ nfsmout:
if (dirp)
vrele(dirp);
if (vp)
- vrele(vp);
+ vput(vp);
if (nd.ni_dvp) {
if (nd.ni_dvp == nd.ni_vp)
vrele(nd.ni_dvp);
@@ -2534,15 +2582,10 @@ nfsrv_symlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART;
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp) {
- if (v3) {
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
- td);
- } else {
- vrele(dirp);
- dirp = NULL;
- }
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
+ if (dirp && !v3) {
+ vrele(dirp);
+ dirp = NULL;
}
if (error)
goto out;
@@ -2630,9 +2673,9 @@ out:
pathcp = NULL;
}
if (dirp) {
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
- vrele(dirp);
- dirp = NULL;
+ VOP_UNLOCK(dirp, 0, td);
}
if (nd.ni_startdir) {
vrele(nd.ni_startdir);
@@ -2719,15 +2762,10 @@ nfsrv_mkdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_flags = LOCKPARENT;
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp) {
- if (v3) {
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
- td);
- } else {
- vrele(dirp);
- dirp = NULL;
- }
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
+ if (dirp && !v3) {
+ vrele(dirp);
+ dirp = NULL;
}
if (error) {
nfsm_reply(NFSX_WCCDATA(v3));
@@ -2778,8 +2816,31 @@ nfsrv_mkdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
error = VOP_GETATTR(nd.ni_vp, vap, cred, td);
}
out:
- if (dirp)
- diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ if (dirp) {
+ if (dirp == nd.ni_dvp) {
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ } else {
+ /* Release existing locks to prevent deadlock. */
+ if (nd.ni_dvp) {
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if (nd.ni_dvp == nd.ni_vp && vpexcl)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ }
+ if (nd.ni_vp) {
+ if (vpexcl)
+ vput(nd.ni_vp);
+ else
+ vrele(nd.ni_vp);
+ }
+ nd.ni_dvp = NULL;
+ nd.ni_vp = NULL;
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ VOP_UNLOCK(dirp, 0, td);
+ }
+ }
nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
if (!error) {
@@ -2859,15 +2920,10 @@ nfsrv_rmdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos,
- &dirp, td, FALSE);
- if (dirp) {
- if (v3) {
- dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred,
- td);
- } else {
- vrele(dirp);
- dirp = NULL;
- }
+ &dirp, v3, &dirfor, &dirfor_ret, td, FALSE);
+ if (dirp && !v3) {
+ vrele(dirp);
+ dirp = NULL;
}
if (error) {
nfsm_reply(NFSX_WCCDATA(v3));
@@ -2902,8 +2958,26 @@ out:
error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
NDFREE(&nd, NDF_ONLY_PNBUF);
- if (dirp)
- diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ if (dirp) {
+ if (dirp == nd.ni_dvp)
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ else {
+ /* Release existing locks to prevent deadlock. */
+ if (nd.ni_dvp) {
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ }
+ if (nd.ni_vp)
+ vput(nd.ni_vp);
+ nd.ni_dvp = NULL;
+ nd.ni_vp = NULL;
+ vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td);
+ diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td);
+ VOP_UNLOCK(dirp, 0, td);
+ }
+ }
nfsm_reply(NFSX_WCCDATA(v3));
error = 0;
if (v3)
diff --git a/sys/nfsserver/nfs_srvsubs.c b/sys/nfsserver/nfs_srvsubs.c
index 5ac51ad..ceb6851 100644
--- a/sys/nfsserver/nfs_srvsubs.c
+++ b/sys/nfsserver/nfs_srvsubs.c
@@ -592,7 +592,8 @@ MODULE_VERSION(nfsserver, 1);
int
nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
- caddr_t *dposp, struct vnode **retdirp, struct thread *td, int pubflag)
+ caddr_t *dposp, struct vnode **retdirp, int v3, struct vattr *retdirattrp,
+ int *retdirattr_retp, struct thread *td, int pubflag)
{
int i, rem;
struct mbuf *md;
@@ -602,6 +603,7 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
struct vnode *dp;
int error, rdonly, linklen;
struct componentname *cnp = &ndp->ni_cnd;
+ int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0;
*retdirp = NULL;
cnp->cn_flags |= NOMACCHECK;
@@ -664,6 +666,12 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
* to the returned pointer
*/
*retdirp = dp;
+ if (v3) {
+ vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td);
+ *retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
+ ndp->ni_cnd.cn_cred, td);
+ VOP_UNLOCK(dp, 0, td);
+ }
if (pubflag) {
/*
@@ -736,6 +744,8 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
VREF(dp);
ndp->ni_startdir = dp;
+ if (!lockleaf)
+ cnp->cn_flags |= LOCKLEAF;
for (;;) {
cnp->cn_nameptr = cnp->cn_pnbuf;
/*
@@ -761,6 +771,8 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
cnp->cn_flags |= HASBUF;
else
uma_zfree(namei_zone, cnp->cn_pnbuf);
+ if (ndp->ni_vp && !lockleaf)
+ VOP_UNLOCK(ndp->ni_vp, 0, td);
break;
}
@@ -840,6 +852,8 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
ndp->ni_startdir = ndp->ni_dvp;
ndp->ni_dvp = NULL;
}
+ if (!lockleaf)
+ cnp->cn_flags &= ~LOCKLEAF;
/*
* nfs_namei() guarentees that fields will not contain garbage
OpenPOWER on IntegriCloud