+ slotstatus = FOUND;
+ if (flag == CREATE && *cp == 0) {
+ slotstatus = NONE;
+ slotfreespace = 0;
+ slotneeded = DIRSIZ(&ndp->ni_dent);
+ }
+ /*
+ * If this is the same directory that this process
+ * previously searched, pick up where we last left off.
+ * We cache only lookups as these are the most common
+ * and have the greatest payoff. Caching CREATE has little
+ * benefit as it usually must search the entire directory
+ * to determine that the entry does not exist. Caching the
+ * location of the last DELETE has not reduced profiling time
+ * and hence has been removed in the interest of simplicity.
+ */
+ if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber ||
+ dp->i_dev != u.u_ncache.nc_dev) {
+ ndp->ni_offset = 0;
+ numdirpasses = 1;
+ } else {
+ if (u.u_ncache.nc_prevoffset > dp->i_size)
+ u.u_ncache.nc_prevoffset = 0;
+ ndp->ni_offset = u.u_ncache.nc_prevoffset;
+ entryoffsetinblock = blkoff(fs, ndp->ni_offset);
+ if (entryoffsetinblock != 0) {
+ bp = blkatoff(dp, ndp->ni_offset, (char **)0);
+ if (bp == 0)
+ goto bad;
+ }
+ numdirpasses = 2;
+ nchstats.ncs_2passes++;
+ }
+ endsearch = roundup(dp->i_size, DIRBLKSIZ);
+ enduseful = 0;
+
+searchloop:
+ while (ndp->ni_offset < endsearch) {
+ /*
+ * If offset is on a block boundary,
+ * read the next directory block.
+ * Release previous if it exists.
+ */
+ if (blkoff(fs, ndp->ni_offset) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ bp = blkatoff(dp, ndp->ni_offset, (char **)0);
+ if (bp == 0)
+ goto bad;
+ entryoffsetinblock = 0;
+ }
+ /*
+ * If still looking for a slot, and at a DIRBLKSIZE
+ * boundary, have to start looking for free space again.
+ */
+ if (slotstatus == NONE &&
+ (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
+ slotoffset = -1;
+ slotfreespace = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ * Full validation checks are slow, so we only check
+ * enough to insure forward progress through the
+ * directory. Complete checks can be run by patching
+ * "dirchk" to be true.
+ */
+ ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
+ if (ep->d_reclen == 0 ||
+ dirchk && dirbadentry(ep, entryoffsetinblock)) {
+ dirbad(dp, ndp->ni_offset, "mangled entry");
+ i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
+ ndp->ni_offset += i;
+ entryoffsetinblock += i;
+ continue;
+ }
+
+ /*
+ * If an appropriate sized slot has not yet been found,
+ * check to see if one is available. Also accumulate space
+ * in the current block so that we can determine if
+ * compaction is viable.
+ */
+ if (slotstatus != FOUND) {
+ int size = ep->d_reclen;
+
+ if (ep->d_ino != 0)
+ size -= DIRSIZ(ep);
+ if (size > 0) {
+ if (size >= slotneeded) {
+ slotstatus = FOUND;
+ slotoffset = ndp->ni_offset;
+ slotsize = ep->d_reclen;
+ } else if (slotstatus == NONE) {
+ slotfreespace += size;
+ if (slotoffset == -1)
+ slotoffset = ndp->ni_offset;
+ if (slotfreespace >= slotneeded) {
+ slotstatus = COMPACT;
+ slotsize = ndp->ni_offset +
+ ep->d_reclen - slotoffset;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check for a name match.
+ */
+ if (ep->d_ino) {
+ if (ep->d_namlen == ndp->ni_dent.d_namlen &&
+ !bcmp(ndp->ni_dent.d_name, ep->d_name,
+ (unsigned)ep->d_namlen))
+ goto found;
+ }
+ prevoff = ndp->ni_offset;
+ ndp->ni_offset += ep->d_reclen;
+ entryoffsetinblock += ep->d_reclen;
+ if (ep->d_ino)
+ enduseful = ndp->ni_offset;
+ }
+/* notfound: */
+ /*
+ * If we started in the middle of the directory and failed
+ * to find our target, we must check the beginning as well.
+ */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ ndp->ni_offset = 0;
+ endsearch = u.u_ncache.nc_prevoffset;
+ goto searchloop;
+ }
+ /*
+ * If creating, and at end of pathname and current
+ * directory has not been removed, then can consider
+ * allowing file to be created.
+ */
+ if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) {
+ /*
+ * Access for write is interpreted as allowing
+ * creation of files in the directory.
+ */
+ if (access(dp, IWRITE))
+ goto bad;
+ /*
+ * Return an indication of where the new directory
+ * entry should be put. If we didn't find a slot,
+ * then set ndp->ni_count to 0 indicating that the new
+ * slot belongs at the end of the directory. If we found
+ * a slot, then the new entry can be put in the range
+ * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count)
+ */
+ if (slotstatus == NONE) {
+ ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ);
+ ndp->ni_count = 0;
+ enduseful = ndp->ni_offset;
+ } else {
+ ndp->ni_offset = slotoffset;
+ ndp->ni_count = slotsize;
+ if (enduseful < slotoffset + slotsize)
+ enduseful = slotoffset + slotsize;
+ }
+ ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ);
+ dp->i_flag |= IUPD|ICHG;
+ if (bp)
+ brelse(bp);
+ FREE(nbp, M_NAMEI);
+ /*
+ * We return with the directory locked, so that
+ * the parameters we set up above will still be
+ * valid if we actually decide to do a direnter().
+ * We return NULL to indicate that the entry doesn't
+ * currently exist, leaving a pointer to the (locked)
+ * directory inode in ndp->ni_pdir.
+ */
+ ndp->ni_pdir = dp;
+ return (NULL);
+ }
+ u.u_error = ENOENT;
+ goto bad;
+found:
+ if (numdirpasses == 2)
+ nchstats.ncs_pass2++;
+ /*
+ * Check that directory length properly reflects presence
+ * of this entry.
+ */
+ if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
+ dirbad(dp, ndp->ni_offset, "i_size too small");
+ dp->i_size = entryoffsetinblock + DIRSIZ(ep);
+ dp->i_flag |= IUPD|ICHG;
+ }
+
+ /*
+ * Found component in pathname.
+ * If the final component of path name, save information
+ * in the cache as to where the entry was found.
+ */
+ if (*cp == '\0' && flag == LOOKUP) {
+ u.u_ncache.nc_prevoffset = ndp->ni_offset &~ (DIRBLKSIZ - 1);
+ u.u_ncache.nc_inumber = dp->i_number;
+ u.u_ncache.nc_dev = dp->i_dev;
+ }
+ /*
+ * Save directory entry's inode number and reclen in ndp->ni_dent,
+ * and release directory buffer.
+ */
+ ndp->ni_dent.d_ino = ep->d_ino;
+ ndp->ni_dent.d_reclen = ep->d_reclen;
+ brelse(bp);