]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/miscfs/union/union_subr.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / bsd / miscfs / union / union_subr.c
diff --git a/bsd/miscfs/union/union_subr.c b/bsd/miscfs/union/union_subr.c
deleted file mode 100644 (file)
index 34dbe14..0000000
+++ /dev/null
@@ -1,1604 +0,0 @@
-/*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
- *
- * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. The rights granted to you under the License
- * may not be used to create, or enable the creation or redistribution of,
- * unlawful or unlicensed copies of an Apple operating system, or to
- * circumvent, violate, or enable the circumvention or violation of, any
- * terms of an Apple operating system software license agreement.
- * 
- * Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
- */
-/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
-/*
- * Copyright (c) 1994 Jan-Simon Pendry
- * Copyright (c) 1994
- *     The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Jan-Simon Pendry.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *     @(#)union_subr.c        8.20 (Berkeley) 5/20/95
- */
-/*
- * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
- * support for mandatory and extensible security protections.  This notice
- * is included in support of clause 2.2 (b) of the Apple Public License,
- * Version 2.0.
- */
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/proc_internal.h>
-#include <sys/kauth.h>
-#include <sys/time.h>
-#include <sys/kernel.h>
-#include <sys/vnode_internal.h>
-#include <sys/namei.h>
-#include <sys/malloc.h>
-#include <sys/file_internal.h>
-#include <sys/filedesc.h>
-#include <sys/queue.h>
-#include <sys/mount_internal.h>
-#include <sys/stat.h>
-#include <sys/ubc.h>
-#include <sys/uio_internal.h>
-#include <miscfs/union/union.h>
-#include <sys/lock.h>
-#include <sys/kdebug.h>
-#if CONFIG_MACF
-#include <security/mac_framework.h>
-#endif
-
-
-static int union_vn_close(struct vnode *vp, int fmode, vfs_context_t ctx);
-
-/* must be power of two, otherwise change UNION_HASH() */
-#define NHASH 32
-
-/* unsigned int ... */
-#define UNION_HASH(u, l) \
-       (((((uintptr_t) (u)) + ((uintptr_t) l)) >> 8) & (NHASH-1))
-
-static LIST_HEAD(unhead, union_node) unhead[NHASH];
-static int unvplock[NHASH];
-
-static lck_grp_t * union_lck_grp;
-static lck_grp_attr_t * union_lck_grp_attr;
-static lck_attr_t * union_lck_attr;
-static lck_mtx_t *  union_mtxp;
-
-static int union_dircheck(struct vnode **, struct fileproc *, vfs_context_t ctx);
-static void union_newlower(struct union_node *, struct vnode *);
-static void union_newupper(struct union_node *, struct vnode *);
-
-
-int
-union_init(__unused struct vfsconf *vfsp)
-{
-       int i;
-
-       union_lck_grp_attr= lck_grp_attr_alloc_init();
-#if DIAGNOSTIC
-       lck_grp_attr_setstat(union_lck_grp_attr);
-#endif
-       union_lck_grp = lck_grp_alloc_init("union",  union_lck_grp_attr);
-       union_lck_attr = lck_attr_alloc_init();
-#if DIAGNOSTIC
-       lck_attr_setdebug(union_lck_attr);
-#endif
-       union_mtxp = lck_mtx_alloc_init(union_lck_grp, union_lck_attr);
-
-       for (i = 0; i < NHASH; i++)
-               LIST_INIT(&unhead[i]);
-       bzero((caddr_t) unvplock, sizeof(unvplock));
-       /* add the hook for getdirentries */
-       union_dircheckp = union_dircheck;
-       
-       return (0);
-}
-
-void
-union_lock()
-{
-       lck_mtx_lock(union_mtxp);
-}
-
-void
-union_unlock()
-{
-       lck_mtx_unlock(union_mtxp);
-}
-
-
-static int
-union_list_lock(int ix)
-{
-
-       if (unvplock[ix] & UNVP_LOCKED) {
-               unvplock[ix] |= UNVP_WANT;
-               msleep((caddr_t) &unvplock[ix], union_mtxp, PINOD, "union_list_lock", NULL);
-               return (1);
-       }
-
-       unvplock[ix] |= UNVP_LOCKED;
-
-       return (0);
-}
-
-static void
-union_list_unlock(int ix)
-{
-
-       unvplock[ix] &= ~UNVP_LOCKED;
-
-       if (unvplock[ix] & UNVP_WANT) {
-               unvplock[ix] &= ~UNVP_WANT;
-               wakeup((caddr_t) &unvplock[ix]);
-       }
-}
-
-/*
- *     union_updatevp:
- *
- *     The uppervp, if not NULL, must be referenced and not locked by us
- *     The lowervp, if not NULL, must be referenced.
- *
- *     If uppervp and lowervp match pointers already installed, then
- *     nothing happens. The passed vp's (when matching) are not adjusted.
- *
- *     This routine may only be called by union_newupper() and
- *     union_newlower().
- */
-
-/* always called with union lock held */
-void
-union_updatevp(struct union_node *un, struct vnode *uppervp,
-               struct vnode *lowervp)
-{
-       int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
-       int nhash = UNION_HASH(uppervp, lowervp);
-       int docache = (lowervp != NULLVP || uppervp != NULLVP);
-       int lhash, uhash;
-       vnode_t freevp;
-       vnode_t freedirvp;
-       caddr_t freepath;
-
-       /*
-        * Ensure locking is ordered from lower to higher
-        * to avoid deadlocks.
-        */
-       if (nhash < ohash) {
-               lhash = nhash;
-               uhash = ohash;
-       } else {
-               lhash = ohash;
-               uhash = nhash;
-       }
-
-       if (lhash != uhash) {
-               while (union_list_lock(lhash))
-                       continue;
-       }
-
-       while (union_list_lock(uhash))
-               continue;
-
-       if (ohash != nhash || !docache) {
-               if (un->un_flags & UN_CACHED) {
-                       un->un_flags &= ~UN_CACHED;
-                       LIST_REMOVE(un, un_cache);
-               }
-       }
-
-       if (ohash != nhash)
-               union_list_unlock(ohash);
-
-       if (un->un_lowervp != lowervp) {
-               freevp = freedirvp = NULLVP;
-               freepath = (caddr_t)0;
-               if (un->un_lowervp) {
-                       freevp = un->un_lowervp;
-                       un->un_lowervp = lowervp;
-                       if (un->un_path) {
-                               freepath = un->un_path; 
-                               un->un_path = 0;
-                       }
-                       if (un->un_dirvp) {
-                               freedirvp = un->un_dirvp;
-                               un->un_dirvp = NULLVP;
-                       }
-                       union_unlock();
-                       if (freevp)
-                               vnode_put(freevp);
-                       if (freedirvp)
-                               vnode_put(freedirvp);
-                       if (freepath)
-                               _FREE(un->un_path, M_TEMP);
-                       union_lock();
-               } else 
-                       un->un_lowervp = lowervp;
-               if (lowervp != NULLVP)
-                       un->un_lowervid = vnode_vid(lowervp);
-               un->un_lowersz = VNOVAL;
-       }
-
-       if (un->un_uppervp != uppervp) {
-               freevp = NULLVP;
-               if (un->un_uppervp) {
-                       freevp = un->un_uppervp;
-               }
-               un->un_uppervp = uppervp;
-               if (uppervp != NULLVP)
-                       un->un_uppervid = vnode_vid(uppervp);
-               un->un_uppersz = VNOVAL;
-               union_unlock();
-               if (freevp)
-                       vnode_put(freevp);
-               union_lock();
-       }
-
-       if (docache && (ohash != nhash)) {
-               LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
-               un->un_flags |= UN_CACHED;
-       }
-
-       union_list_unlock(nhash);
-}
-
-/*
- * Set a new lowervp.  The passed lowervp must be referenced and will be
- * stored in the vp in a referenced state. 
- */
-/* always called with union lock held */
-
-static void
-union_newlower(un, lowervp)
-       struct union_node *un;
-       struct vnode *lowervp;
-{
-       union_updatevp(un, un->un_uppervp, lowervp);
-}
-
-/*
- * Set a new uppervp.  The passed uppervp must be locked and will be 
- * stored in the vp in a locked state.  The caller should not unlock
- * uppervp.
- */
-
-/* always called with union lock held */
-static void
-union_newupper(un, uppervp)
-       struct union_node *un;
-       struct vnode *uppervp;
-{
-       union_updatevp(un, uppervp, un->un_lowervp);
-}
-
-/*
- * Keep track of size changes in the underlying vnodes.
- * If the size changes, then callback to the vm layer
- * giving priority to the upper layer size.
- */
-/* always called with union lock held */
-void
-union_newsize(vp, uppersz, lowersz)
-       struct vnode *vp;
-       off_t uppersz, lowersz;
-{
-       struct union_node *un;
-       off_t sz;
-
-       /* only interested in regular files */
-       if (vp->v_type != VREG)
-               return;
-
-       un = VTOUNION(vp);
-       sz = VNOVAL;
-
-       if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
-               un->un_uppersz = uppersz;
-               if (sz == VNOVAL)
-                       sz = un->un_uppersz;
-       }
-
-       if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) {
-               un->un_lowersz = lowersz;
-               if (sz == VNOVAL)
-                       sz = un->un_lowersz;
-       }
-
-       if (sz != VNOVAL) {
-#ifdef UNION_DIAGNOSTIC
-               printf("union: %s size now %ld\n",
-                       uppersz != VNOVAL ? "upper" : "lower", (long) sz);
-#endif
-               union_unlock();
-               ubc_setsize(vp, sz);
-               union_lock();
-       }
-}
-
-/*
- *     union_allocvp:  allocate a union_node and associate it with a
- *                     parent union_node and one or two vnodes.
- *
- *     vpp     Holds the returned vnode locked and referenced if no 
- *             error occurs.
- *
- *     mp      Holds the mount point.  mp may or may not be busied. 
- *             allocvp() makes no changes to mp.
- *
- *     dvp     Holds the parent union_node to the one we wish to create.
- *             XXX may only be used to traverse an uncopied lowervp-based
- *             tree?  XXX
- *
- *             dvp may or may not be locked.  allocvp() makes no changes
- *             to dvp.
- *
- *     upperdvp Holds the parent vnode to uppervp, generally used along
- *             with path component information to create a shadow of
- *             lowervp when uppervp does not exist.
- *
- *             upperdvp is referenced but unlocked on entry, and will be
- *             dereferenced on return.
- *
- *     uppervp Holds the new uppervp vnode to be stored in the 
- *             union_node we are allocating.  uppervp is referenced but
- *             not locked, and will be dereferenced on return.
- *
- *     lowervp Holds the new lowervp vnode to be stored in the
- *             union_node we are allocating.  lowervp is referenced but
- *             not locked, and will be dereferenced on return.
- * 
- *     cnp     Holds path component information to be coupled with
- *             lowervp and upperdvp to allow unionfs to create an uppervp
- *             later on.  Only used if lowervp is valid.  The contents
- *             of cnp is only valid for the duration of the call.
- *
- *     docache Determine whether this node should be entered in the
- *             cache or whether it should be destroyed as soon as possible.
- *
- * All union_nodes are maintained on a singly-linked
- * list.  New nodes are only allocated when they cannot
- * be found on this list.  Entries on the list are
- * removed when the vfs reclaim entry is called.
- *
- * A single lock is kept for the entire list.  This is
- * needed because the getnewvnode() function can block
- * waiting for a vnode to become free, in which case there
- * may be more than one process trying to get the same
- * vnode.  This lock is only taken if we are going to
- * call getnewvnode(), since the kernel itself is single-threaded.
- *
- * If an entry is found on the list, then call vget() to
- * take a reference.  This is done because there may be
- * zero references to it and so it needs to removed from
- * the vnode free list.
- */
-
-/* always called with union lock held */
-
-int
-union_allocvp(struct vnode **vpp,
-       struct mount *mp,
-       struct vnode *undvp,
-       struct vnode *dvp,
-       struct componentname *cnp,
-       struct vnode *uppervp,
-       struct vnode *lowervp,
-       int docache)
-{
-       int error;
-       struct union_node *un = NULL;
-       struct union_node *unp;
-       struct vnode *xlowervp = NULLVP;
-       struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
-       int hash = 0;           /* protected by docache */
-       int markroot;
-       int try;
-       struct vnode_fsparam vfsp;
-       enum vtype vtype;
-
-       if (uppervp == NULLVP && lowervp == NULLVP)
-               panic("union: unidentifiable allocation");
-
-       /*
-        * if both upper and lower vp are provided and are off different type
-        * consider lowervp as NULL
-        */
-       if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
-               xlowervp = lowervp;
-               lowervp = NULLVP;
-       }
-
-       /* detect the root vnode (and aliases) */
-       markroot = 0;
-       if ((uppervp == um->um_uppervp) &&
-           ((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
-               if (lowervp == NULLVP) {
-                       lowervp = um->um_lowervp;
-                       if (lowervp != NULLVP) {
-                               union_unlock();
-                               vnode_get(lowervp);
-                               union_lock();
-                       }
-               }
-               markroot = VROOT;
-       }
-
-loop:
-       if (!docache) {
-               un = NULL;
-       } else for (try = 0; try < 3; try++) {
-               switch (try) {
-               case 0:
-                       if (lowervp == NULLVP)
-                               continue;
-                       hash = UNION_HASH(uppervp, lowervp);
-                       break;
-
-               case 1:
-                       if (uppervp == NULLVP)
-                               continue;
-                       hash = UNION_HASH(uppervp, NULLVP);
-                       break;
-
-               case 2:
-                       if (lowervp == NULLVP)
-                               continue;
-                       /* Not sure how this path gets exercised ? */
-                       hash = UNION_HASH(NULLVP, lowervp);
-                       break;
-               }
-
-               while (union_list_lock(hash))
-                       continue;
-
-               for (un = unhead[hash].lh_first; un != 0;
-                                       un = un->un_cache.le_next) {
-                       if ((un->un_lowervp == lowervp ||
-                            un->un_lowervp == NULLVP) &&
-                           (un->un_uppervp == uppervp ||
-                            un->un_uppervp == NULLVP) &&
-                           (un->un_mount == mp)) {
-                               break;
-                       }
-               }
-
-               union_list_unlock(hash);
-
-               if (un)
-                       break;
-       }
-
-       if (un) {
-               /*
-                * Obtain a lock on the union_node.
-                * uppervp is locked, though un->un_uppervp
-                * may not be.  this doesn't break the locking
-                * hierarchy since in the case that un->un_uppervp
-                * is not yet locked it will be vnode_put'd and replaced
-                * with uppervp.
-                */
-
-               if (un->un_flags & UN_LOCKED) {
-                       un->un_flags |= UN_WANT;
-                       msleep((caddr_t) &un->un_flags, union_mtxp, PINOD, "union node locked", 0);
-                       goto loop;
-               }       
-               un->un_flags |= UN_LOCKED;
-                        
-               union_unlock();
-               if (UNIONTOV(un) == NULLVP)
-                       panic("null vnode in union node\n");
-               if (vnode_get(UNIONTOV(un))) {
-                       union_lock();
-                       un->un_flags &= ~UN_LOCKED;
-                       if ((un->un_flags & UN_WANT) == UN_WANT) {
-                               un->un_flags &=  ~UN_LOCKED;
-                               wakeup(&un->un_flags);
-                       }
-                       goto loop;
-               }
-               union_lock();
-
-               /*
-                * At this point, the union_node is locked,
-                * un->un_uppervp may not be locked, and uppervp
-                * is locked or nil.
-                */
-
-               /*
-                * Save information about the upper layer.
-                */
-               if (uppervp != un->un_uppervp) {
-                       union_newupper(un, uppervp);
-               } else if (uppervp) {
-                       union_unlock();
-                       vnode_put(uppervp);
-                       union_lock();
-               }
-
-               /*
-                * Save information about the lower layer.
-                * This needs to keep track of pathname
-                * and directory information which union_vn_create
-                * might need.
-                */
-               if (lowervp != un->un_lowervp) {
-                       union_newlower(un, lowervp);
-                       if (cnp && (lowervp != NULLVP)) {
-                               un->un_hash = cnp->cn_hash;
-                               union_unlock();
-                               MALLOC(un->un_path, caddr_t, cnp->cn_namelen+1,
-                                               M_TEMP, M_WAITOK);
-                               bcopy(cnp->cn_nameptr, un->un_path,
-                                               cnp->cn_namelen);
-                               vnode_get(dvp);
-                               union_lock();
-                               un->un_path[cnp->cn_namelen] = '\0';
-                               un->un_dirvp = dvp;
-                       }
-               } else if (lowervp) {
-                       union_unlock();
-                       vnode_put(lowervp);
-                       union_lock();
-               }
-               *vpp = UNIONTOV(un);
-               un->un_flags &= ~UN_LOCKED;
-               if ((un->un_flags & UN_WANT) == UN_WANT) {
-                       un->un_flags &= ~UN_WANT;
-                       wakeup(&un->un_flags);
-               }
-               return (0);
-       }
-
-       if (docache) {
-               /*
-                * otherwise lock the vp list while we call getnewvnode
-                * since that can block.
-                */ 
-               hash = UNION_HASH(uppervp, lowervp);
-
-               if (union_list_lock(hash))
-                       goto loop;
-       }
-
-       union_unlock();
-       MALLOC(unp, void *, sizeof(struct union_node), M_TEMP, M_WAITOK);
-       union_lock();
-
-       bzero(unp, sizeof(struct union_node));
-       un = unp;
-       un->un_uppervp = uppervp;
-       if (uppervp != NULLVP)
-               un->un_uppervid = vnode_vid(uppervp);
-       un->un_uppersz = VNOVAL;
-       un->un_lowervp = lowervp;
-       if (lowervp != NULLVP)
-               un->un_lowervid = vnode_vid(lowervp);
-       un->un_lowersz = VNOVAL;
-       un->un_pvp = undvp;
-       if (undvp != NULLVP)
-               vnode_get(undvp);
-       un->un_dircache = 0;
-       un->un_openl = 0;
-       un->un_mount = mp;
-       un->un_flags = UN_LOCKED;
-#ifdef FAULTFS
-       if (UNION_FAULTIN(um))
-               un->un_flags |= UN_FAULTFS;
-#endif
-
-       if (docache) {
-               /* Insert with lock held */
-               LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
-               un->un_flags |= UN_CACHED;
-               union_list_unlock(hash);
-       }
-
-       union_unlock();
-
-       if (uppervp)
-               vtype = uppervp->v_type;
-       else
-               vtype = lowervp->v_type;
-
-       bzero(&vfsp, sizeof(struct vnode_fsparam));
-       vfsp.vnfs_mp = mp;
-       vfsp.vnfs_vtype = vtype;
-       vfsp.vnfs_str = "unionfs";
-       vfsp.vnfs_dvp = undvp;
-       vfsp.vnfs_fsnode = unp;
-       vfsp.vnfs_cnp = cnp;
-       vfsp.vnfs_vops = union_vnodeop_p;
-       vfsp.vnfs_rdev = 0;
-       vfsp.vnfs_filesize = 0;
-       vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
-       vfsp.vnfs_marksystem = 0;
-       vfsp.vnfs_markroot = markroot;
-
-       error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, vpp);
-       if (error) {
-               /*  XXXXX Is this right ????  XXXXXXX */
-               if (uppervp) {
-                       vnode_put(uppervp);
-               }
-               if (lowervp)
-                       vnode_put(lowervp);
-
-               union_lock();
-               if (un->un_flags & UN_CACHED) {
-                       un->un_flags &= ~UN_CACHED;
-                       LIST_REMOVE(un, un_cache);
-               }
-               if (docache)
-                       union_list_unlock(hash);
-
-               FREE(unp, M_TEMP);
-
-               return (error);
-       }
-
-       if (cnp && (lowervp != NULLVP)) {
-               un->un_hash = cnp->cn_hash;
-               un->un_path = _MALLOC(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
-               bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
-               un->un_path[cnp->cn_namelen] = '\0';
-               vnode_get(dvp);
-               un->un_dirvp = dvp;
-       } else {
-               un->un_hash = 0;
-               un->un_path = 0;
-               un->un_dirvp = 0;
-       }
-
-       if (xlowervp)
-               vnode_put(xlowervp);
-
-       union_lock();
-
-       vnode_settag(*vpp, VT_UNION);
-       un->un_vnode = *vpp;
-       if (un->un_vnode->v_type == VDIR) {
-               if (un->un_uppervp == NULLVP) {
-                       panic("faulting fs and no upper vp for dir?");
-               }
-
-       }
-
-
-       un->un_flags &= ~UN_LOCKED;
-       if ((un->un_flags & UN_WANT) == UN_WANT) {
-               un->un_flags &=  ~UN_WANT;
-               wakeup(&un->un_flags);
-       } 
-
-       return(error);
-
-}
-
-/* always called with union lock held */
-int
-union_freevp(struct vnode *vp)
-{
-       struct union_node *un = VTOUNION(vp);
-
-       if (un->un_flags & UN_CACHED) {
-               un->un_flags &= ~UN_CACHED;
-               LIST_REMOVE(un, un_cache);
-       }
-
-       union_unlock();
-       if (un->un_pvp != NULLVP)
-               vnode_put(un->un_pvp);
-       if (un->un_uppervp != NULLVP)
-               vnode_put(un->un_uppervp);
-       if (un->un_lowervp != NULLVP)
-               vnode_put(un->un_lowervp);
-       if (un->un_dirvp != NULLVP)
-               vnode_put(un->un_dirvp);
-       if (un->un_path)
-               _FREE(un->un_path, M_TEMP);
-
-       FREE(vp->v_data, M_TEMP);
-       vp->v_data = 0;
-       union_lock();
-
-       return (0);
-}
-
-/*
- * copyfile.  copy the vnode (fvp) to the vnode (tvp)
- * using a sequence of reads and writes.  both (fvp)
- * and (tvp) are locked on entry and exit.
- */
-/* called with no union lock held */
-int
-union_copyfile(struct vnode *fvp, struct vnode *tvp, vfs_context_t context)
-{
-       char *bufp;
-       struct uio *auio;
-       char uio_buf [ UIO_SIZEOF(1) ];
-       int error = 0;
-
-       /*
-        * strategy:
-        * allocate a buffer of size MAXPHYSIO.
-        * loop doing reads and writes, keeping track
-        * of the current uio offset.
-        * give up at the first sign of trouble.
-        */
-
-       auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE,
-               UIO_READ /* will change */, &uio_buf, sizeof(uio_buf));
-
-       bufp = _MALLOC(MAXPHYSIO, M_TEMP, M_WAITOK);
-       if (bufp == NULL) {
-               return ENOMEM;
-       }
-
-       /* ugly loop follows... */
-       do {
-               off_t offset = uio_offset(auio);
-
-               uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
-               uio_addiov(auio, (uintptr_t)bufp, MAXPHYSIO);
-               error = VNOP_READ(fvp, auio, 0, context);
-
-               if (error == 0) {
-                       user_ssize_t resid = uio_resid(auio);
-
-                       uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
-                       uio_addiov(auio, (uintptr_t)bufp, MAXPHYSIO - resid);
-
-                       if (uio_resid(auio) == 0)
-                               break;
-
-                       do {
-                               error = VNOP_WRITE(tvp, auio, 0, context);
-                       } while ((uio_resid(auio) > 0) && (error == 0));
-               }
-
-       } while (error == 0);
-
-       _FREE(bufp, M_TEMP);
-       return (error);
-}
-
-/*
- * (un) is assumed to be locked on entry and remains
- * locked on exit.
- */
-/* always called with union lock held */
-int
-union_copyup(struct union_node *un, int docopy, vfs_context_t context)
-{
-       int error;
-       struct vnode *lvp, *uvp;
-       struct vnode_attr vattr;
-       mode_t  cmode = 0;
-
-       
-       lvp = un->un_lowervp;
-
-       union_unlock();
-
-       if (UNNODE_FAULTIN(un)) {
-               /* Need to inherit exec mode in faulting fs */
-               VATTR_INIT(&vattr);
-               VATTR_WANTED(&vattr, va_flags);
-               if (vnode_getattr(lvp, &vattr, context) == 0 )
-                       cmode = vattr.va_mode;
-               
-       }
-       error = union_vn_create(&uvp, un, cmode, context);
-       if (error) {
-               union_lock();
-               if (error == EEXIST) {
-                       if (uvp != NULLVP) {
-                               union_newupper(un, uvp);
-                               error = 0;      
-                       } 
-               }
-               return (error);
-       }
-
-       union_lock();
-       /* at this point, uppervp is locked */
-       union_newupper(un, uvp);
-       union_unlock();
-
-
-       if (docopy) {
-               /*
-                * XX - should not ignore errors
-                * from vnop_close
-                */
-               error = VNOP_OPEN(lvp, FREAD, context);
-               if (error == 0) {
-                       error = union_copyfile(lvp, uvp, context);
-                       (void) VNOP_CLOSE(lvp, FREAD, context);
-               }
-#ifdef UNION_DIAGNOSTIC
-               if (error == 0)
-                       uprintf("union: copied up %s\n", un->un_path);
-#endif
-
-       }
-       union_vn_close(uvp, FWRITE, context);
-
-       /*
-        * Subsequent IOs will go to the top layer, so
-        * call close on the lower vnode and open on the
-        * upper vnode to ensure that the filesystem keeps
-        * its references counts right.  This doesn't do
-        * the right thing with (cred) and (FREAD) though.
-        * Ignoring error returns is not right, either.
-        */
-
-       /* No need to hold the lock as the union node should be locked for this(it is in faultin mode) */
-       if (error == 0) {
-               int i;
-
-               for (i = 0; i < un->un_openl; i++) {
-                       (void) VNOP_CLOSE(lvp, FREAD, context);
-                       (void) VNOP_OPEN(uvp, FREAD, context);
-               }
-               un->un_openl = 0;
-       }
-
-       union_lock();
-
-       return (error);
-
-}
-
-
-int 
-union_faultin_copyup(struct vnode **vpp, vnode_t udvp, vnode_t lvp, struct componentname * cnp, vfs_context_t context)
-{
-       int error;
-       struct vnode *uvp;
-       struct vnode_attr vattr;
-       struct vnode_attr *vap;
-       mode_t  cmode = 0;
-       int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
-       struct proc * p = vfs_context_proc(context);
-       struct componentname cn;
-       
-
-       vap = &vattr;
-       VATTR_INIT(vap);
-       VATTR_WANTED(vap, va_flags);
-       if (vnode_getattr(lvp, vap, context) == 0 )
-               cmode = vattr.va_mode;
-               
-       *vpp = NULLVP;
-
-
-       if (cmode == (mode_t)0)
-               cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
-       else
-               cmode = cmode & ~p->p_fd->fd_cmask;
-
-
-       /*
-        * Build a new componentname structure (for the same
-        * reasons outlines in union_mkshadow()).
-        * The difference here is that the file is owned by
-        * the current user, rather than by the person who
-        * did the mount, since the current user needs to be
-        * able to write the file (that's why it is being
-        * copied in the first place).
-        */
-       bzero(&cn, sizeof(struct componentname));
-
-       cn.cn_namelen = cnp->cn_namelen;
-       cn.cn_pnbuf = (caddr_t) _MALLOC_ZONE(cn.cn_namelen+1,
-                                               M_NAMEI, M_WAITOK);
-       cn.cn_pnlen = cn.cn_namelen+1;
-       bcopy(cnp->cn_nameptr, cn.cn_pnbuf, cn.cn_namelen+1);
-       cn.cn_nameiop = CREATE;
-       cn.cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN|UNIONCREATED);
-       cn.cn_context = context;
-       cn.cn_nameptr = cn.cn_pnbuf;
-       cn.cn_hash = 0;
-       cn.cn_consume = 0;
-
-       /*
-        * Pass dvp unlocked and referenced on call to relookup().
-        *
-        * If an error occurs, dvp will be returned unlocked and dereferenced.
-        */
-       if ((error = relookup(udvp, &uvp, &cn)) != 0) {
-               goto out;
-       }
-
-       /*
-        * If no error occurs, dvp will be returned locked with the reference
-        * left as before, and vpp will be returned referenced and locked.
-        */
-       if (uvp) {
-               *vpp = uvp;
-               error = EEXIST;
-               goto out;
-       }
-
-       /*
-        * Good - there was no race to create the file
-        * so go ahead and create it.  The permissions
-        * on the file will be 0666 modified by the
-        * current user's umask.  Access to the file, while
-        * it is unioned, will require access to the top *and*
-        * bottom files.  Access when not unioned will simply
-        * require access to the top-level file.
-        *
-        * TODO: confirm choice of access permissions.
-        *       decide on authorisation behaviour
-        */
-       
-       VATTR_INIT(vap);
-       VATTR_SET(vap, va_type, VREG);
-       VATTR_SET(vap, va_mode, cmode);
-
-       cn.cn_flags |= (UNIONCREATED);
-       if ((error = vn_create(udvp, &uvp, &cn, vap, 0, context)) != 0) {
-               goto out;
-       }
-
-       
-       if ((error = VNOP_OPEN(uvp, fmode, context)) != 0) {
-               vn_clearunionwait(uvp, 0);
-               vnode_recycle(uvp);
-               vnode_put(uvp);
-               goto out;
-       }
-
-       error = vnode_ref_ext(uvp, fmode);
-       if (error ) {
-               vn_clearunionwait(uvp, 0);
-               VNOP_CLOSE(uvp, fmode, context);
-               vnode_recycle(uvp);
-               vnode_put(uvp);
-               goto out;
-       }
-
-
-       /*
-        * XX - should not ignore errors
-        * from vnop_close
-        */
-       error = VNOP_OPEN(lvp, FREAD, context);
-       if (error == 0) {
-               error = union_copyfile(lvp, uvp, context);
-               (void) VNOP_CLOSE(lvp, FREAD, context);
-       }
-
-       VNOP_CLOSE(uvp, fmode, context);
-       vnode_rele_ext(uvp, fmode, 0);
-       vn_clearunionwait(uvp, 0);
-
-       *vpp = uvp;
-out:
-       if ((cn.cn_flags & HASBUF) == HASBUF) {
-               FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
-               cn.cn_flags &= ~HASBUF;
-       }
-       return (error);
-}
-
-
-/*
- *     union_relookup:
- *
- *     dvp should be locked on entry and will be locked on return.  No
- *     net change in the ref count will occur.
- *
- *     If an error is returned, *vpp will be invalid, otherwise it
- *     will hold a locked, referenced vnode.  If *vpp == dvp then
- *     remember that only one exclusive lock is held.
- */
-
-/* No union lock held for this call */
-static int
-union_relookup(
-#ifdef XXX_HELP_ME
-       struct union_mount *um,
-#else  /* !XXX_HELP_ME */
-       __unused struct union_mount *um,
-#endif /* !XXX_HELP_ME */
-       struct vnode *dvp,
-       struct vnode **vpp,
-       struct componentname *cnp,
-       struct componentname *cn,
-       char *path,
-       int pathlen)
-{
-       int error;
-
-       /*
-        * A new componentname structure must be faked up because
-        * there is no way to know where the upper level cnp came
-        * from or what it is being used for.  This must duplicate
-        * some of the work done by NDINIT, some of the work done
-        * by namei, some of the work done by lookup and some of
-        * the work done by vnop_lookup when given a CREATE flag.
-        * Conclusion: Horrible.
-        */
-       cn->cn_namelen = pathlen;
-       cn->cn_pnbuf = _MALLOC_ZONE(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
-       cn->cn_pnlen = cn->cn_namelen+1;
-       bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
-       cn->cn_pnbuf[cn->cn_namelen] = '\0';
-
-       cn->cn_nameiop = CREATE;
-       cn->cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN );
-#ifdef XXX_HELP_ME
-       cn->cn_proc = cnp->cn_proc;
-       if (um->um_op == UNMNT_ABOVE)
-               cn->cn_cred = cnp->cn_cred;
-       else
-               cn->cn_cred = um->um_cred;
-#endif
-       cn->cn_context = cnp->cn_context;       /* XXX !UNMNT_ABOVE  case ??? */
-       cn->cn_nameptr = cn->cn_pnbuf;
-       cn->cn_hash = 0;
-       cn->cn_consume = cnp->cn_consume;
-
-       vnode_get(dvp);
-       error = relookup(dvp, vpp, cn);
-       vnode_put(dvp);
-
-       return (error);
-}
-
-/*
- * Create a shadow directory in the upper layer.
- * The new vnode is returned locked.
- *
- * (um) points to the union mount structure for access to the
- * the mounting process's credentials.
- * (dvp) is the directory in which to create the shadow directory,
- * It is locked (but not ref'd) on entry and return.
- * (cnp) is the component name to be created.
- * (vpp) is the returned newly created shadow directory, which
- * is returned locked and ref'd
- */
-/* No union lock held for this call */
-int
-union_mkshadow(um, dvp, cnp, vpp)
-       struct union_mount *um;
-       struct vnode *dvp;
-       struct componentname *cnp;
-       struct vnode **vpp;
-{
-       int error;
-       struct vnode_attr va;
-       struct componentname cn;
-
-       bzero(&cn, sizeof(struct componentname));
-
-
-       error = union_relookup(um, dvp, vpp, cnp, &cn,
-                       cnp->cn_nameptr, cnp->cn_namelen);
-       if (error) 
-               goto out;
-
-       if (*vpp) {
-               error = EEXIST;
-               goto out;
-       }
-
-       /*
-        * Policy: when creating the shadow directory in the
-        * upper layer, create it owned by the user who did
-        * the mount, group from parent directory, and mode
-        * 777 modified by umask (ie mostly identical to the
-        * mkdir syscall).  (jsp, kb)
-        */
-
-       VATTR_INIT(&va);
-       VATTR_SET(&va, va_type, VDIR);
-       VATTR_SET(&va, va_mode, um->um_cmode);
-
-       error = vn_create(dvp, vpp, &cn, &va, 0, cnp->cn_context);
-out:
-       if ((cn.cn_flags & HASBUF) == HASBUF) {
-               FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
-               cn.cn_flags &= ~HASBUF;
-       }
-       return (error);
-}
-
-/*
- * Create a whiteout entry in the upper layer.
- *
- * (um) points to the union mount structure for access to the
- * the mounting process's credentials.
- * (dvp) is the directory in which to create the whiteout.
- * it is locked on entry and exit.
- * (cnp) is the componentname to be created.
- */
-/* No union lock held for this call */
-int
-union_mkwhiteout(um, dvp, cnp, path)
-       struct union_mount *um;
-       struct vnode *dvp;
-       struct componentname *cnp;
-       char *path;
-{
-       int error;
-       struct vnode *wvp;
-       struct componentname cn;
-
-       bzero(&cn, sizeof(struct componentname));
-
-       error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
-       if (error) {
-               goto out;
-       }
-       if (wvp) {
-               error = EEXIST;
-               goto out;
-       }
-
-       error = VNOP_WHITEOUT(dvp, &cn, CREATE, cnp->cn_context);
-
-out:
-       if ((cn.cn_flags & HASBUF) == HASBUF) {
-               FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
-               cn.cn_flags &= ~HASBUF;
-       }
-       return (error);
-}
-
-
-/*
- * union_vn_create: creates and opens a new shadow file
- * on the upper union layer.  This function is similar
- * in spirit to calling vn_open() but it avoids calling namei().
- * The problem with calling namei() is that a) it locks too many
- * things, and b) it doesn't start at the "right" directory,
- * whereas relookup() is told where to start.
- *
- * On entry, the vnode associated with un is locked.  It remains locked
- * on return.
- *
- * If no error occurs, *vpp contains a locked referenced vnode for your
- * use.  If an error occurs *vpp iis undefined.
- */
-/* called with no union lock held */
-int
-union_vn_create(struct vnode **vpp, struct union_node *un, mode_t cmode, vfs_context_t  context)
-{
-       struct vnode *vp;
-       struct vnode_attr vat;
-       struct vnode_attr *vap = &vat;
-       int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
-       int error;
-       struct proc * p = vfs_context_proc(context);
-       struct componentname cn;
-
-       bzero(&cn, sizeof(struct componentname));
-       *vpp = NULLVP;
-
-       if (cmode == (mode_t)0)
-               cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
-       else
-               cmode = cmode & ~p->p_fd->fd_cmask;
-
-
-       /*
-        * Build a new componentname structure (for the same
-        * reasons outlines in union_mkshadow()).
-        * The difference here is that the file is owned by
-        * the current user, rather than by the person who
-        * did the mount, since the current user needs to be
-        * able to write the file (that's why it is being
-        * copied in the first place).
-        */
-       cn.cn_namelen = strlen(un->un_path);
-       cn.cn_pnbuf = (caddr_t) _MALLOC_ZONE(cn.cn_namelen+1,
-                                               M_NAMEI, M_WAITOK);
-       cn.cn_pnlen = cn.cn_namelen+1;
-       bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
-       cn.cn_nameiop = CREATE;
-       if (UNNODE_FAULTIN(un))
-               cn.cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN|UNIONCREATED);
-       else
-               cn.cn_flags = (HASBUF|SAVENAME|SAVESTART|ISLASTCN);
-       cn.cn_context = context;
-       cn.cn_nameptr = cn.cn_pnbuf;
-       cn.cn_hash = un->un_hash;
-       cn.cn_consume = 0;
-
-       /*
-        * Pass dvp unlocked and referenced on call to relookup().
-        *
-        * If an error occurs, dvp will be returned unlocked and dereferenced.
-        */
-       vnode_get(un->un_dirvp);
-       if ((error = relookup(un->un_dirvp, &vp, &cn)) != 0) {
-               vnode_put(un->un_dirvp);
-               goto out;
-       }
-       vnode_put(un->un_dirvp);
-
-       /*
-        * If no error occurs, dvp will be returned locked with the reference
-        * left as before, and vpp will be returned referenced and locked.
-        */
-       if (vp) {
-               *vpp = vp;
-               error = EEXIST;
-               goto out;
-       }
-
-       /*
-        * Good - there was no race to create the file
-        * so go ahead and create it.  The permissions
-        * on the file will be 0666 modified by the
-        * current user's umask.  Access to the file, while
-        * it is unioned, will require access to the top *and*
-        * bottom files.  Access when not unioned will simply
-        * require access to the top-level file.
-        *
-        * TODO: confirm choice of access permissions.
-        *       decide on authorisation behaviour
-        */
-       
-       VATTR_INIT(vap);
-       VATTR_SET(vap, va_type, VREG);
-       VATTR_SET(vap, va_mode, cmode);
-
-       if ((error = vn_create(un->un_dirvp, &vp, &cn, vap, 0, context)) != 0) {
-               goto out;
-       }
-
-       if ((error = VNOP_OPEN(vp, fmode, context)) != 0) {
-               vnode_put(vp);
-               goto out;
-       }
-
-       vnode_lock(vp);
-       if (++vp->v_writecount <= 0)
-               panic("union: v_writecount");
-       vnode_unlock(vp);
-       *vpp = vp;
-       error = 0;
-
-out:
-       if ((cn.cn_flags & HASBUF) == HASBUF) {
-               FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
-               cn.cn_flags &= ~HASBUF;
-       }
-       return(error);
-}
-
-/* called with no union lock held */
-static int
-union_vn_close(struct vnode *vp, int fmode, vfs_context_t context)
-{
-
-       if (fmode & FWRITE) {
-               vnode_lock(vp);
-               --vp->v_writecount;
-               vnode_unlock(vp);
-       }
-       return (VNOP_CLOSE(vp, fmode, context));
-}
-
-/*
- *     union_removed_upper:
- *
- *     An upper-only file/directory has been removed; un-cache it so
- *     that unionfs vnode gets reclaimed and the last uppervp reference
- *     disappears.
- *
- *     Called with union_node unlocked.
- */
-/* always called with union lock held */
-void
-union_removed_upper(un)
-       struct union_node *un;
-{
-       union_newupper(un, NULLVP);
-       if (un->un_flags & UN_CACHED) {
-               un->un_flags &= ~UN_CACHED;
-               LIST_REMOVE(un, un_cache);
-       }
-
-}
-
-#if 0
-struct vnode *
-union_lowervp(vp)
-       struct vnode *vp;
-{
-       struct union_node *un = VTOUNION(vp);
-
-       if ((un->un_lowervp != NULLVP) &&
-           (vp->v_type == un->un_lowervp->v_type)) {
-               if (vnode_get(un->un_lowervp) == 0)
-                       return (un->un_lowervp);
-       }
-
-       return (NULLVP);
-}
-#endif
-
-/*
- * Determine whether a whiteout is needed
- * during a remove/rmdir operation.
- */
-/* called with no union lock held */
-int
-union_dowhiteout(struct union_node *un, vfs_context_t ctx)
-{
-       struct vnode_attr va;
-
-       if (UNNODE_FAULTIN(un))
-               return(0);
-
-       if ((un->un_lowervp != NULLVP) )
-               return (1);
-
-       VATTR_INIT(&va);
-       VATTR_WANTED(&va, va_flags);
-       if (vnode_getattr(un->un_uppervp, &va, ctx) == 0 &&
-           (va.va_flags & OPAQUE))
-               return (1);
-
-       return (0);
-}
-
-/* called with no union lock held */
-static void
-union_dircache_r(struct vnode *vp, struct vnode ***vppp, int *cntp)
-{
-       struct union_node *un;
-
-       if (vp->v_op != union_vnodeop_p) {
-               if (vppp) {
-                       vnode_get(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);
-}
-
-/* called with no union lock held */
-struct vnode *
-union_dircache(struct vnode *vp, __unused vfs_context_t context)
-{
-       int count;
-       struct vnode *nvp, *lvp;
-       struct vnode **vpp;
-       struct vnode **dircache, **newdircache;
-       struct union_node *un;
-       int error;
-       int alloced = 0;
-
-       union_lock();
-       newdircache = NULL;
-
-       nvp = NULLVP;
-       un = VTOUNION(vp);
-
-       dircache = un->un_dircache;
-       if (dircache == 0) {
-               union_unlock();
-               count = 0;
-               union_dircache_r(vp, 0, &count);
-               count++;
-#if 0
-               /* too bad; we need Union now! */
-#if MAC_XXX
-                panic("MAC Framework doesn't support unionfs (yet)\n");
-#endif /* MAC */
-#endif
-
-               dircache = (struct vnode **)
-                               _MALLOC(count * sizeof(struct vnode *),
-                                       M_TEMP, M_WAITOK);
-               if (dircache == NULL) {
-                       goto out;
-               }
-               newdircache = dircache;
-               alloced = 1;
-               vpp = dircache;
-               union_dircache_r(vp, &vpp, &count);
-               *vpp = NULLVP;
-               vpp = dircache + 1;
-               union_lock();
-       } else {
-               vpp = dircache;
-               do {
-                       if (*vpp++ == un->un_uppervp)
-                               break;
-               } while (*vpp != NULLVP);
-       }
-
-       lvp = *vpp;
-       union_unlock();
-       if (lvp == NULLVP) {
-               goto out;
-       }
-
-       vnode_get(lvp);
-       union_lock();
-
-       error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, lvp, NULLVP, 0);
-       if (error) {
-               union_unlock();
-               vnode_put(lvp);
-               goto out;
-       }
-
-       un->un_dircache = 0;
-       un = VTOUNION(nvp);
-#if 0
-       if ((alloced != 0) && (un->un_dircache != 0)) {
-               union_unlock();
-               for (vpp = newdircache; *vpp != NULLVP; vpp++)
-                       vnode_put(*vpp);
-               _FREE(newdircache, M_TEMP);
-               newdircache = NULL;
-               union_lock();
-               if (nvp != NULLVP)
-                       union_freevp(nvp);
-               goto loop;
-       }
-#endif
-       un->un_dircache = dircache;
-       un->un_flags |= UN_DIRENVN;
-       
-       newdircache = NULL;
-       union_unlock();
-       return (nvp);
-
-out:
-       /*
-        * If we allocated a new dircache and couldn't attach
-        * it to a new vp, free the resources we allocated.
-        */
-       if (newdircache) {
-               for (vpp = newdircache; *vpp != NULLVP; vpp++)
-                       vnode_put(*vpp);
-               _FREE(newdircache, M_TEMP);
-       }
-       return (NULLVP);
-}
-
-/*
- * Module glue to remove #ifdef UNION from vfs_syscalls.c
- */
-/* Called with no union lock, the union_dircache takes locks when necessary */
-static int
-union_dircheck(struct vnode **vpp, struct fileproc *fp, vfs_context_t ctx)
-{
-       int error = 0;
-       vnode_t vp = *vpp;
-       
-       if (vp->v_op == union_vnodeop_p) {
-               struct vnode *lvp;
-
-               lvp = union_dircache(vp, ctx);
-               if (lvp != NULLVP) {
-                       struct vnode_attr va;
-                       /*
-                        * If the directory is opaque,
-                        * then don't show lower entries
-                        */
-                       VATTR_INIT(&va);
-                       VATTR_WANTED(&va, va_flags);
-                       error = vnode_getattr(vp, &va, ctx);
-                       if (va.va_flags & OPAQUE) {
-                               vnode_put(lvp);
-                               lvp = NULL;
-                       }
-               }
-
-               if (lvp != NULLVP) {
-#if CONFIG_MACF
-                       error = mac_vnode_check_open(ctx, lvp, FREAD);
-                       if (error) {
-                               vnode_put(lvp);
-                               return(error);
-                       }
-#endif /* MAC */
-                       error = VNOP_OPEN(lvp, FREAD, ctx);
-                       if (error) {
-                               vnode_put(lvp);
-                               return(error);
-                       }
-                       vnode_ref(lvp);
-                       fp->f_fglob->fg_data = (caddr_t) lvp;
-                       fp->f_fglob->fg_offset = 0;
-
-                       error = VNOP_CLOSE(vp, FREAD, ctx);
-                       vnode_rele(vp);
-                       vnode_put(vp);
-                       if (error)
-                               return(error);
-
-                       *vpp = lvp;
-                       return -1;      /* goto unionread */
-               }
-       }
-       return error;
-}
-
-/*  called from inactive with union lock held */
-void
-union_dircache_free(struct union_node *un)
-{
-       struct vnode **vpp;
-
-       vpp = un->un_dircache;
-       un->un_dircache = NULL;
-       union_unlock(); 
-       
-       for (; *vpp != NULLVP; vpp++)
-               vnode_put(*vpp);
-       _FREE(un->un_dircache, M_TEMP);
-       union_lock();
-}
-