Operation union_readdirhook() stores the lower directory as un_uppervp.

This breaks the assumption that un_uppervp->v_mount is the upper mount.

Fix by storing the directory as un_lowervp and adapt union_readdir().

Should fix PR kern/55552: panic with union mount
This commit is contained in:
hannken 2020-08-18 09:44:07 +00:00
parent 17ffbcbdab
commit d117c8147b
3 changed files with 32 additions and 12 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: union.h,v 1.29 2017/07/17 09:22:36 hannken Exp $ */
/* $NetBSD: union.h,v 1.30 2020/08/18 09:44:07 hannken Exp $ */
/*
* Copyright (c) 1994 The Regents of the University of California.
@ -130,6 +130,7 @@ struct union_node {
char *un_path; /* v: saved component name */
int un_openl; /* v: # of opens on lowervp */
unsigned int un_cflags; /* c: cache flags */
bool un_hooknode; /* :: from union_readdirhook */
struct vnode **un_dircache; /* v: cached union stack */
off_t un_uppersz; /* l: size of upper object */
off_t un_lowersz; /* l: size of lower object */

View File

@ -1,4 +1,4 @@
/* $NetBSD: union_subr.c,v 1.78 2020/02/23 15:46:41 ad Exp $ */
/* $NetBSD: union_subr.c,v 1.79 2020/08/18 09:44:07 hannken Exp $ */
/*
* Copyright (c) 1994
@ -72,7 +72,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.78 2020/02/23 15:46:41 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.79 2020/08/18 09:44:07 hannken Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -480,6 +480,7 @@ found:
un->un_dircache = 0;
un->un_openl = 0;
un->un_cflags = 0;
un->un_hooknode = false;
un->un_uppersz = VNOVAL;
un->un_lowersz = VNOVAL;
@ -1067,7 +1068,7 @@ union_dircache(struct vnode *vp, struct lwp *l)
} else {
vpp = dircache;
do {
if (*vpp++ == VTOUNION(vp)->un_uppervp)
if (*vpp++ == VTOUNION(vp)->un_lowervp)
break;
} while (*vpp != NULLVP);
}
@ -1076,10 +1077,12 @@ union_dircache(struct vnode *vp, struct lwp *l)
goto out;
vref(*vpp);
error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0);
error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0,
NULLVP, *vpp, 0);
if (!error) {
vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY);
VTOUNION(vp)->un_dircache = 0;
VTOUNION(nvp)->un_hooknode = true;
VTOUNION(nvp)->un_dircache = dircache;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: union_vnops.c,v 1.73 2020/05/16 18:31:50 christos Exp $ */
/* $NetBSD: union_vnops.c,v 1.74 2020/08/18 09:44:07 hannken Exp $ */
/*
* Copyright (c) 1992, 1993, 1994, 1995
@ -72,7 +72,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: union_vnops.c,v 1.73 2020/05/16 18:31:50 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: union_vnops.c,v 1.74 2020/08/18 09:44:07 hannken Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -1493,13 +1493,29 @@ union_readdir(void *v)
int a_ncookies;
} */ *ap = v;
struct union_node *un = VTOUNION(ap->a_vp);
struct vnode *uvp = un->un_uppervp;
struct vnode *vp;
int dolock, error;
if (uvp == NULLVP)
return (0);
if (un->un_hooknode) {
KASSERT(un->un_uppervp == NULLVP);
KASSERT(un->un_lowervp != NULLVP);
vp = un->un_lowervp;
dolock = 1;
} else {
vp = un->un_uppervp;
dolock = 0;
}
if (vp == NULLVP)
return 0;
ap->a_vp = uvp;
return (VCALL(uvp, VOFFSET(vop_readdir), ap));
if (dolock)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
ap->a_vp = vp;
error = VCALL(vp, VOFFSET(vop_readdir), ap);
if (dolock)
VOP_UNLOCK(vp);
return error;
}
int