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