+
+static void
+union_dircache_r(vp, vppp, cntp)
+ struct vnode *vp;
+ struct vnode ***vppp;
+ int *cntp;
+{
+ struct union_node *un;
+
+ if (vp->v_op != union_vnodeop_p) {
+ if (vppp) {
+ VREF(vp);
+ *(*vppp)++ = vp;
+ if (--(*cntp) == 0)
+ panic("union: dircache table too small");
+ } else {
+ (*cntp)++;
+ }
+
+ return;
+ }
+
+ un = VTOUNION(vp);
+ if (un->un_uppervp != NULLVP)
+ union_dircache_r(un->un_uppervp, vppp, cntp);
+ if (un->un_lowervp != NULLVP)
+ union_dircache_r(un->un_lowervp, vppp, cntp);
+}
+
+struct vnode *
+union_dircache(vp)
+ struct vnode *vp;
+{
+ int cnt;
+ struct vnode *nvp;
+ struct vnode **vpp;
+ struct vnode **dircache = VTOUNION(vp)->un_dircache;
+ struct union_node *un;
+ int error;
+
+ if (dircache == 0) {
+ cnt = 0;
+ union_dircache_r(vp, 0, &cnt);
+ cnt++;
+ dircache = (struct vnode **)
+ malloc(cnt * sizeof(struct vnode *),
+ M_TEMP, M_WAITOK);
+ vpp = dircache;
+ union_dircache_r(vp, &vpp, &cnt);
+ *vpp = NULLVP;
+ vpp = dircache + 1;
+ } else {
+ vpp = dircache;
+ do {
+ if (*vpp++ == VTOUNION(vp)->un_uppervp)
+ break;
+ } while (*vpp != NULLVP);
+ }
+
+ if (*vpp == NULLVP)
+ return (NULLVP);
+
+ VOP_LOCK(*vpp);
+ VREF(*vpp);
+ error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0);
+ if (error)
+ return (NULLVP);
+ VTOUNION(vp)->un_dircache = 0;
+ un = VTOUNION(nvp);
+ un->un_dircache = dircache;
+
+ return (nvp);
+}