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