home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / sys / ufs / ufs_lookup.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-20  |  27.0 KB  |  913 lines

  1. /*
  2.  * Copyright (c) 1989 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  *
  33.  *    @(#)ufs_lookup.c    7.33 (Berkeley) 5/19/91
  34.  */
  35.  
  36. #include "param.h"
  37. #include "namei.h"
  38. #include "buf.h"
  39. #include "file.h"
  40. #include "vnode.h"
  41.  
  42. #include "quota.h"
  43. #include "inode.h"
  44. #include "dir.h"
  45. #include "fs.h"
  46.  
  47. struct    nchstats nchstats;
  48. #ifdef DIAGNOSTIC
  49. int    dirchk = 1;
  50. #else
  51. int    dirchk = 0;
  52. #endif
  53.  
  54. /*
  55.  * Convert a component of a pathname into a pointer to a locked inode.
  56.  * This is a very central and rather complicated routine.
  57.  * If the file system is not maintained in a strict tree hierarchy,
  58.  * this can result in a deadlock situation (see comments in code below).
  59.  *
  60.  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
  61.  * whether the name is to be looked up, created, renamed, or deleted.
  62.  * When CREATE, RENAME, or DELETE is specified, information usable in
  63.  * creating, renaming, or deleting a directory entry may be calculated.
  64.  * If flag has LOCKPARENT or'ed into it and the target of the pathname
  65.  * exists, lookup returns both the target and its parent directory locked.
  66.  * When creating or renaming and LOCKPARENT is specified, the target may
  67.  * not be ".".  When deleting and LOCKPARENT is specified, the target may
  68.  * be "."., but the caller must check to ensure it does an vrele and iput
  69.  * instead of two iputs.
  70.  *
  71.  * Overall outline of ufs_lookup:
  72.  *
  73.  *    check accessibility of directory
  74.  *    look for name in cache, if found, then if at end of path
  75.  *      and deleting or creating, drop it, else return name
  76.  *    search for name in directory, to found or notfound
  77.  * notfound:
  78.  *    if creating, return locked directory, leaving info on available slots
  79.  *    else return error
  80.  * found:
  81.  *    if at end of path and deleting, return information to allow delete
  82.  *    if at end of path and rewriting (RENAME and LOCKPARENT), lock target
  83.  *      inode and return info to allow rewrite
  84.  *    if not at end, add name to cache; if at end and neither creating
  85.  *      nor deleting, add name to cache
  86.  *
  87.  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
  88.  */
  89. ufs_lookup(vdp, ndp, p)
  90.     register struct vnode *vdp;
  91.     register struct nameidata *ndp;
  92.     struct proc *p;
  93. {
  94.     register struct inode *dp;    /* the directory we are searching */
  95.     register struct fs *fs;        /* file system that directory is in */
  96.     struct buf *bp = 0;        /* a buffer of directory entries */
  97.     register struct direct *ep;    /* the current directory entry */
  98.     int entryoffsetinblock;        /* offset of ep in bp's buffer */
  99.     enum {NONE, COMPACT, FOUND} slotstatus;
  100.     int slotoffset = -1;        /* offset of area with free space */
  101.     int slotsize;            /* size of area at slotoffset */
  102.     int slotfreespace;        /* amount of space free in slot */
  103.     int slotneeded;            /* size of the entry we're seeking */
  104.     int numdirpasses;        /* strategy for directory search */
  105.     int endsearch;            /* offset to end directory search */
  106.     int prevoff;            /* ndp->ni_ufs.ufs_offset of previous entry */
  107.     struct inode *pdp;        /* saved dp during symlink work */
  108.     struct inode *tdp;        /* returned by iget */
  109.     off_t enduseful;        /* pointer past last used dir slot */
  110.     int flag;            /* LOOKUP, CREATE, RENAME, or DELETE */
  111.     int lockparent;            /* 1 => lockparent flag is set */
  112.     int wantparent;            /* 1 => wantparent or lockparent flag */
  113.     int error;
  114.  
  115.     ndp->ni_dvp = vdp;
  116.     ndp->ni_vp = NULL;
  117.     dp = VTOI(vdp);
  118.     fs = dp->i_fs;
  119.     lockparent = ndp->ni_nameiop & LOCKPARENT;
  120.     flag = ndp->ni_nameiop & OPMASK;
  121.     wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
  122.  
  123.     /*
  124.      * Check accessiblity of directory.
  125.      */
  126.     if ((dp->i_mode&IFMT) != IFDIR)
  127.         return (ENOTDIR);
  128.     if (error = ufs_access(vdp, VEXEC, ndp->ni_cred, p))
  129.         return (error);
  130.  
  131.     /*
  132.      * We now have a segment name to search for, and a directory to search.
  133.      *
  134.      * Before tediously performing a linear scan of the directory,
  135.      * check the name cache to see if the directory/name pair
  136.      * we are looking for is known already.
  137.      */
  138.     if (error = cache_lookup(ndp)) {
  139.         int vpid;    /* capability number of vnode */
  140.  
  141.         if (error == ENOENT)
  142.             return (error);
  143. #ifdef PARANOID
  144.         if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
  145.             panic("ufs_lookup: .. through root");
  146. #endif
  147.         /*
  148.          * Get the next vnode in the path.
  149.          * See comment below starting `Step through' for
  150.          * an explaination of the locking protocol.
  151.          */
  152.         pdp = dp;
  153.         dp = VTOI(ndp->ni_vp);
  154.         vdp = ndp->ni_vp;
  155.         vpid = vdp->v_id;
  156.         if (pdp == dp) {
  157.             VREF(vdp);
  158.             error = 0;
  159.         } else if (ndp->ni_isdotdot) {
  160.             IUNLOCK(pdp);
  161.             error = vget(vdp);
  162.             if (!error && lockparent && *ndp->ni_next == '\0')
  163.                 ILOCK(pdp);
  164.         } else {
  165.             error = vget(vdp);
  166.             if (!lockparent || error || *ndp->ni_next != '\0')
  167.                 IUNLOCK(pdp);
  168.         }
  169.         /*
  170.          * Check that the capability number did not change
  171.          * while we were waiting for the lock.
  172.          */
  173.         if (!error) {
  174.             if (vpid == vdp->v_id)
  175.                 return (0);
  176.             iput(dp);
  177.             if (lockparent && pdp != dp && *ndp->ni_next == '\0')
  178.                 IUNLOCK(pdp);
  179.         }
  180.         ILOCK(pdp);
  181.         dp = pdp;
  182.         vdp = ITOV(dp);
  183.         ndp->ni_vp = NULL;
  184.     }
  185.  
  186.     /*
  187.      * Suppress search for slots unless creating
  188.      * file and at end of pathname, in which case
  189.      * we watch for a place to put the new file in
  190.      * case it doesn't already exist.
  191.      */
  192.     slotstatus = FOUND;
  193.     if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) {
  194.         slotstatus = NONE;
  195.         slotfreespace = 0;
  196.         slotneeded = ((sizeof (struct direct) - (MAXNAMLEN + 1)) +
  197.             ((ndp->ni_namelen + 1 + 3) &~ 3));
  198.     }
  199.  
  200.     /*
  201.      * If there is cached information on a previous search of
  202.      * this directory, pick up where we last left off.
  203.      * We cache only lookups as these are the most common
  204.      * and have the greatest payoff. Caching CREATE has little
  205.      * benefit as it usually must search the entire directory
  206.      * to determine that the entry does not exist. Caching the
  207.      * location of the last DELETE or RENAME has not reduced
  208.      * profiling time and hence has been removed in the interest
  209.      * of simplicity.
  210.      */
  211.     if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
  212.         ndp->ni_ufs.ufs_offset = 0;
  213.         numdirpasses = 1;
  214.     } else {
  215.         ndp->ni_ufs.ufs_offset = dp->i_diroff;
  216.         entryoffsetinblock = blkoff(fs, ndp->ni_ufs.ufs_offset);
  217.         if (entryoffsetinblock != 0) {
  218.             if (error = blkatoff(dp, ndp->ni_ufs.ufs_offset,
  219.                 (char **)0, &bp))
  220.                 return (error);
  221.         }
  222.         numdirpasses = 2;
  223.         nchstats.ncs_2passes++;
  224.     }
  225.     endsearch = roundup(dp->i_size, DIRBLKSIZ);
  226.     enduseful = 0;
  227.  
  228. searchloop:
  229.     while (ndp->ni_ufs.ufs_offset < endsearch) {
  230.         /*
  231.          * If offset is on a block boundary,
  232.          * read the next directory block.
  233.          * Release previous if it exists.
  234.          */
  235.         if (blkoff(fs, ndp->ni_ufs.ufs_offset) == 0) {
  236.             if (bp != NULL)
  237.                 brelse(bp);
  238.             if (error = blkatoff(dp, ndp->ni_ufs.ufs_offset,
  239.                 (char **)0, &bp))
  240.                 return (error);
  241.             entryoffsetinblock = 0;
  242.         }
  243.         /*
  244.          * If still looking for a slot, and at a DIRBLKSIZE
  245.          * boundary, have to start looking for free space again.
  246.          */
  247.         if (slotstatus == NONE &&
  248.             (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
  249.             slotoffset = -1;
  250.             slotfreespace = 0;
  251.         }
  252.         /*
  253.          * Get pointer to next entry.
  254.          * Full validation checks are slow, so we only check
  255.          * enough to insure forward progress through the
  256.          * directory. Complete checks can be run by patching
  257.          * "dirchk" to be true.
  258.          */
  259.         ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
  260.         if (ep->d_reclen == 0 ||
  261.             dirchk && dirbadentry(ep, entryoffsetinblock)) {
  262.             int i;
  263.  
  264.             dirbad(dp, ndp->ni_ufs.ufs_offset, "mangled entry");
  265.             i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
  266.             ndp->ni_ufs.ufs_offset += i;
  267.             entryoffsetinblock += i;
  268.             continue;
  269.         }
  270.  
  271.         /*
  272.          * If an appropriate sized slot has not yet been found,
  273.          * check to see if one is available. Also accumulate space
  274.          * in the current block so that we can determine if
  275.          * compaction is viable.
  276.          */
  277.         if (slotstatus != FOUND) {
  278.             int size = ep->d_reclen;
  279.  
  280.             if (ep->d_ino != 0)
  281.                 size -= DIRSIZ(ep);
  282.             if (size > 0) {
  283.                 if (size >= slotneeded) {
  284.                     slotstatus = FOUND;
  285.                     slotoffset = ndp->ni_ufs.ufs_offset;
  286.                     slotsize = ep->d_reclen;
  287.                 } else if (slotstatus == NONE) {
  288.                     slotfreespace += size;
  289.                     if (slotoffset == -1)
  290.                         slotoffset =
  291.                               ndp->ni_ufs.ufs_offset;
  292.                     if (slotfreespace >= slotneeded) {
  293.                         slotstatus = COMPACT;
  294.                         slotsize =
  295.                               ndp->ni_ufs.ufs_offset +
  296.                               ep->d_reclen - slotoffset;
  297.                     }
  298.                 }
  299.             }
  300.         }
  301.  
  302.         /*
  303.          * Check for a name match.
  304.          */
  305.         if (ep->d_ino) {
  306.             if (ep->d_namlen == ndp->ni_namelen &&
  307.                 !bcmp(ndp->ni_ptr, ep->d_name,
  308.                 (unsigned)ep->d_namlen)) {
  309.                 /*
  310.                  * Save directory entry's inode number and
  311.                  * reclen in ndp->ni_ufs area, and release
  312.                  * directory buffer.
  313.                  */
  314.                 ndp->ni_ufs.ufs_ino = ep->d_ino;
  315.                 ndp->ni_ufs.ufs_reclen = ep->d_reclen;
  316.                 brelse(bp);
  317.                 goto found;
  318.             }
  319.         }
  320.         prevoff = ndp->ni_ufs.ufs_offset;
  321.         ndp->ni_ufs.ufs_offset += ep->d_reclen;
  322.         entryoffsetinblock += ep->d_reclen;
  323.         if (ep->d_ino)
  324.             enduseful = ndp->ni_ufs.ufs_offset;
  325.     }
  326. /* notfound: */
  327.     /*
  328.      * If we started in the middle of the directory and failed
  329.      * to find our target, we must check the beginning as well.
  330.      */
  331.     if (numdirpasses == 2) {
  332.         numdirpasses--;
  333.         ndp->ni_ufs.ufs_offset = 0;
  334.         endsearch = dp->i_diroff;
  335.         goto searchloop;
  336.     }
  337.     if (bp != NULL)
  338.         brelse(bp);
  339.     /*
  340.      * If creating, and at end of pathname and current
  341.      * directory has not been removed, then can consider
  342.      * allowing file to be created.
  343.      */
  344.     if ((flag == CREATE || flag == RENAME) &&
  345.         *ndp->ni_next == 0 && dp->i_nlink != 0) {
  346.         /*
  347.          * Access for write is interpreted as allowing
  348.          * creation of files in the directory.
  349.          */
  350.         if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
  351.             return (error);
  352.         /*
  353.          * Return an indication of where the new directory
  354.          * entry should be put.  If we didn't find a slot,
  355.          * then set ndp->ni_ufs.ufs_count to 0 indicating
  356.          * that the new slot belongs at the end of the
  357.          * directory. If we found a slot, then the new entry
  358.          * can be put in the range from ndp->ni_ufs.ufs_offset
  359.          * to ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count.
  360.          */
  361.         if (slotstatus == NONE) {
  362.             ndp->ni_ufs.ufs_offset = roundup(dp->i_size, DIRBLKSIZ);
  363.             ndp->ni_ufs.ufs_count = 0;
  364.             enduseful = ndp->ni_ufs.ufs_offset;
  365.         } else {
  366.             ndp->ni_ufs.ufs_offset = slotoffset;
  367.             ndp->ni_ufs.ufs_count = slotsize;
  368.             if (enduseful < slotoffset + slotsize)
  369.                 enduseful = slotoffset + slotsize;
  370.         }
  371.         ndp->ni_ufs.ufs_endoff = roundup(enduseful, DIRBLKSIZ);
  372.         dp->i_flag |= IUPD|ICHG;
  373.         /*
  374.          * We return with the directory locked, so that
  375.          * the parameters we set up above will still be
  376.          * valid if we actually decide to do a direnter().
  377.          * We return ni_vp == NULL to indicate that the entry
  378.          * does not currently exist; we leave a pointer to
  379.          * the (locked) directory inode in ndp->ni_dvp.
  380.          * The pathname buffer is saved so that the name
  381.          * can be obtained later.
  382.          *
  383.          * NB - if the directory is unlocked, then this
  384.          * information cannot be used.
  385.          */
  386.         ndp->ni_nameiop |= SAVENAME;
  387.         if (!lockparent)
  388.             IUNLOCK(dp);
  389.     }
  390.     /*
  391.      * Insert name into cache (as non-existent) if appropriate.
  392.      */
  393.     if (ndp->ni_makeentry && flag != CREATE)
  394.         cache_enter(ndp);
  395.     return (ENOENT);
  396.  
  397. found:
  398.     if (numdirpasses == 2)
  399.         nchstats.ncs_pass2++;
  400.     /*
  401.      * Check that directory length properly reflects presence
  402.      * of this entry.
  403.      */
  404.     if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
  405.         dirbad(dp, ndp->ni_ufs.ufs_offset, "i_size too small");
  406.         dp->i_size = entryoffsetinblock + DIRSIZ(ep);
  407.         dp->i_flag |= IUPD|ICHG;
  408.     }
  409.  
  410.     /*
  411.      * Found component in pathname.
  412.      * If the final component of path name, save information
  413.      * in the cache as to where the entry was found.
  414.      */
  415.     if (*ndp->ni_next == '\0' && flag == LOOKUP)
  416.         dp->i_diroff = ndp->ni_ufs.ufs_offset &~ (DIRBLKSIZ - 1);
  417.  
  418.     /*
  419.      * If deleting, and at end of pathname, return
  420.      * parameters which can be used to remove file.
  421.      * If the wantparent flag isn't set, we return only
  422.      * the directory (in ndp->ni_dvp), otherwise we go
  423.      * on and lock the inode, being careful with ".".
  424.      */
  425.     if (flag == DELETE && *ndp->ni_next == 0) {
  426.         /*
  427.          * Write access to directory required to delete files.
  428.          */
  429.         if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
  430.             return (error);
  431.         /*
  432.          * Return pointer to current entry in ndp->ni_ufs.ufs_offset,
  433.          * and distance past previous entry (if there
  434.          * is a previous entry in this block) in ndp->ni_ufs.ufs_count.
  435.          * Save directory inode pointer in ndp->ni_dvp for dirremove().
  436.          */
  437.         if ((ndp->ni_ufs.ufs_offset&(DIRBLKSIZ-1)) == 0)
  438.             ndp->ni_ufs.ufs_count = 0;
  439.         else
  440.             ndp->ni_ufs.ufs_count = ndp->ni_ufs.ufs_offset - prevoff;
  441.         if (dp->i_number == ndp->ni_ufs.ufs_ino) {
  442.             VREF(vdp);
  443.             ndp->ni_vp = vdp;
  444.             return (0);
  445.         }
  446.         if (error = iget(dp, ndp->ni_ufs.ufs_ino, &tdp))
  447.             return (error);
  448.         /*
  449.          * If directory is "sticky", then user must own
  450.          * the directory, or the file in it, else she
  451.          * may not delete it (unless she's root). This
  452.          * implements append-only directories.
  453.          */
  454.         if ((dp->i_mode & ISVTX) &&
  455.             ndp->ni_cred->cr_uid != 0 &&
  456.             ndp->ni_cred->cr_uid != dp->i_uid &&
  457.             tdp->i_uid != ndp->ni_cred->cr_uid) {
  458.             iput(tdp);
  459.             return (EPERM);
  460.         }
  461.         ndp->ni_vp = ITOV(tdp);
  462.         if (!lockparent)
  463.             IUNLOCK(dp);
  464.         return (0);
  465.     }
  466.  
  467.     /*
  468.      * If rewriting (RENAME), return the inode and the
  469.      * information required to rewrite the present directory
  470.      * Must get inode of directory entry to verify it's a
  471.      * regular file, or empty directory.
  472.      */
  473.     if (flag == RENAME && wantparent && *ndp->ni_next == 0) {
  474.         if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
  475.             return (error);
  476.         /*
  477.          * Careful about locking second inode.
  478.          * This can only occur if the target is ".".
  479.          */
  480.         if (dp->i_number == ndp->ni_ufs.ufs_ino)
  481.             return (EISDIR);
  482.         if (error = iget(dp, ndp->ni_ufs.ufs_ino, &tdp))
  483.             return (error);
  484.         ndp->ni_vp = ITOV(tdp);
  485.         ndp->ni_nameiop |= SAVENAME;
  486.         if (!lockparent)
  487.             IUNLOCK(dp);
  488.         return (0);
  489.     }
  490.  
  491.     /*
  492.      * Step through the translation in the name.  We do not `iput' the
  493.      * directory because we may need it again if a symbolic link
  494.      * is relative to the current directory.  Instead we save it
  495.      * unlocked as "pdp".  We must get the target inode before unlocking
  496.      * the directory to insure that the inode will not be removed
  497.      * before we get it.  We prevent deadlock by always fetching
  498.      * inodes from the root, moving down the directory tree. Thus
  499.      * when following backward pointers ".." we must unlock the
  500.      * parent directory before getting the requested directory.
  501.      * There is a potential race condition here if both the current
  502.      * and parent directories are removed before the `iget' for the
  503.      * inode associated with ".." returns.  We hope that this occurs
  504.      * infrequently since we cannot avoid this race condition without
  505.      * implementing a sophisticated deadlock detection algorithm.
  506.      * Note also that this simple deadlock detection scheme will not
  507.      * work if the file system has any hard links other than ".."
  508.      * that point backwards in the directory structure.
  509.      */
  510.     pdp = dp;
  511.     if (ndp->ni_isdotdot) {
  512.         IUNLOCK(pdp);    /* race to get the inode */
  513.         if (error = iget(dp, ndp->ni_ufs.ufs_ino, &tdp)) {
  514.             ILOCK(pdp);
  515.             return (error);
  516.         }
  517.         if (lockparent && *ndp->ni_next == '\0')
  518.             ILOCK(pdp);
  519.         ndp->ni_vp = ITOV(tdp);
  520.     } else if (dp->i_number == ndp->ni_ufs.ufs_ino) {
  521.         VREF(vdp);    /* we want ourself, ie "." */
  522.         ndp->ni_vp = vdp;
  523.     } else {
  524.         if (error = iget(dp, ndp->ni_ufs.ufs_ino, &tdp))
  525.             return (error);
  526.         if (!lockparent || *ndp->ni_next != '\0')
  527.             IUNLOCK(pdp);
  528.         ndp->ni_vp = ITOV(tdp);
  529.     }
  530.  
  531.     /*
  532.      * Insert name into cache if appropriate.
  533.      */
  534.     if (ndp->ni_makeentry)
  535.         cache_enter(ndp);
  536.     return (0);
  537. }
  538.  
  539.  
  540. dirbad(ip, offset, how)
  541.     struct inode *ip;
  542.     off_t offset;
  543.     char *how;
  544. {
  545.  
  546.     printf("%s: bad dir ino %d at offset %d: %s\n",
  547.         ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
  548.     if (ip->i_fs->fs_ronly == 0)
  549.         panic("bad dir");
  550. }
  551.  
  552. /*
  553.  * Do consistency checking on a directory entry:
  554.  *    record length must be multiple of 4
  555.  *    entry must fit in rest of its DIRBLKSIZ block
  556.  *    record must be large enough to contain entry
  557.  *    name is not longer than MAXNAMLEN
  558.  *    name must be as long as advertised, and null terminated
  559.  */
  560. dirbadentry(ep, entryoffsetinblock)
  561.     register struct direct *ep;
  562.     int entryoffsetinblock;
  563. {
  564.     register int i;
  565.  
  566.     if ((ep->d_reclen & 0x3) != 0 ||
  567.         ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
  568.         ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
  569.         return (1);
  570.     for (i = 0; i < ep->d_namlen; i++)
  571.         if (ep->d_name[i] == '\0')
  572.             return (1);
  573.     return (ep->d_name[i]);
  574. }
  575.  
  576. /*
  577.  * Write a directory entry after a call to namei, using the parameters
  578.  * that it left in nameidata.  The argument ip is the inode which the new
  579.  * directory entry will refer to.  The nameidata field ndp->ni_dvp is a
  580.  * pointer to the directory to be written, which was left locked by namei.
  581.  * Remaining parameters (ndp->ni_ufs.ufs_offset, ndp->ni_ufs.ufs_count)
  582.  * indicate how the space for the new entry is to be obtained.
  583.  */
  584. direnter(ip, ndp)
  585.     struct inode *ip;
  586.     register struct nameidata *ndp;
  587. {
  588.     register struct direct *ep, *nep;
  589.     register struct inode *dp = VTOI(ndp->ni_dvp);
  590.     struct buf *bp;
  591.     int loc, spacefree, error = 0;
  592.     u_int dsize;
  593.     int newentrysize;
  594.     char *dirbuf;
  595.     struct uio auio;
  596.     struct iovec aiov;
  597.     struct direct newdir;
  598.  
  599. #ifdef DIAGNOSTIC
  600.     if ((ndp->ni_nameiop & SAVENAME) == 0)
  601.         panic("direnter: missing name");
  602. #endif
  603.     newdir.d_ino = ip->i_number;
  604.     newdir.d_namlen = ndp->ni_namelen;
  605.     bcopy(ndp->ni_ptr, newdir.d_name, (unsigned)ndp->ni_namelen + 1);
  606.     newentrysize = DIRSIZ(&newdir);
  607.     if (ndp->ni_ufs.ufs_count == 0) {
  608.         /*
  609.          * If ndp->ni_ufs.ufs_count is 0, then namei could find no
  610.          * space in the directory. Here, ndp->ni_ufs.ufs_offset will
  611.          * be on a directory block boundary and we will write the
  612.          * new entry into a fresh block.
  613.          */
  614.         if (ndp->ni_ufs.ufs_offset & (DIRBLKSIZ - 1))
  615.             panic("wdir: newblk");
  616.         auio.uio_offset = ndp->ni_ufs.ufs_offset;
  617.         newdir.d_reclen = DIRBLKSIZ;
  618.         auio.uio_resid = newentrysize;
  619.         aiov.iov_len = newentrysize;
  620.         aiov.iov_base = (caddr_t)&newdir;
  621.         auio.uio_iov = &aiov;
  622.         auio.uio_iovcnt = 1;
  623.         auio.uio_rw = UIO_WRITE;
  624.         auio.uio_segflg = UIO_SYSSPACE;
  625.         auio.uio_procp = (struct proc *)0;
  626.         error = ufs_write(ndp->ni_dvp, &auio, IO_SYNC, ndp->ni_cred);
  627.         if (DIRBLKSIZ > dp->i_fs->fs_fsize) {
  628.             panic("wdir: blksize"); /* XXX - should grow w/balloc */
  629.         } else if (!error) {
  630.             dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
  631.             dp->i_flag |= ICHG;
  632.         }
  633.         return (error);
  634.     }
  635.  
  636.     /*
  637.      * If ndp->ni_ufs.ufs_count is non-zero, then namei found space
  638.      * for the new entry in the range ndp->ni_ufs.ufs_offset to
  639.      * ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count in the directory.
  640.      * To use this space, we may have to compact the entries located
  641.      * there, by copying them together towards the beginning of the
  642.      * block, leaving the free space in one usable chunk at the end.
  643.      */
  644.  
  645.     /*
  646.      * Increase size of directory if entry eats into new space.
  647.      * This should never push the size past a new multiple of
  648.      * DIRBLKSIZE.
  649.      *
  650.      * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
  651.      */
  652.     if (ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count > dp->i_size)
  653.         dp->i_size = ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count;
  654.     /*
  655.      * Get the block containing the space for the new directory entry.
  656.      */
  657.     if (error = blkatoff(dp, ndp->ni_ufs.ufs_offset, (char **)&dirbuf, &bp))
  658.         return (error);
  659.     /*
  660.      * Find space for the new entry. In the simple case, the entry at
  661.      * offset base will have the space. If it does not, then namei
  662.      * arranged that compacting the region ndp->ni_ufs.ufs_offset to
  663.      * ndp->ni_ufs.ufs_offset + ndp->ni_ufs.ufs_count would yield the
  664.      * space.
  665.      */
  666.     ep = (struct direct *)dirbuf;
  667.     dsize = DIRSIZ(ep);
  668.     spacefree = ep->d_reclen - dsize;
  669.     for (loc = ep->d_reclen; loc < ndp->ni_ufs.ufs_count; ) {
  670.         nep = (struct direct *)(dirbuf + loc);
  671.         if (ep->d_ino) {
  672.             /* trim the existing slot */
  673.             ep->d_reclen = dsize;
  674.             ep = (struct direct *)((char *)ep + dsize);
  675.         } else {
  676.             /* overwrite; nothing there; header is ours */
  677.             spacefree += dsize;
  678.         }
  679.         dsize = DIRSIZ(nep);
  680.         spacefree += nep->d_reclen - dsize;
  681.         loc += nep->d_reclen;
  682.         bcopy((caddr_t)nep, (caddr_t)ep, dsize);
  683.     }
  684.     /*
  685.      * Update the pointer fields in the previous entry (if any),
  686.      * copy in the new entry, and write out the block.
  687.      */
  688.     if (ep->d_ino == 0) {
  689.         if (spacefree + dsize < newentrysize)
  690.             panic("wdir: compact1");
  691.         newdir.d_reclen = spacefree + dsize;
  692.     } else {
  693.         if (spacefree < newentrysize)
  694.             panic("wdir: compact2");
  695.         newdir.d_reclen = spacefree;
  696.         ep->d_reclen = dsize;
  697.         ep = (struct direct *)((char *)ep + dsize);
  698.     }
  699.     bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
  700.     error = bwrite(bp);
  701.     dp->i_flag |= IUPD|ICHG;
  702.     if (!error && ndp->ni_ufs.ufs_endoff &&
  703.         ndp->ni_ufs.ufs_endoff < dp->i_size)
  704.         error = itrunc(dp, (u_long)ndp->ni_ufs.ufs_endoff, IO_SYNC);
  705.     return (error);
  706. }
  707.  
  708. /*
  709.  * Remove a directory entry after a call to namei, using
  710.  * the parameters which it left in nameidata. The entry
  711.  * ni_ufs.ufs_offset contains the offset into the directory of the
  712.  * entry to be eliminated.  The ni_ufs.ufs_count field contains the
  713.  * size of the previous record in the directory.  If this
  714.  * is 0, the first entry is being deleted, so we need only
  715.  * zero the inode number to mark the entry as free.  If the
  716.  * entry is not the first in the directory, we must reclaim
  717.  * the space of the now empty record by adding the record size
  718.  * to the size of the previous entry.
  719.  */
  720. dirremove(ndp)
  721.     register struct nameidata *ndp;
  722. {
  723.     register struct inode *dp = VTOI(ndp->ni_dvp);
  724.     struct direct *ep;
  725.     struct buf *bp;
  726.     int error;
  727.  
  728.     if (ndp->ni_ufs.ufs_count == 0) {
  729.         /*
  730.          * First entry in block: set d_ino to zero.
  731.          */
  732.         error = blkatoff(dp, ndp->ni_ufs.ufs_offset, (char **)&ep, &bp);
  733.         if (error)
  734.             return (error);
  735.         ep->d_ino = 0;
  736.         error = bwrite(bp);
  737.         dp->i_flag |= IUPD|ICHG;
  738.         return (error);
  739.     }
  740.     /*
  741.      * Collapse new free space into previous entry.
  742.      */
  743.     if (error = blkatoff(dp, ndp->ni_ufs.ufs_offset - ndp->ni_ufs.ufs_count,
  744.         (char **)&ep, &bp)) {
  745.         return (error);
  746.     }
  747.     ep->d_reclen += ndp->ni_ufs.ufs_reclen;
  748.     error = bwrite(bp);
  749.     dp->i_flag |= IUPD|ICHG;
  750.     return (error);
  751. }
  752.  
  753. /*
  754.  * Rewrite an existing directory entry to point at the inode
  755.  * supplied.  The parameters describing the directory entry are
  756.  * set up by a call to namei.
  757.  */
  758. dirrewrite(dp, ip, ndp)
  759.     struct inode *dp, *ip;
  760.     struct nameidata *ndp;
  761. {
  762.     struct direct *ep;
  763.     struct buf *bp;
  764.     int error;
  765.  
  766.     if (error = blkatoff(dp, ndp->ni_ufs.ufs_offset, (char **)&ep, &bp))
  767.         return (error);
  768.     ep->d_ino = ip->i_number;
  769.     error = bwrite(bp);
  770.     dp->i_flag |= IUPD|ICHG;
  771.     return (error);
  772. }
  773.  
  774. /*
  775.  * Return buffer with contents of block "offset"
  776.  * from the beginning of directory "ip".  If "res"
  777.  * is non-zero, fill it in with a pointer to the
  778.  * remaining space in the directory.
  779.  */
  780. blkatoff(ip, offset, res, bpp)
  781.     struct inode *ip;
  782.     off_t offset;
  783.     char **res;
  784.     struct buf **bpp;
  785. {
  786.     register struct fs *fs = ip->i_fs;
  787.     daddr_t lbn = lblkno(fs, offset);
  788.     int bsize = blksize(fs, ip, lbn);
  789.     struct buf *bp;
  790.     daddr_t bn;
  791.     int error;
  792.  
  793.     *bpp = 0;
  794.     if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) {
  795.         brelse(bp);
  796.         return (error);
  797.     }
  798.     if (res)
  799.         *res = bp->b_un.b_addr + blkoff(fs, offset);
  800.     *bpp = bp;
  801.     return (0);
  802. }
  803.  
  804. /*
  805.  * Check if a directory is empty or not.
  806.  * Inode supplied must be locked.
  807.  *
  808.  * Using a struct dirtemplate here is not precisely
  809.  * what we want, but better than using a struct direct.
  810.  *
  811.  * NB: does not handle corrupted directories.
  812.  */
  813. dirempty(ip, parentino, cred)
  814.     register struct inode *ip;
  815.     ino_t parentino;
  816.     struct ucred *cred;
  817. {
  818.     register off_t off;
  819.     struct dirtemplate dbuf;
  820.     register struct direct *dp = (struct direct *)&dbuf;
  821.     int error, count;
  822. #define    MINDIRSIZ (sizeof (struct dirtemplate) / 2)
  823.  
  824.     for (off = 0; off < ip->i_size; off += dp->d_reclen) {
  825.         error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
  826.            UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
  827.         /*
  828.          * Since we read MINDIRSIZ, residual must
  829.          * be 0 unless we're at end of file.
  830.          */
  831.         if (error || count != 0)
  832.             return (0);
  833.         /* avoid infinite loops */
  834.         if (dp->d_reclen == 0)
  835.             return (0);
  836.         /* skip empty entries */
  837.         if (dp->d_ino == 0)
  838.             continue;
  839.         /* accept only "." and ".." */
  840.         if (dp->d_namlen > 2)
  841.             return (0);
  842.         if (dp->d_name[0] != '.')
  843.             return (0);
  844.         /*
  845.          * At this point d_namlen must be 1 or 2.
  846.          * 1 implies ".", 2 implies ".." if second
  847.          * char is also "."
  848.          */
  849.         if (dp->d_namlen == 1)
  850.             continue;
  851.         if (dp->d_name[1] == '.' && dp->d_ino == parentino)
  852.             continue;
  853.         return (0);
  854.     }
  855.     return (1);
  856. }
  857.  
  858. /*
  859.  * Check if source directory is in the path of the target directory.
  860.  * Target is supplied locked, source is unlocked.
  861.  * The target is always iput() before returning.
  862.  */
  863. checkpath(source, target, cred)
  864.     struct inode *source, *target;
  865.     struct ucred *cred;
  866. {
  867.     struct dirtemplate dirbuf;
  868.     struct inode *ip;
  869.     int error = 0;
  870.  
  871.     ip = target;
  872.     if (ip->i_number == source->i_number) {
  873.         error = EEXIST;
  874.         goto out;
  875.     }
  876.     if (ip->i_number == ROOTINO)
  877.         goto out;
  878.  
  879.     for (;;) {
  880.         if ((ip->i_mode&IFMT) != IFDIR) {
  881.             error = ENOTDIR;
  882.             break;
  883.         }
  884.         error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)&dirbuf,
  885.             sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
  886.             IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
  887.         if (error != 0)
  888.             break;
  889.         if (dirbuf.dotdot_namlen != 2 ||
  890.             dirbuf.dotdot_name[0] != '.' ||
  891.             dirbuf.dotdot_name[1] != '.') {
  892.             error = ENOTDIR;
  893.             break;
  894.         }
  895.         if (dirbuf.dotdot_ino == source->i_number) {
  896.             error = EINVAL;
  897.             break;
  898.         }
  899.         if (dirbuf.dotdot_ino == ROOTINO)
  900.             break;
  901.         iput(ip);
  902.         if (error = iget(ip, dirbuf.dotdot_ino, &ip))
  903.             break;
  904.     }
  905.  
  906. out:
  907.     if (error == ENOTDIR)
  908.         printf("checkpath: .. not a directory\n");
  909.     if (ip != NULL)
  910.         iput(ip);
  911.     return (error);
  912. }
  913.