xref: /csrg-svn/sys/kern/vfs_lookup.c (revision 7534)
1*7534Sroot /*	vfs_lookup.c	4.19	82/07/25	*/
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*7534Sroot struct	buf *batoffset();
18*7534Sroot int	dirchk = 1;
1930Sbill /*
20*7534Sroot  * Convert a pathname into a pointer to a locked inode,
21*7534Sroot  * with side effects usable in creating and removing files.
22*7534Sroot  * This is a very central and rather complicated routine.
2330Sbill  *
24*7534Sroot  * The func argument gives the routine which returns successive
25*7534Sroot  * characters of the name to be translated.  The flag
26*7534Sroot  * argument is (0, 1, 2) depending on whether the name is to be
27*7534Sroot  * (looked up, created, deleted).  The follow argument is 1 when
28*7534Sroot  * symbolic links are to be followed when they occur at the end of
29*7534Sroot  * the name translation process.
30*7534Sroot  *
31*7534Sroot  * Overall outline:
32*7534Sroot  *
33*7534Sroot  *	copy in name
34*7534Sroot  *	get starting directory
35*7534Sroot  * dirloop:
36*7534Sroot  *	check accessibility of directory
37*7534Sroot  * dirloop2:
38*7534Sroot  *	copy next component of name to u.u_dent
39*7534Sroot  *	handle degenerate case where name is null string
40*7534Sroot  *	search for name in directory, to found or notfound
41*7534Sroot  * notfound:
42*7534Sroot  *	if creating, return locked inode, leaving information on avail. slots
43*7534Sroot  *	else return error
44*7534Sroot  * found:
45*7534Sroot  *	if at end of path and deleting, return information to allow delete
46*7534Sroot  *	if .. and on mounted filesys, look in mount table for parent
47*7534Sroot  *	if symbolic link, massage name in buffer and continue at dirloop
48*7534Sroot  *	if more components of name, do next level at dirloop
49*7534Sroot  *	return the answer as locked inode
5030Sbill  */
5130Sbill struct inode *
525972Swnj namei(func, flag, follow)
535972Swnj 	int (*func)(), flag, follow;
5430Sbill {
55*7534Sroot 	register char *cp;		/* pointer into pathname argument */
56*7534Sroot /* these variables refer to things which must be freed or unlocked */
57*7534Sroot 	register struct inode *dp = 0;	/* the directory we are searching */
58*7534Sroot 	register struct fs *fs;		/* file system that directory is in */
59*7534Sroot 	register struct buf *bp = 0;	/* a buffer of directory entries */
60*7534Sroot 	register struct direct *ep;	/* the current directory entry */
61*7534Sroot 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
62*7534Sroot 	register struct buf *nbp;	/* buffer storing path name argument */
63*7534Sroot /* these variables hold information about the search for a slot */
64*7534Sroot 	enum {NONE, COMPACT, FOUND} slotstatus;
65*7534Sroot 	int slotoffset = -1;		/* offset of area with free space */
66*7534Sroot 	int slotsize;			/* size of area at slotoffset */
67*7534Sroot 	int slotfreespace;		/* amount of space free in slot */
68*7534Sroot 	int slotneeded;			/* size of the entry we're seeking */
69*7534Sroot /* */
70*7534Sroot 	int dirsize;
71*7534Sroot 	int prevoff;			/* u.u_offset of previous entry */
72*7534Sroot 	int nlink = 0;			/* number of symbolic links taken */
73*7534Sroot 	struct inode *pdp;		/* saved dp during symlink work */
74*7534Sroot 	int i;
7530Sbill 
7630Sbill 	/*
77*7534Sroot 	 * Get a buffer for the name to be translated, and copy the
78*7534Sroot 	 * name into the buffer.
795972Swnj 	 */
806571Smckusic 	nbp = geteblk(MAXPATHLEN);
81*7534Sroot 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
82*7534Sroot 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
836066Sroot 			u.u_error = EPERM;
84*7534Sroot 			goto bad;
856066Sroot 		}
866066Sroot 		cp++;
876571Smckusic 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
885972Swnj 			u.u_error = ENOENT;
89*7534Sroot 			goto bad;
905972Swnj 		}
915972Swnj 	}
92*7534Sroot 	if (u.u_error)
93*7534Sroot 		goto bad;
94*7534Sroot 
955972Swnj 	/*
96*7534Sroot 	 * Get starting directory.
9730Sbill 	 */
98*7534Sroot 	cp = nbp->b_un.b_addr;
995972Swnj 	if (*cp == '/') {
1005972Swnj 		while (*cp == '/')
1015972Swnj 			cp++;
10230Sbill 		if ((dp = u.u_rdir) == NULL)
10330Sbill 			dp = rootdir;
104*7534Sroot 	} else
105*7534Sroot 		dp = u.u_cdir;
106*7534Sroot 	fs = dp->i_fs;
1075972Swnj 	ilock(dp);
1085972Swnj 	dp->i_count++;
109*7534Sroot 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
110*7534Sroot 
111*7534Sroot 	/*
112*7534Sroot 	 * We come to dirloop to search a new directory.
113*7534Sroot 	 * The directory must be locked so that it can be
114*7534Sroot 	 * iput, and fs must be already set to dp->i_fs.
115*7534Sroot 	 */
1166571Smckusic dirloop:
11730Sbill 	/*
118*7534Sroot 	 * 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
126*7534Sroot 	if ((dp->i_mode&IFMT) != IFDIR) {
12730Sbill 		u.u_error = ENOTDIR;
128*7534Sroot 		goto bad;
129*7534Sroot 	}
130*7534Sroot 	if (access(dp, IEXEC))
131*7534Sroot 		goto bad;
132*7534Sroot 
1336384Swnj dirloop2:
134*7534Sroot 	/*
135*7534Sroot 	 * Copy next component of name to u.u_dent.
136*7534Sroot 	 */
137*7534Sroot 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
1386571Smckusic 		if (i >= MAXNAMLEN) {
1395972Swnj 			u.u_error = ENOENT;
140*7534Sroot 			goto bad;
1415972Swnj 		}
142*7534Sroot 		u.u_dent.d_name[i++] = *cp;
1435972Swnj 	}
1446571Smckusic 	u.u_dent.d_namlen = i;
145*7534Sroot 	u.u_dent.d_name[i] = 0;
146*7534Sroot 
147*7534Sroot 	/*
148*7534Sroot 	 * Check for degenerate name (e.g. / or "")
149*7534Sroot 	 * which is a way of talking about a directory,
150*7534Sroot 	 * e.g. like "/." or ".".
151*7534Sroot 	 */
152*7534Sroot 	if (u.u_dent.d_name[0] == 0) {
153*7534Sroot 		if (flag) {
1545972Swnj 			u.u_error = ENOENT;
155*7534Sroot 			goto bad;
1565972Swnj 		}
1576571Smckusic 		brelse(nbp);
1586571Smckusic 		return (dp);
1595972Swnj 	}
160*7534Sroot 
1616571Smckusic 	/*
162*7534Sroot 	 * Suppress search for slots unless creating
163*7534Sroot 	 * file and at end of pathname, in which case
164*7534Sroot 	 * we watch for a place to put the new file in
165*7534Sroot 	 * case it doesn't already exist.
1666571Smckusic 	 */
167*7534Sroot 	slotstatus = FOUND;
168*7534Sroot 	if (flag == 1 && *cp == 0) {
169*7534Sroot 		slotstatus = NONE;
170*7534Sroot 		slotfreespace = 0;
171*7534Sroot 		slotneeded = DIRSIZ(&u.u_dent);
172*7534Sroot 	}
173*7534Sroot 
174*7534Sroot 	dirsize = roundup(dp->i_size, DIRBLKSIZ);
1756571Smckusic 	u.u_offset = 0;
176*7534Sroot 	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*7534Sroot 			bp = batoffset(dp, u.u_offset, (char **)0);
186*7534Sroot 			if (bp == 0)
187*7534Sroot 				goto bad;
188*7534Sroot 			entryoffsetinblock = 0;
1895972Swnj 		}
190*7534Sroot 
1915972Swnj 		/*
192*7534Sroot 		 * If still looking for a slot, and at a DIRBLKSIZE
193*7534Sroot 		 * boundary, have to start looking for free space
194*7534Sroot 		 * again.
1956571Smckusic 		 */
196*7534Sroot 		if (slotstatus == NONE &&
197*7534Sroot 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
198*7534Sroot 			slotoffset = -1;
199*7534Sroot 			slotfreespace = 0;
200*7534Sroot 		}
201*7534Sroot 
202*7534Sroot 		/*
203*7534Sroot 		 * Get pointer to next entry, and do consistency checking:
204*7534Sroot 		 *	record length must be multiple of 4
205*7534Sroot 		 *	entry must fit in rest of this DIRBLKSIZ block
206*7534Sroot 		 *	record must be large enough to contain name
207*7534Sroot 		 *	name must be as long as advertised, and null terminated
208*7534Sroot 		 * Checking last condition is expensive, it is done only
209*7534Sroot 		 * when dirchk is set.
210*7534Sroot 		 */
211*7534Sroot 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
212*7534Sroot 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
213*7534Sroot 		if ((ep->d_reclen & 0x3) || ep->d_reclen > i ||
214*7534Sroot 		    DIRSIZ(ep) > ep->d_reclen || dirchk && dirbadname(ep)) {
215*7534Sroot 			dirbad(dp, "mangled entry");
2166571Smckusic 			u.u_offset += i;
217*7534Sroot 			entryoffsetinblock += i;
2186571Smckusic 			continue;
2196571Smckusic 		}
220*7534Sroot 
2216571Smckusic 		/*
222*7534Sroot 		 * If an appropriate sized slot has not yet been found,
2236571Smckusic 		 * check to see if one is available. Also accumulate space
2246571Smckusic 		 * in the current block so that we can determine if
2256571Smckusic 		 * compaction is viable.
2266571Smckusic 		 */
227*7534Sroot 		if (slotstatus != FOUND) {
228*7534Sroot 			int size = ep->d_reclen;
229*7534Sroot 
2306571Smckusic 			if (ep->d_ino != 0)
2316571Smckusic 				size -= DIRSIZ(ep);
2326571Smckusic 			if (size > 0) {
233*7534Sroot 				if (size >= slotneeded) {
234*7534Sroot 					slotstatus = FOUND;
235*7534Sroot 					slotoffset = u.u_offset;
236*7534Sroot 					slotsize = ep->d_reclen;
237*7534Sroot 				} else if (slotstatus == NONE) {
238*7534Sroot 					slotfreespace += size;
239*7534Sroot 					if (slotoffset == -1)
240*7534Sroot 						slotoffset = u.u_offset;
241*7534Sroot 					if (slotfreespace >= slotneeded) {
242*7534Sroot 						slotstatus = COMPACT;
243*7534Sroot 						slotsize =
244*7534Sroot 						    u.u_offset+ep->d_reclen -
245*7534Sroot 						      slotoffset;
246*7534Sroot 					}
2476571Smckusic 				}
2486571Smckusic 			}
2496571Smckusic 		}
250*7534Sroot 
2516571Smckusic 		/*
252*7534Sroot 		 * Check for a name match.
2535972Swnj 		 */
254*7534Sroot 		if (ep->d_ino) {
255*7534Sroot 			if (ep->d_namlen == u.u_dent.d_namlen &&
256*7534Sroot 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
257*7534Sroot 				goto found;
258*7534Sroot 		}
259*7534Sroot 		prevoff = u.u_offset;
2606571Smckusic 		u.u_offset += ep->d_reclen;
261*7534Sroot 		entryoffsetinblock += ep->d_reclen;
262*7534Sroot 	}
263*7534Sroot /* notfound: */
264*7534Sroot 	/*
265*7534Sroot 	 * If creating, and at end of pathname and current
266*7534Sroot 	 * directory has not been removed, then can consider allowing
267*7534Sroot 	 * file to be created.
268*7534Sroot 	 */
269*7534Sroot 	if (flag == 1 && *cp == 0 && dp->i_nlink != 0) {
2705972Swnj 		/*
271*7534Sroot 		 * Access for write is interpreted as allowing
272*7534Sroot 		 * creation of files in the directory.
2735972Swnj 		 */
274*7534Sroot 		if (access(dp, IWRITE))
275*7534Sroot 			goto bad;
2765972Swnj 		/*
277*7534Sroot 		 * Return an indication of where the new directory
278*7534Sroot 		 * entry should be put.  If we didn't find a slot,
279*7534Sroot 		 * then set u.u_count to 0 indicating that the
280*7534Sroot 		 * new slot belongs at the end of the directory.
281*7534Sroot 		 * If we found a slot, then the new entry can be
282*7534Sroot 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
2835972Swnj 		 */
284*7534Sroot 		if (slotstatus == NONE)
285*7534Sroot 			u.u_count = 0;
286*7534Sroot 		else {
287*7534Sroot 			u.u_offset = slotoffset;
288*7534Sroot 			u.u_count = slotsize;
2895972Swnj 		}
290*7534Sroot 		dp->i_flag |= IUPD|ICHG;
291*7534Sroot 		if (bp)
292*7534Sroot 			brelse(bp);
293*7534Sroot 		brelse(nbp);
2945972Swnj 		/*
295*7534Sroot 		 * We return with the directory locked, so that
296*7534Sroot 		 * the parameters we set up above will still be
297*7534Sroot 		 * valid if we actually decide to do a direnter().
298*7534Sroot 		 * We return NULL to indicate that the entry doesn't
299*7534Sroot 		 * currently exist, leaving a pointer to the (locked)
300*7534Sroot 		 * directory inode in u.u_pdir.
3015972Swnj 		 */
302*7534Sroot 		u.u_pdir = dp;
303*7534Sroot 		return (NULL);
304*7534Sroot 	}
305*7534Sroot 	u.u_error = ENOENT;
306*7534Sroot 	goto bad;
307*7534Sroot found:
308*7534Sroot 	/*
309*7534Sroot 	 * Check that directory length properly reflects presence
310*7534Sroot 	 * of this entry.
311*7534Sroot 	 */
312*7534Sroot 	if (entryoffsetinblock + ep->d_reclen > dp->i_size) {
313*7534Sroot 		dirbad(dp, "i_size too small");
314*7534Sroot 		dp->i_size = entryoffsetinblock + ep->d_reclen;
315*7534Sroot 		dp->i_flag |= IUPD|ICHG;
316*7534Sroot 	}
317*7534Sroot 
318*7534Sroot 	/*
319*7534Sroot 	 * Found component in pathname; save directory
320*7534Sroot 	 * entry in u.u_dent, and release directory buffer.
321*7534Sroot 	 */
322*7534Sroot 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, DIRSIZ(ep));
323*7534Sroot 	brelse(bp);
324*7534Sroot 	bp = NULL;
325*7534Sroot 
326*7534Sroot 	/*
327*7534Sroot 	 * If deleting, and at end of pathname, return
328*7534Sroot 	 * parameters which can be used to remove file.
329*7534Sroot 	 * Note that in this case we return the directory
330*7534Sroot 	 * inode, not the inode of the file being deleted.
331*7534Sroot 	 */
332*7534Sroot 	if (flag == 2 && *cp == 0) {
333*7534Sroot 		/*
334*7534Sroot 		 * Write access to directory required to delete files.
335*7534Sroot 		 */
336*7534Sroot 		if (access(dp, IWRITE))
337*7534Sroot 			goto bad;
338*7534Sroot 		/*
339*7534Sroot 		 * Return pointer to current entry in u.u_offset,
340*7534Sroot 		 * and distance past previous entry (if there
341*7534Sroot 		 * is a previous entry in this block) in u.u_count.
342*7534Sroot 		 * Save directory inode pointer in u.u_pdir for dirremove().
343*7534Sroot 		 */
344*7534Sroot 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
345*7534Sroot 			u.u_count = 0;
346*7534Sroot 		else
347*7534Sroot 			u.u_count = u.u_offset - prevoff;
348*7534Sroot 		brelse(nbp);
349*7534Sroot 		u.u_pdir = dp;		/* for dirremove() */
350*7534Sroot 		return (dp);
351*7534Sroot 	}
352*7534Sroot 
353*7534Sroot 	/*
354*7534Sroot 	 * Special handling for ".." allowing chdir out of mounted
355*7534Sroot 	 * file system: indirect .. in root inode to reevaluate
356*7534Sroot 	 * in directory file system was mounted on.
357*7534Sroot 	 */
358*7534Sroot 	if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
359*7534Sroot 	    u.u_dent.d_name[2] == '\0') {
360*7534Sroot 		if (dp == u.u_rdir)
361*7534Sroot 			u.u_dent.d_ino = dp->i_number;
362*7534Sroot 		else if (u.u_dent.d_ino == ROOTINO &&
363*7534Sroot 		   dp->i_number == ROOTINO) {
364*7534Sroot 			for (i = 1; i < NMOUNT; i++)
365*7534Sroot 			if (mount[i].m_bufp != NULL &&
366*7534Sroot 			   mount[i].m_dev == dp->i_dev) {
3676571Smckusic 				iput(dp);
368*7534Sroot 				dp = mount[i].m_inodp;
3695972Swnj 				ilock(dp);
3705972Swnj 				dp->i_count++;
371*7534Sroot 				fs = dp->i_fs;
372*7534Sroot 				cp -= 2;     /* back over .. */
373*7534Sroot 				goto dirloop2;
3745972Swnj 			}
37530Sbill 		}
376*7534Sroot 	}
377*7534Sroot 
378*7534Sroot 	/*
379*7534Sroot 	 * Check for symbolic link, which may require us
380*7534Sroot 	 * to massage the name before we continue translation.
381*7534Sroot 	 * To avoid deadlock have to unlock the current directory,
382*7534Sroot 	 * but don't iput it because we may need it again (if
383*7534Sroot 	 * the symbolic link is relative to .).  Instead save
384*7534Sroot 	 * it (unlocked) as pdp.
385*7534Sroot 	 */
386*7534Sroot 	pdp = dp;
387*7534Sroot 	iunlock(pdp);
388*7534Sroot 	dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
389*7534Sroot 	if (dp == NULL)
390*7534Sroot 		goto bad2;
391*7534Sroot 	fs = dp->i_fs;
392*7534Sroot 
393*7534Sroot 	/*
394*7534Sroot 	 * Check for symbolic link
395*7534Sroot 	 */
396*7534Sroot 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
397*7534Sroot 		int pathlen = strlen(cp) + 1;
398*7534Sroot 		int bn;
399*7534Sroot 
400*7534Sroot 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
401*7534Sroot 		    ++nlink > MAXSYMLINKS) {
402*7534Sroot 			u.u_error = ELOOP;
403*7534Sroot 			goto bad2;
404*7534Sroot 		}
405*7534Sroot 		bcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
406*7534Sroot 		u.u_segflg = 1;
407*7534Sroot 		u.u_base = nbp->b_un.b_addr;
408*7534Sroot 		u.u_count = dp->i_size;
409*7534Sroot 		readi(dp);
410*7534Sroot 		if (u.u_error)
411*7534Sroot 			goto bad2;
412*7534Sroot 		cp = nbp->b_un.b_addr;
413*7534Sroot 		iput(dp);
4145972Swnj 		if (*cp == '/') {
415*7534Sroot 			irele(pdp);
4165972Swnj 			while (*cp == '/')
4175972Swnj 				cp++;
418*7534Sroot 			if ((dp = u.u_rdir) == NULL)
419*7534Sroot 				dp = rootdir;
420*7534Sroot 			ilock(dp);
421*7534Sroot 			dp->i_count++;
422*7534Sroot 		} else {
423*7534Sroot 			dp = pdp;
424*7534Sroot 			ilock(dp);
4255972Swnj 		}
426*7534Sroot 		fs = dp->i_fs;
427*7534Sroot 		goto dirloop;
42830Sbill 	}
429*7534Sroot 	irele(pdp);
430*7534Sroot 
43130Sbill 	/*
432*7534Sroot 	 * Not a symbolic link.  If more pathname,
433*7534Sroot 	 * continue at next component, else return.
43430Sbill 	 */
435*7534Sroot 	if (*cp == '/') {
436*7534Sroot 		while (*cp == '/')
437*7534Sroot 			cp++;
438*7534Sroot 		goto dirloop;
43930Sbill 	}
4405972Swnj 	brelse(nbp);
441*7534Sroot 	return (dp);
442*7534Sroot bad2:
443*7534Sroot 	irele(pdp);
444*7534Sroot bad:
445*7534Sroot 	if (bp)
446*7534Sroot 		brelse(bp);
447*7534Sroot 	if (dp)
448*7534Sroot 		iput(dp);
449*7534Sroot 	brelse(nbp);
4506571Smckusic 	return (NULL);
45130Sbill }
45230Sbill 
453*7534Sroot dirbad(ip, how)
454*7534Sroot 	struct inode *ip;
455*7534Sroot 	char *how;
456*7534Sroot {
457*7534Sroot 
458*7534Sroot 	printf("%s: bad dir ino %d at offset %d: %s\n",
459*7534Sroot 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
460*7534Sroot }
461*7534Sroot 
462*7534Sroot dirbadname(ep)
463*7534Sroot 	register struct direct *ep;
464*7534Sroot {
465*7534Sroot 	register char *cp;
466*7534Sroot 	register int i;
467*7534Sroot 
468*7534Sroot 	for (i = 0; i < ep->d_namlen; i++)
469*7534Sroot 		if (ep->d_name[i] == 0)
470*7534Sroot 			return (1);
471*7534Sroot 	return (ep->d_name[i]);
472*7534Sroot }
473*7534Sroot 
47430Sbill /*
47530Sbill  * Return the next character from the
47630Sbill  * kernel string pointed at by dirp.
47730Sbill  */
47830Sbill schar()
47930Sbill {
48030Sbill 
4815972Swnj 	return (*u.u_dirp++ & 0377);
48230Sbill }
48330Sbill 
48430Sbill /*
48530Sbill  * Return the next character from the
48630Sbill  * user string pointed at by dirp.
48730Sbill  */
48830Sbill uchar()
48930Sbill {
49030Sbill 	register c;
49130Sbill 
49230Sbill 	c = fubyte(u.u_dirp++);
4935972Swnj 	if (c == -1) {
49430Sbill 		u.u_error = EFAULT;
4955972Swnj 		c = 0;
4965972Swnj 	}
4975972Swnj 	return (c);
49830Sbill }
4995972Swnj 
500*7534Sroot /*
501*7534Sroot  * Write a directory entry after a call to namei, using the parameters
502*7534Sroot  * which it left in the u. area.  The argument ip is the inode which
503*7534Sroot  * the new directory entry will refer to.  The u. area field u.u_pdir is
504*7534Sroot  * a pointer to the directory to be written, which was left locked by
505*7534Sroot  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
506*7534Sroot  * how the space for the new entry is to be gotten.
507*7534Sroot  */
508*7534Sroot direnter(ip)
509*7534Sroot 	struct inode *ip;
5105972Swnj {
511*7534Sroot 	register struct direct *ep, *nep;
512*7534Sroot 	struct fs *fs;
513*7534Sroot 	struct buf *bp;
514*7534Sroot 	int loc, dsize, freespace, newentrysize;
515*7534Sroot 	char *dirbuf;
5165972Swnj 
517*7534Sroot 	u.u_dent.d_ino = ip->i_number;
518*7534Sroot 	u.u_segflg = 1;
519*7534Sroot 	newentrysize = DIRSIZ(&u.u_dent);
520*7534Sroot 	if (u.u_count == 0) {
521*7534Sroot 		/*
522*7534Sroot 		 * If u.u_count is 0, then namei could find no space in the
523*7534Sroot 		 * directory.  In this case u.u_offset will be on a directory
524*7534Sroot 		 * block boundary and we will write the new entry into a fresh
525*7534Sroot 		 * block.
526*7534Sroot 		 */
527*7534Sroot 		if (u.u_offset&(DIRBLKSIZ-1))
528*7534Sroot 			panic("wdir: newblk");
529*7534Sroot 		u.u_dent.d_reclen = DIRBLKSIZ;
530*7534Sroot 		u.u_count = newentrysize;
531*7534Sroot 		u.u_base = (caddr_t)&u.u_dent;
532*7534Sroot 		u.u_segflg = 1;
533*7534Sroot 		writei(u.u_pdir);
534*7534Sroot 		iput(u.u_pdir);
535*7534Sroot 		return;
536*7534Sroot 	}
537*7534Sroot 
538*7534Sroot 	/*
539*7534Sroot 	 * If u.u_count is non-zero, then namei found space for the
540*7534Sroot 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
541*7534Sroot 	 * in the directory.  To use this space, we may have to compact
542*7534Sroot 	 * the entries located there, by copying them together towards
543*7534Sroot 	 * the beginning of the block, leaving the free space in
544*7534Sroot 	 * one usable chunk at the end.
545*7534Sroot 	 */
546*7534Sroot 
547*7534Sroot 	/*
548*7534Sroot 	 * Increase size of directory if entry eats into new space.
549*7534Sroot 	 * This should never push the size past a new multiple of
550*7534Sroot 	 * DIRBLKSIZE.
551*7534Sroot 	 */
552*7534Sroot 	if (u.u_offset+u.u_count > u.u_pdir->i_size) {
553*7534Sroot 		if (((u.u_offset+u.u_count-1)&~(DIRBLKSIZ-1)) !=
554*7534Sroot 		    ((u.u_pdir->i_size-1)&~(DIRBLKSIZ-1))) {
555*7534Sroot printf("wdir i_size dir %s/%d (of=%d,cnt=%d,psz=%d))\n",
556*7534Sroot u.u_pdir->i_fs->fs_fsmnt,u.u_pdir->i_number,u.u_offset,
557*7534Sroot u.u_count,u.u_pdir->i_size);
558*7534Sroot 			panic("wdir: span");
559*7534Sroot 		}
560*7534Sroot 		u.u_pdir->i_size = u.u_offset + u.u_count;
561*7534Sroot 	}
562*7534Sroot 
563*7534Sroot 	/*
564*7534Sroot 	 * Get the block containing the space for the new directory
565*7534Sroot 	 * entry.
566*7534Sroot 	 */
567*7534Sroot 	bp = batoffset(u.u_pdir, u.u_offset, (char **)&dirbuf);
568*7534Sroot 	if (bp == 0)
569*7534Sroot 		return;
570*7534Sroot printf("direnter u.u_offset %d u.u_count %d, bpaddr %x, dirbuf %x\n",
571*7534Sroot     u.u_offset, u.u_count, bp->b_un.b_addr, dirbuf);
572*7534Sroot 
573*7534Sroot 	/*
574*7534Sroot 	 * Find space for the new entry.  In the simple case, the
575*7534Sroot 	 * entry at offset base will have the space.  If it does
576*7534Sroot 	 * not, then namei arranged that compacting the region
577*7534Sroot 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
578*7534Sroot 	 */
579*7534Sroot 	ep = (struct direct *)dirbuf;
580*7534Sroot 	dsize = DIRSIZ(ep);
581*7534Sroot 	freespace = ep->d_reclen - dsize;
582*7534Sroot 	for (loc = ep->d_reclen; loc < u.u_count; ) {
583*7534Sroot 		nep = (struct direct *)(dirbuf + loc);
584*7534Sroot 		if (ep->d_ino) {
585*7534Sroot 			/* trim the existing slot */
586*7534Sroot 			ep->d_reclen = dsize;
587*7534Sroot 			ep = (struct direct *)((char *)ep + dsize);
588*7534Sroot 		} else {
589*7534Sroot 			/* overwrite; nothing there; header is ours */
590*7534Sroot 			freespace += dsize;
591*7534Sroot 		}
592*7534Sroot 		dsize = DIRSIZ(nep);
593*7534Sroot 		freespace += nep->d_reclen - dsize;
594*7534Sroot 		loc += nep->d_reclen;
595*7534Sroot /*ZZ*/if((loc&~0x1ff)!=(loc+nep->d_reclen-1&~0x1ff))
596*7534Sroot /*ZZ*/printf("wdir: compact loc %d reclen %d (dir %s/%d)\n",loc,nep->d_reclen,
597*7534Sroot /*ZZ*/u.u_pdir->i_fs->fs_fsmnt,u.u_pdir->i_number);
598*7534Sroot 		bcopy(nep, ep, dsize);
599*7534Sroot 	}
600*7534Sroot 	/*
601*7534Sroot 	 * Update the pointer fields in the previous entry (if any),
602*7534Sroot 	 * copy in the new entry, and write out the block.
603*7534Sroot 	 */
604*7534Sroot 	if (ep->d_ino == 0) {
605*7534Sroot 		if (freespace + dsize < newentrysize)
606*7534Sroot 			panic("wdir: compact1");
607*7534Sroot /*ZZ*/if(freespace+dsize>512)panic("wdir: compact screwup");
608*7534Sroot 		u.u_dent.d_reclen = freespace + dsize;
609*7534Sroot 	} else {
610*7534Sroot 		if (freespace < newentrysize)
611*7534Sroot 			panic("wdir: compact2");
612*7534Sroot 		u.u_dent.d_reclen = freespace;
613*7534Sroot /*ZZ*/if ((((char *)ep-bp->b_un.b_addr)&0x1ff)+dsize>512) panic("wdir: reclen");
614*7534Sroot 		ep->d_reclen = dsize;
615*7534Sroot 		ep = (struct direct *)((char *)ep + dsize);
616*7534Sroot 	}
617*7534Sroot /*ZZ*/if((((char*)ep-bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen>512)panic("wdir: botch");
618*7534Sroot 	bcopy(&u.u_dent, ep, newentrysize);
619*7534Sroot 	bwrite(bp);
620*7534Sroot 	u.u_pdir->i_flag |= IUPD|ICHG;
621*7534Sroot 	iput(u.u_pdir);
6225972Swnj }
6236571Smckusic 
624*7534Sroot dirremove()
6256571Smckusic {
626*7534Sroot 	register struct inode *dp = u.u_pdir;
627*7534Sroot 	register struct fs *fs = dp->i_fs;
628*7534Sroot 	register struct buf *bp;
629*7534Sroot 	struct direct *ep;
6306571Smckusic 
631*7534Sroot printf("dirremove u.u_offset %d u.u_count %d\n", u.u_offset, u.u_count);
632*7534Sroot 	if (u.u_count == 0) {
633*7534Sroot 		/*
634*7534Sroot 		 * First entry in block: set d_ino to zero.
635*7534Sroot 		 */
636*7534Sroot /*ZZ*/if(u.u_offset&0x1ff)printf("missed dir compact dir %s/%d off %d file %s\n"
637*7534Sroot /*ZZ*/,dp->i_fs->fs_fsmnt,dp->i_number,u.u_offset,u.u_dent.d_name);
638*7534Sroot 		u.u_base = (caddr_t)&u.u_dent;
639*7534Sroot 		u.u_count = DIRSIZ(&u.u_dent);
640*7534Sroot 		u.u_dent.d_ino = 0;
641*7534Sroot 		writei(dp);
642*7534Sroot 	} else {
643*7534Sroot 		/*
644*7534Sroot 		 * Collapse new free space into previous entry.
645*7534Sroot 		 */
646*7534Sroot 		bp = batoffset(dp, u.u_offset - u.u_count, (char **)&ep);
647*7534Sroot 		if (bp == 0)
648*7534Sroot 			return (0);
649*7534Sroot 		ep->d_reclen += u.u_dent.d_reclen;
650*7534Sroot /*ZZ*/if((((char *)ep - bp->b_un.b_addr)&0x1ff)+u.u_dent.d_reclen > 512)
651*7534Sroot /*ZZ*/	panic("unlink: reclen");
652*7534Sroot 		bwrite(bp);
653*7534Sroot 		dp->i_flag |= IUPD|ICHG;
654*7534Sroot 	}
655*7534Sroot 	return (1);
6566571Smckusic }
657*7534Sroot 
658*7534Sroot struct buf *
659*7534Sroot batoffset(ip, offset, res)
660*7534Sroot 	struct inode *ip;
661*7534Sroot 	off_t offset;
662*7534Sroot 	char **res;
663*7534Sroot {
664*7534Sroot 	register struct fs *fs = ip->i_fs;
665*7534Sroot 	int lbn = lblkno(fs, offset);
666*7534Sroot 	int base = blkoff(fs, offset);
667*7534Sroot 	int bsize = blksize(fs, ip, lbn);
668*7534Sroot 	int bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
669*7534Sroot 	register struct buf *bp;
670*7534Sroot 
671*7534Sroot 	if (u.u_error)
672*7534Sroot 		return (0);
673*7534Sroot 	bp = bread(ip->i_dev, bn, bsize);
674*7534Sroot 	if (bp->b_flags & B_ERROR) {
675*7534Sroot 		brelse(bp);
676*7534Sroot 		return (0);
677*7534Sroot 	}
678*7534Sroot 	if (res)
679*7534Sroot 		*res = bp->b_un.b_addr + base;
680*7534Sroot printf("b_addr %x res pointer %x\n", bp->b_un.b_addr, *res);
681*7534Sroot 	return (bp);
682*7534Sroot }
683