+ ep->d_reclen += u.u_dent.d_reclen;
+ bwrite(bp);
+ dp->i_flag |= IUPD|ICHG;
+ }
+ return (1);
+}
+
+/*
+ * Rewrite an existing directory entry to point at the inode
+ * supplied. The parameters describing the directory entry are
+ * set up by a call to namei.
+ */
+dirrewrite(dp, ip)
+ struct inode *dp, *ip;
+{
+
+ u.u_dent.d_ino = ip->i_number;
+ u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
+ (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
+ iput(dp);
+}
+
+/*
+ * Return buffer with contents of block "offset"
+ * from the beginning of directory "ip". If "res"
+ * is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+struct buf *
+blkatoff(ip, offset, res)
+ struct inode *ip;
+ off_t offset;
+ char **res;
+{
+ register struct fs *fs = ip->i_fs;
+ daddr_t lbn = lblkno(fs, offset);
+ int base = blkoff(fs, offset);
+ int bsize = blksize(fs, ip, lbn);
+ daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
+ register struct buf *bp;
+
+ if (u.u_error)
+ return (0);
+ bp = bread(ip->i_dev, bn, bsize);
+ if (bp->b_flags & B_ERROR) {
+ brelse(bp);
+ return (0);
+ }
+ if (res)
+ *res = bp->b_un.b_addr + base;
+ return (bp);
+}
+
+/*
+ * Check if a directory is empty or not.
+ * Inode supplied must be locked.
+ *
+ * Using a struct dirtemplate here is not precisely
+ * what we want, but better than using a struct direct.
+ *
+ * NB: does not handle corrupted directories.
+ */
+dirempty(ip)
+ register struct inode *ip;
+{
+ register off_t off;
+ struct dirtemplate dbuf;
+ register struct direct *dp = (struct direct *)&dbuf;
+ int error, count;
+#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
+
+ for (off = 0; off < ip->i_size; off += dp->d_reclen) {
+ error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ,
+ off, 1, &count);
+ /*
+ * Since we read MINDIRSIZ, residual must
+ * be 0 unless we're at end of file.
+ */
+ if (error || count != 0)
+ return (0);
+ /* skip empty entries */
+ if (dp->d_ino == 0)
+ continue;
+ /* accept only "." and ".." */
+ if (dp->d_namlen > 2)
+ return (0);
+ if (dp->d_name[0] != '.')
+ return (0);
+ /*
+ * At this point d_namlen must be 1 or 2.
+ * 1 implies ".", 2 implies ".." if second
+ * char is also "."
+ */
+ if (dp->d_namlen == 1 || dp->d_name[1] == '.')
+ continue;
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Check if source directory is in the path of the target directory.
+ * Target is supplied locked, source is unlocked.
+ * The target is always iput() before returning.
+ */
+checkpath(source, target)
+ struct inode *source, *target;
+{
+ struct dirtemplate dirbuf;
+ register struct inode *ip;
+ int error = 0;
+
+ ip = target;
+ if (ip->i_number == source->i_number) {
+ error = EEXIST;
+ goto out;
+ }
+ if (ip->i_number == ROOTINO)
+ goto out;
+
+ for (;;) {
+ if ((ip->i_mode&IFMT) != IFDIR) {
+ error = ENOTDIR;
+ break;
+ }
+ error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ if (error != 0)
+ break;
+ if (dirbuf.dotdot_namlen != 2 ||
+ bcmp(dirbuf.dotdot_name, "..", 3) != 0) {
+ error = ENOTDIR;
+ break;
+ }
+ if (dirbuf.dotdot_ino == source->i_number) {
+ error = EINVAL;
+ break;
+ }
+ if (dirbuf.dotdot_ino == ROOTINO)
+ break;
+ iput(ip);
+ ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino);
+ if (ip == NULL) {
+ error = u.u_error;
+ break;
+ }
+ }
+
+out:
+ if (error == ENOTDIR)
+ printf("checkpath: .. not a directory\n");
+ if (ip != NULL)
+ iput(ip);
+ return (error);
+}
+
+/*
+ * Name cache initialization, from main() when we are booting
+ */
+nchinit()
+{
+ register union nchash *nchp;
+ register struct nch *ncp;
+
+ nchhead = 0;
+ nchtail = &nchhead;
+
+ for (ncp = nch; ncp < &nch[nchsize]; ncp++) {
+ ncp->nc_forw = ncp; /* hash chain */
+ ncp->nc_back = ncp;
+
+ ncp->nc_nxt = NULL; /* lru chain */
+ *nchtail = ncp;
+ ncp->nc_prev = nchtail;
+ nchtail = &ncp->nc_nxt;
+
+ /* all else is zero already */
+ }
+
+ for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) {
+ nchp->nch_head[0] = nchp;
+ nchp->nch_head[1] = nchp;
+ }
+}
+
+/*
+ * Cache flush, called when filesys is umounted to
+ * remove entries that would now be invalid
+ *
+ * The line "nxtcp = nchhead" near the end is to avoid potential problems
+ * if the cache lru chain is modified while we are dumping the
+ * inode. This makes the algorithm O(n^2), but do you think I care?
+ */
+nchinval(dev)
+ register dev_t dev;
+{
+ register struct nch *ncp, *nxtcp;
+
+ for (ncp = nchhead; ncp; ncp = nxtcp) {
+ nxtcp = ncp->nc_nxt;
+
+ if (ncp->nc_ip == NULL ||
+ (ncp->nc_idev != dev && ncp->nc_dev != dev))
+ continue;
+
+ ncp->nc_idev = NODEV;
+ ncp->nc_dev = NODEV;
+ ncp->nc_ino = 0;
+
+ /* remove the entry from its hash chain */
+ remque(ncp);
+ /* and make a dummy one */
+ ncp->nc_forw = ncp;
+ ncp->nc_back = ncp;
+
+ /* delete this entry from LRU chain */
+ *ncp->nc_prev = nxtcp;
+ if (nxtcp)
+ nxtcp->nc_prev = ncp->nc_prev;
+ else
+ nchtail = ncp->nc_prev;
+
+ /* free the inode we had */
+ irele(ncp->nc_ip);
+ ncp->nc_ip = NULL;
+
+ /* cause rescan of list, it may have altered */
+ nxtcp = nchhead;
+ /* put the now-free entry at head of LRU */
+ ncp->nc_nxt = nxtcp;
+ ncp->nc_prev = &nchhead;
+ nxtcp->nc_prev = &ncp->nc_nxt;
+ nchhead = ncp;
+ }