[BACK]Return to vfs_lookup.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

Annotation of src/sys/kern/vfs_lookup.c, Revision 1.135

1.135   ! dholland    1: /*     $NetBSD: vfs_lookup.c,v 1.134 2011/04/11 01:35:00 dholland Exp $        */
1.13      cgd         2:
1.10      cgd         3: /*
1.12      mycroft     4:  * Copyright (c) 1982, 1986, 1989, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
1.10      cgd         6:  * (c) UNIX System Laboratories, Inc.
                      7:  * All or some portions of this file are derived from material licensed
                      8:  * to the University of California by American Telephone and Telegraph
                      9:  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
                     10:  * the permission of UNIX System Laboratories, Inc.
                     11:  *
                     12:  * Redistribution and use in source and binary forms, with or without
                     13:  * modification, are permitted provided that the following conditions
                     14:  * are met:
                     15:  * 1. Redistributions of source code must retain the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer.
                     17:  * 2. Redistributions in binary form must reproduce the above copyright
                     18:  *    notice, this list of conditions and the following disclaimer in the
                     19:  *    documentation and/or other materials provided with the distribution.
1.49      agc        20:  * 3. Neither the name of the University nor the names of its contributors
1.10      cgd        21:  *    may be used to endorse or promote products derived from this software
                     22:  *    without specific prior written permission.
                     23:  *
                     24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     34:  * SUCH DAMAGE.
                     35:  *
1.26      fvdl       36:  *     @(#)vfs_lookup.c        8.10 (Berkeley) 5/27/95
1.10      cgd        37:  */
1.38      lukem      38:
                     39: #include <sys/cdefs.h>
1.135   ! dholland   40: __KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.134 2011/04/11 01:35:00 dholland Exp $");
1.27      thorpej    41:
1.67      chs        42: #include "opt_magiclinks.h"
1.10      cgd        43:
                     44: #include <sys/param.h>
1.15      cgd        45: #include <sys/systm.h>
1.61      thorpej    46: #include <sys/kernel.h>
1.10      cgd        47: #include <sys/syslimits.h>
                     48: #include <sys/time.h>
                     49: #include <sys/namei.h>
                     50: #include <sys/vnode.h>
                     51: #include <sys/mount.h>
                     52: #include <sys/errno.h>
1.39      lukem      53: #include <sys/filedesc.h>
                     54: #include <sys/hash.h>
1.10      cgd        55: #include <sys/proc.h>
1.40      wrstuden   56: #include <sys/syslog.h>
1.70      elad       57: #include <sys/kauth.h>
1.97      ad         58: #include <sys/ktrace.h>
1.12      mycroft    59:
1.67      chs        60: #ifndef MAGICLINKS
                     61: #define MAGICLINKS 0
                     62: #endif
                     63:
                     64: int vfs_magiclinks = MAGICLINKS;
                     65:
1.10      cgd        66: /*
1.61      thorpej    67:  * Substitute replacement text for 'magic' strings in symlinks.
                     68:  * Returns 0 if successful, and returns non-zero if an error
                     69:  * occurs.  (Currently, the only possible error is running out
                     70:  * of temporary pathname space.)
                     71:  *
                     72:  * Looks for "@<string>" and "@<string>/", where <string> is a
                     73:  * recognized 'magic' string.  Replaces the "@<string>" with the
                     74:  * appropriate replacement text.  (Note that in some cases the
                     75:  * replacement text may have zero length.)
                     76:  *
                     77:  * This would have been table driven, but the variance in
                     78:  * replacement strings (and replacement string lengths) made
                     79:  * that impractical.
                     80:  */
1.63      thorpej    81: #define        VNL(x)                                                  \
                     82:        (sizeof(x) - 1)
                     83:
                     84: #define        VO      '{'
                     85: #define        VC      '}'
                     86:
1.61      thorpej    87: #define        MATCH(str)                                              \
1.63      thorpej    88:        ((termchar == '/' && i + VNL(str) == *len) ||           \
                     89:         (i + VNL(str) < *len &&                                \
                     90:          cp[i + VNL(str)] == termchar)) &&                     \
                     91:        !strncmp((str), &cp[i], VNL(str))
1.61      thorpej    92:
                     93: #define        SUBSTITUTE(m, s, sl)                                    \
1.115     christos   94:        if ((newlen + (sl)) >= MAXPATHLEN)                      \
                     95:                return 1;                                       \
1.63      thorpej    96:        i += VNL(m);                                            \
                     97:        if (termchar != '/')                                    \
                     98:                i++;                                            \
1.115     christos   99:        (void)memcpy(&tmp[newlen], (s), (sl));                  \
1.63      thorpej   100:        newlen += (sl);                                         \
                    101:        change = 1;                                             \
                    102:        termchar = '/';
1.61      thorpej   103:
                    104: static int
1.115     christos  105: symlink_magic(struct proc *p, char *cp, size_t *len)
1.61      thorpej   106: {
1.66      yamt      107:        char *tmp;
1.115     christos  108:        size_t change, i, newlen, slen;
                    109:        char termchar = '/';
                    110:        char idtmp[11]; /* enough for 32 bit *unsigned* integer */
1.101     mjf       111:
1.61      thorpej   112:
1.66      yamt      113:        tmp = PNBUF_GET();
1.61      thorpej   114:        for (change = i = newlen = 0; i < *len; ) {
1.63      thorpej   115:                if (cp[i] != '@') {
1.61      thorpej   116:                        tmp[newlen++] = cp[i++];
1.63      thorpej   117:                        continue;
                    118:                }
                    119:
                    120:                i++;
                    121:
                    122:                /* Check for @{var} syntax. */
                    123:                if (cp[i] == VO) {
                    124:                        termchar = VC;
1.61      thorpej   125:                        i++;
1.63      thorpej   126:                }
                    127:
                    128:                /*
                    129:                 * The following checks should be ordered according
                    130:                 * to frequency of use.
                    131:                 */
                    132:                if (MATCH("machine_arch")) {
1.115     christos  133:                        slen = VNL(MACHINE_ARCH);
                    134:                        SUBSTITUTE("machine_arch", MACHINE_ARCH, slen);
1.63      thorpej   135:                } else if (MATCH("machine")) {
1.115     christos  136:                        slen = VNL(MACHINE);
                    137:                        SUBSTITUTE("machine", MACHINE, slen);
1.63      thorpej   138:                } else if (MATCH("hostname")) {
1.115     christos  139:                        SUBSTITUTE("hostname", hostname, hostnamelen);
1.63      thorpej   140:                } else if (MATCH("osrelease")) {
1.115     christos  141:                        slen = strlen(osrelease);
                    142:                        SUBSTITUTE("osrelease", osrelease, slen);
1.63      thorpej   143:                } else if (MATCH("emul")) {
1.115     christos  144:                        slen = strlen(p->p_emul->e_name);
                    145:                        SUBSTITUTE("emul", p->p_emul->e_name, slen);
1.63      thorpej   146:                } else if (MATCH("kernel_ident")) {
1.115     christos  147:                        slen = strlen(kernel_ident);
                    148:                        SUBSTITUTE("kernel_ident", kernel_ident, slen);
1.63      thorpej   149:                } else if (MATCH("domainname")) {
1.115     christos  150:                        SUBSTITUTE("domainname", domainname, domainnamelen);
1.63      thorpej   151:                } else if (MATCH("ostype")) {
1.115     christos  152:                        slen = strlen(ostype);
                    153:                        SUBSTITUTE("ostype", ostype, slen);
1.72      elad      154:                } else if (MATCH("uid")) {
1.115     christos  155:                        slen = snprintf(idtmp, sizeof(idtmp), "%u",
1.72      elad      156:                            kauth_cred_geteuid(kauth_cred_get()));
1.115     christos  157:                        SUBSTITUTE("uid", idtmp, slen);
1.101     mjf       158:                } else if (MATCH("ruid")) {
1.115     christos  159:                        slen = snprintf(idtmp, sizeof(idtmp), "%u",
1.101     mjf       160:                            kauth_cred_getuid(kauth_cred_get()));
1.115     christos  161:                        SUBSTITUTE("ruid", idtmp, slen);
                    162:                } else if (MATCH("gid")) {
                    163:                        slen = snprintf(idtmp, sizeof(idtmp), "%u",
                    164:                            kauth_cred_getegid(kauth_cred_get()));
                    165:                        SUBSTITUTE("gid", idtmp, slen);
                    166:                } else if (MATCH("rgid")) {
                    167:                        slen = snprintf(idtmp, sizeof(idtmp), "%u",
                    168:                            kauth_cred_getgid(kauth_cred_get()));
                    169:                        SUBSTITUTE("rgid", idtmp, slen);
1.63      thorpej   170:                } else {
                    171:                        tmp[newlen++] = '@';
                    172:                        if (termchar == VC)
                    173:                                tmp[newlen++] = VO;
1.61      thorpej   174:                }
                    175:        }
                    176:
1.66      yamt      177:        if (change) {
1.115     christos  178:                (void)memcpy(cp, tmp, newlen);
1.66      yamt      179:                *len = newlen;
                    180:        }
                    181:        PNBUF_PUT(tmp);
1.61      thorpej   182:
1.115     christos  183:        return 0;
1.61      thorpej   184: }
                    185:
1.63      thorpej   186: #undef VNL
                    187: #undef VO
                    188: #undef VC
                    189: #undef MATCH
                    190: #undef SUBSTITUTE
                    191:
1.123     dholland  192: ////////////////////////////////////////////////////////////
                    193:
                    194: /*
1.131     dholland  195:  * Determine the namei hash (for cn_hash) for name.
                    196:  * If *ep != NULL, hash from name to ep-1.
                    197:  * If *ep == NULL, hash from name until the first NUL or '/', and
                    198:  * return the location of this termination character in *ep.
                    199:  *
                    200:  * This function returns an equivalent hash to the MI hash32_strn().
                    201:  * The latter isn't used because in the *ep == NULL case, determining
                    202:  * the length of the string to the first NUL or `/' and then calling
                    203:  * hash32_strn() involves unnecessary double-handling of the data.
                    204:  */
                    205: uint32_t
                    206: namei_hash(const char *name, const char **ep)
                    207: {
                    208:        uint32_t        hash;
                    209:
                    210:        hash = HASH32_STR_INIT;
                    211:        if (*ep != NULL) {
                    212:                for (; name < *ep; name++)
                    213:                        hash = hash * 33 + *(const uint8_t *)name;
                    214:        } else {
                    215:                for (; *name != '\0' && *name != '/'; name++)
                    216:                        hash = hash * 33 + *(const uint8_t *)name;
                    217:                *ep = name;
                    218:        }
                    219:        return (hash + (hash >> 5));
                    220: }
                    221:
                    222: ////////////////////////////////////////////////////////////
                    223:
                    224: /*
1.123     dholland  225:  * Sealed abstraction for pathnames.
                    226:  *
                    227:  * System-call-layer level code that is going to call namei should
                    228:  * first create a pathbuf and adjust all the bells and whistles on it
                    229:  * as needed by context
                    230:  */
                    231:
                    232: struct pathbuf {
                    233:        char *pb_path;
                    234:        char *pb_pathcopy;
                    235:        unsigned pb_pathcopyuses;
                    236: };
                    237:
                    238: static struct pathbuf *
                    239: pathbuf_create_raw(void)
                    240: {
                    241:        struct pathbuf *pb;
                    242:
                    243:        pb = kmem_alloc(sizeof(*pb), KM_SLEEP);
                    244:        if (pb == NULL) {
                    245:                return NULL;
                    246:        }
                    247:        pb->pb_path = PNBUF_GET();
                    248:        if (pb->pb_path == NULL) {
                    249:                kmem_free(pb, sizeof(*pb));
                    250:                return NULL;
                    251:        }
                    252:        pb->pb_pathcopy = NULL;
                    253:        pb->pb_pathcopyuses = 0;
                    254:        return pb;
                    255: }
                    256:
                    257: void
                    258: pathbuf_destroy(struct pathbuf *pb)
                    259: {
                    260:        KASSERT(pb->pb_pathcopyuses == 0);
                    261:        KASSERT(pb->pb_pathcopy == NULL);
                    262:        PNBUF_PUT(pb->pb_path);
                    263:        kmem_free(pb, sizeof(*pb));
                    264: }
                    265:
                    266: struct pathbuf *
1.124     dholland  267: pathbuf_assimilate(char *pnbuf)
                    268: {
                    269:        struct pathbuf *pb;
                    270:
                    271:        pb = kmem_alloc(sizeof(*pb), KM_SLEEP);
                    272:        if (pb == NULL) {
                    273:                return NULL;
                    274:        }
                    275:        pb->pb_path = pnbuf;
                    276:        pb->pb_pathcopy = NULL;
                    277:        pb->pb_pathcopyuses = 0;
                    278:        return pb;
                    279: }
                    280:
                    281: struct pathbuf *
1.123     dholland  282: pathbuf_create(const char *path)
                    283: {
                    284:        struct pathbuf *pb;
                    285:        int error;
                    286:
                    287:        pb = pathbuf_create_raw();
                    288:        if (pb == NULL) {
                    289:                return NULL;
                    290:        }
                    291:        error = copystr(path, pb->pb_path, PATH_MAX, NULL);
                    292:        if (error != 0) {
                    293:                KASSERT(!"kernel path too long in pathbuf_create");
                    294:                /* make sure it's null-terminated, just in case */
                    295:                pb->pb_path[PATH_MAX-1] = '\0';
                    296:        }
                    297:        return pb;
                    298: }
                    299:
                    300: int
                    301: pathbuf_copyin(const char *userpath, struct pathbuf **ret)
                    302: {
                    303:        struct pathbuf *pb;
                    304:        int error;
                    305:
                    306:        pb = pathbuf_create_raw();
                    307:        if (pb == NULL) {
                    308:                return ENOMEM;
                    309:        }
                    310:        error = copyinstr(userpath, pb->pb_path, PATH_MAX, NULL);
                    311:        if (error) {
                    312:                pathbuf_destroy(pb);
                    313:                return error;
                    314:        }
                    315:        *ret = pb;
                    316:        return 0;
                    317: }
                    318:
                    319: /*
                    320:  * XXX should not exist
                    321:  */
                    322: int
                    323: pathbuf_maybe_copyin(const char *path, enum uio_seg seg, struct pathbuf **ret)
                    324: {
                    325:        if (seg == UIO_USERSPACE) {
                    326:                return pathbuf_copyin(path, ret);
                    327:        } else {
                    328:                *ret = pathbuf_create(path);
                    329:                if (*ret == NULL) {
                    330:                        return ENOMEM;
                    331:                }
                    332:                return 0;
                    333:        }
                    334: }
                    335:
                    336: /*
                    337:  * Get a copy of the path buffer as it currently exists. If this is
                    338:  * called after namei starts the results may be arbitrary.
                    339:  */
                    340: void
                    341: pathbuf_copystring(const struct pathbuf *pb, char *buf, size_t maxlen)
                    342: {
                    343:        strlcpy(buf, pb->pb_path, maxlen);
                    344: }
                    345:
                    346: /*
                    347:  * These two functions allow access to a saved copy of the original
                    348:  * path string. The first copy should be gotten before namei is
                    349:  * called. Each copy that is gotten should be put back.
                    350:  */
                    351:
                    352: const char *
                    353: pathbuf_stringcopy_get(struct pathbuf *pb)
                    354: {
                    355:        if (pb->pb_pathcopyuses == 0) {
                    356:                pb->pb_pathcopy = PNBUF_GET();
                    357:                strcpy(pb->pb_pathcopy, pb->pb_path);
                    358:        }
                    359:        pb->pb_pathcopyuses++;
                    360:        return pb->pb_pathcopy;
                    361: }
                    362:
                    363: void
                    364: pathbuf_stringcopy_put(struct pathbuf *pb, const char *str)
                    365: {
                    366:        KASSERT(str == pb->pb_pathcopy);
                    367:        KASSERT(pb->pb_pathcopyuses > 0);
                    368:        pb->pb_pathcopyuses--;
                    369:        if (pb->pb_pathcopyuses == 0) {
                    370:                PNBUF_PUT(pb->pb_pathcopy);
                    371:                pb->pb_pathcopy = NULL;
                    372:        }
                    373: }
                    374:
                    375:
                    376: ////////////////////////////////////////////////////////////
                    377:
1.61      thorpej   378: /*
1.69      rumble    379:  * Convert a pathname into a pointer to a locked vnode.
1.10      cgd       380:  *
                    381:  * The FOLLOW flag is set when symbolic links are to be followed
                    382:  * when they occur at the end of the name translation process.
                    383:  * Symbolic links are always followed for all other pathname
                    384:  * components other than the last.
                    385:  *
                    386:  * The segflg defines whether the name is to be copied from user
                    387:  * space or kernel space.
                    388:  *
                    389:  * Overall outline of namei:
                    390:  *
                    391:  *     copy in name
                    392:  *     get starting directory
                    393:  *     while (!done && !error) {
                    394:  *             call lookup to search path.
                    395:  *             if symbolic link, massage name in buffer and continue
                    396:  *     }
                    397:  */
1.117     dholland  398:
                    399: /*
                    400:  * Internal state for a namei operation.
                    401:  */
                    402: struct namei_state {
                    403:        struct nameidata *ndp;
                    404:        struct componentname *cnp;
                    405:
1.118     dholland  406:        /* used by the pieces of namei */
                    407:        struct vnode *namei_startdir; /* The directory namei() starts from. */
                    408:
                    409:        /* used by the pieces of lookup */
                    410:        int lookup_alldone;
                    411:
                    412:        int docache;                    /* == 0 do not cache last component */
                    413:        int rdonly;                     /* lookup read-only flag bit */
1.117     dholland  414:        struct vnode *dp;               /* the directory we are searching */
1.118     dholland  415:        int slashes;
1.117     dholland  416: };
                    417:
1.118     dholland  418:
1.117     dholland  419: /*
                    420:  * Initialize the namei working state.
                    421:  */
                    422: static void
                    423: namei_init(struct namei_state *state, struct nameidata *ndp)
                    424: {
                    425:        state->ndp = ndp;
                    426:        state->cnp = &ndp->ni_cnd;
1.129     dholland  427:        KASSERT((state->cnp->cn_flags & INRELOOKUP) == 0);
1.117     dholland  428:
1.118     dholland  429:        state->namei_startdir = NULL;
                    430:
                    431:        state->lookup_alldone = 0;
                    432:
                    433:        state->docache = 0;
                    434:        state->rdonly = 0;
1.117     dholland  435:        state->dp = NULL;
1.118     dholland  436:        state->slashes = 0;
1.133     dholland  437:
                    438: #ifdef DIAGNOSTIC
                    439:        if (!state->cnp->cn_cred)
                    440:                panic("namei: bad cred/proc");
                    441:        if (state->cnp->cn_nameiop & (~OPMASK))
                    442:                panic("namei: nameiop contaminated with flags");
                    443:        if (state->cnp->cn_flags & OPMASK)
                    444:                panic("namei: flags contaminated with nameiops");
                    445: #endif
                    446:
                    447:        /*
                    448:         * The buffer for name translation shall be the one inside the
                    449:         * pathbuf.
                    450:         */
                    451:        state->ndp->ni_pnbuf = state->ndp->ni_pathbuf->pb_path;
1.117     dholland  452: }
                    453:
                    454: /*
                    455:  * Clean up the working namei state, leaving things ready for return
                    456:  * from namei.
                    457:  */
                    458: static void
                    459: namei_cleanup(struct namei_state *state)
                    460: {
                    461:        KASSERT(state->cnp == &state->ndp->ni_cnd);
                    462:
1.118     dholland  463:        //KASSERT(state->namei_startdir == NULL);       // not yet
1.117     dholland  464:
                    465:        /* nothing for now */
                    466:        (void)state;
                    467: }
                    468:
                    469: //////////////////////////////
                    470:
                    471: /*
1.133     dholland  472:  * Get the directory context.
                    473:  * Initializes the rootdir and erootdir state and returns a reference
                    474:  * to the starting dir.
1.117     dholland  475:  */
1.133     dholland  476: static struct vnode *
                    477: namei_getstartdir(struct namei_state *state)
1.117     dholland  478: {
                    479:        struct nameidata *ndp = state->ndp;
                    480:        struct componentname *cnp = state->cnp;
                    481:        struct cwdinfo *cwdi;           /* pointer to cwd state */
                    482:        struct lwp *self = curlwp;      /* thread doing namei() */
1.133     dholland  483:        struct vnode *rootdir, *erootdir, *curdir, *startdir;
1.117     dholland  484:
1.133     dholland  485:        cwdi = self->l_proc->p_cwdi;
                    486:        rw_enter(&cwdi->cwdi_lock, RW_READER);
1.21      kleink    487:
1.133     dholland  488:        /* root dir */
                    489:        if (cwdi->cwdi_rdir == NULL || (cnp->cn_flags & NOCHROOT)) {
                    490:                rootdir = rootvnode;
                    491:        } else {
                    492:                rootdir = cwdi->cwdi_rdir;
1.10      cgd       493:        }
1.123     dholland  494:
1.133     dholland  495:        /* emulation root dir, if any */
                    496:        if ((cnp->cn_flags & TRYEMULROOT) == 0) {
                    497:                /* if we don't want it, don't fetch it */
                    498:                erootdir = NULL;
                    499:        } else if (cnp->cn_flags & EMULROOTSET) {
                    500:                /* explicitly set emulroot; "/../" doesn't override this */
                    501:                erootdir = ndp->ni_erootdir;
                    502:        } else if (!strncmp(ndp->ni_pnbuf, "/../", 4)) {
                    503:                /* explicit reference to real rootdir */
                    504:                erootdir = NULL;
                    505:        } else {
                    506:                /* may be null */
                    507:                erootdir = cwdi->cwdi_edir;
                    508:        }
1.21      kleink    509:
1.133     dholland  510:        /* current dir */
                    511:        curdir = cwdi->cwdi_cdir;
1.85      dsl       512:
1.133     dholland  513:        if (ndp->ni_pnbuf[0] != '/') {
                    514:                startdir = curdir;
                    515:                erootdir = NULL;
                    516:        } else if (cnp->cn_flags & TRYEMULROOT && erootdir != NULL) {
                    517:                startdir = erootdir;
1.23      mycroft   518:        } else {
1.133     dholland  519:                startdir = rootdir;
                    520:                erootdir = NULL;
1.23      mycroft   521:        }
1.133     dholland  522:
                    523:        state->ndp->ni_rootdir = rootdir;
                    524:        state->ndp->ni_erootdir = erootdir;
1.117     dholland  525:
                    526:        /*
1.133     dholland  527:         * Get a reference to the start dir so we can safely unlock cwdi.
                    528:         *
                    529:         * XXX: should we hold references to rootdir and erootdir while
                    530:         * we're running? What happens if a multithreaded process chroots
                    531:         * during namei?
1.117     dholland  532:         */
1.133     dholland  533:        vref(startdir);
                    534:
                    535:        rw_exit(&cwdi->cwdi_lock);
                    536:        return startdir;
                    537: }
                    538:
                    539: /*
                    540:  * Get the directory context for the nfsd case, in parallel to
                    541:  * getstartdir. Initializes the rootdir and erootdir state and
                    542:  * returns a reference to the passed-instarting dir.
                    543:  */
                    544: static struct vnode *
                    545: namei_getstartdir_for_nfsd(struct namei_state *state, struct vnode *startdir)
                    546: {
                    547:        /* always use the real root, and never set an emulation root */
                    548:        state->ndp->ni_rootdir = rootvnode;
                    549:        state->ndp->ni_erootdir = NULL;
                    550:
                    551:        vref(startdir);
                    552:        return startdir;
                    553: }
                    554:
                    555:
                    556: /*
                    557:  * Ktrace the namei operation.
                    558:  */
                    559: static void
                    560: namei_ktrace(struct namei_state *state)
                    561: {
                    562:        struct nameidata *ndp = state->ndp;
                    563:        struct componentname *cnp = state->cnp;
                    564:        struct lwp *self = curlwp;      /* thread doing namei() */
                    565:        const char *emul_path;
                    566:
1.97      ad        567:        if (ktrpoint(KTR_NAMEI)) {
1.90      dsl       568:                if (ndp->ni_erootdir != NULL) {
1.89      dsl       569:                        /*
                    570:                         * To make any sense, the trace entry need to have the
                    571:                         * text of the emulation path prepended.
                    572:                         * Usually we can get this from the current process,
                    573:                         * but when called from emul_find_interp() it is only
                    574:                         * in the exec_package - so we get it passed in ni_next
                    575:                         * (this is a hack).
                    576:                         */
1.88      dsl       577:                        if (cnp->cn_flags & EMULROOTSET)
1.89      dsl       578:                                emul_path = ndp->ni_next;
1.88      dsl       579:                        else
1.117     dholland  580:                                emul_path = self->l_proc->p_emul->e_path;
1.97      ad        581:                        ktrnamei2(emul_path, strlen(emul_path),
1.124     dholland  582:                            ndp->ni_pnbuf, ndp->ni_pathlen);
1.88      dsl       583:                } else
1.124     dholland  584:                        ktrnamei(ndp->ni_pnbuf, ndp->ni_pathlen);
1.88      dsl       585:        }
1.133     dholland  586: }
                    587:
                    588: /*
                    589:  * Start up namei. Copy the path, find the root dir and cwd, establish
                    590:  * the starting directory for lookup, and lock it. Also calls ktrace when
                    591:  * appropriate.
                    592:  */
                    593: static int
1.134     dholland  594: namei_start(struct namei_state *state, struct vnode *forcecwd)
1.133     dholland  595: {
                    596:        struct nameidata *ndp = state->ndp;
                    597:
                    598:        /* length includes null terminator (was originally from copyinstr) */
                    599:        ndp->ni_pathlen = strlen(ndp->ni_pnbuf) + 1;
                    600:
                    601:        /*
                    602:         * POSIX.1 requirement: "" is not a valid file name.
                    603:         */
                    604:        if (ndp->ni_pathlen == 1) {
                    605:                ndp->ni_vp = NULL;
                    606:                return ENOENT;
                    607:        }
                    608:
                    609:        ndp->ni_loopcnt = 0;
                    610:
                    611:        /* Get starting directory, set up root, and ktrace. */
1.134     dholland  612:        if (forcecwd != NULL) {
1.133     dholland  613:                state->namei_startdir = namei_getstartdir_for_nfsd(state,
                    614:                                                                   forcecwd);
                    615:                /* no ktrace */
                    616:        } else {
                    617:                state->namei_startdir = namei_getstartdir(state);
                    618:                namei_ktrace(state);
                    619:        }
1.97      ad        620:
1.118     dholland  621:        vn_lock(state->namei_startdir, LK_EXCLUSIVE | LK_RETRY);
1.117     dholland  622:
                    623:        return 0;
                    624: }
                    625:
                    626: /*
1.133     dholland  627:  * Undo namei_start: unlock and release the current lookup directory.
1.117     dholland  628:  */
                    629: static void
                    630: namei_end(struct namei_state *state)
                    631: {
1.118     dholland  632:        vput(state->namei_startdir);
1.117     dholland  633: }
                    634:
                    635: /*
                    636:  * Check for being at a symlink.
                    637:  */
                    638: static inline int
                    639: namei_atsymlink(struct namei_state *state)
                    640: {
                    641:        return (state->cnp->cn_flags & ISSYMLINK) != 0;
                    642: }
                    643:
                    644: /*
                    645:  * Follow a symlink.
                    646:  */
                    647: static inline int
1.134     dholland  648: namei_follow(struct namei_state *state, int inhibitmagic)
1.117     dholland  649: {
                    650:        struct nameidata *ndp = state->ndp;
                    651:        struct componentname *cnp = state->cnp;
                    652:
                    653:        struct lwp *self = curlwp;      /* thread doing namei() */
                    654:        struct iovec aiov;              /* uio for reading symbolic links */
                    655:        struct uio auio;
                    656:        char *cp;                       /* pointer into pathname argument */
                    657:        size_t linklen;
                    658:        int error;
                    659:
                    660:        if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
                    661:                return ELOOP;
                    662:        }
                    663:        if (ndp->ni_vp->v_mount->mnt_flag & MNT_SYMPERM) {
                    664:                error = VOP_ACCESS(ndp->ni_vp, VEXEC, cnp->cn_cred);
                    665:                if (error != 0)
                    666:                        return error;
                    667:        }
1.124     dholland  668:
                    669:        /* FUTURE: fix this to not use a second buffer */
                    670:        cp = PNBUF_GET();
1.117     dholland  671:        aiov.iov_base = cp;
                    672:        aiov.iov_len = MAXPATHLEN;
                    673:        auio.uio_iov = &aiov;
                    674:        auio.uio_iovcnt = 1;
                    675:        auio.uio_offset = 0;
                    676:        auio.uio_rw = UIO_READ;
                    677:        auio.uio_resid = MAXPATHLEN;
                    678:        UIO_SETUP_SYSSPACE(&auio);
                    679:        error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
                    680:        if (error) {
1.124     dholland  681:                PNBUF_PUT(cp);
1.117     dholland  682:                return error;
                    683:        }
                    684:        linklen = MAXPATHLEN - auio.uio_resid;
                    685:        if (linklen == 0) {
1.124     dholland  686:                PNBUF_PUT(cp);
                    687:                return ENOENT;
1.117     dholland  688:        }
                    689:
                    690:        /*
                    691:         * Do symlink substitution, if appropriate, and
                    692:         * check length for potential overflow.
1.134     dholland  693:         *
                    694:         * Inhibit symlink substitution for nfsd.
                    695:         * XXX: This is how it was before; is that a bug or a feature?
1.117     dholland  696:         */
1.134     dholland  697:        if ((!inhibitmagic && vfs_magiclinks &&
1.117     dholland  698:             symlink_magic(self->l_proc, cp, &linklen)) ||
                    699:            (linklen + ndp->ni_pathlen >= MAXPATHLEN)) {
1.124     dholland  700:                PNBUF_PUT(cp);
                    701:                return ENAMETOOLONG;
1.117     dholland  702:        }
                    703:        if (ndp->ni_pathlen > 1) {
1.124     dholland  704:                /* includes a null-terminator */
1.117     dholland  705:                memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen);
1.124     dholland  706:        } else {
                    707:                cp[linklen] = '\0';
                    708:        }
1.117     dholland  709:        ndp->ni_pathlen += linklen;
1.124     dholland  710:        memcpy(ndp->ni_pnbuf, cp, ndp->ni_pathlen);
                    711:        PNBUF_PUT(cp);
1.117     dholland  712:        vput(ndp->ni_vp);
1.118     dholland  713:        state->namei_startdir = ndp->ni_dvp;
1.117     dholland  714:
                    715:        /*
                    716:         * Check if root directory should replace current directory.
                    717:         */
1.124     dholland  718:        if (ndp->ni_pnbuf[0] == '/') {
1.118     dholland  719:                vput(state->namei_startdir);
1.117     dholland  720:                /* Keep absolute symbolic links inside emulation root */
1.118     dholland  721:                state->namei_startdir = ndp->ni_erootdir;
1.124     dholland  722:                if (state->namei_startdir == NULL ||
                    723:                    (ndp->ni_pnbuf[1] == '.'
                    724:                     && ndp->ni_pnbuf[2] == '.'
                    725:                     && ndp->ni_pnbuf[3] == '/')) {
1.117     dholland  726:                        ndp->ni_erootdir = NULL;
1.118     dholland  727:                        state->namei_startdir = ndp->ni_rootdir;
1.117     dholland  728:                }
1.121     pooka     729:                vref(state->namei_startdir);
1.118     dholland  730:                vn_lock(state->namei_startdir, LK_EXCLUSIVE | LK_RETRY);
1.117     dholland  731:        }
                    732:
                    733:        return 0;
                    734: }
                    735:
                    736: //////////////////////////////
                    737:
1.39      lukem     738: /*
1.10      cgd       739:  * Search a pathname.
                    740:  * This is a very central and rather complicated routine.
                    741:  *
                    742:  * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
1.128     dholland  743:  * The starting directory is passed in. The pathname is descended
                    744:  * until done, or a symbolic link is encountered. The variable ni_more
                    745:  * is clear if the path is completed; it is set to one if a symbolic
                    746:  * link needing interpretation is encountered.
1.10      cgd       747:  *
                    748:  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
                    749:  * whether the name is to be looked up, created, renamed, or deleted.
                    750:  * When CREATE, RENAME, or DELETE is specified, information usable in
                    751:  * creating, renaming, or deleting a directory entry may be calculated.
                    752:  * If flag has LOCKPARENT or'ed into it, the parent directory is returned
1.79      pooka     753:  * locked.  Otherwise the parent directory is not returned. If the target
                    754:  * of the pathname exists and LOCKLEAF is or'ed into the flag the target
                    755:  * is returned locked, otherwise it is returned unlocked.  When creating
                    756:  * or renaming and LOCKPARENT is specified, the target may not be ".".
                    757:  * When deleting and LOCKPARENT is specified, the target may be ".".
1.56      perry     758:  *
1.10      cgd       759:  * Overall outline of lookup:
                    760:  *
                    761:  * dirloop:
                    762:  *     identify next component of name at ndp->ni_ptr
                    763:  *     handle degenerate case where name is null string
                    764:  *     if .. and crossing mount points and on mounted filesys, find parent
                    765:  *     call VOP_LOOKUP routine for next component name
1.79      pooka     766:  *         directory vnode returned in ni_dvp, locked.
1.10      cgd       767:  *         component vnode returned in ni_vp (if it exists), locked.
                    768:  *     if result vnode is mounted on and crossing mount points,
                    769:  *         find mounted on vnode
                    770:  *     if more components of name, do next level at dirloop
                    771:  *     return the answer in ni_vp, locked if LOCKLEAF set
                    772:  *         if LOCKPARENT set, return locked parent in ni_dvp
                    773:  */
1.118     dholland  774:
                    775: /*
                    776:  * Begin lookup().
                    777:  */
                    778: static int
1.128     dholland  779: lookup_start(struct namei_state *state, struct vnode *startdir)
1.10      cgd       780: {
1.33      augustss  781:        const char *cp;                 /* pointer into pathname argument */
1.118     dholland  782:
                    783:        struct componentname *cnp = state->cnp;
                    784:        struct nameidata *ndp = state->ndp;
                    785:
                    786:        KASSERT(cnp == &ndp->ni_cnd);
                    787:
                    788:        state->lookup_alldone = 0;
                    789:        state->dp = NULL;
1.10      cgd       790:
                    791:        /*
                    792:         * Setup: break out flag bits into variables.
                    793:         */
1.118     dholland  794:        state->docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
1.78      pooka     795:        if (cnp->cn_nameiop == DELETE)
1.118     dholland  796:                state->docache = 0;
                    797:        state->rdonly = cnp->cn_flags & RDONLY;
1.10      cgd       798:        ndp->ni_dvp = NULL;
1.12      mycroft   799:        cnp->cn_flags &= ~ISSYMLINK;
1.128     dholland  800:        state->dp = startdir;
1.10      cgd       801:
1.23      mycroft   802:        /*
                    803:         * If we have a leading string of slashes, remove them, and just make
                    804:         * sure the current node is a directory.
                    805:         */
                    806:        cp = cnp->cn_nameptr;
                    807:        if (*cp == '/') {
                    808:                do {
                    809:                        cp++;
                    810:                } while (*cp == '/');
                    811:                ndp->ni_pathlen -= cp - cnp->cn_nameptr;
                    812:                cnp->cn_nameptr = cp;
                    813:
1.118     dholland  814:                if (state->dp->v_type != VDIR) {
                    815:                        vput(state->dp);
                    816:                        return ENOTDIR;
1.23      mycroft   817:                }
                    818:
                    819:                /*
                    820:                 * If we've exhausted the path name, then just return the
1.87      dsl       821:                 * current node.
1.23      mycroft   822:                 */
                    823:                if (cnp->cn_nameptr[0] == '\0') {
1.118     dholland  824:                        ndp->ni_vp = state->dp;
1.23      mycroft   825:                        cnp->cn_flags |= ISLASTCN;
1.118     dholland  826:
                    827:                        /* bleh */
                    828:                        state->lookup_alldone = 1;
                    829:                        return 0;
1.23      mycroft   830:                }
                    831:        }
                    832:
1.118     dholland  833:        return 0;
                    834: }
                    835:
                    836: static int
                    837: lookup_parsepath(struct namei_state *state)
                    838: {
                    839:        const char *cp;                 /* pointer into pathname argument */
                    840:
                    841:        struct componentname *cnp = state->cnp;
                    842:        struct nameidata *ndp = state->ndp;
                    843:
                    844:        KASSERT(cnp == &ndp->ni_cnd);
                    845:
1.10      cgd       846:        /*
                    847:         * Search a new directory.
                    848:         *
1.12      mycroft   849:         * The cn_hash value is for use by vfs_cache.
1.10      cgd       850:         * The last component of the filename is left accessible via
1.12      mycroft   851:         * cnp->cn_nameptr for callers that need the name. Callers needing
1.10      cgd       852:         * the name set the SAVENAME flag. When done, they assume
                    853:         * responsibility for freeing the pathname buffer.
1.127     yamt      854:         *
                    855:         * At this point, our only vnode state is that "dp" is held and locked.
1.10      cgd       856:         */
1.12      mycroft   857:        cnp->cn_consume = 0;
1.39      lukem     858:        cp = NULL;
                    859:        cnp->cn_hash = namei_hash(cnp->cn_nameptr, &cp);
1.12      mycroft   860:        cnp->cn_namelen = cp - cnp->cn_nameptr;
                    861:        if (cnp->cn_namelen > NAME_MAX) {
1.127     yamt      862:                vput(state->dp);
                    863:                ndp->ni_dvp = NULL;
1.118     dholland  864:                return ENAMETOOLONG;
1.10      cgd       865:        }
                    866: #ifdef NAMEI_DIAGNOSTIC
                    867:        { char c = *cp;
1.41      soren     868:        *(char *)cp = '\0';
1.19      christos  869:        printf("{%s}: ", cnp->cn_nameptr);
1.41      soren     870:        *(char *)cp = c; }
1.52      yamt      871: #endif /* NAMEI_DIAGNOSTIC */
1.12      mycroft   872:        ndp->ni_pathlen -= cnp->cn_namelen;
1.10      cgd       873:        ndp->ni_next = cp;
1.23      mycroft   874:        /*
                    875:         * If this component is followed by a slash, then move the pointer to
                    876:         * the next component forward, and remember that this component must be
                    877:         * a directory.
                    878:         */
                    879:        if (*cp == '/') {
                    880:                do {
                    881:                        cp++;
                    882:                } while (*cp == '/');
1.118     dholland  883:                state->slashes = cp - ndp->ni_next;
                    884:                ndp->ni_pathlen -= state->slashes;
1.23      mycroft   885:                ndp->ni_next = cp;
                    886:                cnp->cn_flags |= REQUIREDIR;
                    887:        } else {
1.118     dholland  888:                state->slashes = 0;
1.23      mycroft   889:                cnp->cn_flags &= ~REQUIREDIR;
                    890:        }
                    891:        /*
                    892:         * We do special processing on the last component, whether or not it's
                    893:         * a directory.  Cache all intervening lookups, but not the final one.
                    894:         */
                    895:        if (*cp == '\0') {
1.118     dholland  896:                if (state->docache)
1.23      mycroft   897:                        cnp->cn_flags |= MAKEENTRY;
                    898:                else
                    899:                        cnp->cn_flags &= ~MAKEENTRY;
                    900:                cnp->cn_flags |= ISLASTCN;
                    901:        } else {
                    902:                cnp->cn_flags |= MAKEENTRY;
                    903:                cnp->cn_flags &= ~ISLASTCN;
                    904:        }
1.12      mycroft   905:        if (cnp->cn_namelen == 2 &&
                    906:            cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
                    907:                cnp->cn_flags |= ISDOTDOT;
                    908:        else
                    909:                cnp->cn_flags &= ~ISDOTDOT;
1.10      cgd       910:
1.118     dholland  911:        return 0;
                    912: }
                    913:
                    914: static int
                    915: lookup_once(struct namei_state *state)
                    916: {
                    917:        struct vnode *tdp;              /* saved dp */
                    918:        struct mount *mp;               /* mount table entry */
                    919:        struct lwp *l = curlwp;
                    920:        int error;
                    921:
                    922:        struct componentname *cnp = state->cnp;
                    923:        struct nameidata *ndp = state->ndp;
                    924:
                    925:        KASSERT(cnp == &ndp->ni_cnd);
                    926:
1.10      cgd       927:        /*
                    928:         * Handle "..": two special cases.
                    929:         * 1. If at root directory (e.g. after chroot)
1.12      mycroft   930:         *    or at absolute root directory
1.10      cgd       931:         *    then ignore it so can't get out.
1.85      dsl       932:         * 1a. If at the root of the emulation filesystem go to the real
                    933:         *    root. So "/../<path>" is always absolute.
                    934:         * 1b. If we have somehow gotten out of a jail, warn
1.40      wrstuden  935:         *    and also ignore it so we can't get farther out.
1.10      cgd       936:         * 2. If this vnode is the root of a mounted
                    937:         *    filesystem, then replace it with the
                    938:         *    vnode which was mounted on so we take the
                    939:         *    .. in the other file system.
                    940:         */
1.12      mycroft   941:        if (cnp->cn_flags & ISDOTDOT) {
1.64      christos  942:                struct proc *p = l->l_proc;
                    943:
1.10      cgd       944:                for (;;) {
1.118     dholland  945:                        if (state->dp == ndp->ni_rootdir || state->dp == rootvnode) {
                    946:                                ndp->ni_dvp = state->dp;
                    947:                                ndp->ni_vp = state->dp;
1.121     pooka     948:                                vref(state->dp);
1.118     dholland  949:                                return 0;
1.40      wrstuden  950:                        }
                    951:                        if (ndp->ni_rootdir != rootvnode) {
                    952:                                int retval;
1.73      chs       953:
1.122     hannken   954:                                VOP_UNLOCK(state->dp);
1.118     dholland  955:                                retval = vn_isunder(state->dp, ndp->ni_rootdir, l);
                    956:                                vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
1.40      wrstuden  957:                                if (!retval) {
                    958:                                    /* Oops! We got out of jail! */
                    959:                                    log(LOG_WARNING,
                    960:                                        "chrooted pid %d uid %d (%s) "
                    961:                                        "detected outside of its chroot\n",
1.71      ad        962:                                        p->p_pid, kauth_cred_geteuid(l->l_cred),
1.64      christos  963:                                        p->p_comm);
1.40      wrstuden  964:                                    /* Put us at the jail root. */
1.118     dholland  965:                                    vput(state->dp);
                    966:                                    state->dp = ndp->ni_rootdir;
                    967:                                    ndp->ni_dvp = state->dp;
                    968:                                    ndp->ni_vp = state->dp;
1.121     pooka     969:                                    vref(state->dp);
                    970:                                    vref(state->dp);
1.118     dholland  971:                                    vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
                    972:                                    return 0;
1.40      wrstuden  973:                                }
1.10      cgd       974:                        }
1.118     dholland  975:                        if ((state->dp->v_vflag & VV_ROOT) == 0 ||
1.12      mycroft   976:                            (cnp->cn_flags & NOCROSSMOUNT))
1.10      cgd       977:                                break;
1.118     dholland  978:                        tdp = state->dp;
                    979:                        state->dp = state->dp->v_mount->mnt_vnodecovered;
1.10      cgd       980:                        vput(tdp);
1.121     pooka     981:                        vref(state->dp);
1.118     dholland  982:                        vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
1.10      cgd       983:                }
                    984:        }
                    985:
                    986:        /*
                    987:         * We now have a segment name to search for, and a directory to search.
1.73      chs       988:         * Again, our only vnode state is that "dp" is held and locked.
1.10      cgd       989:         */
1.12      mycroft   990: unionlookup:
1.118     dholland  991:        ndp->ni_dvp = state->dp;
1.26      fvdl      992:        ndp->ni_vp = NULL;
1.118     dholland  993:        error = VOP_LOOKUP(state->dp, &ndp->ni_vp, cnp);
1.73      chs       994:        if (error != 0) {
1.10      cgd       995: #ifdef DIAGNOSTIC
                    996:                if (ndp->ni_vp != NULL)
1.43      christos  997:                        panic("leaf `%s' should be empty", cnp->cn_nameptr);
1.52      yamt      998: #endif /* DIAGNOSTIC */
1.10      cgd       999: #ifdef NAMEI_DIAGNOSTIC
1.19      christos 1000:                printf("not found\n");
1.52      yamt     1001: #endif /* NAMEI_DIAGNOSTIC */
1.12      mycroft  1002:                if ((error == ENOENT) &&
1.118     dholland 1003:                    (state->dp->v_vflag & VV_ROOT) &&
                   1004:                    (state->dp->v_mount->mnt_flag & MNT_UNION)) {
                   1005:                        tdp = state->dp;
                   1006:                        state->dp = state->dp->v_mount->mnt_vnodecovered;
1.73      chs      1007:                        vput(tdp);
1.121     pooka    1008:                        vref(state->dp);
1.118     dholland 1009:                        vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
1.12      mycroft  1010:                        goto unionlookup;
1.10      cgd      1011:                }
1.12      mycroft  1012:
1.10      cgd      1013:                if (error != EJUSTRETURN)
1.118     dholland 1014:                        return error;
1.73      chs      1015:
1.10      cgd      1016:                /*
1.23      mycroft  1017:                 * If this was not the last component, or there were trailing
1.51      christos 1018:                 * slashes, and we are not going to create a directory,
                   1019:                 * then the name must exist.
1.23      mycroft  1020:                 */
1.51      christos 1021:                if ((cnp->cn_flags & (REQUIREDIR | CREATEDIR)) == REQUIREDIR) {
1.118     dholland 1022:                        return ENOENT;
1.23      mycroft  1023:                }
1.73      chs      1024:
1.23      mycroft  1025:                /*
1.10      cgd      1026:                 * If creating and at end of pathname, then can consider
                   1027:                 * allowing file to be created.
                   1028:                 */
1.118     dholland 1029:                if (state->rdonly) {
                   1030:                        return EROFS;
1.10      cgd      1031:                }
1.73      chs      1032:
1.10      cgd      1033:                /*
                   1034:                 * We return with ni_vp NULL to indicate that the entry
                   1035:                 * doesn't currently exist, leaving a pointer to the
1.69      rumble   1036:                 * (possibly locked) directory vnode in ndp->ni_dvp.
1.10      cgd      1037:                 */
1.118     dholland 1038:                state->lookup_alldone = 1;
1.10      cgd      1039:                return (0);
                   1040:        }
                   1041: #ifdef NAMEI_DIAGNOSTIC
1.19      christos 1042:        printf("found\n");
1.52      yamt     1043: #endif /* NAMEI_DIAGNOSTIC */
1.10      cgd      1044:
1.12      mycroft  1045:        /*
1.23      mycroft  1046:         * Take into account any additional components consumed by the
                   1047:         * underlying filesystem.  This will include any trailing slashes after
                   1048:         * the last component consumed.
1.12      mycroft  1049:         */
                   1050:        if (cnp->cn_consume > 0) {
1.118     dholland 1051:                ndp->ni_pathlen -= cnp->cn_consume - state->slashes;
                   1052:                ndp->ni_next += cnp->cn_consume - state->slashes;
1.12      mycroft  1053:                cnp->cn_consume = 0;
1.23      mycroft  1054:                if (ndp->ni_next[0] == '\0')
                   1055:                        cnp->cn_flags |= ISLASTCN;
1.12      mycroft  1056:        }
                   1057:
1.118     dholland 1058:        state->dp = ndp->ni_vp;
1.73      chs      1059:
                   1060:        /*
1.118     dholland 1061:         * "state->dp" and "ndp->ni_dvp" are both locked and held,
1.73      chs      1062:         * and may be the same vnode.
                   1063:         */
                   1064:
1.10      cgd      1065:        /*
                   1066:         * Check to see if the vnode has been mounted on;
                   1067:         * if so find the root of the mounted file system.
                   1068:         */
1.118     dholland 1069:        while (state->dp->v_type == VDIR && (mp = state->dp->v_mountedhere) &&
1.12      mycroft  1070:               (cnp->cn_flags & NOCROSSMOUNT) == 0) {
1.108     ad       1071:                error = vfs_busy(mp, NULL);
1.107     ad       1072:                if (error != 0) {
1.118     dholland 1073:                        vput(state->dp);
                   1074:                        return error;
1.107     ad       1075:                }
1.118     dholland 1076:                KASSERT(ndp->ni_dvp != state->dp);
1.122     hannken  1077:                VOP_UNLOCK(ndp->ni_dvp);
1.118     dholland 1078:                vput(state->dp);
1.47      thorpej  1079:                error = VFS_ROOT(mp, &tdp);
1.106     ad       1080:                vfs_unbusy(mp, false, NULL);
1.32      wrstuden 1081:                if (error) {
1.77      chs      1082:                        vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY);
1.118     dholland 1083:                        return error;
1.32      wrstuden 1084:                }
1.127     yamt     1085:                VOP_UNLOCK(tdp);
                   1086:                ndp->ni_vp = state->dp = tdp;
                   1087:                vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY);
                   1088:                vn_lock(ndp->ni_vp, LK_EXCLUSIVE | LK_RETRY);
1.14      mycroft  1089:        }
                   1090:
1.118     dholland 1091:        return 0;
                   1092: }
                   1093:
                   1094: static int
1.128     dholland 1095: do_lookup(struct namei_state *state, struct vnode *startdir)
1.118     dholland 1096: {
                   1097:        int error = 0;
                   1098:
                   1099:        struct componentname *cnp = state->cnp;
                   1100:        struct nameidata *ndp = state->ndp;
                   1101:
                   1102:        KASSERT(cnp == &ndp->ni_cnd);
                   1103:
1.133     dholland 1104:        cnp->cn_nameptr = ndp->ni_pnbuf;
                   1105:
1.128     dholland 1106:        error = lookup_start(state, startdir);
1.118     dholland 1107:        if (error) {
                   1108:                goto bad;
                   1109:        }
                   1110:        // XXX: this case should not be necessary given proper handling
                   1111:        // of slashes elsewhere.
                   1112:        if (state->lookup_alldone) {
                   1113:                goto terminal;
                   1114:        }
                   1115:
                   1116: dirloop:
                   1117:        error = lookup_parsepath(state);
                   1118:        if (error) {
                   1119:                goto bad;
                   1120:        }
                   1121:
                   1122:        error = lookup_once(state);
                   1123:        if (error) {
                   1124:                goto bad;
                   1125:        }
                   1126:        // XXX ought to be able to avoid this case too
                   1127:        if (state->lookup_alldone) {
                   1128:                /* this should NOT be "goto terminal;" */
                   1129:                return 0;
                   1130:        }
                   1131:
1.14      mycroft  1132:        /*
1.23      mycroft  1133:         * Check for symbolic link.  Back up over any slashes that we skipped,
                   1134:         * as we will need them again.
1.14      mycroft  1135:         */
1.118     dholland 1136:        if ((state->dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
                   1137:                ndp->ni_pathlen += state->slashes;
                   1138:                ndp->ni_next -= state->slashes;
1.14      mycroft  1139:                cnp->cn_flags |= ISSYMLINK;
                   1140:                return (0);
1.10      cgd      1141:        }
                   1142:
1.23      mycroft  1143:        /*
                   1144:         * Check for directory, if the component was followed by a series of
                   1145:         * slashes.
                   1146:         */
1.118     dholland 1147:        if ((state->dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
1.23      mycroft  1148:                error = ENOTDIR;
1.118     dholland 1149:                KASSERT(state->dp != ndp->ni_dvp);
                   1150:                vput(state->dp);
1.73      chs      1151:                goto bad;
1.23      mycroft  1152:        }
                   1153:
1.10      cgd      1154:        /*
1.23      mycroft  1155:         * Not a symbolic link.  If this was not the last component, then
                   1156:         * continue at the next component, else return.
1.10      cgd      1157:         */
1.23      mycroft  1158:        if (!(cnp->cn_flags & ISLASTCN)) {
1.12      mycroft  1159:                cnp->cn_nameptr = ndp->ni_next;
1.118     dholland 1160:                if (ndp->ni_dvp == state->dp) {
1.73      chs      1161:                        vrele(ndp->ni_dvp);
                   1162:                } else {
                   1163:                        vput(ndp->ni_dvp);
                   1164:                }
1.10      cgd      1165:                goto dirloop;
                   1166:        }
1.23      mycroft  1167:
                   1168: terminal:
1.118     dholland 1169:        if (state->dp == ndp->ni_erootdir) {
1.87      dsl      1170:                /*
                   1171:                 * We are about to return the emulation root.
                   1172:                 * This isn't a good idea because code might repeatedly
                   1173:                 * lookup ".." until the file matches that returned
                   1174:                 * for "/" and loop forever.
                   1175:                 * So convert it to the real root.
                   1176:                 */
1.118     dholland 1177:                if (ndp->ni_dvp == state->dp)
                   1178:                        vrele(state->dp);
1.87      dsl      1179:                else
                   1180:                        if (ndp->ni_dvp != NULL)
                   1181:                                vput(ndp->ni_dvp);
                   1182:                ndp->ni_dvp = NULL;
1.118     dholland 1183:                vput(state->dp);
                   1184:                state->dp = ndp->ni_rootdir;
1.121     pooka    1185:                vref(state->dp);
1.118     dholland 1186:                vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
                   1187:                ndp->ni_vp = state->dp;
1.87      dsl      1188:        }
                   1189:
                   1190:        /*
                   1191:         * If the caller requested the parent node (i.e.
                   1192:         * it's a CREATE, DELETE, or RENAME), and we don't have one
                   1193:         * (because this is the root directory), then we must fail.
                   1194:         */
                   1195:        if (ndp->ni_dvp == NULL && cnp->cn_nameiop != LOOKUP) {
                   1196:                switch (cnp->cn_nameiop) {
                   1197:                case CREATE:
                   1198:                        error = EEXIST;
                   1199:                        break;
                   1200:                case DELETE:
                   1201:                case RENAME:
                   1202:                        error = EBUSY;
                   1203:                        break;
                   1204:                default:
                   1205:                        KASSERT(0);
                   1206:                }
1.118     dholland 1207:                vput(state->dp);
1.87      dsl      1208:                goto bad;
                   1209:        }
1.73      chs      1210:
1.10      cgd      1211:        /*
1.94      pooka    1212:         * Disallow directory write attempts on read-only lookups.
1.96      pooka    1213:         * Prefers EEXIST over EROFS for the CREATE case.
1.10      cgd      1214:         */
1.118     dholland 1215:        if (state->rdonly &&
1.96      pooka    1216:            (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
1.26      fvdl     1217:                error = EROFS;
1.118     dholland 1218:                if (state->dp != ndp->ni_dvp) {
                   1219:                        vput(state->dp);
1.73      chs      1220:                }
                   1221:                goto bad;
1.10      cgd      1222:        }
1.73      chs      1223:        if ((cnp->cn_flags & LOCKLEAF) == 0) {
1.122     hannken  1224:                VOP_UNLOCK(state->dp);
1.73      chs      1225:        }
1.10      cgd      1226:        return (0);
                   1227:
                   1228: bad:
                   1229:        ndp->ni_vp = NULL;
1.12      mycroft  1230:        return (error);
                   1231: }
                   1232:
1.131     dholland 1233: //////////////////////////////
                   1234:
                   1235: static int
1.134     dholland 1236: do_namei(struct namei_state *state, struct vnode *forcecwd,
                   1237:         int neverfollow, int inhibitmagic)
1.131     dholland 1238: {
                   1239:        int error;
                   1240:
                   1241:        struct nameidata *ndp = state->ndp;
                   1242:        struct componentname *cnp = state->cnp;
                   1243:        const char *savepath = NULL;
                   1244:
                   1245:        KASSERT(cnp == &ndp->ni_cnd);
                   1246:
                   1247:        if (cnp->cn_flags & TRYEMULROOT) {
                   1248:                savepath = pathbuf_stringcopy_get(ndp->ni_pathbuf);
                   1249:        }
                   1250:
                   1251:     emul_retry:
                   1252:
                   1253:        if (savepath != NULL) {
                   1254:                /* kinda gross */
                   1255:                strcpy(ndp->ni_pathbuf->pb_path, savepath);
                   1256:                pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
                   1257:                savepath = NULL;
                   1258:        }
                   1259:
1.134     dholland 1260:        error = namei_start(state, forcecwd);
1.131     dholland 1261:        if (error) {
                   1262:                if (savepath != NULL) {
                   1263:                        pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
                   1264:                }
                   1265:                return error;
                   1266:        }
                   1267:
1.133     dholland 1268:        /*
                   1269:         * Keep going until we run out of path components.
                   1270:         */
1.131     dholland 1271:        for (;;) {
1.133     dholland 1272:
                   1273:                /*
                   1274:                 * If the directory we're on is unmounted, bail out.
                   1275:                 * XXX: should this also check if it's unlinked?
                   1276:                 */
1.131     dholland 1277:                if (state->namei_startdir->v_mount == NULL) {
                   1278:                        if (savepath != NULL) {
                   1279:                                pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
                   1280:                        }
                   1281:                        namei_end(state);
                   1282:                        return (ENOENT);
                   1283:                }
1.133     dholland 1284:
                   1285:                /*
                   1286:                 * Look up the next path component.
                   1287:                 * (currently, this may consume more than one)
                   1288:                 */
1.131     dholland 1289:                error = do_lookup(state, state->namei_startdir);
                   1290:                if (error != 0) {
                   1291:                        /* XXX this should use namei_end() */
                   1292:                        if (ndp->ni_dvp) {
                   1293:                                vput(ndp->ni_dvp);
                   1294:                        }
                   1295:                        if (ndp->ni_erootdir != NULL) {
                   1296:                                /* Retry the whole thing from the normal root */
                   1297:                                cnp->cn_flags &= ~TRYEMULROOT;
                   1298:                                goto emul_retry;
                   1299:                        }
                   1300:                        KASSERT(savepath == NULL);
                   1301:                        return (error);
                   1302:                }
                   1303:
                   1304:                /*
1.133     dholland 1305:                 * If we've reached a symbolic link, follow it, unless we
                   1306:                 * aren't supposed to.
1.131     dholland 1307:                 */
                   1308:                if (namei_atsymlink(state)) {
1.134     dholland 1309:                        if (neverfollow) {
                   1310:                                error = EINVAL;
                   1311:                        } else {
                   1312:                                error = namei_follow(state, inhibitmagic);
                   1313:                        }
1.131     dholland 1314:                        if (error) {
                   1315:                                KASSERT(ndp->ni_dvp != ndp->ni_vp);
                   1316:                                vput(ndp->ni_dvp);
                   1317:                                vput(ndp->ni_vp);
                   1318:                                ndp->ni_vp = NULL;
                   1319:                                if (savepath != NULL) {
                   1320:                                        pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
                   1321:                                }
                   1322:                                return error;
                   1323:                        }
                   1324:                }
                   1325:                else {
                   1326:                        break;
                   1327:                }
                   1328:        }
                   1329:
                   1330:        /*
1.133     dholland 1331:         * Done.
1.131     dholland 1332:         */
                   1333:
1.133     dholland 1334:        /*
                   1335:         * If LOCKPARENT is not set, the parent directory isn't returned.
                   1336:         */
                   1337:        if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != NULL) {
1.131     dholland 1338:                if (ndp->ni_dvp == ndp->ni_vp) {
                   1339:                        vrele(ndp->ni_dvp);
                   1340:                } else {
                   1341:                        vput(ndp->ni_dvp);
                   1342:                }
1.133     dholland 1343:                ndp->ni_dvp = NULL;
1.131     dholland 1344:        }
                   1345:
                   1346:        if (savepath != NULL) {
                   1347:                pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
                   1348:        }
                   1349:
                   1350:        return 0;
                   1351: }
                   1352:
                   1353: int
                   1354: namei(struct nameidata *ndp)
                   1355: {
                   1356:        struct namei_state state;
                   1357:        int error;
                   1358:
                   1359:        namei_init(&state, ndp);
1.134     dholland 1360:        error = do_namei(&state, NULL, 0/*!neverfollow*/, 0/*!inhibitmagic*/);
1.131     dholland 1361:        namei_cleanup(&state);
                   1362:
                   1363:        return error;
                   1364: }
                   1365:
                   1366: ////////////////////////////////////////////////////////////
                   1367:
1.12      mycroft  1368: /*
1.119     dholland 1369:  * Externally visible interfaces used by nfsd (bletch, yuk, XXX)
                   1370:  *
                   1371:  * The "index" version differs from the "main" version in that it's
                   1372:  * called from a different place in a different context. For now I
                   1373:  * want to be able to shuffle code in from one call site without
                   1374:  * affecting the other.
1.135   ! dholland 1375:  *
        !          1376:  * It turns out that the "main" version was a cut and pasted copy of
        !          1377:  * namei with a few changes; the "index" version on the other hand
        !          1378:  * always takes a single component and is an elaborate form of calling
        !          1379:  * VOP_LOOKUP once.
1.118     dholland 1380:  */
1.119     dholland 1381:
1.134     dholland 1382: int
1.135   ! dholland 1383: lookup_for_nfsd(struct nameidata *ndp, struct vnode *forcecwd, int neverfollow)
1.134     dholland 1384: {
                   1385:        struct namei_state state;
                   1386:        int error;
1.120     dholland 1387:
1.134     dholland 1388:        namei_init(&state, ndp);
1.135   ! dholland 1389:        error = do_namei(&state, forcecwd, neverfollow, 1/*inhibitmagic*/);
1.119     dholland 1390:        namei_cleanup(&state);
                   1391:
                   1392:        return error;
                   1393: }
                   1394:
1.118     dholland 1395: int
1.128     dholland 1396: lookup_for_nfsd_index(struct nameidata *ndp, struct vnode *startdir)
1.118     dholland 1397: {
                   1398:        struct namei_state state;
                   1399:        int error;
                   1400:
1.133     dholland 1401:        /*
1.135   ! dholland 1402:         * Note: the name sent in here (is not|should not be) allowed
        !          1403:         * to contain a slash.
1.133     dholland 1404:         */
                   1405:
                   1406:        ndp->ni_pathlen = strlen(ndp->ni_pathbuf->pb_path) + 1;
                   1407:        ndp->ni_pnbuf = NULL;
                   1408:        ndp->ni_cnd.cn_nameptr = NULL;
                   1409:
1.128     dholland 1410:        vref(startdir);
                   1411:
1.118     dholland 1412:        namei_init(&state, ndp);
1.128     dholland 1413:        error = do_lookup(&state, startdir);
1.118     dholland 1414:        namei_cleanup(&state);
                   1415:
                   1416:        return error;
                   1417: }
                   1418:
1.131     dholland 1419: ////////////////////////////////////////////////////////////
                   1420:
1.118     dholland 1421: /*
1.12      mycroft  1422:  * Reacquire a path name component.
1.73      chs      1423:  * dvp is locked on entry and exit.
                   1424:  * *vpp is locked on exit unless it's NULL.
1.12      mycroft  1425:  */
                   1426: int
1.130     dholland 1427: relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int dummy)
1.12      mycroft  1428: {
                   1429:        int rdonly;                     /* lookup read-only flag bit */
                   1430:        int error = 0;
1.52      yamt     1431: #ifdef DEBUG
1.81      chs      1432:        uint32_t newhash;               /* DEBUG: check name hash */
1.41      soren    1433:        const char *cp;                 /* DEBUG: check name ptr/len */
1.52      yamt     1434: #endif /* DEBUG */
1.12      mycroft  1435:
1.130     dholland 1436:        (void)dummy;
                   1437:
1.12      mycroft  1438:        /*
                   1439:         * Setup: break out flag bits into variables.
                   1440:         */
                   1441:        rdonly = cnp->cn_flags & RDONLY;
                   1442:        cnp->cn_flags &= ~ISSYMLINK;
                   1443:
                   1444:        /*
                   1445:         * Search a new directory.
                   1446:         *
                   1447:         * The cn_hash value is for use by vfs_cache.
                   1448:         * The last component of the filename is left accessible via
                   1449:         * cnp->cn_nameptr for callers that need the name. Callers needing
                   1450:         * the name set the SAVENAME flag. When done, they assume
                   1451:         * responsibility for freeing the pathname buffer.
                   1452:         */
1.52      yamt     1453: #ifdef DEBUG
1.39      lukem    1454:        cp = NULL;
                   1455:        newhash = namei_hash(cnp->cn_nameptr, &cp);
1.81      chs      1456:        if ((uint32_t)newhash != (uint32_t)cnp->cn_hash)
1.12      mycroft  1457:                panic("relookup: bad hash");
                   1458:        if (cnp->cn_namelen != cp - cnp->cn_nameptr)
1.58      christos 1459:                panic("relookup: bad len");
1.53      yamt     1460:        while (*cp == '/')
                   1461:                cp++;
1.12      mycroft  1462:        if (*cp != 0)
                   1463:                panic("relookup: not last component");
1.52      yamt     1464: #endif /* DEBUG */
1.12      mycroft  1465:
                   1466:        /*
                   1467:         * Check for degenerate name (e.g. / or "")
                   1468:         * which is a way of talking about a directory,
                   1469:         * e.g. like "/." or ".".
                   1470:         */
1.23      mycroft  1471:        if (cnp->cn_nameptr[0] == '\0')
                   1472:                panic("relookup: null name");
1.12      mycroft  1473:
                   1474:        if (cnp->cn_flags & ISDOTDOT)
1.58      christos 1475:                panic("relookup: lookup on dot-dot");
1.12      mycroft  1476:
                   1477:        /*
                   1478:         * We now have a segment name to search for, and a directory to search.
                   1479:         */
1.129     dholland 1480:        cnp->cn_flags |= INRELOOKUP;
                   1481:        error = VOP_LOOKUP(dvp, vpp, cnp);
                   1482:        cnp->cn_flags &= ~INRELOOKUP;
                   1483:        if ((error) != 0) {
1.12      mycroft  1484: #ifdef DIAGNOSTIC
                   1485:                if (*vpp != NULL)
1.43      christos 1486:                        panic("leaf `%s' should be empty", cnp->cn_nameptr);
1.12      mycroft  1487: #endif
                   1488:                if (error != EJUSTRETURN)
                   1489:                        goto bad;
                   1490:        }
                   1491:
                   1492: #ifdef DIAGNOSTIC
                   1493:        /*
                   1494:         * Check for symbolic link
                   1495:         */
1.81      chs      1496:        if (*vpp && (*vpp)->v_type == VLNK && (cnp->cn_flags & FOLLOW))
1.58      christos 1497:                panic("relookup: symlink found");
1.12      mycroft  1498: #endif
                   1499:
                   1500:        /*
1.94      pooka    1501:         * Check for read-only lookups.
1.12      mycroft  1502:         */
1.81      chs      1503:        if (rdonly && cnp->cn_nameiop != LOOKUP) {
1.26      fvdl     1504:                error = EROFS;
1.81      chs      1505:                if (*vpp) {
                   1506:                        vput(*vpp);
                   1507:                }
1.73      chs      1508:                goto bad;
1.12      mycroft  1509:        }
                   1510:        return (0);
                   1511:
                   1512: bad:
                   1513:        *vpp = NULL;
1.10      cgd      1514:        return (error);
                   1515: }
1.116     dholland 1516:
                   1517: /*
                   1518:  * namei_simple - simple forms of namei.
                   1519:  *
                   1520:  * These are wrappers to allow the simple case callers of namei to be
                   1521:  * left alone while everything else changes under them.
                   1522:  */
                   1523:
                   1524: /* Flags */
                   1525: struct namei_simple_flags_type {
                   1526:        int dummy;
                   1527: };
                   1528: static const struct namei_simple_flags_type ns_nn, ns_nt, ns_fn, ns_ft;
                   1529: const namei_simple_flags_t NSM_NOFOLLOW_NOEMULROOT = &ns_nn;
                   1530: const namei_simple_flags_t NSM_NOFOLLOW_TRYEMULROOT = &ns_nt;
                   1531: const namei_simple_flags_t NSM_FOLLOW_NOEMULROOT = &ns_fn;
                   1532: const namei_simple_flags_t NSM_FOLLOW_TRYEMULROOT = &ns_ft;
                   1533:
                   1534: static
                   1535: int
                   1536: namei_simple_convert_flags(namei_simple_flags_t sflags)
                   1537: {
                   1538:        if (sflags == NSM_NOFOLLOW_NOEMULROOT)
                   1539:                return NOFOLLOW | 0;
                   1540:        if (sflags == NSM_NOFOLLOW_TRYEMULROOT)
                   1541:                return NOFOLLOW | TRYEMULROOT;
                   1542:        if (sflags == NSM_FOLLOW_NOEMULROOT)
                   1543:                return FOLLOW | 0;
                   1544:        if (sflags == NSM_FOLLOW_TRYEMULROOT)
                   1545:                return FOLLOW | TRYEMULROOT;
                   1546:        panic("namei_simple_convert_flags: bogus sflags\n");
                   1547:        return 0;
                   1548: }
                   1549:
                   1550: int
                   1551: namei_simple_kernel(const char *path, namei_simple_flags_t sflags,
                   1552:                        struct vnode **vp_ret)
                   1553: {
                   1554:        struct nameidata nd;
1.123     dholland 1555:        struct pathbuf *pb;
1.116     dholland 1556:        int err;
                   1557:
1.123     dholland 1558:        pb = pathbuf_create(path);
                   1559:        if (pb == NULL) {
                   1560:                return ENOMEM;
                   1561:        }
                   1562:
1.116     dholland 1563:        NDINIT(&nd,
                   1564:                LOOKUP,
                   1565:                namei_simple_convert_flags(sflags),
1.123     dholland 1566:                pb);
1.116     dholland 1567:        err = namei(&nd);
                   1568:        if (err != 0) {
1.123     dholland 1569:                pathbuf_destroy(pb);
1.116     dholland 1570:                return err;
                   1571:        }
                   1572:        *vp_ret = nd.ni_vp;
1.123     dholland 1573:        pathbuf_destroy(pb);
1.116     dholland 1574:        return 0;
                   1575: }
                   1576:
                   1577: int
                   1578: namei_simple_user(const char *path, namei_simple_flags_t sflags,
                   1579:                        struct vnode **vp_ret)
                   1580: {
1.123     dholland 1581:        struct pathbuf *pb;
1.116     dholland 1582:        struct nameidata nd;
                   1583:        int err;
                   1584:
1.123     dholland 1585:        err = pathbuf_copyin(path, &pb);
                   1586:        if (err) {
                   1587:                return err;
                   1588:        }
                   1589:
1.116     dholland 1590:        NDINIT(&nd,
                   1591:                LOOKUP,
                   1592:                namei_simple_convert_flags(sflags),
1.123     dholland 1593:                pb);
1.116     dholland 1594:        err = namei(&nd);
                   1595:        if (err != 0) {
1.123     dholland 1596:                pathbuf_destroy(pb);
1.116     dholland 1597:                return err;
                   1598:        }
                   1599:        *vp_ret = nd.ni_vp;
1.123     dholland 1600:        pathbuf_destroy(pb);
1.116     dholland 1601:        return 0;
                   1602: }

CVSweb <webmaster@jp.NetBSD.org>