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