xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 8672)
1*8672Sroot /*	vfs_lookup.c	4.28	82/10/19	*/
230Sbill 
330Sbill #include "../h/param.h"
430Sbill #include "../h/systm.h"
530Sbill #include "../h/inode.h"
66571Smckusic #include "../h/fs.h"
730Sbill #include "../h/mount.h"
830Sbill #include "../h/dir.h"
930Sbill #include "../h/user.h"
1030Sbill #include "../h/buf.h"
112275Swnj #include "../h/conf.h"
127825Sroot #include "../h/uio.h"
1330Sbill 
147605Ssam struct	buf *blkatoff();
157534Sroot int	dirchk = 1;
1630Sbill /*
177534Sroot  * Convert a pathname into a pointer to a locked inode,
187534Sroot  * with side effects usable in creating and removing files.
197534Sroot  * This is a very central and rather complicated routine.
2030Sbill  *
217534Sroot  * The func argument gives the routine which returns successive
227534Sroot  * characters of the name to be translated.  The flag
237534Sroot  * argument is (0, 1, 2) depending on whether the name is to be
247534Sroot  * (looked up, created, deleted).  The follow argument is 1 when
257534Sroot  * symbolic links are to be followed when they occur at the end of
267534Sroot  * the name translation process.
277534Sroot  *
287534Sroot  * Overall outline:
297534Sroot  *
307534Sroot  *	copy in name
317534Sroot  *	get starting directory
327534Sroot  * dirloop:
337534Sroot  *	check accessibility of directory
347534Sroot  * dirloop2:
357534Sroot  *	copy next component of name to u.u_dent
367534Sroot  *	handle degenerate case where name is null string
377534Sroot  *	search for name in directory, to found or notfound
387534Sroot  * notfound:
397534Sroot  *	if creating, return locked inode, leaving information on avail. slots
407534Sroot  *	else return error
417534Sroot  * found:
427534Sroot  *	if at end of path and deleting, return information to allow delete
437534Sroot  *	if .. and on mounted filesys, look in mount table for parent
447534Sroot  *	if symbolic link, massage name in buffer and continue at dirloop
457534Sroot  *	if more components of name, do next level at dirloop
467534Sroot  *	return the answer as locked inode
4730Sbill  */
4830Sbill struct inode *
495972Swnj namei(func, flag, follow)
505972Swnj 	int (*func)(), flag, follow;
5130Sbill {
527534Sroot 	register char *cp;		/* pointer into pathname argument */
537534Sroot /* these variables refer to things which must be freed or unlocked */
547534Sroot 	register struct inode *dp = 0;	/* the directory we are searching */
557534Sroot 	register struct fs *fs;		/* file system that directory is in */
567534Sroot 	register struct buf *bp = 0;	/* a buffer of directory entries */
577534Sroot 	register struct direct *ep;	/* the current directory entry */
587534Sroot 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
597534Sroot 	register struct buf *nbp;	/* buffer storing path name argument */
607534Sroot /* these variables hold information about the search for a slot */
617534Sroot 	enum {NONE, COMPACT, FOUND} slotstatus;
627534Sroot 	int slotoffset = -1;		/* offset of area with free space */
637534Sroot 	int slotsize;			/* size of area at slotoffset */
647534Sroot 	int slotfreespace;		/* amount of space free in slot */
657534Sroot 	int slotneeded;			/* size of the entry we're seeking */
667534Sroot /* */
677534Sroot 	int dirsize;
687534Sroot 	int prevoff;			/* u.u_offset of previous entry */
697534Sroot 	int nlink = 0;			/* number of symbolic links taken */
707534Sroot 	struct inode *pdp;		/* saved dp during symlink work */
717534Sroot 	int i;
7230Sbill 
7330Sbill 	/*
747534Sroot 	 * Get a buffer for the name to be translated, and copy the
757534Sroot 	 * name into the buffer.
765972Swnj 	 */
776571Smckusic 	nbp = geteblk(MAXPATHLEN);
787534Sroot 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
797534Sroot 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
806066Sroot 			u.u_error = EPERM;
817534Sroot 			goto bad;
826066Sroot 		}
836066Sroot 		cp++;
846571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
855972Swnj 			u.u_error = ENOENT;
867534Sroot 			goto bad;
875972Swnj 		}
885972Swnj 	}
897534Sroot 	if (u.u_error)
907534Sroot 		goto bad;
917534Sroot 
925972Swnj 	/*
937534Sroot 	 * Get starting directory.
9430Sbill 	 */
957534Sroot 	cp = nbp->b_un.b_addr;
965972Swnj 	if (*cp == '/') {
975972Swnj 		while (*cp == '/')
985972Swnj 			cp++;
9930Sbill 		if ((dp = u.u_rdir) == NULL)
10030Sbill 			dp = rootdir;
1017534Sroot 	} else
1027534Sroot 		dp = u.u_cdir;
1037534Sroot 	fs = dp->i_fs;
1045972Swnj 	ilock(dp);
1055972Swnj 	dp->i_count++;
1067534Sroot 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
1077534Sroot 
1087534Sroot 	/*
1097534Sroot 	 * We come to dirloop to search a new directory.
1107534Sroot 	 * The directory must be locked so that it can be
1117534Sroot 	 * iput, and fs must be already set to dp->i_fs.
1127534Sroot 	 */
1136571Smckusic dirloop:
11430Sbill 	/*
1157534Sroot 	 * Check accessiblity of directory.
11630Sbill 	 */
1177534Sroot 	if ((dp->i_mode&IFMT) != IFDIR) {
11830Sbill 		u.u_error = ENOTDIR;
1197534Sroot 		goto bad;
1207534Sroot 	}
1217534Sroot 	if (access(dp, IEXEC))
1227534Sroot 		goto bad;
1237534Sroot 
1246384Swnj dirloop2:
1257534Sroot 	/*
1267534Sroot 	 * Copy next component of name to u.u_dent.
1277534Sroot 	 */
1287534Sroot 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
1296571Smckusic 		if (i >= MAXNAMLEN) {
1305972Swnj 			u.u_error = ENOENT;
1317534Sroot 			goto bad;
1325972Swnj 		}
1337534Sroot 		u.u_dent.d_name[i++] = *cp;
1345972Swnj 	}
1356571Smckusic 	u.u_dent.d_namlen = i;
1367534Sroot 	u.u_dent.d_name[i] = 0;
1377534Sroot 
1387534Sroot 	/*
1397534Sroot 	 * Check for degenerate name (e.g. / or "")
1407534Sroot 	 * which is a way of talking about a directory,
1417534Sroot 	 * e.g. like "/." or ".".
1427534Sroot 	 */
1437534Sroot 	if (u.u_dent.d_name[0] == 0) {
1447534Sroot 		if (flag) {
1455972Swnj 			u.u_error = ENOENT;
1467534Sroot 			goto bad;
1475972Swnj 		}
1486571Smckusic 		brelse(nbp);
1496571Smckusic 		return (dp);
1505972Swnj 	}
1517534Sroot 
1526571Smckusic 	/*
1537534Sroot 	 * Suppress search for slots unless creating
1547534Sroot 	 * file and at end of pathname, in which case
1557534Sroot 	 * we watch for a place to put the new file in
1567534Sroot 	 * case it doesn't already exist.
1576571Smckusic 	 */
1587534Sroot 	slotstatus = FOUND;
1597534Sroot 	if (flag == 1 && *cp == 0) {
1607534Sroot 		slotstatus = NONE;
1617534Sroot 		slotfreespace = 0;
1627534Sroot 		slotneeded = DIRSIZ(&u.u_dent);
1637534Sroot 	}
1647534Sroot 
1657534Sroot 	dirsize = roundup(dp->i_size, DIRBLKSIZ);
1666571Smckusic 	u.u_offset = 0;
1677534Sroot 	while (u.u_offset < dirsize) {
1685972Swnj 		/*
1695972Swnj 		 * If offset is on a block boundary,
1705972Swnj 		 * read the next directory block.
1715972Swnj 		 * Release previous if it exists.
1725972Swnj 		 */
1736571Smckusic 		if (blkoff(fs, u.u_offset) == 0) {
1745972Swnj 			if (bp != NULL)
1755972Swnj 				brelse(bp);
1767605Ssam 			bp = blkatoff(dp, u.u_offset, (char **)0);
1777534Sroot 			if (bp == 0)
1787534Sroot 				goto bad;
1797534Sroot 			entryoffsetinblock = 0;
1805972Swnj 		}
1817534Sroot 
1825972Swnj 		/*
1837534Sroot 		 * If still looking for a slot, and at a DIRBLKSIZE
1847534Sroot 		 * boundary, have to start looking for free space
1857534Sroot 		 * again.
1866571Smckusic 		 */
1877534Sroot 		if (slotstatus == NONE &&
1887534Sroot 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
1897534Sroot 			slotoffset = -1;
1907534Sroot 			slotfreespace = 0;
1917534Sroot 		}
1927534Sroot 
1937534Sroot 		/*
1947534Sroot 		 * Get pointer to next entry, and do consistency checking:
1957534Sroot 		 *	record length must be multiple of 4
1967605Ssam 		 *	record length must not be zero
1977534Sroot 		 *	entry must fit in rest of this DIRBLKSIZ block
1987534Sroot 		 *	record must be large enough to contain name
1997605Ssam 		 * When dirchk is set we also check:
2007605Ssam 		 *	name is not longer than MAXNAMLEN
2017534Sroot 		 *	name must be as long as advertised, and null terminated
2027605Ssam 		 * Checking last two conditions is done only when dirchk is
2037605Ssam 		 * set, to save time.
2047534Sroot 		 */
2057534Sroot 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
2067534Sroot 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
2077605Ssam 		if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
2087605Ssam 		    ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
2097605Ssam 		    dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
2107534Sroot 			dirbad(dp, "mangled entry");
2116571Smckusic 			u.u_offset += i;
2127534Sroot 			entryoffsetinblock += i;
2136571Smckusic 			continue;
2146571Smckusic 		}
2157534Sroot 
2166571Smckusic 		/*
2177534Sroot 		 * If an appropriate sized slot has not yet been found,
2186571Smckusic 		 * check to see if one is available. Also accumulate space
2196571Smckusic 		 * in the current block so that we can determine if
2206571Smckusic 		 * compaction is viable.
2216571Smckusic 		 */
2227534Sroot 		if (slotstatus != FOUND) {
2237534Sroot 			int size = ep->d_reclen;
2247534Sroot 
2256571Smckusic 			if (ep->d_ino != 0)
2266571Smckusic 				size -= DIRSIZ(ep);
2276571Smckusic 			if (size > 0) {
2287534Sroot 				if (size >= slotneeded) {
2297534Sroot 					slotstatus = FOUND;
2307534Sroot 					slotoffset = u.u_offset;
2317534Sroot 					slotsize = ep->d_reclen;
2327534Sroot 				} else if (slotstatus == NONE) {
2337534Sroot 					slotfreespace += size;
2347534Sroot 					if (slotoffset == -1)
2357534Sroot 						slotoffset = u.u_offset;
2367534Sroot 					if (slotfreespace >= slotneeded) {
2377534Sroot 						slotstatus = COMPACT;
2387534Sroot 						slotsize =
2397534Sroot 						    u.u_offset+ep->d_reclen -
2407534Sroot 						      slotoffset;
2417534Sroot 					}
2426571Smckusic 				}
2436571Smckusic 			}
2446571Smckusic 		}
2457534Sroot 
2466571Smckusic 		/*
2477534Sroot 		 * Check for a name match.
2485972Swnj 		 */
2497534Sroot 		if (ep->d_ino) {
2507534Sroot 			if (ep->d_namlen == u.u_dent.d_namlen &&
2517534Sroot 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
2527534Sroot 				goto found;
2537534Sroot 		}
2547534Sroot 		prevoff = u.u_offset;
2556571Smckusic 		u.u_offset += ep->d_reclen;
2567534Sroot 		entryoffsetinblock += ep->d_reclen;
2577534Sroot 	}
2587534Sroot /* notfound: */
2597534Sroot 	/*
2607534Sroot 	 * If creating, and at end of pathname and current
2617534Sroot 	 * directory has not been removed, then can consider allowing
2627534Sroot 	 * file to be created.
2637534Sroot 	 */
2647534Sroot 	if (flag == 1 && *cp == 0 && dp->i_nlink != 0) {
2655972Swnj 		/*
2667534Sroot 		 * Access for write is interpreted as allowing
2677534Sroot 		 * creation of files in the directory.
2685972Swnj 		 */
2697534Sroot 		if (access(dp, IWRITE))
2707534Sroot 			goto bad;
2715972Swnj 		/*
2727534Sroot 		 * Return an indication of where the new directory
2737534Sroot 		 * entry should be put.  If we didn't find a slot,
2747534Sroot 		 * then set u.u_count to 0 indicating that the
2757534Sroot 		 * new slot belongs at the end of the directory.
2767534Sroot 		 * If we found a slot, then the new entry can be
2777534Sroot 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
2785972Swnj 		 */
2797534Sroot 		if (slotstatus == NONE)
2807534Sroot 			u.u_count = 0;
2817534Sroot 		else {
2827534Sroot 			u.u_offset = slotoffset;
2837534Sroot 			u.u_count = slotsize;
2845972Swnj 		}
2857534Sroot 		dp->i_flag |= IUPD|ICHG;
2867534Sroot 		if (bp)
2877534Sroot 			brelse(bp);
2887534Sroot 		brelse(nbp);
2895972Swnj 		/*
2907534Sroot 		 * We return with the directory locked, so that
2917534Sroot 		 * the parameters we set up above will still be
2927534Sroot 		 * valid if we actually decide to do a direnter().
2937534Sroot 		 * We return NULL to indicate that the entry doesn't
2947534Sroot 		 * currently exist, leaving a pointer to the (locked)
2957534Sroot 		 * directory inode in u.u_pdir.
2965972Swnj 		 */
2977534Sroot 		u.u_pdir = dp;
2987534Sroot 		return (NULL);
2997534Sroot 	}
3007534Sroot 	u.u_error = ENOENT;
3017534Sroot 	goto bad;
3027534Sroot found:
3037534Sroot 	/*
3047534Sroot 	 * Check that directory length properly reflects presence
3057534Sroot 	 * of this entry.
3067534Sroot 	 */
3077605Ssam 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
3087534Sroot 		dirbad(dp, "i_size too small");
3097605Ssam 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
3107534Sroot 		dp->i_flag |= IUPD|ICHG;
3117534Sroot 	}
3127534Sroot 
3137534Sroot 	/*
3147534Sroot 	 * Found component in pathname; save directory
3157534Sroot 	 * entry in u.u_dent, and release directory buffer.
3167534Sroot 	 */
3177825Sroot 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep));
3187534Sroot 	brelse(bp);
3197534Sroot 	bp = NULL;
3207534Sroot 
3217534Sroot 	/*
3227534Sroot 	 * If deleting, and at end of pathname, return
3237534Sroot 	 * parameters which can be used to remove file.
3247534Sroot 	 * Note that in this case we return the directory
3257534Sroot 	 * inode, not the inode of the file being deleted.
3267534Sroot 	 */
3277534Sroot 	if (flag == 2 && *cp == 0) {
3287534Sroot 		/*
3297534Sroot 		 * Write access to directory required to delete files.
3307534Sroot 		 */
3317534Sroot 		if (access(dp, IWRITE))
3327534Sroot 			goto bad;
3337534Sroot 		/*
3347534Sroot 		 * Return pointer to current entry in u.u_offset,
3357534Sroot 		 * and distance past previous entry (if there
3367534Sroot 		 * is a previous entry in this block) in u.u_count.
3377534Sroot 		 * Save directory inode pointer in u.u_pdir for dirremove().
3387534Sroot 		 */
3397534Sroot 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
3407534Sroot 			u.u_count = 0;
3417534Sroot 		else
3427534Sroot 			u.u_count = u.u_offset - prevoff;
3437534Sroot 		brelse(nbp);
3447534Sroot 		u.u_pdir = dp;		/* for dirremove() */
3457534Sroot 		return (dp);
3467534Sroot 	}
3477534Sroot 
3487534Sroot 	/*
3497534Sroot 	 * Special handling for ".." allowing chdir out of mounted
3507534Sroot 	 * file system: indirect .. in root inode to reevaluate
3517534Sroot 	 * in directory file system was mounted on.
3527534Sroot 	 */
3537534Sroot 	if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
3547534Sroot 	    u.u_dent.d_name[2] == '\0') {
3557534Sroot 		if (dp == u.u_rdir)
3567534Sroot 			u.u_dent.d_ino = dp->i_number;
3577534Sroot 		else if (u.u_dent.d_ino == ROOTINO &&
3587534Sroot 		   dp->i_number == ROOTINO) {
3597534Sroot 			for (i = 1; i < NMOUNT; i++)
3607534Sroot 			if (mount[i].m_bufp != NULL &&
3617534Sroot 			   mount[i].m_dev == dp->i_dev) {
3626571Smckusic 				iput(dp);
3637534Sroot 				dp = mount[i].m_inodp;
3645972Swnj 				ilock(dp);
3655972Swnj 				dp->i_count++;
3667534Sroot 				fs = dp->i_fs;
3677534Sroot 				cp -= 2;     /* back over .. */
3687534Sroot 				goto dirloop2;
3695972Swnj 			}
37030Sbill 		}
3717534Sroot 	}
3727534Sroot 
3737534Sroot 	/*
3747534Sroot 	 * Check for symbolic link, which may require us
3757534Sroot 	 * to massage the name before we continue translation.
3767534Sroot 	 * To avoid deadlock have to unlock the current directory,
3777534Sroot 	 * but don't iput it because we may need it again (if
3787534Sroot 	 * the symbolic link is relative to .).  Instead save
3797534Sroot 	 * it (unlocked) as pdp.
3807534Sroot 	 */
3817534Sroot 	pdp = dp;
3827534Sroot 	iunlock(pdp);
3837534Sroot 	dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
3847534Sroot 	if (dp == NULL)
3857534Sroot 		goto bad2;
3867534Sroot 	fs = dp->i_fs;
3877534Sroot 
3887534Sroot 	/*
3897534Sroot 	 * Check for symbolic link
3907534Sroot 	 */
3917534Sroot 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
3927825Sroot 		u_int pathlen = strlen(cp) + 1;
3937534Sroot 
3947534Sroot 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
3957534Sroot 		    ++nlink > MAXSYMLINKS) {
3967534Sroot 			u.u_error = ELOOP;
3977534Sroot 			goto bad2;
3987534Sroot 		}
3997534Sroot 		bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
4007751Sroot 		u.u_error =
4017825Sroot 		    rdwri(UIO_READ, dp, nbp->b_un.b_addr, dp->i_size,
4027825Sroot 			0, 1, (int *)0);
4037534Sroot 		if (u.u_error)
4047534Sroot 			goto bad2;
4057534Sroot 		cp = nbp->b_un.b_addr;
4067534Sroot 		iput(dp);
4075972Swnj 		if (*cp == '/') {
4087534Sroot 			irele(pdp);
4095972Swnj 			while (*cp == '/')
4105972Swnj 				cp++;
4117534Sroot 			if ((dp = u.u_rdir) == NULL)
4127534Sroot 				dp = rootdir;
4137534Sroot 			ilock(dp);
4147534Sroot 			dp->i_count++;
4157534Sroot 		} else {
4167534Sroot 			dp = pdp;
4177534Sroot 			ilock(dp);
4185972Swnj 		}
4197534Sroot 		fs = dp->i_fs;
4207534Sroot 		goto dirloop;
42130Sbill 	}
4227534Sroot 	irele(pdp);
4237534Sroot 
42430Sbill 	/*
4257534Sroot 	 * Not a symbolic link.  If more pathname,
4267534Sroot 	 * continue at next component, else return.
42730Sbill 	 */
4287534Sroot 	if (*cp == '/') {
4297534Sroot 		while (*cp == '/')
4307534Sroot 			cp++;
4317534Sroot 		goto dirloop;
43230Sbill 	}
4335972Swnj 	brelse(nbp);
4347534Sroot 	return (dp);
4357534Sroot bad2:
4367534Sroot 	irele(pdp);
4377534Sroot bad:
4387534Sroot 	if (bp)
4397534Sroot 		brelse(bp);
4407534Sroot 	if (dp)
4417534Sroot 		iput(dp);
4427534Sroot 	brelse(nbp);
4436571Smckusic 	return (NULL);
44430Sbill }
44530Sbill 
4467534Sroot dirbad(ip, how)
4477534Sroot 	struct inode *ip;
4487534Sroot 	char *how;
4497534Sroot {
4507534Sroot 
4517534Sroot 	printf("%s: bad dir ino %d at offset %d: %s\n",
4527534Sroot 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
4537534Sroot }
4547534Sroot 
4557534Sroot dirbadname(ep)
4567534Sroot 	register struct direct *ep;
4577534Sroot {
4587534Sroot 	register int i;
4597534Sroot 
4607534Sroot 	for (i = 0; i < ep->d_namlen; i++)
4617534Sroot 		if (ep->d_name[i] == 0)
4627534Sroot 			return (1);
4637534Sroot 	return (ep->d_name[i]);
4647534Sroot }
4657534Sroot 
46630Sbill /*
4677534Sroot  * Write a directory entry after a call to namei, using the parameters
4687534Sroot  * which it left in the u. area.  The argument ip is the inode which
4697534Sroot  * the new directory entry will refer to.  The u. area field u.u_pdir is
4707534Sroot  * a pointer to the directory to be written, which was left locked by
4717534Sroot  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
4727534Sroot  * how the space for the new entry is to be gotten.
4737534Sroot  */
4747534Sroot direnter(ip)
4757534Sroot 	struct inode *ip;
4765972Swnj {
4777534Sroot 	register struct direct *ep, *nep;
4787534Sroot 	struct buf *bp;
4797825Sroot 	int loc, freespace;
4808631Sroot 	u_int dsize;
4818631Sroot 	int newentrysize;
4827534Sroot 	char *dirbuf;
4835972Swnj 
4847534Sroot 	u.u_dent.d_ino = ip->i_number;
4857534Sroot 	u.u_segflg = 1;
4867534Sroot 	newentrysize = DIRSIZ(&u.u_dent);
4877534Sroot 	if (u.u_count == 0) {
4887534Sroot 		/*
4897534Sroot 		 * If u.u_count is 0, then namei could find no space in the
4907534Sroot 		 * directory.  In this case u.u_offset will be on a directory
4917534Sroot 		 * block boundary and we will write the new entry into a fresh
4927534Sroot 		 * block.
4937534Sroot 		 */
4947534Sroot 		if (u.u_offset&(DIRBLKSIZ-1))
4957534Sroot 			panic("wdir: newblk");
4967534Sroot 		u.u_dent.d_reclen = DIRBLKSIZ;
4978631Sroot 		(void) rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
4988631Sroot 		    newentrysize, u.u_offset, 1, (int *)0);
4997534Sroot 		iput(u.u_pdir);
5007534Sroot 		return;
5017534Sroot 	}
5027534Sroot 
5037534Sroot 	/*
5047534Sroot 	 * If u.u_count is non-zero, then namei found space for the
5057534Sroot 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
5067534Sroot 	 * in the directory.  To use this space, we may have to compact
5077534Sroot 	 * the entries located there, by copying them together towards
5087534Sroot 	 * the beginning of the block, leaving the free space in
5097534Sroot 	 * one usable chunk at the end.
5107534Sroot 	 */
5117534Sroot 
5127534Sroot 	/*
5137534Sroot 	 * Increase size of directory if entry eats into new space.
5147534Sroot 	 * This should never push the size past a new multiple of
5157534Sroot 	 * DIRBLKSIZE.
5167534Sroot 	 */
5178631Sroot 	if (u.u_offset+u.u_count > u.u_pdir->i_size)
5187534Sroot 		u.u_pdir->i_size = u.u_offset + u.u_count;
5197534Sroot 
5207534Sroot 	/*
5217534Sroot 	 * Get the block containing the space for the new directory
5227534Sroot 	 * entry.
5237534Sroot 	 */
5247605Ssam 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
5257534Sroot 	if (bp == 0)
5267534Sroot 		return;
5277534Sroot 
5287534Sroot 	/*
5297534Sroot 	 * Find space for the new entry.  In the simple case, the
5307534Sroot 	 * entry at offset base will have the space.  If it does
5317534Sroot 	 * not, then namei arranged that compacting the region
5327534Sroot 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
5337534Sroot 	 */
5347534Sroot 	ep = (struct direct *)dirbuf;
5357534Sroot 	dsize = DIRSIZ(ep);
5367534Sroot 	freespace = ep->d_reclen - dsize;
5377534Sroot 	for (loc = ep->d_reclen; loc < u.u_count; ) {
5387534Sroot 		nep = (struct direct *)(dirbuf + loc);
5397534Sroot 		if (ep->d_ino) {
5407534Sroot 			/* trim the existing slot */
5417534Sroot 			ep->d_reclen = dsize;
5427534Sroot 			ep = (struct direct *)((char *)ep + dsize);
5437534Sroot 		} else {
5447534Sroot 			/* overwrite; nothing there; header is ours */
5457534Sroot 			freespace += dsize;
5467534Sroot 		}
5477534Sroot 		dsize = DIRSIZ(nep);
5487534Sroot 		freespace += nep->d_reclen - dsize;
5497534Sroot 		loc += nep->d_reclen;
5507825Sroot 		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
5517534Sroot 	}
5527534Sroot 	/*
5537534Sroot 	 * Update the pointer fields in the previous entry (if any),
5547534Sroot 	 * copy in the new entry, and write out the block.
5557534Sroot 	 */
5567534Sroot 	if (ep->d_ino == 0) {
5577534Sroot 		if (freespace + dsize < newentrysize)
5587534Sroot 			panic("wdir: compact1");
5597534Sroot 		u.u_dent.d_reclen = freespace + dsize;
5607534Sroot 	} else {
5617534Sroot 		if (freespace < newentrysize)
5627534Sroot 			panic("wdir: compact2");
5637534Sroot 		u.u_dent.d_reclen = freespace;
5647534Sroot 		ep->d_reclen = dsize;
5657534Sroot 		ep = (struct direct *)((char *)ep + dsize);
5667534Sroot 	}
567*8672Sroot 	bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
5687534Sroot 	bwrite(bp);
5697534Sroot 	u.u_pdir->i_flag |= IUPD|ICHG;
5707534Sroot 	iput(u.u_pdir);
5715972Swnj }
5726571Smckusic 
5737534Sroot dirremove()
5746571Smckusic {
5757534Sroot 	register struct inode *dp = u.u_pdir;
5767534Sroot 	register struct buf *bp;
5777534Sroot 	struct direct *ep;
5786571Smckusic 
5797534Sroot 	if (u.u_count == 0) {
5807534Sroot 		/*
5817534Sroot 		 * First entry in block: set d_ino to zero.
5827534Sroot 		 */
5837534Sroot 		u.u_dent.d_ino = 0;
5848619Sroot 		(void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
5858631Sroot 		    (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
5867534Sroot 	} else {
5877534Sroot 		/*
5887534Sroot 		 * Collapse new free space into previous entry.
5897534Sroot 		 */
5907825Sroot 		bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
5917534Sroot 		if (bp == 0)
5927534Sroot 			return (0);
5937534Sroot 		ep->d_reclen += u.u_dent.d_reclen;
5947534Sroot 		bwrite(bp);
5957534Sroot 		dp->i_flag |= IUPD|ICHG;
5967534Sroot 	}
5977534Sroot 	return (1);
5986571Smckusic }
5997534Sroot 
6007605Ssam /*
6017605Ssam  * Return buffer with contents of block "offset"
6027605Ssam  * from the beginning of directory "ip".  If "res"
6037605Ssam  * is non-zero, fill it in with a pointer to the
6047605Ssam  * remaining space in the directory.
6057605Ssam  */
6067534Sroot struct buf *
6077605Ssam blkatoff(ip, offset, res)
6087534Sroot 	struct inode *ip;
6097534Sroot 	off_t offset;
6107534Sroot 	char **res;
6117534Sroot {
6127534Sroot 	register struct fs *fs = ip->i_fs;
613*8672Sroot 	daddr_t lbn = lblkno(fs, offset);
6147534Sroot 	int base = blkoff(fs, offset);
6157534Sroot 	int bsize = blksize(fs, ip, lbn);
616*8672Sroot 	daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
6177534Sroot 	register struct buf *bp;
6187534Sroot 
6197534Sroot 	if (u.u_error)
6207534Sroot 		return (0);
6217534Sroot 	bp = bread(ip->i_dev, bn, bsize);
6227534Sroot 	if (bp->b_flags & B_ERROR) {
6237534Sroot 		brelse(bp);
6247534Sroot 		return (0);
6257534Sroot 	}
6267534Sroot 	if (res)
6277534Sroot 		*res = bp->b_un.b_addr + base;
6287534Sroot 	return (bp);
6297534Sroot }
630