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

Annotation of src/sys/ufs/ufs/ufs_lookup.c, Revision 1.90

1.90    ! ad          1: /*     $NetBSD: ufs_lookup.c,v 1.89 2007/05/17 07:26:22 hannken Exp $  */
1.2       cgd         2:
1.1       mycroft     3: /*
                      4:  * Copyright (c) 1989, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
                      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.50      agc        20:  * 3. Neither the name of the University nor the names of its contributors
1.1       mycroft    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.3       mycroft    36:  *     @(#)ufs_lookup.c        8.9 (Berkeley) 8/11/94
1.1       mycroft    37:  */
1.35      lukem      38:
                     39: #include <sys/cdefs.h>
1.90    ! ad         40: __KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.89 2007/05/17 07:26:22 hannken Exp $");
1.65      thorpej    41:
                     42: #ifdef _KERNEL_OPT
                     43: #include "opt_ffs.h"
                     44: #endif
1.1       mycroft    45:
                     46: #include <sys/param.h>
1.7       christos   47: #include <sys/systm.h>
1.1       mycroft    48: #include <sys/namei.h>
                     49: #include <sys/buf.h>
                     50: #include <sys/file.h>
1.27      fvdl       51: #include <sys/stat.h>
1.1       mycroft    52: #include <sys/mount.h>
                     53: #include <sys/vnode.h>
1.27      fvdl       54: #include <sys/kernel.h>
1.76      elad       55: #include <sys/kauth.h>
1.83      hannken    56: #include <sys/fstrans.h>
1.90    ! ad         57: #include <sys/lwp.h>
1.1       mycroft    58:
                     59: #include <ufs/ufs/inode.h>
                     60: #include <ufs/ufs/dir.h>
1.61      rumble     61: #ifdef UFS_DIRHASH
                     62: #include <ufs/ufs/dirhash.h>
                     63: #endif
1.1       mycroft    64: #include <ufs/ufs/ufsmount.h>
                     65: #include <ufs/ufs/ufs_extern.h>
1.15      bouyer     66: #include <ufs/ufs/ufs_bswap.h>
1.1       mycroft    67:
1.80      joerg      68: #include "fs_ffs.h"
                     69:
1.1       mycroft    70: #ifdef DIAGNOSTIC
                     71: int    dirchk = 1;
                     72: #else
                     73: int    dirchk = 0;
                     74: #endif
                     75:
1.71      yamt       76: #define        FSFMT(vp)       (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
1.1       mycroft    77:
                     78: /*
                     79:  * Convert a component of a pathname into a pointer to a locked inode.
                     80:  * This is a very central and rather complicated routine.
                     81:  * If the file system is not maintained in a strict tree hierarchy,
                     82:  * this can result in a deadlock situation (see comments in code below).
                     83:  *
                     84:  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
                     85:  * on whether the name is to be looked up, created, renamed, or deleted.
                     86:  * When CREATE, RENAME, or DELETE is specified, information usable in
                     87:  * creating, renaming, or deleting a directory entry may be calculated.
                     88:  * If flag has LOCKPARENT or'ed into it and the target of the pathname
                     89:  * exists, lookup returns both the target and its parent directory locked.
                     90:  * When creating or renaming and LOCKPARENT is specified, the target may
                     91:  * not be ".".  When deleting and LOCKPARENT is specified, the target may
                     92:  * be "."., but the caller must check to ensure it does an vrele and vput
                     93:  * instead of two vputs.
                     94:  *
                     95:  * Overall outline of ufs_lookup:
                     96:  *
                     97:  *     check accessibility of directory
                     98:  *     look for name in cache, if found, then if at end of path
                     99:  *       and deleting or creating, drop it, else return name
                    100:  *     search for name in directory, to found or notfound
                    101:  * notfound:
                    102:  *     if creating, return locked directory, leaving info on available slots
                    103:  *     else return error
                    104:  * found:
                    105:  *     if at end of path and deleting, return information to allow delete
                    106:  *     if at end of path and rewriting (RENAME and LOCKPARENT), lock target
                    107:  *       inode and return info to allow rewrite
                    108:  *     if not at end, add name to cache; if at end and neither creating
                    109:  *       nor deleting, add name to cache
                    110:  */
                    111: int
1.64      thorpej   112: ufs_lookup(void *v)
1.7       christos  113: {
1.1       mycroft   114:        struct vop_lookup_args /* {
                    115:                struct vnode *a_dvp;
                    116:                struct vnode **a_vpp;
                    117:                struct componentname *a_cnp;
1.7       christos  118:        } */ *ap = v;
1.58      mycroft   119:        struct vnode *vdp = ap->a_dvp;  /* vnode for directory being searched */
                    120:        struct inode *dp = VTOI(vdp);   /* inode for directory being searched */
1.1       mycroft   121:        struct buf *bp;                 /* a buffer of directory entries */
1.30      augustss  122:        struct direct *ep;              /* the current directory entry */
1.1       mycroft   123:        int entryoffsetinblock;         /* offset of ep in bp's buffer */
                    124:        enum {NONE, COMPACT, FOUND} slotstatus;
                    125:        doff_t slotoffset;              /* offset of area with free space */
                    126:        int slotsize;                   /* size of area at slotoffset */
                    127:        int slotfreespace;              /* amount of space free in slot */
                    128:        int slotneeded;                 /* size of the entry we're seeking */
                    129:        int numdirpasses;               /* strategy for directory search */
                    130:        doff_t endsearch;               /* offset to end directory search */
                    131:        doff_t prevoff;                 /* prev entry dp->i_offset */
                    132:        struct vnode *pdp;              /* saved dp during symlink work */
                    133:        struct vnode *tdp;              /* returned by VFS_VGET */
                    134:        doff_t enduseful;               /* pointer past last used dir slot */
                    135:        u_long bmask;                   /* block offset mask */
                    136:        int namlen, error;
                    137:        struct vnode **vpp = ap->a_vpp;
                    138:        struct componentname *cnp = ap->a_cnp;
1.76      elad      139:        kauth_cred_t cred = cnp->cn_cred;
1.21      wrstuden  140:        int flags;
1.1       mycroft   141:        int nameiop = cnp->cn_nameiop;
1.58      mycroft   142:        struct ufsmount *ump = dp->i_ump;
                    143:        const int needswap = UFS_MPNEEDSWAP(ump);
                    144:        int dirblksiz = ump->um_dirblksiz;
1.42      yamt      145:        ino_t foundino;
1.1       mycroft   146:
1.21      wrstuden  147:        flags = cnp->cn_flags;
                    148:
1.1       mycroft   149:        bp = NULL;
                    150:        slotoffset = -1;
                    151:        *vpp = NULL;
1.61      rumble    152:        endsearch = 0; /* silence compiler warning */
1.1       mycroft   153:        /*
                    154:         * Check accessiblity of directory.
                    155:         */
1.70      christos  156:        if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_lwp)) != 0)
1.1       mycroft   157:                return (error);
                    158:
1.14      fvdl      159:        if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
                    160:            (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
                    161:                return (EROFS);
                    162:
1.1       mycroft   163:        /*
                    164:         * We now have a segment name to search for, and a directory to search.
                    165:         *
                    166:         * Before tediously performing a linear scan of the directory,
                    167:         * check the name cache to see if the directory/name pair
                    168:         * we are looking for is known already.
                    169:         */
1.81      chs       170:        if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) {
1.26      jdolecek  171:                return (error);
1.81      chs       172:        }
1.1       mycroft   173:
1.89      hannken   174:        fstrans_start(vdp->v_mount, FSTRANS_SHARED);
1.83      hannken   175:
1.1       mycroft   176:        /*
                    177:         * Suppress search for slots unless creating
                    178:         * file and at end of pathname, in which case
                    179:         * we watch for a place to put the new file in
                    180:         * case it doesn't already exist.
                    181:         */
                    182:        slotstatus = FOUND;
                    183:        slotfreespace = slotsize = slotneeded = 0;
                    184:        if ((nameiop == CREATE || nameiop == RENAME) &&
                    185:            (flags & ISLASTCN)) {
                    186:                slotstatus = NONE;
1.58      mycroft   187:                slotneeded = DIRECTSIZ(cnp->cn_namelen);
1.1       mycroft   188:        }
                    189:
                    190:        /*
                    191:         * If there is cached information on a previous search of
                    192:         * this directory, pick up where we last left off.
                    193:         * We cache only lookups as these are the most common
                    194:         * and have the greatest payoff. Caching CREATE has little
                    195:         * benefit as it usually must search the entire directory
                    196:         * to determine that the entry does not exist. Caching the
                    197:         * location of the last DELETE or RENAME has not reduced
                    198:         * profiling time and hence has been removed in the interest
                    199:         * of simplicity.
                    200:         */
1.54      dbj       201:        bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
1.61      rumble    202:
                    203: #ifdef UFS_DIRHASH
                    204:        /*
                    205:         * Use dirhash for fast operations on large directories. The logic
                    206:         * to determine whether to hash the directory is contained within
                    207:         * ufsdirhash_build(); a zero return means that it decided to hash
                    208:         * this directory and it successfully built up the hash table.
                    209:         */
                    210:        if (ufsdirhash_build(dp) == 0) {
                    211:                /* Look for a free slot if needed. */
                    212:                enduseful = dp->i_size;
                    213:                if (slotstatus != FOUND) {
                    214:                        slotoffset = ufsdirhash_findfree(dp, slotneeded,
                    215:                            &slotsize);
                    216:                        if (slotoffset >= 0) {
                    217:                                slotstatus = COMPACT;
                    218:                                enduseful = ufsdirhash_enduseful(dp);
                    219:                                if (enduseful < 0)
                    220:                                        enduseful = dp->i_size;
                    221:                        }
                    222:                }
                    223:                /* Look up the component. */
                    224:                numdirpasses = 1;
                    225:                entryoffsetinblock = 0; /* silence compiler warning */
                    226:                switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
                    227:                    &dp->i_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) {
                    228:                case 0:
                    229:                        ep = (struct direct *)((char *)bp->b_data +
                    230:                            (dp->i_offset & bmask));
                    231:                        goto foundentry;
                    232:                case ENOENT:
                    233:                        dp->i_offset = roundup(dp->i_size, dirblksiz);
                    234:                        goto notfound;
                    235:                default:
                    236:                        /* Something failed; just do a linear search. */
                    237:                        break;
                    238:                }
                    239:        }
                    240: #endif /* UFS_DIRHASH */
                    241:
1.1       mycroft   242:        if (nameiop != LOOKUP || dp->i_diroff == 0 ||
1.43      fvdl      243:            dp->i_diroff >= dp->i_size) {
1.1       mycroft   244:                entryoffsetinblock = 0;
                    245:                dp->i_offset = 0;
                    246:                numdirpasses = 1;
                    247:        } else {
                    248:                dp->i_offset = dp->i_diroff;
                    249:                if ((entryoffsetinblock = dp->i_offset & bmask) &&
1.73      yamt      250:                    (error = ufs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp)))
1.83      hannken   251:                        goto out;
1.1       mycroft   252:                numdirpasses = 2;
                    253:                nchstats.ncs_2passes++;
                    254:        }
                    255:        prevoff = dp->i_offset;
1.43      fvdl      256:        endsearch = roundup(dp->i_size, dirblksiz);
1.1       mycroft   257:        enduseful = 0;
                    258:
                    259: searchloop:
                    260:        while (dp->i_offset < endsearch) {
1.48      yamt      261:                if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
1.87      ad        262:                        preempt();
1.1       mycroft   263:                /*
                    264:                 * If necessary, get the next directory block.
                    265:                 */
                    266:                if ((dp->i_offset & bmask) == 0) {
                    267:                        if (bp != NULL)
                    268:                                brelse(bp);
1.73      yamt      269:                        error = ufs_blkatoff(vdp, (off_t)dp->i_offset, NULL,
1.58      mycroft   270:                            &bp);
1.7       christos  271:                        if (error)
1.83      hannken   272:                                goto out;
1.1       mycroft   273:                        entryoffsetinblock = 0;
                    274:                }
                    275:                /*
                    276:                 * If still looking for a slot, and at a DIRBLKSIZE
                    277:                 * boundary, have to start looking for free space again.
                    278:                 */
                    279:                if (slotstatus == NONE &&
1.40      dbj       280:                    (entryoffsetinblock & (dirblksiz - 1)) == 0) {
1.1       mycroft   281:                        slotoffset = -1;
                    282:                        slotfreespace = 0;
                    283:                }
                    284:                /*
                    285:                 * Get pointer to next entry.
                    286:                 * Full validation checks are slow, so we only check
                    287:                 * enough to insure forward progress through the
                    288:                 * directory. Complete checks can be run by patching
                    289:                 * "dirchk" to be true.
                    290:                 */
1.75      christos  291:                KASSERT(bp != NULL);
1.1       mycroft   292:                ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
                    293:                if (ep->d_reclen == 0 ||
1.7       christos  294:                    (dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) {
1.1       mycroft   295:                        int i;
                    296:
                    297:                        ufs_dirbad(dp, dp->i_offset, "mangled entry");
1.40      dbj       298:                        i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
1.1       mycroft   299:                        dp->i_offset += i;
                    300:                        entryoffsetinblock += i;
                    301:                        continue;
                    302:                }
                    303:
                    304:                /*
                    305:                 * If an appropriate sized slot has not yet been found,
                    306:                 * check to see if one is available. Also accumulate space
                    307:                 * in the current block so that we can determine if
                    308:                 * compaction is viable.
                    309:                 */
                    310:                if (slotstatus != FOUND) {
1.15      bouyer    311:                        int size = ufs_rw16(ep->d_reclen, needswap);
1.1       mycroft   312:
                    313:                        if (ep->d_ino != 0)
1.15      bouyer    314:                                size -= DIRSIZ(FSFMT(vdp), ep, needswap);
1.1       mycroft   315:                        if (size > 0) {
                    316:                                if (size >= slotneeded) {
                    317:                                        slotstatus = FOUND;
                    318:                                        slotoffset = dp->i_offset;
1.15      bouyer    319:                                        slotsize = ufs_rw16(ep->d_reclen,
1.58      mycroft   320:                                            needswap);
1.1       mycroft   321:                                } else if (slotstatus == NONE) {
                    322:                                        slotfreespace += size;
                    323:                                        if (slotoffset == -1)
                    324:                                                slotoffset = dp->i_offset;
                    325:                                        if (slotfreespace >= slotneeded) {
                    326:                                                slotstatus = COMPACT;
                    327:                                                slotsize = dp->i_offset +
1.16      kleink    328:                                                    ufs_rw16(ep->d_reclen,
1.58      mycroft   329:                                                             needswap) -
                    330:                                                    slotoffset;
1.1       mycroft   331:                                        }
                    332:                                }
                    333:                        }
                    334:                }
                    335:
                    336:                /*
                    337:                 * Check for a name match.
                    338:                 */
                    339:                if (ep->d_ino) {
1.15      bouyer    340: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.58      mycroft   341:                        if (FSFMT(vdp) && needswap == 0)
                    342:                                namlen = ep->d_type;
                    343:                        else
1.52      yamt      344:                                namlen = ep->d_namlen;
1.15      bouyer    345: #else
1.62      perry     346:                        if (FSFMT(vdp) && needswap != 0)
1.52      yamt      347:                                namlen = ep->d_type;
                    348:                        else
1.1       mycroft   349:                                namlen = ep->d_namlen;
1.15      bouyer    350: #endif
1.1       mycroft   351:                        if (namlen == cnp->cn_namelen &&
1.18      perry     352:                            !memcmp(cnp->cn_nameptr, ep->d_name,
1.58      mycroft   353:                            (unsigned)namlen)) {
1.61      rumble    354: #ifdef UFS_DIRHASH
                    355: foundentry:
                    356: #endif
1.1       mycroft   357:                                /*
                    358:                                 * Save directory entry's inode number and
                    359:                                 * reclen in ndp->ni_ufs area, and release
                    360:                                 * directory buffer.
                    361:                                 */
1.58      mycroft   362:                                if (!FSFMT(vdp) && ep->d_type == DT_WHT) {
1.3       mycroft   363:                                        slotstatus = FOUND;
                    364:                                        slotoffset = dp->i_offset;
1.16      kleink    365:                                        slotsize = ufs_rw16(ep->d_reclen,
                    366:                                            needswap);
1.3       mycroft   367:                                        dp->i_reclen = slotsize;
1.6       mycroft   368:                                        /*
                    369:                                         * This is used to set dp->i_endoff,
                    370:                                         * which may be used by ufs_direnter2()
                    371:                                         * as a length to truncate the
                    372:                                         * directory to.  Therefore, it must
                    373:                                         * point past the end of the last
                    374:                                         * non-empty directory entry.  We don't
                    375:                                         * know where that is in this case, so
                    376:                                         * we effectively disable shrinking by
                    377:                                         * using the existing size of the
                    378:                                         * directory.
                    379:                                         *
                    380:                                         * Note that we wouldn't expect to
                    381:                                         * shrink the directory while rewriting
                    382:                                         * an existing entry anyway.
                    383:                                         */
                    384:                                        enduseful = endsearch;
1.3       mycroft   385:                                        ap->a_cnp->cn_flags |= ISWHITEOUT;
                    386:                                        numdirpasses--;
                    387:                                        goto notfound;
                    388:                                }
1.42      yamt      389:                                foundino = ufs_rw32(ep->d_ino, needswap);
1.15      bouyer    390:                                dp->i_reclen = ufs_rw16(ep->d_reclen, needswap);
1.1       mycroft   391:                                goto found;
                    392:                        }
                    393:                }
                    394:                prevoff = dp->i_offset;
1.15      bouyer    395:                dp->i_offset += ufs_rw16(ep->d_reclen, needswap);
                    396:                entryoffsetinblock += ufs_rw16(ep->d_reclen, needswap);
1.1       mycroft   397:                if (ep->d_ino)
                    398:                        enduseful = dp->i_offset;
                    399:        }
1.3       mycroft   400: notfound:
1.1       mycroft   401:        /*
                    402:         * If we started in the middle of the directory and failed
                    403:         * to find our target, we must check the beginning as well.
                    404:         */
                    405:        if (numdirpasses == 2) {
                    406:                numdirpasses--;
                    407:                dp->i_offset = 0;
                    408:                endsearch = dp->i_diroff;
                    409:                goto searchloop;
                    410:        }
                    411:        if (bp != NULL)
                    412:                brelse(bp);
                    413:        /*
                    414:         * If creating, and at end of pathname and current
                    415:         * directory has not been removed, then can consider
                    416:         * allowing file to be created.
                    417:         */
1.3       mycroft   418:        if ((nameiop == CREATE || nameiop == RENAME ||
                    419:             (nameiop == DELETE &&
                    420:              (ap->a_cnp->cn_flags & DOWHITEOUT) &&
                    421:              (ap->a_cnp->cn_flags & ISWHITEOUT))) &&
1.27      fvdl      422:            (flags & ISLASTCN) && dp->i_ffs_effnlink != 0) {
1.12      kleink    423:                /*
1.1       mycroft   424:                 * Access for write is interpreted as allowing
                    425:                 * creation of files in the directory.
                    426:                 */
1.70      christos  427:                error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
1.7       christos  428:                if (error)
1.83      hannken   429:                        goto out;
1.1       mycroft   430:                /*
                    431:                 * Return an indication of where the new directory
                    432:                 * entry should be put.  If we didn't find a slot,
                    433:                 * then set dp->i_count to 0 indicating
                    434:                 * that the new slot belongs at the end of the
                    435:                 * directory. If we found a slot, then the new entry
                    436:                 * can be put in the range from dp->i_offset to
                    437:                 * dp->i_offset + dp->i_count.
                    438:                 */
                    439:                if (slotstatus == NONE) {
1.43      fvdl      440:                        dp->i_offset = roundup(dp->i_size, dirblksiz);
1.1       mycroft   441:                        dp->i_count = 0;
1.6       mycroft   442:                        enduseful = dp->i_offset;
1.3       mycroft   443:                } else if (nameiop == DELETE) {
                    444:                        dp->i_offset = slotoffset;
1.40      dbj       445:                        if ((dp->i_offset & (dirblksiz - 1)) == 0)
1.3       mycroft   446:                                dp->i_count = 0;
                    447:                        else
                    448:                                dp->i_count = dp->i_offset - prevoff;
1.1       mycroft   449:                } else {
                    450:                        dp->i_offset = slotoffset;
                    451:                        dp->i_count = slotsize;
                    452:                        if (enduseful < slotoffset + slotsize)
                    453:                                enduseful = slotoffset + slotsize;
                    454:                }
1.40      dbj       455:                dp->i_endoff = roundup(enduseful, dirblksiz);
1.54      dbj       456: #if 0 /* commented out by dbj. none of the on disk fields changed */
1.1       mycroft   457:                dp->i_flag |= IN_CHANGE | IN_UPDATE;
1.54      dbj       458: #endif
1.1       mycroft   459:                /*
                    460:                 * We return with the directory locked, so that
                    461:                 * the parameters we set up above will still be
                    462:                 * valid if we actually decide to do a direnter().
                    463:                 * We return ni_vp == NULL to indicate that the entry
                    464:                 * does not currently exist; we leave a pointer to
                    465:                 * the (locked) directory inode in ndp->ni_dvp.
                    466:                 * The pathname buffer is saved so that the name
                    467:                 * can be obtained later.
                    468:                 *
                    469:                 * NB - if the directory is unlocked, then this
                    470:                 * information cannot be used.
                    471:                 */
                    472:                cnp->cn_flags |= SAVENAME;
1.83      hannken   473:                error = EJUSTRETURN;
                    474:                goto out;
1.1       mycroft   475:        }
                    476:        /*
                    477:         * Insert name into cache (as non-existent) if appropriate.
                    478:         */
                    479:        if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
                    480:                cache_enter(vdp, *vpp, cnp);
1.83      hannken   481:        error = ENOENT;
                    482:        goto out;
1.1       mycroft   483:
                    484: found:
                    485:        if (numdirpasses == 2)
                    486:                nchstats.ncs_pass2++;
                    487:        /*
                    488:         * Check that directory length properly reflects presence
                    489:         * of this entry.
                    490:         */
1.43      fvdl      491:        if (dp->i_offset + DIRSIZ(FSFMT(vdp), ep, needswap) > dp->i_size) {
1.1       mycroft   492:                ufs_dirbad(dp, dp->i_offset, "i_size too small");
1.43      fvdl      493:                dp->i_size = dp->i_offset + DIRSIZ(FSFMT(vdp), ep, needswap);
1.44      kristerw  494:                DIP_ASSIGN(dp, size, dp->i_size);
1.1       mycroft   495:                dp->i_flag |= IN_CHANGE | IN_UPDATE;
                    496:        }
1.33      fvdl      497:        brelse(bp);
1.1       mycroft   498:
                    499:        /*
                    500:         * Found component in pathname.
                    501:         * If the final component of path name, save information
                    502:         * in the cache as to where the entry was found.
                    503:         */
                    504:        if ((flags & ISLASTCN) && nameiop == LOOKUP)
1.40      dbj       505:                dp->i_diroff = dp->i_offset &~ (dirblksiz - 1);
1.1       mycroft   506:
                    507:        /*
                    508:         * If deleting, and at end of pathname, return
                    509:         * parameters which can be used to remove file.
1.81      chs       510:         * Lock the inode, being careful with ".".
1.1       mycroft   511:         */
                    512:        if (nameiop == DELETE && (flags & ISLASTCN)) {
                    513:                /*
                    514:                 * Write access to directory required to delete files.
                    515:                 */
1.70      christos  516:                error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
1.7       christos  517:                if (error)
1.83      hannken   518:                        goto out;
1.1       mycroft   519:                /*
                    520:                 * Return pointer to current entry in dp->i_offset,
                    521:                 * and distance past previous entry (if there
                    522:                 * is a previous entry in this block) in dp->i_count.
                    523:                 * Save directory inode pointer in ndp->ni_dvp for dirremove().
                    524:                 */
1.40      dbj       525:                if ((dp->i_offset & (dirblksiz - 1)) == 0)
1.1       mycroft   526:                        dp->i_count = 0;
                    527:                else
                    528:                        dp->i_count = dp->i_offset - prevoff;
1.42      yamt      529:                if (dp->i_number == foundino) {
1.1       mycroft   530:                        VREF(vdp);
                    531:                        *vpp = vdp;
1.83      hannken   532:                        error = 0;
                    533:                        goto out;
1.1       mycroft   534:                }
1.28      fvdl      535:                if (flags & ISDOTDOT)
                    536:                        VOP_UNLOCK(vdp, 0); /* race to get the inode */
1.46      thorpej   537:                error = VFS_VGET(vdp->v_mount, foundino, &tdp);
1.28      fvdl      538:                if (flags & ISDOTDOT)
                    539:                        vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
1.7       christos  540:                if (error)
1.83      hannken   541:                        goto out;
1.1       mycroft   542:                /*
                    543:                 * If directory is "sticky", then user must own
                    544:                 * the directory, or the file in it, else she
                    545:                 * may not delete it (unless she's root). This
                    546:                 * implements append-only directories.
                    547:                 */
1.43      fvdl      548:                if ((dp->i_mode & ISVTX) &&
1.82      elad      549:                    kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
                    550:                     NULL) != 0 &&
1.76      elad      551:                    kauth_cred_geteuid(cred) != dp->i_uid &&
                    552:                    VTOI(tdp)->i_uid != kauth_cred_geteuid(cred)) {
1.1       mycroft   553:                        vput(tdp);
1.83      hannken   554:                        error = EPERM;
                    555:                        goto out;
1.1       mycroft   556:                }
                    557:                *vpp = tdp;
1.83      hannken   558:                error = 0;
                    559:                goto out;
1.1       mycroft   560:        }
                    561:
                    562:        /*
                    563:         * If rewriting (RENAME), return the inode and the
                    564:         * information required to rewrite the present directory
                    565:         * Must get inode of directory entry to verify it's a
                    566:         * regular file, or empty directory.
                    567:         */
1.81      chs       568:        if (nameiop == RENAME && (flags & ISLASTCN)) {
1.70      christos  569:                error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
1.7       christos  570:                if (error)
1.83      hannken   571:                        goto out;
1.1       mycroft   572:                /*
                    573:                 * Careful about locking second inode.
                    574:                 * This can only occur if the target is ".".
                    575:                 */
1.83      hannken   576:                if (dp->i_number == foundino) {
                    577:                        error = EISDIR;
                    578:                        goto out;
                    579:                }
1.28      fvdl      580:                if (flags & ISDOTDOT)
                    581:                        VOP_UNLOCK(vdp, 0); /* race to get the inode */
1.46      thorpej   582:                error = VFS_VGET(vdp->v_mount, foundino, &tdp);
1.28      fvdl      583:                if (flags & ISDOTDOT)
                    584:                        vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
1.7       christos  585:                if (error)
1.83      hannken   586:                        goto out;
1.1       mycroft   587:                *vpp = tdp;
                    588:                cnp->cn_flags |= SAVENAME;
1.83      hannken   589:                error = 0;
                    590:                goto out;
1.1       mycroft   591:        }
                    592:
                    593:        /*
                    594:         * Step through the translation in the name.  We do not `vput' the
                    595:         * directory because we may need it again if a symbolic link
                    596:         * is relative to the current directory.  Instead we save it
                    597:         * unlocked as "pdp".  We must get the target inode before unlocking
                    598:         * the directory to insure that the inode will not be removed
                    599:         * before we get it.  We prevent deadlock by always fetching
                    600:         * inodes from the root, moving down the directory tree. Thus
                    601:         * when following backward pointers ".." we must unlock the
                    602:         * parent directory before getting the requested directory.
                    603:         * There is a potential race condition here if both the current
                    604:         * and parent directories are removed before the VFS_VGET for the
                    605:         * inode associated with ".." returns.  We hope that this occurs
                    606:         * infrequently since we cannot avoid this race condition without
                    607:         * implementing a sophisticated deadlock detection algorithm.
                    608:         * Note also that this simple deadlock detection scheme will not
                    609:         * work if the file system has any hard links other than ".."
                    610:         * that point backwards in the directory structure.
                    611:         */
                    612:        pdp = vdp;
                    613:        if (flags & ISDOTDOT) {
1.14      fvdl      614:                VOP_UNLOCK(pdp, 0);     /* race to get the inode */
1.46      thorpej   615:                error = VFS_VGET(vdp->v_mount, foundino, &tdp);
1.81      chs       616:                vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY);
1.7       christos  617:                if (error) {
1.83      hannken   618:                        goto out;
1.1       mycroft   619:                }
                    620:                *vpp = tdp;
1.42      yamt      621:        } else if (dp->i_number == foundino) {
1.1       mycroft   622:                VREF(vdp);      /* we want ourself, ie "." */
                    623:                *vpp = vdp;
                    624:        } else {
1.46      thorpej   625:                error = VFS_VGET(vdp->v_mount, foundino, &tdp);
1.7       christos  626:                if (error)
1.83      hannken   627:                        goto out;
1.1       mycroft   628:                *vpp = tdp;
                    629:        }
                    630:
                    631:        /*
                    632:         * Insert name into cache if appropriate.
                    633:         */
                    634:        if (cnp->cn_flags & MAKEENTRY)
                    635:                cache_enter(vdp, *vpp, cnp);
1.83      hannken   636:        error = 0;
                    637:
                    638: out:
                    639:        fstrans_done(vdp->v_mount);
                    640:        return error;
1.1       mycroft   641: }
                    642:
                    643: void
1.64      thorpej   644: ufs_dirbad(struct inode *ip, doff_t offset, const char *how)
1.1       mycroft   645: {
                    646:        struct mount *mp;
                    647:
                    648:        mp = ITOV(ip)->v_mount;
1.66      christos  649:        printf("%s: bad dir ino %llu at offset %d: %s\n",
                    650:            mp->mnt_stat.f_mntonname, (unsigned long long)ip->i_number,
                    651:            offset, how);
1.56      christos  652:        if ((mp->mnt_stat.f_flag & MNT_RDONLY) == 0)
1.1       mycroft   653:                panic("bad dir");
                    654: }
                    655:
                    656: /*
                    657:  * Do consistency checking on a directory entry:
                    658:  *     record length must be multiple of 4
                    659:  *     entry must fit in rest of its DIRBLKSIZ block
                    660:  *     record must be large enough to contain entry
1.67      christos  661:  *     name is not longer than FFS_MAXNAMLEN
1.1       mycroft   662:  *     name must be as long as advertised, and null terminated
                    663:  */
                    664: int
1.64      thorpej   665: ufs_dirbadentry(struct vnode *dp, struct direct *ep, int entryoffsetinblock)
1.1       mycroft   666: {
1.30      augustss  667:        int i;
1.1       mycroft   668:        int namlen;
1.58      mycroft   669:        struct ufsmount *ump = VFSTOUFS(dp->v_mount);
                    670:        const int needswap = UFS_MPNEEDSWAP(ump);
                    671:        int dirblksiz = ump->um_dirblksiz;
1.1       mycroft   672:
1.16      kleink    673: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.58      mycroft   674:        if (FSFMT(dp) && needswap == 0)
                    675:                namlen = ep->d_type;
                    676:        else
1.16      kleink    677:                namlen = ep->d_namlen;
                    678: #else
1.58      mycroft   679:        if (FSFMT(dp) && needswap != 0)
1.16      kleink    680:                namlen = ep->d_type;
                    681:        else
1.58      mycroft   682:                namlen = ep->d_namlen;
1.16      kleink    683: #endif
1.15      bouyer    684:        if ((ufs_rw16(ep->d_reclen, needswap) & 0x3) != 0 ||
                    685:            ufs_rw16(ep->d_reclen, needswap) >
1.40      dbj       686:                dirblksiz - (entryoffsetinblock & (dirblksiz - 1)) ||
1.15      bouyer    687:            ufs_rw16(ep->d_reclen, needswap) <
1.27      fvdl      688:                DIRSIZ(FSFMT(dp), ep, needswap) ||
1.67      christos  689:            namlen > FFS_MAXNAMLEN) {
1.1       mycroft   690:                /*return (1); */
1.20      thorpej   691:                printf("First bad, reclen=%x, DIRSIZ=%lu, namlen=%d, flags=%x "
1.40      dbj       692:                        "entryoffsetinblock=%d, dirblksiz = %d\n",
1.15      bouyer    693:                        ufs_rw16(ep->d_reclen, needswap),
1.20      thorpej   694:                        (u_long)DIRSIZ(FSFMT(dp), ep, needswap),
1.40      dbj       695:                        namlen, dp->v_mount->mnt_flag, entryoffsetinblock,dirblksiz);
1.1       mycroft   696:                goto bad;
                    697:        }
                    698:        if (ep->d_ino == 0)
                    699:                return (0);
                    700:        for (i = 0; i < namlen; i++)
                    701:                if (ep->d_name[i] == '\0') {
                    702:                        /*return (1); */
1.9       christos  703:                        printf("Second bad\n");
1.1       mycroft   704:                        goto bad;
                    705:        }
                    706:        if (ep->d_name[i])
                    707:                goto bad;
                    708:        return (0);
                    709: bad:
                    710:        return (1);
                    711: }
                    712:
                    713: /*
1.27      fvdl      714:  * Construct a new directory entry after a call to namei, using the
                    715:  * parameters that it left in the componentname argument cnp. The
                    716:  * argument ip is the inode to which the new directory entry will refer.
1.1       mycroft   717:  */
1.27      fvdl      718: void
1.64      thorpej   719: ufs_makedirentry(struct inode *ip, struct componentname *cnp,
                    720:     struct direct *newdirp)
1.1       mycroft   721: {
                    722: #ifdef DIAGNOSTIC
                    723:        if ((cnp->cn_flags & SAVENAME) == 0)
1.27      fvdl      724:                panic("makedirentry: missing name");
1.1       mycroft   725: #endif
1.27      fvdl      726:        newdirp->d_ino = ip->i_number;
                    727:        newdirp->d_namlen = cnp->cn_namelen;
1.51      christos  728:        memcpy(newdirp->d_name, cnp->cn_nameptr, (size_t)cnp->cn_namelen);
                    729:        newdirp->d_name[cnp->cn_namelen] = '\0';
1.58      mycroft   730:        if (FSFMT(ITOV(ip)))
                    731:                newdirp->d_type = 0;
                    732:        else
1.43      fvdl      733:                newdirp->d_type = IFTODT(ip->i_mode);
1.3       mycroft   734: }
                    735:
                    736: /*
1.27      fvdl      737:  * Write a directory entry after a call to namei, using the parameters
                    738:  * that it left in nameidata. The argument dirp is the new directory
                    739:  * entry contents. Dvp is a pointer to the directory to be written,
                    740:  * which was left locked by namei. Remaining parameters (dp->i_offset,
                    741:  * dp->i_count) indicate how the space for the new entry is to be obtained.
                    742:  * Non-null bp indicates that a directory is being created (for the
                    743:  * soft dependency code).
1.3       mycroft   744:  */
1.7       christos  745: int
1.64      thorpej   746: ufs_direnter(struct vnode *dvp, struct vnode *tvp, struct direct *dirp,
                    747:     struct componentname *cnp, struct buf *newdirbp)
1.27      fvdl      748: {
1.76      elad      749:        kauth_cred_t cr;
1.70      christos  750:        struct lwp *l;
1.3       mycroft   751:        int newentrysize;
                    752:        struct inode *dp;
                    753:        struct buf *bp;
                    754:        u_int dsize;
                    755:        struct direct *ep, *nep;
1.27      fvdl      756:        int error, ret, blkoff, loc, spacefree, flags;
1.3       mycroft   757:        char *dirbuf;
1.27      fvdl      758:        struct timespec ts;
1.58      mycroft   759:        struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
                    760:        const int needswap = UFS_MPNEEDSWAP(ump);
                    761:        int dirblksiz = ump->um_dirblksiz;
1.3       mycroft   762:
1.27      fvdl      763:        error = 0;
                    764:        cr = cnp->cn_cred;
1.70      christos  765:        l = cnp->cn_lwp;
1.27      fvdl      766:
1.3       mycroft   767:        dp = VTOI(dvp);
1.15      bouyer    768:        newentrysize = DIRSIZ(0, dirp, 0);
1.3       mycroft   769:
1.1       mycroft   770:        if (dp->i_count == 0) {
                    771:                /*
                    772:                 * If dp->i_count is 0, then namei could find no
                    773:                 * space in the directory. Here, dp->i_offset will
                    774:                 * be on a directory block boundary and we will write the
                    775:                 * new entry into a fresh block.
                    776:                 */
1.40      dbj       777:                if (dp->i_offset & (dirblksiz - 1))
1.27      fvdl      778:                        panic("ufs_direnter: newblk");
                    779:                flags = B_CLRBUF;
1.55      yamt      780:                if (!DOINGSOFTDEP(dvp))
1.27      fvdl      781:                        flags |= B_SYNC;
1.69      yamt      782:                if ((error = UFS_BALLOC(dvp, (off_t)dp->i_offset, dirblksiz,
1.27      fvdl      783:                    cr, flags, &bp)) != 0) {
                    784:                        if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
                    785:                                bdwrite(newdirbp);
                    786:                        return (error);
                    787:                }
1.43      fvdl      788:                dp->i_size = dp->i_offset + dirblksiz;
1.44      kristerw  789:                DIP_ASSIGN(dp, size, dp->i_size);
1.27      fvdl      790:                dp->i_flag |= IN_CHANGE | IN_UPDATE;
1.43      fvdl      791:                uvm_vnp_setsize(dvp, dp->i_size);
1.40      dbj       792:                dirp->d_reclen = ufs_rw16(dirblksiz, needswap);
1.15      bouyer    793:                dirp->d_ino = ufs_rw32(dirp->d_ino, needswap);
1.58      mycroft   794:                if (FSFMT(dvp)) {
1.15      bouyer    795: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.16      kleink    796:                        if (needswap == 0) {
1.15      bouyer    797: #else
1.16      kleink    798:                        if (needswap != 0) {
1.15      bouyer    799: #endif
                    800:                                u_char tmp = dirp->d_namlen;
                    801:                                dirp->d_namlen = dirp->d_type;
                    802:                                dirp->d_type = tmp;
                    803:                        }
1.1       mycroft   804:                }
1.58      mycroft   805:                blkoff = dp->i_offset & (ump->um_mountp->mnt_stat.f_iosize - 1);
1.88      christos  806:                memcpy((char *)bp->b_data + blkoff, dirp, newentrysize);
1.61      rumble    807: #ifdef UFS_DIRHASH
                    808:                if (dp->i_dirhash != NULL) {
                    809:                        ufsdirhash_newblk(dp, dp->i_offset);
                    810:                        ufsdirhash_add(dp, dirp, dp->i_offset);
                    811:                        ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff,
                    812:                            dp->i_offset);
                    813:                }
                    814: #endif
1.27      fvdl      815:                if (DOINGSOFTDEP(dvp)) {
                    816:                        /*
                    817:                         * Ensure that the entire newly allocated block is a
                    818:                         * valid directory so that future growth within the
                    819:                         * block does not have to ensure that the block is
                    820:                         * written before the inode.
                    821:                         */
1.40      dbj       822:                        blkoff += dirblksiz;
1.27      fvdl      823:                        while (blkoff < bp->b_bcount) {
                    824:                                ((struct direct *)
1.88      christos  825:                                   ((char *)bp->b_data + blkoff))->d_reclen = dirblksiz;
1.40      dbj       826:                                blkoff += dirblksiz;
1.27      fvdl      827:                        }
1.38      fvdl      828:                        if (softdep_setup_directory_add(bp, dp, dp->i_offset,
                    829:                            ufs_rw32(dirp->d_ino, needswap), newdirbp, 1) == 0) {
                    830:                                bdwrite(bp);
1.78      yamt      831:                                vfs_timestamp(&ts);
1.69      yamt      832:                                return UFS_UPDATE(dvp, &ts, &ts, UPDATE_DIROP);
1.38      fvdl      833:                        }
                    834:                        /* We have just allocated a directory block in an
                    835:                         * indirect block. Rather than tracking when it gets
                    836:                         * claimed by the inode, we simply do a VOP_FSYNC
                    837:                         * now to ensure that it is there (in case the user
                    838:                         * does a future fsync). Note that we have to unlock
                    839:                         * the inode for the entry that we just entered, as
                    840:                         * the VOP_FSYNC may need to lock other inodes which
                    841:                         * can lead to deadlock if we also hold a lock on
                    842:                         * the newly entered node.
                    843:                         */
                    844:                        error = VOP_BWRITE(bp);
                    845:                        if (error != 0)
                    846:                                return (error);
                    847:                        if (tvp != NULL)
                    848:                                VOP_UNLOCK(tvp, 0);
1.79      ad        849:                        error = VOP_FSYNC(dvp, l->l_cred, FSYNC_WAIT, 0, 0, l);
1.38      fvdl      850:                        if (tvp != 0)
                    851:                                vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
                    852:                        return (error);
1.27      fvdl      853:                } else {
1.55      yamt      854:                        error = VOP_BWRITE(bp);
1.27      fvdl      855:                }
1.78      yamt      856:                vfs_timestamp(&ts);
1.69      yamt      857:                ret = UFS_UPDATE(dvp, &ts, &ts, UPDATE_DIROP);
1.27      fvdl      858:                if (error == 0)
                    859:                        return (ret);
1.1       mycroft   860:                return (error);
                    861:        }
                    862:
                    863:        /*
1.27      fvdl      864:         * If dp->i_count is non-zero, then namei found space for the new
                    865:         * entry in the range dp->i_offset to dp->i_offset + dp->i_count
                    866:         * in the directory. To use this space, we may have to compact
                    867:         * the entries located there, by copying them together towards the
                    868:         * beginning of the block, leaving the free space in one usable
                    869:         * chunk at the end.
1.1       mycroft   870:         */
                    871:
                    872:        /*
                    873:         * Increase size of directory if entry eats into new space.
                    874:         * This should never push the size past a new multiple of
                    875:         * DIRBLKSIZE.
                    876:         *
                    877:         * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
                    878:         */
1.43      fvdl      879:        if (dp->i_offset + dp->i_count > dp->i_size) {
                    880:                dp->i_size = dp->i_offset + dp->i_count;
1.44      kristerw  881:                DIP_ASSIGN(dp, size, dp->i_size);
1.54      dbj       882:                dp->i_flag |= IN_CHANGE | IN_UPDATE;
1.43      fvdl      883:        }
1.1       mycroft   884:        /*
                    885:         * Get the block containing the space for the new directory entry.
                    886:         */
1.73      yamt      887:        error = ufs_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, &bp);
1.27      fvdl      888:        if (error) {
                    889:                if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
                    890:                        bdwrite(newdirbp);
1.1       mycroft   891:                return (error);
1.27      fvdl      892:        }
1.1       mycroft   893:        /*
                    894:         * Find space for the new entry. In the simple case, the entry at
                    895:         * offset base will have the space. If it does not, then namei
                    896:         * arranged that compacting the region dp->i_offset to
1.27      fvdl      897:         * dp->i_offset + dp->i_count would yield the space.
1.1       mycroft   898:         */
                    899:        ep = (struct direct *)dirbuf;
1.85      bouyer    900:        dsize = (ep->d_ino != 0) ?  DIRSIZ(FSFMT(dvp), ep, needswap) : 0;
1.15      bouyer    901:        spacefree = ufs_rw16(ep->d_reclen, needswap) - dsize;
                    902:        for (loc = ufs_rw16(ep->d_reclen, needswap); loc < dp->i_count; ) {
1.72      yamt      903:                uint16_t reclen;
                    904:
1.1       mycroft   905:                nep = (struct direct *)(dirbuf + loc);
1.72      yamt      906:
                    907:                /* Trim the existing slot (NB: dsize may be zero). */
                    908:                ep->d_reclen = ufs_rw16(dsize, needswap);
                    909:                ep = (struct direct *)((char *)ep + dsize);
                    910:
                    911:                reclen = ufs_rw16(nep->d_reclen, needswap);
                    912:                loc += reclen;
                    913:                if (nep->d_ino == 0) {
                    914:                        /*
                    915:                         * A mid-block unused entry. Such entries are
                    916:                         * never created by the kernel, but fsck_ffs
                    917:                         * can create them (and it doesn't fix them).
                    918:                         *
                    919:                         * Add up the free space, and initialise the
                    920:                         * relocated entry since we don't memcpy it.
                    921:                         */
                    922:                        spacefree += reclen;
                    923:                        ep->d_ino = 0;
                    924:                        dsize = 0;
                    925:                        continue;
1.1       mycroft   926:                }
1.15      bouyer    927:                dsize = DIRSIZ(FSFMT(dvp), nep, needswap);
1.72      yamt      928:                spacefree += reclen - dsize;
1.61      rumble    929: #ifdef UFS_DIRHASH
                    930:                if (dp->i_dirhash != NULL)
                    931:                        ufsdirhash_move(dp, nep,
                    932:                            dp->i_offset + ((char *)nep - dirbuf),
                    933:                            dp->i_offset + ((char *)ep - dirbuf));
                    934: #endif
1.27      fvdl      935:                if (DOINGSOFTDEP(dvp))
                    936:                        softdep_change_directoryentry_offset(dp, dirbuf,
1.88      christos  937:                            (void *)nep, (void *)ep, dsize);
1.27      fvdl      938:                else
1.88      christos  939:                        memcpy((void *)ep, (void *)nep, dsize);
1.1       mycroft   940:        }
                    941:        /*
1.72      yamt      942:         * Here, `ep' points to a directory entry containing `dsize' in-use
                    943:         * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0,
                    944:         * then the entry is completely unused (dsize == 0). The value
                    945:         * of ep->d_reclen is always indeterminate.
                    946:         *
1.1       mycroft   947:         * Update the pointer fields in the previous entry (if any),
                    948:         * copy in the new entry, and write out the block.
                    949:         */
1.3       mycroft   950:        if (ep->d_ino == 0 ||
1.15      bouyer    951:            (ufs_rw32(ep->d_ino, needswap) == WINO &&
1.18      perry     952:             memcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) {
1.1       mycroft   953:                if (spacefree + dsize < newentrysize)
1.27      fvdl      954:                        panic("ufs_direnter: compact1");
1.3       mycroft   955:                dirp->d_reclen = spacefree + dsize;
1.1       mycroft   956:        } else {
                    957:                if (spacefree < newentrysize)
1.27      fvdl      958:                        panic("ufs_direnter: compact2");
1.3       mycroft   959:                dirp->d_reclen = spacefree;
1.15      bouyer    960:                ep->d_reclen = ufs_rw16(dsize, needswap);
1.1       mycroft   961:                ep = (struct direct *)((char *)ep + dsize);
                    962:        }
1.15      bouyer    963:        dirp->d_reclen = ufs_rw16(dirp->d_reclen, needswap);
                    964:        dirp->d_ino = ufs_rw32(dirp->d_ino, needswap);
1.58      mycroft   965:        if (FSFMT(dvp)) {
1.15      bouyer    966: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.16      kleink    967:                if (needswap == 0) {
1.15      bouyer    968: #else
1.16      kleink    969:                if (needswap != 0) {
1.15      bouyer    970: #endif
                    971:                        u_char tmp = dirp->d_namlen;
                    972:                        dirp->d_namlen = dirp->d_type;
                    973:                        dirp->d_type = tmp;
                    974:                }
1.27      fvdl      975:        }
1.61      rumble    976: #ifdef UFS_DIRHASH
                    977:        if (dp->i_dirhash != NULL && (ep->d_ino == 0 ||
                    978:            dirp->d_reclen == spacefree))
                    979:                ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf));
                    980: #endif
1.88      christos  981:        memcpy((void *)ep, (void *)dirp, (u_int)newentrysize);
1.61      rumble    982: #ifdef UFS_DIRHASH
                    983:        if (dp->i_dirhash != NULL)
                    984:                ufsdirhash_checkblock(dp, dirbuf -
                    985:                    (dp->i_offset & (dirblksiz - 1)),
                    986:                    dp->i_offset & ~(dirblksiz - 1));
                    987: #endif
1.27      fvdl      988:        if (DOINGSOFTDEP(dvp)) {
                    989:                softdep_setup_directory_add(bp, dp,
1.88      christos  990:                    dp->i_offset + (char *)ep - dirbuf,
1.38      fvdl      991:                        ufs_rw32(dirp->d_ino, needswap), newdirbp, 0);
1.27      fvdl      992:                bdwrite(bp);
                    993:        } else {
1.55      yamt      994:                error = VOP_BWRITE(bp);
1.27      fvdl      995:        }
1.1       mycroft   996:        dp->i_flag |= IN_CHANGE | IN_UPDATE;
1.27      fvdl      997:        /*
                    998:         * If all went well, and the directory can be shortened, proceed
                    999:         * with the truncation. Note that we have to unlock the inode for
                   1000:         * the entry that we just entered, as the truncation may need to
                   1001:         * lock other inodes which can lead to deadlock if we also hold a
                   1002:         * lock on the newly entered node.
                   1003:         */
1.43      fvdl     1004:        if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_size) {
1.54      dbj      1005:                if (DOINGSOFTDEP(dvp) && (tvp != NULL))
1.27      fvdl     1006:                        VOP_UNLOCK(tvp, 0);
1.61      rumble   1007: #ifdef UFS_DIRHASH
                   1008:                if (dp->i_dirhash != NULL)
                   1009:                        ufsdirhash_dirtrunc(dp, dp->i_endoff);
                   1010: #endif
1.70      christos 1011:                (void) UFS_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cr, l);
1.54      dbj      1012:                if (DOINGSOFTDEP(dvp) && (tvp != NULL))
1.27      fvdl     1013:                        vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
                   1014:        }
1.1       mycroft  1015:        return (error);
                   1016: }
                   1017:
                   1018: /*
                   1019:  * Remove a directory entry after a call to namei, using
                   1020:  * the parameters which it left in nameidata. The entry
                   1021:  * dp->i_offset contains the offset into the directory of the
                   1022:  * entry to be eliminated.  The dp->i_count field contains the
                   1023:  * size of the previous record in the directory.  If this
                   1024:  * is 0, the first entry is being deleted, so we need only
                   1025:  * zero the inode number to mark the entry as free.  If the
                   1026:  * entry is not the first in the directory, we must reclaim
                   1027:  * the space of the now empty record by adding the record size
                   1028:  * to the size of the previous entry.
                   1029:  */
                   1030: int
1.64      thorpej  1031: ufs_dirremove(struct vnode *dvp, struct inode *ip, int flags, int isrmdir)
1.1       mycroft  1032: {
1.59      mycroft  1033:        struct inode *dp = VTOI(dvp);
1.1       mycroft  1034:        struct direct *ep;
                   1035:        struct buf *bp;
                   1036:        int error;
1.37      lukem    1037: #ifdef FFS_EI
1.59      mycroft  1038:        const int needswap = UFS_MPNEEDSWAP(dp->i_ump);
1.37      lukem    1039: #endif
1.1       mycroft  1040:
1.27      fvdl     1041:        if (flags & DOWHITEOUT) {
1.3       mycroft  1042:                /*
                   1043:                 * Whiteout entry: set d_ino to WINO.
                   1044:                 */
1.73      yamt     1045:                error = ufs_blkatoff(dvp, (off_t)dp->i_offset, (void *)&ep,
1.7       christos 1046:                                     &bp);
                   1047:                if (error)
1.3       mycroft  1048:                        return (error);
1.36      lukem    1049:                ep->d_ino = ufs_rw32(WINO, needswap);
1.3       mycroft  1050:                ep->d_type = DT_WHT;
1.27      fvdl     1051:                goto out;
                   1052:        }
                   1053:
1.73      yamt     1054:        if ((error = ufs_blkatoff(dvp,
1.41      thorpej  1055:            (off_t)(dp->i_offset - dp->i_count), (void *)&ep, &bp)) != 0)
1.3       mycroft  1056:                return (error);
                   1057:
1.61      rumble   1058: #ifdef UFS_DIRHASH
                   1059:        /*
                   1060:         * Remove the dirhash entry. This is complicated by the fact
                   1061:         * that `ep' is the previous entry when dp->i_count != 0.
                   1062:         */
                   1063:        if (dp->i_dirhash != NULL)
                   1064:                ufsdirhash_remove(dp, (dp->i_count == 0) ? ep :
1.85      bouyer   1065:                   (struct direct *)((char *)ep +
1.86      elad     1066:                   ufs_rw16(ep->d_reclen, needswap)), dp->i_offset);
1.61      rumble   1067: #endif
                   1068:
1.1       mycroft  1069:        if (dp->i_count == 0) {
                   1070:                /*
                   1071:                 * First entry in block: set d_ino to zero.
                   1072:                 */
                   1073:                ep->d_ino = 0;
1.27      fvdl     1074:        } else {
                   1075:                /*
                   1076:                 * Collapse new free space into previous entry.
                   1077:                 */
                   1078:                ep->d_reclen =
1.36      lukem    1079:                    ufs_rw16(ufs_rw16(ep->d_reclen, needswap) + dp->i_reclen,
                   1080:                        needswap);
1.27      fvdl     1081:        }
1.61      rumble   1082:
                   1083: #ifdef UFS_DIRHASH
                   1084:        if (dp->i_dirhash != NULL) {
                   1085:                int dirblksiz = ip->i_ump->um_dirblksiz;
                   1086:                ufsdirhash_checkblock(dp, (char *)ep -
                   1087:                    ((dp->i_offset - dp->i_count) & (dirblksiz - 1)),
                   1088:                    dp->i_offset & ~(dirblksiz - 1));
                   1089:        }
                   1090: #endif
                   1091:
1.27      fvdl     1092: out:
                   1093:        if (DOINGSOFTDEP(dvp)) {
1.28      fvdl     1094:                if (ip) {
                   1095:                        ip->i_ffs_effnlink--;
                   1096:                        softdep_change_linkcnt(ip);
1.27      fvdl     1097:                        softdep_setup_remove(bp, dp, ip, isrmdir);
1.28      fvdl     1098:                }
1.27      fvdl     1099:                bdwrite(bp);
                   1100:        } else {
1.28      fvdl     1101:                if (ip) {
                   1102:                        ip->i_ffs_effnlink--;
1.43      fvdl     1103:                        ip->i_nlink--;
1.44      kristerw 1104:                        DIP_ASSIGN(ip, nlink, ip->i_nlink);
1.28      fvdl     1105:                        ip->i_flag |= IN_CHANGE;
                   1106:                }
1.55      yamt     1107:                error = VOP_BWRITE(bp);
1.1       mycroft  1108:        }
                   1109:        dp->i_flag |= IN_CHANGE | IN_UPDATE;
1.80      joerg    1110: #ifdef FFS
1.57      hannken  1111:        /*
                   1112:         * If the last named reference to a snapshot goes away,
                   1113:         * drop its snapshot reference so that it will be reclaimed
                   1114:         * when last open reference goes away.
                   1115:         */
                   1116:        if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 &&
                   1117:            ip->i_ffs_effnlink == 0)
                   1118:                ffs_snapgone(ip);
1.80      joerg    1119: #endif
1.1       mycroft  1120:        return (error);
                   1121: }
                   1122:
                   1123: /*
                   1124:  * Rewrite an existing directory entry to point at the inode
                   1125:  * supplied.  The parameters describing the directory entry are
                   1126:  * set up by a call to namei.
                   1127:  */
                   1128: int
1.64      thorpej  1129: ufs_dirrewrite(struct inode *dp, struct inode *oip, ino_t newinum, int newtype,
                   1130:     int isrmdir, int iflags)
1.1       mycroft  1131: {
                   1132:        struct buf *bp;
                   1133:        struct direct *ep;
                   1134:        struct vnode *vdp = ITOV(dp);
                   1135:        int error;
                   1136:
1.73      yamt     1137:        error = ufs_blkatoff(vdp, (off_t)dp->i_offset, (void *)&ep, &bp);
1.7       christos 1138:        if (error)
1.1       mycroft  1139:                return (error);
1.59      mycroft  1140:        ep->d_ino = ufs_rw32(newinum, UFS_MPNEEDSWAP(dp->i_ump));
1.58      mycroft  1141:        if (!FSFMT(vdp))
1.27      fvdl     1142:                ep->d_type = newtype;
                   1143:        oip->i_ffs_effnlink--;
                   1144:        if (DOINGSOFTDEP(vdp)) {
1.28      fvdl     1145:                softdep_change_linkcnt(oip);
1.27      fvdl     1146:                softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir);
                   1147:                bdwrite(bp);
                   1148:        } else {
1.43      fvdl     1149:                oip->i_nlink--;
1.44      kristerw 1150:                DIP_ASSIGN(oip, nlink, oip->i_nlink);
1.28      fvdl     1151:                oip->i_flag |= IN_CHANGE;
1.55      yamt     1152:                error = VOP_BWRITE(bp);
1.27      fvdl     1153:        }
1.49      pk       1154:        dp->i_flag |= iflags;
1.80      joerg    1155: #ifdef FFS
1.57      hannken  1156:        /*
                   1157:         * If the last named reference to a snapshot goes away,
                   1158:         * drop its snapshot reference so that it will be reclaimed
                   1159:         * when last open reference goes away.
                   1160:         */
                   1161:        if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_ffs_effnlink == 0)
                   1162:                ffs_snapgone(oip);
1.80      joerg    1163: #endif
1.1       mycroft  1164:        return (error);
                   1165: }
                   1166:
                   1167: /*
                   1168:  * Check if a directory is empty or not.
                   1169:  * Inode supplied must be locked.
                   1170:  *
                   1171:  * Using a struct dirtemplate here is not precisely
                   1172:  * what we want, but better than using a struct direct.
                   1173:  *
                   1174:  * NB: does not handle corrupted directories.
                   1175:  */
                   1176: int
1.76      elad     1177: ufs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
1.1       mycroft  1178: {
1.43      fvdl     1179:        doff_t off;
1.1       mycroft  1180:        struct dirtemplate dbuf;
1.30      augustss 1181:        struct direct *dp = (struct direct *)&dbuf;
1.17      thorpej  1182:        int error, namlen;
                   1183:        size_t count;
1.36      lukem    1184:        const int needswap = UFS_IPNEEDSWAP(ip);
1.1       mycroft  1185: #define        MINDIRSIZ (sizeof (struct dirtemplate) / 2)
                   1186:
1.43      fvdl     1187:        for (off = 0; off < ip->i_size;
1.36      lukem    1188:            off += ufs_rw16(dp->d_reclen, needswap)) {
1.88      christos 1189:                error = vn_rdwr(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off,
1.60      skrll    1190:                   UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL);
1.1       mycroft  1191:                /*
                   1192:                 * Since we read MINDIRSIZ, residual must
                   1193:                 * be 0 unless we're at end of file.
                   1194:                 */
                   1195:                if (error || count != 0)
                   1196:                        return (0);
                   1197:                /* avoid infinite loops */
                   1198:                if (dp->d_reclen == 0)
                   1199:                        return (0);
                   1200:                /* skip empty entries */
1.36      lukem    1201:                if (dp->d_ino == 0 || ufs_rw32(dp->d_ino, needswap) == WINO)
1.1       mycroft  1202:                        continue;
                   1203:                /* accept only "." and ".." */
1.16      kleink   1204: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.58      mycroft  1205:                if (FSFMT(ITOV(ip)) && needswap == 0)
                   1206:                        namlen = dp->d_type;
                   1207:                else
1.16      kleink   1208:                        namlen = dp->d_namlen;
                   1209: #else
1.58      mycroft  1210:                if (FSFMT(ITOV(ip)) && needswap != 0)
1.16      kleink   1211:                        namlen = dp->d_type;
                   1212:                else
                   1213:                        namlen = dp->d_namlen;
                   1214: #endif
1.1       mycroft  1215:                if (namlen > 2)
                   1216:                        return (0);
                   1217:                if (dp->d_name[0] != '.')
                   1218:                        return (0);
                   1219:                /*
                   1220:                 * At this point namlen must be 1 or 2.
                   1221:                 * 1 implies ".", 2 implies ".." if second
                   1222:                 * char is also "."
                   1223:                 */
1.27      fvdl     1224:                if (namlen == 1 &&
1.36      lukem    1225:                    ufs_rw32(dp->d_ino, needswap) == ip->i_number)
1.1       mycroft  1226:                        continue;
1.15      bouyer   1227:                if (dp->d_name[1] == '.' &&
1.36      lukem    1228:                    ufs_rw32(dp->d_ino, needswap) == parentino)
1.1       mycroft  1229:                        continue;
                   1230:                return (0);
                   1231:        }
                   1232:        return (1);
                   1233: }
                   1234:
                   1235: /*
                   1236:  * Check if source directory is in the path of the target directory.
                   1237:  * Target is supplied locked, source is unlocked.
                   1238:  * The target is always vput before returning.
                   1239:  */
                   1240: int
1.76      elad     1241: ufs_checkpath(struct inode *source, struct inode *target, kauth_cred_t cred)
1.1       mycroft  1242: {
1.27      fvdl     1243:        struct vnode *vp = ITOV(target);
1.1       mycroft  1244:        int error, rootino, namlen;
                   1245:        struct dirtemplate dirbuf;
1.59      mycroft  1246:        const int needswap = UFS_MPNEEDSWAP(target->i_ump);
1.1       mycroft  1247:
                   1248:        vp = ITOV(target);
                   1249:        if (target->i_number == source->i_number) {
                   1250:                error = EEXIST;
                   1251:                goto out;
                   1252:        }
                   1253:        rootino = ROOTINO;
                   1254:        error = 0;
                   1255:        if (target->i_number == rootino)
                   1256:                goto out;
                   1257:
                   1258:        for (;;) {
                   1259:                if (vp->v_type != VDIR) {
                   1260:                        error = ENOTDIR;
                   1261:                        break;
                   1262:                }
1.88      christos 1263:                error = vn_rdwr(UIO_READ, vp, (void *)&dirbuf,
1.27      fvdl     1264:                    sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1.60      skrll    1265:                    IO_NODELOCKED, cred, NULL, NULL);
1.1       mycroft  1266:                if (error != 0)
                   1267:                        break;
1.16      kleink   1268: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.58      mycroft  1269:                if (FSFMT(vp) && needswap == 0)
                   1270:                        namlen = dirbuf.dotdot_type;
                   1271:                else
1.16      kleink   1272:                        namlen = dirbuf.dotdot_namlen;
                   1273: #else
1.58      mycroft  1274:                if (FSFMT(vp) && needswap != 0)
1.16      kleink   1275:                        namlen = dirbuf.dotdot_type;
                   1276:                else
1.1       mycroft  1277:                        namlen = dirbuf.dotdot_namlen;
1.16      kleink   1278: #endif
1.1       mycroft  1279:                if (namlen != 2 ||
                   1280:                    dirbuf.dotdot_name[0] != '.' ||
                   1281:                    dirbuf.dotdot_name[1] != '.') {
                   1282:                        error = ENOTDIR;
                   1283:                        break;
                   1284:                }
1.15      bouyer   1285:                if (ufs_rw32(dirbuf.dotdot_ino, needswap) == source->i_number) {
1.1       mycroft  1286:                        error = EINVAL;
                   1287:                        break;
                   1288:                }
1.15      bouyer   1289:                if (ufs_rw32(dirbuf.dotdot_ino, needswap) == rootino)
1.1       mycroft  1290:                        break;
                   1291:                vput(vp);
1.15      bouyer   1292:                error = VFS_VGET(vp->v_mount,
1.46      thorpej  1293:                    ufs_rw32(dirbuf.dotdot_ino, needswap), &vp);
1.7       christos 1294:                if (error) {
1.1       mycroft  1295:                        vp = NULL;
                   1296:                        break;
                   1297:                }
                   1298:        }
                   1299:
                   1300: out:
                   1301:        if (error == ENOTDIR)
1.9       christos 1302:                printf("checkpath: .. not a directory\n");
1.1       mycroft  1303:        if (vp != NULL)
                   1304:                vput(vp);
                   1305:        return (error);
                   1306: }
1.73      yamt     1307:
                   1308: #define        UFS_DIRRABLKS 0
                   1309: int ufs_dirrablks = UFS_DIRRABLKS;
                   1310:
                   1311: /*
                   1312:  * ufs_blkatoff: Return buffer with the contents of block "offset" from
                   1313:  * the beginning of directory "vp".  If "res" is non-zero, fill it in with
                   1314:  * a pointer to the remaining space in the directory.
                   1315:  */
                   1316:
                   1317: int
                   1318: ufs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp)
                   1319: {
                   1320:        struct inode *ip;
                   1321:        struct buf *bp;
                   1322:        daddr_t lbn;
                   1323:        int error;
                   1324:        const int dirrablks = ufs_dirrablks;
                   1325:        daddr_t blks[1 + dirrablks];
                   1326:        int blksizes[1 + dirrablks];
                   1327:        int run;
                   1328:        struct mount *mp = vp->v_mount;
                   1329:        const int bshift = mp->mnt_fs_bshift;
                   1330:        const int bsize = 1 << bshift;
                   1331:        off_t eof;
                   1332:
                   1333:        ip = VTOI(vp);
                   1334:        KASSERT(vp->v_size == ip->i_size);
1.74      yamt     1335:        GOP_SIZE(vp, vp->v_size, &eof, 0);
1.73      yamt     1336:        lbn = offset >> bshift;
                   1337:        for (run = 0; run <= dirrablks;) {
                   1338:                const off_t curoff = lbn << bshift;
                   1339:                const int size = MIN(eof - curoff, bsize);
                   1340:
                   1341:                if (size == 0) {
                   1342:                        break;
                   1343:                }
                   1344:                KASSERT(curoff < eof);
                   1345:                blks[run] = lbn;
                   1346:                blksizes[run] = size;
                   1347:                lbn++;
                   1348:                run++;
                   1349:                if (size != bsize) {
                   1350:                        break;
                   1351:                }
                   1352:        }
                   1353:        KASSERT(run >= 1);
                   1354:        error = breadn(vp, blks[0], blksizes[0], &blks[1], &blksizes[1],
                   1355:            run - 1, NOCRED, &bp);
                   1356:        if (error != 0) {
                   1357:                brelse(bp);
                   1358:                *bpp = NULL;
                   1359:                return error;
                   1360:        }
                   1361:        if (res) {
                   1362:                *res = (char *)bp->b_data + (offset & (bsize - 1));
                   1363:        }
                   1364:        *bpp = bp;
                   1365:        return 0;
                   1366: }

CVSweb <webmaster@jp.NetBSD.org>