1 /* $NetBSD: vfs_getcwd.c,v 1.40 2007/12/08 19:29:49 pooka 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.40 2007/12/08 19:29:49 pooka 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 <sys/kauth.h> 56 57 #include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 58 59 #include <sys/syscallargs.h> 60 61 /* 62 * Vnode variable naming conventions in this file: 63 * 64 * rvp: the current root we're aiming towards. 65 * lvp, *lvpp: the "lower" vnode 66 * uvp, *uvpp: the "upper" vnode. 67 * 68 * Since all the vnodes we're dealing with are directories, and the 69 * lookups are going *up* in the filesystem rather than *down*, the 70 * usual "pvp" (parent) or "dvp" (directory) naming conventions are 71 * too confusing. 72 */ 73 74 /* 75 * XXX Will infinite loop in certain cases if a directory read reliably 76 * returns EINVAL on last block. 77 * XXX is EINVAL the right thing to return if a directory is malformed? 78 */ 79 80 /* 81 * XXX Untested vs. mount -o union; probably does the wrong thing. 82 */ 83 84 /* 85 * Find parent vnode of *lvpp, return in *uvpp 86 * 87 * If we care about the name, scan it looking for name of directory 88 * entry pointing at lvp. 89 * 90 * Place the name in the buffer which starts at bufp, immediately 91 * before *bpp, and move bpp backwards to point at the start of it. 92 * 93 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 94 * On exit, *uvpp is either NULL or is a locked vnode reference. 95 */ 96 static int 97 getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp, 98 char *bufp, struct lwp *l) 99 { 100 int error = 0; 101 int eofflag; 102 off_t off; 103 int tries; 104 struct uio uio; 105 struct iovec iov; 106 char *dirbuf = NULL; 107 int dirbuflen; 108 ino_t fileno; 109 struct vattr va; 110 struct vnode *uvp = NULL; 111 struct vnode *lvp = *lvpp; 112 kauth_cred_t cred = l->l_cred; 113 struct componentname cn; 114 int len, reclen; 115 tries = 0; 116 117 /* 118 * If we want the filename, get some info we need while the 119 * current directory is still locked. 120 */ 121 if (bufp != NULL) { 122 error = VOP_GETATTR(lvp, &va, cred); 123 if (error) { 124 vput(lvp); 125 *lvpp = NULL; 126 *uvpp = NULL; 127 return error; 128 } 129 } 130 131 /* 132 * Ok, we have to do it the hard way.. 133 * Next, get parent vnode using lookup of .. 134 */ 135 cn.cn_nameiop = LOOKUP; 136 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 137 cn.cn_cred = cred; 138 cn.cn_pnbuf = NULL; 139 cn.cn_nameptr = ".."; 140 cn.cn_namelen = 2; 141 cn.cn_hash = 0; 142 cn.cn_consume = 0; 143 144 /* 145 * At this point, lvp is locked. 146 * On successful return, *uvpp will be locked 147 */ 148 error = VOP_LOOKUP(lvp, uvpp, &cn); 149 vput(lvp); 150 if (error) { 151 *lvpp = NULL; 152 *uvpp = NULL; 153 return error; 154 } 155 uvp = *uvpp; 156 157 /* If we don't care about the pathname, we're done */ 158 if (bufp == NULL) { 159 *lvpp = NULL; 160 return 0; 161 } 162 163 fileno = va.va_fileid; 164 165 dirbuflen = DIRBLKSIZ; 166 if (dirbuflen < va.va_blocksize) 167 dirbuflen = va.va_blocksize; 168 dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 169 170 #if 0 171 unionread: 172 #endif 173 off = 0; 174 do { 175 /* call VOP_READDIR of parent */ 176 iov.iov_base = dirbuf; 177 iov.iov_len = dirbuflen; 178 179 uio.uio_iov = &iov; 180 uio.uio_iovcnt = 1; 181 uio.uio_offset = off; 182 uio.uio_resid = dirbuflen; 183 uio.uio_rw = UIO_READ; 184 UIO_SETUP_SYSSPACE(&uio); 185 186 eofflag = 0; 187 188 error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0); 189 190 off = uio.uio_offset; 191 192 /* 193 * Try again if NFS tosses its cookies. 194 * XXX this can still loop forever if the directory is busted 195 * such that the second or subsequent page of it always 196 * returns EINVAL 197 */ 198 if ((error == EINVAL) && (tries < 3)) { 199 off = 0; 200 tries++; 201 continue; /* once more, with feeling */ 202 } 203 204 if (!error) { 205 char *cpos; 206 struct dirent *dp; 207 208 cpos = dirbuf; 209 tries = 0; 210 211 /* scan directory page looking for matching vnode */ 212 for (len = (dirbuflen - uio.uio_resid); len > 0; 213 len -= reclen) { 214 dp = (struct dirent *) cpos; 215 reclen = dp->d_reclen; 216 217 /* check for malformed directory.. */ 218 if (reclen < _DIRENT_MINSIZE(dp)) { 219 error = EINVAL; 220 goto out; 221 } 222 /* 223 * XXX should perhaps do VOP_LOOKUP to 224 * check that we got back to the right place, 225 * but getting the locking games for that 226 * right would be heinous. 227 */ 228 if ((dp->d_type != DT_WHT) && 229 (dp->d_fileno == fileno)) { 230 char *bp = *bpp; 231 232 bp -= dp->d_namlen; 233 if (bp <= bufp) { 234 error = ERANGE; 235 goto out; 236 } 237 memcpy(bp, dp->d_name, dp->d_namlen); 238 error = 0; 239 *bpp = bp; 240 goto out; 241 } 242 cpos += reclen; 243 } 244 } else 245 goto out; 246 } while (!eofflag); 247 #if 0 248 /* 249 * Deal with mount -o union, which unions only the 250 * root directory of the mount. 251 */ 252 if ((uvp->v_vflag & VV_ROOT) && 253 (uvp->v_mount->mnt_flag & MNT_UNION)) { 254 struct vnode *tvp = uvp; 255 256 uvp = uvp->v_mount->mnt_vnodecovered; 257 vput(tvp); 258 VREF(uvp); 259 *uvpp = uvp; 260 vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY); 261 goto unionread; 262 } 263 #endif 264 error = ENOENT; 265 266 out: 267 *lvpp = NULL; 268 free(dirbuf, M_TEMP); 269 return error; 270 } 271 272 /* 273 * Look in the vnode-to-name reverse cache to see if 274 * we can find things the easy way. 275 * 276 * XXX vget failure path is untested. 277 * 278 * On entry, *lvpp is a locked vnode reference. 279 * On exit, one of the following is the case: 280 * 0) Both *lvpp and *uvpp are NULL and failure is returned. 281 * 1) *uvpp is NULL, *lvpp remains locked and -1 is returned (cache miss) 282 * 2) *uvpp is a locked vnode reference, *lvpp is vput and NULL'ed 283 * and 0 is returned (cache hit) 284 */ 285 286 static int 287 getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp, 288 char *bufp) 289 { 290 struct vnode *lvp, *uvp = NULL; 291 char *obp = *bpp; 292 int error; 293 294 lvp = *lvpp; 295 296 /* 297 * This returns 0 on a cache hit, -1 on a clean cache miss, 298 * or an errno on other failure. 299 */ 300 error = cache_revlookup(lvp, uvpp, bpp, bufp); 301 if (error) { 302 if (error != -1) { 303 vput(lvp); 304 *lvpp = NULL; 305 *uvpp = NULL; 306 } 307 return error; 308 } 309 uvp = *uvpp; 310 311 /* 312 * Since we're going up, we have to release the current lock 313 * before we take the parent lock. 314 */ 315 316 VOP_UNLOCK(lvp, 0); 317 error = vget(uvp, LK_EXCLUSIVE | LK_RETRY); 318 319 /* 320 * Verify that vget succeeded while we were waiting for the 321 * lock. 322 */ 323 if (error) { 324 325 /* 326 * Oops, we missed. If the vget failed, get our lock back 327 * then rewind the `bp' and tell the caller to try things 328 * the hard way. 329 */ 330 *uvpp = NULL; 331 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 332 *bpp = obp; 333 return -1; 334 } 335 vrele(lvp); 336 *lvpp = NULL; 337 338 return error; 339 } 340 341 /* 342 * common routine shared by sys___getcwd() and vn_isunder() 343 */ 344 345 int 346 getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp, 347 int limit, int flags, struct lwp *l) 348 { 349 struct cwdinfo *cwdi = l->l_proc->p_cwdi; 350 kauth_cred_t cred = l->l_cred; 351 struct vnode *uvp = NULL; 352 char *bp = NULL; 353 int error; 354 int perms = VEXEC; 355 356 error = 0; 357 if (rvp == NULL) { 358 rvp = cwdi->cwdi_rdir; 359 if (rvp == NULL) 360 rvp = rootvnode; 361 } 362 363 VREF(rvp); 364 VREF(lvp); 365 366 /* 367 * Error handling invariant: 368 * Before a `goto out': 369 * lvp is either NULL, or locked and held. 370 * uvp is either NULL, or locked and held. 371 */ 372 373 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 374 if (bufp) 375 bp = *bpp; 376 377 /* 378 * this loop will terminate when one of the following happens: 379 * - we hit the root 380 * - getdirentries or lookup fails 381 * - we run out of space in the buffer. 382 */ 383 if (lvp == rvp) { 384 if (bp) 385 *(--bp) = '/'; 386 goto out; 387 } 388 do { 389 /* 390 * access check here is optional, depending on 391 * whether or not caller cares. 392 */ 393 if (flags & GETCWD_CHECK_ACCESS) { 394 error = VOP_ACCESS(lvp, perms, cred); 395 if (error) 396 goto out; 397 perms = VEXEC|VREAD; 398 } 399 400 /* 401 * step up if we're a covered vnode.. 402 */ 403 while (lvp->v_vflag & VV_ROOT) { 404 struct vnode *tvp; 405 406 if (lvp == rvp) 407 goto out; 408 409 tvp = lvp; 410 lvp = lvp->v_mount->mnt_vnodecovered; 411 vput(tvp); 412 /* 413 * hodie natus est radici frater 414 */ 415 if (lvp == NULL) { 416 error = ENOENT; 417 goto out; 418 } 419 VREF(lvp); 420 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 421 if (error != 0) { 422 vrele(lvp); 423 lvp = NULL; 424 goto out; 425 } 426 } 427 /* 428 * Look in the name cache; if that fails, look in the 429 * directory.. 430 */ 431 error = getcwd_getcache(&lvp, &uvp, &bp, bufp); 432 if (error == -1) { 433 if (lvp->v_type != VDIR) { 434 error = ENOTDIR; 435 goto out; 436 } 437 error = getcwd_scandir(&lvp, &uvp, &bp, bufp, l); 438 } 439 if (error) 440 goto out; 441 #if DIAGNOSTIC 442 if (lvp != NULL) 443 panic("getcwd: oops, forgot to null lvp"); 444 if (bufp && (bp <= bufp)) { 445 panic("getcwd: oops, went back too far"); 446 } 447 #endif 448 if (bp) 449 *(--bp) = '/'; 450 lvp = uvp; 451 uvp = NULL; 452 limit--; 453 } while ((lvp != rvp) && (limit > 0)); 454 455 out: 456 if (bpp) 457 *bpp = bp; 458 if (uvp) 459 vput(uvp); 460 if (lvp) 461 vput(lvp); 462 vrele(rvp); 463 return error; 464 } 465 466 /* 467 * Check if one directory can be found inside another in the directory 468 * hierarchy. 469 * 470 * Intended to be used in chroot, chdir, fchdir, etc., to ensure that 471 * chroot() actually means something. 472 */ 473 int 474 vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l) 475 { 476 int error; 477 478 error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l); 479 480 if (!error) 481 return 1; 482 else 483 return 0; 484 } 485 486 /* 487 * Returns true if proc p1's root directory equal to or under p2's 488 * root directory. 489 * 490 * Intended to be used from ptrace/procfs sorts of things. 491 */ 492 493 int 494 proc_isunder(struct proc *p1, struct lwp *l2) 495 { 496 struct vnode *r1 = p1->p_cwdi->cwdi_rdir; 497 struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir; 498 499 if (r1 == NULL) 500 return (r2 == NULL); 501 else if (r2 == NULL) 502 return 1; 503 else 504 return vn_isunder(r1, r2, l2); 505 } 506 507 /* 508 * Find pathname of process's current directory. 509 * 510 * Use vfs vnode-to-name reverse cache; if that fails, fall back 511 * to reading directory contents. 512 */ 513 514 int 515 sys___getcwd(struct lwp *l, void *v, register_t *retval) 516 { 517 struct sys___getcwd_args /* { 518 syscallarg(char *) bufp; 519 syscallarg(size_t) length; 520 } */ *uap = v; 521 522 int error; 523 char *path; 524 char *bp, *bend; 525 int len = SCARG(uap, length); 526 int lenused; 527 struct cwdinfo *cwdi; 528 529 if (len > MAXPATHLEN * 4) 530 len = MAXPATHLEN * 4; 531 else if (len < 2) 532 return ERANGE; 533 534 path = (char *)malloc(len, M_TEMP, M_WAITOK); 535 if (!path) 536 return ENOMEM; 537 538 bp = &path[len]; 539 bend = bp; 540 *(--bp) = '\0'; 541 542 /* 543 * 5th argument here is "max number of vnodes to traverse". 544 * Since each entry takes up at least 2 bytes in the output buffer, 545 * limit it to N/2 vnodes for an N byte buffer. 546 */ 547 cwdi = l->l_proc->p_cwdi; 548 rw_enter(&cwdi->cwdi_lock, RW_READER); 549 error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path, 550 len/2, GETCWD_CHECK_ACCESS, l); 551 rw_exit(&cwdi->cwdi_lock); 552 553 if (error) 554 goto out; 555 lenused = bend - bp; 556 *retval = lenused; 557 /* put the result into user buffer */ 558 error = copyout(bp, SCARG(uap, bufp), lenused); 559 560 out: 561 free(path, M_TEMP); 562 return error; 563 } 564 565 /* 566 * Try to find a pathname for a vnode. Since there is no mapping 567 * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE 568 * option to work (to make cache_revlookup succeed). 569 */ 570 int 571 vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl, 572 struct proc *p) 573 { 574 struct proc *curp = curl->l_proc; 575 int error, lenused, elen; 576 char *bp, *bend; 577 struct vnode *dvp; 578 579 bp = bend = &path[len]; 580 *(--bp) = '\0'; 581 582 error = vget(vp, LK_EXCLUSIVE | LK_RETRY); 583 if (error != 0) 584 return error; 585 error = cache_revlookup(vp, &dvp, &bp, path); 586 vput(vp); 587 if (error != 0) 588 return (error == -1 ? ENOENT : error); 589 590 error = vget(dvp, 0); 591 if (error != 0) 592 return error; 593 *(--bp) = '/'; 594 /* XXX GETCWD_CHECK_ACCESS == 0x0001 */ 595 error = getcwd_common(dvp, NULL, &bp, path, len / 2, 1, curl); 596 597 /* 598 * Strip off emulation path for emulated processes looking at 599 * the maps file of a process of the same emulation. (Won't 600 * work if /emul/xxx is a symlink..) 601 */ 602 if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) { 603 elen = strlen(curp->p_emul->e_path); 604 if (!strncmp(bp, curp->p_emul->e_path, elen)) 605 bp = &bp[elen]; 606 } 607 608 lenused = bend - bp; 609 610 memcpy(path, bp, lenused); 611 path[lenused] = 0; 612 613 return 0; 614 } 615