xref: /original-bsd/sys/kern/vfs_lookup.c (revision f0fd5f8a)
1 /*	vfs_lookup.c	4.31	82/11/17	*/
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) {
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
432 	 * to massage the name before we continue translation.
433 	 * To avoid deadlock have to unlock the current directory,
434 	 * but don't iput it because we may need it again (if
435 	 * the symbolic link is relative to .).  Instead save
436 	 * it (unlocked) as pdp.
437 	 */
438 	pdp = dp;
439 	iunlock(pdp);
440 	dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
441 	if (dp == NULL)
442 		goto bad2;
443 	fs = dp->i_fs;
444 
445 	/*
446 	 * Check for symbolic link
447 	 */
448 	if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
449 		u_int pathlen = strlen(cp) + 1;
450 
451 		if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
452 		    ++nlink > MAXSYMLINKS) {
453 			u.u_error = ELOOP;
454 			goto bad2;
455 		}
456 		ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
457 		u.u_error =
458 		    rdwri(UIO_READ, dp, nbp->b_un.b_addr, (int)dp->i_size,
459 			0, 1, (int *)0);
460 		if (u.u_error)
461 			goto bad2;
462 		cp = nbp->b_un.b_addr;
463 		iput(dp);
464 		if (*cp == '/') {
465 			irele(pdp);
466 			while (*cp == '/')
467 				cp++;
468 			if ((dp = u.u_rdir) == NULL)
469 				dp = rootdir;
470 			ilock(dp);
471 			dp->i_count++;
472 		} else {
473 			dp = pdp;
474 			ilock(dp);
475 		}
476 		fs = dp->i_fs;
477 		goto dirloop;
478 	}
479 
480 	/*
481 	 * Not a symbolic link.  If more pathname,
482 	 * continue at next component, else return.
483 	 */
484 	if (*cp == '/') {
485 		while (*cp == '/')
486 			cp++;
487 		irele(pdp);
488 		goto dirloop;
489 	}
490 	brelse(nbp);
491 	if (lockparent)
492 		u.u_pdir = pdp;
493 	else
494 		irele(pdp);
495 	return (dp);
496 bad2:
497 	irele(pdp);
498 bad:
499 	if (bp)
500 		brelse(bp);
501 	if (dp)
502 		iput(dp);
503 	brelse(nbp);
504 	return (NULL);
505 }
506 
507 dirbad(ip, how)
508 	struct inode *ip;
509 	char *how;
510 {
511 
512 	printf("%s: bad dir ino %d at offset %d: %s\n",
513 	    ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
514 }
515 
516 dirbadname(ep)
517 	register struct direct *ep;
518 {
519 	register int i;
520 
521 	for (i = 0; i < ep->d_namlen; i++)
522 		if (ep->d_name[i] == 0)
523 			return (1);
524 	return (ep->d_name[i]);
525 }
526 
527 /*
528  * Write a directory entry after a call to namei, using the parameters
529  * which it left in the u. area.  The argument ip is the inode which
530  * the new directory entry will refer to.  The u. area field u.u_pdir is
531  * a pointer to the directory to be written, which was left locked by
532  * namei.  Remaining parameters (u.u_offset, u.u_count) indicate
533  * how the space for the new entry is to be gotten.
534  */
535 direnter(ip)
536 	struct inode *ip;
537 {
538 	register struct direct *ep, *nep;
539 	struct buf *bp;
540 	int loc, freespace;
541 	u_int dsize;
542 	int newentrysize;
543 	char *dirbuf;
544 
545 	u.u_dent.d_ino = ip->i_number;
546 	u.u_segflg = 1;
547 	newentrysize = DIRSIZ(&u.u_dent);
548 	if (u.u_count == 0) {
549 		/*
550 		 * If u.u_count is 0, then namei could find no space in the
551 		 * directory.  In this case u.u_offset will be on a directory
552 		 * block boundary and we will write the new entry into a fresh
553 		 * block.
554 		 */
555 		if (u.u_offset&(DIRBLKSIZ-1))
556 			panic("wdir: newblk");
557 		u.u_dent.d_reclen = DIRBLKSIZ;
558 		(void) rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
559 		    newentrysize, u.u_offset, 1, (int *)0);
560 		iput(u.u_pdir);
561 		return;
562 	}
563 
564 	/*
565 	 * If u.u_count is non-zero, then namei found space for the
566 	 * new entry in the range u.u_offset to u.u_offset+u.u_count.
567 	 * in the directory.  To use this space, we may have to compact
568 	 * the entries located there, by copying them together towards
569 	 * the beginning of the block, leaving the free space in
570 	 * one usable chunk at the end.
571 	 */
572 
573 	/*
574 	 * Increase size of directory if entry eats into new space.
575 	 * This should never push the size past a new multiple of
576 	 * DIRBLKSIZE.
577 	 */
578 	if (u.u_offset + u.u_count > u.u_pdir->i_size)
579 		u.u_pdir->i_size = u.u_offset + u.u_count;
580 
581 	/*
582 	 * Get the block containing the space for the new directory
583 	 * entry.
584 	 */
585 	bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
586 	if (bp == 0) {
587 		iput(u.u_pdir);
588 		return;
589 	}
590 
591 	/*
592 	 * Find space for the new entry.  In the simple case, the
593 	 * entry at offset base will have the space.  If it does
594 	 * not, then namei arranged that compacting the region
595 	 * u.u_offset to u.u_offset+u.u_count would yield the space.
596 	 */
597 	ep = (struct direct *)dirbuf;
598 	dsize = DIRSIZ(ep);
599 	freespace = ep->d_reclen - dsize;
600 	for (loc = ep->d_reclen; loc < u.u_count; ) {
601 		nep = (struct direct *)(dirbuf + loc);
602 		if (ep->d_ino) {
603 			/* trim the existing slot */
604 			ep->d_reclen = dsize;
605 			ep = (struct direct *)((char *)ep + dsize);
606 		} else {
607 			/* overwrite; nothing there; header is ours */
608 			freespace += dsize;
609 		}
610 		dsize = DIRSIZ(nep);
611 		freespace += nep->d_reclen - dsize;
612 		loc += nep->d_reclen;
613 		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
614 	}
615 	/*
616 	 * Update the pointer fields in the previous entry (if any),
617 	 * copy in the new entry, and write out the block.
618 	 */
619 	if (ep->d_ino == 0) {
620 		if (freespace + dsize < newentrysize)
621 			panic("wdir: compact1");
622 		u.u_dent.d_reclen = freespace + dsize;
623 	} else {
624 		if (freespace < newentrysize)
625 			panic("wdir: compact2");
626 		u.u_dent.d_reclen = freespace;
627 		ep->d_reclen = dsize;
628 		ep = (struct direct *)((char *)ep + dsize);
629 	}
630 	bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
631 	bwrite(bp);
632 	u.u_pdir->i_flag |= IUPD|ICHG;
633 	iput(u.u_pdir);
634 }
635 
636 /*
637  * Remove a directory entry after a call to namei, using the
638  * parameters which it left in the u. area.  The u. entry
639  * u_offset contains the offset into the directory of the
640  * entry to be eliminated.  The u_count field contains the
641  * size of the previous record in the directory.  If this
642  * is 0, the first entry is being deleted, so we need only
643  * zero the inode number to mark the entry as free.  If the
644  * entry isn't the first in the directory, we must reclaim
645  * the space of the now empty record by adding the record size
646  * to the size of the previous entry.
647  */
648 dirremove()
649 {
650 	register struct inode *dp = u.u_pdir;
651 	register struct buf *bp;
652 	struct direct *ep;
653 
654 	if (u.u_count == 0) {
655 		/*
656 		 * First entry in block: set d_ino to zero.
657 		 */
658 		u.u_dent.d_ino = 0;
659 		(void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
660 		    (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
661 	} else {
662 		/*
663 		 * Collapse new free space into previous entry.
664 		 */
665 		bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
666 		if (bp == 0)
667 			return (0);
668 		ep->d_reclen += u.u_dent.d_reclen;
669 		bwrite(bp);
670 		dp->i_flag |= IUPD|ICHG;
671 	}
672 	return (1);
673 }
674 
675 /*
676  * Rewrite an existing directory entry to point at the inode
677  * supplied.  The parameters describing the directory entry are
678  * set up by a call to namei.
679  */
680 dirrewrite(dp, ip)
681 	struct inode *dp, *ip;
682 {
683 
684 	u.u_dent.d_ino = ip->i_number;
685 	u.u_error = rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
686 		(int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
687 	iput(dp);
688 }
689 
690 /*
691  * Return buffer with contents of block "offset"
692  * from the beginning of directory "ip".  If "res"
693  * is non-zero, fill it in with a pointer to the
694  * remaining space in the directory.
695  */
696 struct buf *
697 blkatoff(ip, offset, res)
698 	struct inode *ip;
699 	off_t offset;
700 	char **res;
701 {
702 	register struct fs *fs = ip->i_fs;
703 	daddr_t lbn = lblkno(fs, offset);
704 	int base = blkoff(fs, offset);
705 	int bsize = blksize(fs, ip, lbn);
706 	daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
707 	register struct buf *bp;
708 
709 	if (u.u_error)
710 		return (0);
711 	bp = bread(ip->i_dev, bn, bsize);
712 	if (bp->b_flags & B_ERROR) {
713 		brelse(bp);
714 		return (0);
715 	}
716 	if (res)
717 		*res = bp->b_un.b_addr + base;
718 	return (bp);
719 }
720 
721 /*
722  * Check if a directory is empty or not.
723  * Inode supplied must be locked.
724  */
725 dirempty(ip)
726 	struct inode *ip;
727 {
728 	register off_t off;
729 	struct direct dbuf;
730 	register struct direct *dp = &dbuf;
731 	int error;
732 
733 	for (off = 0; off < ip->i_size; off += dp->d_reclen) {
734 		error = rdwri(UIO_READ, ip, (caddr_t)dp,
735 			sizeof (struct direct), off, 1, (int *)0);
736 		if (error)
737 			return (0);
738 		if (dp->d_ino == 0)
739 			continue;
740 		if (dp->d_name[0] != '.')
741 			return (0);
742 		if (dp->d_namlen == 1 ||
743 		    (dp->d_namlen == 2 && dp->d_name[1] == '.'))
744 			continue;
745 		return (0);
746 	}
747 	return (1);
748 }
749