[BACK]Return to vfs_getcwd.c CVS log [TXT][DIR] Up to [local] / sys / kern

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