From owner-svn-src-all@FreeBSD.ORG Fri Aug 8 11:39:05 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id BA259C10 for ; Fri, 8 Aug 2014 11:39:05 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 97F732598 for ; Fri, 8 Aug 2014 11:39:05 +0000 (UTC) Received: from kib (uid 1100) (envelope-from kib@FreeBSD.org) id 2f82 by svn.freebsd.org (DragonFly Mail Agent v0.9+); Fri, 08 Aug 2014 11:39:05 +0000 From: Konstantin Belousov Date: Fri, 8 Aug 2014 11:39:05 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r269708 - head/sys/fs/nullfs X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message-Id: <53e4b6d9.2f82.d4811ca@svn.freebsd.org> X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 Aug 2014 11:39:05 -0000 Author: kib Date: Fri Aug 8 11:39:05 2014 New Revision: 269708 URL: http://svnweb.freebsd.org/changeset/base/269708 Log: VOP_LOOKUP() may relock the directory vnode for some reasons. Since nullfs vnode shares vnode lock with lower vnode, this allows the reclamation of nullfs directory vnode in null_lookup(). In this situation, VOP must return ENOENT. More, since after the reclamation, the locks of nullfs directory vnode and lower vnode are no longer shared, the relock of the ldvp does not restore the correct locking state of dvp, and leaks ldvp lock. Correct this by unlocking ldvp and locking dvp. Use cached value of dvp->v_mount. Reported by: bdrewery Tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Modified: head/sys/fs/nullfs/null_vnops.c Modified: head/sys/fs/nullfs/null_vnops.c ============================================================================== --- head/sys/fs/nullfs/null_vnops.c Fri Aug 8 09:47:01 2014 (r269707) +++ head/sys/fs/nullfs/null_vnops.c Fri Aug 8 11:39:05 2014 (r269708) @@ -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; }