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