diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c index 45065e0be7b5..aa39b86cf1af 100644 --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -375,8 +375,8 @@ null_lookup(struct vop_lookup_args *ap) struct componentname *cnp = ap->a_cnp; struct vnode *dvp = ap->a_dvp; int flags = cnp->cn_flags; - struct vnode *vp, *ldvp, *lvp; - struct mount *mp; + struct vnode *vp, *ldvp, *lvp, *tdp; + struct mount *mp, *lmp; int error; mp = dvp->v_mount; @@ -394,6 +394,33 @@ null_lookup(struct vop_lookup_args *ap) ("ldvp %p fl %#x dvp %p fl %#x flags %#x", ldvp, ldvp->v_vflag, dvp, dvp->v_vflag, flags)); + if ((flags & ISDOTDOT) == 0 && (cnp->cn_flags & NOCROSSMOUNT) == 0) { + while (ldvp->v_type == VDIR && (lmp = ldvp->v_mountedhere)) { + if (vfs_busy(lmp, 0)) + continue; + error = VFS_ROOT(lmp, VOP_ISLOCKED(ldvp), &tdp); + vfs_unbusy(lmp); + if (error != 0) + return (error); + ldvp = tdp; + } + } + + if ((flags & ISDOTDOT) && (ldvp->v_vflag & VV_ROOT) && + (tdp = ldvp->v_mount->mnt_vnodecovered) && tdp->v_mount != mp) { + vref(tdp); + vn_lock(tdp, VOP_ISLOCKED(ldvp) | LK_RETRY); + ldvp = tdp; + } + + if (ldvp != NULLVPTOLOWERVP(dvp)) { + error = null_nodeget(mp, ldvp, &dvp); + if (error != 0) { + vput(ldvp); + return (error); + } + } + /* * Hold ldvp. The reference on it, owned by dvp, is lost in * case of dvp reclamation, and we need ldvp to move our lock @@ -433,6 +460,24 @@ null_lookup(struct vop_lookup_args *ap) (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) error = EROFS; + if (error == 0 && (cnp->cn_flags & NOCROSSMOUNT) == 0) { + while ((lvp->v_type == VDIR) && (lmp = lvp->v_mountedhere)) { + if (vfs_busy(lmp, 0)) + continue; + error = VFS_ROOT(lmp, VOP_ISLOCKED(lvp), &tdp); + vfs_unbusy(lmp); + if (ldvp == lvp) + vrele(lvp); + else + vput(lvp); + if (error != 0) { + lvp = NULL; + break; + } + lvp = tdp; + } + } + if ((error == 0 || error == EJUSTRETURN) && lvp != NULL) { if (ldvp == lvp) { *ap->a_vpp = dvp; @@ -444,6 +489,9 @@ null_lookup(struct vop_lookup_args *ap) *ap->a_vpp = vp; } } + + if (dvp != ap->a_dvp) + vput(dvp); return (error); } @@ -906,22 +954,38 @@ null_vptocnp(struct vop_vptocnp_args *ap) { struct vnode *vp = ap->a_vp; struct vnode **dvp = ap->a_vpp; - struct vnode *lvp, *ldvp; + struct vnode *lvp, *ldvp, *tdp; struct mount *mp; int error, locked; - locked = VOP_ISLOCKED(vp); lvp = NULLVPTOLOWERVP(vp); - vhold(lvp); mp = vp->v_mount; + + if ((lvp->v_vflag & VV_ROOT) && + (tdp = lvp->v_mount->mnt_vnodecovered) && (tdp->v_mount != mp)) { + vref(tdp); + vn_lock(tdp, LK_SHARED | LK_RETRY); + lvp = tdp; + error = null_nodeget(mp, lvp, &vp); + if (error != 0) { + vput(lvp); + return (error); + } + } + + vhold(lvp); vfs_ref(mp); + locked = VOP_ISLOCKED(vp); VOP_UNLOCK(vp); /* vp is held by vn_vptocnp_locked that called us */ ldvp = lvp; vref(lvp); error = vn_vptocnp(&ldvp, ap->a_buf, ap->a_buflen); vdrop(lvp); if (error != 0) { - vn_lock(vp, locked | LK_RETRY); + if (vp != ap->a_vp) + vrele(vp); + else + vn_lock(vp, locked | LK_RETRY); vfs_rel(mp); return (ENOENT); } @@ -929,7 +993,10 @@ null_vptocnp(struct vop_vptocnp_args *ap) error = vn_lock(ldvp, LK_SHARED); if (error != 0) { vrele(ldvp); - vn_lock(vp, locked | LK_RETRY); + if (vp != ap->a_vp) + vrele(vp); + else + vn_lock(vp, locked | LK_RETRY); vfs_rel(mp); return (ENOENT); } @@ -940,7 +1007,10 @@ null_vptocnp(struct vop_vptocnp_args *ap) #endif VOP_UNLOCK(*dvp); /* keep reference on *dvp */ } - vn_lock(vp, locked | LK_RETRY); + if (vp != ap->a_vp) + vrele(vp); + else + vn_lock(vp, locked | LK_RETRY); vfs_rel(mp); return (error); }