xref: /original-bsd/sys/ufs/ffs/ufs_lookup.c (revision 77936e01)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  *	@(#)ufs_lookup.c	7.21 (Berkeley) 05/02/90
18  */
19 
20 #include "param.h"
21 #include "user.h"
22 #include "buf.h"
23 #include "file.h"
24 #include "vnode.h"
25 #include "../ufs/quota.h"
26 #include "../ufs/inode.h"
27 #include "../ufs/fs.h"
28 
29 struct	nchstats nchstats;
30 int	dirchk = 1;
31 
32 /*
33  * Convert a component of a pathname into a pointer to a locked inode.
34  * This is a very central and rather complicated routine.
35  * If the file system is not maintained in a strict tree hierarchy,
36  * this can result in a deadlock situation (see comments in code below).
37  *
38  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
39  * whether the name is to be looked up, created, renamed, or deleted.
40  * When CREATE, RENAME, or DELETE is specified, information usable in
41  * creating, renaming, or deleting a directory entry may be calculated.
42  * If flag has LOCKPARENT or'ed into it and the target of the pathname
43  * exists, lookup returns both the target and its parent directory locked.
44  * When creating or renaming and LOCKPARENT is specified, the target may
45  * not be ".".  When deleting and LOCKPARENT is specified, the target may
46  * be "."., but the caller must check to ensure it does an vrele and iput
47  * instead of two iputs.
48  *
49  * Overall outline of ufs_lookup:
50  *
51  *	check accessibility of directory
52  *	look for name in cache, if found, then if at end of path
53  *	  and deleting or creating, drop it, else return name
54  *	search for name in directory, to found or notfound
55  * notfound:
56  *	if creating, return locked directory, leaving info on available slots
57  *	else return error
58  * found:
59  *	if at end of path and deleting, return information to allow delete
60  *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
61  *	  inode and return info to allow rewrite
62  *	if not at end, add name to cache; if at end and neither creating
63  *	  nor deleting, add name to cache
64  *
65  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
66  */
67 ufs_lookup(vdp, ndp)
68 	register struct vnode *vdp;
69 	register struct nameidata *ndp;
70 {
71 	register struct inode *dp;	/* the directory we are searching */
72 	register struct fs *fs;		/* file system that directory is in */
73 	struct buf *bp = 0;		/* a buffer of directory entries */
74 	register struct direct *ep;	/* the current directory entry */
75 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
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 	int numdirpasses;		/* strategy for directory search */
82 	int endsearch;			/* offset to end directory search */
83 	int prevoff;			/* ndp->ni_offset of previous entry */
84 	struct inode *pdp;		/* saved dp during symlink work */
85 	struct inode *tdp;		/* returned by iget */
86 	off_t enduseful;		/* pointer past last used dir slot */
87 	int flag;			/* LOOKUP, CREATE, RENAME, or DELETE */
88 	int lockparent;			/* 1 => lockparent flag is set */
89 	int wantparent;			/* 1 => wantparent or lockparent flag */
90 	int error;
91 
92 	ndp->ni_dvp = vdp;
93 	ndp->ni_vp = NULL;
94 	dp = VTOI(vdp);
95 	fs = dp->i_fs;
96 	lockparent = ndp->ni_nameiop & LOCKPARENT;
97 	flag = ndp->ni_nameiop & OPFLAG;
98 	wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
99 
100 	/*
101 	 * Check accessiblity of directory.
102 	 */
103 	if ((dp->i_mode&IFMT) != IFDIR)
104 		return (ENOTDIR);
105 	if (error = ufs_access(vdp, VEXEC, ndp->ni_cred))
106 		return (error);
107 
108 	/*
109 	 * We now have a segment name to search for, and a directory to search.
110 	 *
111 	 * Before tediously performing a linear scan of the directory,
112 	 * check the name cache to see if the directory/name pair
113 	 * we are looking for is known already.
114 	 */
115 	if (error = cache_lookup(ndp)) {
116 		int vpid;	/* capability number of vnode */
117 
118 		if (error == ENOENT)
119 			return (error);
120 		if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
121 			panic("ufs_lookup: .. through root");
122 		/*
123 		 * Get the next vnode in the path.
124 		 * See comment below starting `Step through' for
125 		 * an explaination of the locking protocol.
126 		 */
127 		pdp = dp;
128 		dp = VTOI(ndp->ni_vp);
129 		vdp = ndp->ni_vp;
130 		vpid = vdp->v_id;
131 		if (pdp == dp) {
132 			VREF(vdp);
133 			error = 0;
134 		} else if (ndp->ni_isdotdot) {
135 			IUNLOCK(pdp);
136 			error = vget(vdp);
137 		} else {
138 			error = vget(vdp);
139 			IUNLOCK(pdp);
140 		}
141 		/*
142 		 * Check that the capability number did not change
143 		 * while we were waiting for the lock.
144 		 */
145 		if (!error) {
146 			if (vpid == vdp->v_id)
147 				return (0);
148 			else
149 				iput(dp);
150 		}
151 		ILOCK(pdp);
152 		dp = pdp;
153 		vdp = ITOV(dp);
154 		ndp->ni_vp = NULL;
155 	}
156 
157 	/*
158 	 * Suppress search for slots unless creating
159 	 * file and at end of pathname, in which case
160 	 * we watch for a place to put the new file in
161 	 * case it doesn't already exist.
162 	 */
163 	slotstatus = FOUND;
164 	if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) {
165 		slotstatus = NONE;
166 		slotfreespace = 0;
167 		slotneeded = DIRSIZ(&ndp->ni_dent);
168 	}
169 
170 	/*
171 	 * If there is cached information on a previous search of
172 	 * this directory, pick up where we last left off.
173 	 * We cache only lookups as these are the most common
174 	 * and have the greatest payoff. Caching CREATE has little
175 	 * benefit as it usually must search the entire directory
176 	 * to determine that the entry does not exist. Caching the
177 	 * location of the last DELETE or RENAME has not reduced
178 	 * profiling time and hence has been removed in the interest
179 	 * of simplicity.
180 	 */
181 	if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
182 		ndp->ni_offset = 0;
183 		numdirpasses = 1;
184 	} else {
185 		ndp->ni_offset = dp->i_diroff;
186 		entryoffsetinblock = blkoff(fs, ndp->ni_offset);
187 		if (entryoffsetinblock != 0) {
188 			error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
189 			if (error)
190 				return (error);
191 		}
192 		numdirpasses = 2;
193 		nchstats.ncs_2passes++;
194 	}
195 	endsearch = roundup(dp->i_size, DIRBLKSIZ);
196 	enduseful = 0;
197 
198 searchloop:
199 	while (ndp->ni_offset < endsearch) {
200 		/*
201 		 * If offset is on a block boundary,
202 		 * read the next directory block.
203 		 * Release previous if it exists.
204 		 */
205 		if (blkoff(fs, ndp->ni_offset) == 0) {
206 			if (bp != NULL)
207 				brelse(bp);
208 			error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
209 			if (error)
210 				return (error);
211 			entryoffsetinblock = 0;
212 		}
213 		/*
214 		 * If still looking for a slot, and at a DIRBLKSIZE
215 		 * boundary, have to start looking for free space again.
216 		 */
217 		if (slotstatus == NONE &&
218 		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
219 			slotoffset = -1;
220 			slotfreespace = 0;
221 		}
222 		/*
223 		 * Get pointer to next entry.
224 		 * Full validation checks are slow, so we only check
225 		 * enough to insure forward progress through the
226 		 * directory. Complete checks can be run by patching
227 		 * "dirchk" to be true.
228 		 */
229 		ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
230 		if (ep->d_reclen == 0 ||
231 		    dirchk && dirbadentry(ep, entryoffsetinblock)) {
232 			int i;
233 
234 			dirbad(dp, ndp->ni_offset, "mangled entry");
235 			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
236 			ndp->ni_offset += i;
237 			entryoffsetinblock += i;
238 			continue;
239 		}
240 
241 		/*
242 		 * If an appropriate sized slot has not yet been found,
243 		 * check to see if one is available. Also accumulate space
244 		 * in the current block so that we can determine if
245 		 * compaction is viable.
246 		 */
247 		if (slotstatus != FOUND) {
248 			int size = ep->d_reclen;
249 
250 			if (ep->d_ino != 0)
251 				size -= DIRSIZ(ep);
252 			if (size > 0) {
253 				if (size >= slotneeded) {
254 					slotstatus = FOUND;
255 					slotoffset = ndp->ni_offset;
256 					slotsize = ep->d_reclen;
257 				} else if (slotstatus == NONE) {
258 					slotfreespace += size;
259 					if (slotoffset == -1)
260 						slotoffset = ndp->ni_offset;
261 					if (slotfreespace >= slotneeded) {
262 						slotstatus = COMPACT;
263 						slotsize = ndp->ni_offset +
264 						      ep->d_reclen - slotoffset;
265 					}
266 				}
267 			}
268 		}
269 
270 		/*
271 		 * Check for a name match.
272 		 */
273 		if (ep->d_ino) {
274 			if (ep->d_namlen == ndp->ni_dent.d_namlen &&
275 			    !bcmp(ndp->ni_ptr, ep->d_name,
276 				(unsigned)ep->d_namlen)) {
277 				/*
278 				 * Save directory entry's inode number and
279 				 * reclen in ndp->ni_dent, and release
280 				 * directory buffer.
281 				 */
282 				ndp->ni_dent.d_ino = ep->d_ino;
283 				ndp->ni_dent.d_reclen = ep->d_reclen;
284 				brelse(bp);
285 				goto found;
286 			}
287 		}
288 		prevoff = ndp->ni_offset;
289 		ndp->ni_offset += ep->d_reclen;
290 		entryoffsetinblock += ep->d_reclen;
291 		if (ep->d_ino)
292 			enduseful = ndp->ni_offset;
293 	}
294 /* notfound: */
295 	/*
296 	 * If we started in the middle of the directory and failed
297 	 * to find our target, we must check the beginning as well.
298 	 */
299 	if (numdirpasses == 2) {
300 		numdirpasses--;
301 		ndp->ni_offset = 0;
302 		endsearch = dp->i_diroff;
303 		goto searchloop;
304 	}
305 	if (bp != NULL)
306 		brelse(bp);
307 	/*
308 	 * If creating, and at end of pathname and current
309 	 * directory has not been removed, then can consider
310 	 * allowing file to be created.
311 	 */
312 	if ((flag == CREATE || flag == RENAME) &&
313 	    *ndp->ni_next == 0 && dp->i_nlink != 0) {
314 		/*
315 		 * Access for write is interpreted as allowing
316 		 * creation of files in the directory.
317 		 */
318 		if (error = ufs_access(vdp, VWRITE, ndp->ni_cred))
319 			return (error);
320 		/*
321 		 * Return an indication of where the new directory
322 		 * entry should be put.  If we didn't find a slot,
323 		 * then set ndp->ni_count to 0 indicating that the new
324 		 * slot belongs at the end of the directory. If we found
325 		 * a slot, then the new entry can be put in the range
326 		 * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count)
327 		 */
328 		if (slotstatus == NONE) {
329 			ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ);
330 			ndp->ni_count = 0;
331 			enduseful = ndp->ni_offset;
332 		} else {
333 			ndp->ni_offset = slotoffset;
334 			ndp->ni_count = slotsize;
335 			if (enduseful < slotoffset + slotsize)
336 				enduseful = slotoffset + slotsize;
337 		}
338 		ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ);
339 		dp->i_flag |= IUPD|ICHG;
340 		/*
341 		 * We return with the directory locked, so that
342 		 * the parameters we set up above will still be
343 		 * valid if we actually decide to do a direnter().
344 		 * We return ni_vp == NULL to indicate that the entry
345 		 * does not currently exist; we leave a pointer to
346 		 * the (locked) directory inode in ndp->ni_dvp.
347 		 *
348 		 * NB - if the directory is unlocked, then this
349 		 * information cannot be used.
350 		 */
351 		if (!lockparent)
352 			IUNLOCK(dp);
353 	}
354 	/*
355 	 * Insert name into cache (as non-existent) if appropriate.
356 	 */
357 	if (ndp->ni_makeentry)
358 		cache_enter(ndp);
359 	return (ENOENT);
360 
361 found:
362 	if (numdirpasses == 2)
363 		nchstats.ncs_pass2++;
364 	/*
365 	 * Check that directory length properly reflects presence
366 	 * of this entry.
367 	 */
368 	if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
369 		dirbad(dp, ndp->ni_offset, "i_size too small");
370 		dp->i_size = entryoffsetinblock + DIRSIZ(ep);
371 		dp->i_flag |= IUPD|ICHG;
372 	}
373 
374 	/*
375 	 * Found component in pathname.
376 	 * If the final component of path name, save information
377 	 * in the cache as to where the entry was found.
378 	 */
379 	if (*ndp->ni_next == '\0' && flag == LOOKUP)
380 		dp->i_diroff = ndp->ni_offset &~ (DIRBLKSIZ - 1);
381 
382 	/*
383 	 * If deleting, and at end of pathname, return
384 	 * parameters which can be used to remove file.
385 	 * If the wantparent flag isn't set, we return only
386 	 * the directory (in ndp->ni_dvp), otherwise we go
387 	 * on and lock the inode, being careful with ".".
388 	 */
389 	if (flag == DELETE && *ndp->ni_next == 0) {
390 		/*
391 		 * Write access to directory required to delete files.
392 		 */
393 		if (error = ufs_access(vdp, VWRITE, ndp->ni_cred))
394 			return (error);
395 		/*
396 		 * Return pointer to current entry in ndp->ni_offset,
397 		 * and distance past previous entry (if there
398 		 * is a previous entry in this block) in ndp->ni_count.
399 		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
400 		 */
401 		if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0)
402 			ndp->ni_count = 0;
403 		else
404 			ndp->ni_count = ndp->ni_offset - prevoff;
405 		if (dp->i_number == ndp->ni_dent.d_ino) {
406 			VREF(vdp);
407 			ndp->ni_vp = vdp;
408 			return (0);
409 		}
410 		if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
411 			return (error);
412 		/*
413 		 * If directory is "sticky", then user must own
414 		 * the directory, or the file in it, else she
415 		 * may not delete it (unless she's root). This
416 		 * implements append-only directories.
417 		 */
418 		if ((dp->i_mode & ISVTX) &&
419 		    ndp->ni_cred->cr_uid != 0 &&
420 		    ndp->ni_cred->cr_uid != dp->i_uid &&
421 		    tdp->i_uid != ndp->ni_cred->cr_uid) {
422 			iput(tdp);
423 			return (EPERM);
424 		}
425 		ndp->ni_vp = ITOV(tdp);
426 		if (!lockparent)
427 			IUNLOCK(dp);
428 		return (0);
429 	}
430 
431 	/*
432 	 * If rewriting (RENAME), return the inode and the
433 	 * information required to rewrite the present directory
434 	 * Must get inode of directory entry to verify it's a
435 	 * regular file, or empty directory.
436 	 */
437 	if (flag == RENAME && wantparent && *ndp->ni_next == 0) {
438 		if (error = ufs_access(vdp, VWRITE, ndp->ni_cred))
439 			return (error);
440 		/*
441 		 * Careful about locking second inode.
442 		 * This can only occur if the target is ".".
443 		 */
444 		if (dp->i_number == ndp->ni_dent.d_ino)
445 			return (EISDIR);
446 		if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
447 			return (error);
448 		ndp->ni_vp = ITOV(tdp);
449 		if (!lockparent)
450 			IUNLOCK(dp);
451 		return (0);
452 	}
453 
454 	/*
455 	 * Step through the translation in the name.  We do not `iput' the
456 	 * directory because we may need it again if a symbolic link
457 	 * is relative to the current directory.  Instead we save it
458 	 * unlocked as "pdp".  We must get the target inode before unlocking
459 	 * the directory to insure that the inode will not be removed
460 	 * before we get it.  We prevent deadlock by always fetching
461 	 * inodes from the root, moving down the directory tree. Thus
462 	 * when following backward pointers ".." we must unlock the
463 	 * parent directory before getting the requested directory.
464 	 * There is a potential race condition here if both the current
465 	 * and parent directories are removed before the `iget' for the
466 	 * inode associated with ".." returns.  We hope that this occurs
467 	 * infrequently since we cannot avoid this race condition without
468 	 * implementing a sophisticated deadlock detection algorithm.
469 	 * Note also that this simple deadlock detection scheme will not
470 	 * work if the file system has any hard links other than ".."
471 	 * that point backwards in the directory structure.
472 	 */
473 	pdp = dp;
474 	if (ndp->ni_isdotdot) {
475 		IUNLOCK(pdp);	/* race to get the inode */
476 		if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) {
477 			ILOCK(pdp);
478 			return (error);
479 		}
480 		if (lockparent && *ndp->ni_next == '\0')
481 			ILOCK(pdp);
482 		ndp->ni_vp = ITOV(tdp);
483 	} else if (dp->i_number == ndp->ni_dent.d_ino) {
484 		VREF(vdp);	/* we want ourself, ie "." */
485 		ndp->ni_vp = vdp;
486 	} else {
487 		if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
488 			return (error);
489 		if (!lockparent || *ndp->ni_next != '\0')
490 			IUNLOCK(pdp);
491 		ndp->ni_vp = ITOV(tdp);
492 	}
493 
494 	/*
495 	 * Insert name into cache if appropriate.
496 	 */
497 	if (ndp->ni_makeentry)
498 		cache_enter(ndp);
499 	return (0);
500 }
501 
502 
503 dirbad(ip, offset, how)
504 	struct inode *ip;
505 	off_t offset;
506 	char *how;
507 {
508 
509 	printf("%s: bad dir ino %d at offset %d: %s\n",
510 	    ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
511 	panic("bad dir");
512 }
513 
514 /*
515  * Do consistency checking on a directory entry:
516  *	record length must be multiple of 4
517  *	entry must fit in rest of its DIRBLKSIZ block
518  *	record must be large enough to contain entry
519  *	name is not longer than MAXNAMLEN
520  *	name must be as long as advertised, and null terminated
521  */
522 dirbadentry(ep, entryoffsetinblock)
523 	register struct direct *ep;
524 	int entryoffsetinblock;
525 {
526 	register int i;
527 
528 	if ((ep->d_reclen & 0x3) != 0 ||
529 	    ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
530 	    ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
531 		return (1);
532 	for (i = 0; i < ep->d_namlen; i++)
533 		if (ep->d_name[i] == '\0')
534 			return (1);
535 	return (ep->d_name[i]);
536 }
537 
538 /*
539  * Write a directory entry after a call to namei, using the parameters
540  * which it left in nameidata.  The argument ip is the inode which the
541  * new directory entry will refer to.  The nameidata field ndp->ni_dvp
542  * is a pointer to the directory to be written, which was left locked by
543  * namei.  Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate
544  * how the space for the new entry is to be gotten.
545  */
546 direnter(ip, ndp)
547 	struct inode *ip;
548 	register struct nameidata *ndp;
549 {
550 	register struct direct *ep, *nep;
551 	register struct inode *dp = VTOI(ndp->ni_dvp);
552 	struct buf *bp;
553 	int loc, spacefree, error = 0;
554 	u_int dsize;
555 	int newentrysize;
556 	char *dirbuf;
557 
558 	ndp->ni_dent.d_ino = ip->i_number;
559 	newentrysize = DIRSIZ(&ndp->ni_dent);
560 	if (ndp->ni_count == 0) {
561 		/*
562 		 * If ndp->ni_count is 0, then namei could find no space in the
563 		 * directory. In this case ndp->ni_offset will be on a directory
564 		 * block boundary and we will write the new entry into a fresh
565 		 * block.
566 		 */
567 		if (ndp->ni_offset&(DIRBLKSIZ-1))
568 			panic("wdir: newblk");
569 		ndp->ni_dent.d_reclen = DIRBLKSIZ;
570 		ndp->ni_count = newentrysize;
571 		ndp->ni_resid = newentrysize;
572 		ndp->ni_base = (caddr_t)&ndp->ni_dent;
573 		ndp->ni_uioseg = UIO_SYSSPACE;
574 		error =
575 		    ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred);
576 		if (DIRBLKSIZ > dp->i_fs->fs_fsize)
577 			panic("wdir: blksize"); /* XXX - should grow w/balloc */
578 		else
579 			dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
580 		iput(dp);
581 		return (error);
582 	}
583 
584 	/*
585 	 * If ndp->ni_count is non-zero, then namei found space for the new
586 	 * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count.
587 	 * in the directory.  To use this space, we may have to compact
588 	 * the entries located there, by copying them together towards
589 	 * the beginning of the block, leaving the free space in
590 	 * one usable chunk at the end.
591 	 */
592 
593 	/*
594 	 * Increase size of directory if entry eats into new space.
595 	 * This should never push the size past a new multiple of
596 	 * DIRBLKSIZE.
597 	 *
598 	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
599 	 */
600 	if (ndp->ni_offset + ndp->ni_count > dp->i_size)
601 		dp->i_size = ndp->ni_offset + ndp->ni_count;
602 	/*
603 	 * Get the block containing the space for the new directory entry.
604 	 */
605 	if (error = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf, &bp)) {
606 		iput(dp);
607 		return (error);
608 	}
609 	/*
610 	 * Find space for the new entry.  In the simple case, the
611 	 * entry at offset base will have the space.  If it does
612 	 * not, then namei arranged that compacting the region
613 	 * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space.
614 	 */
615 	ep = (struct direct *)dirbuf;
616 	dsize = DIRSIZ(ep);
617 	spacefree = ep->d_reclen - dsize;
618 	for (loc = ep->d_reclen; loc < ndp->ni_count; ) {
619 		nep = (struct direct *)(dirbuf + loc);
620 		if (ep->d_ino) {
621 			/* trim the existing slot */
622 			ep->d_reclen = dsize;
623 			ep = (struct direct *)((char *)ep + dsize);
624 		} else {
625 			/* overwrite; nothing there; header is ours */
626 			spacefree += dsize;
627 		}
628 		dsize = DIRSIZ(nep);
629 		spacefree += nep->d_reclen - dsize;
630 		loc += nep->d_reclen;
631 		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
632 	}
633 	/*
634 	 * Update the pointer fields in the previous entry (if any),
635 	 * copy in the new entry, and write out the block.
636 	 */
637 	if (ep->d_ino == 0) {
638 		if (spacefree + dsize < newentrysize)
639 			panic("wdir: compact1");
640 		ndp->ni_dent.d_reclen = spacefree + dsize;
641 	} else {
642 		if (spacefree < newentrysize)
643 			panic("wdir: compact2");
644 		ndp->ni_dent.d_reclen = spacefree;
645 		ep->d_reclen = dsize;
646 		ep = (struct direct *)((char *)ep + dsize);
647 	}
648 	bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize);
649 	error = bwrite(bp);
650 	dp->i_flag |= IUPD|ICHG;
651 	if (!error && ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
652 		error = itrunc(dp, (u_long)ndp->ni_endoff, IO_SYNC);
653 	iput(dp);
654 	return (error);
655 }
656 
657 /*
658  * Remove a directory entry after a call to namei, using
659  * the parameters which it left in nameidata. The entry
660  * ni_offset contains the offset into the directory of the
661  * entry to be eliminated.  The ni_count field contains the
662  * size of the previous record in the directory.  If this
663  * is 0, the first entry is being deleted, so we need only
664  * zero the inode number to mark the entry as free.  If the
665  * entry isn't the first in the directory, we must reclaim
666  * the space of the now empty record by adding the record size
667  * to the size of the previous entry.
668  */
669 dirremove(ndp)
670 	register struct nameidata *ndp;
671 {
672 	register struct inode *dp = VTOI(ndp->ni_dvp);
673 	struct direct *ep;
674 	struct buf *bp;
675 	int error;
676 
677 	if (ndp->ni_count == 0) {
678 		/*
679 		 * First entry in block: set d_ino to zero.
680 		 */
681 		ndp->ni_dent.d_ino = 0;
682 		ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent);
683 		ndp->ni_base = (caddr_t)&ndp->ni_dent;
684 		ndp->ni_uioseg = UIO_SYSSPACE;
685 		error =
686 		    ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred);
687 	} else {
688 		/*
689 		 * Collapse new free space into previous entry.
690 		 */
691 		if (error = blkatoff(dp, ndp->ni_offset - ndp->ni_count,
692 		    (char **)&ep, &bp)) {
693 			return (error);
694 		}
695 		ep->d_reclen += ndp->ni_dent.d_reclen;
696 		error = bwrite(bp);
697 		dp->i_flag |= IUPD|ICHG;
698 	}
699 	return (error);
700 }
701 
702 /*
703  * Rewrite an existing directory entry to point at the inode
704  * supplied.  The parameters describing the directory entry are
705  * set up by a call to namei.
706  */
707 dirrewrite(dp, ip, ndp)
708 	struct inode *dp, *ip;
709 	struct nameidata *ndp;
710 {
711 
712 	ndp->ni_dent.d_ino = ip->i_number;
713 	ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent);
714 	ndp->ni_base = (caddr_t)&ndp->ni_dent;
715 	ndp->ni_uioseg = UIO_SYSSPACE;
716 	return (ufs_write(ITOV(dp), &ndp->ni_uio, IO_SYNC, ndp->ni_cred));
717 }
718 
719 /*
720  * Return buffer with contents of block "offset"
721  * from the beginning of directory "ip".  If "res"
722  * is non-zero, fill it in with a pointer to the
723  * remaining space in the directory.
724  */
725 blkatoff(ip, offset, res, bpp)
726 	struct inode *ip;
727 	off_t offset;
728 	char **res;
729 	struct buf **bpp;
730 {
731 	register struct fs *fs = ip->i_fs;
732 	daddr_t lbn = lblkno(fs, offset);
733 	int bsize = blksize(fs, ip, lbn);
734 	struct buf *bp;
735 	daddr_t bn;
736 	int error;
737 
738 	*bpp = 0;
739 	if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) {
740 		brelse(bp);
741 		return (error);
742 	}
743 	if (res)
744 		*res = bp->b_un.b_addr + blkoff(fs, offset);
745 	*bpp = bp;
746 	return (0);
747 }
748 
749 /*
750  * Check if a directory is empty or not.
751  * Inode supplied must be locked.
752  *
753  * Using a struct dirtemplate here is not precisely
754  * what we want, but better than using a struct direct.
755  *
756  * NB: does not handle corrupted directories.
757  */
758 dirempty(ip, parentino, cred)
759 	register struct inode *ip;
760 	ino_t parentino;
761 	struct ucred *cred;
762 {
763 	register off_t off;
764 	struct dirtemplate dbuf;
765 	register struct direct *dp = (struct direct *)&dbuf;
766 	int error, count;
767 #define	MINDIRSIZ (sizeof (struct dirtemplate) / 2)
768 
769 	for (off = 0; off < ip->i_size; off += dp->d_reclen) {
770 		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
771 		    off, UIO_SYSSPACE, IO_NODELOCKED, cred, &count);
772 		/*
773 		 * Since we read MINDIRSIZ, residual must
774 		 * be 0 unless we're at end of file.
775 		 */
776 		if (error || count != 0)
777 			return (0);
778 		/* avoid infinite loops */
779 		if (dp->d_reclen == 0)
780 			return (0);
781 		/* skip empty entries */
782 		if (dp->d_ino == 0)
783 			continue;
784 		/* accept only "." and ".." */
785 		if (dp->d_namlen > 2)
786 			return (0);
787 		if (dp->d_name[0] != '.')
788 			return (0);
789 		/*
790 		 * At this point d_namlen must be 1 or 2.
791 		 * 1 implies ".", 2 implies ".." if second
792 		 * char is also "."
793 		 */
794 		if (dp->d_namlen == 1)
795 			continue;
796 		if (dp->d_name[1] == '.' && dp->d_ino == parentino)
797 			continue;
798 		return (0);
799 	}
800 	return (1);
801 }
802 
803 /*
804  * Check if source directory is in the path of the target directory.
805  * Target is supplied locked, source is unlocked.
806  * The target is always iput() before returning.
807  */
808 checkpath(source, target, cred)
809 	struct inode *source, *target;
810 	struct ucred *cred;
811 {
812 	struct dirtemplate dirbuf;
813 	struct inode *ip;
814 	int error = 0;
815 
816 	ip = target;
817 	if (ip->i_number == source->i_number) {
818 		error = EEXIST;
819 		goto out;
820 	}
821 	if (ip->i_number == ROOTINO)
822 		goto out;
823 
824 	for (;;) {
825 		if ((ip->i_mode&IFMT) != IFDIR) {
826 			error = ENOTDIR;
827 			break;
828 		}
829 		error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)&dirbuf,
830 			sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
831 			IO_NODELOCKED, cred, (int *)0);
832 		if (error != 0)
833 			break;
834 		if (dirbuf.dotdot_namlen != 2 ||
835 		    dirbuf.dotdot_name[0] != '.' ||
836 		    dirbuf.dotdot_name[1] != '.') {
837 			error = ENOTDIR;
838 			break;
839 		}
840 		if (dirbuf.dotdot_ino == source->i_number) {
841 			error = EINVAL;
842 			break;
843 		}
844 		if (dirbuf.dotdot_ino == ROOTINO)
845 			break;
846 		iput(ip);
847 		if (error = iget(ip, dirbuf.dotdot_ino, &ip))
848 			break;
849 	}
850 
851 out:
852 	if (error == ENOTDIR)
853 		printf("checkpath: .. not a directory\n");
854 	if (ip != NULL)
855 		iput(ip);
856 	return (error);
857 }
858