+ }
+
+ /*
+ * 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 == CREATE && lockparent) && *cp == 0) {
+ if (access(dp, IWRITE))
+ goto bad;
+ ndp->ni_pdir = dp; /* for dirrewrite() */
+ /*
+ * Careful about locking second inode.
+ * This can only occur if the target is ".".
+ */
+ if (dp->i_number == ndp->ni_dent.d_ino) {
+ u.u_error = EISDIR; /* XXX */
+ goto bad;
+ }
+ dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
+ if (dp == NULL) {
+ iput(ndp->ni_pdir);
+ goto bad;
+ }
+ nbp->av_forw = freenamebuf;
+ freenamebuf = nbp;
+ return (dp);
+ }
+
+ /*
+ * Check for symbolic link, which may require us to massage the
+ * name before we continue translation. We do not `iput' the
+ * directory because we may need it again if the 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 (isdotdot) {
+ IUNLOCK(pdp); /* race to get the inode */
+ dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
+ if (dp == NULL)
+ goto bad2;
+ } else if (dp->i_number == ndp->ni_dent.d_ino) {
+ dp->i_count++; /* we want ourself, ie "." */
+ } else {
+ dp = iget(dp->i_dev, fs, ndp->ni_dent.d_ino);
+ IUNLOCK(pdp);
+ if (dp == NULL)
+ goto bad2;
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (makeentry) {
+ if (ncp != NULL)
+ panic("namei: duplicating cache");
+ /*
+ * Free the cache slot at head of lru chain.
+ */
+ if (ncp = nchhead) {
+ /* remove from lru chain */
+ *ncp->nc_prev = ncp->nc_nxt;
+ if (ncp->nc_nxt)
+ ncp->nc_nxt->nc_prev = ncp->nc_prev;
+ else
+ nchtail = ncp->nc_prev;
+ remque(ncp); /* remove from old hash chain */
+ /* grab the inode we just found */
+ ncp->nc_ip = dp;
+ /* fill in cache info */
+ ncp->nc_ino = pdp->i_number; /* parents inum */
+ ncp->nc_dev = pdp->i_dev; /* & device */
+ ncp->nc_idev = dp->i_dev; /* our device */
+ ncp->nc_id = dp->i_id; /* identifier */
+ ncp->nc_nlen = ndp->ni_dent.d_namlen;
+ bcopy(ndp->ni_dent.d_name, ncp->nc_name,
+ (unsigned)ncp->nc_nlen);
+ /* link at end of lru chain */
+ ncp->nc_nxt = NULL;
+ ncp->nc_prev = nchtail;
+ *nchtail = ncp;
+ nchtail = &ncp->nc_nxt;
+ /* and insert on hash chain */
+ insque(ncp, nhp);
+ }
+ }
+
+haveino:
+ fs = dp->i_fs;
+
+ /*
+ * Check for symbolic link
+ */
+ if ((dp->i_mode & IFMT) == IFLNK &&
+ ((ndp->ni_nameiop & FOLLOW) || *cp == '/')) {
+ u_int pathlen = strlen(cp) + 1;
+
+ if (dp->i_size + pathlen >= MAXPATHLEN - 1) {
+ u.u_error = ENAMETOOLONG;
+ goto bad2;
+ }
+ if (++nlink > MAXSYMLINKS) {
+ u.u_error = ELOOP;
+ goto bad2;
+ }
+ ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
+ u.u_error =
+ rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size,
+ (off_t)0, 1, (int *)0);
+ if (u.u_error)
+ goto bad2;
+ cp = nbp->b_un.b_addr;
+ iput(dp);