xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 15798)
1*15798Smckusick /*	vfs_lookup.c	6.4	84/01/03	*/
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"
1415660Smckusick #include "../h/kernel.h"
1530Sbill 
167605Ssam struct	buf *blkatoff();
179166Ssam int	dirchk = 0;
18*15798Smckusick 
1930Sbill /*
20*15798Smckusick  * Structures associated with name cacheing.
21*15798Smckusick  */
22*15798Smckusick #define	NCHHASH		32	/* size of hash table */
23*15798Smckusick 
24*15798Smckusick #if	((NCHHASH)&((NCHHASH)-1)) != 0
25*15798Smckusick #define	NHASH(h, i, d)	((unsigned)((h) + (i) + 13 * (int)(d)) % (NCHHASH))
26*15798Smckusick #else
27*15798Smckusick #define	NHASH(h, i, d)	((unsigned)((h) + (i) + 13 * (int)(d)) & ((NCHHASH)-1))
28*15798Smckusick #endif
29*15798Smckusick 
30*15798Smckusick union	nchash	{
31*15798Smckusick 	union	nchash	*nch_head[2];
32*15798Smckusick 	struct	nch	*nch_chain[2];
33*15798Smckusick } nchash[NCHHASH];
34*15798Smckusick #define	nch_forw	nch_chain[0]
35*15798Smckusick #define	nch_back	nch_chain[1]
36*15798Smckusick 
37*15798Smckusick struct	nch	*nchhead, **nchtail;		/* LRU chain pointers */
38*15798Smckusick 
39*15798Smckusick struct	nchstats {		/* stats on usefulness */
40*15798Smckusick 	long	ncs_goodhits;		/* hits that we can reall use */
41*15798Smckusick 	long	ncs_badhits;		/* hits we must drop */
42*15798Smckusick 	long	ncs_miss;		/* misses */
43*15798Smckusick 	long	ncs_long;		/* long names that ignore cache */
44*15798Smckusick 	long	ncs_pass2;		/* names found with passes == 2 */
45*15798Smckusick 	long	ncs_2passes;		/* number of times we attempt it */
46*15798Smckusick } nchstats;
47*15798Smckusick 
48*15798Smckusick /*
497534Sroot  * Convert a pathname into a pointer to a locked inode,
507534Sroot  * with side effects usable in creating and removing files.
517534Sroot  * This is a very central and rather complicated routine.
5230Sbill  *
537534Sroot  * The func argument gives the routine which returns successive
549166Ssam  * characters of the name to be translated.
557534Sroot  *
569166Ssam  * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether
579166Ssam  * the name is to be (looked up, created, deleted).  If flag has
589166Ssam  * LOCKPARENT or'ed into it and the target of the pathname exists,
599166Ssam  * namei returns both the target and its parent directory locked.
609166Ssam  * If the file system is not maintained in a strict tree hierarchy,
619166Ssam  * this can result in a deadlock situation.  When creating and
629166Ssam  * LOCKPARENT is specified, the target may not be ".".  When deleting
639166Ssam  * and LOCKPARENT is specified, the target may be ".", but the caller
649166Ssam  * must check to insure it does an irele and iput instead of two iputs.
659166Ssam  *
669166Ssam  * The follow argument is 1 when symbolic links are to be followed
679166Ssam  * when they occur at the end of the name translation process.
689166Ssam  *
69*15798Smckusick  * Name caching works as follows:
707534Sroot  *
71*15798Smckusick  *	names found by directory scans are retained in a cache
72*15798Smckusick  *	for future reference.  It is managed LRU, so frequently
73*15798Smckusick  *	used names will hang around.  Cache is indexed by hash value
74*15798Smckusick  *	obtained from (ino,dev,name) where ino & dev refer to the
75*15798Smckusick  *	directory containing name.
76*15798Smckusick  *
77*15798Smckusick  *	For simplicity (and economy of storage), names longer than
78*15798Smckusick  *	some (small) maximum length are not cached, they occur
79*15798Smckusick  *	infrequently in any case, and are almost never of interest.
80*15798Smckusick  *
81*15798Smckusick  *	Upon reaching the last segment of a path, if the reference
82*15798Smckusick  *	is for DELETE, or NOCACHE is set (rewrite), and the
83*15798Smckusick  *	name is located in the cache, it will be dropped.
84*15798Smckusick  *
85*15798Smckusick  *	We must be sure never to enter the name ".." into the cache
86*15798Smckusick  *	because of the extremely kludgey way that rename() alters
87*15798Smckusick  *	".." in a situation like
88*15798Smckusick  *		mv a/x b/x
89*15798Smckusick  *	where x is a directory, and x/.. is the ".." in question.
90*15798Smckusick  *
91*15798Smckusick  * Overall outline of namei:
92*15798Smckusick  *
937534Sroot  *	copy in name
947534Sroot  *	get starting directory
957534Sroot  * dirloop:
967534Sroot  *	check accessibility of directory
977534Sroot  * dirloop2:
987534Sroot  *	copy next component of name to u.u_dent
997534Sroot  *	handle degenerate case where name is null string
100*15798Smckusick  *	look for name in cache, if found, then if at end of path
101*15798Smckusick  *	  and deleting or creating, drop it, else to haveino
1027534Sroot  *	search for name in directory, to found or notfound
1037534Sroot  * notfound:
1049166Ssam  *	if creating, return locked directory, leaving info on avail. slots
1057534Sroot  *	else return error
1067534Sroot  * found:
1077534Sroot  *	if at end of path and deleting, return information to allow delete
108*15798Smckusick  *	if at end of path and rewriting (create and LOCKPARENT), lock target
1099166Ssam  *	  inode and return info to allow rewrite
1107534Sroot  *	if .. and on mounted filesys, look in mount table for parent
111*15798Smckusick  *	if not at end, if neither creating nor deleting, add name to cache
112*15798Smckusick  * haveino:
1137534Sroot  *	if symbolic link, massage name in buffer and continue at dirloop
1147534Sroot  *	if more components of name, do next level at dirloop
1157534Sroot  *	return the answer as locked inode
1169166Ssam  *
1179166Ssam  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode,
1189166Ssam  *	 but unlocked.
11930Sbill  */
12030Sbill struct inode *
1215972Swnj namei(func, flag, follow)
1225972Swnj 	int (*func)(), flag, follow;
12330Sbill {
1247534Sroot 	register char *cp;		/* pointer into pathname argument */
1257534Sroot /* these variables refer to things which must be freed or unlocked */
1267534Sroot 	register struct inode *dp = 0;	/* the directory we are searching */
127*15798Smckusick 	register struct nch *ncp;	/* cache slot for entry */
1287534Sroot 	register struct fs *fs;		/* file system that directory is in */
1297534Sroot 	register struct buf *bp = 0;	/* a buffer of directory entries */
1307534Sroot 	register struct direct *ep;	/* the current directory entry */
1317534Sroot 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
1327534Sroot 	register struct buf *nbp;	/* buffer storing path name argument */
1337534Sroot /* these variables hold information about the search for a slot */
1347534Sroot 	enum {NONE, COMPACT, FOUND} slotstatus;
1357534Sroot 	int slotoffset = -1;		/* offset of area with free space */
1367534Sroot 	int slotsize;			/* size of area at slotoffset */
1377534Sroot 	int slotfreespace;		/* amount of space free in slot */
1387534Sroot 	int slotneeded;			/* size of the entry we're seeking */
1397534Sroot /* */
14015660Smckusick 	int numdirpasses;		/* strategy for directory search */
14115660Smckusick 	int endsearch;			/* offset to end directory search */
1427534Sroot 	int prevoff;			/* u.u_offset of previous entry */
1437534Sroot 	int nlink = 0;			/* number of symbolic links taken */
1447534Sroot 	struct inode *pdp;		/* saved dp during symlink work */
1457534Sroot 	int i;
1469166Ssam 	int lockparent;
147*15798Smckusick 	int docache;
148*15798Smckusick 	unsigned hash;			/* value of name hash for entry */
149*15798Smckusick 	union nchash *nhp;		/* cache chain head for entry */
150*15798Smckusick 	int isdotdot;			/* != 0 if current name is ".." */
15130Sbill 
1529166Ssam 	lockparent = flag & LOCKPARENT;
153*15798Smckusick 	docache = (flag & NOCACHE) ^ NOCACHE;
154*15798Smckusick 	flag &= ~(LOCKPARENT|NOCACHE);
155*15798Smckusick 	if (flag == DELETE)
156*15798Smckusick 		docache = 0;
15730Sbill 	/*
1587534Sroot 	 * Get a buffer for the name to be translated, and copy the
1597534Sroot 	 * name into the buffer.
1605972Swnj 	 */
1616571Smckusic 	nbp = geteblk(MAXPATHLEN);
1627534Sroot 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
163*15798Smckusick 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != LOOKUP) {
1646066Sroot 			u.u_error = EPERM;
1657534Sroot 			goto bad;
1666066Sroot 		}
1676066Sroot 		cp++;
1686571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
1695972Swnj 			u.u_error = ENOENT;
1707534Sroot 			goto bad;
1715972Swnj 		}
1725972Swnj 	}
1737534Sroot 	if (u.u_error)
1747534Sroot 		goto bad;
1757534Sroot 
1765972Swnj 	/*
1777534Sroot 	 * Get starting directory.
17830Sbill 	 */
1797534Sroot 	cp = nbp->b_un.b_addr;
1805972Swnj 	if (*cp == '/') {
1815972Swnj 		while (*cp == '/')
1825972Swnj 			cp++;
18330Sbill 		if ((dp = u.u_rdir) == NULL)
18430Sbill 			dp = rootdir;
1857534Sroot 	} else
1867534Sroot 		dp = u.u_cdir;
1877534Sroot 	fs = dp->i_fs;
1885972Swnj 	ilock(dp);
1895972Swnj 	dp->i_count++;
1907534Sroot 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
1917534Sroot 
1927534Sroot 	/*
1937534Sroot 	 * We come to dirloop to search a new directory.
1947534Sroot 	 * The directory must be locked so that it can be
1957534Sroot 	 * iput, and fs must be already set to dp->i_fs.
1967534Sroot 	 */
1976571Smckusic dirloop:
19830Sbill 	/*
1997534Sroot 	 * Check accessiblity of directory.
20030Sbill 	 */
2017534Sroot 	if ((dp->i_mode&IFMT) != IFDIR) {
20230Sbill 		u.u_error = ENOTDIR;
2037534Sroot 		goto bad;
2047534Sroot 	}
2057534Sroot 	if (access(dp, IEXEC))
2067534Sroot 		goto bad;
2077534Sroot 
2086384Swnj dirloop2:
2097534Sroot 	/*
2107534Sroot 	 * Copy next component of name to u.u_dent.
2117534Sroot 	 */
212*15798Smckusick 	hash = 0;
2137534Sroot 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
2146571Smckusic 		if (i >= MAXNAMLEN) {
2155972Swnj 			u.u_error = ENOENT;
2167534Sroot 			goto bad;
2175972Swnj 		}
2187534Sroot 		u.u_dent.d_name[i++] = *cp;
219*15798Smckusick 		hash += (unsigned char)*cp * i;
2205972Swnj 	}
2216571Smckusic 	u.u_dent.d_namlen = i;
2227534Sroot 	u.u_dent.d_name[i] = 0;
2237534Sroot 
2247534Sroot 	/*
2257534Sroot 	 * Check for degenerate name (e.g. / or "")
2267534Sroot 	 * which is a way of talking about a directory,
2277534Sroot 	 * e.g. like "/." or ".".
2287534Sroot 	 */
2297534Sroot 	if (u.u_dent.d_name[0] == 0) {
230*15798Smckusick 		if (flag != LOOKUP || lockparent) {
23114937Smckusick 			u.u_error = EISDIR;
2327534Sroot 			goto bad;
2335972Swnj 		}
2346571Smckusic 		brelse(nbp);
2356571Smckusic 		return (dp);
2365972Swnj 	}
2377534Sroot 
2386571Smckusic 	/*
239*15798Smckusick 	 * We now have a segment name to search for, and a directory to search.
240*15798Smckusick 	 *
241*15798Smckusick 	 * Before tediously performing a linear scan of the directory,
242*15798Smckusick 	 * check the name cache to see if the directory/name pair
243*15798Smckusick 	 * we are looking for is known already.  We don't do this
244*15798Smckusick 	 * if the segment name is long, simply so the cache can avoid
245*15798Smckusick 	 * holding long names (which would either waste space, or
246*15798Smckusick 	 * add greatly to the complexity).
247*15798Smckusick 	 */
248*15798Smckusick 	if (u.u_dent.d_namlen > NCHNAMLEN) {
249*15798Smckusick 		nchstats.ncs_long++;
250*15798Smckusick 		docache = 0;
251*15798Smckusick 	} else {
252*15798Smckusick 		nhp = &nchash[NHASH(hash, dp->i_number, dp->i_dev)];
253*15798Smckusick 		for (ncp = nhp->nch_forw; ncp != (struct nch *)nhp;
254*15798Smckusick 		    ncp = ncp->nc_forw) {
255*15798Smckusick 			if (ncp->nc_ino == dp->i_number &&
256*15798Smckusick 			    ncp->nc_dev == dp->i_dev &&
257*15798Smckusick 			    ncp->nc_nlen == u.u_dent.d_namlen &&
258*15798Smckusick 			    !bcmp(ncp->nc_name, u.u_dent.d_name, ncp->nc_nlen))
259*15798Smckusick 				break;
260*15798Smckusick 		}
261*15798Smckusick 
262*15798Smckusick 		if (ncp == (struct nch *)nhp) {
263*15798Smckusick 			nchstats.ncs_miss++;
264*15798Smckusick 			ncp = NULL;
265*15798Smckusick 		} else {
266*15798Smckusick 			if (*cp == '/' || docache) {
267*15798Smckusick 
268*15798Smckusick 				nchstats.ncs_goodhits++;
269*15798Smckusick 
270*15798Smckusick 					/*
271*15798Smckusick 					 * move this slot to end of LRU
272*15798Smckusick 					 * chain, if not already there
273*15798Smckusick 					 */
274*15798Smckusick 				if (ncp->nc_nxt) {
275*15798Smckusick 						/* remove from LRU chain */
276*15798Smckusick 					*ncp->nc_prev = ncp->nc_nxt;
277*15798Smckusick 					ncp->nc_nxt->nc_prev = ncp->nc_prev;
278*15798Smckusick 
279*15798Smckusick 						/* and replace at end of it */
280*15798Smckusick 					ncp->nc_nxt = NULL;
281*15798Smckusick 					ncp->nc_prev = nchtail;
282*15798Smckusick 					*nchtail = ncp;
283*15798Smckusick 					nchtail = &ncp->nc_nxt;
284*15798Smckusick 				}
285*15798Smckusick 
286*15798Smckusick 				pdp = dp;
287*15798Smckusick 				dp = ncp->nc_ip;
288*15798Smckusick 				if (dp == NULL)
289*15798Smckusick 					panic("nami: null cache ino");
290*15798Smckusick 				if (pdp != dp) {
291*15798Smckusick 					ilock(dp);
292*15798Smckusick 					dp->i_count++;
293*15798Smckusick 					iunlock(pdp);
294*15798Smckusick 				} else
295*15798Smckusick 					dp->i_count++;
296*15798Smckusick 
297*15798Smckusick 				u.u_dent.d_ino = dp->i_number;
298*15798Smckusick 				/* u_dent.d_reclen is garbage ... */
299*15798Smckusick 
300*15798Smckusick 				goto haveino;
301*15798Smckusick 			}
302*15798Smckusick 
303*15798Smckusick 			/*
304*15798Smckusick 			 * last segment and we are renaming or deleting
305*15798Smckusick 			 * or otherwise don't want cache entry to exist
306*15798Smckusick 			 */
307*15798Smckusick 
308*15798Smckusick 			nchstats.ncs_badhits++;
309*15798Smckusick 
310*15798Smckusick 				/* remove from LRU chain */
311*15798Smckusick 			*ncp->nc_prev = ncp->nc_nxt;
312*15798Smckusick 			if (ncp->nc_nxt)
313*15798Smckusick 				ncp->nc_nxt->nc_prev = ncp->nc_prev;
314*15798Smckusick 			else
315*15798Smckusick 				nchtail = ncp->nc_prev;
316*15798Smckusick 
317*15798Smckusick 				/* remove from hash chain */
318*15798Smckusick 			remque(ncp);
319*15798Smckusick 
320*15798Smckusick 				/* release ref on the inode */
321*15798Smckusick 			irele(ncp->nc_ip);
322*15798Smckusick 			ncp->nc_ip = NULL;
323*15798Smckusick 
324*15798Smckusick 				/* insert at head of LRU list (first to grab) */
325*15798Smckusick 			ncp->nc_nxt = nchhead;
326*15798Smckusick 			ncp->nc_prev = &nchhead;
327*15798Smckusick 			nchhead->nc_prev = &ncp->nc_nxt;
328*15798Smckusick 			nchhead = ncp;
329*15798Smckusick 
330*15798Smckusick 				/* and make a dummy hash chain */
331*15798Smckusick 			ncp->nc_forw = ncp;
332*15798Smckusick 			ncp->nc_back = ncp;
333*15798Smckusick 
334*15798Smckusick 			ncp = NULL;
335*15798Smckusick 		}
336*15798Smckusick 	}
337*15798Smckusick 
338*15798Smckusick 	/*
3397534Sroot 	 * Suppress search for slots unless creating
3407534Sroot 	 * file and at end of pathname, in which case
3417534Sroot 	 * we watch for a place to put the new file in
3427534Sroot 	 * case it doesn't already exist.
3436571Smckusic 	 */
3447534Sroot 	slotstatus = FOUND;
3459166Ssam 	if (flag == CREATE && *cp == 0) {
3467534Sroot 		slotstatus = NONE;
3477534Sroot 		slotfreespace = 0;
3487534Sroot 		slotneeded = DIRSIZ(&u.u_dent);
3497534Sroot 	}
35015660Smckusick 	/*
35115660Smckusick 	 * If this is the same directory that this process
35215660Smckusick 	 * previously searched, pick up where we last left off.
353*15798Smckusick 	 * We cache only lookups as these are the most common
35415660Smckusick 	 * and have the greatest payoff. Caching CREATE has little
35515660Smckusick 	 * benefit as it usually must search the entire directory
35615660Smckusick 	 * to determine that the entry does not exist. Caching the
35715660Smckusick 	 * location of the last DELETE has not reduced profiling time
35815660Smckusick 	 * and hence has been removed in the interest of simplicity.
35915660Smckusick 	 */
36015660Smckusick 	if (flag != LOOKUP || dp->i_number != u.u_ncache.nc_inumber ||
36115660Smckusick 	    dp->i_dev != u.u_ncache.nc_dev) {
36215660Smckusick 		u.u_offset = 0;
36315660Smckusick 		numdirpasses = 1;
36415660Smckusick 	} else {
365*15798Smckusick 		if ((dp->i_flag & ICHG) || dp->i_ctime >= u.u_ncache.nc_time) {
36615660Smckusick 			u.u_ncache.nc_prevoffset &= ~(DIRBLKSIZ - 1);
36715660Smckusick 			u.u_ncache.nc_time = time.tv_sec;
36815660Smckusick 		}
36915660Smckusick 		u.u_offset = u.u_ncache.nc_prevoffset;
37015660Smckusick 		entryoffsetinblock = blkoff(fs, u.u_offset);
37115660Smckusick 		if (entryoffsetinblock != 0) {
37215660Smckusick 			bp = blkatoff(dp, u.u_offset, (char **)0);
37315660Smckusick 			if (bp == 0)
37415660Smckusick 				goto bad;
37515660Smckusick 		}
37615660Smckusick 		numdirpasses = 2;
377*15798Smckusick 		nchstats.ncs_2passes++;
37815660Smckusick 	}
37915660Smckusick 	endsearch = roundup(dp->i_size, DIRBLKSIZ);
3807534Sroot 
38115660Smckusick searchloop:
38215660Smckusick 	while (u.u_offset < endsearch) {
3835972Swnj 		/*
3845972Swnj 		 * If offset is on a block boundary,
3855972Swnj 		 * read the next directory block.
3865972Swnj 		 * Release previous if it exists.
3875972Swnj 		 */
3886571Smckusic 		if (blkoff(fs, u.u_offset) == 0) {
3895972Swnj 			if (bp != NULL)
3905972Swnj 				brelse(bp);
3917605Ssam 			bp = blkatoff(dp, u.u_offset, (char **)0);
3927534Sroot 			if (bp == 0)
3937534Sroot 				goto bad;
3947534Sroot 			entryoffsetinblock = 0;
3955972Swnj 		}
3967534Sroot 
3975972Swnj 		/*
3987534Sroot 		 * If still looking for a slot, and at a DIRBLKSIZE
3997534Sroot 		 * boundary, have to start looking for free space
4007534Sroot 		 * again.
4016571Smckusic 		 */
4027534Sroot 		if (slotstatus == NONE &&
4037534Sroot 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
4047534Sroot 			slotoffset = -1;
4057534Sroot 			slotfreespace = 0;
4067534Sroot 		}
4077534Sroot 
4087534Sroot 		/*
4097534Sroot 		 * Get pointer to next entry, and do consistency checking:
4107534Sroot 		 *	record length must be multiple of 4
4117605Ssam 		 *	record length must not be zero
4127534Sroot 		 *	entry must fit in rest of this DIRBLKSIZ block
4137534Sroot 		 *	record must be large enough to contain name
4147605Ssam 		 * When dirchk is set we also check:
4157605Ssam 		 *	name is not longer than MAXNAMLEN
4167534Sroot 		 *	name must be as long as advertised, and null terminated
4177605Ssam 		 * Checking last two conditions is done only when dirchk is
4187605Ssam 		 * set, to save time.
4197534Sroot 		 */
4207534Sroot 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
4217534Sroot 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
4227605Ssam 		if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
4237605Ssam 		    ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
4247605Ssam 		    dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
4257534Sroot 			dirbad(dp, "mangled entry");
4266571Smckusic 			u.u_offset += i;
4277534Sroot 			entryoffsetinblock += i;
4286571Smckusic 			continue;
4296571Smckusic 		}
4307534Sroot 
4316571Smckusic 		/*
4327534Sroot 		 * If an appropriate sized slot has not yet been found,
4336571Smckusic 		 * check to see if one is available. Also accumulate space
4346571Smckusic 		 * in the current block so that we can determine if
4356571Smckusic 		 * compaction is viable.
4366571Smckusic 		 */
4377534Sroot 		if (slotstatus != FOUND) {
4387534Sroot 			int size = ep->d_reclen;
4397534Sroot 
4406571Smckusic 			if (ep->d_ino != 0)
4416571Smckusic 				size -= DIRSIZ(ep);
4426571Smckusic 			if (size > 0) {
4437534Sroot 				if (size >= slotneeded) {
4447534Sroot 					slotstatus = FOUND;
4457534Sroot 					slotoffset = u.u_offset;
4467534Sroot 					slotsize = ep->d_reclen;
4477534Sroot 				} else if (slotstatus == NONE) {
4487534Sroot 					slotfreespace += size;
4497534Sroot 					if (slotoffset == -1)
4507534Sroot 						slotoffset = u.u_offset;
4517534Sroot 					if (slotfreespace >= slotneeded) {
4527534Sroot 						slotstatus = COMPACT;
4537534Sroot 						slotsize =
4547534Sroot 						    u.u_offset+ep->d_reclen -
4557534Sroot 						      slotoffset;
4567534Sroot 					}
4576571Smckusic 				}
4586571Smckusic 			}
4596571Smckusic 		}
4607534Sroot 
4616571Smckusic 		/*
4627534Sroot 		 * Check for a name match.
4635972Swnj 		 */
4647534Sroot 		if (ep->d_ino) {
4657534Sroot 			if (ep->d_namlen == u.u_dent.d_namlen &&
4667534Sroot 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
4677534Sroot 				goto found;
4687534Sroot 		}
4697534Sroot 		prevoff = u.u_offset;
4706571Smckusic 		u.u_offset += ep->d_reclen;
4717534Sroot 		entryoffsetinblock += ep->d_reclen;
4727534Sroot 	}
473*15798Smckusick /* notfound: */
47415660Smckusick 	/*
475*15798Smckusick 	 * If we started in the middle of the directory and failed
47615660Smckusick 	 * to find our target, we must check the beginning as well.
47715660Smckusick 	 */
47815660Smckusick 	if (numdirpasses == 2) {
47915660Smckusick 		numdirpasses--;
48015660Smckusick 		u.u_offset = 0;
48115660Smckusick 		endsearch = u.u_ncache.nc_prevoffset;
48215660Smckusick 		goto searchloop;
48315660Smckusick 	}
4847534Sroot 	/*
4857534Sroot 	 * If creating, and at end of pathname and current
4869166Ssam 	 * directory has not been removed, then can consider
4879166Ssam 	 * allowing file to be created.
4887534Sroot 	 */
4899166Ssam 	if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) {
4905972Swnj 		/*
4917534Sroot 		 * Access for write is interpreted as allowing
4927534Sroot 		 * creation of files in the directory.
4935972Swnj 		 */
4947534Sroot 		if (access(dp, IWRITE))
4957534Sroot 			goto bad;
4965972Swnj 		/*
4977534Sroot 		 * Return an indication of where the new directory
4987534Sroot 		 * entry should be put.  If we didn't find a slot,
4997534Sroot 		 * then set u.u_count to 0 indicating that the
5007534Sroot 		 * new slot belongs at the end of the directory.
5017534Sroot 		 * If we found a slot, then the new entry can be
5027534Sroot 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
5035972Swnj 		 */
50415660Smckusick 		if (slotstatus == NONE) {
50515660Smckusick 			u.u_offset = roundup(dp->i_size, DIRBLKSIZ);
5067534Sroot 			u.u_count = 0;
50715660Smckusick 		} else {
5087534Sroot 			u.u_offset = slotoffset;
5097534Sroot 			u.u_count = slotsize;
5105972Swnj 		}
5117534Sroot 		dp->i_flag |= IUPD|ICHG;
5127534Sroot 		if (bp)
5137534Sroot 			brelse(bp);
5147534Sroot 		brelse(nbp);
5155972Swnj 		/*
5167534Sroot 		 * We return with the directory locked, so that
5177534Sroot 		 * the parameters we set up above will still be
5187534Sroot 		 * valid if we actually decide to do a direnter().
5197534Sroot 		 * We return NULL to indicate that the entry doesn't
5207534Sroot 		 * currently exist, leaving a pointer to the (locked)
5217534Sroot 		 * directory inode in u.u_pdir.
5225972Swnj 		 */
5237534Sroot 		u.u_pdir = dp;
5247534Sroot 		return (NULL);
5257534Sroot 	}
5267534Sroot 	u.u_error = ENOENT;
5277534Sroot 	goto bad;
5287534Sroot found:
529*15798Smckusick 	if (numdirpasses == 2)
530*15798Smckusick 		nchstats.ncs_pass2++;
5317534Sroot 	/*
5327534Sroot 	 * Check that directory length properly reflects presence
5337534Sroot 	 * of this entry.
5347534Sroot 	 */
5357605Ssam 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
5367534Sroot 		dirbad(dp, "i_size too small");
5377605Ssam 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
5387534Sroot 		dp->i_flag |= IUPD|ICHG;
5397534Sroot 	}
5407534Sroot 
5417534Sroot 	/*
54215660Smckusick 	 * Found component in pathname.
543*15798Smckusick 	 * If the final component of path name, save information
54415660Smckusick 	 * in the cache as to where the entry was found.
5457534Sroot 	 */
54615660Smckusick 	if (*cp == '\0' && flag == LOOKUP) {
54715660Smckusick 		u.u_ncache.nc_prevoffset = u.u_offset;
54815660Smckusick 		u.u_ncache.nc_inumber = dp->i_number;
54915660Smckusick 		u.u_ncache.nc_dev = dp->i_dev;
55015660Smckusick 		u.u_ncache.nc_time = time.tv_sec;
55115660Smckusick 	}
55215660Smckusick 	/*
55315660Smckusick 	 * Save directory entry in u.u_dent,
55415660Smckusick 	 * and release directory buffer.
55515660Smckusick 	 */
5567825Sroot 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep));
5577534Sroot 	brelse(bp);
5587534Sroot 	bp = NULL;
5597534Sroot 
5607534Sroot 	/*
5617534Sroot 	 * If deleting, and at end of pathname, return
5627534Sroot 	 * parameters which can be used to remove file.
5639166Ssam 	 * If the lockparent flag isn't set, we return only
5649166Ssam 	 * the directory (in u.u_pdir), otherwise we go
5659166Ssam 	 * on and lock the inode, being careful with ".".
5667534Sroot 	 */
5679166Ssam 	if (flag == DELETE && *cp == 0) {
5687534Sroot 		/*
5697534Sroot 		 * Write access to directory required to delete files.
5707534Sroot 		 */
5717534Sroot 		if (access(dp, IWRITE))
5727534Sroot 			goto bad;
5739166Ssam 		u.u_pdir = dp;		/* for dirremove() */
5747534Sroot 		/*
5757534Sroot 		 * Return pointer to current entry in u.u_offset,
5767534Sroot 		 * and distance past previous entry (if there
5777534Sroot 		 * is a previous entry in this block) in u.u_count.
5787534Sroot 		 * Save directory inode pointer in u.u_pdir for dirremove().
5797534Sroot 		 */
5807534Sroot 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
5817534Sroot 			u.u_count = 0;
5827534Sroot 		else
5837534Sroot 			u.u_count = u.u_offset - prevoff;
5849166Ssam 		if (lockparent) {
5859166Ssam 			if (dp->i_number == u.u_dent.d_ino)
5869166Ssam 				dp->i_count++;
5879166Ssam 			else {
5889166Ssam 				dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
5899166Ssam 				if (dp == NULL) {
5909166Ssam 					iput(u.u_pdir);
5919166Ssam 					goto bad;
5929166Ssam 				}
593*15798Smckusick 				/*
594*15798Smckusick 				 * If directory is setuid, then user must own
595*15798Smckusick 				 * the directory, or the file in it, else he
596*15798Smckusick 				 * may not delete it (unless he's root). This
597*15798Smckusick 				 * implements append-only directories.
598*15798Smckusick 				 */
599*15798Smckusick 				if ((u.u_pdir->i_mode & ISUID) &&
600*15798Smckusick 				    u.u_uid != 0 &&
601*15798Smckusick 				    u.u_uid != u.u_pdir->i_uid &&
602*15798Smckusick 				    dp->i_uid != u.u_uid) {
603*15798Smckusick 					iput(u.u_pdir);
604*15798Smckusick 					u.u_error = EPERM;
605*15798Smckusick 					goto bad;
606*15798Smckusick 				}
6079166Ssam 			}
6089166Ssam 		}
6097534Sroot 		brelse(nbp);
6107534Sroot 		return (dp);
6117534Sroot 	}
6127534Sroot 
6137534Sroot 	/*
6147534Sroot 	 * Special handling for ".." allowing chdir out of mounted
6157534Sroot 	 * file system: indirect .. in root inode to reevaluate
6167534Sroot 	 * in directory file system was mounted on.
6177534Sroot 	 */
618*15798Smckusick 	isdotdot = 0;
619*15798Smckusick 	if (bcmp(u.u_dent.d_name, "..", 3) == 0) {
620*15798Smckusick 		isdotdot++;
6217534Sroot 		if (dp == u.u_rdir)
6227534Sroot 			u.u_dent.d_ino = dp->i_number;
6237534Sroot 		else if (u.u_dent.d_ino == ROOTINO &&
6247534Sroot 		   dp->i_number == ROOTINO) {
6257534Sroot 			for (i = 1; i < NMOUNT; i++)
6267534Sroot 			if (mount[i].m_bufp != NULL &&
6277534Sroot 			   mount[i].m_dev == dp->i_dev) {
6286571Smckusic 				iput(dp);
6297534Sroot 				dp = mount[i].m_inodp;
6305972Swnj 				ilock(dp);
6315972Swnj 				dp->i_count++;
6327534Sroot 				fs = dp->i_fs;
6337534Sroot 				cp -= 2;     /* back over .. */
6347534Sroot 				goto dirloop2;
6355972Swnj 			}
63630Sbill 		}
6377534Sroot 	}
6387534Sroot 
6397534Sroot 	/*
6409166Ssam 	 * If rewriting (rename), return the inode and the
6419166Ssam 	 * information required to rewrite the present directory
6429166Ssam 	 * Must get inode of directory entry to verify it's a
6439166Ssam 	 * regular file, or empty directory.
6449166Ssam 	 */
6459166Ssam 	if ((flag == CREATE && lockparent) && *cp == 0) {
6469166Ssam 		if (access(dp, IWRITE))
6479166Ssam 			goto bad;
6489166Ssam 		u.u_pdir = dp;		/* for dirrewrite() */
6499166Ssam 		/*
6509166Ssam 		 * Careful about locking second inode.
6519166Ssam 		 * This can only occur if the target is ".".
6529166Ssam 		 */
6539166Ssam 		if (dp->i_number == u.u_dent.d_ino) {
6549166Ssam 			u.u_error = EISDIR;		/* XXX */
6559166Ssam 			goto bad;
6569166Ssam 		}
6579166Ssam 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
6589166Ssam 		if (dp == NULL) {
6599166Ssam 			iput(u.u_pdir);
6609166Ssam 			goto bad;
6619166Ssam 		}
6629166Ssam 		brelse(nbp);
6639166Ssam 		return (dp);
6649166Ssam 	}
6659166Ssam 
6669166Ssam 	/*
66712011Smckusick 	 * Check for symbolic link, which may require us to massage the
66812011Smckusick 	 * name before we continue translation.  We do not `iput' the
66912011Smckusick 	 * directory because we may need it again if the symbolic link
67012011Smckusick 	 * is relative to the current directory.  Instead we save it
67112011Smckusick 	 * unlocked as "pdp".  We must get the target inode before unlocking
67212011Smckusick 	 * the directory to insure that the inode will not be removed
67312011Smckusick 	 * before we get it.  We prevent deadlock by always fetching
67412011Smckusick 	 * inodes from the root, moving down the directory tree. Thus
67512011Smckusick 	 * when following backward pointers ".." we must unlock the
67612011Smckusick 	 * parent directory before getting the requested directory.
67712011Smckusick 	 * There is a potential race condition here if both the current
67812011Smckusick 	 * and parent directories are removed before the `iget' for the
67912011Smckusick 	 * inode associated with ".." returns.  We hope that this occurs
68012011Smckusick 	 * infrequently since we cannot avoid this race condition without
68112492Ssam 	 * implementing a sophisticated deadlock detection algorithm.
68212011Smckusick 	 * Note also that this simple deadlock detection scheme will not
68312011Smckusick 	 * work if the file system has any hard links other than ".."
68412011Smckusick 	 * that point backwards in the directory structure.
6857534Sroot 	 */
6867534Sroot 	pdp = dp;
687*15798Smckusick 	if (isdotdot) {
68812011Smckusick 		iunlock(pdp);	/* race to get the inode */
68912011Smckusick 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
69012011Smckusick 		if (dp == NULL)
69112011Smckusick 			goto bad2;
69212011Smckusick 	} else if (dp->i_number == u.u_dent.d_ino) {
69312011Smckusick 		dp->i_count++;	/* we want ourself, ie "." */
69412011Smckusick 	} else {
69512011Smckusick 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
69612011Smckusick 		iunlock(pdp);
69712011Smckusick 		if (dp == NULL)
69812011Smckusick 			goto bad2;
69912011Smckusick 	}
700*15798Smckusick 
701*15798Smckusick 	/*
702*15798Smckusick 	 * insert name into cache (if we want it, and it isn't "." or "..")
703*15798Smckusick 	 *
704*15798Smckusick 	 * all other cases where making a cache entry would be wrong
705*15798Smckusick 	 * have already departed from the code sequence somewhere above.
706*15798Smckusick 	 */
707*15798Smckusick 	if (bcmp(u.u_dent.d_name, ".", 2) != 0 && !isdotdot && docache) {
708*15798Smckusick 		if (ncp != NULL)
709*15798Smckusick 			panic("nami: duplicating cache");
710*15798Smckusick 
711*15798Smckusick 			/*
712*15798Smckusick 			 * free the cache slot at head of lru chain
713*15798Smckusick 			 */
714*15798Smckusick 		if (ncp = nchhead) {
715*15798Smckusick 				/* remove from lru chain */
716*15798Smckusick 			*ncp->nc_prev = ncp->nc_nxt;
717*15798Smckusick 			if (ncp->nc_nxt)
718*15798Smckusick 				ncp->nc_nxt->nc_prev = ncp->nc_prev;
719*15798Smckusick 			else
720*15798Smckusick 				nchtail = ncp->nc_prev;
721*15798Smckusick 
722*15798Smckusick 				/* remove from old hash chain */
723*15798Smckusick 			remque(ncp);
724*15798Smckusick 
725*15798Smckusick 				/* drop hold on inode (if we had one) */
726*15798Smckusick 			if (ncp->nc_ip)
727*15798Smckusick 				irele(ncp->nc_ip);
728*15798Smckusick 
729*15798Smckusick 				/* grab the inode we just found */
730*15798Smckusick 			ncp->nc_ip = dp;
731*15798Smckusick 			dp->i_count++;
732*15798Smckusick 
733*15798Smckusick 				/* fill in cache info */
734*15798Smckusick 			ncp->nc_ino = pdp->i_number;	/* parents inum */
735*15798Smckusick 			ncp->nc_dev = pdp->i_dev;	/* & device */
736*15798Smckusick 			ncp->nc_idev = dp->i_dev;	/* our device */
737*15798Smckusick 			ncp->nc_nlen = u.u_dent.d_namlen;
738*15798Smckusick 			bcopy(u.u_dent.d_name, ncp->nc_name, ncp->nc_nlen);
739*15798Smckusick 
740*15798Smckusick 				/* link at end of lru chain */
741*15798Smckusick 			ncp->nc_nxt = NULL;
742*15798Smckusick 			ncp->nc_prev = nchtail;
743*15798Smckusick 			*nchtail = ncp;
744*15798Smckusick 			nchtail = &ncp->nc_nxt;
745*15798Smckusick 
746*15798Smckusick 				/* and insert on hash chain */
747*15798Smckusick 			insque(ncp, nhp);
748*15798Smckusick 		}
749*15798Smckusick 	}
750*15798Smckusick 
751*15798Smckusick haveino:
7527534Sroot 	fs = dp->i_fs;
7537534Sroot 
7547534Sroot 	/*
7557534Sroot 	 * Check for symbolic link
7567534Sroot 	 */
7577534Sroot 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
7587825Sroot 		u_int pathlen = strlen(cp) + 1;
7597534Sroot 
7607534Sroot 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
7617534Sroot 		    ++nlink > MAXSYMLINKS) {
7627534Sroot 			u.u_error = ELOOP;
7637534Sroot 			goto bad2;
7647534Sroot 		}
7658957Sroot 		ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
7667751Sroot 		u.u_error =
7679166Ssam 		    rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size,
7687825Sroot 			0, 1, (int *)0);
7697534Sroot 		if (u.u_error)
7707534Sroot 			goto bad2;
7717534Sroot 		cp = nbp->b_un.b_addr;
7727534Sroot 		iput(dp);
7735972Swnj 		if (*cp == '/') {
7747534Sroot 			irele(pdp);
7755972Swnj 			while (*cp == '/')
7765972Swnj 				cp++;
7777534Sroot 			if ((dp = u.u_rdir) == NULL)
7787534Sroot 				dp = rootdir;
7797534Sroot 			ilock(dp);
7807534Sroot 			dp->i_count++;
7817534Sroot 		} else {
7827534Sroot 			dp = pdp;
7837534Sroot 			ilock(dp);
7845972Swnj 		}
7857534Sroot 		fs = dp->i_fs;
7867534Sroot 		goto dirloop;
78730Sbill 	}
7887534Sroot 
78930Sbill 	/*
7907534Sroot 	 * Not a symbolic link.  If more pathname,
7917534Sroot 	 * continue at next component, else return.
79230Sbill 	 */
7937534Sroot 	if (*cp == '/') {
7947534Sroot 		while (*cp == '/')
7957534Sroot 			cp++;
7969166Ssam 		irele(pdp);
7977534Sroot 		goto dirloop;
79830Sbill 	}
7995972Swnj 	brelse(nbp);
8009166Ssam 	if (lockparent)
8019166Ssam 		u.u_pdir = pdp;
8029166Ssam 	else
8039166Ssam 		irele(pdp);
8047534Sroot 	return (dp);
8057534Sroot bad2:
8067534Sroot 	irele(pdp);
8077534Sroot bad:
8087534Sroot 	if (bp)
8097534Sroot 		brelse(bp);
8107534Sroot 	if (dp)
8117534Sroot 		iput(dp);
8127534Sroot 	brelse(nbp);
8136571Smckusic 	return (NULL);
81430Sbill }
81530Sbill 
816*15798Smckusick 
8177534Sroot dirbad(ip, how)
8187534Sroot 	struct inode *ip;
8197534Sroot 	char *how;
8207534Sroot {
8217534Sroot 
8227534Sroot 	printf("%s: bad dir ino %d at offset %d: %s\n",
8237534Sroot 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
8247534Sroot }
8257534Sroot 
8267534Sroot dirbadname(ep)
8277534Sroot 	register struct direct *ep;
8287534Sroot {
8297534Sroot 	register int i;
8307534Sroot 
8317534Sroot 	for (i = 0; i < ep->d_namlen; i++)
8327534Sroot 		if (ep->d_name[i] == 0)
8337534Sroot 			return (1);
8347534Sroot 	return (ep->d_name[i]);
8357534Sroot }
8367534Sroot 
83730Sbill /*
8387534Sroot  * Write a directory entry after a call to namei, using the parameters
8397534Sroot  * which it left in the u. area.  The argument ip is the inode which
8407534Sroot  * the new directory entry will refer to.  The u. area field u.u_pdir is
8417534Sroot  * a pointer to the directory to be written, which was left locked by
8427534Sroot  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
8437534Sroot  * how the space for the new entry is to be gotten.
8447534Sroot  */
8457534Sroot direnter(ip)
8467534Sroot 	struct inode *ip;
8475972Swnj {
8487534Sroot 	register struct direct *ep, *nep;
8497534Sroot 	struct buf *bp;
85011639Ssam 	int loc, spacefree, error = 0;
8518631Sroot 	u_int dsize;
8528631Sroot 	int newentrysize;
8537534Sroot 	char *dirbuf;
8545972Swnj 
8557534Sroot 	u.u_dent.d_ino = ip->i_number;
8567534Sroot 	u.u_segflg = 1;
8577534Sroot 	newentrysize = DIRSIZ(&u.u_dent);
8587534Sroot 	if (u.u_count == 0) {
8597534Sroot 		/*
8607534Sroot 		 * If u.u_count is 0, then namei could find no space in the
8617534Sroot 		 * directory.  In this case u.u_offset will be on a directory
8627534Sroot 		 * block boundary and we will write the new entry into a fresh
8637534Sroot 		 * block.
8647534Sroot 		 */
8657534Sroot 		if (u.u_offset&(DIRBLKSIZ-1))
8667534Sroot 			panic("wdir: newblk");
8677534Sroot 		u.u_dent.d_reclen = DIRBLKSIZ;
86810849Ssam 		error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
8698631Sroot 		    newentrysize, u.u_offset, 1, (int *)0);
8707534Sroot 		iput(u.u_pdir);
87110849Ssam 		return (error);
8727534Sroot 	}
8737534Sroot 
8747534Sroot 	/*
8757534Sroot 	 * If u.u_count is non-zero, then namei found space for the
8767534Sroot 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
8777534Sroot 	 * in the directory.  To use this space, we may have to compact
8787534Sroot 	 * the entries located there, by copying them together towards
8797534Sroot 	 * the beginning of the block, leaving the free space in
8807534Sroot 	 * one usable chunk at the end.
8817534Sroot 	 */
8827534Sroot 
8837534Sroot 	/*
8847534Sroot 	 * Increase size of directory if entry eats into new space.
8857534Sroot 	 * This should never push the size past a new multiple of
8867534Sroot 	 * DIRBLKSIZE.
8877534Sroot 	 */
8889166Ssam 	if (u.u_offset + u.u_count > u.u_pdir->i_size)
8897534Sroot 		u.u_pdir->i_size = u.u_offset + u.u_count;
8907534Sroot 
8917534Sroot 	/*
8927534Sroot 	 * Get the block containing the space for the new directory
89310849Ssam 	 * entry.  Should return error by result instead of u.u_error.
8947534Sroot 	 */
8957605Ssam 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
8969166Ssam 	if (bp == 0) {
8979166Ssam 		iput(u.u_pdir);
89810849Ssam 		return (u.u_error);
8999166Ssam 	}
9007534Sroot 
9017534Sroot 	/*
9027534Sroot 	 * Find space for the new entry.  In the simple case, the
9037534Sroot 	 * entry at offset base will have the space.  If it does
9047534Sroot 	 * not, then namei arranged that compacting the region
9057534Sroot 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
9067534Sroot 	 */
9077534Sroot 	ep = (struct direct *)dirbuf;
9087534Sroot 	dsize = DIRSIZ(ep);
90911639Ssam 	spacefree = ep->d_reclen - dsize;
9107534Sroot 	for (loc = ep->d_reclen; loc < u.u_count; ) {
9117534Sroot 		nep = (struct direct *)(dirbuf + loc);
9127534Sroot 		if (ep->d_ino) {
9137534Sroot 			/* trim the existing slot */
9147534Sroot 			ep->d_reclen = dsize;
9157534Sroot 			ep = (struct direct *)((char *)ep + dsize);
9167534Sroot 		} else {
9177534Sroot 			/* overwrite; nothing there; header is ours */
91811639Ssam 			spacefree += dsize;
9197534Sroot 		}
9207534Sroot 		dsize = DIRSIZ(nep);
92111639Ssam 		spacefree += nep->d_reclen - dsize;
9227534Sroot 		loc += nep->d_reclen;
9237825Sroot 		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
9247534Sroot 	}
9257534Sroot 	/*
9267534Sroot 	 * Update the pointer fields in the previous entry (if any),
9277534Sroot 	 * copy in the new entry, and write out the block.
9287534Sroot 	 */
9297534Sroot 	if (ep->d_ino == 0) {
93011639Ssam 		if (spacefree + dsize < newentrysize)
9317534Sroot 			panic("wdir: compact1");
93211639Ssam 		u.u_dent.d_reclen = spacefree + dsize;
9337534Sroot 	} else {
93411639Ssam 		if (spacefree < newentrysize)
9357534Sroot 			panic("wdir: compact2");
93611639Ssam 		u.u_dent.d_reclen = spacefree;
9377534Sroot 		ep->d_reclen = dsize;
9387534Sroot 		ep = (struct direct *)((char *)ep + dsize);
9397534Sroot 	}
9408672Sroot 	bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
9417534Sroot 	bwrite(bp);
9427534Sroot 	u.u_pdir->i_flag |= IUPD|ICHG;
9437534Sroot 	iput(u.u_pdir);
94410849Ssam 	return (error);
9455972Swnj }
9466571Smckusic 
9479166Ssam /*
9489166Ssam  * Remove a directory entry after a call to namei, using the
9499166Ssam  * parameters which it left in the u. area.  The u. entry
9509166Ssam  * u_offset contains the offset into the directory of the
9519166Ssam  * entry to be eliminated.  The u_count field contains the
9529166Ssam  * size of the previous record in the directory.  If this
9539166Ssam  * is 0, the first entry is being deleted, so we need only
9549166Ssam  * zero the inode number to mark the entry as free.  If the
9559166Ssam  * entry isn't the first in the directory, we must reclaim
9569166Ssam  * the space of the now empty record by adding the record size
9579166Ssam  * to the size of the previous entry.
9589166Ssam  */
9597534Sroot dirremove()
9606571Smckusic {
9617534Sroot 	register struct inode *dp = u.u_pdir;
9627534Sroot 	register struct buf *bp;
9637534Sroot 	struct direct *ep;
9646571Smckusic 
9659269Ssam 	if (u.u_count == 0) {
9667534Sroot 		/*
9677534Sroot 		 * First entry in block: set d_ino to zero.
9687534Sroot 		 */
9699269Ssam 		u.u_dent.d_ino = 0;
9708619Sroot 		(void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
9718631Sroot 		    (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
9729269Ssam 	} else {
9737534Sroot 		/*
9747534Sroot 		 * Collapse new free space into previous entry.
9757534Sroot 		 */
9767825Sroot 		bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
9777534Sroot 		if (bp == 0)
9787534Sroot 			return (0);
9797534Sroot 		ep->d_reclen += u.u_dent.d_reclen;
9807534Sroot 		bwrite(bp);
9817534Sroot 		dp->i_flag |= IUPD|ICHG;
9827534Sroot 	}
9837534Sroot 	return (1);
9846571Smckusic }
9857534Sroot 
9867605Ssam /*
9879166Ssam  * Rewrite an existing directory entry to point at the inode
9889166Ssam  * supplied.  The parameters describing the directory entry are
9899166Ssam  * set up by a call to namei.
9909166Ssam  */
9919166Ssam dirrewrite(dp, ip)
9929166Ssam 	struct inode *dp, *ip;
9939166Ssam {
9949166Ssam 
9959166Ssam 	u.u_dent.d_ino = ip->i_number;
9969166Ssam 	u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
9979166Ssam 		(int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
9989166Ssam 	iput(dp);
9999166Ssam }
10009166Ssam 
10019166Ssam /*
10027605Ssam  * Return buffer with contents of block "offset"
10037605Ssam  * from the beginning of directory "ip".  If "res"
10047605Ssam  * is non-zero, fill it in with a pointer to the
10057605Ssam  * remaining space in the directory.
10067605Ssam  */
10077534Sroot struct buf *
10087605Ssam blkatoff(ip, offset, res)
10097534Sroot 	struct inode *ip;
10107534Sroot 	off_t offset;
10117534Sroot 	char **res;
10127534Sroot {
10137534Sroot 	register struct fs *fs = ip->i_fs;
10148672Sroot 	daddr_t lbn = lblkno(fs, offset);
10157534Sroot 	int base = blkoff(fs, offset);
10167534Sroot 	int bsize = blksize(fs, ip, lbn);
10178672Sroot 	daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
10187534Sroot 	register struct buf *bp;
10197534Sroot 
10207534Sroot 	if (u.u_error)
10217534Sroot 		return (0);
10227534Sroot 	bp = bread(ip->i_dev, bn, bsize);
10237534Sroot 	if (bp->b_flags & B_ERROR) {
10247534Sroot 		brelse(bp);
10257534Sroot 		return (0);
10267534Sroot 	}
10277534Sroot 	if (res)
10287534Sroot 		*res = bp->b_un.b_addr + base;
10297534Sroot 	return (bp);
10307534Sroot }
10319166Ssam 
10329166Ssam /*
10339166Ssam  * Check if a directory is empty or not.
10349166Ssam  * Inode supplied must be locked.
103512817Ssam  *
103612817Ssam  * Using a struct dirtemplate here is not precisely
103712817Ssam  * what we want, but better than using a struct direct.
103812817Ssam  *
103912817Ssam  * NB: does not handle corrupted directories.
10409166Ssam  */
10419166Ssam dirempty(ip)
10429863Ssam 	register struct inode *ip;
10439166Ssam {
10449166Ssam 	register off_t off;
104512817Ssam 	struct dirtemplate dbuf;
104612817Ssam 	register struct direct *dp = (struct direct *)&dbuf;
10479863Ssam 	int error, count;
104812817Ssam #define	MINDIRSIZ (sizeof (struct dirtemplate) / 2)
10499166Ssam 
10509166Ssam 	for (off = 0; off < ip->i_size; off += dp->d_reclen) {
105112817Ssam 		error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ,
105212817Ssam 		    off, 1, &count);
105312817Ssam 		/*
105412817Ssam 		 * Since we read MINDIRSIZ, residual must
105512817Ssam 		 * be 0 unless we're at end of file.
105612817Ssam 		 */
105712817Ssam 		if (error || count != 0)
10589166Ssam 			return (0);
105912817Ssam 		/* skip empty entries */
10609166Ssam 		if (dp->d_ino == 0)
10619166Ssam 			continue;
106212817Ssam 		/* accept only "." and ".." */
106312817Ssam 		if (dp->d_namlen > 2)
106412817Ssam 			return (0);
10659166Ssam 		if (dp->d_name[0] != '.')
10669166Ssam 			return (0);
106712817Ssam 		/*
106812817Ssam 		 * At this point d_namlen must be 1 or 2.
106912817Ssam 		 * 1 implies ".", 2 implies ".." if second
107012817Ssam 		 * char is also "."
107112817Ssam 		 */
107212817Ssam 		if (dp->d_namlen == 1 || dp->d_name[1] == '.')
10739166Ssam 			continue;
10749166Ssam 		return (0);
10759166Ssam 	}
10769166Ssam 	return (1);
10779166Ssam }
107812815Smckusick 
107912815Smckusick /*
108012815Smckusick  * Check if source directory is in the path of the target directory.
108112815Smckusick  * Target is supplied locked, source is unlocked.
108212815Smckusick  * The target is always iput() before returning.
108312815Smckusick  */
108412815Smckusick checkpath(source, target)
108512815Smckusick 	struct inode *source, *target;
108612815Smckusick {
108712815Smckusick 	struct dirtemplate dirbuf;
108812815Smckusick 	register struct inode *ip;
108912815Smckusick 	int error = 0;
109012815Smckusick 
109112815Smckusick 	ip = target;
109212815Smckusick 	if (ip->i_number == source->i_number) {
109312815Smckusick 		error = EEXIST;
109412815Smckusick 		goto out;
109512815Smckusick 	}
109612815Smckusick 	if (ip->i_number == ROOTINO)
109712815Smckusick 		goto out;
109812815Smckusick 
109912815Smckusick 	for (;;) {
110012815Smckusick 		if ((ip->i_mode&IFMT) != IFDIR) {
110112815Smckusick 			error = ENOTDIR;
110212815Smckusick 			break;
110312815Smckusick 		}
110412815Smckusick 		error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
110512815Smckusick 			sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
110612815Smckusick 		if (error != 0)
110712815Smckusick 			break;
110812815Smckusick 		if (dirbuf.dotdot_namlen != 2 ||
110912815Smckusick 		    bcmp(dirbuf.dotdot_name, "..", 3) != 0) {
111012815Smckusick 			error = ENOTDIR;
111112815Smckusick 			break;
111212815Smckusick 		}
111312815Smckusick 		if (dirbuf.dotdot_ino == source->i_number) {
111412815Smckusick 			error = EINVAL;
111512815Smckusick 			break;
111612815Smckusick 		}
111712815Smckusick 		if (dirbuf.dotdot_ino == ROOTINO)
111812815Smckusick 			break;
111912815Smckusick 		iput(ip);
112012815Smckusick 		ip = iget(ip->i_dev, ip->i_fs, dirbuf.dotdot_ino);
112112815Smckusick 		if (ip == NULL) {
112212815Smckusick 			error = u.u_error;
112312815Smckusick 			break;
112412815Smckusick 		}
112512815Smckusick 	}
112612815Smckusick 
112712815Smckusick out:
112812815Smckusick 	if (error == ENOTDIR)
112912815Smckusick 		printf("checkpath: .. not a directory\n");
113012815Smckusick 	if (ip != NULL)
113112815Smckusick 		iput(ip);
113212815Smckusick 	return (error);
113312815Smckusick }
1134*15798Smckusick 
1135*15798Smckusick /*
1136*15798Smckusick  * Name cache initialization, from main() when we are booting
1137*15798Smckusick  */
1138*15798Smckusick nchinit()
1139*15798Smckusick {
1140*15798Smckusick 	register union nchash *nchp;
1141*15798Smckusick 	register struct nch *ncp;
1142*15798Smckusick 
1143*15798Smckusick 	nchhead = 0;
1144*15798Smckusick 	nchtail = &nchhead;
1145*15798Smckusick 
1146*15798Smckusick 	for (ncp = nch; ncp < &nch[nchsize]; ncp++) {
1147*15798Smckusick 		ncp->nc_forw = ncp;			/* hash chain */
1148*15798Smckusick 		ncp->nc_back = ncp;
1149*15798Smckusick 
1150*15798Smckusick 		ncp->nc_nxt = NULL;			/* lru chain */
1151*15798Smckusick 		*nchtail = ncp;
1152*15798Smckusick 		ncp->nc_prev = nchtail;
1153*15798Smckusick 		nchtail = &ncp->nc_nxt;
1154*15798Smckusick 
1155*15798Smckusick 		/* all else is zero already */
1156*15798Smckusick 	}
1157*15798Smckusick 
1158*15798Smckusick 	for (nchp = nchash; nchp < &nchash[NCHHASH]; nchp++) {
1159*15798Smckusick 		nchp->nch_head[0] = nchp;
1160*15798Smckusick 		nchp->nch_head[1] = nchp;
1161*15798Smckusick 	}
1162*15798Smckusick }
1163*15798Smckusick 
1164*15798Smckusick /*
1165*15798Smckusick  * Cache flush, called when filesys is umounted to
1166*15798Smckusick  * remove entries that would now be invalid
1167*15798Smckusick  *
1168*15798Smckusick  * The line "nxtcp = nchhead" near the end is to avoid potential problems
1169*15798Smckusick  * if the cache lru chain is modified while we are dumping the
1170*15798Smckusick  * inode.  This makes the algorithm O(n^2), but do you think I care?
1171*15798Smckusick  */
1172*15798Smckusick nchinval(dev)
1173*15798Smckusick 	register dev_t dev;
1174*15798Smckusick {
1175*15798Smckusick 	register struct nch *ncp, *nxtcp;
1176*15798Smckusick 
1177*15798Smckusick 	for (ncp = nchhead; ncp; ncp = nxtcp) {
1178*15798Smckusick 		nxtcp = ncp->nc_nxt;
1179*15798Smckusick 
1180*15798Smckusick 		if (ncp->nc_ip == NULL ||
1181*15798Smckusick 		    (ncp->nc_idev != dev && ncp->nc_dev != dev))
1182*15798Smckusick 			continue;
1183*15798Smckusick 
1184*15798Smckusick 		ncp->nc_idev = NODEV;
1185*15798Smckusick 		ncp->nc_dev = NODEV;
1186*15798Smckusick 		ncp->nc_ino = 0;
1187*15798Smckusick 
1188*15798Smckusick 			/* remove the entry from its hash chain */
1189*15798Smckusick 		remque(ncp);
1190*15798Smckusick 			/* and make a dummy one */
1191*15798Smckusick 		ncp->nc_forw = ncp;
1192*15798Smckusick 		ncp->nc_back = ncp;
1193*15798Smckusick 
1194*15798Smckusick 			/* delete this entry from LRU chain */
1195*15798Smckusick 		*ncp->nc_prev = nxtcp;
1196*15798Smckusick 		if (nxtcp)
1197*15798Smckusick 			nxtcp->nc_prev = ncp->nc_prev;
1198*15798Smckusick 		else
1199*15798Smckusick 			nchtail = ncp->nc_prev;
1200*15798Smckusick 
1201*15798Smckusick 			/* free the inode we had */
1202*15798Smckusick 		irele(ncp->nc_ip);
1203*15798Smckusick 		ncp->nc_ip = NULL;
1204*15798Smckusick 
1205*15798Smckusick 			/* cause rescan of list, it may have altered */
1206*15798Smckusick 		nxtcp = nchhead;
1207*15798Smckusick 			/* put the now-free entry at head of LRU */
1208*15798Smckusick 		ncp->nc_nxt = nxtcp;
1209*15798Smckusick 		ncp->nc_prev = &nchhead;
1210*15798Smckusick 		nxtcp->nc_prev = &ncp->nc_nxt;
1211*15798Smckusick 		nchhead = ncp;
1212*15798Smckusick 	}
1213*15798Smckusick }
1214