summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2014-08-22 07:09:54 +0000
committerkib <kib@FreeBSD.org>2014-08-22 07:09:54 +0000
commit396919643bc13b3760e21e882607f3e266425c74 (patch)
tree8467e58adda22294c97ad26ded7ad3dbe84bd048 /sys/fs
parentbdf2b76da25fd305451e3971430d4a973e854c39 (diff)
downloadFreeBSD-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')
-rw-r--r--sys/fs/nullfs/null_vnops.c44
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;
}
OpenPOWER on IntegriCloud