[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/bsd/vfs/vfs_cache.c

Version: -  FREEBSD  -  FREEBSD8  -  FREEBSD7  -  FREEBSD72  -  FREEBSD71  -  FREEBSD70  -  FREEBSD6  -  FREEBSD64  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  OPENSOLARIS  -  minix-3-1-1  -  FREEBSD-LIBC  -  FREEBSD7-LIBC  -  FREEBSD6-LIBC  -  GLIBC27 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
    3  *
    4  * @APPLE_LICENSE_HEADER_START@
    5  * 
    6  * The contents of this file constitute Original Code as defined in and
    7  * are subject to the Apple Public Source License Version 1.1 (the
    8  * "License").  You may not use this file except in compliance with the
    9  * License.  Please obtain a copy of the License at
   10  * http://www.apple.com/publicsource and read it before using this file.
   11  * 
   12  * This Original Code and all software distributed under the License are
   13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
   14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
   15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
   16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
   17  * License for the specific language governing rights and limitations
   18  * under the License.
   19  * 
   20  * @APPLE_LICENSE_HEADER_END@
   21  */
   22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
   23 /*
   24  * Copyright (c) 1989, 1993, 1995
   25  *      The Regents of the University of California.  All rights reserved.
   26  *
   27  * This code is derived from software contributed to Berkeley by
   28  * Poul-Henning Kamp of the FreeBSD Project.
   29  *
   30  * Redistribution and use in source and binary forms, with or without
   31  * modification, are permitted provided that the following conditions
   32  * are met:
   33  * 1. Redistributions of source code must retain the above copyright
   34  *    notice, this list of conditions and the following disclaimer.
   35  * 2. Redistributions in binary form must reproduce the above copyright
   36  *    notice, this list of conditions and the following disclaimer in the
   37  *    documentation and/or other materials provided with the distribution.
   38  * 3. All advertising materials mentioning features or use of this software
   39  *    must display the following acknowledgement:
   40  *      This product includes software developed by the University of
   41  *      California, Berkeley and its contributors.
   42  * 4. Neither the name of the University nor the names of its contributors
   43  *    may be used to endorse or promote products derived from this software
   44  *    without specific prior written permission.
   45  *
   46  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   47  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   49  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   50  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   51  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   52  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   53  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   54  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   55  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   56  * SUCH DAMAGE.
   57  *
   58  *
   59  *      @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95
   60  */
   61 #include <sys/param.h>
   62 #include <sys/systm.h>
   63 #include <sys/time.h>
   64 #include <sys/mount_internal.h>
   65 #include <sys/vnode_internal.h>
   66 #include <sys/namei.h>
   67 #include <sys/errno.h>
   68 #include <sys/malloc.h>
   69 #include <sys/kauth.h>
   70 #include <sys/user.h>
   71 
   72 /*
   73  * Name caching works as follows:
   74  *
   75  * Names found by directory scans are retained in a cache
   76  * for future reference.  It is managed LRU, so frequently
   77  * used names will hang around.  Cache is indexed by hash value
   78  * obtained from (vp, name) where vp refers to the directory
   79  * containing name.
   80  *
   81  * If it is a "negative" entry, (i.e. for a name that is known NOT to
   82  * exist) the vnode pointer will be NULL.
   83  *
   84  * Upon reaching the last segment of a path, if the reference
   85  * is for DELETE, or NOCACHE is set (rewrite), and the
   86  * name is located in the cache, it will be dropped.
   87  */
   88 
   89 /*
   90  * Structures associated with name cacheing.
   91  */
   92 
   93 LIST_HEAD(nchashhead, namecache) *nchashtbl;    /* Hash Table */
   94 u_long  nchashmask;
   95 u_long  nchash;                         /* size of hash table - 1 */
   96 long    numcache;                       /* number of cache entries allocated */
   97 int     desiredNodes;
   98 int     desiredNegNodes;
   99 TAILQ_HEAD(, namecache) nchead;         /* chain of all name cache entries */
  100 TAILQ_HEAD(, namecache) neghead;        /* chain of only negative cache entries */
  101 struct  nchstats nchstats;              /* cache effectiveness statistics */
  102 
  103 /* vars for name cache list lock */
  104 lck_grp_t * namecache_lck_grp;
  105 lck_grp_attr_t * namecache_lck_grp_attr;
  106 lck_attr_t * namecache_lck_attr;
  107 lck_mtx_t * namecache_mtx_lock;
  108 
  109 static vnode_t cache_lookup_locked(vnode_t dvp, struct componentname *cnp);
  110 static int  remove_name_locked(const char *);
  111 static char *add_name_locked(const char *, size_t, u_int, u_int);
  112 static void init_string_table(void);
  113 static void cache_delete(struct namecache *, int);
  114 static void dump_string_table(void);
  115 
  116 static void init_crc32(void);
  117 static unsigned int crc32tab[256];
  118 
  119 
  120 #define NCHHASH(dvp, hash_val) \
  121         (&nchashtbl[(dvp->v_id ^ (hash_val)) & nchashmask])
  122 
  123 
  124 
  125 //
  126 // This function builds the path to a filename in "buff".  The
  127 // length of the buffer *INCLUDING* the trailing zero byte is
  128 // returned in outlen.  NOTE: the length includes the trailing
  129 // zero byte and thus the length is one greater than what strlen
  130 // would return.  This is important and lots of code elsewhere
  131 // in the kernel assumes this behavior.
  132 //
  133 int
  134 build_path(vnode_t first_vp, char *buff, int buflen, int *outlen)
  135 {
  136         vnode_t vp = first_vp;
  137         char *end, *str;
  138         int   len, ret=0, counter=0;
  139 
  140         end = &buff[buflen-1];
  141         *end = '\0';
  142 
  143         /*
  144          * if this is the root dir of a file system...
  145          */
  146         if (vp && (vp->v_flag & VROOT) && vp->v_mount) {
  147                 /*
  148                  * then if it's the root fs, just put in a '/' and get out of here
  149                  */
  150                 if (vp->v_mount->mnt_flag & MNT_ROOTFS) {
  151                         *--end = '/';
  152                         goto out;
  153                 } else {
  154                         /*
  155                          * else just use the covered vnode to get the mount path
  156                          */
  157                         vp = vp->v_mount->mnt_vnodecovered;
  158                 }
  159         }
  160         name_cache_lock();
  161 
  162         while (vp && vp->v_parent != vp) {
  163                 /*
  164                  * the maximum depth of a file system hierarchy is MAXPATHLEN/2
  165                  * (with single-char names separated by slashes).  we panic if
  166                  * we've ever looped more than that.
  167                  */
  168                 if (counter++ > MAXPATHLEN/2) {
  169                         panic("build_path: vnode parent chain is too long! vp 0x%x\n", vp);
  170                 }
  171                 str = vp->v_name;
  172 
  173                 if (str == NULL) {
  174                         if (vp->v_parent != NULL) {
  175                                 ret = EINVAL;
  176                         }
  177                         break;
  178                 }
  179                 len = strlen(str);
  180 
  181                 /*
  182                  * check that there's enough space (make sure to include space for the '/')
  183                  */
  184                 if ((end - buff) < (len + 1)) {
  185                         ret = ENOSPC;
  186                         break;
  187                 }
  188                 /*
  189                  * copy it backwards
  190                  */
  191                 str += len;
  192 
  193                 for (; len > 0; len--) {
  194                        *--end = *--str;
  195                 }
  196                 /*
  197                  * put in the path separator
  198                  */
  199                 *--end = '/';
  200 
  201                 /*
  202                  * walk up the chain (as long as we're not the root)  
  203                  */
  204                 if (vp == first_vp && (vp->v_flag & VROOT)) {
  205                         if (vp->v_mount && vp->v_mount->mnt_vnodecovered) {
  206                                 vp = vp->v_mount->mnt_vnodecovered->v_parent;
  207                         } else {
  208                                 vp = NULLVP;
  209                         }
  210                 } else {
  211                         vp = vp->v_parent;
  212                 }
  213                 /*
  214                  * check if we're crossing a mount point and
  215                  * switch the vp if we are.
  216                  */
  217                 if (vp && (vp->v_flag & VROOT) && vp->v_mount) {
  218                         vp = vp->v_mount->mnt_vnodecovered;
  219                 }
  220         }
  221         name_cache_unlock();
  222 out:
  223         /*
  224          * slide it down to the beginning of the buffer
  225          */
  226         memmove(buff, end, &buff[buflen] - end);
  227     
  228         *outlen = &buff[buflen] - end;  // length includes the trailing zero byte
  229  
  230         return ret;
  231 }
  232 
  233 
  234 /*
  235  * return NULLVP if vp's parent doesn't
  236  * exist, or we can't get a valid iocount
  237  * else return the parent of vp
  238  */
  239 vnode_t
  240 vnode_getparent(vnode_t vp)
  241 {
  242         vnode_t pvp = NULLVP;
  243         int     pvid;
  244 
  245         name_cache_lock();
  246         /*
  247          * v_parent is stable behind the name_cache lock
  248          * however, the only thing we can really guarantee
  249          * is that we've grabbed a valid iocount on the
  250          * parent of 'vp' at the time we took the name_cache lock...
  251          * once we drop the lock, vp could get re-parented
  252          */
  253         if ( (pvp = vp->v_parent) != NULLVP ) {
  254                 pvid = pvp->v_id;
  255 
  256                 name_cache_unlock();
  257 
  258                 if (vnode_getwithvid(pvp, pvid) != 0)
  259                         pvp = NULL;
  260         } else
  261                 name_cache_unlock();
  262 
  263         return (pvp);
  264 }
  265 
  266 char *
  267 vnode_getname(vnode_t vp)
  268 {
  269         char *name = NULL;
  270 
  271         name_cache_lock();
  272         
  273         if (vp->v_name)
  274                 name = add_name_locked(vp->v_name, strlen(vp->v_name), 0, 0);
  275         name_cache_unlock();
  276 
  277         return (name);
  278 }
  279 
  280 void
  281 vnode_putname(char *name)
  282 {
  283         name_cache_lock();
  284 
  285         remove_name_locked(name);
  286 
  287         name_cache_unlock();
  288 }
  289 
  290 
  291 /*
  292  * if VNODE_UPDATE_PARENT, and we can take
  293  * a reference on dvp, then update vp with
  294  * it's new parent... if vp already has a parent,
  295  * then drop the reference vp held on it
  296  *
  297  * if VNODE_UPDATE_NAME,
  298  * then drop string ref on v_name if it exists, and if name is non-NULL
  299  * then pick up a string reference on name and record it in v_name...
  300  * optionally pass in the length and hashval of name if known
  301  *
  302  * if VNODE_UPDATE_CACHE, flush the name cache entries associated with vp
  303  */
  304 void
  305 vnode_update_identity(vnode_t vp, vnode_t dvp, char *name, int name_len, int name_hashval, int flags)
  306 {
  307         struct  namecache *ncp;
  308         vnode_t old_parentvp = NULLVP;
  309 
  310 
  311         if (flags & VNODE_UPDATE_PARENT) {
  312                 if (dvp && vnode_ref(dvp) != 0)
  313                         dvp = NULLVP;
  314         } else
  315                 dvp = NULLVP;
  316         name_cache_lock();
  317 
  318         if ( (flags & VNODE_UPDATE_NAME) && (name != vp->v_name) ) {
  319                 if (vp->v_name != NULL) {
  320                         remove_name_locked(vp->v_name);
  321                         vp->v_name = NULL;
  322                 }
  323                 if (name && *name) {
  324                         if (name_len == 0)
  325                                 name_len = strlen(name);
  326                         vp->v_name = add_name_locked(name, name_len, name_hashval, 0);
  327                 }
  328         }
  329         if (flags & VNODE_UPDATE_PARENT) {
  330                 if (dvp != vp && dvp != vp->v_parent) {
  331                         old_parentvp = vp->v_parent;
  332                         vp->v_parent = dvp;
  333                         dvp = NULLVP;
  334 
  335                         if (old_parentvp)
  336                                 flags |= VNODE_UPDATE_CACHE;
  337                 }
  338         }
  339         if (flags & VNODE_UPDATE_CACHE) {
  340                 while ( (ncp = LIST_FIRST(&vp->v_nclinks)) )
  341                         cache_delete(ncp, 1);
  342         }
  343         name_cache_unlock();
  344         
  345         if (dvp != NULLVP)
  346                 vnode_rele(dvp);
  347         
  348         if (old_parentvp) {
  349                 struct  uthread *ut;
  350 
  351                 ut = get_bsdthread_info(current_thread());
  352 
  353                 /*
  354                  * indicated to vnode_rele that it shouldn't do a
  355                  * vnode_reclaim at this time... instead it will
  356                  * chain the vnode to the uu_vreclaims list...
  357                  * we'll be responsible for calling vnode_reclaim
  358                  * on each of the vnodes in this list...
  359                  */
  360                 ut->uu_defer_reclaims = 1;
  361                 ut->uu_vreclaims = NULLVP;
  362 
  363                 while ( (vp = old_parentvp) != NULLVP ) {
  364           
  365                         vnode_lock(vp);
  366 
  367                         vnode_rele_internal(vp, 0, 0, 1);
  368 
  369                         /*
  370                          * check to see if the vnode is now in the state
  371                          * that would have triggered a vnode_reclaim in vnode_rele
  372                          * if it is, we save it's parent pointer and then NULL
  373                          * out the v_parent field... we'll drop the reference
  374                          * that was held on the next iteration of this loop...
  375                          * this short circuits a potential deep recursion if we
  376                          * have a long chain of parents in this state... 
  377                          * we'll sit in this loop until we run into
  378                          * a parent in this chain that is not in this state
  379                          *
  380                          * make our check and the node_rele atomic
  381                          * with respect to the current vnode we're working on
  382                          * by holding the vnode lock
  383                          * if vnode_rele deferred the vnode_reclaim and has put
  384                          * this vnode on the list to be reaped by us, than
  385                          * it has left this vnode with an iocount == 1
  386                          */
  387                         if ( (vp->v_iocount == 1) && (vp->v_usecount == 0) &&
  388                              ((vp->v_lflag & (VL_MARKTERM | VL_TERMINATE | VL_DEAD)) == VL_MARKTERM)) {
  389                                 /*
  390                                  * vnode_rele wanted to do a vnode_reclaim on this vnode
  391                                  * it should be sitting on the head of the uu_vreclaims chain
  392                                  * pull the parent pointer now so that when we do the
  393                                  * vnode_reclaim for each of the vnodes in the uu_vreclaims
  394                                  * list, we won't recurse back through here
  395                                  */
  396                                 name_cache_lock();
  397                                 old_parentvp = vp->v_parent;
  398                                 vp->v_parent = NULLVP;
  399                                 name_cache_unlock();
  400                         } else {
  401                                 /*
  402                                  * we're done... we ran into a vnode that isn't
  403                                  * being terminated
  404                                  */
  405                                 old_parentvp = NULLVP;
  406                         }
  407                         vnode_unlock(vp);
  408                 }
  409                 ut->uu_defer_reclaims = 0;
  410 
  411                 while ( (vp = ut->uu_vreclaims) != NULLVP) {
  412                         ut->uu_vreclaims = vp->v_defer_reclaimlist;
  413                         
  414                         /*
  415                          * vnode_put will drive the vnode_reclaim if
  416                          * we are still the only reference on this vnode
  417                          */
  418                         vnode_put(vp);
  419                 }
  420         }
  421 }
  422 
  423 
  424 /*
  425  * Mark a vnode as having multiple hard links.  HFS makes use of this
  426  * because it keeps track of each link separately, and wants to know
  427  * which link was actually used.
  428  *
  429  * This will cause the name cache to force a VNOP_LOOKUP on the vnode
  430  * so that HFS can post-process the lookup.  Also, volfs will call
  431  * VNOP_GETATTR2 to determine the parent, instead of using v_parent.
  432  */
  433 void vnode_set_hard_link(vnode_t vp)
  434 {
  435         vnode_lock(vp);
  436 
  437         /*
  438          * In theory, we're changing the vnode's identity as far as the
  439          * name cache is concerned, so we ought to grab the name cache lock
  440          * here.  However, there is already a race, and grabbing the name
  441          * cache lock only makes the race window slightly smaller.
  442          *
  443          * The race happens because the vnode already exists in the name
  444          * cache, and could be found by one thread before another thread
  445          * can set the hard link flag.
  446          */
  447 
  448         vp->v_flag |= VISHARDLINK;
  449 
  450         vnode_unlock(vp);
  451 }
  452 
  453 
  454 void vnode_uncache_credentials(vnode_t vp)
  455 {
  456         kauth_cred_t ucred = NULL;
  457 
  458         if (vp->v_cred) {
  459                 vnode_lock(vp);
  460 
  461                 ucred = vp->v_cred;
  462                 vp->v_cred = NULL;
  463 
  464                 vnode_unlock(vp);
  465 
  466                 if (ucred)
  467                         kauth_cred_rele(ucred);
  468         }
  469 }
  470 
  471 
  472 void vnode_cache_credentials(vnode_t vp, vfs_context_t context)
  473 {
  474         kauth_cred_t ucred;
  475         kauth_cred_t tcred = NOCRED;
  476         struct timeval tv;
  477 
  478         ucred = vfs_context_ucred(context);
  479 
  480         if (vp->v_cred != ucred || (vp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE)) {
  481                 vnode_lock(vp);
  482 
  483                 microuptime(&tv);
  484                 vp->v_cred_timestamp = tv.tv_sec;
  485 
  486                 if (vp->v_cred != ucred) {
  487                         kauth_cred_ref(ucred);
  488         
  489                         tcred = vp->v_cred;
  490                         vp->v_cred = ucred;
  491                 }
  492                 vnode_unlock(vp);
  493         
  494                 if (tcred)
  495                         kauth_cred_rele(tcred);
  496         }
  497 }
  498 
  499 /*      reverse_lookup - lookup by walking back up the parent chain while leveraging
  500  *      use of the name cache lock in order to protect our starting vnode.
  501  *      NOTE - assumes you already have search access to starting point.
  502  *  returns 0 when we have reached the root, current working dir, or chroot root 
  503  *
  504  */
  505 int
  506 reverse_lookup(vnode_t start_vp, vnode_t *lookup_vpp, struct filedesc *fdp, vfs_context_t context, int *dp_authorized)
  507 {
  508         int                             vid, done = 0;
  509         int                             auth_opaque = 0;
  510         vnode_t                 dp = start_vp;
  511         vnode_t                 vp = NULLVP;
  512         kauth_cred_t    ucred;
  513         struct timeval  tv;
  514 
  515         ucred = vfs_context_ucred(context);
  516         *lookup_vpp = start_vp;
  517 
  518         name_cache_lock();
  519 
  520         if ( dp->v_mount && (dp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE) ) {
  521                 auth_opaque = 1;
  522                 microuptime(&tv);
  523         }
  524         for (;;) {
  525                 *dp_authorized = 0;
  526 
  527                 if (auth_opaque && ((tv.tv_sec - dp->v_cred_timestamp) > VCRED_EXPIRED))
  528                         break;
  529                 if (dp->v_cred != ucred)
  530                         break;
  531                 /*
  532                  * indicate that we're allowed to traverse this directory...
  533                  * even if we bail for some reason, this information is valid and is used
  534                  * to avoid doing a vnode_authorize
  535                  */
  536                 *dp_authorized = 1;
  537 
  538                 if ((dp->v_flag & VROOT) != 0   ||              /* Hit "/" */
  539                     (dp == fdp->fd_cdir)        ||              /* Hit process's working directory */
  540                     (dp == fdp->fd_rdir)) {                     /* Hit process chroot()-ed root */
  541                         done = 1;
  542                         break;
  543                 }
  544 
  545                 if ( (vp = dp->v_parent) == NULLVP)
  546                         break;
  547 
  548                 dp = vp;
  549                 *lookup_vpp = dp;
  550         } /* for (;;) */
  551 
  552         vid = dp->v_id;
  553         
  554         name_cache_unlock();
  555         
  556         if (done == 0 && dp != start_vp) {
  557                 if (vnode_getwithvid(dp, vid) != 0) {
  558                         *lookup_vpp = start_vp;
  559                 }
  560         }
  561 
  562         return((done == 1) ? 0 : -1);
  563 }
  564 
  565 int 
  566 cache_lookup_path(struct nameidata *ndp, struct componentname *cnp, vnode_t dp, vfs_context_t context, int *trailing_slash, int *dp_authorized)
  567 {
  568         char            *cp;            /* pointer into pathname argument */
  569         int             vid, vvid;
  570         int             auth_opaque = 0;
  571         vnode_t         vp = NULLVP;
  572         vnode_t         tdp = NULLVP;
  573         kauth_cred_t    ucred;
  574         struct timeval tv;
  575         unsigned int    hash;
  576 
  577         ucred = vfs_context_ucred(context);
  578         *trailing_slash = 0;
  579 
  580         name_cache_lock();
  581 
  582 
  583         if ( dp->v_mount && (dp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE) ) {
  584                 auth_opaque = 1;
  585                 microuptime(&tv);
  586         }
  587         for (;;) {
  588                 /*
  589                  * Search a directory.
  590                  *
  591                  * The cn_hash value is for use by cache_lookup
  592                  * The last component of the filename is left accessible via
  593                  * cnp->cn_nameptr for callers that need the name.
  594                  */
  595                 hash = 0;
  596                 cp = cnp->cn_nameptr;
  597 
  598                 while (*cp && (*cp != '/')) {
  599                         hash ^= crc32tab[((hash >> 24) ^ (unsigned char)*cp++)];
  600                 }
  601                 /*
  602                  * the crc generator can legitimately generate
  603                  * a 0... however, 0 for us means that we
  604                  * haven't computed a hash, so use 1 instead
  605                  */
  606                 if (hash == 0)
  607                         hash = 1;
  608                 cnp->cn_hash = hash;
  609                 cnp->cn_namelen = cp - cnp->cn_nameptr;
  610 
  611                 ndp->ni_pathlen -= cnp->cn_namelen;
  612                 ndp->ni_next = cp;
  613 
  614                 /*
  615                  * Replace multiple slashes by a single slash and trailing slashes
  616                  * by a null.  This must be done before VNOP_LOOKUP() because some
  617                  * fs's don't know about trailing slashes.  Remember if there were
  618                  * trailing slashes to handle symlinks, existing non-directories
  619                  * and non-existing files that won't be directories specially later.
  620                  */
  621                 while (*cp == '/' && (cp[1] == '/' || cp[1] == '\0')) {
  622                         cp++;
  623                         ndp->ni_pathlen--;
  624 
  625                         if (*cp == '\0') {
  626                                 *trailing_slash = 1;
  627                                 *ndp->ni_next = '\0';
  628                         }
  629                 }
  630                 ndp->ni_next = cp;
  631 
  632                 cnp->cn_flags &= ~(MAKEENTRY | ISLASTCN | ISDOTDOT);
  633 
  634                 if (*cp == '\0')
  635                         cnp->cn_flags |= ISLASTCN;
  636 
  637                 if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
  638                         cnp->cn_flags |= ISDOTDOT;
  639 
  640                 *dp_authorized = 0;
  641 
  642                 if (auth_opaque && ((tv.tv_sec - dp->v_cred_timestamp) > VCRED_EXPIRED))
  643                         break;
  644 
  645                 if (dp->v_cred != ucred)
  646                         break;
  647                 /*
  648                  * indicate that we're allowed to traverse this directory...
  649                  * even if we fail the cache lookup or decide to bail for
  650                  * some other reason, this information is valid and is used
  651                  * to avoid doing a vnode_authorize before the call to VNOP_LOOKUP
  652                  */
  653                 *dp_authorized = 1;
  654 
  655                 if ( (cnp->cn_flags & (ISLASTCN | ISDOTDOT)) ) {
  656                         if (cnp->cn_nameiop != LOOKUP)
  657                                 break;
  658                         if (cnp->cn_flags & (LOCKPARENT | NOCACHE))
  659                                 break;
  660                         if (cnp->cn_flags & ISDOTDOT) {
  661                                 /*
  662                                  * Quit here only if we can't use
  663                                  * the parent directory pointer or
  664                                  * don't have one.  Otherwise, we'll
  665                                  * use it below.
  666                                  */
  667                                 if ((dp->v_flag & VROOT) ||
  668                                     dp->v_parent == NULLVP)
  669                                         break;
  670                         }
  671                 }
  672 
  673                 /*
  674                  * "." and ".." aren't supposed to be cached, so check
  675                  * for them before checking the cache.
  676                  */
  677                 if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
  678                         vp = dp;
  679                 else if (cnp->cn_flags & ISDOTDOT)
  680                         vp = dp->v_parent;
  681                 else {
  682                         if ( (vp = cache_lookup_locked(dp, cnp)) == NULLVP)
  683                                 break;
  684                 }
  685 
  686                 if ( (cnp->cn_flags & ISLASTCN) )
  687                         break;
  688 
  689                 if (vp->v_type != VDIR) {
  690                         if (vp->v_type != VLNK)
  691                                 vp = NULL;
  692                         break;
  693                 }
  694                 if (vp->v_mountedhere && ((cnp->cn_flags & NOCROSSMOUNT) == 0))
  695                         break;
  696 
  697                 dp = vp;
  698                 vp = NULLVP;
  699 
  700                 cnp->cn_nameptr = ndp->ni_next + 1;
  701                 ndp->ni_pathlen--;
  702                 while (*cnp->cn_nameptr == '/') {
  703                         cnp->cn_nameptr++;
  704                         ndp->ni_pathlen--;
  705                 }
  706         }
  707         if (vp != NULLVP)
  708                 vvid = vp->v_id;
  709         vid = dp->v_id;
  710         
  711         name_cache_unlock();
  712 
  713 
  714         if ((vp != NULLVP) && (vp->v_type != VLNK) &&
  715             ((cnp->cn_flags & (ISLASTCN | LOCKPARENT | WANTPARENT | SAVESTART)) == ISLASTCN)) {
  716                 /*
  717                  * if we've got a child and it's the last component, and 
  718                  * the lookup doesn't need to return the parent then we
  719                  * can skip grabbing an iocount on the parent, since all
  720                  * we're going to do with it is a vnode_put just before
  721                  * we return from 'lookup'.  If it's a symbolic link,
  722                  * we need the parent in case the link happens to be
  723                  * a relative pathname.
  724                  */
  725                 tdp = dp;
  726                 dp = NULLVP;
  727         } else {
  728 need_dp:
  729                 /*
  730                  * return the last directory we looked at
  731                  * with an io reference held
  732                  */
  733                 if (dp == ndp->ni_usedvp) {
  734                         /*
  735                          * if this vnode matches the one passed in via USEDVP
  736                          * than this context already holds an io_count... just
  737                          * use vnode_get to get an extra ref for lookup to play
  738                          * with... can't use the getwithvid variant here because
  739                          * it will block behind a vnode_drain which would result
  740                          * in a deadlock (since we already own an io_count that the
  741                          * vnode_drain is waiting on)... vnode_get grabs the io_count
  742                          * immediately w/o waiting... it always succeeds
  743                          */
  744                         vnode_get(dp);
  745                 } else if ( (vnode_getwithvid(dp, vid)) ) {
  746                         /*
  747                          * failure indicates the vnode
  748                          * changed identity or is being
  749                          * TERMINATED... in either case
  750                          * punt this lookup
  751                          */
  752                         return (ENOENT);
  753                 }
  754         }
  755         if (vp != NULLVP) {
  756                 if ( (vnode_getwithvid(vp, vvid)) ) {
  757                         vp = NULLVP;
  758 
  759                         /*
  760                          * can't get reference on the vp we'd like
  761                          * to return... if we didn't grab a reference
  762                          * on the directory (due to fast path bypass),
  763                          * then we need to do it now... we can't return
  764                          * with both ni_dvp and ni_vp NULL, and no 
  765                          * error condition
  766                          */
  767                         if (dp == NULLVP) {
  768                                 dp = tdp;
  769                                 goto need_dp;
  770                         }
  771                 }
  772         }
  773         ndp->ni_dvp = dp;
  774         ndp->ni_vp  = vp;
  775 
  776         return (0);
  777 }
  778 
  779 
  780 static vnode_t
  781 cache_lookup_locked(vnode_t dvp, struct componentname *cnp)
  782 {
  783         register struct namecache *ncp;
  784         register struct nchashhead *ncpp;
  785         register long namelen = cnp->cn_namelen;
  786         char *nameptr = cnp->cn_nameptr;
  787         unsigned int hashval = (cnp->cn_hash & NCHASHMASK);
  788         vnode_t vp;
  789         
  790         ncpp = NCHHASH(dvp, cnp->cn_hash);
  791         LIST_FOREACH(ncp, ncpp, nc_hash) {
  792                 if ((ncp->nc_dvp == dvp) && (ncp->nc_hashval == hashval)) {
  793                         if (memcmp(ncp->nc_name, nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
  794                                 break;
  795                 }
  796         }
  797         if (ncp == 0)
  798                 /*
  799                  * We failed to find an entry
  800                  */
  801                 return (NULL);
  802 
  803         vp = ncp->nc_vp;
  804         if (vp && (vp->v_flag & VISHARDLINK)) {
  805                         /*
  806                          * The file system wants a VNOP_LOOKUP on this vnode
  807                          */
  808                         vp = NULL;
  809         }
  810         
  811         return (vp);
  812 }
  813 
  814 
  815 //
  816 // Have to take a len argument because we may only need to
  817 // hash part of a componentname.
  818 //
  819 static unsigned int
  820 hash_string(const char *cp, int len)
  821 {
  822     unsigned hash = 0;
  823 
  824     if (len) {
  825             while (len--) {
  826                     hash ^= crc32tab[((hash >> 24) ^ (unsigned char)*cp++)];
  827             }
  828     } else {
  829             while (*cp != '\0') {
  830                     hash ^= crc32tab[((hash >> 24) ^ (unsigned char)*cp++)];
  831             }
  832     }
  833     /*
  834      * the crc generator can legitimately generate
  835      * a 0... however, 0 for us means that we
  836      * haven't computed a hash, so use 1 instead
  837      */
  838     if (hash == 0)
  839             hash = 1;
  840     return hash;
  841 }
  842 
  843 
  844 /*
  845  * Lookup an entry in the cache 
  846  *
  847  * We don't do this if the segment name is long, simply so the cache 
  848  * can avoid holding long names (which would either waste space, or
  849  * add greatly to the complexity).
  850  *
  851  * Lookup is called with dvp pointing to the directory to search,
  852  * cnp pointing to the name of the entry being sought. If the lookup
  853  * succeeds, the vnode is returned in *vpp, and a status of -1 is
  854  * returned. If the lookup determines that the name does not exist
  855  * (negative cacheing), a status of ENOENT is returned. If the lookup
  856  * fails, a status of zero is returned.
  857  */
  858 
  859 int
  860 cache_lookup(dvp, vpp, cnp)
  861         struct vnode *dvp;
  862         struct vnode **vpp;
  863         struct componentname *cnp;
  864 {
  865         register struct namecache *ncp;
  866         register struct nchashhead *ncpp;
  867         register long namelen = cnp->cn_namelen;
  868         char *nameptr = cnp->cn_nameptr;
  869         unsigned int hashval = (cnp->cn_hash & NCHASHMASK);
  870         uint32_t vid;
  871         vnode_t  vp;
  872 
  873         name_cache_lock();
  874 
  875         ncpp = NCHHASH(dvp, cnp->cn_hash);
  876         LIST_FOREACH(ncp, ncpp, nc_hash) {
  877                 if ((ncp->nc_dvp == dvp) && (ncp->nc_hashval == hashval)) {
  878                         if (memcmp(ncp->nc_name, nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0)
  879                                 break;
  880                 }
  881         }
  882         /* We failed to find an entry */
  883         if (ncp == 0) {
  884                 nchstats.ncs_miss++;
  885                 name_cache_unlock();
  886                 return (0);
  887         }
  888 
  889         /* We don't want to have an entry, so dump it */
  890         if ((cnp->cn_flags & MAKEENTRY) == 0) {
  891                 nchstats.ncs_badhits++;
  892                 cache_delete(ncp, 1);
  893                 name_cache_unlock();
  894                 return (0);
  895         } 
  896         vp = ncp->nc_vp;
  897 
  898         /* We found a "positive" match, return the vnode */
  899         if (vp) {
  900                 nchstats.ncs_goodhits++;
  901 
  902                 vid = vp->v_id;
  903                 name_cache_unlock();
  904 
  905                 if (vnode_getwithvid(vp, vid)) {
  906                         name_cache_lock();
  907                         nchstats.ncs_badvid++;
  908                         name_cache_unlock();
  909                         return (0);
  910                 }
  911                 *vpp = vp;
  912                 return (-1);
  913         }
  914 
  915         /* We found a negative match, and want to create it, so purge */
  916         if (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) {
  917                 nchstats.ncs_badhits++;
  918                 cache_delete(ncp, 1);
  919                 name_cache_unlock();
  920                 return (0);
  921         }
  922 
  923         /*
  924          * We found a "negative" match, ENOENT notifies client of this match.
  925          * The nc_whiteout field records whether this is a whiteout.
  926          */
  927         nchstats.ncs_neghits++;
  928 
  929         if (ncp->nc_whiteout)
  930                 cnp->cn_flags |= ISWHITEOUT;
  931         name_cache_unlock();
  932         return (ENOENT);
  933 }
  934 
  935 /*
  936  * Add an entry to the cache.
  937  */
  938 void
  939 cache_enter(dvp, vp, cnp)
  940         struct vnode *dvp;
  941         struct vnode *vp;
  942         struct componentname *cnp;
  943 {
  944         register struct namecache *ncp, *negp;
  945         register struct nchashhead *ncpp;
  946 
  947         if (cnp->cn_hash == 0)
  948                 cnp->cn_hash = hash_string(cnp->cn_nameptr, cnp->cn_namelen);
  949 
  950         name_cache_lock();
  951 
  952         /* if the entry is for -ve caching vp is null */
  953         if ((vp != NULLVP) && (LIST_FIRST(&vp->v_nclinks))) {
  954                 /*
  955                  * someone beat us to the punch..
  956                  * this vnode is already in the cache
  957                  */
  958                 name_cache_unlock();
  959                         return;
  960         }
  961         /*
  962          * We allocate a new entry if we are less than the maximum
  963          * allowed and the one at the front of the list is in use.
  964          * Otherwise we use the one at the front of the list.
  965          */
  966         if (numcache < desiredNodes &&
  967             ((ncp = nchead.tqh_first) == NULL ||
  968               ncp->nc_hash.le_prev != 0)) {
  969                 /*
  970                  * Allocate one more entry
  971                  */
  972                 ncp = (struct namecache *)_MALLOC_ZONE((u_long)sizeof *ncp, M_CACHE, M_WAITOK);
  973                 numcache++;
  974         } else {
  975                 /*
  976                  * reuse an old entry
  977                  */
  978                 ncp = TAILQ_FIRST(&nchead);
  979                 TAILQ_REMOVE(&nchead, ncp, nc_entry);
  980 
  981                 if (ncp->nc_hash.le_prev != 0) {
  982                        /*
  983                         * still in use... we need to
  984                         * delete it before re-using it
  985                         */
  986                         nchstats.ncs_stolen++;
  987                         cache_delete(ncp, 0);
  988                 }
  989         }
  990         nchstats.ncs_enters++;
  991 
  992         /*
  993          * Fill in cache info, if vp is NULL this is a "negative" cache entry.
  994          */
  995         ncp->nc_vp = vp;
  996         ncp->nc_dvp = dvp;
  997         ncp->nc_hashval = cnp->cn_hash;
  998         ncp->nc_whiteout = FALSE;
  999         ncp->nc_name = add_name_locked(cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_hash, 0);
 1000 
 1001         /*
 1002          * make us the newest entry in the cache
 1003          * i.e. we'll be the last to be stolen
 1004          */
 1005         TAILQ_INSERT_TAIL(&nchead, ncp, nc_entry);
 1006 
 1007         ncpp = NCHHASH(dvp, cnp->cn_hash);
 1008 #if DIAGNOSTIC
 1009         {
 1010                 register struct namecache *p;
 1011 
 1012                 for (p = ncpp->lh_first; p != 0; p = p->nc_hash.le_next)
 1013                         if (p == ncp)
 1014                                 panic("cache_enter: duplicate");
 1015         }
 1016 #endif
 1017         /*
 1018          * make us available to be found via lookup
 1019          */
 1020         LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
 1021 
 1022         if (vp) {
 1023                /*
 1024                 * add to the list of name cache entries
 1025                 * that point at vp
 1026                 */
 1027                 LIST_INSERT_HEAD(&vp->v_nclinks, ncp, nc_un.nc_link);
 1028         } else {
 1029                 /*
 1030                  * this is a negative cache entry (vp == NULL)
 1031                  * stick it on the negative cache list
 1032                  * and record the whiteout state
 1033                  */
 1034                 TAILQ_INSERT_TAIL(&neghead, ncp, nc_un.nc_negentry);
 1035           
 1036                 if (cnp->cn_flags & ISWHITEOUT)
 1037                         ncp->nc_whiteout = TRUE;
 1038                 nchstats.ncs_negtotal++;
 1039 
 1040                 if (nchstats.ncs_negtotal > desiredNegNodes) {
 1041                        /*
 1042                         * if we've reached our desired limit
 1043                         * of negative cache entries, delete
 1044                         * the oldest
 1045                         */
 1046                         negp = TAILQ_FIRST(&neghead);
 1047                         TAILQ_REMOVE(&neghead, negp, nc_un.nc_negentry);
 1048 
 1049                         cache_delete(negp, 1);
 1050                 }
 1051         }
 1052         /*
 1053          * add us to the list of name cache entries that
 1054          * are children of dvp
 1055          */
 1056         LIST_INSERT_HEAD(&dvp->v_ncchildren, ncp, nc_child);
 1057 
 1058         name_cache_unlock();
 1059 }
 1060 
 1061 
 1062 /*
 1063  * Initialize CRC-32 remainder table.
 1064  */
 1065 static void init_crc32(void)
 1066 {
 1067         /*
 1068          * the CRC-32 generator polynomial is:
 1069          *   x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^10
 1070          *        + x^8  + x^7  + x^5  + x^4  + x^2  + x + 1
 1071          */
 1072         unsigned int crc32_polynomial = 0x04c11db7;
 1073         unsigned int i,j;
 1074 
 1075         /*
 1076          * pre-calculate the CRC-32 remainder for each possible octet encoding
 1077          */
 1078         for (i = 0;  i < 256;  i++) {
 1079                 unsigned int crc_rem = i << 24;
 1080 
 1081                 for (j = 0;  j < 8;  j++) {
 1082                         if (crc_rem & 0x80000000)
 1083                                 crc_rem = (crc_rem << 1) ^ crc32_polynomial;
 1084                         else
 1085                                 crc_rem = (crc_rem << 1);
 1086                 }
 1087                 crc32tab[i] = crc_rem;
 1088         }
 1089 }
 1090 
 1091 
 1092 /*
 1093  * Name cache initialization, from vfs_init() when we are booting
 1094  */
 1095 void
 1096 nchinit(void)
 1097 {
 1098         desiredNegNodes = (desiredvnodes / 10);
 1099         desiredNodes = desiredvnodes + desiredNegNodes;
 1100 
 1101         TAILQ_INIT(&nchead);
 1102         TAILQ_INIT(&neghead);
 1103 
 1104         init_crc32();
 1105 
 1106         nchashtbl = hashinit(MAX(4096, (2 *desiredNodes)), M_CACHE, &nchash);
 1107         nchashmask = nchash;
 1108         nchash++;
 1109 
 1110         init_string_table();
 1111         
 1112         /* Allocate mount list lock group attribute and group */
 1113         namecache_lck_grp_attr= lck_grp_attr_alloc_init();
 1114         lck_grp_attr_setstat(namecache_lck_grp_attr);
 1115 
 1116         namecache_lck_grp = lck_grp_alloc_init("Name Cache",  namecache_lck_grp_attr);
 1117         
 1118         /* Allocate mount list lock attribute */
 1119         namecache_lck_attr = lck_attr_alloc_init();
 1120         //lck_attr_setdebug(namecache_lck_attr);
 1121 
 1122         /* Allocate mount list lock */
 1123         namecache_mtx_lock = lck_mtx_alloc_init(namecache_lck_grp, namecache_lck_attr);
 1124 
 1125 
 1126 }
 1127 
 1128 void
 1129 name_cache_lock(void)
 1130 {
 1131         lck_mtx_lock(namecache_mtx_lock);
 1132 }
 1133 
 1134 void
 1135 name_cache_unlock(void)
 1136 {
 1137         lck_mtx_unlock(namecache_mtx_lock);
 1138 
 1139 }
 1140 
 1141 
 1142 int
 1143 resize_namecache(u_int newsize)
 1144 {
 1145     struct nchashhead   *new_table;
 1146     struct nchashhead   *old_table;
 1147     struct nchashhead   *old_head, *head;
 1148     struct namecache    *entry, *next;
 1149     uint32_t            i, hashval;
 1150     int                 dNodes, dNegNodes;
 1151     u_long              new_size, old_size;
 1152 
 1153     dNegNodes = (newsize / 10);
 1154     dNodes = newsize + dNegNodes;
 1155 
 1156     // we don't support shrinking yet
 1157     if (dNodes < desiredNodes) {
 1158         return 0;
 1159     }
 1160     new_table = hashinit(2 * dNodes, M_CACHE, &nchashmask);
 1161     new_size  = nchashmask + 1;
 1162 
 1163     if (new_table == NULL) {
 1164         return ENOMEM;
 1165     }
 1166 
 1167     name_cache_lock();
 1168     // do the switch!
 1169     old_table = nchashtbl;
 1170     nchashtbl = new_table;
 1171     old_size  = nchash;
 1172     nchash    = new_size;
 1173 
 1174     // walk the old table and insert all the entries into
 1175     // the new table
 1176     //
 1177     for(i=0; i < old_size; i++) {
 1178         old_head = &old_table[i];
 1179         for (entry=old_head->lh_first; entry != NULL; entry=next) {
 1180             //
 1181             // XXXdbg - Beware: this assumes that hash_string() does
 1182             //                  the same thing as what happens in
 1183             //                  lookup() over in vfs_lookup.c
 1184             hashval = hash_string(entry->nc_name, 0);
 1185             entry->nc_hashval = hashval;
 1186             head = NCHHASH(entry->nc_dvp, hashval);
 1187             
 1188             next = entry->nc_hash.le_next;
 1189             LIST_INSERT_HEAD(head, entry, nc_hash);
 1190         }
 1191     }
 1192     desiredNodes = dNodes;
 1193     desiredNegNodes = dNegNodes;
 1194     
 1195     name_cache_unlock();
 1196     FREE(old_table, M_CACHE);
 1197 
 1198     return 0;
 1199 }
 1200 
 1201 static void
 1202 cache_delete(struct namecache *ncp, int age_entry)
 1203 {
 1204         nchstats.ncs_deletes++;
 1205 
 1206         if (ncp->nc_vp) {
 1207                 LIST_REMOVE(ncp, nc_un.nc_link);
 1208         } else {
 1209                 TAILQ_REMOVE(&neghead, ncp, nc_un.nc_negentry);
 1210                 nchstats.ncs_negtotal--;
 1211         }
 1212         LIST_REMOVE(ncp, nc_child);
 1213 
 1214         LIST_REMOVE(ncp, nc_hash);
 1215         /*
 1216          * this field is used to indicate
 1217          * that the entry is in use and
 1218          * must be deleted before it can 
 1219          * be reused...
 1220          */
 1221         ncp->nc_hash.le_prev = NULL;
 1222 
 1223         if (age_entry) {
 1224                 /*
 1225                  * make it the next one available
 1226                  * for cache_enter's use
 1227                  */
 1228                 TAILQ_REMOVE(&nchead, ncp, nc_entry);
 1229                 TAILQ_INSERT_HEAD(&nchead, ncp, nc_entry);
 1230         }
 1231         remove_name_locked(ncp->nc_name);
 1232         ncp->nc_name = NULL;
 1233 }
 1234 
 1235 
 1236 /*
 1237  * purge the entry associated with the 
 1238  * specified vnode from the name cache
 1239  */
 1240 void
 1241 cache_purge(vnode_t vp)
 1242 {
 1243         struct namecache *ncp;
 1244 
 1245         if ((LIST_FIRST(&vp->v_nclinks) == NULL) && (LIST_FIRST(&vp->v_ncchildren) == NULL))
 1246                 return;
 1247 
 1248         name_cache_lock();
 1249 
 1250         while ( (ncp = LIST_FIRST(&vp->v_nclinks)) )
 1251                 cache_delete(ncp, 1);
 1252 
 1253         while ( (ncp = LIST_FIRST(&vp->v_ncchildren)) )
 1254                 cache_delete(ncp, 1);
 1255 
 1256         name_cache_unlock();
 1257 }
 1258 
 1259 /*
 1260  * Purge all negative cache entries that are children of the
 1261  * given vnode.  A case-insensitive file system (or any file
 1262  * system that has multiple equivalent names for the same
 1263  * directory entry) can use this when creating or renaming
 1264  * to remove negative entries that may no longer apply.
 1265  */
 1266 void
 1267 cache_purge_negatives(vnode_t vp)
 1268 {
 1269         struct namecache *ncp;
 1270 
 1271         name_cache_lock();
 1272 
 1273         LIST_FOREACH(ncp, &vp->v_ncchildren, nc_child)
 1274                 if (ncp->nc_vp == NULL)
 1275                         cache_delete(ncp , 1);
 1276 
 1277         name_cache_unlock();
 1278 }
 1279 
 1280 /*
 1281  * Flush all entries referencing a particular filesystem.
 1282  *
 1283  * Since we need to check it anyway, we will flush all the invalid
 1284  * entries at the same time.
 1285  */
 1286 void
 1287 cache_purgevfs(mp)
 1288         struct mount *mp;
 1289 {
 1290         struct nchashhead *ncpp;
 1291         struct namecache *ncp;
 1292 
 1293         name_cache_lock();
 1294         /* Scan hash tables for applicable entries */
 1295         for (ncpp = &nchashtbl[nchash - 1]; ncpp >= nchashtbl; ncpp--) {
 1296 restart:          
 1297                 for (ncp = ncpp->lh_first; ncp != 0; ncp = ncp->nc_hash.le_next) {
 1298                         if (ncp->nc_dvp->v_mount == mp) {
 1299                                 cache_delete(ncp, 0);
 1300                                 goto restart;
 1301                         }
 1302                 }
 1303         }
 1304         name_cache_unlock();
 1305 }
 1306 
 1307 
 1308 
 1309 //
 1310 // String ref routines
 1311 //
 1312 static LIST_HEAD(stringhead, string_t) *string_ref_table;
 1313 static u_long   string_table_mask;
 1314 static uint32_t max_chain_len=0;
 1315 static struct stringhead *long_chain_head=NULL;
 1316 static uint32_t filled_buckets=0;
 1317 static uint32_t num_dups=0;
 1318 static uint32_t nstrings=0;
 1319 
 1320 typedef struct string_t {
 1321     LIST_ENTRY(string_t)  hash_chain;
 1322     unsigned char        *str;
 1323     uint32_t              refcount;
 1324 } string_t;
 1325 
 1326 
 1327 
 1328 static int
 1329 resize_string_ref_table(void)
 1330 {
 1331     struct stringhead *new_table;
 1332     struct stringhead *old_table;
 1333     struct stringhead *old_head, *head;
 1334     string_t          *entry, *next;
 1335     uint32_t           i, hashval;
 1336     u_long             new_mask, old_mask;
 1337 
 1338     new_table = hashinit((string_table_mask + 1) * 2, M_CACHE, &new_mask);
 1339     if (new_table == NULL) {
 1340         return ENOMEM;
 1341     }
 1342 
 1343     // do the switch!
 1344     old_table         = string_ref_table;
 1345     string_ref_table  = new_table;
 1346     old_mask          = string_table_mask;
 1347     string_table_mask = new_mask;
 1348 
 1349     printf("resize: max chain len %d, new table size %d\n",
 1350            max_chain_len, new_mask + 1);
 1351     max_chain_len   = 0;
 1352     long_chain_head = NULL;
 1353     filled_buckets  = 0;
 1354 
 1355     // walk the old table and insert all the entries into
 1356     // the new table
 1357     //
 1358     for(i=0; i <= old_mask; i++) {
 1359         old_head = &old_table[i];
 1360         for (entry=old_head->lh_first; entry != NULL; entry=next) {
 1361             hashval = hash_string(entry->str, 0);
 1362             head = &string_ref_table[hashval & string_table_mask];
 1363             if (head->lh_first == NULL) {
 1364                 filled_buckets++;
 1365             }
 1366 
 1367             next = entry->hash_chain.le_next;
 1368             LIST_INSERT_HEAD(head, entry, hash_chain);
 1369         }
 1370     }
 1371     
 1372     FREE(old_table, M_CACHE);
 1373 
 1374     return 0;
 1375 }
 1376 
 1377 
 1378 static void
 1379 init_string_table(void)
 1380 {
 1381     string_ref_table = hashinit(4096, M_CACHE, &string_table_mask);
 1382 }
 1383 
 1384 
 1385 char *
 1386 vfs_addname(const char *name, size_t len, u_int hashval, u_int flags)
 1387 {
 1388         char * ptr;
 1389 
 1390         name_cache_lock();
 1391         ptr = add_name_locked(name, len, hashval, flags);
 1392         name_cache_unlock();
 1393 
 1394         return(ptr);
 1395 }
 1396 
 1397 static char *
 1398 add_name_locked(const char *name, size_t len, u_int hashval, __unused u_int flags)
 1399 {
 1400     struct stringhead *head;
 1401     string_t          *entry;
 1402     uint32_t          chain_len = 0;
 1403     
 1404     //
 1405     // If the table gets more than 3/4 full, resize it
 1406     //
 1407     if (4*filled_buckets >= ((string_table_mask + 1) * 3)) {
 1408                 if (resize_string_ref_table() != 0) {
 1409                         printf("failed to resize the hash table.\n");
 1410                 }
 1411     }
 1412     if (hashval == 0) {
 1413         hashval = hash_string(name, 0);
 1414     }
 1415 
 1416     head = &string_ref_table[hashval & string_table_mask];
 1417     for (entry=head->lh_first; entry != NULL; chain_len++, entry=entry->hash_chain.le_next) {
 1418         if (memcmp(entry->str, name, len) == 0 && entry->str[len] == '\0') {
 1419             entry->refcount++;
 1420             num_dups++;
 1421             break;
 1422         }
 1423     }
 1424 
 1425     if (entry == NULL) {
 1426         // it wasn't already there so add it.
 1427         MALLOC(entry, string_t *, sizeof(string_t) + len + 1, M_TEMP, M_WAITOK);
 1428 
 1429         // have to get "head" again because we could have blocked
 1430         // in malloc and thus head could have changed.
 1431         //
 1432         head = &string_ref_table[hashval & string_table_mask];
 1433         if (head->lh_first == NULL) {
 1434             filled_buckets++;
 1435         }
 1436 
 1437         entry->str = (char *)((char *)entry + sizeof(string_t));
 1438         strncpy(entry->str, name, len);
 1439         entry->str[len] = '\0';
 1440         entry->refcount = 1;
 1441         LIST_INSERT_HEAD(head, entry, hash_chain);
 1442 
 1443         if (chain_len > max_chain_len) {
 1444             max_chain_len   = chain_len;
 1445             long_chain_head = head;
 1446         }
 1447 
 1448         nstrings++;
 1449     }
 1450     
 1451     return entry->str;
 1452 }
 1453 
 1454 int
 1455 vfs_removename(const char *nameref)
 1456 {
 1457         int i;
 1458 
 1459         name_cache_lock();
 1460         i = remove_name_locked(nameref);
 1461         name_cache_unlock();
 1462 
 1463         return(i);
 1464         
 1465 }
 1466 
 1467 
 1468 static int
 1469 remove_name_locked(const char *nameref)
 1470 {
 1471     struct stringhead *head;
 1472     string_t          *entry;
 1473     uint32_t           hashval;
 1474     char * ptr;
 1475 
 1476     hashval = hash_string(nameref, 0);
 1477     head = &string_ref_table[hashval & string_table_mask];
 1478     for (entry=head->lh_first; entry != NULL; entry=entry->hash_chain.le_next) {
 1479         if (entry->str == (unsigned char *)nameref) {
 1480             entry->refcount--;
 1481             if (entry->refcount == 0) {
 1482                 LIST_REMOVE(entry, hash_chain);
 1483                 if (head->lh_first == NULL) {
 1484                     filled_buckets--;
 1485                 }
 1486                 ptr = entry->str;
 1487                 entry->str = NULL;
 1488                 nstrings--;
 1489 
 1490                 FREE(entry, M_TEMP);
 1491             } else {
 1492                 num_dups--;
 1493             }
 1494 
 1495             return 0;
 1496         }
 1497     }
 1498 
 1499     return ENOENT;
 1500 }
 1501 
 1502 
 1503 void
 1504 dump_string_table(void)
 1505 {
 1506     struct stringhead *head;
 1507     string_t          *entry;
 1508     u_long            i;
 1509     
 1510     name_cache_lock();
 1511     for (i = 0; i <= string_table_mask; i++) {
 1512         head = &string_ref_table[i];
 1513         for (entry=head->lh_first; entry != NULL; entry=entry->hash_chain.le_next) {
 1514             printf("%6d - %s\n", entry->refcount, entry->str);
 1515         }
 1516     }
 1517     name_cache_unlock();
 1518 }

Cache object: ed72058b1bfe33b43924877d76b77495


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.