diff options
author | kib <kib@FreeBSD.org> | 2014-08-22 07:09:54 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2014-08-22 07:09:54 +0000 |
commit | 396919643bc13b3760e21e882607f3e266425c74 (patch) | |
tree | 8467e58adda22294c97ad26ded7ad3dbe84bd048 /sys/fs/nullfs/null_vnops.c | |
parent | bdf2b76da25fd305451e3971430d4a973e854c39 (diff) | |
download | FreeBSD-src-396919643bc13b3760e21e882607f3e266425c74.zip FreeBSD-src-396919643bc13b3760e21e882607f3e266425c74.tar.gz |
MFC r269708:
Unlock ldvp and lock dvp to compensate for possible ldvp unlock in lower
VOP_LOOKUP() and dvp reclamation. Use cached value of dvp->v_mount.
Diffstat (limited to 'sys/fs/nullfs/null_vnops.c')
-rw-r--r-- | sys/fs/nullfs/null_vnops.c | 44 |
1 files changed, 40 insertions, 4 deletions
diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c index 481644c..4762a3c 100644 --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -361,9 +361,11 @@ null_lookup(struct vop_lookup_args *ap) struct vnode *dvp = ap->a_dvp; int flags = cnp->cn_flags; struct vnode *vp, *ldvp, *lvp; + struct mount *mp; int error; - if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && + mp = dvp->v_mount; + if ((flags & ISLASTCN) != 0 && (mp->mnt_flag & MNT_RDONLY) != 0 && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); /* @@ -376,9 +378,43 @@ null_lookup(struct vop_lookup_args *ap) ((dvp->v_vflag & VV_ROOT) != 0 && (flags & ISDOTDOT) == 0), ("ldvp %p fl %#x dvp %p fl %#x flags %#x", ldvp, ldvp->v_vflag, dvp, dvp->v_vflag, flags)); + + /* + * Hold ldvp. The reference on it, owned by dvp, is lost in + * case of dvp reclamation, and we need ldvp to move our lock + * from ldvp to dvp. + */ + vhold(ldvp); + error = VOP_LOOKUP(ldvp, &lvp, cnp); - if (error == EJUSTRETURN && (flags & ISLASTCN) && - (dvp->v_mount->mnt_flag & MNT_RDONLY) && + + /* + * VOP_LOOKUP() on lower vnode may unlock ldvp, which allows + * dvp to be reclaimed due to shared v_vnlock. Check for the + * doomed state and return error. + */ + if ((error == 0 || error == EJUSTRETURN) && + (dvp->v_iflag & VI_DOOMED) != 0) { + error = ENOENT; + if (lvp != NULL) + vput(lvp); + + /* + * If vgone() did reclaimed dvp before curthread + * relocked ldvp, the locks of dvp and ldpv are no + * longer shared. In this case, relock of ldvp in + * lower fs VOP_LOOKUP() does not restore the locking + * state of dvp. Compensate for this by unlocking + * ldvp and locking dvp, which is also correct if the + * locks are still shared. + */ + VOP_UNLOCK(ldvp, 0); + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); + } + vdrop(ldvp); + + if (error == EJUSTRETURN && (flags & ISLASTCN) != 0 && + (mp->mnt_flag & MNT_RDONLY) != 0 && (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) error = EROFS; @@ -388,7 +424,7 @@ null_lookup(struct vop_lookup_args *ap) VREF(dvp); vrele(lvp); } else { - error = null_nodeget(dvp->v_mount, lvp, &vp); + error = null_nodeget(mp, lvp, &vp); if (error == 0) *ap->a_vpp = vp; } |