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