diff options
author | kato <kato@FreeBSD.org> | 1997-04-14 10:52:25 +0000 |
---|---|---|
committer | kato <kato@FreeBSD.org> | 1997-04-14 10:52:25 +0000 |
commit | 69a13fa7d18543170feb73d80f9459a7514f73b1 (patch) | |
tree | 38c833a53269ec2314c4fc0b6f969d5f0810364b /sys/fs | |
parent | c834ab375e027589e1cd0d6676a752f98100c75f (diff) | |
download | FreeBSD-src-69a13fa7d18543170feb73d80f9459a7514f73b1.zip FreeBSD-src-69a13fa7d18543170feb73d80f9459a7514f73b1.tar.gz |
Fix `lockmgr: locking against myself' panic by multi union mount of
same directory pair.
If we do:
mount -t union a b
mount -t union a b
then, (1) namei tries to lock fs which has been already locked by
first union mount and (2) union_root() tries to lock locked fs. To
avoid first deadlock condition, unlock vnode if lowerrootvp is union
node, and to avoid second case, union_mount returns EDEADLK when multi
union mount is detected.
Diffstat (limited to 'sys/fs')
-rw-r--r-- | sys/fs/unionfs/union_vfsops.c | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c index 2f5837d..13e94f2 100644 --- a/sys/fs/unionfs/union_vfsops.c +++ b/sys/fs/unionfs/union_vfsops.c @@ -35,7 +35,7 @@ * SUCH DAMAGE. * * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 - * $Id$ + * $Id: union_vfsops.c,v 1.14 1997/02/22 09:40:41 peter Exp $ */ /* @@ -99,6 +99,7 @@ union_mount(mp, path, data, ndp, p) char *cp = 0; int len; u_int size; + int islowerunlocked = 0; #ifdef UNION_DIAGNOSTIC printf("union_mount(mp = %x)\n", mp); @@ -128,12 +129,27 @@ union_mount(mp, path, data, ndp, p) VREF(lowerrootvp); /* + * Unlock lower node to avoid deadlock. + * (XXX) VOP_ISLOCKED is needed? + */ + if ((lowerrootvp->v_op == union_vnodeop_p) && VOP_ISLOCKED(lowerrootvp)) { + VOP_UNLOCK(lowerrootvp, 0, p); + islowerunlocked = 1; + } + + /* * Find upper node. */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, UIO_USERSPACE, args.target, p); error = namei(ndp); + /* + * Re-lock vnode. + * (XXX) VOP_ISLOCKED is needed? + */ + if (islowerunlocked && !VOP_ISLOCKED(lowerrootvp)) + vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, p); if (error) goto bad; @@ -141,6 +157,14 @@ union_mount(mp, path, data, ndp, p) vrele(ndp->ni_dvp); ndp->ni_dvp = NULL; + /* + * Check multi union mount to avoid `lock myself again' panic. + */ + if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) { + error = EDEADLK; + goto bad; + } + if (upperrootvp->v_type != VDIR) { error = EINVAL; goto bad; |