+ /*
+ * Insert name into cache (as non-existent) if appropriate.
+ */
+ if (ndp->ni_makeentry && flag != CREATE)
+ cache_enter(ndp);
+ return (ENOENT);
+
+found:
+ if (numdirpasses == 2)
+ nchstats.ncs_pass2++;
+ /*
+ * Check that directory length properly reflects presence
+ * of this entry.
+ */
+ if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
+ ufs_dirbad(dp, ndp->ni_ufs.ufs_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 (*ndp->ni_next == '\0' && flag == LOOKUP)
+ dp->i_diroff = ndp->ni_ufs.ufs_offset &~ (DIRBLKSIZ - 1);
+
+ /*
+ * If deleting, and at end of pathname, return
+ * parameters which can be used to remove file.
+ * If the wantparent flag isn't set, we return only
+ * the directory (in ndp->ni_dvp), otherwise we go
+ * on and lock the inode, being careful with ".".
+ */
+ if (flag == DELETE && *ndp->ni_next == 0) {
+ /*
+ * Write access to directory required to delete files.
+ */
+ if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
+ return (error);
+ /*
+ * Return pointer to current entry in ndp->ni_ufs.ufs_offset,
+ * and distance past previous entry (if there
+ * is a previous entry in this block) in ndp->ni_ufs.ufs_count.
+ * Save directory inode pointer in ndp->ni_dvp for dirremove().
+ */
+ if ((ndp->ni_ufs.ufs_offset&(DIRBLKSIZ-1)) == 0)
+ ndp->ni_ufs.ufs_count = 0;
+ else
+ ndp->ni_ufs.ufs_count =
+ ndp->ni_ufs.ufs_offset - prevoff;
+ if (dp->i_number == ndp->ni_ufs.ufs_ino) {
+ VREF(vdp);
+ ndp->ni_vp = vdp;
+ return (0);
+ }
+ if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp))
+ return (error);
+ /*
+ * If directory is "sticky", then user must own
+ * the directory, or the file in it, else she
+ * may not delete it (unless she's root). This
+ * implements append-only directories.
+ */
+ if ((dp->i_mode & ISVTX) &&
+ ndp->ni_cred->cr_uid != 0 &&
+ ndp->ni_cred->cr_uid != dp->i_uid &&
+ VTOI(tdp)->i_uid != ndp->ni_cred->cr_uid) {
+ vput(tdp);
+ return (EPERM);
+ }
+ ndp->ni_vp = tdp;
+ if (!lockparent)
+ IUNLOCK(dp);
+ return (0);
+ }
+
+ /*
+ * If rewriting (RENAME), return the inode and the
+ * information required to rewrite the present directory
+ * Must get inode of directory entry to verify it's a
+ * regular file, or empty directory.
+ */
+ if (flag == RENAME && wantparent && *ndp->ni_next == 0) {
+ if (error = ufs_access(vdp, VWRITE, ndp->ni_cred, p))
+ return (error);
+ /*
+ * Careful about locking second inode.
+ * This can only occur if the target is ".".
+ */
+ if (dp->i_number == ndp->ni_ufs.ufs_ino)
+ return (EISDIR);
+ if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp))
+ return (error);
+ ndp->ni_vp = tdp;
+ ndp->ni_nameiop |= SAVENAME;
+ if (!lockparent)
+ IUNLOCK(dp);
+ return (0);
+ }
+
+ /*
+ * Step through the translation in the name. We do not `iput' the
+ * directory because we may need it again if a symbolic link
+ * is relative to the current directory. Instead we save it
+ * unlocked as "pdp". We must get the target inode before unlocking
+ * the directory to insure that the inode will not be removed
+ * before we get it. We prevent deadlock by always fetching
+ * inodes from the root, moving down the directory tree. Thus
+ * when following backward pointers ".." we must unlock the
+ * parent directory before getting the requested directory.
+ * There is a potential race condition here if both the current
+ * and parent directories are removed before the `iget' for the
+ * inode associated with ".." returns. We hope that this occurs
+ * infrequently since we cannot avoid this race condition without
+ * implementing a sophisticated deadlock detection algorithm.
+ * Note also that this simple deadlock detection scheme will not
+ * work if the file system has any hard links other than ".."
+ * that point backwards in the directory structure.
+ */
+ pdp = dp;
+ if (ndp->ni_isdotdot) {
+ IUNLOCK(pdp); /* race to get the inode */
+ if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp)) {
+ ILOCK(pdp);
+ return (error);
+ }
+ if (lockparent && *ndp->ni_next == '\0')
+ ILOCK(pdp);
+ ndp->ni_vp = tdp;
+ } else if (dp->i_number == ndp->ni_ufs.ufs_ino) {
+ VREF(vdp); /* we want ourself, ie "." */
+ ndp->ni_vp = vdp;
+ } else {
+ if (error = VOP_VGET(vdp, ndp->ni_ufs.ufs_ino, &tdp))
+ return (error);
+ if (!lockparent || *ndp->ni_next != '\0')
+ IUNLOCK(pdp);
+ ndp->ni_vp = tdp;
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (ndp->ni_makeentry)
+ cache_enter(ndp);
+ return (0);
+}
+
+void
+ufs_dirbad(ip, offset, how)
+ struct inode *ip;
+ off_t offset;
+ char *how;
+{
+ struct mount *mp;
+
+ mp = ITOV(ip)->v_mount;
+ (void)printf("%s: bad dir ino %d at offset %d: %s\n",
+ mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
+ if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
+ panic("bad dir");