[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

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