xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 7605)
1*7605Ssam /*	vfs_lookup.c	4.20	82/07/30	*/
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"
1230Sbill 
137441Sroot #ifdef	EFS
147441Sroot extern	int	efs_major;
157441Sroot #endif
167441Sroot 
17*7605Ssam struct	buf *blkatoff();
187534Sroot int	dirchk = 1;
1930Sbill /*
207534Sroot  * Convert a pathname into a pointer to a locked inode,
217534Sroot  * with side effects usable in creating and removing files.
227534Sroot  * This is a very central and rather complicated routine.
2330Sbill  *
247534Sroot  * The func argument gives the routine which returns successive
257534Sroot  * characters of the name to be translated.  The flag
267534Sroot  * argument is (0, 1, 2) depending on whether the name is to be
277534Sroot  * (looked up, created, deleted).  The follow argument is 1 when
287534Sroot  * symbolic links are to be followed when they occur at the end of
297534Sroot  * the name translation process.
307534Sroot  *
317534Sroot  * Overall outline:
327534Sroot  *
337534Sroot  *	copy in name
347534Sroot  *	get starting directory
357534Sroot  * dirloop:
367534Sroot  *	check accessibility of directory
377534Sroot  * dirloop2:
387534Sroot  *	copy next component of name to u.u_dent
397534Sroot  *	handle degenerate case where name is null string
407534Sroot  *	search for name in directory, to found or notfound
417534Sroot  * notfound:
427534Sroot  *	if creating, return locked inode, leaving information on avail. slots
437534Sroot  *	else return error
447534Sroot  * found:
457534Sroot  *	if at end of path and deleting, return information to allow delete
467534Sroot  *	if .. and on mounted filesys, look in mount table for parent
477534Sroot  *	if symbolic link, massage name in buffer and continue at dirloop
487534Sroot  *	if more components of name, do next level at dirloop
497534Sroot  *	return the answer as locked inode
5030Sbill  */
5130Sbill struct inode *
525972Swnj namei(func, flag, follow)
535972Swnj 	int (*func)(), flag, follow;
5430Sbill {
557534Sroot 	register char *cp;		/* pointer into pathname argument */
567534Sroot /* these variables refer to things which must be freed or unlocked */
577534Sroot 	register struct inode *dp = 0;	/* the directory we are searching */
587534Sroot 	register struct fs *fs;		/* file system that directory is in */
597534Sroot 	register struct buf *bp = 0;	/* a buffer of directory entries */
607534Sroot 	register struct direct *ep;	/* the current directory entry */
617534Sroot 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
627534Sroot 	register struct buf *nbp;	/* buffer storing path name argument */
637534Sroot /* these variables hold information about the search for a slot */
647534Sroot 	enum {NONE, COMPACT, FOUND} slotstatus;
657534Sroot 	int slotoffset = -1;		/* offset of area with free space */
667534Sroot 	int slotsize;			/* size of area at slotoffset */
677534Sroot 	int slotfreespace;		/* amount of space free in slot */
687534Sroot 	int slotneeded;			/* size of the entry we're seeking */
697534Sroot /* */
707534Sroot 	int dirsize;
717534Sroot 	int prevoff;			/* u.u_offset of previous entry */
727534Sroot 	int nlink = 0;			/* number of symbolic links taken */
737534Sroot 	struct inode *pdp;		/* saved dp during symlink work */
747534Sroot 	int i;
7530Sbill 
7630Sbill 	/*
777534Sroot 	 * Get a buffer for the name to be translated, and copy the
787534Sroot 	 * name into the buffer.
795972Swnj 	 */
806571Smckusic 	nbp = geteblk(MAXPATHLEN);
817534Sroot 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
827534Sroot 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
836066Sroot 			u.u_error = EPERM;
847534Sroot 			goto bad;
856066Sroot 		}
866066Sroot 		cp++;
876571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
885972Swnj 			u.u_error = ENOENT;
897534Sroot 			goto bad;
905972Swnj 		}
915972Swnj 	}
927534Sroot 	if (u.u_error)
937534Sroot 		goto bad;
947534Sroot 
955972Swnj 	/*
967534Sroot 	 * Get starting directory.
9730Sbill 	 */
987534Sroot 	cp = nbp->b_un.b_addr;
995972Swnj 	if (*cp == '/') {
1005972Swnj 		while (*cp == '/')
1015972Swnj 			cp++;
10230Sbill 		if ((dp = u.u_rdir) == NULL)
10330Sbill 			dp = rootdir;
1047534Sroot 	} else
1057534Sroot 		dp = u.u_cdir;
1067534Sroot 	fs = dp->i_fs;
1075972Swnj 	ilock(dp);
1085972Swnj 	dp->i_count++;
1097534Sroot 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
1107534Sroot 
1117534Sroot 	/*
1127534Sroot 	 * We come to dirloop to search a new directory.
1137534Sroot 	 * The directory must be locked so that it can be
1147534Sroot 	 * iput, and fs must be already set to dp->i_fs.
1157534Sroot 	 */
1166571Smckusic dirloop:
11730Sbill 	/*
1187534Sroot 	 * Check accessiblity of directory.
11930Sbill 	 */
1207441Sroot #ifdef	EFS
1217441Sroot 	if ((dp->i_mode & IFMT) == IFCHR && major(dp->i_rdev) == efs_major) {
1227441Sroot 		brelse(nbp);
1237441Sroot 		return(dp);
1247441Sroot 	}
1257441Sroot #endif
1267534Sroot 	if ((dp->i_mode&IFMT) != IFDIR) {
12730Sbill 		u.u_error = ENOTDIR;
1287534Sroot 		goto bad;
1297534Sroot 	}
1307534Sroot 	if (access(dp, IEXEC))
1317534Sroot 		goto bad;
1327534Sroot 
1336384Swnj dirloop2:
1347534Sroot 	/*
1357534Sroot 	 * Copy next component of name to u.u_dent.
1367534Sroot 	 */
1377534Sroot 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
1386571Smckusic 		if (i >= MAXNAMLEN) {
1395972Swnj 			u.u_error = ENOENT;
1407534Sroot 			goto bad;
1415972Swnj 		}
1427534Sroot 		u.u_dent.d_name[i++] = *cp;
1435972Swnj 	}
1446571Smckusic 	u.u_dent.d_namlen = i;
1457534Sroot 	u.u_dent.d_name[i] = 0;
1467534Sroot 
1477534Sroot 	/*
1487534Sroot 	 * Check for degenerate name (e.g. / or "")
1497534Sroot 	 * which is a way of talking about a directory,
1507534Sroot 	 * e.g. like "/." or ".".
1517534Sroot 	 */
1527534Sroot 	if (u.u_dent.d_name[0] == 0) {
1537534Sroot 		if (flag) {
1545972Swnj 			u.u_error = ENOENT;
1557534Sroot 			goto bad;
1565972Swnj 		}
1576571Smckusic 		brelse(nbp);
1586571Smckusic 		return (dp);
1595972Swnj 	}
1607534Sroot 
1616571Smckusic 	/*
1627534Sroot 	 * Suppress search for slots unless creating
1637534Sroot 	 * file and at end of pathname, in which case
1647534Sroot 	 * we watch for a place to put the new file in
1657534Sroot 	 * case it doesn't already exist.
1666571Smckusic 	 */
1677534Sroot 	slotstatus = FOUND;
1687534Sroot 	if (flag == 1 && *cp == 0) {
1697534Sroot 		slotstatus = NONE;
1707534Sroot 		slotfreespace = 0;
1717534Sroot 		slotneeded = DIRSIZ(&u.u_dent);
1727534Sroot 	}
1737534Sroot 
1747534Sroot 	dirsize = roundup(dp->i_size, DIRBLKSIZ);
1756571Smckusic 	u.u_offset = 0;
1767534Sroot 	while (u.u_offset < dirsize) {
1775972Swnj 		/*
1785972Swnj 		 * If offset is on a block boundary,
1795972Swnj 		 * read the next directory block.
1805972Swnj 		 * Release previous if it exists.
1815972Swnj 		 */
1826571Smckusic 		if (blkoff(fs, u.u_offset) == 0) {
1835972Swnj 			if (bp != NULL)
1845972Swnj 				brelse(bp);
185*7605Ssam 			bp = blkatoff(dp, u.u_offset, (char **)0);
1867534Sroot 			if (bp == 0)
1877534Sroot 				goto bad;
1887534Sroot 			entryoffsetinblock = 0;
1895972Swnj 		}
1907534Sroot 
1915972Swnj 		/*
1927534Sroot 		 * If still looking for a slot, and at a DIRBLKSIZE
1937534Sroot 		 * boundary, have to start looking for free space
1947534Sroot 		 * again.
1956571Smckusic 		 */
1967534Sroot 		if (slotstatus == NONE &&
1977534Sroot 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
1987534Sroot 			slotoffset = -1;
1997534Sroot 			slotfreespace = 0;
2007534Sroot 		}
2017534Sroot 
2027534Sroot 		/*
2037534Sroot 		 * Get pointer to next entry, and do consistency checking:
2047534Sroot 		 *	record length must be multiple of 4
205*7605Ssam 		 *	record length must not be zero
2067534Sroot 		 *	entry must fit in rest of this DIRBLKSIZ block
2077534Sroot 		 *	record must be large enough to contain name
208*7605Ssam 		 * When dirchk is set we also check:
209*7605Ssam 		 *	name is not longer than MAXNAMLEN
2107534Sroot 		 *	name must be as long as advertised, and null terminated
211*7605Ssam 		 * Checking last two conditions is done only when dirchk is
212*7605Ssam 		 * set, to save time.
2137534Sroot 		 */
2147534Sroot 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
2157534Sroot 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
216*7605Ssam 		if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
217*7605Ssam 		    ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
218*7605Ssam 		    dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
2197534Sroot 			dirbad(dp, "mangled entry");
2206571Smckusic 			u.u_offset += i;
2217534Sroot 			entryoffsetinblock += i;
2226571Smckusic 			continue;
2236571Smckusic 		}
2247534Sroot 
2256571Smckusic 		/*
2267534Sroot 		 * If an appropriate sized slot has not yet been found,
2276571Smckusic 		 * check to see if one is available. Also accumulate space
2286571Smckusic 		 * in the current block so that we can determine if
2296571Smckusic 		 * compaction is viable.
2306571Smckusic 		 */
2317534Sroot 		if (slotstatus != FOUND) {
2327534Sroot 			int size = ep->d_reclen;
2337534Sroot 
2346571Smckusic 			if (ep->d_ino != 0)
2356571Smckusic 				size -= DIRSIZ(ep);
2366571Smckusic 			if (size > 0) {
2377534Sroot 				if (size >= slotneeded) {
2387534Sroot 					slotstatus = FOUND;
2397534Sroot 					slotoffset = u.u_offset;
2407534Sroot 					slotsize = ep->d_reclen;
2417534Sroot 				} else if (slotstatus == NONE) {
2427534Sroot 					slotfreespace += size;
2437534Sroot 					if (slotoffset == -1)
2447534Sroot 						slotoffset = u.u_offset;
2457534Sroot 					if (slotfreespace >= slotneeded) {
2467534Sroot 						slotstatus = COMPACT;
2477534Sroot 						slotsize =
2487534Sroot 						    u.u_offset+ep->d_reclen -
2497534Sroot 						      slotoffset;
2507534Sroot 					}
2516571Smckusic 				}
2526571Smckusic 			}
2536571Smckusic 		}
2547534Sroot 
2556571Smckusic 		/*
2567534Sroot 		 * Check for a name match.
2575972Swnj 		 */
2587534Sroot 		if (ep->d_ino) {
2597534Sroot 			if (ep->d_namlen == u.u_dent.d_namlen &&
2607534Sroot 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
2617534Sroot 				goto found;
2627534Sroot 		}
2637534Sroot 		prevoff = u.u_offset;
2646571Smckusic 		u.u_offset += ep->d_reclen;
2657534Sroot 		entryoffsetinblock += ep->d_reclen;
2667534Sroot 	}
2677534Sroot /* notfound: */
2687534Sroot 	/*
2697534Sroot 	 * If creating, and at end of pathname and current
2707534Sroot 	 * directory has not been removed, then can consider allowing
2717534Sroot 	 * file to be created.
2727534Sroot 	 */
2737534Sroot 	if (flag == 1 && *cp == 0 && dp->i_nlink != 0) {
2745972Swnj 		/*
2757534Sroot 		 * Access for write is interpreted as allowing
2767534Sroot 		 * creation of files in the directory.
2775972Swnj 		 */
2787534Sroot 		if (access(dp, IWRITE))
2797534Sroot 			goto bad;
2805972Swnj 		/*
2817534Sroot 		 * Return an indication of where the new directory
2827534Sroot 		 * entry should be put.  If we didn't find a slot,
2837534Sroot 		 * then set u.u_count to 0 indicating that the
2847534Sroot 		 * new slot belongs at the end of the directory.
2857534Sroot 		 * If we found a slot, then the new entry can be
2867534Sroot 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
2875972Swnj 		 */
2887534Sroot 		if (slotstatus == NONE)
2897534Sroot 			u.u_count = 0;
2907534Sroot 		else {
2917534Sroot 			u.u_offset = slotoffset;
2927534Sroot 			u.u_count = slotsize;
2935972Swnj 		}
2947534Sroot 		dp->i_flag |= IUPD|ICHG;
2957534Sroot 		if (bp)
2967534Sroot 			brelse(bp);
2977534Sroot 		brelse(nbp);
2985972Swnj 		/*
2997534Sroot 		 * We return with the directory locked, so that
3007534Sroot 		 * the parameters we set up above will still be
3017534Sroot 		 * valid if we actually decide to do a direnter().
3027534Sroot 		 * We return NULL to indicate that the entry doesn't
3037534Sroot 		 * currently exist, leaving a pointer to the (locked)
3047534Sroot 		 * directory inode in u.u_pdir.
3055972Swnj 		 */
3067534Sroot 		u.u_pdir = dp;
3077534Sroot 		return (NULL);
3087534Sroot 	}
3097534Sroot 	u.u_error = ENOENT;
3107534Sroot 	goto bad;
3117534Sroot found:
3127534Sroot 	/*
3137534Sroot 	 * Check that directory length properly reflects presence
3147534Sroot 	 * of this entry.
3157534Sroot 	 */
316*7605Ssam 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
3177534Sroot 		dirbad(dp, "i_size too small");
318*7605Ssam 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
3197534Sroot 		dp->i_flag |= IUPD|ICHG;
3207534Sroot 	}
3217534Sroot 
3227534Sroot 	/*
3237534Sroot 	 * Found component in pathname; save directory
3247534Sroot 	 * entry in u.u_dent, and release directory buffer.
3257534Sroot 	 */
3267534Sroot 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
3277534Sroot 	brelse(bp);
3287534Sroot 	bp = NULL;
3297534Sroot 
3307534Sroot 	/*
3317534Sroot 	 * If deleting, and at end of pathname, return
3327534Sroot 	 * parameters which can be used to remove file.
3337534Sroot 	 * Note that in this case we return the directory
3347534Sroot 	 * inode, not the inode of the file being deleted.
3357534Sroot 	 */
3367534Sroot 	if (flag == 2 && *cp == 0) {
3377534Sroot 		/*
3387534Sroot 		 * Write access to directory required to delete files.
3397534Sroot 		 */
3407534Sroot 		if (access(dp, IWRITE))
3417534Sroot 			goto bad;
3427534Sroot 		/*
3437534Sroot 		 * Return pointer to current entry in u.u_offset,
3447534Sroot 		 * and distance past previous entry (if there
3457534Sroot 		 * is a previous entry in this block) in u.u_count.
3467534Sroot 		 * Save directory inode pointer in u.u_pdir for dirremove().
3477534Sroot 		 */
3487534Sroot 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
3497534Sroot 			u.u_count = 0;
3507534Sroot 		else
3517534Sroot 			u.u_count = u.u_offset - prevoff;
3527534Sroot 		brelse(nbp);
3537534Sroot 		u.u_pdir = dp;		/* for dirremove() */
3547534Sroot 		return (dp);
3557534Sroot 	}
3567534Sroot 
3577534Sroot 	/*
3587534Sroot 	 * Special handling for ".." allowing chdir out of mounted
3597534Sroot 	 * file system: indirect .. in root inode to reevaluate
3607534Sroot 	 * in directory file system was mounted on.
3617534Sroot 	 */
3627534Sroot 	if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
3637534Sroot 	    u.u_dent.d_name[2] == '\0') {
3647534Sroot 		if (dp == u.u_rdir)
3657534Sroot 			u.u_dent.d_ino = dp->i_number;
3667534Sroot 		else if (u.u_dent.d_ino == ROOTINO &&
3677534Sroot 		   dp->i_number == ROOTINO) {
3687534Sroot 			for (i = 1; i < NMOUNT; i++)
3697534Sroot 			if (mount[i].m_bufp != NULL &&
3707534Sroot 			   mount[i].m_dev == dp->i_dev) {
3716571Smckusic 				iput(dp);
3727534Sroot 				dp = mount[i].m_inodp;
3735972Swnj 				ilock(dp);
3745972Swnj 				dp->i_count++;
3757534Sroot 				fs = dp->i_fs;
3767534Sroot 				cp -= 2;     /* back over .. */
3777534Sroot 				goto dirloop2;
3785972Swnj 			}
37930Sbill 		}
3807534Sroot 	}
3817534Sroot 
3827534Sroot 	/*
3837534Sroot 	 * Check for symbolic link, which may require us
3847534Sroot 	 * to massage the name before we continue translation.
3857534Sroot 	 * To avoid deadlock have to unlock the current directory,
3867534Sroot 	 * but don't iput it because we may need it again (if
3877534Sroot 	 * the symbolic link is relative to .).  Instead save
3887534Sroot 	 * it (unlocked) as pdp.
3897534Sroot 	 */
3907534Sroot 	pdp = dp;
3917534Sroot 	iunlock(pdp);
3927534Sroot 	dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
3937534Sroot 	if (dp == NULL)
3947534Sroot 		goto bad2;
3957534Sroot 	fs = dp->i_fs;
3967534Sroot 
3977534Sroot 	/*
3987534Sroot 	 * Check for symbolic link
3997534Sroot 	 */
4007534Sroot 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
4017534Sroot 		int pathlen = strlen(cp) + 1;
4027534Sroot 		int bn;
4037534Sroot 
4047534Sroot 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
4057534Sroot 		    ++nlink > MAXSYMLINKS) {
4067534Sroot 			u.u_error = ELOOP;
4077534Sroot 			goto bad2;
4087534Sroot 		}
4097534Sroot 		bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
4107534Sroot 		u.u_segflg = 1;
4117534Sroot 		u.u_base = nbp->b_un.b_addr;
4127534Sroot 		u.u_count = dp->i_size;
413*7605Ssam 		u.u_offset = 0;
4147534Sroot 		readi(dp);
4157534Sroot 		if (u.u_error)
4167534Sroot 			goto bad2;
4177534Sroot 		cp = nbp->b_un.b_addr;
4187534Sroot 		iput(dp);
4195972Swnj 		if (*cp == '/') {
4207534Sroot 			irele(pdp);
4215972Swnj 			while (*cp == '/')
4225972Swnj 				cp++;
4237534Sroot 			if ((dp = u.u_rdir) == NULL)
4247534Sroot 				dp = rootdir;
4257534Sroot 			ilock(dp);
4267534Sroot 			dp->i_count++;
4277534Sroot 		} else {
4287534Sroot 			dp = pdp;
4297534Sroot 			ilock(dp);
4305972Swnj 		}
4317534Sroot 		fs = dp->i_fs;
4327534Sroot 		goto dirloop;
43330Sbill 	}
4347534Sroot 	irele(pdp);
4357534Sroot 
43630Sbill 	/*
4377534Sroot 	 * Not a symbolic link.  If more pathname,
4387534Sroot 	 * continue at next component, else return.
43930Sbill 	 */
4407534Sroot 	if (*cp == '/') {
4417534Sroot 		while (*cp == '/')
4427534Sroot 			cp++;
4437534Sroot 		goto dirloop;
44430Sbill 	}
4455972Swnj 	brelse(nbp);
4467534Sroot 	return (dp);
4477534Sroot bad2:
4487534Sroot 	irele(pdp);
4497534Sroot bad:
4507534Sroot 	if (bp)
4517534Sroot 		brelse(bp);
4527534Sroot 	if (dp)
4537534Sroot 		iput(dp);
4547534Sroot 	brelse(nbp);
4556571Smckusic 	return (NULL);
45630Sbill }
45730Sbill 
4587534Sroot dirbad(ip, how)
4597534Sroot 	struct inode *ip;
4607534Sroot 	char *how;
4617534Sroot {
4627534Sroot 
4637534Sroot 	printf("%s: bad dir ino %d at offset %d: %s\n",
4647534Sroot 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
4657534Sroot }
4667534Sroot 
4677534Sroot dirbadname(ep)
4687534Sroot 	register struct direct *ep;
4697534Sroot {
4707534Sroot 	register char *cp;
4717534Sroot 	register int i;
4727534Sroot 
4737534Sroot 	for (i = 0; i < ep->d_namlen; i++)
4747534Sroot 		if (ep->d_name[i] == 0)
4757534Sroot 			return (1);
4767534Sroot 	return (ep->d_name[i]);
4777534Sroot }
4787534Sroot 
47930Sbill /*
4807534Sroot  * Write a directory entry after a call to namei, using the parameters
4817534Sroot  * which it left in the u. area.  The argument ip is the inode which
4827534Sroot  * the new directory entry will refer to.  The u. area field u.u_pdir is
4837534Sroot  * a pointer to the directory to be written, which was left locked by
4847534Sroot  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
4857534Sroot  * how the space for the new entry is to be gotten.
4867534Sroot  */
4877534Sroot direnter(ip)
4887534Sroot 	struct inode *ip;
4895972Swnj {
4907534Sroot 	register struct direct *ep, *nep;
4917534Sroot 	struct fs *fs;
4927534Sroot 	struct buf *bp;
4937534Sroot 	int loc, dsize, freespace, newentrysize;
4947534Sroot 	char *dirbuf;
4955972Swnj 
4967534Sroot 	u.u_dent.d_ino = ip->i_number;
4977534Sroot 	u.u_segflg = 1;
4987534Sroot 	newentrysize = DIRSIZ(&u.u_dent);
4997534Sroot 	if (u.u_count == 0) {
5007534Sroot 		/*
5017534Sroot 		 * If u.u_count is 0, then namei could find no space in the
5027534Sroot 		 * directory.  In this case u.u_offset will be on a directory
5037534Sroot 		 * block boundary and we will write the new entry into a fresh
5047534Sroot 		 * block.
5057534Sroot 		 */
5067534Sroot 		if (u.u_offset&(DIRBLKSIZ-1))
5077534Sroot 			panic("wdir: newblk");
5087534Sroot 		u.u_dent.d_reclen = DIRBLKSIZ;
5097534Sroot 		u.u_count = newentrysize;
5107534Sroot 		u.u_base = (caddr_t)&u.u_dent;
5117534Sroot 		u.u_segflg = 1;
5127534Sroot 		writei(u.u_pdir);
5137534Sroot 		iput(u.u_pdir);
5147534Sroot 		return;
5157534Sroot 	}
5167534Sroot 
5177534Sroot 	/*
5187534Sroot 	 * If u.u_count is non-zero, then namei found space for the
5197534Sroot 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
5207534Sroot 	 * in the directory.  To use this space, we may have to compact
5217534Sroot 	 * the entries located there, by copying them together towards
5227534Sroot 	 * the beginning of the block, leaving the free space in
5237534Sroot 	 * one usable chunk at the end.
5247534Sroot 	 */
5257534Sroot 
5267534Sroot 	/*
5277534Sroot 	 * Increase size of directory if entry eats into new space.
5287534Sroot 	 * This should never push the size past a new multiple of
5297534Sroot 	 * DIRBLKSIZE.
5307534Sroot 	 */
5317534Sroot 	if (u.u_offset+u.u_count > u.u_pdir->i_size) {
532*7605Ssam /*ZZ*/		if (((u.u_offset+u.u_count-1)&~(DIRBLKSIZ-1)) !=
533*7605Ssam /*ZZ*/		    ((u.u_pdir->i_size-1)&~(DIRBLKSIZ-1))) {
534*7605Ssam /*ZZ*/			panic("wdir: span");
535*7605Ssam /*ZZ*/		}
5367534Sroot 		u.u_pdir->i_size = u.u_offset + u.u_count;
5377534Sroot 	}
5387534Sroot 
5397534Sroot 	/*
5407534Sroot 	 * Get the block containing the space for the new directory
5417534Sroot 	 * entry.
5427534Sroot 	 */
543*7605Ssam 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
5447534Sroot 	if (bp == 0)
5457534Sroot 		return;
5467534Sroot 
5477534Sroot 	/*
5487534Sroot 	 * Find space for the new entry.  In the simple case, the
5497534Sroot 	 * entry at offset base will have the space.  If it does
5507534Sroot 	 * not, then namei arranged that compacting the region
5517534Sroot 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
5527534Sroot 	 */
5537534Sroot 	ep = (struct direct *)dirbuf;
5547534Sroot 	dsize = DIRSIZ(ep);
5557534Sroot 	freespace = ep->d_reclen - dsize;
5567534Sroot 	for (loc = ep->d_reclen; loc < u.u_count; ) {
5577534Sroot 		nep = (struct direct *)(dirbuf + loc);
5587534Sroot 		if (ep->d_ino) {
5597534Sroot 			/* trim the existing slot */
5607534Sroot 			ep->d_reclen = dsize;
5617534Sroot 			ep = (struct direct *)((char *)ep + dsize);
5627534Sroot 		} else {
5637534Sroot 			/* overwrite; nothing there; header is ours */
5647534Sroot 			freespace += dsize;
5657534Sroot 		}
5667534Sroot 		dsize = DIRSIZ(nep);
5677534Sroot 		freespace += nep->d_reclen - dsize;
5687534Sroot 		loc += nep->d_reclen;
5697534Sroot /*ZZ*/if((loc&~0x1ff)!=(loc+nep->d_reclen-1&~0x1ff))
5707534Sroot /*ZZ*/printf("wdir: compact loc %d reclen %d (dir %s/%d)\n",loc,nep->d_reclen,
5717534Sroot /*ZZ*/u.u_pdir->i_fs->fs_fsmnt,u.u_pdir->i_number);
5727534Sroot 		bcopy(nep, ep, dsize);
5737534Sroot 	}
5747534Sroot 	/*
5757534Sroot 	 * Update the pointer fields in the previous entry (if any),
5767534Sroot 	 * copy in the new entry, and write out the block.
5777534Sroot 	 */
5787534Sroot 	if (ep->d_ino == 0) {
5797534Sroot 		if (freespace + dsize < newentrysize)
5807534Sroot 			panic("wdir: compact1");
5817534Sroot /*ZZ*/if(freespace+dsize>512)panic("wdir: compact screwup");
5827534Sroot 		u.u_dent.d_reclen = freespace + dsize;
5837534Sroot 	} else {
5847534Sroot 		if (freespace < newentrysize)
5857534Sroot 			panic("wdir: compact2");
5867534Sroot 		u.u_dent.d_reclen = freespace;
5877534Sroot /*ZZ*/if ((((char *)ep-bp->b_un.b_addr)&0x1ff)+dsize>512) panic("wdir: reclen");
5887534Sroot 		ep->d_reclen = dsize;
5897534Sroot 		ep = (struct direct *)((char *)ep + dsize);
5907534Sroot 	}
5917534Sroot /*ZZ*/if((((char*)ep-bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen>512)panic("wdir: botch");
5927534Sroot 	bcopy(&u.u_dent, ep, newentrysize);
5937534Sroot 	bwrite(bp);
5947534Sroot 	u.u_pdir->i_flag |= IUPD|ICHG;
5957534Sroot 	iput(u.u_pdir);
5965972Swnj }
5976571Smckusic 
5987534Sroot dirremove()
5996571Smckusic {
6007534Sroot 	register struct inode *dp = u.u_pdir;
6017534Sroot 	register struct fs *fs = dp->i_fs;
6027534Sroot 	register struct buf *bp;
6037534Sroot 	struct direct *ep;
6046571Smckusic 
6057534Sroot 	if (u.u_count == 0) {
6067534Sroot 		/*
6077534Sroot 		 * First entry in block: set d_ino to zero.
6087534Sroot 		 */
6097534Sroot /*ZZ*/if(u.u_offset&0x1ff)printf("missed dir compact dir %s/%d off %d file %s\n"
6107534Sroot /*ZZ*/,dp->i_fs->fs_fsmnt,dp->i_number,u.u_offset,u.u_dent.d_name);
6117534Sroot 		u.u_base = (caddr_t)&u.u_dent;
6127534Sroot 		u.u_count = DIRSIZ(&u.u_dent);
613*7605Ssam 		u.u_segflg = 1;
6147534Sroot 		u.u_dent.d_ino = 0;
6157534Sroot 		writei(dp);
6167534Sroot 	} else {
6177534Sroot 		/*
6187534Sroot 		 * Collapse new free space into previous entry.
6197534Sroot 		 */
620*7605Ssam 		bp = blkatoff(dp, u.u_offset - u.u_count, (char **)&ep);
6217534Sroot 		if (bp == 0)
6227534Sroot 			return (0);
6237534Sroot 		ep->d_reclen += u.u_dent.d_reclen;
6247534Sroot /*ZZ*/if((((char *)ep - bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen > 512)
6257534Sroot /*ZZ*/	panic("unlink: reclen");
6267534Sroot 		bwrite(bp);
6277534Sroot 		dp->i_flag |= IUPD|ICHG;
6287534Sroot 	}
6297534Sroot 	return (1);
6306571Smckusic }
6317534Sroot 
632*7605Ssam /*
633*7605Ssam  * Return buffer with contents of block "offset"
634*7605Ssam  * from the beginning of directory "ip".  If "res"
635*7605Ssam  * is non-zero, fill it in with a pointer to the
636*7605Ssam  * remaining space in the directory.
637*7605Ssam  */
6387534Sroot struct buf *
639*7605Ssam blkatoff(ip, offset, res)
6407534Sroot 	struct inode *ip;
6417534Sroot 	off_t offset;
6427534Sroot 	char **res;
6437534Sroot {
6447534Sroot 	register struct fs *fs = ip->i_fs;
6457534Sroot 	int lbn = lblkno(fs, offset);
6467534Sroot 	int base = blkoff(fs, offset);
6477534Sroot 	int bsize = blksize(fs, ip, lbn);
6487534Sroot 	int bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
6497534Sroot 	register struct buf *bp;
6507534Sroot 
6517534Sroot 	if (u.u_error)
6527534Sroot 		return (0);
6537534Sroot 	bp = bread(ip->i_dev, bn, bsize);
6547534Sroot 	if (bp->b_flags & B_ERROR) {
6557534Sroot 		brelse(bp);
6567534Sroot 		return (0);
6577534Sroot 	}
6587534Sroot 	if (res)
6597534Sroot 		*res = bp->b_un.b_addr + base;
6607534Sroot 	return (bp);
6617534Sroot }
662