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