xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 12492)
1*12492Ssam /*	vfs_lookup.c	4.38	83/05/18	*/
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"
139166Ssam #include "../h/nami.h"
1430Sbill 
157605Ssam struct	buf *blkatoff();
169166Ssam int	dirchk = 0;
1730Sbill /*
187534Sroot  * Convert a pathname into a pointer to a locked inode,
197534Sroot  * with side effects usable in creating and removing files.
207534Sroot  * This is a very central and rather complicated routine.
2130Sbill  *
227534Sroot  * The func argument gives the routine which returns successive
239166Ssam  * characters of the name to be translated.
247534Sroot  *
259166Ssam  * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether
269166Ssam  * the name is to be (looked up, created, deleted).  If flag has
279166Ssam  * LOCKPARENT or'ed into it and the target of the pathname exists,
289166Ssam  * namei returns both the target and its parent directory locked.
299166Ssam  * If the file system is not maintained in a strict tree hierarchy,
309166Ssam  * this can result in a deadlock situation.  When creating and
319166Ssam  * LOCKPARENT is specified, the target may not be ".".  When deleting
329166Ssam  * and LOCKPARENT is specified, the target may be ".", but the caller
339166Ssam  * must check to insure it does an irele and iput instead of two iputs.
349166Ssam  *
359166Ssam  * The follow argument is 1 when symbolic links are to be followed
369166Ssam  * when they occur at the end of the name translation process.
379166Ssam  *
387534Sroot  * Overall outline:
397534Sroot  *
407534Sroot  *	copy in name
417534Sroot  *	get starting directory
427534Sroot  * dirloop:
437534Sroot  *	check accessibility of directory
447534Sroot  * dirloop2:
457534Sroot  *	copy next component of name to u.u_dent
467534Sroot  *	handle degenerate case where name is null string
477534Sroot  *	search for name in directory, to found or notfound
487534Sroot  * notfound:
499166Ssam  *	if creating, return locked directory, leaving info on avail. slots
507534Sroot  *	else return error
517534Sroot  * found:
527534Sroot  *	if at end of path and deleting, return information to allow delete
539166Ssam  *	if at end of path and rewriting (create and LOCKPARENT), lock targe
549166Ssam  *	  inode and return info to allow rewrite
557534Sroot  *	if .. and on mounted filesys, look in mount table for parent
567534Sroot  *	if symbolic link, massage name in buffer and continue at dirloop
577534Sroot  *	if more components of name, do next level at dirloop
587534Sroot  *	return the answer as locked inode
599166Ssam  *
609166Ssam  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode,
619166Ssam  *	 but unlocked.
6230Sbill  */
6330Sbill struct inode *
645972Swnj namei(func, flag, follow)
655972Swnj 	int (*func)(), flag, follow;
6630Sbill {
677534Sroot 	register char *cp;		/* pointer into pathname argument */
687534Sroot /* these variables refer to things which must be freed or unlocked */
697534Sroot 	register struct inode *dp = 0;	/* the directory we are searching */
707534Sroot 	register struct fs *fs;		/* file system that directory is in */
717534Sroot 	register struct buf *bp = 0;	/* a buffer of directory entries */
727534Sroot 	register struct direct *ep;	/* the current directory entry */
737534Sroot 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
747534Sroot 	register struct buf *nbp;	/* buffer storing path name argument */
757534Sroot /* these variables hold information about the search for a slot */
767534Sroot 	enum {NONE, COMPACT, FOUND} slotstatus;
777534Sroot 	int slotoffset = -1;		/* offset of area with free space */
787534Sroot 	int slotsize;			/* size of area at slotoffset */
797534Sroot 	int slotfreespace;		/* amount of space free in slot */
807534Sroot 	int slotneeded;			/* size of the entry we're seeking */
817534Sroot /* */
827534Sroot 	int dirsize;
837534Sroot 	int prevoff;			/* u.u_offset of previous entry */
847534Sroot 	int nlink = 0;			/* number of symbolic links taken */
857534Sroot 	struct inode *pdp;		/* saved dp during symlink work */
867534Sroot 	int i;
879166Ssam 	int lockparent;
8830Sbill 
899166Ssam 	lockparent = flag & LOCKPARENT;
909166Ssam 	flag &= ~LOCKPARENT;
9130Sbill 	/*
927534Sroot 	 * Get a buffer for the name to be translated, and copy the
937534Sroot 	 * name into the buffer.
945972Swnj 	 */
956571Smckusic 	nbp = geteblk(MAXPATHLEN);
967534Sroot 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
977534Sroot 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
986066Sroot 			u.u_error = EPERM;
997534Sroot 			goto bad;
1006066Sroot 		}
1016066Sroot 		cp++;
1026571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
1035972Swnj 			u.u_error = ENOENT;
1047534Sroot 			goto bad;
1055972Swnj 		}
1065972Swnj 	}
1077534Sroot 	if (u.u_error)
1087534Sroot 		goto bad;
1097534Sroot 
1105972Swnj 	/*
1117534Sroot 	 * Get starting directory.
11230Sbill 	 */
1137534Sroot 	cp = nbp->b_un.b_addr;
1145972Swnj 	if (*cp == '/') {
1155972Swnj 		while (*cp == '/')
1165972Swnj 			cp++;
11730Sbill 		if ((dp = u.u_rdir) == NULL)
11830Sbill 			dp = rootdir;
1197534Sroot 	} else
1207534Sroot 		dp = u.u_cdir;
1217534Sroot 	fs = dp->i_fs;
1225972Swnj 	ilock(dp);
1235972Swnj 	dp->i_count++;
1247534Sroot 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
1257534Sroot 
1267534Sroot 	/*
1277534Sroot 	 * We come to dirloop to search a new directory.
1287534Sroot 	 * The directory must be locked so that it can be
1297534Sroot 	 * iput, and fs must be already set to dp->i_fs.
1307534Sroot 	 */
1316571Smckusic dirloop:
13230Sbill 	/*
1337534Sroot 	 * Check accessiblity of directory.
13430Sbill 	 */
1357534Sroot 	if ((dp->i_mode&IFMT) != IFDIR) {
13630Sbill 		u.u_error = ENOTDIR;
1377534Sroot 		goto bad;
1387534Sroot 	}
1397534Sroot 	if (access(dp, IEXEC))
1407534Sroot 		goto bad;
1417534Sroot 
1426384Swnj dirloop2:
1437534Sroot 	/*
1447534Sroot 	 * Copy next component of name to u.u_dent.
1457534Sroot 	 */
1467534Sroot 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
1476571Smckusic 		if (i >= MAXNAMLEN) {
1485972Swnj 			u.u_error = ENOENT;
1497534Sroot 			goto bad;
1505972Swnj 		}
1517534Sroot 		u.u_dent.d_name[i++] = *cp;
1525972Swnj 	}
1536571Smckusic 	u.u_dent.d_namlen = i;
1547534Sroot 	u.u_dent.d_name[i] = 0;
1557534Sroot 
1567534Sroot 	/*
1577534Sroot 	 * Check for degenerate name (e.g. / or "")
1587534Sroot 	 * which is a way of talking about a directory,
1597534Sroot 	 * e.g. like "/." or ".".
1607534Sroot 	 */
1617534Sroot 	if (u.u_dent.d_name[0] == 0) {
1629870Ssam 		if (flag || lockparent) {
1635972Swnj 			u.u_error = ENOENT;
1647534Sroot 			goto bad;
1655972Swnj 		}
1666571Smckusic 		brelse(nbp);
1676571Smckusic 		return (dp);
1685972Swnj 	}
1697534Sroot 
1706571Smckusic 	/*
1717534Sroot 	 * Suppress search for slots unless creating
1727534Sroot 	 * file and at end of pathname, in which case
1737534Sroot 	 * we watch for a place to put the new file in
1747534Sroot 	 * case it doesn't already exist.
1756571Smckusic 	 */
1767534Sroot 	slotstatus = FOUND;
1779166Ssam 	if (flag == CREATE && *cp == 0) {
1787534Sroot 		slotstatus = NONE;
1797534Sroot 		slotfreespace = 0;
1807534Sroot 		slotneeded = DIRSIZ(&u.u_dent);
1817534Sroot 	}
1827534Sroot 
1837534Sroot 	dirsize = roundup(dp->i_size, DIRBLKSIZ);
1846571Smckusic 	u.u_offset = 0;
1857534Sroot 	while (u.u_offset < dirsize) {
1865972Swnj 		/*
1875972Swnj 		 * If offset is on a block boundary,
1885972Swnj 		 * read the next directory block.
1895972Swnj 		 * Release previous if it exists.
1905972Swnj 		 */
1916571Smckusic 		if (blkoff(fs, u.u_offset) == 0) {
1925972Swnj 			if (bp != NULL)
1935972Swnj 				brelse(bp);
1947605Ssam 			bp = blkatoff(dp, u.u_offset, (char **)0);
1957534Sroot 			if (bp == 0)
1967534Sroot 				goto bad;
1977534Sroot 			entryoffsetinblock = 0;
1985972Swnj 		}
1997534Sroot 
2005972Swnj 		/*
2017534Sroot 		 * If still looking for a slot, and at a DIRBLKSIZE
2027534Sroot 		 * boundary, have to start looking for free space
2037534Sroot 		 * again.
2046571Smckusic 		 */
2057534Sroot 		if (slotstatus == NONE &&
2067534Sroot 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
2077534Sroot 			slotoffset = -1;
2087534Sroot 			slotfreespace = 0;
2097534Sroot 		}
2107534Sroot 
2117534Sroot 		/*
2127534Sroot 		 * Get pointer to next entry, and do consistency checking:
2137534Sroot 		 *	record length must be multiple of 4
2147605Ssam 		 *	record length must not be zero
2157534Sroot 		 *	entry must fit in rest of this DIRBLKSIZ block
2167534Sroot 		 *	record must be large enough to contain name
2177605Ssam 		 * When dirchk is set we also check:
2187605Ssam 		 *	name is not longer than MAXNAMLEN
2197534Sroot 		 *	name must be as long as advertised, and null terminated
2207605Ssam 		 * Checking last two conditions is done only when dirchk is
2217605Ssam 		 * set, to save time.
2227534Sroot 		 */
2237534Sroot 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
2247534Sroot 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
2257605Ssam 		if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
2267605Ssam 		    ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
2277605Ssam 		    dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
2287534Sroot 			dirbad(dp, "mangled entry");
2296571Smckusic 			u.u_offset += i;
2307534Sroot 			entryoffsetinblock += i;
2316571Smckusic 			continue;
2326571Smckusic 		}
2337534Sroot 
2346571Smckusic 		/*
2357534Sroot 		 * If an appropriate sized slot has not yet been found,
2366571Smckusic 		 * check to see if one is available. Also accumulate space
2376571Smckusic 		 * in the current block so that we can determine if
2386571Smckusic 		 * compaction is viable.
2396571Smckusic 		 */
2407534Sroot 		if (slotstatus != FOUND) {
2417534Sroot 			int size = ep->d_reclen;
2427534Sroot 
2436571Smckusic 			if (ep->d_ino != 0)
2446571Smckusic 				size -= DIRSIZ(ep);
2456571Smckusic 			if (size > 0) {
2467534Sroot 				if (size >= slotneeded) {
2477534Sroot 					slotstatus = FOUND;
2487534Sroot 					slotoffset = u.u_offset;
2497534Sroot 					slotsize = ep->d_reclen;
2507534Sroot 				} else if (slotstatus == NONE) {
2517534Sroot 					slotfreespace += size;
2527534Sroot 					if (slotoffset == -1)
2537534Sroot 						slotoffset = u.u_offset;
2547534Sroot 					if (slotfreespace >= slotneeded) {
2557534Sroot 						slotstatus = COMPACT;
2567534Sroot 						slotsize =
2577534Sroot 						    u.u_offset+ep->d_reclen -
2587534Sroot 						      slotoffset;
2597534Sroot 					}
2606571Smckusic 				}
2616571Smckusic 			}
2626571Smckusic 		}
2637534Sroot 
2646571Smckusic 		/*
2657534Sroot 		 * Check for a name match.
2665972Swnj 		 */
2677534Sroot 		if (ep->d_ino) {
2687534Sroot 			if (ep->d_namlen == u.u_dent.d_namlen &&
2697534Sroot 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
2707534Sroot 				goto found;
2717534Sroot 		}
2727534Sroot 		prevoff = u.u_offset;
2736571Smckusic 		u.u_offset += ep->d_reclen;
2747534Sroot 		entryoffsetinblock += ep->d_reclen;
2757534Sroot 	}
2767534Sroot /* notfound: */
2777534Sroot 	/*
2787534Sroot 	 * If creating, and at end of pathname and current
2799166Ssam 	 * directory has not been removed, then can consider
2809166Ssam 	 * allowing file to be created.
2817534Sroot 	 */
2829166Ssam 	if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) {
2835972Swnj 		/*
2847534Sroot 		 * Access for write is interpreted as allowing
2857534Sroot 		 * creation of files in the directory.
2865972Swnj 		 */
2877534Sroot 		if (access(dp, IWRITE))
2887534Sroot 			goto bad;
2895972Swnj 		/*
2907534Sroot 		 * Return an indication of where the new directory
2917534Sroot 		 * entry should be put.  If we didn't find a slot,
2927534Sroot 		 * then set u.u_count to 0 indicating that the
2937534Sroot 		 * new slot belongs at the end of the directory.
2947534Sroot 		 * If we found a slot, then the new entry can be
2957534Sroot 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
2965972Swnj 		 */
2977534Sroot 		if (slotstatus == NONE)
2987534Sroot 			u.u_count = 0;
2997534Sroot 		else {
3007534Sroot 			u.u_offset = slotoffset;
3017534Sroot 			u.u_count = slotsize;
3025972Swnj 		}
3037534Sroot 		dp->i_flag |= IUPD|ICHG;
3047534Sroot 		if (bp)
3057534Sroot 			brelse(bp);
3067534Sroot 		brelse(nbp);
3075972Swnj 		/*
3087534Sroot 		 * We return with the directory locked, so that
3097534Sroot 		 * the parameters we set up above will still be
3107534Sroot 		 * valid if we actually decide to do a direnter().
3117534Sroot 		 * We return NULL to indicate that the entry doesn't
3127534Sroot 		 * currently exist, leaving a pointer to the (locked)
3137534Sroot 		 * directory inode in u.u_pdir.
3145972Swnj 		 */
3157534Sroot 		u.u_pdir = dp;
3167534Sroot 		return (NULL);
3177534Sroot 	}
3187534Sroot 	u.u_error = ENOENT;
3197534Sroot 	goto bad;
3207534Sroot found:
3217534Sroot 	/*
3227534Sroot 	 * Check that directory length properly reflects presence
3237534Sroot 	 * of this entry.
3247534Sroot 	 */
3257605Ssam 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
3267534Sroot 		dirbad(dp, "i_size too small");
3277605Ssam 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
3287534Sroot 		dp->i_flag |= IUPD|ICHG;
3297534Sroot 	}
3307534Sroot 
3317534Sroot 	/*
3327534Sroot 	 * Found component in pathname; save directory
3337534Sroot 	 * entry in u.u_dent, and release directory buffer.
3347534Sroot 	 */
3357825Sroot 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep));
3367534Sroot 	brelse(bp);
3377534Sroot 	bp = NULL;
3387534Sroot 
3397534Sroot 	/*
3407534Sroot 	 * If deleting, and at end of pathname, return
3417534Sroot 	 * parameters which can be used to remove file.
3429166Ssam 	 * If the lockparent flag isn't set, we return only
3439166Ssam 	 * the directory (in u.u_pdir), otherwise we go
3449166Ssam 	 * on and lock the inode, being careful with ".".
3457534Sroot 	 */
3469166Ssam 	if (flag == DELETE && *cp == 0) {
3477534Sroot 		/*
3487534Sroot 		 * Write access to directory required to delete files.
3497534Sroot 		 */
3507534Sroot 		if (access(dp, IWRITE))
3517534Sroot 			goto bad;
3529166Ssam 		u.u_pdir = dp;		/* for dirremove() */
3537534Sroot 		/*
3547534Sroot 		 * Return pointer to current entry in u.u_offset,
3557534Sroot 		 * and distance past previous entry (if there
3567534Sroot 		 * is a previous entry in this block) in u.u_count.
3577534Sroot 		 * Save directory inode pointer in u.u_pdir for dirremove().
3587534Sroot 		 */
3597534Sroot 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
3607534Sroot 			u.u_count = 0;
3617534Sroot 		else
3627534Sroot 			u.u_count = u.u_offset - prevoff;
3639166Ssam 		if (lockparent) {
3649166Ssam 			if (dp->i_number == u.u_dent.d_ino)
3659166Ssam 				dp->i_count++;
3669166Ssam 			else {
3679166Ssam 				dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
3689166Ssam 				if (dp == NULL) {
3699166Ssam 					iput(u.u_pdir);
3709166Ssam 					goto bad;
3719166Ssam 				}
3729166Ssam 			}
3739166Ssam 		}
3747534Sroot 		brelse(nbp);
3757534Sroot 		return (dp);
3767534Sroot 	}
3777534Sroot 
3787534Sroot 	/*
3797534Sroot 	 * Special handling for ".." allowing chdir out of mounted
3807534Sroot 	 * file system: indirect .. in root inode to reevaluate
3817534Sroot 	 * in directory file system was mounted on.
3827534Sroot 	 */
3837534Sroot 	if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
3847534Sroot 	    u.u_dent.d_name[2] == '\0') {
3857534Sroot 		if (dp == u.u_rdir)
3867534Sroot 			u.u_dent.d_ino = dp->i_number;
3877534Sroot 		else if (u.u_dent.d_ino == ROOTINO &&
3887534Sroot 		   dp->i_number == ROOTINO) {
3897534Sroot 			for (i = 1; i < NMOUNT; i++)
3907534Sroot 			if (mount[i].m_bufp != NULL &&
3917534Sroot 			   mount[i].m_dev == dp->i_dev) {
3926571Smckusic 				iput(dp);
3937534Sroot 				dp = mount[i].m_inodp;
3945972Swnj 				ilock(dp);
3955972Swnj 				dp->i_count++;
3967534Sroot 				fs = dp->i_fs;
3977534Sroot 				cp -= 2;     /* back over .. */
3987534Sroot 				goto dirloop2;
3995972Swnj 			}
40030Sbill 		}
4017534Sroot 	}
4027534Sroot 
4037534Sroot 	/*
4049166Ssam 	 * If rewriting (rename), return the inode and the
4059166Ssam 	 * information required to rewrite the present directory
4069166Ssam 	 * Must get inode of directory entry to verify it's a
4079166Ssam 	 * regular file, or empty directory.
4089166Ssam 	 */
4099166Ssam 	if ((flag == CREATE && lockparent) && *cp == 0) {
4109166Ssam 		if (access(dp, IWRITE))
4119166Ssam 			goto bad;
4129166Ssam 		u.u_pdir = dp;		/* for dirrewrite() */
4139166Ssam 		/*
4149166Ssam 		 * Careful about locking second inode.
4159166Ssam 		 * This can only occur if the target is ".".
4169166Ssam 		 */
4179166Ssam 		if (dp->i_number == u.u_dent.d_ino) {
4189166Ssam 			u.u_error = EISDIR;		/* XXX */
4199166Ssam 			goto bad;
4209166Ssam 		}
4219166Ssam 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
4229166Ssam 		if (dp == NULL) {
4239166Ssam 			iput(u.u_pdir);
4249166Ssam 			goto bad;
4259166Ssam 		}
4269166Ssam 		brelse(nbp);
4279166Ssam 		return (dp);
4289166Ssam 	}
4299166Ssam 
4309166Ssam 	/*
43112011Smckusick 	 * Check for symbolic link, which may require us to massage the
43212011Smckusick 	 * name before we continue translation.  We do not `iput' the
43312011Smckusick 	 * directory because we may need it again if the symbolic link
43412011Smckusick 	 * is relative to the current directory.  Instead we save it
43512011Smckusick 	 * unlocked as "pdp".  We must get the target inode before unlocking
43612011Smckusick 	 * the directory to insure that the inode will not be removed
43712011Smckusick 	 * before we get it.  We prevent deadlock by always fetching
43812011Smckusick 	 * inodes from the root, moving down the directory tree. Thus
43912011Smckusick 	 * when following backward pointers ".." we must unlock the
44012011Smckusick 	 * parent directory before getting the requested directory.
44112011Smckusick 	 * There is a potential race condition here if both the current
44212011Smckusick 	 * and parent directories are removed before the `iget' for the
44312011Smckusick 	 * inode associated with ".." returns.  We hope that this occurs
44412011Smckusick 	 * infrequently since we cannot avoid this race condition without
445*12492Ssam 	 * implementing a sophisticated deadlock detection algorithm.
44612011Smckusick 	 * Note also that this simple deadlock detection scheme will not
44712011Smckusick 	 * work if the file system has any hard links other than ".."
44812011Smckusick 	 * that point backwards in the directory structure.
4497534Sroot 	 */
4507534Sroot 	pdp = dp;
45112011Smckusick 	if (bcmp(u.u_dent.d_name, "..", 3) == 0) {
45212011Smckusick 		iunlock(pdp);	/* race to get the inode */
45312011Smckusick 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
45412011Smckusick 		if (dp == NULL)
45512011Smckusick 			goto bad2;
45612011Smckusick 	} else if (dp->i_number == u.u_dent.d_ino) {
45712011Smckusick 		dp->i_count++;	/* we want ourself, ie "." */
45812011Smckusick 	} else {
45912011Smckusick 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
46012011Smckusick 		iunlock(pdp);
46112011Smckusick 		if (dp == NULL)
46212011Smckusick 			goto bad2;
46312011Smckusick 	}
4647534Sroot 	fs = dp->i_fs;
4657534Sroot 
4667534Sroot 	/*
4677534Sroot 	 * Check for symbolic link
4687534Sroot 	 */
4697534Sroot 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
4707825Sroot 		u_int pathlen = strlen(cp) + 1;
4717534Sroot 
4727534Sroot 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
4737534Sroot 		    ++nlink > MAXSYMLINKS) {
4747534Sroot 			u.u_error = ELOOP;
4757534Sroot 			goto bad2;
4767534Sroot 		}
4778957Sroot 		ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
4787751Sroot 		u.u_error =
4799166Ssam 		    rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size,
4807825Sroot 			0, 1, (int *)0);
4817534Sroot 		if (u.u_error)
4827534Sroot 			goto bad2;
4837534Sroot 		cp = nbp->b_un.b_addr;
4847534Sroot 		iput(dp);
4855972Swnj 		if (*cp == '/') {
4867534Sroot 			irele(pdp);
4875972Swnj 			while (*cp == '/')
4885972Swnj 				cp++;
4897534Sroot 			if ((dp = u.u_rdir) == NULL)
4907534Sroot 				dp = rootdir;
4917534Sroot 			ilock(dp);
4927534Sroot 			dp->i_count++;
4937534Sroot 		} else {
4947534Sroot 			dp = pdp;
4957534Sroot 			ilock(dp);
4965972Swnj 		}
4977534Sroot 		fs = dp->i_fs;
4987534Sroot 		goto dirloop;
49930Sbill 	}
5007534Sroot 
50130Sbill 	/*
5027534Sroot 	 * Not a symbolic link.  If more pathname,
5037534Sroot 	 * continue at next component, else return.
50430Sbill 	 */
5057534Sroot 	if (*cp == '/') {
5067534Sroot 		while (*cp == '/')
5077534Sroot 			cp++;
5089166Ssam 		irele(pdp);
5097534Sroot 		goto dirloop;
51030Sbill 	}
5115972Swnj 	brelse(nbp);
5129166Ssam 	if (lockparent)
5139166Ssam 		u.u_pdir = pdp;
5149166Ssam 	else
5159166Ssam 		irele(pdp);
5167534Sroot 	return (dp);
5177534Sroot bad2:
5187534Sroot 	irele(pdp);
5197534Sroot bad:
5207534Sroot 	if (bp)
5217534Sroot 		brelse(bp);
5227534Sroot 	if (dp)
5237534Sroot 		iput(dp);
5247534Sroot 	brelse(nbp);
5256571Smckusic 	return (NULL);
52630Sbill }
52730Sbill 
5287534Sroot dirbad(ip, how)
5297534Sroot 	struct inode *ip;
5307534Sroot 	char *how;
5317534Sroot {
5327534Sroot 
5337534Sroot 	printf("%s: bad dir ino %d at offset %d: %s\n",
5347534Sroot 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
5357534Sroot }
5367534Sroot 
5377534Sroot dirbadname(ep)
5387534Sroot 	register struct direct *ep;
5397534Sroot {
5407534Sroot 	register int i;
5417534Sroot 
5427534Sroot 	for (i = 0; i < ep->d_namlen; i++)
5437534Sroot 		if (ep->d_name[i] == 0)
5447534Sroot 			return (1);
5457534Sroot 	return (ep->d_name[i]);
5467534Sroot }
5477534Sroot 
54830Sbill /*
5497534Sroot  * Write a directory entry after a call to namei, using the parameters
5507534Sroot  * which it left in the u. area.  The argument ip is the inode which
5517534Sroot  * the new directory entry will refer to.  The u. area field u.u_pdir is
5527534Sroot  * a pointer to the directory to be written, which was left locked by
5537534Sroot  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
5547534Sroot  * how the space for the new entry is to be gotten.
5557534Sroot  */
5567534Sroot direnter(ip)
5577534Sroot 	struct inode *ip;
5585972Swnj {
5597534Sroot 	register struct direct *ep, *nep;
5607534Sroot 	struct buf *bp;
56111639Ssam 	int loc, spacefree, error = 0;
5628631Sroot 	u_int dsize;
5638631Sroot 	int newentrysize;
5647534Sroot 	char *dirbuf;
5655972Swnj 
5667534Sroot 	u.u_dent.d_ino = ip->i_number;
5677534Sroot 	u.u_segflg = 1;
5687534Sroot 	newentrysize = DIRSIZ(&u.u_dent);
5697534Sroot 	if (u.u_count == 0) {
5707534Sroot 		/*
5717534Sroot 		 * If u.u_count is 0, then namei could find no space in the
5727534Sroot 		 * directory.  In this case u.u_offset will be on a directory
5737534Sroot 		 * block boundary and we will write the new entry into a fresh
5747534Sroot 		 * block.
5757534Sroot 		 */
5767534Sroot 		if (u.u_offset&(DIRBLKSIZ-1))
5777534Sroot 			panic("wdir: newblk");
5787534Sroot 		u.u_dent.d_reclen = DIRBLKSIZ;
57910849Ssam 		error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
5808631Sroot 		    newentrysize, u.u_offset, 1, (int *)0);
5817534Sroot 		iput(u.u_pdir);
58210849Ssam 		return (error);
5837534Sroot 	}
5847534Sroot 
5857534Sroot 	/*
5867534Sroot 	 * If u.u_count is non-zero, then namei found space for the
5877534Sroot 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
5887534Sroot 	 * in the directory.  To use this space, we may have to compact
5897534Sroot 	 * the entries located there, by copying them together towards
5907534Sroot 	 * the beginning of the block, leaving the free space in
5917534Sroot 	 * one usable chunk at the end.
5927534Sroot 	 */
5937534Sroot 
5947534Sroot 	/*
5957534Sroot 	 * Increase size of directory if entry eats into new space.
5967534Sroot 	 * This should never push the size past a new multiple of
5977534Sroot 	 * DIRBLKSIZE.
5987534Sroot 	 */
5999166Ssam 	if (u.u_offset + u.u_count > u.u_pdir->i_size)
6007534Sroot 		u.u_pdir->i_size = u.u_offset + u.u_count;
6017534Sroot 
6027534Sroot 	/*
6037534Sroot 	 * Get the block containing the space for the new directory
60410849Ssam 	 * entry.  Should return error by result instead of u.u_error.
6057534Sroot 	 */
6067605Ssam 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
6079166Ssam 	if (bp == 0) {
6089166Ssam 		iput(u.u_pdir);
60910849Ssam 		return (u.u_error);
6109166Ssam 	}
6117534Sroot 
6127534Sroot 	/*
6137534Sroot 	 * Find space for the new entry.  In the simple case, the
6147534Sroot 	 * entry at offset base will have the space.  If it does
6157534Sroot 	 * not, then namei arranged that compacting the region
6167534Sroot 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
6177534Sroot 	 */
6187534Sroot 	ep = (struct direct *)dirbuf;
6197534Sroot 	dsize = DIRSIZ(ep);
62011639Ssam 	spacefree = ep->d_reclen - dsize;
6217534Sroot 	for (loc = ep->d_reclen; loc < u.u_count; ) {
6227534Sroot 		nep = (struct direct *)(dirbuf + loc);
6237534Sroot 		if (ep->d_ino) {
6247534Sroot 			/* trim the existing slot */
6257534Sroot 			ep->d_reclen = dsize;
6267534Sroot 			ep = (struct direct *)((char *)ep + dsize);
6277534Sroot 		} else {
6287534Sroot 			/* overwrite; nothing there; header is ours */
62911639Ssam 			spacefree += dsize;
6307534Sroot 		}
6317534Sroot 		dsize = DIRSIZ(nep);
63211639Ssam 		spacefree += nep->d_reclen - dsize;
6337534Sroot 		loc += nep->d_reclen;
6347825Sroot 		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
6357534Sroot 	}
6367534Sroot 	/*
6377534Sroot 	 * Update the pointer fields in the previous entry (if any),
6387534Sroot 	 * copy in the new entry, and write out the block.
6397534Sroot 	 */
6407534Sroot 	if (ep->d_ino == 0) {
64111639Ssam 		if (spacefree + dsize < newentrysize)
6427534Sroot 			panic("wdir: compact1");
64311639Ssam 		u.u_dent.d_reclen = spacefree + dsize;
6447534Sroot 	} else {
64511639Ssam 		if (spacefree < newentrysize)
6467534Sroot 			panic("wdir: compact2");
64711639Ssam 		u.u_dent.d_reclen = spacefree;
6487534Sroot 		ep->d_reclen = dsize;
6497534Sroot 		ep = (struct direct *)((char *)ep + dsize);
6507534Sroot 	}
6518672Sroot 	bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
6527534Sroot 	bwrite(bp);
6537534Sroot 	u.u_pdir->i_flag |= IUPD|ICHG;
6547534Sroot 	iput(u.u_pdir);
65510849Ssam 	return (error);
6565972Swnj }
6576571Smckusic 
6589166Ssam /*
6599166Ssam  * Remove a directory entry after a call to namei, using the
6609166Ssam  * parameters which it left in the u. area.  The u. entry
6619166Ssam  * u_offset contains the offset into the directory of the
6629166Ssam  * entry to be eliminated.  The u_count field contains the
6639166Ssam  * size of the previous record in the directory.  If this
6649166Ssam  * is 0, the first entry is being deleted, so we need only
6659166Ssam  * zero the inode number to mark the entry as free.  If the
6669166Ssam  * entry isn't the first in the directory, we must reclaim
6679166Ssam  * the space of the now empty record by adding the record size
6689166Ssam  * to the size of the previous entry.
6699166Ssam  */
6707534Sroot dirremove()
6716571Smckusic {
6727534Sroot 	register struct inode *dp = u.u_pdir;
6737534Sroot 	register struct buf *bp;
6747534Sroot 	struct direct *ep;
6756571Smckusic 
6769269Ssam 	if (u.u_count == 0) {
6777534Sroot 		/*
6787534Sroot 		 * First entry in block: set d_ino to zero.
6797534Sroot 		 */
6809269Ssam 		u.u_dent.d_ino = 0;
6818619Sroot 		(void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
6828631Sroot 		    (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
6839269Ssam 	} else {
6847534Sroot 		/*
6857534Sroot 		 * Collapse new free space into previous entry.
6867534Sroot 		 */
6877825Sroot 		bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
6887534Sroot 		if (bp == 0)
6897534Sroot 			return (0);
6907534Sroot 		ep->d_reclen += u.u_dent.d_reclen;
6917534Sroot 		bwrite(bp);
6927534Sroot 		dp->i_flag |= IUPD|ICHG;
6937534Sroot 	}
6947534Sroot 	return (1);
6956571Smckusic }
6967534Sroot 
6977605Ssam /*
6989166Ssam  * Rewrite an existing directory entry to point at the inode
6999166Ssam  * supplied.  The parameters describing the directory entry are
7009166Ssam  * set up by a call to namei.
7019166Ssam  */
7029166Ssam dirrewrite(dp, ip)
7039166Ssam 	struct inode *dp, *ip;
7049166Ssam {
7059166Ssam 
7069166Ssam 	u.u_dent.d_ino = ip->i_number;
7079166Ssam 	u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
7089166Ssam 		(int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
7099166Ssam 	iput(dp);
7109166Ssam }
7119166Ssam 
7129166Ssam /*
7137605Ssam  * Return buffer with contents of block "offset"
7147605Ssam  * from the beginning of directory "ip".  If "res"
7157605Ssam  * is non-zero, fill it in with a pointer to the
7167605Ssam  * remaining space in the directory.
7177605Ssam  */
7187534Sroot struct buf *
7197605Ssam blkatoff(ip, offset, res)
7207534Sroot 	struct inode *ip;
7217534Sroot 	off_t offset;
7227534Sroot 	char **res;
7237534Sroot {
7247534Sroot 	register struct fs *fs = ip->i_fs;
7258672Sroot 	daddr_t lbn = lblkno(fs, offset);
7267534Sroot 	int base = blkoff(fs, offset);
7277534Sroot 	int bsize = blksize(fs, ip, lbn);
7288672Sroot 	daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
7297534Sroot 	register struct buf *bp;
7307534Sroot 
7317534Sroot 	if (u.u_error)
7327534Sroot 		return (0);
7337534Sroot 	bp = bread(ip->i_dev, bn, bsize);
7347534Sroot 	if (bp->b_flags & B_ERROR) {
7357534Sroot 		brelse(bp);
7367534Sroot 		return (0);
7377534Sroot 	}
7387534Sroot 	if (res)
7397534Sroot 		*res = bp->b_un.b_addr + base;
7407534Sroot 	return (bp);
7417534Sroot }
7429166Ssam 
7439166Ssam /*
7449166Ssam  * Check if a directory is empty or not.
7459166Ssam  * Inode supplied must be locked.
7469166Ssam  */
7479166Ssam dirempty(ip)
7489863Ssam 	register struct inode *ip;
7499166Ssam {
7509166Ssam 	register off_t off;
7519166Ssam 	struct direct dbuf;
7529166Ssam 	register struct direct *dp = &dbuf;
7539863Ssam 	int error, count;
7549166Ssam 
7559166Ssam 	for (off = 0; off < ip->i_size; off += dp->d_reclen) {
7569166Ssam 		error = rdwri(UIO_READ, ip, (caddr_t)dp,
7579863Ssam 			sizeof (struct direct), off, 1, &count);
7589863Ssam 		count = sizeof (struct direct) - count;
7599863Ssam #define	MINDIRSIZ (sizeof (struct direct) - (MAXNAMLEN + 1))
7609863Ssam 		if (error || count < MINDIRSIZ || count < DIRSIZ(dp))
7619166Ssam 			return (0);
7629166Ssam 		if (dp->d_ino == 0)
7639166Ssam 			continue;
7649166Ssam 		if (dp->d_name[0] != '.')
7659166Ssam 			return (0);
7669166Ssam 		if (dp->d_namlen == 1 ||
7679166Ssam 		    (dp->d_namlen == 2 && dp->d_name[1] == '.'))
7689166Ssam 			continue;
7699166Ssam 		return (0);
7709166Ssam 	}
7719166Ssam 	return (1);
7729166Ssam }
773