1 /* $NetBSD: vfs_getcwd.c,v 1.15 2001/11/12 15:25:37 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Bill Sommerfeld. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: vfs_getcwd.c,v 1.15 2001/11/12 15:25:37 lukem Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/namei.h> 45 #include <sys/filedesc.h> 46 #include <sys/kernel.h> 47 #include <sys/file.h> 48 #include <sys/stat.h> 49 #include <sys/vnode.h> 50 #include <sys/mount.h> 51 #include <sys/proc.h> 52 #include <sys/uio.h> 53 #include <sys/malloc.h> 54 #include <sys/dirent.h> 55 #include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 56 57 #include <sys/syscallargs.h> 58 59 static int 60 getcwd_scandir __P((struct vnode **, struct vnode **, 61 char **, char *, struct proc *)); 62 static int 63 getcwd_getcache __P((struct vnode **, struct vnode **, 64 char **, char *)); 65 int 66 getcwd_common __P((struct vnode *, struct vnode *, 67 char **, char *, int, int, struct proc *)); 68 69 #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) 70 71 /* 72 * Vnode variable naming conventions in this file: 73 * 74 * rvp: the current root we're aiming towards. 75 * lvp, *lvpp: the "lower" vnode 76 * uvp, *uvpp: the "upper" vnode. 77 * 78 * Since all the vnodes we're dealing with are directories, and the 79 * lookups are going *up* in the filesystem rather than *down*, the 80 * usual "pvp" (parent) or "dvp" (directory) naming conventions are 81 * too confusing. 82 */ 83 84 /* 85 * XXX Will infinite loop in certain cases if a directory read reliably 86 * returns EINVAL on last block. 87 * XXX is EINVAL the right thing to return if a directory is malformed? 88 */ 89 90 /* 91 * XXX Untested vs. mount -o union; probably does the wrong thing. 92 */ 93 94 /* 95 * Find parent vnode of *lvpp, return in *uvpp 96 * 97 * If we care about the name, scan it looking for name of directory 98 * entry pointing at lvp. 99 * 100 * Place the name in the buffer which starts at bufp, immediately 101 * before *bpp, and move bpp backwards to point at the start of it. 102 * 103 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 104 * On exit, *uvpp is either NULL or is a locked vnode reference. 105 */ 106 static int 107 getcwd_scandir(lvpp, uvpp, bpp, bufp, p) 108 struct vnode **lvpp; 109 struct vnode **uvpp; 110 char **bpp; 111 char *bufp; 112 struct proc *p; 113 { 114 int error = 0; 115 int eofflag; 116 off_t off; 117 int tries; 118 struct uio uio; 119 struct iovec iov; 120 char *dirbuf = NULL; 121 int dirbuflen; 122 ino_t fileno; 123 struct vattr va; 124 struct vnode *uvp = NULL; 125 struct vnode *lvp = *lvpp; 126 struct componentname cn; 127 int len, reclen; 128 tries = 0; 129 130 /* 131 * If we want the filename, get some info we need while the 132 * current directory is still locked. 133 */ 134 if (bufp != NULL) { 135 error = VOP_GETATTR(lvp, &va, p->p_ucred, p); 136 if (error) { 137 vput(lvp); 138 *lvpp = NULL; 139 *uvpp = NULL; 140 return error; 141 } 142 } 143 144 /* 145 * Ok, we have to do it the hard way.. 146 * Next, get parent vnode using lookup of .. 147 */ 148 cn.cn_nameiop = LOOKUP; 149 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 150 cn.cn_proc = p; 151 cn.cn_cred = p->p_ucred; 152 cn.cn_pnbuf = NULL; 153 cn.cn_nameptr = ".."; 154 cn.cn_namelen = 2; 155 cn.cn_hash = 0; 156 cn.cn_consume = 0; 157 158 /* 159 * At this point, lvp is locked and will be unlocked by the lookup. 160 * On successful return, *uvpp will be locked 161 */ 162 error = VOP_LOOKUP(lvp, uvpp, &cn); 163 if (error) { 164 vput(lvp); 165 *lvpp = NULL; 166 *uvpp = NULL; 167 return error; 168 } 169 uvp = *uvpp; 170 171 /* If we don't care about the pathname, we're done */ 172 if (bufp == NULL) { 173 vrele(lvp); 174 *lvpp = NULL; 175 return 0; 176 } 177 178 fileno = va.va_fileid; 179 180 dirbuflen = DIRBLKSIZ; 181 if (dirbuflen < va.va_blocksize) 182 dirbuflen = va.va_blocksize; 183 dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 184 185 #if 0 186 unionread: 187 #endif 188 off = 0; 189 do { 190 /* call VOP_READDIR of parent */ 191 iov.iov_base = dirbuf; 192 iov.iov_len = dirbuflen; 193 194 uio.uio_iov = &iov; 195 uio.uio_iovcnt = 1; 196 uio.uio_offset = off; 197 uio.uio_resid = dirbuflen; 198 uio.uio_segflg = UIO_SYSSPACE; 199 uio.uio_rw = UIO_READ; 200 uio.uio_procp = p; 201 202 eofflag = 0; 203 204 error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag, 0, 0); 205 206 off = uio.uio_offset; 207 208 /* 209 * Try again if NFS tosses its cookies. 210 * XXX this can still loop forever if the directory is busted 211 * such that the second or subsequent page of it always 212 * returns EINVAL 213 */ 214 if ((error == EINVAL) && (tries < 3)) { 215 off = 0; 216 tries++; 217 continue; /* once more, with feeling */ 218 } 219 220 if (!error) { 221 char *cpos; 222 struct dirent *dp; 223 224 cpos = dirbuf; 225 tries = 0; 226 227 /* scan directory page looking for matching vnode */ 228 for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { 229 dp = (struct dirent *) cpos; 230 reclen = dp->d_reclen; 231 232 /* check for malformed directory.. */ 233 if (reclen < DIRENT_MINSIZE) { 234 error = EINVAL; 235 goto out; 236 } 237 /* 238 * XXX should perhaps do VOP_LOOKUP to 239 * check that we got back to the right place, 240 * but getting the locking games for that 241 * right would be heinous. 242 */ 243 if ((dp->d_type != DT_WHT) && 244 (dp->d_fileno == fileno)) { 245 char *bp = *bpp; 246 bp -= dp->d_namlen; 247 248 if (bp <= bufp) { 249 error = ERANGE; 250 goto out; 251 } 252 memcpy(bp, dp->d_name, dp->d_namlen); 253 error = 0; 254 *bpp = bp; 255 goto out; 256 } 257 cpos += reclen; 258 } 259 } else 260 goto out; 261 } while (!eofflag); 262 #if 0 263 /* 264 * Deal with mount -o union, which unions only the 265 * root directory of the mount. 266 */ 267 if ((uvp->v_flag & VROOT) && 268 (uvp->v_mount->mnt_flag & MNT_UNION)) { 269 struct vnode *tvp = uvp; 270 uvp = uvp->v_mount->mnt_vnodecovered; 271 vput(tvp); 272 VREF(uvp); 273 *uvpp = uvp; 274 error = vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY); 275 if (error != 0) { 276 vrele(uvp); 277 *uvpp = uvp = NULL; 278 goto out; 279 } 280 goto unionread; 281 } 282 #endif 283 error = ENOENT; 284 285 out: 286 vrele(lvp); 287 *lvpp = NULL; 288 free(dirbuf, M_TEMP); 289 return error; 290 } 291 292 /* 293 * Look in the vnode-to-name reverse cache to see if 294 * we can find things the easy way. 295 * 296 * XXX vget failure path is untested. 297 * 298 * On entry, *lvpp is a locked vnode reference. 299 * On exit, one of the following is the case: 300 * 0) Both *lvpp and *uvpp are NULL and failure is returned. 301 * 1) *uvpp is NULL, *lvpp remains locked and -1 is returned (cache miss) 302 * 2) *uvpp is a locked vnode reference, *lvpp is vput and NULL'ed 303 * and 0 is returned (cache hit) 304 */ 305 306 static int 307 getcwd_getcache(lvpp, uvpp, bpp, bufp) 308 struct vnode **lvpp, **uvpp; 309 char **bpp; 310 char *bufp; 311 { 312 struct vnode *lvp, *uvp = NULL; 313 int error; 314 int vpid; 315 316 lvp = *lvpp; 317 318 /* 319 * This returns 0 on a cache hit, -1 on a clean cache miss, 320 * or an errno on other failure. 321 */ 322 error = cache_revlookup(lvp, uvpp, bpp, bufp); 323 if (error) { 324 if (error != -1) { 325 vput(lvp); 326 *lvpp = NULL; 327 *uvpp = NULL; 328 } 329 return error; 330 } 331 uvp = *uvpp; 332 vpid = uvp->v_id; 333 334 /* 335 * Since we're going up, we have to release the current lock 336 * before we take the parent lock. 337 */ 338 339 VOP_UNLOCK(lvp, 0); 340 341 error = vget(uvp, LK_EXCLUSIVE | LK_RETRY); 342 if (error != 0) 343 *uvpp = NULL; 344 /* 345 * Verify that vget succeeded, and check that vnode capability 346 * didn't change while we were waiting for the lock. 347 */ 348 if (error || (vpid != uvp->v_id)) { 349 /* 350 * Oops, we missed. If the vget failed, or the 351 * capability changed, try to get our lock back; if 352 * that works, tell caller to try things the hard way, 353 * otherwise give up. 354 */ 355 if (!error) vput(uvp); 356 *uvpp = NULL; 357 358 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 359 360 if (!error) 361 return -1; 362 } 363 vrele(lvp); 364 *lvpp = NULL; 365 366 return error; 367 } 368 369 /* 370 * common routine shared by sys___getcwd() and vn_isunder() 371 */ 372 373 #define GETCWD_CHECK_ACCESS 0x0001 374 375 int 376 getcwd_common (lvp, rvp, bpp, bufp, limit, flags, p) 377 struct vnode *lvp; 378 struct vnode *rvp; 379 char **bpp; 380 char *bufp; 381 int limit; 382 int flags; 383 struct proc *p; 384 { 385 struct cwdinfo *cwdi = p->p_cwdi; 386 struct vnode *uvp = NULL; 387 char *bp = NULL; 388 int error; 389 int perms = VEXEC; 390 391 if (rvp == NULL) { 392 rvp = cwdi->cwdi_rdir; 393 if (rvp == NULL) 394 rvp = rootvnode; 395 } 396 397 VREF(rvp); 398 VREF(lvp); 399 400 /* 401 * Error handling invariant: 402 * Before a `goto out': 403 * lvp is either NULL, or locked and held. 404 * uvp is either NULL, or locked and held. 405 */ 406 407 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 408 if (error) { 409 vrele(lvp); 410 lvp = NULL; 411 goto out; 412 } 413 if (bufp) 414 bp = *bpp; 415 /* 416 * this loop will terminate when one of the following happens: 417 * - we hit the root 418 * - getdirentries or lookup fails 419 * - we run out of space in the buffer. 420 */ 421 if (lvp == rvp) { 422 if (bp) 423 *(--bp) = '/'; 424 goto out; 425 } 426 do { 427 if (lvp->v_type != VDIR) { 428 error = ENOTDIR; 429 goto out; 430 } 431 432 /* 433 * access check here is optional, depending on 434 * whether or not caller cares. 435 */ 436 if (flags & GETCWD_CHECK_ACCESS) { 437 error = VOP_ACCESS(lvp, perms, p->p_ucred, p); 438 if (error) 439 goto out; 440 perms = VEXEC|VREAD; 441 } 442 443 /* 444 * step up if we're a covered vnode.. 445 */ 446 while (lvp->v_flag & VROOT) { 447 struct vnode *tvp; 448 449 if (lvp == rvp) 450 goto out; 451 452 tvp = lvp; 453 lvp = lvp->v_mount->mnt_vnodecovered; 454 vput(tvp); 455 /* 456 * hodie natus est radici frater 457 */ 458 if (lvp == NULL) { 459 error = ENOENT; 460 goto out; 461 } 462 VREF(lvp); 463 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 464 if (error != 0) { 465 vrele(lvp); 466 lvp = NULL; 467 goto out; 468 } 469 } 470 /* 471 * Look in the name cache; if that fails, look in the 472 * directory.. 473 */ 474 error = getcwd_getcache(&lvp, &uvp, &bp, bufp); 475 if (error == -1) 476 error = getcwd_scandir(&lvp, &uvp, &bp, bufp, p); 477 if (error) 478 goto out; 479 #if DIAGNOSTIC 480 if (lvp != NULL) 481 panic("getcwd: oops, forgot to null lvp"); 482 if (bufp && (bp <= bufp)) { 483 panic("getcwd: oops, went back too far"); 484 } 485 #endif 486 if (bp) 487 *(--bp) = '/'; 488 lvp = uvp; 489 uvp = NULL; 490 limit--; 491 } while ((lvp != rvp) && (limit > 0)); 492 493 out: 494 if (bpp) 495 *bpp = bp; 496 if (uvp) 497 vput(uvp); 498 if (lvp) 499 vput(lvp); 500 vrele(rvp); 501 return error; 502 } 503 504 /* 505 * Check if one directory can be found inside another in the directory 506 * hierarchy. 507 * 508 * Intended to be used in chroot, chdir, fchdir, etc., to ensure that 509 * chroot() actually means something. 510 */ 511 int 512 vn_isunder(lvp, rvp, p) 513 struct vnode *lvp; 514 struct vnode *rvp; 515 struct proc *p; 516 { 517 int error; 518 519 error = getcwd_common (lvp, rvp, NULL, NULL, MAXPATHLEN/2, 0, p); 520 521 if (!error) 522 return 1; 523 else 524 return 0; 525 } 526 527 /* 528 * Returns true if proc p1's root directory equal to or under p2's 529 * root directory. 530 * 531 * Intended to be used from ptrace/procfs sorts of things. 532 */ 533 534 int 535 proc_isunder (p1, p2) 536 struct proc *p1; 537 struct proc *p2; 538 { 539 struct vnode *r1 = p1->p_cwdi->cwdi_rdir; 540 struct vnode *r2 = p2->p_cwdi->cwdi_rdir; 541 542 if (r1 == NULL) 543 return (r2 == NULL); 544 else if (r2 == NULL) 545 return 1; 546 else 547 return vn_isunder(r1, r2, p2); 548 } 549 550 /* 551 * Find pathname of process's current directory. 552 * 553 * Use vfs vnode-to-name reverse cache; if that fails, fall back 554 * to reading directory contents. 555 */ 556 557 int 558 sys___getcwd(p, v, retval) 559 struct proc *p; 560 void *v; 561 register_t *retval; 562 { 563 struct sys___getcwd_args /* { 564 syscallarg(char *) bufp; 565 syscallarg(size_t) length; 566 } */ *uap = v; 567 568 int error; 569 char *path; 570 char *bp, *bend; 571 int len = SCARG(uap, length); 572 int lenused; 573 574 if (len > MAXPATHLEN*4) 575 len = MAXPATHLEN*4; 576 else if (len < 2) 577 return ERANGE; 578 579 path = (char *)malloc(len, M_TEMP, M_WAITOK); 580 if (!path) 581 return ENOMEM; 582 583 bp = &path[len]; 584 bend = bp; 585 *(--bp) = '\0'; 586 587 /* 588 * 5th argument here is "max number of vnodes to traverse". 589 * Since each entry takes up at least 2 bytes in the output buffer, 590 * limit it to N/2 vnodes for an N byte buffer. 591 */ 592 error = getcwd_common (p->p_cwdi->cwdi_cdir, NULL, &bp, path, len/2, 593 GETCWD_CHECK_ACCESS, p); 594 595 if (error) 596 goto out; 597 lenused = bend - bp; 598 *retval = lenused; 599 /* put the result into user buffer */ 600 error = copyout(bp, SCARG(uap, bufp), lenused); 601 602 out: 603 free(path, M_TEMP); 604 return error; 605 } 606