xref: /original-bsd/sys/ufs/ufs/ufs_lookup.c (revision 9cf5e8d3)
1 /*	ufs_lookup.c	4.37	83/04/22	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/inode.h"
6 #include "../h/fs.h"
7 #include "../h/mount.h"
8 #include "../h/dir.h"
9 #include "../h/user.h"
10 #include "../h/buf.h"
11 #include "../h/conf.h"
12 #include "../h/uio.h"
13 #include "../h/nami.h"
14 
15 struct	buf *blkatoff();
16 int	dirchk = 0;
17 /*
18  * Convert a pathname into a pointer to a locked inode,
19  * with side effects usable in creating and removing files.
20  * This is a very central and rather complicated routine.
21  *
22  * The func argument gives the routine which returns successive
23  * characters of the name to be translated.
24  *
25  * The flag argument is (LOOKUP, CREATE, DELETE) depending on whether
26  * the name is to be (looked up, created, deleted).  If flag has
27  * LOCKPARENT or'ed into it and the target of the pathname exists,
28  * namei returns both the target and its parent directory locked.
29  * If the file system is not maintained in a strict tree hierarchy,
30  * this can result in a deadlock situation.  When creating and
31  * LOCKPARENT is specified, the target may not be ".".  When deleting
32  * and LOCKPARENT is specified, the target may be ".", but the caller
33  * must check to insure it does an irele and iput instead of two iputs.
34  *
35  * The follow argument is 1 when symbolic links are to be followed
36  * when they occur at the end of the name translation process.
37  *
38  * Overall outline:
39  *
40  *	copy in name
41  *	get starting directory
42  * dirloop:
43  *	check accessibility of directory
44  * dirloop2:
45  *	copy next component of name to u.u_dent
46  *	handle degenerate case where name is null string
47  *	search for name in directory, to found or notfound
48  * notfound:
49  *	if creating, return locked directory, leaving info on avail. slots
50  *	else return error
51  * found:
52  *	if at end of path and deleting, return information to allow delete
53  *	if at end of path and rewriting (create and LOCKPARENT), lock targe
54  *	  inode and return info to allow rewrite
55  *	if .. and on mounted filesys, look in mount table for parent
56  *	if symbolic link, massage name in buffer and continue at dirloop
57  *	if more components of name, do next level at dirloop
58  *	return the answer as locked inode
59  *
60  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode,
61  *	 but unlocked.
62  */
63 struct inode *
64 namei(func, flag, follow)
65 	int (*func)(), flag, follow;
66 {
67 	register char *cp;		/* pointer into pathname argument */
68 /* these variables refer to things which must be freed or unlocked */
69 	register struct inode *dp = 0;	/* the directory we are searching */
70 	register struct fs *fs;		/* file system that directory is in */
71 	register struct buf *bp = 0;	/* a buffer of directory entries */
72 	register struct direct *ep;	/* the current directory entry */
73 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
74 	register struct buf *nbp;	/* buffer storing path name argument */
75 /* these variables hold information about the search for a slot */
76 	enum {NONE, COMPACT, FOUND} slotstatus;
77 	int slotoffset = -1;		/* offset of area with free space */
78 	int slotsize;			/* size of area at slotoffset */
79 	int slotfreespace;		/* amount of space free in slot */
80 	int slotneeded;			/* size of the entry we're seeking */
81 /* */
82 	int dirsize;
83 	int prevoff;			/* u.u_offset of previous entry */
84 	int nlink = 0;			/* number of symbolic links taken */
85 	struct inode *pdp;		/* saved dp during symlink work */
86 	int i;
87 	int lockparent;
88 
89 	lockparent = flag & LOCKPARENT;
90 	flag &= ~LOCKPARENT;
91 	/*
92 	 * Get a buffer for the name to be translated, and copy the
93 	 * name into the buffer.
94 	 */
95 	nbp = geteblk(MAXPATHLEN);
96 	for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
97 		if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
98 			u.u_error = EPERM;
99 			goto bad;
100 		}
101 		cp++;
102 		if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
103 			u.u_error = ENOENT;
104 			goto bad;
105 		}
106 	}
107 	if (u.u_error)
108 		goto bad;
109 
110 	/*
111 	 * Get starting directory.
112 	 */
113 	cp = nbp->b_un.b_addr;
114 	if (*cp == '/') {
115 		while (*cp == '/')
116 			cp++;
117 		if ((dp = u.u_rdir) == NULL)
118 			dp = rootdir;
119 	} else
120 		dp = u.u_cdir;
121 	fs = dp->i_fs;
122 	ilock(dp);
123 	dp->i_count++;
124 	u.u_pdir = (struct inode *)0xc0000000;		/* illegal */
125 
126 	/*
127 	 * We come to dirloop to search a new directory.
128 	 * The directory must be locked so that it can be
129 	 * iput, and fs must be already set to dp->i_fs.
130 	 */
131 dirloop:
132 	/*
133 	 * Check accessiblity of directory.
134 	 */
135 	if ((dp->i_mode&IFMT) != IFDIR) {
136 		u.u_error = ENOTDIR;
137 		goto bad;
138 	}
139 	if (access(dp, IEXEC))
140 		goto bad;
141 
142 dirloop2:
143 	/*
144 	 * Copy next component of name to u.u_dent.
145 	 */
146 	for (i = 0; *cp != 0 && *cp != '/'; cp++) {
147 		if (i >= MAXNAMLEN) {
148 			u.u_error = ENOENT;
149 			goto bad;
150 		}
151 		u.u_dent.d_name[i++] = *cp;
152 	}
153 	u.u_dent.d_namlen = i;
154 	u.u_dent.d_name[i] = 0;
155 
156 	/*
157 	 * Check for degenerate name (e.g. / or "")
158 	 * which is a way of talking about a directory,
159 	 * e.g. like "/." or ".".
160 	 */
161 	if (u.u_dent.d_name[0] == 0) {
162 		if (flag || lockparent) {
163 			u.u_error = ENOENT;
164 			goto bad;
165 		}
166 		brelse(nbp);
167 		return (dp);
168 	}
169 
170 	/*
171 	 * Suppress search for slots unless creating
172 	 * file and at end of pathname, in which case
173 	 * we watch for a place to put the new file in
174 	 * case it doesn't already exist.
175 	 */
176 	slotstatus = FOUND;
177 	if (flag == CREATE && *cp == 0) {
178 		slotstatus = NONE;
179 		slotfreespace = 0;
180 		slotneeded = DIRSIZ(&u.u_dent);
181 	}
182 
183 	dirsize = roundup(dp->i_size, DIRBLKSIZ);
184 	u.u_offset = 0;
185 	while (u.u_offset < dirsize) {
186 		/*
187 		 * If offset is on a block boundary,
188 		 * read the next directory block.
189 		 * Release previous if it exists.
190 		 */
191 		if (blkoff(fs, u.u_offset) == 0) {
192 			if (bp != NULL)
193 				brelse(bp);
194 			bp = blkatoff(dp, u.u_offset, (char **)0);
195 			if (bp == 0)
196 				goto bad;
197 			entryoffsetinblock = 0;
198 		}
199 
200 		/*
201 		 * If still looking for a slot, and at a DIRBLKSIZE
202 		 * boundary, have to start looking for free space
203 		 * again.
204 		 */
205 		if (slotstatus == NONE &&
206 		    (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
207 			slotoffset = -1;
208 			slotfreespace = 0;
209 		}
210 
211 		/*
212 		 * Get pointer to next entry, and do consistency checking:
213 		 *	record length must be multiple of 4
214 		 *	record length must not be zero
215 		 *	entry must fit in rest of this DIRBLKSIZ block
216 		 *	record must be large enough to contain name
217 		 * When dirchk is set we also check:
218 		 *	name is not longer than MAXNAMLEN
219 		 *	name must be as long as advertised, and null terminated
220 		 * Checking last two conditions is done only when dirchk is
221 		 * set, to save time.
222 		 */
223 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
224 		i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
225 		if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
226 		    ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
227 		    dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
228 			dirbad(dp, "mangled entry");
229 			u.u_offset += i;
230 			entryoffsetinblock += i;
231 			continue;
232 		}
233 
234 		/*
235 		 * If an appropriate sized slot has not yet been found,
236 		 * check to see if one is available. Also accumulate space
237 		 * in the current block so that we can determine if
238 		 * compaction is viable.
239 		 */
240 		if (slotstatus != FOUND) {
241 			int size = ep->d_reclen;
242 
243 			if (ep->d_ino != 0)
244 				size -= DIRSIZ(ep);
245 			if (size > 0) {
246 				if (size >= slotneeded) {
247 					slotstatus = FOUND;
248 					slotoffset = u.u_offset;
249 					slotsize = ep->d_reclen;
250 				} else if (slotstatus == NONE) {
251 					slotfreespace += size;
252 					if (slotoffset == -1)
253 						slotoffset = u.u_offset;
254 					if (slotfreespace >= slotneeded) {
255 						slotstatus = COMPACT;
256 						slotsize =
257 						    u.u_offset+ep->d_reclen -
258 						      slotoffset;
259 					}
260 				}
261 			}
262 		}
263 
264 		/*
265 		 * Check for a name match.
266 		 */
267 		if (ep->d_ino) {
268 			if (ep->d_namlen == u.u_dent.d_namlen &&
269 			    !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
270 				goto found;
271 		}
272 		prevoff = u.u_offset;
273 		u.u_offset += ep->d_reclen;
274 		entryoffsetinblock += ep->d_reclen;
275 	}
276 /* notfound: */
277 	/*
278 	 * If creating, and at end of pathname and current
279 	 * directory has not been removed, then can consider
280 	 * allowing file to be created.
281 	 */
282 	if (flag == CREATE && *cp == 0 && dp->i_nlink != 0) {
283 		/*
284 		 * Access for write is interpreted as allowing
285 		 * creation of files in the directory.
286 		 */
287 		if (access(dp, IWRITE))
288 			goto bad;
289 		/*
290 		 * Return an indication of where the new directory
291 		 * entry should be put.  If we didn't find a slot,
292 		 * then set u.u_count to 0 indicating that the
293 		 * new slot belongs at the end of the directory.
294 		 * If we found a slot, then the new entry can be
295 		 * put in the range [u.u_offset..u.u_offset+u.u_count)
296 		 */
297 		if (slotstatus == NONE)
298 			u.u_count = 0;
299 		else {
300 			u.u_offset = slotoffset;
301 			u.u_count = slotsize;
302 		}
303 		dp->i_flag |= IUPD|ICHG;
304 		if (bp)
305 			brelse(bp);
306 		brelse(nbp);
307 		/*
308 		 * We return with the directory locked, so that
309 		 * the parameters we set up above will still be
310 		 * valid if we actually decide to do a direnter().
311 		 * We return NULL to indicate that the entry doesn't
312 		 * currently exist, leaving a pointer to the (locked)
313 		 * directory inode in u.u_pdir.
314 		 */
315 		u.u_pdir = dp;
316 		return (NULL);
317 	}
318 	u.u_error = ENOENT;
319 	goto bad;
320 found:
321 	/*
322 	 * Check that directory length properly reflects presence
323 	 * of this entry.
324 	 */
325 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
326 		dirbad(dp, "i_size too small");
327 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
328 		dp->i_flag |= IUPD|ICHG;
329 	}
330 
331 	/*
332 	 * Found component in pathname; save directory
333 	 * entry in u.u_dent, and release directory buffer.
334 	 */
335 	bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep));
336 	brelse(bp);
337 	bp = NULL;
338 
339 	/*
340 	 * If deleting, and at end of pathname, return
341 	 * parameters which can be used to remove file.
342 	 * If the lockparent flag isn't set, we return only
343 	 * the directory (in u.u_pdir), otherwise we go
344 	 * on and lock the inode, being careful with ".".
345 	 */
346 	if (flag == DELETE && *cp == 0) {
347 		/*
348 		 * Write access to directory required to delete files.
349 		 */
350 		if (access(dp, IWRITE))
351 			goto bad;
352 		u.u_pdir = dp;		/* for dirremove() */
353 		/*
354 		 * Return pointer to current entry in u.u_offset,
355 		 * and distance past previous entry (if there
356 		 * is a previous entry in this block) in u.u_count.
357 		 * Save directory inode pointer in u.u_pdir for dirremove().
358 		 */
359 		if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
360 			u.u_count = 0;
361 		else
362 			u.u_count = u.u_offset - prevoff;
363 		if (lockparent) {
364 			if (dp->i_number == u.u_dent.d_ino)
365 				dp->i_count++;
366 			else {
367 				dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
368 				if (dp == NULL) {
369 					iput(u.u_pdir);
370 					goto bad;
371 				}
372 			}
373 		}
374 		brelse(nbp);
375 		return (dp);
376 	}
377 
378 	/*
379 	 * Special handling for ".." allowing chdir out of mounted
380 	 * file system: indirect .. in root inode to reevaluate
381 	 * in directory file system was mounted on.
382 	 */
383 	if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
384 	    u.u_dent.d_name[2] == '\0') {
385 		if (dp == u.u_rdir)
386 			u.u_dent.d_ino = dp->i_number;
387 		else if (u.u_dent.d_ino == ROOTINO &&
388 		   dp->i_number == ROOTINO) {
389 			for (i = 1; i < NMOUNT; i++)
390 			if (mount[i].m_bufp != NULL &&
391 			   mount[i].m_dev == dp->i_dev) {
392 				iput(dp);
393 				dp = mount[i].m_inodp;
394 				ilock(dp);
395 				dp->i_count++;
396 				fs = dp->i_fs;
397 				cp -= 2;     /* back over .. */
398 				goto dirloop2;
399 			}
400 		}
401 	}
402 
403 	/*
404 	 * If rewriting (rename), return the inode and the
405 	 * information required to rewrite the present directory
406 	 * Must get inode of directory entry to verify it's a
407 	 * regular file, or empty directory.
408 	 */
409 	if ((flag == CREATE && lockparent) && *cp == 0) {
410 		if (access(dp, IWRITE))
411 			goto bad;
412 		u.u_pdir = dp;		/* for dirrewrite() */
413 		/*
414 		 * Careful about locking second inode.
415 		 * This can only occur if the target is ".".
416 		 */
417 		if (dp->i_number == u.u_dent.d_ino) {
418 			u.u_error = EISDIR;		/* XXX */
419 			goto bad;
420 		}
421 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
422 		if (dp == NULL) {
423 			iput(u.u_pdir);
424 			goto bad;
425 		}
426 		brelse(nbp);
427 		return (dp);
428 	}
429 
430 	/*
431 	 * Check for symbolic link, which may require us to massage the
432 	 * name before we continue translation.  We do not `iput' the
433 	 * directory because we may need it again if the symbolic link
434 	 * is relative to the current directory.  Instead we save it
435 	 * unlocked as "pdp".  We must get the target inode before unlocking
436 	 * the directory to insure that the inode will not be removed
437 	 * before we get it.  We prevent deadlock by always fetching
438 	 * inodes from the root, moving down the directory tree. Thus
439 	 * when following backward pointers ".." we must unlock the
440 	 * parent directory before getting the requested directory.
441 	 * There is a potential race condition here if both the current
442 	 * and parent directories are removed before the `iget' for the
443 	 * inode associated with ".." returns.  We hope that this occurs
444 	 * infrequently since we cannot avoid this race condition without
445 	 * implementing a sophistocated deadlock detection algorithm.
446 	 * Note also that this simple deadlock detection scheme will not
447 	 * work if the file system has any hard links other than ".."
448 	 * that point backwards in the directory structure.
449 	 */
450 	pdp = dp;
451 	if (bcmp(u.u_dent.d_name, "..", 3) == 0) {
452 		iunlock(pdp);	/* race to get the inode */
453 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
454 		if (dp == NULL)
455 			goto bad2;
456 	} else if (dp->i_number == u.u_dent.d_ino) {
457 		dp->i_count++;	/* we want ourself, ie "." */
458 	} else {
459 		dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
460 		iunlock(pdp);
461 		if (dp == NULL)
462 			goto bad2;
463 	}
464 	fs = dp->i_fs;
465 
466 	/*
467 	 * Check for symbolic link
468 	 */
469 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
470 		u_int pathlen = strlen(cp) + 1;
471 
472 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
473 		    ++nlink > MAXSYMLINKS) {
474 			u.u_error = ELOOP;
475 			goto bad2;
476 		}
477 		ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
478 		u.u_error =
479 		    rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size,
480 			0, 1, (int *)0);
481 		if (u.u_error)
482 			goto bad2;
483 		cp = nbp->b_un.b_addr;
484 		iput(dp);
485 		if (*cp == '/') {
486 			irele(pdp);
487 			while (*cp == '/')
488 				cp++;
489 			if ((dp = u.u_rdir) == NULL)
490 				dp = rootdir;
491 			ilock(dp);
492 			dp->i_count++;
493 		} else {
494 			dp = pdp;
495 			ilock(dp);
496 		}
497 		fs = dp->i_fs;
498 		goto dirloop;
499 	}
500 
501 	/*
502 	 * Not a symbolic link.  If more pathname,
503 	 * continue at next component, else return.
504 	 */
505 	if (*cp == '/') {
506 		while (*cp == '/')
507 			cp++;
508 		irele(pdp);
509 		goto dirloop;
510 	}
511 	brelse(nbp);
512 	if (lockparent)
513 		u.u_pdir = pdp;
514 	else
515 		irele(pdp);
516 	return (dp);
517 bad2:
518 	irele(pdp);
519 bad:
520 	if (bp)
521 		brelse(bp);
522 	if (dp)
523 		iput(dp);
524 	brelse(nbp);
525 	return (NULL);
526 }
527 
528 dirbad(ip, how)
529 	struct inode *ip;
530 	char *how;
531 {
532 
533 	printf("%s: bad dir ino %d at offset %d: %s\n",
534 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
535 }
536 
537 dirbadname(ep)
538 	register struct direct *ep;
539 {
540 	register int i;
541 
542 	for (i = 0; i < ep->d_namlen; i++)
543 		if (ep->d_name[i] == 0)
544 			return (1);
545 	return (ep->d_name[i]);
546 }
547 
548 /*
549  * Write a directory entry after a call to namei, using the parameters
550  * which it left in the u. area.  The argument ip is the inode which
551  * the new directory entry will refer to.  The u. area field u.u_pdir is
552  * a pointer to the directory to be written, which was left locked by
553  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
554  * how the space for the new entry is to be gotten.
555  */
556 direnter(ip)
557 	struct inode *ip;
558 {
559 	register struct direct *ep, *nep;
560 	struct buf *bp;
561 	int loc, spacefree, error = 0;
562 	u_int dsize;
563 	int newentrysize;
564 	char *dirbuf;
565 
566 	u.u_dent.d_ino = ip->i_number;
567 	u.u_segflg = 1;
568 	newentrysize = DIRSIZ(&u.u_dent);
569 	if (u.u_count == 0) {
570 		/*
571 		 * If u.u_count is 0, then namei could find no space in the
572 		 * directory.  In this case u.u_offset will be on a directory
573 		 * block boundary and we will write the new entry into a fresh
574 		 * block.
575 		 */
576 		if (u.u_offset&(DIRBLKSIZ-1))
577 			panic("wdir: newblk");
578 		u.u_dent.d_reclen = DIRBLKSIZ;
579 		error = rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
580 		    newentrysize, u.u_offset, 1, (int *)0);
581 		iput(u.u_pdir);
582 		return (error);
583 	}
584 
585 	/*
586 	 * If u.u_count is non-zero, then namei found space for the
587 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
588 	 * in the directory.  To use this space, we may have to compact
589 	 * the entries located there, by copying them together towards
590 	 * the beginning of the block, leaving the free space in
591 	 * one usable chunk at the end.
592 	 */
593 
594 	/*
595 	 * Increase size of directory if entry eats into new space.
596 	 * This should never push the size past a new multiple of
597 	 * DIRBLKSIZE.
598 	 */
599 	if (u.u_offset + u.u_count > u.u_pdir->i_size)
600 		u.u_pdir->i_size = u.u_offset + u.u_count;
601 
602 	/*
603 	 * Get the block containing the space for the new directory
604 	 * entry.  Should return error by result instead of u.u_error.
605 	 */
606 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
607 	if (bp == 0) {
608 		iput(u.u_pdir);
609 		return (u.u_error);
610 	}
611 
612 	/*
613 	 * Find space for the new entry.  In the simple case, the
614 	 * entry at offset base will have the space.  If it does
615 	 * not, then namei arranged that compacting the region
616 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
617 	 */
618 	ep = (struct direct *)dirbuf;
619 	dsize = DIRSIZ(ep);
620 	spacefree = ep->d_reclen - dsize;
621 	for (loc = ep->d_reclen; loc < u.u_count; ) {
622 		nep = (struct direct *)(dirbuf + loc);
623 		if (ep->d_ino) {
624 			/* trim the existing slot */
625 			ep->d_reclen = dsize;
626 			ep = (struct direct *)((char *)ep + dsize);
627 		} else {
628 			/* overwrite; nothing there; header is ours */
629 			spacefree += dsize;
630 		}
631 		dsize = DIRSIZ(nep);
632 		spacefree += nep->d_reclen - dsize;
633 		loc += nep->d_reclen;
634 		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
635 	}
636 	/*
637 	 * Update the pointer fields in the previous entry (if any),
638 	 * copy in the new entry, and write out the block.
639 	 */
640 	if (ep->d_ino == 0) {
641 		if (spacefree + dsize < newentrysize)
642 			panic("wdir: compact1");
643 		u.u_dent.d_reclen = spacefree + dsize;
644 	} else {
645 		if (spacefree < newentrysize)
646 			panic("wdir: compact2");
647 		u.u_dent.d_reclen = spacefree;
648 		ep->d_reclen = dsize;
649 		ep = (struct direct *)((char *)ep + dsize);
650 	}
651 	bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
652 	bwrite(bp);
653 	u.u_pdir->i_flag |= IUPD|ICHG;
654 	iput(u.u_pdir);
655 	return (error);
656 }
657 
658 /*
659  * Remove a directory entry after a call to namei, using the
660  * parameters which it left in the u. area.  The u. entry
661  * u_offset contains the offset into the directory of the
662  * entry to be eliminated.  The u_count field contains the
663  * size of the previous record in the directory.  If this
664  * is 0, the first entry is being deleted, so we need only
665  * zero the inode number to mark the entry as free.  If the
666  * entry isn't the first in the directory, we must reclaim
667  * the space of the now empty record by adding the record size
668  * to the size of the previous entry.
669  */
670 dirremove()
671 {
672 	register struct inode *dp = u.u_pdir;
673 	register struct buf *bp;
674 	struct direct *ep;
675 
676 	if (u.u_count == 0) {
677 		/*
678 		 * First entry in block: set d_ino to zero.
679 		 */
680 		u.u_dent.d_ino = 0;
681 		(void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
682 		    (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
683 	} else {
684 		/*
685 		 * Collapse new free space into previous entry.
686 		 */
687 		bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
688 		if (bp == 0)
689 			return (0);
690 		ep->d_reclen += u.u_dent.d_reclen;
691 		bwrite(bp);
692 		dp->i_flag |= IUPD|ICHG;
693 	}
694 	return (1);
695 }
696 
697 /*
698  * Rewrite an existing directory entry to point at the inode
699  * supplied.  The parameters describing the directory entry are
700  * set up by a call to namei.
701  */
702 dirrewrite(dp, ip)
703 	struct inode *dp, *ip;
704 {
705 
706 	u.u_dent.d_ino = ip->i_number;
707 	u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
708 		(int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
709 	iput(dp);
710 }
711 
712 /*
713  * Return buffer with contents of block "offset"
714  * from the beginning of directory "ip".  If "res"
715  * is non-zero, fill it in with a pointer to the
716  * remaining space in the directory.
717  */
718 struct buf *
719 blkatoff(ip, offset, res)
720 	struct inode *ip;
721 	off_t offset;
722 	char **res;
723 {
724 	register struct fs *fs = ip->i_fs;
725 	daddr_t lbn = lblkno(fs, offset);
726 	int base = blkoff(fs, offset);
727 	int bsize = blksize(fs, ip, lbn);
728 	daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
729 	register struct buf *bp;
730 
731 	if (u.u_error)
732 		return (0);
733 	bp = bread(ip->i_dev, bn, bsize);
734 	if (bp->b_flags & B_ERROR) {
735 		brelse(bp);
736 		return (0);
737 	}
738 	if (res)
739 		*res = bp->b_un.b_addr + base;
740 	return (bp);
741 }
742 
743 /*
744  * Check if a directory is empty or not.
745  * Inode supplied must be locked.
746  */
747 dirempty(ip)
748 	register struct inode *ip;
749 {
750 	register off_t off;
751 	struct direct dbuf;
752 	register struct direct *dp = &dbuf;
753 	int error, count;
754 
755 	for (off = 0; off < ip->i_size; off += dp->d_reclen) {
756 		error = rdwri(UIO_READ, ip, (caddr_t)dp,
757 			sizeof (struct direct), off, 1, &count);
758 		count = sizeof (struct direct) - count;
759 #define	MINDIRSIZ (sizeof (struct direct) - (MAXNAMLEN + 1))
760 		if (error || count < MINDIRSIZ || count < DIRSIZ(dp))
761 			return (0);
762 		if (dp->d_ino == 0)
763 			continue;
764 		if (dp->d_name[0] != '.')
765 			return (0);
766 		if (dp->d_namlen == 1 ||
767 		    (dp->d_namlen == 2 && dp->d_name[1] == '.'))
768 			continue;
769 		return (0);
770 	}
771 	return (1);
772 }
773