The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


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

FreeBSD/Linux Kernel Cross Reference
sys/kern/vfs_cache.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 1989, 1993, 1995
    3  *      The Regents of the University of California.  All rights reserved.
    4  *
    5  * This code is derived from software contributed to Berkeley by
    6  * Poul-Henning Kamp of the FreeBSD Project.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. All advertising materials mentioning features or use of this software
   17  *    must display the following acknowledgement:
   18  *      This product includes software developed by the University of
   19  *      California, Berkeley and its contributors.
   20  * 4. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  *
   36  *      @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95
   37  * $FreeBSD$
   38  */
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/kernel.h>
   43 #include <sys/sysctl.h>
   44 #include <sys/mount.h>
   45 #include <sys/vnode.h>
   46 #include <sys/namei.h>
   47 #include <sys/malloc.h>
   48 #include <sys/sysproto.h>
   49 #include <sys/proc.h>
   50 #include <sys/filedesc.h>
   51 #include <sys/fnv_hash.h>
   52 
   53 /*
   54  * This structure describes the elements in the cache of recent
   55  * names looked up by namei.
   56  */
   57 
   58 struct  namecache {
   59         LIST_ENTRY(namecache) nc_hash;  /* hash chain */
   60         LIST_ENTRY(namecache) nc_src;   /* source vnode list */
   61         TAILQ_ENTRY(namecache) nc_dst;  /* destination vnode list */
   62         struct  vnode *nc_dvp;          /* vnode of parent of name */
   63         struct  vnode *nc_vp;           /* vnode the name refers to */
   64         u_char  nc_flag;                /* flag bits */
   65         u_char  nc_nlen;                /* length of name */
   66         char    nc_name[0];             /* segment name */
   67 };
   68 
   69 /*
   70  * Name caching works as follows:
   71  *
   72  * Names found by directory scans are retained in a cache
   73  * for future reference.  It is managed LRU, so frequently
   74  * used names will hang around.  Cache is indexed by hash value
   75  * obtained from (vp, name) where vp refers to the directory
   76  * containing name.
   77  *
   78  * If it is a "negative" entry, (i.e. for a name that is known NOT to
   79  * exist) the vnode pointer will be NULL.
   80  *
   81  * Upon reaching the last segment of a path, if the reference
   82  * is for DELETE, or NOCACHE is set (rewrite), and the
   83  * name is located in the cache, it will be dropped.
   84  */
   85 
   86 /*
   87  * Structures associated with name cacheing.
   88  */
   89 #define NCHHASH(hash) \
   90         (&nchashtbl[(hash) & nchash])
   91 static LIST_HEAD(nchashhead, namecache) *nchashtbl;     /* Hash Table */
   92 static TAILQ_HEAD(, namecache) ncneg;   /* Hash Table */
   93 static u_long   nchash;                 /* size of hash table */
   94 SYSCTL_ULONG(_debug, OID_AUTO, nchash, CTLFLAG_RD, &nchash, 0, "");
   95 static u_long   ncnegfactor = 16;       /* ratio of negative entries */
   96 SYSCTL_ULONG(_debug, OID_AUTO, ncnegfactor, CTLFLAG_RW, &ncnegfactor, 0, "");
   97 static u_long   numneg;         /* number of cache entries allocated */
   98 SYSCTL_ULONG(_debug, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0, "");
   99 static u_long   numcache;               /* number of cache entries allocated */
  100 SYSCTL_ULONG(_debug, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0, "");
  101 struct  nchstats nchstats;              /* cache effectiveness statistics */
  102 
  103 static int      doingcache = 1;         /* 1 => enable the cache */
  104 SYSCTL_INT(_debug, OID_AUTO, vfscache, CTLFLAG_RW, &doingcache, 0, "");
  105 SYSCTL_INT(_debug, OID_AUTO, vnsize, CTLFLAG_RD, 0, sizeof(struct vnode), "");
  106 SYSCTL_INT(_debug, OID_AUTO, ncsize, CTLFLAG_RD, 0, sizeof(struct namecache), "");
  107 
  108 /*
  109  * The new name cache statistics
  110  */
  111 SYSCTL_NODE(_vfs, OID_AUTO, cache, CTLFLAG_RW, 0, "Name cache statistics");
  112 #define STATNODE(mode, name, var) \
  113         SYSCTL_ULONG(_vfs_cache, OID_AUTO, name, mode, var, 0, "");
  114 STATNODE(CTLFLAG_RD, numneg, &numneg);
  115 STATNODE(CTLFLAG_RD, numcache, &numcache);
  116 static u_long numcalls; STATNODE(CTLFLAG_RD, numcalls, &numcalls);
  117 static u_long dothits; STATNODE(CTLFLAG_RD, dothits, &dothits);
  118 static u_long dotdothits; STATNODE(CTLFLAG_RD, dotdothits, &dotdothits);
  119 static u_long numchecks; STATNODE(CTLFLAG_RD, numchecks, &numchecks);
  120 static u_long nummiss; STATNODE(CTLFLAG_RD, nummiss, &nummiss);
  121 static u_long nummisszap; STATNODE(CTLFLAG_RD, nummisszap, &nummisszap);
  122 static u_long numposzaps; STATNODE(CTLFLAG_RD, numposzaps, &numposzaps);
  123 static u_long numposhits; STATNODE(CTLFLAG_RD, numposhits, &numposhits);
  124 static u_long numnegzaps; STATNODE(CTLFLAG_RD, numnegzaps, &numnegzaps);
  125 static u_long numneghits; STATNODE(CTLFLAG_RD, numneghits, &numneghits);
  126 
  127 
  128 static void cache_zap __P((struct namecache *ncp));
  129 
  130 MALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries");
  131 
  132 /*
  133  * Flags in namecache.nc_flag
  134  */
  135 #define NCF_WHITE       1
  136 /*
  137  * Delete an entry from its hash list and move it to the front
  138  * of the LRU list for immediate reuse.
  139  */
  140 static void
  141 cache_zap(ncp)
  142         struct namecache *ncp;
  143 {
  144         LIST_REMOVE(ncp, nc_hash);
  145         LIST_REMOVE(ncp, nc_src);
  146         if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src)) 
  147                 vdrop(ncp->nc_dvp);
  148         if (ncp->nc_vp) {
  149                 TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_dst);
  150         } else {
  151                 TAILQ_REMOVE(&ncneg, ncp, nc_dst);
  152                 numneg--;
  153         }
  154         numcache--;
  155         free(ncp, M_VFSCACHE);
  156 }
  157 
  158 /*
  159  * Lookup an entry in the cache
  160  *
  161  * We don't do this if the segment name is long, simply so the cache
  162  * can avoid holding long names (which would either waste space, or
  163  * add greatly to the complexity).
  164  *
  165  * Lookup is called with dvp pointing to the directory to search,
  166  * cnp pointing to the name of the entry being sought. If the lookup
  167  * succeeds, the vnode is returned in *vpp, and a status of -1 is
  168  * returned. If the lookup determines that the name does not exist
  169  * (negative cacheing), a status of ENOENT is returned. If the lookup
  170  * fails, a status of zero is returned.
  171  */
  172 
  173 int
  174 cache_lookup(dvp, vpp, cnp)
  175         struct vnode *dvp;
  176         struct vnode **vpp;
  177         struct componentname *cnp;
  178 {
  179         struct namecache *ncp;
  180         u_int32_t hash;
  181 
  182         if (!doingcache) {
  183                 cnp->cn_flags &= ~MAKEENTRY;
  184                 return (0);
  185         }
  186 
  187         numcalls++;
  188 
  189         if (cnp->cn_nameptr[0] == '.') {
  190                 if (cnp->cn_namelen == 1) {
  191                         *vpp = dvp;
  192                         dothits++;
  193                         return (-1);
  194                 }
  195                 if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') {
  196                         dotdothits++;
  197                         if (dvp->v_dd->v_id != dvp->v_ddid ||
  198                             (cnp->cn_flags & MAKEENTRY) == 0) {
  199                                 dvp->v_ddid = 0;
  200                                 return (0);
  201                         }
  202                         *vpp = dvp->v_dd;
  203                         return (-1);
  204                 }
  205         }
  206 
  207         hash = fnv_32_buf(cnp->cn_nameptr, cnp->cn_namelen, FNV1_32_INIT);
  208         hash = fnv_32_buf(&dvp->v_id, sizeof(dvp->v_id), hash);
  209         LIST_FOREACH(ncp, (NCHHASH(hash)), nc_hash) {
  210                 numchecks++;
  211                 if (ncp->nc_dvp == dvp && ncp->nc_nlen == cnp->cn_namelen &&
  212                     !bcmp(ncp->nc_name, cnp->cn_nameptr, ncp->nc_nlen))
  213                         break;
  214         }
  215 
  216         /* We failed to find an entry */
  217         if (ncp == 0) {
  218                 if ((cnp->cn_flags & MAKEENTRY) == 0) {
  219                         nummisszap++;
  220                 } else {
  221                         nummiss++;
  222                 }
  223                 nchstats.ncs_miss++;
  224                 return (0);
  225         }
  226 
  227         /* We don't want to have an entry, so dump it */
  228         if ((cnp->cn_flags & MAKEENTRY) == 0) {
  229                 numposzaps++;
  230                 nchstats.ncs_badhits++;
  231                 cache_zap(ncp);
  232                 return (0);
  233         }
  234 
  235         /* We found a "positive" match, return the vnode */
  236         if (ncp->nc_vp) {
  237                 numposhits++;
  238                 nchstats.ncs_goodhits++;
  239                 *vpp = ncp->nc_vp;
  240                 return (-1);
  241         }
  242 
  243         /* We found a negative match, and want to create it, so purge */
  244         if (cnp->cn_nameiop == CREATE) {
  245                 numnegzaps++;
  246                 nchstats.ncs_badhits++;
  247                 cache_zap(ncp);
  248                 return (0);
  249         }
  250 
  251         numneghits++;
  252         /*
  253          * We found a "negative" match, ENOENT notifies client of this match.
  254          * The nc_vpid field records whether this is a whiteout.
  255          */
  256         TAILQ_REMOVE(&ncneg, ncp, nc_dst);
  257         TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst);
  258         nchstats.ncs_neghits++;
  259         if (ncp->nc_flag & NCF_WHITE)
  260                 cnp->cn_flags |= ISWHITEOUT;
  261         return (ENOENT);
  262 }
  263 
  264 /*
  265  * Add an entry to the cache.
  266  */
  267 void
  268 cache_enter(dvp, vp, cnp)
  269         struct vnode *dvp;
  270         struct vnode *vp;
  271         struct componentname *cnp;
  272 {
  273         struct namecache *ncp;
  274         struct nchashhead *ncpp;
  275         u_int32_t hash;
  276         int len;
  277 
  278         if (!doingcache)
  279                 return;
  280 
  281         if (cnp->cn_nameptr[0] == '.') {
  282                 if (cnp->cn_namelen == 1) {
  283                         return;
  284                 }
  285                 if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') {
  286                         if (vp) {
  287                                 dvp->v_dd = vp;
  288                                 dvp->v_ddid = vp->v_id;
  289                         } else {
  290                                 dvp->v_dd = dvp;
  291                                 dvp->v_ddid = 0;
  292                         }
  293                         return;
  294                 }
  295         }
  296          
  297         ncp = (struct namecache *)
  298                 malloc(sizeof *ncp + cnp->cn_namelen, M_VFSCACHE, M_WAITOK);
  299         bzero((char *)ncp, sizeof *ncp);
  300         numcache++;
  301         if (!vp) {
  302                 numneg++;
  303                 ncp->nc_flag = cnp->cn_flags & ISWHITEOUT ? NCF_WHITE : 0;
  304         } else if (vp->v_type == VDIR) {
  305                 vp->v_dd = dvp;
  306                 vp->v_ddid = dvp->v_id;
  307         }
  308 
  309         /*
  310          * Fill in cache info, if vp is NULL this is a "negative" cache entry.
  311          * For negative entries, we have to record whether it is a whiteout.
  312          * the whiteout flag is stored in the nc_vpid field which is
  313          * otherwise unused.
  314          */
  315         ncp->nc_vp = vp;
  316         ncp->nc_dvp = dvp;
  317         len = ncp->nc_nlen = cnp->cn_namelen;
  318         hash = fnv_32_buf(cnp->cn_nameptr, len, FNV1_32_INIT);
  319         bcopy(cnp->cn_nameptr, ncp->nc_name, len);
  320         hash = fnv_32_buf(&dvp->v_id, sizeof(dvp->v_id), hash);
  321         ncpp = NCHHASH(hash);
  322         LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
  323         if (LIST_EMPTY(&dvp->v_cache_src))
  324                 vhold(dvp);
  325         LIST_INSERT_HEAD(&dvp->v_cache_src, ncp, nc_src);
  326         if (vp) {
  327                 TAILQ_INSERT_HEAD(&vp->v_cache_dst, ncp, nc_dst);
  328         } else {
  329                 TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst);
  330         }
  331         if (numneg * ncnegfactor > numcache) {
  332                 ncp = TAILQ_FIRST(&ncneg);
  333                 cache_zap(ncp);
  334         }
  335 }
  336 
  337 /*
  338  * Name cache initialization, from vfs_init() when we are booting
  339  */
  340 void
  341 nchinit()
  342 {
  343 
  344         TAILQ_INIT(&ncneg);
  345         nchashtbl = hashinit(desiredvnodes*2, M_VFSCACHE, &nchash);
  346 }
  347 
  348 /*
  349  * Invalidate all entries to a particular vnode.
  350  *
  351  * Remove all entries in the namecache relating to this vnode and
  352  * change the v_id.  We take the v_id from a global counter, since
  353  * it becomes a handy sequence number in crash-dumps that way.
  354  * No valid vnode will ever have (v_id == 0).
  355  *
  356  * XXX: Only time and the size of v_id prevents this from failing:
  357  * XXX: In theory we should hunt down all (struct vnode*, v_id)
  358  * XXX: soft references and nuke them, at least on the global
  359  * XXX: v_id wraparound.  The period of resistance can be extended
  360  * XXX: by incrementing each vnodes v_id individually instead of
  361  * XXX: using the global v_id.
  362  */
  363 
  364 void
  365 cache_purge(vp)
  366         struct vnode *vp;
  367 {
  368         static u_long nextid;
  369 
  370         while (!LIST_EMPTY(&vp->v_cache_src)) 
  371                 cache_zap(LIST_FIRST(&vp->v_cache_src));
  372         while (!TAILQ_EMPTY(&vp->v_cache_dst)) 
  373                 cache_zap(TAILQ_FIRST(&vp->v_cache_dst));
  374 
  375         do
  376                 nextid++;
  377         while (nextid == vp->v_id || !nextid);
  378         vp->v_id = nextid;
  379         vp->v_dd = vp;
  380         vp->v_ddid = 0;
  381 }
  382 
  383 /*
  384  * Flush all entries referencing a particular filesystem.
  385  *
  386  * Since we need to check it anyway, we will flush all the invalid
  387  * entries at the same time.
  388  */
  389 void
  390 cache_purgevfs(mp)
  391         struct mount *mp;
  392 {
  393         struct nchashhead *ncpp;
  394         struct namecache *ncp, *nnp;
  395 
  396         /* Scan hash tables for applicable entries */
  397         for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
  398                 for (ncp = LIST_FIRST(ncpp); ncp != 0; ncp = nnp) {
  399                         nnp = LIST_NEXT(ncp, nc_hash);
  400                         if (ncp->nc_dvp->v_mount == mp) {
  401                                 cache_zap(ncp);
  402                         }
  403                 }
  404         }
  405 }
  406 
  407 /*
  408  * cache_leaf_test()
  409  *
  410  *      Test whether this (directory) vnode's namei cache entry contains
  411  *      subdirectories or not.  Used to determine whether the directory is
  412  *      a leaf in the namei cache or not.  Note: the directory may still
  413  *      contain files in the namei cache.
  414  *
  415  *      Returns 0 if the directory is a leaf, -1 if it isn't.
  416  */
  417 int
  418 cache_leaf_test(struct vnode *vp)
  419 {
  420         struct namecache *ncpc;
  421 
  422         for (ncpc = LIST_FIRST(&vp->v_cache_src);
  423              ncpc != NULL;
  424              ncpc = LIST_NEXT(ncpc, nc_src)
  425         ) {
  426                 if (ncpc->nc_vp != NULL && ncpc->nc_vp->v_type == VDIR)
  427                         return(-1);
  428         }
  429         return(0);
  430 }
  431 
  432 /*
  433  * Perform canonical checks and cache lookup and pass on to filesystem
  434  * through the vop_cachedlookup only if needed.
  435  */
  436 
  437 int
  438 vfs_cache_lookup(ap)
  439         struct vop_lookup_args /* {
  440                 struct vnode *a_dvp;
  441                 struct vnode **a_vpp;
  442                 struct componentname *a_cnp;
  443         } */ *ap;
  444 {
  445         struct vnode *dvp, *vp;
  446         int lockparent;
  447         int error;
  448         struct vnode **vpp = ap->a_vpp;
  449         struct componentname *cnp = ap->a_cnp;
  450         struct ucred *cred = cnp->cn_cred;
  451         int flags = cnp->cn_flags;
  452         struct proc *p = cnp->cn_proc;
  453         u_long vpid;    /* capability number of vnode */
  454 
  455         *vpp = NULL;
  456         dvp = ap->a_dvp;
  457         lockparent = flags & LOCKPARENT;
  458 
  459         if (dvp->v_type != VDIR)
  460                 return (ENOTDIR);
  461 
  462         if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
  463             (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
  464                 return (EROFS);
  465 
  466         error = VOP_ACCESS(dvp, VEXEC, cred, p);
  467 
  468         if (error)
  469                 return (error);
  470 
  471         error = cache_lookup(dvp, vpp, cnp);
  472 
  473         if (!error) 
  474                 return (VOP_CACHEDLOOKUP(dvp, vpp, cnp));
  475 
  476         if (error == ENOENT)
  477                 return (error);
  478 
  479         vp = *vpp;
  480         vpid = vp->v_id;
  481         cnp->cn_flags &= ~PDIRUNLOCK;
  482         if (dvp == vp) {   /* lookup on "." */
  483                 VREF(vp);
  484                 error = 0;
  485         } else if (flags & ISDOTDOT) {
  486                 VOP_UNLOCK(dvp, 0, p);
  487                 cnp->cn_flags |= PDIRUNLOCK;
  488                 error = vget(vp, LK_EXCLUSIVE, p);
  489                 if (!error && lockparent && (flags & ISLASTCN)) {
  490                         if ((error = vn_lock(dvp, LK_EXCLUSIVE, p)) == 0)
  491                                 cnp->cn_flags &= ~PDIRUNLOCK;
  492                 }
  493         } else {
  494                 error = vget(vp, LK_EXCLUSIVE, p);
  495                 if (!lockparent || error || !(flags & ISLASTCN)) {
  496                         VOP_UNLOCK(dvp, 0, p);
  497                         cnp->cn_flags |= PDIRUNLOCK;
  498                 }
  499         }
  500         /*
  501          * Check that the capability number did not change
  502          * while we were waiting for the lock.
  503          */
  504         if (!error) {
  505                 if (vpid == vp->v_id)
  506                         return (0);
  507                 vput(vp);
  508                 if (lockparent && dvp != vp && (flags & ISLASTCN)) {
  509                         VOP_UNLOCK(dvp, 0, p);
  510                         cnp->cn_flags |= PDIRUNLOCK;
  511                 }
  512         }
  513         if (cnp->cn_flags & PDIRUNLOCK) {
  514                 error = vn_lock(dvp, LK_EXCLUSIVE, p);
  515                 if (error)
  516                         return (error);
  517                 cnp->cn_flags &= ~PDIRUNLOCK;
  518         }
  519         return (VOP_CACHEDLOOKUP(dvp, vpp, cnp));
  520 }
  521 
  522 
  523 #ifndef _SYS_SYSPROTO_H_
  524 struct  __getcwd_args {
  525         u_char  *buf;
  526         u_int   buflen;
  527 };
  528 #endif
  529 
  530 static int disablecwd;
  531 SYSCTL_INT(_debug, OID_AUTO, disablecwd, CTLFLAG_RW, &disablecwd, 0, "");
  532 
  533 static u_long numcwdcalls; STATNODE(CTLFLAG_RD, numcwdcalls, &numcwdcalls);
  534 static u_long numcwdfail1; STATNODE(CTLFLAG_RD, numcwdfail1, &numcwdfail1);
  535 static u_long numcwdfail2; STATNODE(CTLFLAG_RD, numcwdfail2, &numcwdfail2);
  536 static u_long numcwdfail3; STATNODE(CTLFLAG_RD, numcwdfail3, &numcwdfail3);
  537 static u_long numcwdfail4; STATNODE(CTLFLAG_RD, numcwdfail4, &numcwdfail4);
  538 static u_long numcwdfound; STATNODE(CTLFLAG_RD, numcwdfound, &numcwdfound);
  539 int
  540 __getcwd(p, uap)
  541         struct proc *p;
  542         struct __getcwd_args *uap;
  543 {
  544         char *bp, *buf;
  545         int error, i, slash_prefixed;
  546         struct filedesc *fdp;
  547         struct namecache *ncp;
  548         struct vnode *vp;
  549 
  550         numcwdcalls++;
  551         if (disablecwd)
  552                 return (ENODEV);
  553         if (uap->buflen < 2)
  554                 return (EINVAL);
  555         if (uap->buflen > MAXPATHLEN)
  556                 uap->buflen = MAXPATHLEN;
  557         buf = bp = malloc(uap->buflen, M_TEMP, M_WAITOK);
  558         bp += uap->buflen - 1;
  559         *bp = '\0';
  560         fdp = p->p_fd;
  561         slash_prefixed = 0;
  562         for (vp = fdp->fd_cdir; vp != fdp->fd_rdir && vp != rootvnode;) {
  563                 if (vp->v_flag & VROOT) {
  564                         if (vp->v_mount == NULL) {      /* forced unmount */
  565                                 free(buf, M_TEMP);
  566                                 return (EBADF);
  567                         }
  568                         vp = vp->v_mount->mnt_vnodecovered;
  569                         continue;
  570                 }
  571                 if (vp->v_dd->v_id != vp->v_ddid) {
  572                         numcwdfail1++;
  573                         free(buf, M_TEMP);
  574                         return (ENOTDIR);
  575                 }
  576                 ncp = TAILQ_FIRST(&vp->v_cache_dst);
  577                 if (!ncp) {
  578                         numcwdfail2++;
  579                         free(buf, M_TEMP);
  580                         return (ENOENT);
  581                 }
  582                 if (ncp->nc_dvp != vp->v_dd) {
  583                         numcwdfail3++;
  584                         free(buf, M_TEMP);
  585                         return (EBADF);
  586                 }
  587                 for (i = ncp->nc_nlen - 1; i >= 0; i--) {
  588                         if (bp == buf) {
  589                                 numcwdfail4++;
  590                                 free(buf, M_TEMP);
  591                                 return (ENOMEM);
  592                         }
  593                         *--bp = ncp->nc_name[i];
  594                 }
  595                 if (bp == buf) {
  596                         numcwdfail4++;
  597                         free(buf, M_TEMP);
  598                         return (ENOMEM);
  599                 }
  600                 *--bp = '/';
  601                 slash_prefixed = 1;
  602                 vp = vp->v_dd;
  603         }
  604         if (!slash_prefixed) {
  605                 if (bp == buf) {
  606                         numcwdfail4++;
  607                         free(buf, M_TEMP);
  608                         return (ENOMEM);
  609                 }
  610                 *--bp = '/';
  611         }
  612         numcwdfound++;
  613         error = copyout(bp, uap->buf, strlen(bp) + 1);
  614         free(buf, M_TEMP);
  615         return (error);
  616 }
  617 
  618 /*
  619  * Thus begins the fullpath magic.
  620  */
  621 
  622 #undef STATNODE
  623 #define STATNODE(name)                                                  \
  624         static u_int name;                                              \
  625         SYSCTL_UINT(_vfs_cache, OID_AUTO, name, CTLFLAG_RD, &name, 0, "")
  626 
  627 static int disablefullpath;
  628 SYSCTL_INT(_debug, OID_AUTO, disablefullpath, CTLFLAG_RW,
  629     &disablefullpath, 0, "");
  630 
  631 STATNODE(numfullpathcalls);
  632 STATNODE(numfullpathfail1);
  633 STATNODE(numfullpathfail2);
  634 STATNODE(numfullpathfail3);
  635 STATNODE(numfullpathfail4);
  636 STATNODE(numfullpathfound);
  637 
  638 int
  639 textvp_fullpath(struct proc *p, char **retbuf, char **retfreebuf) {
  640         char *bp, *buf;
  641         int i, slash_prefixed;
  642         struct filedesc *fdp;
  643         struct namecache *ncp;
  644         struct vnode *vp, *textvp;
  645 
  646         numfullpathcalls++;
  647         if (disablefullpath)
  648                 return (ENODEV);
  649         textvp = p->p_textvp;
  650         if (textvp == NULL)
  651                 return (EINVAL);
  652         buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
  653         bp = buf + MAXPATHLEN - 1;
  654         *bp = '\0';
  655         fdp = p->p_fd;
  656         slash_prefixed = 0;
  657         for (vp = textvp; vp != fdp->fd_rdir && vp != rootvnode;) {
  658                 if (vp->v_flag & VROOT) {
  659                         if (vp->v_mount == NULL) {      /* forced unmount */
  660                                 free(buf, M_TEMP);
  661                                 return (EBADF);
  662                         }
  663                         vp = vp->v_mount->mnt_vnodecovered;
  664                         continue;
  665                 }
  666                 if (vp != textvp && vp->v_dd->v_id != vp->v_ddid) {
  667                         numfullpathfail1++;
  668                         free(buf, M_TEMP);
  669                         return (ENOTDIR);
  670                 }
  671                 ncp = TAILQ_FIRST(&vp->v_cache_dst);
  672                 if (!ncp) {
  673                         numfullpathfail2++;
  674                         free(buf, M_TEMP);
  675                         return (ENOENT);
  676                 }
  677                 if (vp != textvp && ncp->nc_dvp != vp->v_dd) {
  678                         numfullpathfail3++;
  679                         free(buf, M_TEMP);
  680                         return (EBADF);
  681                 }
  682                 for (i = ncp->nc_nlen - 1; i >= 0; i--) {
  683                         if (bp == buf) {
  684                                 numfullpathfail4++;
  685                                 free(buf, M_TEMP);
  686                                 return (ENOMEM);
  687                         }
  688                         *--bp = ncp->nc_name[i];
  689                 }
  690                 if (bp == buf) {
  691                         numfullpathfail4++;
  692                         free(buf, M_TEMP);
  693                         return (ENOMEM);
  694                 }
  695                 *--bp = '/';
  696                 slash_prefixed = 1;
  697                 vp = ncp->nc_dvp;
  698         }
  699         if (!slash_prefixed) {
  700                 if (bp == buf) {
  701                         numfullpathfail4++;
  702                         free(buf, M_TEMP);
  703                         return (ENOMEM);
  704                 }
  705                 *--bp = '/';
  706         }
  707         numfullpathfound++;
  708         *retbuf = bp; 
  709         *retfreebuf = buf;
  710         return (0);
  711 }

Cache object: 3dddac15db9afa998535af30eb6106f7


[ 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.