vfs_cache.c revision 65665
11541Srgrimes/* 222521Sdyson * Copyright (c) 1989, 1993, 1995 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 522521Sdyson * This code is derived from software contributed to Berkeley by 622521Sdyson * Poul-Henning Kamp of the FreeBSD Project. 722521Sdyson * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 3. All advertising materials mentioning features or use of this software 171541Srgrimes * must display the following acknowledgement: 181541Srgrimes * This product includes software developed by the University of 191541Srgrimes * California, Berkeley and its contributors. 201541Srgrimes * 4. Neither the name of the University nor the names of its contributors 211541Srgrimes * may be used to endorse or promote products derived from this software 221541Srgrimes * without specific prior written permission. 231541Srgrimes * 241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341541Srgrimes * SUCH DAMAGE. 351541Srgrimes * 3623521Sbde * @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95 3750477Speter * $FreeBSD: head/sys/kern/vfs_cache.c 65665 2000-09-10 03:46:12Z bp $ 381541Srgrimes */ 391541Srgrimes 401541Srgrimes#include <sys/param.h> 411541Srgrimes#include <sys/systm.h> 4212820Sphk#include <sys/kernel.h> 4312820Sphk#include <sys/sysctl.h> 441541Srgrimes#include <sys/mount.h> 451541Srgrimes#include <sys/vnode.h> 461541Srgrimes#include <sys/namei.h> 471541Srgrimes#include <sys/malloc.h> 4851906Sphk#include <sys/sysproto.h> 4951906Sphk#include <sys/proc.h> 5051906Sphk#include <sys/filedesc.h> 511541Srgrimes 5251906Sphk/* 5359652Sgreen * This structure describes the elements in the cache of recent 5459652Sgreen * names looked up by namei. 5559652Sgreen */ 5659652Sgreen 5759652Sgreenstruct namecache { 5860938Sjake LIST_ENTRY(namecache) nc_hash; /* hash chain */ 5960938Sjake LIST_ENTRY(namecache) nc_src; /* source vnode list */ 6060938Sjake TAILQ_ENTRY(namecache) nc_dst; /* destination vnode list */ 6159652Sgreen struct vnode *nc_dvp; /* vnode of parent of name */ 6259652Sgreen struct vnode *nc_vp; /* vnode the name refers to */ 6359652Sgreen u_char nc_flag; /* flag bits */ 6459652Sgreen u_char nc_nlen; /* length of name */ 6559652Sgreen char nc_name[0]; /* segment name */ 6659652Sgreen}; 6759652Sgreen 6859652Sgreen/* 691541Srgrimes * Name caching works as follows: 701541Srgrimes * 711541Srgrimes * Names found by directory scans are retained in a cache 721541Srgrimes * for future reference. It is managed LRU, so frequently 731541Srgrimes * used names will hang around. Cache is indexed by hash value 741541Srgrimes * obtained from (vp, name) where vp refers to the directory 751541Srgrimes * containing name. 761541Srgrimes * 7722521Sdyson * If it is a "negative" entry, (i.e. for a name that is known NOT to 7822521Sdyson * exist) the vnode pointer will be NULL. 796968Sphk * 801541Srgrimes * Upon reaching the last segment of a path, if the reference 811541Srgrimes * is for DELETE, or NOCACHE is set (rewrite), and the 821541Srgrimes * name is located in the cache, it will be dropped. 831541Srgrimes */ 841541Srgrimes 851541Srgrimes/* 861541Srgrimes * Structures associated with name cacheing. 871541Srgrimes */ 8851906Sphk#define NCHHASH(dvp, hash) \ 8951906Sphk (&nchashtbl[((dvp)->v_id + (hash)) & nchash]) 9060938Sjakestatic LIST_HEAD(nchashhead, namecache) *nchashtbl; /* Hash Table */ 9160938Sjakestatic TAILQ_HEAD(, namecache) ncneg; /* Hash Table */ 9223521Sbdestatic u_long nchash; /* size of hash table */ 9362622SjhbSYSCTL_ULONG(_debug, OID_AUTO, nchash, CTLFLAG_RD, &nchash, 0, ""); 9425453Sphkstatic u_long ncnegfactor = 16; /* ratio of negative entries */ 9562622SjhbSYSCTL_ULONG(_debug, OID_AUTO, ncnegfactor, CTLFLAG_RW, &ncnegfactor, 0, ""); 9625453Sphkstatic u_long numneg; /* number of cache entries allocated */ 9762622SjhbSYSCTL_ULONG(_debug, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0, ""); 9823521Sbdestatic u_long numcache; /* number of cache entries allocated */ 9962622SjhbSYSCTL_ULONG(_debug, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0, ""); 10022521Sdysonstruct nchstats nchstats; /* cache effectiveness statistics */ 1011541Srgrimes 10223521Sbdestatic int doingcache = 1; /* 1 => enable the cache */ 10323521SbdeSYSCTL_INT(_debug, OID_AUTO, vfscache, CTLFLAG_RW, &doingcache, 0, ""); 10425453SphkSYSCTL_INT(_debug, OID_AUTO, vnsize, CTLFLAG_RD, 0, sizeof(struct vnode), ""); 10525453SphkSYSCTL_INT(_debug, OID_AUTO, ncsize, CTLFLAG_RD, 0, sizeof(struct namecache), ""); 10623521Sbde 10729788Sphk/* 10829788Sphk * The new name cache statistics 10929788Sphk */ 11038984SbdeSYSCTL_NODE(_vfs, OID_AUTO, cache, CTLFLAG_RW, 0, "Name cache statistics"); 11129788Sphk#define STATNODE(mode, name, var) \ 11262622Sjhb SYSCTL_ULONG(_vfs_cache, OID_AUTO, name, mode, var, 0, ""); 11329788SphkSTATNODE(CTLFLAG_RD, numneg, &numneg); 11429788SphkSTATNODE(CTLFLAG_RD, numcache, &numcache); 11529788Sphkstatic u_long numcalls; STATNODE(CTLFLAG_RD, numcalls, &numcalls); 11629788Sphkstatic u_long dothits; STATNODE(CTLFLAG_RD, dothits, &dothits); 11729788Sphkstatic u_long dotdothits; STATNODE(CTLFLAG_RD, dotdothits, &dotdothits); 11829788Sphkstatic u_long numchecks; STATNODE(CTLFLAG_RD, numchecks, &numchecks); 11929788Sphkstatic u_long nummiss; STATNODE(CTLFLAG_RD, nummiss, &nummiss); 12029804Sphkstatic u_long nummisszap; STATNODE(CTLFLAG_RD, nummisszap, &nummisszap); 12129788Sphkstatic u_long numposzaps; STATNODE(CTLFLAG_RD, numposzaps, &numposzaps); 12229788Sphkstatic u_long numposhits; STATNODE(CTLFLAG_RD, numposhits, &numposhits); 12329788Sphkstatic u_long numnegzaps; STATNODE(CTLFLAG_RD, numnegzaps, &numnegzaps); 12429788Sphkstatic u_long numneghits; STATNODE(CTLFLAG_RD, numneghits, &numneghits); 12529788Sphk 12629788Sphk 12725453Sphkstatic void cache_zap __P((struct namecache *ncp)); 1286968Sphk 12951906SphkMALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries"); 13051906Sphk 13122521Sdyson/* 13225453Sphk * Flags in namecache.nc_flag 13325453Sphk */ 13425453Sphk#define NCF_WHITE 1 13525453Sphk/* 13622521Sdyson * Delete an entry from its hash list and move it to the front 13722521Sdyson * of the LRU list for immediate reuse. 13822521Sdyson */ 13925453Sphkstatic void 14025453Sphkcache_zap(ncp) 14125453Sphk struct namecache *ncp; 14225453Sphk{ 14325453Sphk LIST_REMOVE(ncp, nc_hash); 14425453Sphk LIST_REMOVE(ncp, nc_src); 14528954Sphk if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src)) 14628954Sphk vdrop(ncp->nc_dvp); 14725453Sphk if (ncp->nc_vp) { 14825453Sphk TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_dst); 14925453Sphk } else { 15025453Sphk TAILQ_REMOVE(&ncneg, ncp, nc_dst); 15125453Sphk numneg--; 15225453Sphk } 15325453Sphk numcache--; 15451906Sphk free(ncp, M_VFSCACHE); 15522521Sdyson} 1566968Sphk 15722521Sdyson/* 15823521Sbde * Lookup an entry in the cache 1596968Sphk * 16023521Sbde * We don't do this if the segment name is long, simply so the cache 1616968Sphk * can avoid holding long names (which would either waste space, or 1621541Srgrimes * add greatly to the complexity). 1631541Srgrimes * 1646968Sphk * Lookup is called with dvp pointing to the directory to search, 16522521Sdyson * cnp pointing to the name of the entry being sought. If the lookup 16622521Sdyson * succeeds, the vnode is returned in *vpp, and a status of -1 is 16722521Sdyson * returned. If the lookup determines that the name does not exist 16822521Sdyson * (negative cacheing), a status of ENOENT is returned. If the lookup 16922521Sdyson * fails, a status of zero is returned. 1701541Srgrimes */ 1716968Sphk 1721541Srgrimesint 1731541Srgrimescache_lookup(dvp, vpp, cnp) 1741541Srgrimes struct vnode *dvp; 1751541Srgrimes struct vnode **vpp; 1761541Srgrimes struct componentname *cnp; 1771541Srgrimes{ 17851906Sphk struct namecache *ncp; 17951906Sphk u_long hash; 18051906Sphk u_char *cp; 18151906Sphk int len; 1821541Srgrimes 1836928Sphk if (!doingcache) { 1846928Sphk cnp->cn_flags &= ~MAKEENTRY; 1851541Srgrimes return (0); 1866928Sphk } 18725453Sphk 18829788Sphk numcalls++; 18929788Sphk 19025453Sphk if (cnp->cn_nameptr[0] == '.') { 19125453Sphk if (cnp->cn_namelen == 1) { 19225453Sphk *vpp = dvp; 19329788Sphk dothits++; 19425453Sphk return (-1); 19525453Sphk } 19625453Sphk if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { 19729788Sphk dotdothits++; 19825453Sphk if (dvp->v_dd->v_id != dvp->v_ddid || 19925453Sphk (cnp->cn_flags & MAKEENTRY) == 0) { 20025453Sphk dvp->v_ddid = 0; 20125453Sphk return (0); 20225453Sphk } 20325453Sphk *vpp = dvp->v_dd; 20425453Sphk return (-1); 20525453Sphk } 2061541Srgrimes } 2076968Sphk 20851906Sphk hash = 0; 20951906Sphk len = cnp->cn_namelen; 21051906Sphk for (cp = cnp->cn_nameptr; len; len--, cp++) 21151906Sphk hash += *cp; 21251906Sphk LIST_FOREACH(ncp, (NCHHASH(dvp, hash)), nc_hash) { 21329788Sphk numchecks++; 21425453Sphk if (ncp->nc_dvp == dvp && ncp->nc_nlen == cnp->cn_namelen && 21531879Sbde !bcmp(ncp->nc_name, cnp->cn_nameptr, ncp->nc_nlen)) 21622521Sdyson break; 2171541Srgrimes } 2186968Sphk 21922521Sdyson /* We failed to find an entry */ 22022521Sdyson if (ncp == 0) { 22129804Sphk if ((cnp->cn_flags & MAKEENTRY) == 0) { 22229804Sphk nummisszap++; 22329804Sphk } else { 22429804Sphk nummiss++; 22529804Sphk } 22622521Sdyson nchstats.ncs_miss++; 22722521Sdyson return (0); 22822521Sdyson } 22922521Sdyson 2306968Sphk /* We don't want to have an entry, so dump it */ 2316928Sphk if ((cnp->cn_flags & MAKEENTRY) == 0) { 23229788Sphk numposzaps++; 2331541Srgrimes nchstats.ncs_badhits++; 23425453Sphk cache_zap(ncp); 2356968Sphk return (0); 23623521Sbde } 2376968Sphk 2386968Sphk /* We found a "positive" match, return the vnode */ 23922521Sdyson if (ncp->nc_vp) { 24029788Sphk numposhits++; 2411541Srgrimes nchstats.ncs_goodhits++; 2421541Srgrimes *vpp = ncp->nc_vp; 2431541Srgrimes return (-1); 2441541Srgrimes } 2451541Srgrimes 2466968Sphk /* We found a negative match, and want to create it, so purge */ 2476968Sphk if (cnp->cn_nameiop == CREATE) { 24829788Sphk numnegzaps++; 2497013Sphk nchstats.ncs_badhits++; 25025453Sphk cache_zap(ncp); 2516968Sphk return (0); 2526968Sphk } 2536968Sphk 25429788Sphk numneghits++; 25522521Sdyson /* 25622521Sdyson * We found a "negative" match, ENOENT notifies client of this match. 25722521Sdyson * The nc_vpid field records whether this is a whiteout. 25822521Sdyson */ 25925453Sphk TAILQ_REMOVE(&ncneg, ncp, nc_dst); 26025453Sphk TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst); 2616968Sphk nchstats.ncs_neghits++; 26225453Sphk if (ncp->nc_flag & NCF_WHITE) 26325453Sphk cnp->cn_flags |= ISWHITEOUT; 2646968Sphk return (ENOENT); 2651541Srgrimes} 2661541Srgrimes 2671541Srgrimes/* 2686968Sphk * Add an entry to the cache. 2691541Srgrimes */ 2701549Srgrimesvoid 2711541Srgrimescache_enter(dvp, vp, cnp) 2721541Srgrimes struct vnode *dvp; 2731541Srgrimes struct vnode *vp; 2741541Srgrimes struct componentname *cnp; 2751541Srgrimes{ 27651906Sphk struct namecache *ncp; 27751906Sphk struct nchashhead *ncpp; 27851906Sphk u_long hash; 27951906Sphk u_char *cp, *dp; 28051906Sphk int len; 2811541Srgrimes 2821541Srgrimes if (!doingcache) 2831541Srgrimes return; 2846968Sphk 28525453Sphk if (cnp->cn_nameptr[0] == '.') { 28625453Sphk if (cnp->cn_namelen == 1) { 28725453Sphk return; 2886928Sphk } 28925453Sphk if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { 29025453Sphk if (vp) { 29125453Sphk dvp->v_dd = vp; 29225453Sphk dvp->v_ddid = vp->v_id; 29325453Sphk } else { 29425453Sphk dvp->v_dd = dvp; 29525453Sphk dvp->v_ddid = 0; 29625453Sphk } 29725453Sphk return; 29825453Sphk } 2996968Sphk } 30025453Sphk 30125453Sphk ncp = (struct namecache *) 30251906Sphk malloc(sizeof *ncp + cnp->cn_namelen, M_VFSCACHE, M_WAITOK); 30325453Sphk bzero((char *)ncp, sizeof *ncp); 30425453Sphk numcache++; 30528954Sphk if (!vp) { 30625453Sphk numneg++; 30728954Sphk ncp->nc_flag = cnp->cn_flags & ISWHITEOUT ? NCF_WHITE : 0; 30829071Sphk } else if (vp->v_type == VDIR) { 30929071Sphk vp->v_dd = dvp; 31029071Sphk vp->v_ddid = dvp->v_id; 31128954Sphk } 31223521Sbde 31322521Sdyson /* 31422521Sdyson * Fill in cache info, if vp is NULL this is a "negative" cache entry. 31522521Sdyson * For negative entries, we have to record whether it is a whiteout. 31622521Sdyson * the whiteout flag is stored in the nc_vpid field which is 31722521Sdyson * otherwise unused. 31822521Sdyson */ 3191541Srgrimes ncp->nc_vp = vp; 3201541Srgrimes ncp->nc_dvp = dvp; 32151906Sphk len = ncp->nc_nlen = cnp->cn_namelen; 32251906Sphk hash = 0; 32351906Sphk dp = ncp->nc_name; 32451906Sphk for (cp = cnp->cn_nameptr; len; len--, cp++, dp++) 32551906Sphk hash += (*dp = *cp); 32651906Sphk ncpp = NCHHASH(dvp, hash); 3276928Sphk LIST_INSERT_HEAD(ncpp, ncp, nc_hash); 32828954Sphk if (LIST_EMPTY(&dvp->v_cache_src)) 32928954Sphk vhold(dvp); 33025453Sphk LIST_INSERT_HEAD(&dvp->v_cache_src, ncp, nc_src); 33125453Sphk if (vp) { 33225453Sphk TAILQ_INSERT_HEAD(&vp->v_cache_dst, ncp, nc_dst); 33325453Sphk } else { 33425453Sphk TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst); 33525453Sphk } 33651906Sphk if (numneg * ncnegfactor > numcache) { 33725453Sphk ncp = TAILQ_FIRST(&ncneg); 33825453Sphk cache_zap(ncp); 33925453Sphk } 3401541Srgrimes} 3411541Srgrimes 3421541Srgrimes/* 3431541Srgrimes * Name cache initialization, from vfs_init() when we are booting 3441541Srgrimes */ 3451549Srgrimesvoid 3461541Srgrimesnchinit() 3471541Srgrimes{ 34823521Sbde 34925453Sphk TAILQ_INIT(&ncneg); 35051906Sphk nchashtbl = hashinit(desiredvnodes*2, M_VFSCACHE, &nchash); 3511541Srgrimes} 3521541Srgrimes 3531541Srgrimes/* 35446011Sphk * Invalidate all entries to a particular vnode. 35523521Sbde * 35646011Sphk * Remove all entries in the namecache relating to this vnode and 35746011Sphk * change the v_id. We take the v_id from a global counter, since 35846011Sphk * it becomes a handy sequence number in crash-dumps that way. 35946011Sphk * No valid vnode will ever have (v_id == 0). 36046011Sphk * 36146011Sphk * XXX: Only time and the size of v_id prevents this from failing: 36246011Sphk * XXX: In theory we should hunt down all (struct vnode*, v_id) 36346011Sphk * XXX: soft references and nuke them, at least on the global 36446011Sphk * XXX: v_id wraparound. The period of resistance can be extended 36546011Sphk * XXX: by incrementing each vnodes v_id individually instead of 36646011Sphk * XXX: using the global v_id. 3671541Srgrimes */ 36846011Sphk 3691549Srgrimesvoid 3701541Srgrimescache_purge(vp) 3711541Srgrimes struct vnode *vp; 3721541Srgrimes{ 37329094Sphk static u_long nextid; 3741541Srgrimes 37525453Sphk while (!LIST_EMPTY(&vp->v_cache_src)) 37625453Sphk cache_zap(LIST_FIRST(&vp->v_cache_src)); 37725453Sphk while (!TAILQ_EMPTY(&vp->v_cache_dst)) 37825453Sphk cache_zap(TAILQ_FIRST(&vp->v_cache_dst)); 37925453Sphk 38046011Sphk do 38146011Sphk nextid++; 38246011Sphk while (nextid == vp->v_id || !nextid); 38329094Sphk vp->v_id = nextid; 38425453Sphk vp->v_dd = vp; 38525453Sphk vp->v_ddid = 0; 3861541Srgrimes} 3871541Srgrimes 3881541Srgrimes/* 3896968Sphk * Flush all entries referencing a particular filesystem. 3901541Srgrimes * 3916968Sphk * Since we need to check it anyway, we will flush all the invalid 39212968Sphk * entries at the same time. 3931541Srgrimes */ 3941549Srgrimesvoid 3951541Srgrimescache_purgevfs(mp) 3961541Srgrimes struct mount *mp; 3971541Srgrimes{ 3986968Sphk struct nchashhead *ncpp; 39922521Sdyson struct namecache *ncp, *nnp; 4001541Srgrimes 4016968Sphk /* Scan hash tables for applicable entries */ 40229071Sphk for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) { 40325453Sphk for (ncp = LIST_FIRST(ncpp); ncp != 0; ncp = nnp) { 40425453Sphk nnp = LIST_NEXT(ncp, nc_hash); 40525453Sphk if (ncp->nc_dvp->v_mount == mp) { 40625453Sphk cache_zap(ncp); 4076968Sphk } 4081541Srgrimes } 4091541Srgrimes } 4101541Srgrimes} 41128787Sphk 41228787Sphk/* 41328787Sphk * Perform canonical checks and cache lookup and pass on to filesystem 41428787Sphk * through the vop_cachedlookup only if needed. 41528787Sphk */ 41628787Sphk 41728787Sphkint 41828787Sphkvfs_cache_lookup(ap) 41928787Sphk struct vop_lookup_args /* { 42028787Sphk struct vnode *a_dvp; 42128787Sphk struct vnode **a_vpp; 42228787Sphk struct componentname *a_cnp; 42328787Sphk } */ *ap; 42428787Sphk{ 42565665Sbp struct vnode *dvp, *vp; 42665665Sbp int lockparent; 42728787Sphk int error; 42828787Sphk struct vnode **vpp = ap->a_vpp; 42928787Sphk struct componentname *cnp = ap->a_cnp; 43028787Sphk struct ucred *cred = cnp->cn_cred; 43128787Sphk int flags = cnp->cn_flags; 43228787Sphk struct proc *p = cnp->cn_proc; 43328787Sphk u_long vpid; /* capability number of vnode */ 43428787Sphk 43528787Sphk *vpp = NULL; 43665665Sbp dvp = ap->a_dvp; 43728787Sphk lockparent = flags & LOCKPARENT; 43828787Sphk 43965665Sbp if (dvp->v_type != VDIR) 44028787Sphk return (ENOTDIR); 44128787Sphk 44265665Sbp if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && 44328787Sphk (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 44428787Sphk return (EROFS); 44528787Sphk 44665665Sbp error = VOP_ACCESS(dvp, VEXEC, cred, p); 44728787Sphk 44828787Sphk if (error) 44928787Sphk return (error); 45028787Sphk 45165665Sbp error = cache_lookup(dvp, vpp, cnp); 45228787Sphk 45328787Sphk if (!error) 45465665Sbp return (VOP_CACHEDLOOKUP(dvp, vpp, cnp)); 45528787Sphk 45628787Sphk if (error == ENOENT) 45728787Sphk return (error); 45828787Sphk 45965665Sbp vp = *vpp; 46065665Sbp vpid = vp->v_id; 46165665Sbp if (dvp == vp) { /* lookup on "." */ 46265665Sbp VREF(vp); 46328787Sphk error = 0; 46428787Sphk } else if (flags & ISDOTDOT) { 46565665Sbp VOP_UNLOCK(dvp, 0, p); 46665665Sbp error = vget(vp, LK_EXCLUSIVE, p); 46728787Sphk if (!error && lockparent && (flags & ISLASTCN)) 46865665Sbp error = vn_lock(dvp, LK_EXCLUSIVE, p); 46928787Sphk } else { 47065665Sbp error = vget(vp, LK_EXCLUSIVE, p); 47128787Sphk if (!lockparent || error || !(flags & ISLASTCN)) 47265665Sbp VOP_UNLOCK(dvp, 0, p); 47328787Sphk } 47428787Sphk /* 47528787Sphk * Check that the capability number did not change 47628787Sphk * while we were waiting for the lock. 47728787Sphk */ 47828787Sphk if (!error) { 47965665Sbp if (vpid == vp->v_id) 48028787Sphk return (0); 48165665Sbp vput(vp); 48265665Sbp if (lockparent && dvp != vp && (flags & ISLASTCN)) 48365665Sbp VOP_UNLOCK(dvp, 0, p); 48428787Sphk } 48565665Sbp error = vn_lock(dvp, LK_EXCLUSIVE, p); 48628787Sphk if (error) 48728787Sphk return (error); 48865665Sbp return (VOP_CACHEDLOOKUP(dvp, vpp, cnp)); 48928787Sphk} 49051906Sphk 49151906Sphk 49251906Sphk#ifndef _SYS_SYSPROTO_H_ 49351906Sphkstruct __getcwd_args { 49451906Sphk u_char *buf; 49551906Sphk u_int buflen; 49651906Sphk}; 49751906Sphk#endif 49851906Sphk 49951906Sphkstatic int disablecwd; 50051906SphkSYSCTL_INT(_debug, OID_AUTO, disablecwd, CTLFLAG_RW, &disablecwd, 0, ""); 50151906Sphk 50251906Sphkstatic u_long numcwdcalls; STATNODE(CTLFLAG_RD, numcwdcalls, &numcwdcalls); 50351906Sphkstatic u_long numcwdfail1; STATNODE(CTLFLAG_RD, numcwdfail1, &numcwdfail1); 50451906Sphkstatic u_long numcwdfail2; STATNODE(CTLFLAG_RD, numcwdfail2, &numcwdfail2); 50551906Sphkstatic u_long numcwdfail3; STATNODE(CTLFLAG_RD, numcwdfail3, &numcwdfail3); 50651906Sphkstatic u_long numcwdfail4; STATNODE(CTLFLAG_RD, numcwdfail4, &numcwdfail4); 50751906Sphkstatic u_long numcwdfound; STATNODE(CTLFLAG_RD, numcwdfound, &numcwdfound); 50851906Sphkint 50951906Sphk__getcwd(p, uap) 51051906Sphk struct proc *p; 51151906Sphk struct __getcwd_args *uap; 51251906Sphk{ 51351906Sphk char *bp, *buf; 51451906Sphk int error, i, slash_prefixed; 51551906Sphk struct filedesc *fdp; 51651906Sphk struct namecache *ncp; 51751906Sphk struct vnode *vp; 51851906Sphk 51951906Sphk numcwdcalls++; 52051906Sphk if (disablecwd) 52151906Sphk return (ENODEV); 52251906Sphk if (uap->buflen < 2) 52351906Sphk return (EINVAL); 52451906Sphk if (uap->buflen > MAXPATHLEN) 52551906Sphk uap->buflen = MAXPATHLEN; 52651906Sphk buf = bp = malloc(uap->buflen, M_TEMP, M_WAITOK); 52751906Sphk bp += uap->buflen - 1; 52851906Sphk *bp = '\0'; 52951906Sphk fdp = p->p_fd; 53051906Sphk slash_prefixed = 0; 53151906Sphk for (vp = fdp->fd_cdir; vp != fdp->fd_rdir && vp != rootvnode;) { 53251906Sphk if (vp->v_flag & VROOT) { 53357199Speter if (vp->v_mount == NULL) /* forced unmount */ 53457199Speter return (EBADF); 53551906Sphk vp = vp->v_mount->mnt_vnodecovered; 53651906Sphk continue; 53751906Sphk } 53851906Sphk if (vp->v_dd->v_id != vp->v_ddid) { 53951906Sphk numcwdfail1++; 54051906Sphk free(buf, M_TEMP); 54151906Sphk return (ENOTDIR); 54251906Sphk } 54351906Sphk ncp = TAILQ_FIRST(&vp->v_cache_dst); 54451906Sphk if (!ncp) { 54551906Sphk numcwdfail2++; 54651906Sphk free(buf, M_TEMP); 54751906Sphk return (ENOENT); 54851906Sphk } 54951906Sphk if (ncp->nc_dvp != vp->v_dd) { 55051906Sphk numcwdfail3++; 55151906Sphk free(buf, M_TEMP); 55251906Sphk return (EBADF); 55351906Sphk } 55451906Sphk for (i = ncp->nc_nlen - 1; i >= 0; i--) { 55551906Sphk if (bp == buf) { 55651906Sphk numcwdfail4++; 55751906Sphk free(buf, M_TEMP); 55851906Sphk return (ENOMEM); 55951906Sphk } 56051906Sphk *--bp = ncp->nc_name[i]; 56151906Sphk } 56251906Sphk if (bp == buf) { 56351906Sphk numcwdfail4++; 56451906Sphk free(buf, M_TEMP); 56551906Sphk return (ENOMEM); 56651906Sphk } 56751906Sphk *--bp = '/'; 56851906Sphk slash_prefixed = 1; 56951906Sphk vp = vp->v_dd; 57051906Sphk } 57151906Sphk if (!slash_prefixed) { 57251906Sphk if (bp == buf) { 57351906Sphk numcwdfail4++; 57451906Sphk free(buf, M_TEMP); 57551906Sphk return (ENOMEM); 57651906Sphk } 57751906Sphk *--bp = '/'; 57851906Sphk } 57951906Sphk numcwdfound++; 58051906Sphk error = copyout(bp, uap->buf, strlen(bp) + 1); 58151906Sphk free(buf, M_TEMP); 58251906Sphk return (error); 58351906Sphk} 58451906Sphk 58559652Sgreen/* 58659652Sgreen * Thus begins the fullpath magic. 58759652Sgreen */ 58859652Sgreen 58959652Sgreen#undef STATNODE 59059652Sgreen#define STATNODE(name) \ 59159652Sgreen static u_int name; \ 59262622Sjhb SYSCTL_UINT(_vfs_cache, OID_AUTO, name, CTLFLAG_RD, &name, 0, "") 59359652Sgreen 59459652Sgreenstatic int disablefullpath; 59559652SgreenSYSCTL_INT(_debug, OID_AUTO, disablefullpath, CTLFLAG_RW, 59659652Sgreen &disablefullpath, 0, ""); 59759652Sgreen 59859652SgreenSTATNODE(numfullpathcalls); 59959652SgreenSTATNODE(numfullpathfail1); 60059652SgreenSTATNODE(numfullpathfail2); 60159652SgreenSTATNODE(numfullpathfail3); 60259652SgreenSTATNODE(numfullpathfail4); 60359652SgreenSTATNODE(numfullpathfound); 60459652Sgreen 60559652Sgreenint 60659652Sgreentextvp_fullpath(struct proc *p, char **retbuf, char **retfreebuf) { 60759652Sgreen char *bp, *buf; 60859652Sgreen int i, slash_prefixed; 60959652Sgreen struct filedesc *fdp; 61059652Sgreen struct namecache *ncp; 61159652Sgreen struct vnode *vp, *textvp; 61259652Sgreen 61359652Sgreen numfullpathcalls++; 61459652Sgreen if (disablefullpath) 61559652Sgreen return (ENODEV); 61659652Sgreen textvp = p->p_textvp; 61759652Sgreen if (textvp == NULL) 61859652Sgreen return (EINVAL); 61959652Sgreen buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 62059652Sgreen bp = buf + MAXPATHLEN - 1; 62159652Sgreen *bp = '\0'; 62259652Sgreen fdp = p->p_fd; 62359652Sgreen slash_prefixed = 0; 62459652Sgreen for (vp = textvp; vp != fdp->fd_rdir && vp != rootvnode;) { 62559652Sgreen if (vp->v_flag & VROOT) { 62659652Sgreen if (vp->v_mount == NULL) { /* forced unmount */ 62759652Sgreen free(buf, M_TEMP); 62859652Sgreen return (EBADF); 62959652Sgreen } 63059652Sgreen vp = vp->v_mount->mnt_vnodecovered; 63159652Sgreen continue; 63259652Sgreen } 63359652Sgreen if (vp != textvp && vp->v_dd->v_id != vp->v_ddid) { 63459652Sgreen numfullpathfail1++; 63559652Sgreen free(buf, M_TEMP); 63659652Sgreen return (ENOTDIR); 63759652Sgreen } 63859652Sgreen ncp = TAILQ_FIRST(&vp->v_cache_dst); 63959652Sgreen if (!ncp) { 64059652Sgreen numfullpathfail2++; 64159652Sgreen free(buf, M_TEMP); 64259652Sgreen return (ENOENT); 64359652Sgreen } 64459652Sgreen if (vp != textvp && ncp->nc_dvp != vp->v_dd) { 64559652Sgreen numfullpathfail3++; 64659652Sgreen free(buf, M_TEMP); 64759652Sgreen return (EBADF); 64859652Sgreen } 64959652Sgreen for (i = ncp->nc_nlen - 1; i >= 0; i--) { 65059652Sgreen if (bp == buf) { 65159652Sgreen numfullpathfail4++; 65259652Sgreen free(buf, M_TEMP); 65359652Sgreen return (ENOMEM); 65459652Sgreen } 65559652Sgreen *--bp = ncp->nc_name[i]; 65659652Sgreen } 65759652Sgreen if (bp == buf) { 65859652Sgreen numfullpathfail4++; 65959652Sgreen free(buf, M_TEMP); 66059652Sgreen return (ENOMEM); 66159652Sgreen } 66259652Sgreen *--bp = '/'; 66359652Sgreen slash_prefixed = 1; 66459652Sgreen vp = ncp->nc_dvp; 66559652Sgreen } 66659652Sgreen if (!slash_prefixed) { 66759652Sgreen if (bp == buf) { 66859652Sgreen numfullpathfail4++; 66959652Sgreen free(buf, M_TEMP); 67059652Sgreen return (ENOMEM); 67159652Sgreen } 67259652Sgreen *--bp = '/'; 67359652Sgreen } 67459652Sgreen numfullpathfound++; 67559652Sgreen *retbuf = bp; 67659652Sgreen *retfreebuf = buf; 67759652Sgreen return (0); 67859652Sgreen} 679