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