xref: /netbsd-src/sbin/fsck_ffs/dir.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: dir.c,v 1.36 2003/04/02 10:39:25 fvdl Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1986, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)dir.c	8.8 (Berkeley) 4/28/95";
40 #else
41 __RCSID("$NetBSD: dir.c,v 1.36 2003/04/02 10:39:25 fvdl Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/time.h>
47 
48 #include <ufs/ufs/dinode.h>
49 #include <ufs/ufs/dir.h>
50 #include <ufs/ffs/fs.h>
51 #include <ufs/ffs/ffs_extern.h>
52 
53 #include <err.h>
54 #include <stdio.h>
55 #include <string.h>
56 
57 #include "fsck.h"
58 #include "fsutil.h"
59 #include "extern.h"
60 
61 char	*lfname = "lost+found";
62 int	lfmode = 01700;
63 ino_t	lfdir;
64 struct	dirtemplate emptydir = { 0, DIRBLKSIZ };
65 struct	dirtemplate dirhead = {
66 	0, 12, DT_DIR, 1, ".",
67 	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
68 };
69 struct	odirtemplate odirhead = {
70 	0, 12, 1, ".",
71 	0, DIRBLKSIZ - 12, 2, ".."
72 };
73 
74 static int chgino __P((struct  inodesc *));
75 static int dircheck __P((struct inodesc *, struct direct *));
76 static int expanddir __P((union dinode *, char *));
77 static void freedir __P((ino_t, ino_t));
78 static struct direct *fsck_readdir __P((struct inodesc *));
79 static struct bufarea *getdirblk __P((daddr_t, long));
80 static int lftempname __P((char *, ino_t));
81 static int mkentry __P((struct inodesc *));
82 void reparent __P((ino_t, ino_t));
83 
84 /*
85  * Propagate connected state through the tree.
86  */
87 void
88 propagate(inumber)
89 	ino_t inumber;
90 {
91 	struct inoinfo *inp;
92 
93 	inp = getinoinfo(inumber);
94 
95 	for (;;) {
96 		inoinfo(inp->i_number)->ino_state = DMARK;
97 		if (inp->i_child &&
98 		    inoinfo(inp->i_child->i_number)->ino_state != DMARK)
99 			inp = inp->i_child;
100 		else if (inp->i_number == inumber)
101 			break;
102 		else if (inp->i_sibling)
103 			inp = inp->i_sibling;
104 		else
105 			inp = inp->i_parentp;
106 	}
107 
108 	for (;;) {
109 		inoinfo(inp->i_number)->ino_state = DFOUND;
110 		if (inp->i_child &&
111 		    inoinfo(inp->i_child->i_number)->ino_state != DFOUND)
112 			inp = inp->i_child;
113 		else if (inp->i_number == inumber)
114 			break;
115 		else if (inp->i_sibling)
116 			inp = inp->i_sibling;
117 		else
118 			inp = inp->i_parentp;
119 	}
120 }
121 
122 void
123 reparent(inumber, parent)
124 	ino_t inumber, parent;
125 {
126 	struct inoinfo *inp, *pinp;
127 
128 	inp = getinoinfo(inumber);
129 	inp->i_parent = inp->i_dotdot = parent;
130 	pinp = getinoinfo(parent);
131 	inp->i_parentp = pinp;
132 	inp->i_sibling = pinp->i_child;
133 	pinp->i_child = inp;
134 	propagate(inumber);
135 }
136 
137 /*
138  * Scan each entry in a directory block.
139  */
140 int
141 dirscan(idesc)
142 	struct inodesc *idesc;
143 {
144 	struct direct *dp;
145 	struct bufarea *bp;
146 	int dsize, n;
147 	long blksiz;
148 #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
149 	char dbuf[DIRBLKSIZ];
150 #else
151 	char dbuf[APPLEUFS_DIRBLKSIZ];
152 #endif
153 
154 	if (idesc->id_type != DATA)
155 		errx(EEXIT, "wrong type to dirscan %d", idesc->id_type);
156 	if (idesc->id_entryno == 0 &&
157 	    (idesc->id_filesize & (dirblksiz - 1)) != 0)
158 		idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
159 	blksiz = idesc->id_numfrags * sblock->fs_fsize;
160 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
161 		idesc->id_filesize -= blksiz;
162 		return (SKIP);
163 	}
164 
165 	/*
166 	 * If we are are swapping byte order in directory entries, just swap
167 	 * this block and return.
168 	 */
169 	if (do_dirswap) {
170 		int off;
171 		bp = getdirblk(idesc->id_blkno, blksiz);
172 		for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
173 			dp = (struct direct *)(bp->b_un.b_buf + off);
174 			dp->d_ino = bswap32(dp->d_ino);
175 			dp->d_reclen = bswap16(dp->d_reclen);
176 			if (!newinofmt) {
177 				u_int8_t tmp = dp->d_namlen;
178 				dp->d_namlen = dp->d_type;
179 				dp->d_type = tmp;
180 			}
181 			if (dp->d_reclen == 0)
182 				break;
183 		}
184 		dirty(bp);
185 		idesc->id_filesize -= blksiz;
186 		return (idesc->id_filesize > 0 ? KEEPON : STOP);
187 	}
188 
189 	idesc->id_loc = 0;
190 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
191 		dsize = iswap16(dp->d_reclen);
192 		if (dsize > sizeof dbuf)
193 			dsize = sizeof dbuf;
194 		memmove(dbuf, dp, (size_t)dsize);
195 #		if (BYTE_ORDER == LITTLE_ENDIAN)
196 			if (!newinofmt && !needswap) {
197 #		else
198 			if (!newinofmt && needswap) {
199 #		endif
200 				struct direct *tdp = (struct direct *)dbuf;
201 				u_char tmp;
202 
203 				tmp = tdp->d_namlen;
204 				tdp->d_namlen = tdp->d_type;
205 				tdp->d_type = tmp;
206 			}
207 		idesc->id_dirp = (struct direct *)dbuf;
208 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
209 #			if (BYTE_ORDER == LITTLE_ENDIAN)
210 				if (!newinofmt && !doinglevel2 && !needswap) {
211 #			else
212 				if (!newinofmt && !doinglevel2 && needswap) {
213 #			endif
214 					struct direct *tdp;
215 					u_char tmp;
216 
217 					tdp = (struct direct *)dbuf;
218 					tmp = tdp->d_namlen;
219 					tdp->d_namlen = tdp->d_type;
220 					tdp->d_type = tmp;
221 				}
222 			bp = getdirblk(idesc->id_blkno, blksiz);
223 			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
224 			    (size_t)dsize);
225 			dirty(bp);
226 			sbdirty();
227 		}
228 		if (n & STOP)
229 			return (n);
230 	}
231 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
232 }
233 
234 /*
235  * get next entry in a directory.
236  */
237 static struct direct *
238 fsck_readdir(idesc)
239 	struct inodesc *idesc;
240 {
241 	struct direct *dp, *ndp;
242 	struct bufarea *bp;
243 	long size, blksiz, fix, dploc;
244 
245 	blksiz = idesc->id_numfrags * sblock->fs_fsize;
246 	bp = getdirblk(idesc->id_blkno, blksiz);
247 	if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
248 	    idesc->id_loc < blksiz) {
249 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
250 		if (dircheck(idesc, dp))
251 			goto dpok;
252 		if (idesc->id_fix == IGNORE)
253 			return (0);
254 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
255 		bp = getdirblk(idesc->id_blkno, blksiz);
256 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
257 		dp->d_reclen = iswap16(dirblksiz);
258 		dp->d_ino = 0;
259 		dp->d_type = 0;
260 		dp->d_namlen = 0;
261 		dp->d_name[0] = '\0';
262 		if (fix)
263 			dirty(bp);
264 		else
265 			markclean=  0;
266 		idesc->id_loc += dirblksiz;
267 		idesc->id_filesize -= dirblksiz;
268 		return (dp);
269 	}
270 dpok:
271 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
272 		return NULL;
273 	dploc = idesc->id_loc;
274 	dp = (struct direct *)(bp->b_un.b_buf + dploc);
275 	idesc->id_loc += iswap16(dp->d_reclen);
276 	idesc->id_filesize -= iswap16(dp->d_reclen);
277 	if ((idesc->id_loc % dirblksiz) == 0)
278 		return (dp);
279 	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
280 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
281 	    dircheck(idesc, ndp) == 0) {
282 		size = dirblksiz - (idesc->id_loc % dirblksiz);
283 		idesc->id_loc += size;
284 		idesc->id_filesize -= size;
285 		if (idesc->id_fix == IGNORE)
286 			return (0);
287 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
288 		bp = getdirblk(idesc->id_blkno, blksiz);
289 		dp = (struct direct *)(bp->b_un.b_buf + dploc);
290 		dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
291 		if (fix)
292 			dirty(bp);
293 		else
294 			markclean=  0;
295 	}
296 	return (dp);
297 }
298 
299 /*
300  * Verify that a directory entry is valid.
301  * This is a superset of the checks made in the kernel.
302  */
303 static int
304 dircheck(idesc, dp)
305 	struct inodesc *idesc;
306 	struct direct *dp;
307 {
308 	int size;
309 	char *cp;
310 	u_char namlen, type;
311 	int spaceleft;
312 
313 	spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
314 	if (iswap32(dp->d_ino) >= maxino ||
315 	    dp->d_reclen == 0 ||
316 	    iswap16(dp->d_reclen) > spaceleft ||
317 	    (iswap16(dp->d_reclen) & 0x3) != 0)
318 		return (0);
319 	if (dp->d_ino == 0)
320 		return (1);
321 	size = DIRSIZ(!newinofmt, dp, needswap);
322 #	if (BYTE_ORDER == LITTLE_ENDIAN)
323 		if (!newinofmt && !needswap) {
324 #	else
325 		if (!newinofmt && needswap) {
326 #	endif
327 			type = dp->d_namlen;
328 			namlen = dp->d_type;
329 		} else {
330 			namlen = dp->d_namlen;
331 			type = dp->d_type;
332 		}
333 	if (iswap16(dp->d_reclen) < size ||
334 	    idesc->id_filesize < size ||
335 	    /* namlen > MAXNAMLEN || */
336 	    type > 15)
337 		return (0);
338 	for (cp = dp->d_name, size = 0; size < namlen; size++)
339 		if (*cp == '\0' || (*cp++ == '/'))
340 			return (0);
341 	if (*cp != '\0')
342 		return (0);
343 	return (1);
344 }
345 
346 void
347 direrror(ino, errmesg)
348 	ino_t ino;
349 	char *errmesg;
350 {
351 
352 	fileerror(ino, ino, errmesg);
353 }
354 
355 void
356 fileerror(cwd, ino, errmesg)
357 	ino_t cwd, ino;
358 	char *errmesg;
359 {
360 	union dinode *dp;
361 	char pathbuf[MAXPATHLEN + 1];
362 	uint16_t mode;
363 
364 	pwarn("%s ", errmesg);
365 	pinode(ino);
366 	printf("\n");
367 	getpathname(pathbuf, cwd, ino);
368 	if (ino < ROOTINO || ino > maxino) {
369 		pfatal("NAME=%s\n", pathbuf);
370 		return;
371 	}
372 	dp = ginode(ino);
373 	if (ftypeok(dp)) {
374 		mode = DIP(dp, mode);
375 		pfatal("%s=%s\n",
376 		    (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
377 	}
378 	else
379 		pfatal("NAME=%s\n", pathbuf);
380 }
381 
382 void
383 adjust(idesc, lcnt)
384 	struct inodesc *idesc;
385 	short lcnt;
386 {
387 	union dinode *dp;
388 	int16_t nlink;
389 	int saveresolved;
390 
391 	dp = ginode(idesc->id_number);
392 	nlink = iswap16(DIP(dp, nlink));
393 	if (nlink == lcnt) {
394 		/*
395 		 * If we have not hit any unresolved problems, are running
396 		 * in preen mode, and are on a file system using soft updates,
397 		 * then just toss any partially allocated files.
398 		 */
399 		if (resolved && preen && usedsoftdep) {
400 			clri(idesc, "UNREF", 1);
401 			return;
402 		} else {
403 			/*
404 			 * The file system can be marked clean even if
405 			 * a file is not linked up, but is cleared.
406 			 * Hence, resolved should not be cleared when
407 			 * linkup is answered no, but clri is answered yes.
408 			 */
409 			saveresolved = resolved;
410 			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
411 				resolved = saveresolved;
412 				clri(idesc, "UNREF", 0);
413 				return;
414 			}
415 			/*
416 			 * Account for the new reference created by linkup().
417 			 */
418 			dp = ginode(idesc->id_number);
419 			lcnt--;
420 		}
421 	}
422 	if (lcnt != 0) {
423 		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
424 			((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ?
425 			"DIR" : "FILE"));
426 		pinode(idesc->id_number);
427 		printf(" COUNT %d SHOULD BE %d",
428 			nlink, nlink - lcnt);
429 		if (preen || usedsoftdep) {
430 			if (lcnt < 0) {
431 				printf("\n");
432 				pfatal("LINK COUNT INCREASING");
433 			}
434 			if (preen)
435 				printf(" (ADJUSTED)\n");
436 		}
437 		if (preen || reply("ADJUST") == 1) {
438 			DIP(dp, nlink) = iswap16(nlink - lcnt);
439 			inodirty();
440 		} else
441 			markclean=  0;
442 	}
443 }
444 
445 static int
446 mkentry(idesc)
447 	struct inodesc *idesc;
448 {
449 	struct direct *dirp = idesc->id_dirp;
450 	struct direct newent;
451 	int newlen, oldlen;
452 
453 	newent.d_namlen = strlen(idesc->id_name);
454 	newlen = DIRSIZ(0, &newent, 0);
455 	if (dirp->d_ino != 0)
456 		oldlen = DIRSIZ(0, dirp, 0);
457 	else
458 		oldlen = 0;
459 	if (iswap16(dirp->d_reclen) - oldlen < newlen)
460 		return (KEEPON);
461 	newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
462 	dirp->d_reclen = iswap16(oldlen);
463 	dirp = (struct direct *)(((char *)dirp) + oldlen);
464 	dirp->d_ino = iswap32(idesc->id_parent);	/* ino to be entered is in id_parent */
465 	dirp->d_reclen = newent.d_reclen;
466 	if (newinofmt)
467 		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
468 	else
469 		dirp->d_type = 0;
470 	dirp->d_namlen = newent.d_namlen;
471 	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
472 #	if (BYTE_ORDER == LITTLE_ENDIAN)
473 		/*
474 		 * If the entry was split, dirscan() will only reverse the byte
475 		 * order of the original entry, and not the new one, before
476 		 * writing it back out.  So, we reverse the byte order here if
477 		 * necessary.
478 		 */
479 		if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) {
480 #	else
481 		if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) {
482 #	endif
483 			u_char tmp;
484 
485 			tmp = dirp->d_namlen;
486 			dirp->d_namlen = dirp->d_type;
487 			dirp->d_type = tmp;
488 		}
489 	return (ALTERED|STOP);
490 }
491 
492 static int
493 chgino(idesc)
494 	struct inodesc *idesc;
495 {
496 	struct direct *dirp = idesc->id_dirp;
497 
498 	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
499 		return (KEEPON);
500 	dirp->d_ino = iswap32(idesc->id_parent);
501 	if (newinofmt)
502 		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
503 	else
504 		dirp->d_type = 0;
505 	return (ALTERED|STOP);
506 }
507 
508 int
509 linkup(orphan, parentdir, name)
510 	ino_t orphan;
511 	ino_t parentdir;
512 	char *name;
513 {
514 	union dinode *dp;
515 	int lostdir;
516 	ino_t oldlfdir;
517 	struct inodesc idesc;
518 	char tempname[BUFSIZ];
519 	int16_t nlink;
520 	uint16_t mode;
521 
522 	memset(&idesc, 0, sizeof(struct inodesc));
523 	dp = ginode(orphan);
524 	mode = iswap16(DIP(dp, mode));
525 	nlink = iswap16(DIP(dp, nlink));
526 	lostdir = (mode & IFMT) == IFDIR;
527 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
528 	pinode(orphan);
529 	if (preen  && DIP(dp, size) == 0)
530 		return (0);
531 	if (preen)
532 		printf(" (RECONNECTED)\n");
533 	else
534 		if (reply("RECONNECT") == 0) {
535 			markclean = 0;
536 			return (0);
537 		}
538 	if (parentdir != 0)
539 		inoinfo(parentdir)->ino_linkcnt++;
540 	if (lfdir == 0) {
541 		dp = ginode(ROOTINO);
542 		idesc.id_name = lfname;
543 		idesc.id_type = DATA;
544 		idesc.id_func = findino;
545 		idesc.id_number = ROOTINO;
546 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
547 			lfdir = idesc.id_parent;
548 		} else {
549 			pwarn("NO lost+found DIRECTORY");
550 			if (preen || reply("CREATE")) {
551 				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
552 				if (lfdir != 0) {
553 					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
554 						numdirs++;
555 						if (preen)
556 							printf(" (CREATED)\n");
557 					} else {
558 						freedir(lfdir, ROOTINO);
559 						lfdir = 0;
560 						if (preen)
561 							printf("\n");
562 					}
563 				}
564 				reparent(lfdir, ROOTINO);
565 			}
566 		}
567 		if (lfdir == 0) {
568 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
569 			markclean = 0;
570 			return (0);
571 		}
572 	}
573 	dp = ginode(lfdir);
574 	mode = DIP(dp, mode);
575 	mode = iswap16(mode);
576 	if ((mode & IFMT) != IFDIR) {
577 		pfatal("lost+found IS NOT A DIRECTORY");
578 		if (reply("REALLOCATE") == 0) {
579 			markclean = 0;
580 			return (0);
581 		}
582 		oldlfdir = lfdir;
583 		lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
584 		if (lfdir == 0) {
585 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
586 			markclean = 0;
587 			return (0);
588 		}
589 		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
590 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
591 			markclean = 0;
592 			return (0);
593 		}
594 		inodirty();
595 		reparent(lfdir, ROOTINO);
596 		idesc.id_type = ADDR;
597 		idesc.id_func = pass4check;
598 		idesc.id_number = oldlfdir;
599 		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
600 		inoinfo(oldlfdir)->ino_linkcnt = 0;
601 		dp = ginode(lfdir);
602 	}
603 	if (inoinfo(lfdir)->ino_state != DFOUND) {
604 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
605 		markclean = 0;
606 		return (0);
607 	}
608 	(void)lftempname(tempname, orphan);
609 	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
610 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
611 		printf("\n\n");
612 		markclean = 0;
613 		return (0);
614 	}
615 	inoinfo(orphan)->ino_linkcnt--;
616 	if (lostdir) {
617 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
618 		    parentdir != (ino_t)-1)
619 			(void)makeentry(orphan, lfdir, "..");
620 		dp = ginode(lfdir);
621 		nlink = DIP(dp, nlink);
622 		DIP(dp, nlink) = iswap16(iswap16(nlink) + 1);
623 		inodirty();
624 		inoinfo(lfdir)->ino_linkcnt++;
625 		reparent(orphan, lfdir);
626 		pwarn("DIR I=%u CONNECTED. ", orphan);
627 		if (parentdir != (ino_t)-1)
628 			printf("PARENT WAS I=%u\n", parentdir);
629 		if (preen == 0)
630 			printf("\n");
631 	}
632 	return (1);
633 }
634 
635 /*
636  * fix an entry in a directory.
637  */
638 int
639 changeino(dir, name, newnum)
640 	ino_t dir;
641 	char *name;
642 	ino_t newnum;
643 {
644 	struct inodesc idesc;
645 
646 	memset(&idesc, 0, sizeof(struct inodesc));
647 	idesc.id_type = DATA;
648 	idesc.id_func = chgino;
649 	idesc.id_number = dir;
650 	idesc.id_fix = DONTKNOW;
651 	idesc.id_name = name;
652 	idesc.id_parent = newnum;	/* new value for name */
653 	return (ckinode(ginode(dir), &idesc));
654 }
655 
656 /*
657  * make an entry in a directory
658  */
659 int
660 makeentry(parent, ino, name)
661 	ino_t parent, ino;
662 	char *name;
663 {
664 	union dinode *dp;
665 	struct inodesc idesc;
666 	char pathbuf[MAXPATHLEN + 1];
667 
668 	if (parent < ROOTINO || parent >= maxino ||
669 	    ino < ROOTINO || ino >= maxino)
670 		return (0);
671 	memset(&idesc, 0, sizeof(struct inodesc));
672 	idesc.id_type = DATA;
673 	idesc.id_func = mkentry;
674 	idesc.id_number = parent;
675 	idesc.id_parent = ino;	/* this is the inode to enter */
676 	idesc.id_fix = DONTKNOW;
677 	idesc.id_name = name;
678 	dp = ginode(parent);
679 	if (iswap64(DIP(dp, size)) % dirblksiz) {
680 		DIP(dp, size) =
681 		    iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz));
682 		inodirty();
683 	}
684 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
685 		return (1);
686 	getpathname(pathbuf, parent, parent);
687 	dp = ginode(parent);
688 	if (expanddir(dp, pathbuf) == 0)
689 		return (0);
690 	return (ckinode(dp, &idesc) & ALTERED);
691 }
692 
693 /*
694  * Attempt to expand the size of a directory
695  */
696 static int
697 expanddir(dp, name)
698 	union dinode *dp;
699 	char *name;
700 {
701 	daddr_t lastbn, newblk, dirblk;
702 	struct bufarea *bp;
703 	char *cp;
704 #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
705 	char firstblk[DIRBLKSIZ];
706 #else
707 	char firstblk[APPLEUFS_DIRBLKSIZ];
708 #endif
709 	struct ufs1_dinode *dp1;
710 	struct ufs2_dinode *dp2;
711 
712 	if (is_ufs2)
713 		dp2 = &dp->dp2;
714 	else
715 		dp1 = &dp->dp1;
716 
717 	lastbn = lblkno(sblock, iswap64(DIP(dp, size)));
718 	if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
719 	    DIP(dp, size) == 0)
720 		return (0);
721 	if ((newblk = allocblk(sblock->fs_frag)) == 0)
722 		return (0);
723 	if (is_ufs2) {
724 		dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
725 		dp2->di_db[lastbn] = iswap64(newblk);
726 		dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
727 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
728 		    btodb(sblock->fs_bsize));
729 		dirblk = iswap64(dp2->di_db[lastbn + 1]);
730 	} else {
731 		dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
732 		dp1->di_db[lastbn] = iswap32((int32_t)newblk);
733 		dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
734 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
735 		    btodb(sblock->fs_bsize));
736 		dirblk = iswap32(dp1->di_db[lastbn + 1]);
737 	}
738 	bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1));
739 	if (bp->b_errs)
740 		goto bad;
741 	memmove(firstblk, bp->b_un.b_buf, dirblksiz);
742 	bp = getdirblk(newblk, sblock->fs_bsize);
743 	if (bp->b_errs)
744 		goto bad;
745 	memmove(bp->b_un.b_buf, firstblk, dirblksiz);
746 	emptydir.dot_reclen = iswap16(dirblksiz);
747 	for (cp = &bp->b_un.b_buf[dirblksiz];
748 	     cp < &bp->b_un.b_buf[sblock->fs_bsize];
749 	     cp += dirblksiz)
750 		memmove(cp, &emptydir, sizeof emptydir);
751 	dirty(bp);
752 	bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1));
753 	if (bp->b_errs)
754 		goto bad;
755 	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
756 	pwarn("NO SPACE LEFT IN %s", name);
757 	if (preen)
758 		printf(" (EXPANDED)\n");
759 	else if (reply("EXPAND") == 0)
760 		goto bad;
761 	dirty(bp);
762 	inodirty();
763 	return (1);
764 bad:
765 	if (is_ufs2) {
766 		dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
767 		dp2->di_db[lastbn + 1] = 0;
768 		dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
769 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
770 		    btodb(sblock->fs_bsize));
771 	} else {
772 		dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
773 		dp1->di_db[lastbn + 1] = 0;
774 		dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
775 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
776 		    btodb(sblock->fs_bsize));
777 	}
778 	freeblk(newblk, sblock->fs_frag);
779 	markclean = 0;
780 	return (0);
781 }
782 
783 /*
784  * allocate a new directory
785  */
786 ino_t
787 allocdir(parent, request, mode)
788 	ino_t parent, request;
789 	int mode;
790 {
791 	ino_t ino;
792 	char *cp;
793 	union dinode *dp;
794 	struct bufarea *bp;
795 	struct inoinfo *inp;
796 	struct dirtemplate *dirp;
797 	daddr_t dirblk;
798 
799 	ino = allocino(request, IFDIR|mode);
800 	dirhead.dot_reclen = iswap16(12);
801 	dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
802 	odirhead.dot_reclen = iswap16(12);
803 	odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
804 	odirhead.dot_namlen = iswap16(1);
805 	odirhead.dotdot_namlen = iswap16(2);
806 	if (newinofmt)
807 		dirp = &dirhead;
808 	else
809 		dirp = (struct dirtemplate *)&odirhead;
810 	dirp->dot_ino = iswap32(ino);
811 	dirp->dotdot_ino = iswap32(parent);
812 	dp = ginode(ino);
813 	dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
814 		    : iswap32(dp->dp1.di_db[0]);
815 	bp = getdirblk(dirblk, sblock->fs_fsize);
816 	if (bp->b_errs) {
817 		freeino(ino);
818 		return (0);
819 	}
820 	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
821 	emptydir.dot_reclen = iswap16(dirblksiz);
822 	for (cp = &bp->b_un.b_buf[dirblksiz];
823 	     cp < &bp->b_un.b_buf[sblock->fs_fsize];
824 	     cp += dirblksiz)
825 		memmove(cp, &emptydir, sizeof emptydir);
826 	dirty(bp);
827 	DIP(dp, nlink) = iswap16(2);
828 	inodirty();
829 	if (ino == ROOTINO) {
830 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
831 		cacheino(dp, ino);
832 		return(ino);
833 	}
834 	if (inoinfo(parent)->ino_state != DSTATE &&
835 	    inoinfo(parent)->ino_state != DFOUND) {
836 		freeino(ino);
837 		return (0);
838 	}
839 	cacheino(dp, ino);
840 	inp = getinoinfo(ino);
841 	inp->i_parent = parent;
842 	inp->i_dotdot = parent;
843 	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
844 	if (inoinfo(ino)->ino_state == DSTATE) {
845 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
846 		inoinfo(parent)->ino_linkcnt++;
847 	}
848 	dp = ginode(parent);
849 	DIP(dp, nlink) = iswap16(iswap16(DIP(dp, nlink)) + 1);
850 	inodirty();
851 	return (ino);
852 }
853 
854 /*
855  * free a directory inode
856  */
857 static void
858 freedir(ino, parent)
859 	ino_t ino, parent;
860 {
861 	union dinode *dp;
862 
863 	if (ino != parent) {
864 		dp = ginode(parent);
865 		DIP(dp, nlink) = iswap16(iswap16(DIP(dp, nlink)) -1);
866 		inodirty();
867 	}
868 	freeino(ino);
869 }
870 
871 /*
872  * generate a temporary name for the lost+found directory.
873  */
874 static int
875 lftempname(bufp, ino)
876 	char *bufp;
877 	ino_t ino;
878 {
879 	ino_t in;
880 	char *cp;
881 	int namlen;
882 
883 	cp = bufp + 2;
884 	for (in = maxino; in > 0; in /= 10)
885 		cp++;
886 	*--cp = 0;
887 	namlen = cp - bufp;
888 	in = ino;
889 	while (cp > bufp) {
890 		*--cp = (in % 10) + '0';
891 		in /= 10;
892 	}
893 	*cp = '#';
894 	return (namlen);
895 }
896 
897 /*
898  * Get a directory block.
899  * Insure that it is held until another is requested.
900  */
901 static struct bufarea *
902 getdirblk(blkno, size)
903 	daddr_t blkno;
904 	long size;
905 {
906 
907 	if (pdirbp != 0)
908 		pdirbp->b_flags &= ~B_INUSE;
909 	pdirbp = getdatablk(blkno, size);
910 	return (pdirbp);
911 }
912