+ }
+ }
+
+ /*
+ * 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 we want it, and it isn't "." or "..")
+ *
+ * all other cases where making a cache entry would be wrong
+ * have already departed from the code sequence somewhere above.
+ */
+ if (makeentry) {
+ if (ncp != NULL)
+ panic("nami: 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;
+
+ /* remove from old hash chain */
+ remque(ncp);
+
+ /* 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, 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 ||
+ ++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,
+ 0, 1, (int *)0);
+ if (u.u_error)
+ goto bad2;
+ cp = nbp->b_un.b_addr;
+ iput(dp);
+ if (*cp == '/') {
+ irele(pdp);
+ while (*cp == '/')
+ cp++;
+ if ((dp = u.u_rdir) == NULL)
+ dp = rootdir;
+ ILOCK(dp);
+ dp->i_count++;
+ } else {
+ dp = pdp;
+ ILOCK(dp);
+ }
+ fs = dp->i_fs;
+ goto dirloop;
+ }
+
+ /*
+ * Not a symbolic link. If more pathname,
+ * continue at next component, else return.
+ */
+ if (*cp == '/') {
+ while (*cp == '/')
+ cp++;
+ irele(pdp);
+ goto dirloop;
+ }
+ nbp->av_forw = freenamebuf;
+ freenamebuf = nbp;
+ if (lockparent)
+ ndp->ni_pdir = pdp;
+ else
+ irele(pdp);
+ return (dp);
+bad2:
+ irele(pdp);
+bad:
+ if (bp)
+ brelse(bp);
+ if (dp)
+ iput(dp);
+ nbp->av_forw = freenamebuf;
+ freenamebuf = nbp;
+ return (NULL);
+}
+
+
+dirbad(ip, offset, how)
+ struct inode *ip;
+ off_t offset;
+ char *how;
+{
+
+ printf("%s: bad dir ino %d at offset %d: %s\n",
+ ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
+}
+
+/*
+ * Do consistency checking on a directory entry:
+ * record length must be multiple of 4
+ * record length must not be non-negative
+ * entry must fit in rest of its DIRBLKSIZ block
+ * record must be large enough to contain entry
+ * name is not longer than MAXNAMLEN
+ * name must be as long as advertised, and null terminated
+ */
+dirbadentry(ep, entryoffsetinblock)
+ register struct direct *ep;
+ int entryoffsetinblock;
+{
+ register int i;
+
+ if ((ep->d_reclen & 0x3) != 0 || ep->d_reclen <= 0 ||
+ ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
+ ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
+ return (1);
+ for (i = 0; i < ep->d_namlen; i++)
+ if (ep->d_name[i] == '\0')
+ return (1);
+ return (ep->d_name[i]);
+}
+
+/*
+ * Write a directory entry after a call to namei, using the parameters
+ * which it left in the u. area. The argument ip is the inode which
+ * the new directory entry will refer to. The u. area field ndp->ni_pdir is
+ * a pointer to the directory to be written, which was left locked by
+ * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate
+ * how the space for the new entry is to be gotten.
+ */
+direnter(ip, ndp)
+ struct inode *ip;
+ register struct nameidata *ndp;
+{
+ register struct direct *ep, *nep;
+ register struct inode *dp = ndp->ni_pdir;
+ struct buf *bp;
+ int loc, spacefree, error = 0;
+ u_int dsize;
+ int newentrysize;
+ char *dirbuf;
+
+ ndp->ni_dent.d_ino = ip->i_number;
+ newentrysize = DIRSIZ(&ndp->ni_dent);
+ if (ndp->ni_count == 0) {
+ /*
+ * If ndp->ni_count is 0, then namei could find no space in the
+ * directory. In this case ndp->ni_offset will be on a directory
+ * block boundary and we will write the new entry into a fresh
+ * block.
+ */
+ if (ndp->ni_offset&(DIRBLKSIZ-1))
+ panic("wdir: newblk");
+ ndp->ni_dent.d_reclen = DIRBLKSIZ;
+ error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
+ newentrysize, ndp->ni_offset, 1, (int *)0);
+ if (DIRBLKSIZ > dp->i_fs->fs_fsize)
+ panic("wdir: blksize"); /* XXX - should grow w/bmap() */
+ else
+ dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
+ iput(dp);
+ return (error);
+ }
+
+ /*
+ * If ndp->ni_count is non-zero, then namei found space for the new
+ * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count.
+ * in the directory. To use this space, we may have to compact
+ * the entries located there, by copying them together towards
+ * the beginning of the block, leaving the free space in
+ * one usable chunk at the end.
+ */
+
+ /*
+ * Increase size of directory if entry eats into new space.
+ * This should never push the size past a new multiple of
+ * DIRBLKSIZE.
+ *
+ * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
+ */
+ if (ndp->ni_offset + ndp->ni_count > dp->i_size)
+ dp->i_size = ndp->ni_offset + ndp->ni_count;
+
+ /*
+ * Get the block containing the space for the new directory
+ * entry. Should return error by result instead of u.u_error.
+ */
+ bp = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf);
+ if (bp == 0) {
+ iput(dp);
+ return (u.u_error);
+ }
+
+ /*
+ * Find space for the new entry. In the simple case, the
+ * entry at offset base will have the space. If it does
+ * not, then namei arranged that compacting the region
+ * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space.
+ */
+ ep = (struct direct *)dirbuf;
+ dsize = DIRSIZ(ep);
+ spacefree = ep->d_reclen - dsize;
+ for (loc = ep->d_reclen; loc < ndp->ni_count; ) {
+ nep = (struct direct *)(dirbuf + loc);
+ if (ep->d_ino) {
+ /* trim the existing slot */
+ ep->d_reclen = dsize;
+ ep = (struct direct *)((char *)ep + dsize);
+ } else {
+ /* overwrite; nothing there; header is ours */
+ spacefree += dsize;
+ }
+ dsize = DIRSIZ(nep);
+ spacefree += nep->d_reclen - dsize;
+ loc += nep->d_reclen;
+ bcopy((caddr_t)nep, (caddr_t)ep, dsize);
+ }
+ /*
+ * Update the pointer fields in the previous entry (if any),
+ * copy in the new entry, and write out the block.
+ */
+ if (ep->d_ino == 0) {
+ if (spacefree + dsize < newentrysize)
+ panic("wdir: compact1");
+ ndp->ni_dent.d_reclen = spacefree + dsize;
+ } else {
+ if (spacefree < newentrysize)
+ panic("wdir: compact2");
+ ndp->ni_dent.d_reclen = spacefree;
+ ep->d_reclen = dsize;
+ ep = (struct direct *)((char *)ep + dsize);
+ }
+ bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize);
+ bwrite(bp);
+ dp->i_flag |= IUPD|ICHG;
+ if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
+ itrunc(dp, ndp->ni_endoff);