Annotation of sys/kern/vfs_getcwd.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: vfs_getcwd.c,v 1.12 2007/08/07 07:41:59 thib Exp $ */
! 2: /* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1999 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Bill Sommerfeld.
! 10: *
! 11: * Redistribution and use in source and binary forms, with or without
! 12: * modification, are permitted provided that the following conditions
! 13: * are met:
! 14: * 1. Redistributions of source code must retain the above copyright
! 15: * notice, this list of conditions and the following disclaimer.
! 16: * 2. Redistributions in binary form must reproduce the above copyright
! 17: * notice, this list of conditions and the following disclaimer in the
! 18: * documentation and/or other materials provided with the distribution.
! 19: * 3. All advertising materials mentioning features or use of this software
! 20: * must display the following acknowledgement:
! 21: * This product includes software developed by the NetBSD
! 22: * Foundation, Inc. and its contributors.
! 23: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 24: * contributors may be used to endorse or promote products derived
! 25: * from this software without specific prior written permission.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 37: * POSSIBILITY OF SUCH DAMAGE.
! 38: */
! 39:
! 40: #include <sys/param.h>
! 41: #include <sys/systm.h>
! 42: #include <sys/namei.h>
! 43: #include <sys/filedesc.h>
! 44: #include <sys/kernel.h>
! 45: #include <sys/file.h>
! 46: #include <sys/stat.h>
! 47: #include <sys/vnode.h>
! 48: #include <sys/mount.h>
! 49: #include <sys/proc.h>
! 50: #include <sys/uio.h>
! 51: #include <sys/malloc.h>
! 52: #include <sys/dirent.h>
! 53: #include <ufs/ufs/dir.h> /* only for DIRBLKSIZ */
! 54:
! 55: #include <sys/syscallargs.h>
! 56:
! 57: #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN + 1) + 4)
! 58:
! 59: /* Find parent vnode of *lvpp, return in *uvpp */
! 60: int
! 61: vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
! 62: char *bufp, struct proc *p)
! 63: {
! 64: int eofflag, tries, dirbuflen, len, reclen, error = 0;
! 65: off_t off;
! 66: struct uio uio;
! 67: struct iovec iov;
! 68: char *dirbuf = NULL;
! 69: ino_t fileno;
! 70: struct vattr va;
! 71: struct vnode *uvp = NULL;
! 72: struct vnode *lvp = *lvpp;
! 73: struct componentname cn;
! 74:
! 75: tries = 0;
! 76:
! 77: /*
! 78: * If we want the filename, get some info we need while the
! 79: * current directory is still locked.
! 80: */
! 81: if (bufp != NULL) {
! 82: error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
! 83: if (error) {
! 84: vput(lvp);
! 85: *lvpp = NULL;
! 86: *uvpp = NULL;
! 87: return (error);
! 88: }
! 89: }
! 90:
! 91: cn.cn_nameiop = LOOKUP;
! 92: cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
! 93: cn.cn_proc = p;
! 94: cn.cn_cred = p->p_ucred;
! 95: cn.cn_pnbuf = NULL;
! 96: cn.cn_nameptr = "..";
! 97: cn.cn_namelen = 2;
! 98: cn.cn_hash = 0;
! 99: cn.cn_consume = 0;
! 100:
! 101: /* Get parent vnode using lookup of '..' */
! 102: error = VOP_LOOKUP(lvp, uvpp, &cn);
! 103: if (error) {
! 104: vput(lvp);
! 105: *lvpp = NULL;
! 106: *uvpp = NULL;
! 107: return (error);
! 108: }
! 109:
! 110: uvp = *uvpp;
! 111:
! 112: /* If we don't care about the pathname, we're done */
! 113: if (bufp == NULL) {
! 114: vrele(lvp);
! 115: *lvpp = NULL;
! 116: return (0);
! 117: }
! 118:
! 119: fileno = va.va_fileid;
! 120:
! 121: dirbuflen = DIRBLKSIZ;
! 122:
! 123: if (dirbuflen < va.va_blocksize)
! 124: dirbuflen = va.va_blocksize;
! 125:
! 126: dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
! 127:
! 128: off = 0;
! 129:
! 130: do {
! 131: char *cpos;
! 132: struct dirent *dp;
! 133:
! 134: iov.iov_base = dirbuf;
! 135: iov.iov_len = dirbuflen;
! 136:
! 137: uio.uio_iov = &iov;
! 138: uio.uio_iovcnt = 1;
! 139: uio.uio_offset = off;
! 140: uio.uio_resid = dirbuflen;
! 141: uio.uio_segflg = UIO_SYSSPACE;
! 142: uio.uio_rw = UIO_READ;
! 143: uio.uio_procp = p;
! 144:
! 145: eofflag = 0;
! 146:
! 147: /* Call VOP_READDIR of parent */
! 148: error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag, 0, 0);
! 149:
! 150: off = uio.uio_offset;
! 151:
! 152: /* Try again if NFS tosses its cookies */
! 153: if (error == EINVAL && tries < 3) {
! 154: tries++;
! 155: off = 0;
! 156: continue;
! 157: } else if (error) {
! 158: goto out; /* Old userland getcwd() behaviour */
! 159: }
! 160:
! 161: cpos = dirbuf;
! 162: tries = 0;
! 163:
! 164: /* Scan directory page looking for matching vnode */
! 165: for (len = (dirbuflen - uio.uio_resid); len > 0;
! 166: len -= reclen) {
! 167: dp = (struct dirent *)cpos;
! 168: reclen = dp->d_reclen;
! 169:
! 170: /* Check for malformed directory */
! 171: if (reclen < DIRENT_MINSIZE) {
! 172: error = EINVAL;
! 173: goto out;
! 174: }
! 175:
! 176: if (dp->d_fileno == fileno) {
! 177: char *bp = *bpp;
! 178: bp -= dp->d_namlen;
! 179:
! 180: if (bp <= bufp) {
! 181: error = ERANGE;
! 182: goto out;
! 183: }
! 184:
! 185: bcopy(dp->d_name, bp, dp->d_namlen);
! 186: error = 0;
! 187: *bpp = bp;
! 188:
! 189: goto out;
! 190: }
! 191:
! 192: cpos += reclen;
! 193: }
! 194:
! 195: } while (!eofflag);
! 196:
! 197: error = ENOENT;
! 198:
! 199: out:
! 200:
! 201: vrele(lvp);
! 202: *lvpp = NULL;
! 203:
! 204: free(dirbuf, M_TEMP);
! 205:
! 206: return (error);
! 207: }
! 208:
! 209: /* Do a lookup in the vnode-to-name reverse */
! 210: int
! 211: vfs_getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
! 212: char *bufp)
! 213: {
! 214: struct vnode *lvp, *uvp = NULL;
! 215: struct proc *p = curproc;
! 216: char *obp;
! 217: int error, vpid;
! 218:
! 219: lvp = *lvpp;
! 220: obp = *bpp; /* Save orginal position to restore to on error */
! 221:
! 222: error = cache_revlookup(lvp, uvpp, bpp, bufp);
! 223: if (error) {
! 224: if (error != -1) {
! 225: vput(lvp);
! 226: *lvpp = NULL;
! 227: *uvpp = NULL;
! 228: }
! 229:
! 230: return (error);
! 231: }
! 232:
! 233: uvp = *uvpp;
! 234: vpid = uvp->v_id;
! 235:
! 236:
! 237: /* Release current lock before acquiring the parent lock */
! 238: VOP_UNLOCK(lvp, 0, p);
! 239:
! 240: error = vget(uvp, LK_EXCLUSIVE | LK_RETRY, p);
! 241: if (error)
! 242: *uvpp = NULL;
! 243:
! 244: /*
! 245: * Verify that vget() succeeded, and check that vnode capability
! 246: * didn't change while we were waiting for the lock.
! 247: */
! 248: if (error || (vpid != uvp->v_id)) {
! 249: /*
! 250: * Try to get our lock back. If that works, tell the caller to
! 251: * try things the hard way, otherwise give up.
! 252: */
! 253: if (!error)
! 254: vput(uvp);
! 255:
! 256: *uvpp = NULL;
! 257:
! 258: error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
! 259: if (!error) {
! 260: *bpp = obp; /* restore the buffer */
! 261: return (-1);
! 262: }
! 263: }
! 264:
! 265: vrele(lvp);
! 266: *lvpp = NULL;
! 267:
! 268: return (error);
! 269: }
! 270:
! 271: #define GETCWD_CHECK_ACCESS 0x0001
! 272:
! 273: /* Common routine shared by sys___getcwd() and vn_isunder() */
! 274: int
! 275: vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
! 276: int limit, int flags, struct proc *p)
! 277: {
! 278: struct filedesc *fdp = p->p_fd;
! 279: struct vnode *uvp = NULL;
! 280: char *bp = NULL;
! 281: int error, perms = VEXEC;
! 282:
! 283: if (rvp == NULL) {
! 284: rvp = fdp->fd_rdir;
! 285: if (rvp == NULL)
! 286: rvp = rootvnode;
! 287: }
! 288:
! 289: VREF(rvp);
! 290: VREF(lvp);
! 291:
! 292: error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
! 293: if (error) {
! 294: vrele(lvp);
! 295: lvp = NULL;
! 296: goto out;
! 297: }
! 298:
! 299: if (bufp)
! 300: bp = *bpp;
! 301:
! 302: if (lvp == rvp) {
! 303: if (bp)
! 304: *(--bp) = '/';
! 305: goto out;
! 306: }
! 307:
! 308: /*
! 309: * This loop will terminate when we hit the root, VOP_READDIR() or
! 310: * VOP_LOOKUP() fails, or we run out of space in the user buffer.
! 311: */
! 312: do {
! 313: if (lvp->v_type != VDIR) {
! 314: error = ENOTDIR;
! 315: goto out;
! 316: }
! 317:
! 318: /* Check for access if caller cares */
! 319: if (flags & GETCWD_CHECK_ACCESS) {
! 320: error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
! 321: if (error)
! 322: goto out;
! 323: perms = VEXEC|VREAD;
! 324: }
! 325:
! 326: /* Step up if we're a covered vnode */
! 327: while (lvp->v_flag & VROOT) {
! 328: struct vnode *tvp;
! 329:
! 330: if (lvp == rvp)
! 331: goto out;
! 332:
! 333: tvp = lvp;
! 334: lvp = lvp->v_mount->mnt_vnodecovered;
! 335:
! 336: vput(tvp);
! 337:
! 338: if (lvp == NULL) {
! 339: error = ENOENT;
! 340: goto out;
! 341: }
! 342:
! 343: VREF(lvp);
! 344:
! 345: error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
! 346: if (error) {
! 347: vrele(lvp);
! 348: lvp = NULL;
! 349: goto out;
! 350: }
! 351: }
! 352:
! 353: /* Look in the name cache */
! 354: error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
! 355:
! 356: if (error == -1) {
! 357: /* If that fails, look in the directory */
! 358: error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
! 359: }
! 360:
! 361: if (error)
! 362: goto out;
! 363:
! 364: #ifdef DIAGNOSTIC
! 365: if (lvp != NULL)
! 366: panic("getcwd: oops, forgot to null lvp");
! 367: if (bufp && (bp <= bufp)) {
! 368: panic("getcwd: oops, went back too far");
! 369: }
! 370: #endif
! 371:
! 372: if (bp)
! 373: *(--bp) = '/';
! 374:
! 375: lvp = uvp;
! 376: uvp = NULL;
! 377: limit--;
! 378:
! 379: } while ((lvp != rvp) && (limit > 0));
! 380:
! 381: out:
! 382:
! 383: if (bpp)
! 384: *bpp = bp;
! 385:
! 386: if (uvp)
! 387: vput(uvp);
! 388:
! 389: if (lvp)
! 390: vput(lvp);
! 391:
! 392: vrele(rvp);
! 393:
! 394: return (error);
! 395: }
! 396:
! 397: /* True if p1's root directory is equal to or under p2's root directory */
! 398: int
! 399: proc_isunder(struct proc *p1, struct proc *p2)
! 400: {
! 401: struct vnode *r1 = p1->p_fd->fd_rdir;
! 402: struct vnode *r2 = p2->p_fd->fd_rdir;
! 403:
! 404: if (r1 == NULL)
! 405: return (r2 == NULL);
! 406:
! 407: if (r2 == NULL)
! 408: return (1);
! 409:
! 410: return (vn_isunder(r1, r2, p2));
! 411: }
! 412:
! 413: /* Find pathname of a process's current directory */
! 414: int
! 415: sys___getcwd(struct proc *p, void *v, register_t *retval)
! 416: {
! 417: struct sys___getcwd_args *uap = v;
! 418: int error, lenused, len = SCARG(uap, len);
! 419: char *path, *bp, *bend;
! 420:
! 421: if (len > MAXPATHLEN * 4)
! 422: len = MAXPATHLEN * 4;
! 423: else if (len < 2)
! 424: return (ERANGE);
! 425:
! 426: path = malloc(len, M_TEMP, M_WAITOK);
! 427:
! 428: bp = &path[len];
! 429: bend = bp;
! 430: *(--bp) = '\0';
! 431:
! 432: /*
! 433: * 5th argument here is "max number of vnodes to traverse".
! 434: * Since each entry takes up at least 2 bytes in the output
! 435: * buffer, limit it to N/2 vnodes for an N byte buffer.
! 436: */
! 437: error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
! 438: GETCWD_CHECK_ACCESS, p);
! 439:
! 440: if (error)
! 441: goto out;
! 442:
! 443: lenused = bend - bp;
! 444: *retval = lenused;
! 445:
! 446: /* Put the result into user buffer */
! 447: error = copyout(bp, SCARG(uap, buf), lenused);
! 448:
! 449: out:
! 450: free(path, M_TEMP);
! 451:
! 452: return (error);
! 453: }
CVSweb