xref: /netbsd-src/sbin/fsck_ffs/dir.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: dir.c,v 1.58 2017/02/08 16:11:40 rin 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)dir.c	8.8 (Berkeley) 4/28/95";
36 #else
37 __RCSID("$NetBSD: dir.c,v 1.58 2017/02/08 16:11:40 rin Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ffs/fs.h>
47 #include <ufs/ffs/ffs_extern.h>
48 
49 #include <err.h>
50 #include <stdio.h>
51 #include <string.h>
52 
53 #include "fsck.h"
54 #include "fsutil.h"
55 #include "extern.h"
56 
57 const char	*lfname = "lost+found";
58 int	lfmode = 01700;
59 ino_t	lfdir;
60 struct	dirtemplate emptydir = {
61 	.dot_ino = 0,
62 	.dot_reclen = UFS_DIRBLKSIZ,
63 };
64 struct	dirtemplate dirhead = {
65 	.dot_ino = 0,
66 	.dot_reclen = 12,
67 	.dot_type = DT_DIR,
68 	.dot_namlen = 1,
69 	.dot_name = ".",
70 	.dotdot_ino = 0,
71 	.dotdot_reclen = UFS_DIRBLKSIZ - 12,
72 	.dotdot_type = DT_DIR,
73 	.dotdot_namlen = 2,
74 	.dotdot_name = "..",
75 };
76 struct	odirtemplate odirhead = {
77 	.dot_ino = 0,
78 	.dot_reclen = 12,
79 	.dot_namlen = 1,
80 	.dot_name = ".",
81 	.dotdot_ino = 0,
82 	.dotdot_reclen = UFS_DIRBLKSIZ - 12,
83 	.dotdot_namlen = 2,
84 	.dotdot_name = "..",
85 };
86 
87 static int chgino(struct  inodesc *);
88 static int dircheck(struct inodesc *, struct direct *);
89 static int expanddir(union dinode *, char *);
90 static void freedir(ino_t, ino_t);
91 static struct direct *fsck_readdir(struct inodesc *);
92 static struct bufarea *getdirblk(daddr_t, long);
93 static int lftempname(char *, ino_t);
94 static int mkentry(struct inodesc *);
95 void reparent(ino_t, ino_t);
96 
97 /*
98  * Propagate connected state through the tree.
99  */
100 void
101 propagate(ino_t inumber)
102 {
103 	struct inoinfo *inp;
104 
105 	inp = getinoinfo(inumber);
106 
107 	for (;;) {
108 		inoinfo(inp->i_number)->ino_state = DMARK;
109 		if (inp->i_child &&
110 		    inoinfo(inp->i_child->i_number)->ino_state != DMARK)
111 			inp = inp->i_child;
112 		else if (inp->i_number == inumber)
113 			break;
114 		else if (inp->i_sibling)
115 			inp = inp->i_sibling;
116 		else
117 			inp = getinoinfo(inp->i_parent);
118 	}
119 
120 	for (;;) {
121 		inoinfo(inp->i_number)->ino_state = DFOUND;
122 		if (inp->i_child &&
123 		    inoinfo(inp->i_child->i_number)->ino_state != DFOUND)
124 			inp = inp->i_child;
125 		else if (inp->i_number == inumber)
126 			break;
127 		else if (inp->i_sibling)
128 			inp = inp->i_sibling;
129 		else
130 			inp = getinoinfo(inp->i_parent);
131 	}
132 }
133 
134 void
135 reparent(ino_t inumber, ino_t parent)
136 {
137 	struct inoinfo *inp, *pinp;
138 
139 	inp = getinoinfo(inumber);
140 	inp->i_parent = inp->i_dotdot = parent;
141 	pinp = getinoinfo(parent);
142 	inp->i_sibling = pinp->i_child;
143 	pinp->i_child = inp;
144 	propagate(inumber);
145 }
146 
147 /*
148  * Scan each entry in a directory block.
149  */
150 int
151 dirscan(struct inodesc *idesc)
152 {
153 	struct direct *dp;
154 	struct bufarea *bp;
155 	int dsize, n;
156 	long blksiz;
157 #if !defined(NO_APPLE_UFS) && UFS_DIRBLKSIZ < APPLEUFS_DIRBLKSIZ
158 	char dbuf[APPLEUFS_DIRBLKSIZ];
159 #else
160 	char dbuf[UFS_DIRBLKSIZ];
161 #endif
162 
163 	if (idesc->id_type != DATA)
164 		errexit("wrong type to dirscan %d", idesc->id_type);
165 	if (idesc->id_entryno == 0 &&
166 	    (idesc->id_filesize & (dirblksiz - 1)) != 0)
167 		idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
168 	blksiz = idesc->id_numfrags * sblock->fs_fsize;
169 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
170 		idesc->id_filesize -= blksiz;
171 		return (SKIP);
172 	}
173 
174 	/*
175 	 * If we are are swapping byte order in directory entries, just swap
176 	 * this block and return.
177 	 */
178 	if (do_dirswap) {
179 		int off;
180 		bp = getdirblk(idesc->id_blkno, blksiz);
181 		for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
182 			dp = (struct direct *)(bp->b_un.b_buf + off);
183 			dp->d_ino = bswap32(dp->d_ino);
184 			dp->d_reclen = bswap16(dp->d_reclen);
185 			if (!newinofmt) {
186 				u_int8_t tmp = dp->d_namlen;
187 				dp->d_namlen = dp->d_type;
188 				dp->d_type = tmp;
189 			}
190 			if (dp->d_reclen == 0)
191 				break;
192 		}
193 		dirty(bp);
194 		idesc->id_filesize -= blksiz;
195 		return (idesc->id_filesize > 0 ? KEEPON : STOP);
196 	}
197 
198 	idesc->id_loc = 0;
199 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
200 		dsize = iswap16(dp->d_reclen);
201 		if (dsize > (int)sizeof dbuf)
202 			dsize = sizeof dbuf;
203 		memmove(dbuf, dp, (size_t)dsize);
204 #		if (BYTE_ORDER == LITTLE_ENDIAN)
205 			if (!newinofmt && !needswap) {
206 #		else
207 			if (!newinofmt && needswap) {
208 #		endif
209 				struct direct *tdp = (struct direct *)dbuf;
210 				u_char tmp;
211 
212 				tmp = tdp->d_namlen;
213 				tdp->d_namlen = tdp->d_type;
214 				tdp->d_type = tmp;
215 			}
216 		idesc->id_dirp = (struct direct *)dbuf;
217 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
218 #			if (BYTE_ORDER == LITTLE_ENDIAN)
219 				if (!newinofmt && !doinglevel2 && !needswap) {
220 #			else
221 				if (!newinofmt && !doinglevel2 && needswap) {
222 #			endif
223 					struct direct *tdp;
224 					u_char tmp;
225 
226 					tdp = (struct direct *)dbuf;
227 					tmp = tdp->d_namlen;
228 					tdp->d_namlen = tdp->d_type;
229 					tdp->d_type = tmp;
230 				}
231 			bp = getdirblk(idesc->id_blkno, blksiz);
232 			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
233 			    (size_t)dsize);
234 			dirty(bp);
235 			sbdirty();
236 		}
237 		if (n & STOP)
238 			return (n);
239 	}
240 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
241 }
242 
243 /*
244  * get next entry in a directory.
245  */
246 static struct direct *
247 fsck_readdir(struct inodesc *idesc)
248 {
249 	struct direct *dp, *ndp;
250 	struct bufarea *bp;
251 	long size, blksiz, fix, dploc;
252 
253 	blksiz = idesc->id_numfrags * sblock->fs_fsize;
254 	bp = getdirblk(idesc->id_blkno, blksiz);
255 	if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
256 	    idesc->id_loc < blksiz) {
257 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
258 		if (dircheck(idesc, dp))
259 			goto dpok;
260 		if (idesc->id_fix == IGNORE)
261 			return (0);
262 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
263 		bp = getdirblk(idesc->id_blkno, blksiz);
264 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
265 		dp->d_reclen = iswap16(dirblksiz);
266 		dp->d_ino = 0;
267 		dp->d_type = 0;
268 		dp->d_namlen = 0;
269 		dp->d_name[0] = '\0';
270 		if (fix)
271 			dirty(bp);
272 		else
273 			markclean = 0;
274 		idesc->id_loc += dirblksiz;
275 		idesc->id_filesize -= dirblksiz;
276 		return (dp);
277 	}
278 dpok:
279 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
280 		return NULL;
281 	dploc = idesc->id_loc;
282 	dp = (struct direct *)(bp->b_un.b_buf + dploc);
283 	idesc->id_loc += iswap16(dp->d_reclen);
284 	idesc->id_filesize -= iswap16(dp->d_reclen);
285 	if ((idesc->id_loc % dirblksiz) == 0)
286 		return (dp);
287 	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
288 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
289 	    dircheck(idesc, ndp) == 0) {
290 		size = dirblksiz - (idesc->id_loc % dirblksiz);
291 		idesc->id_loc += size;
292 		idesc->id_filesize -= size;
293 		if (idesc->id_fix == IGNORE)
294 			return (0);
295 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
296 		bp = getdirblk(idesc->id_blkno, blksiz);
297 		dp = (struct direct *)(bp->b_un.b_buf + dploc);
298 		dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
299 		if (fix)
300 			dirty(bp);
301 		else
302 			markclean = 0;
303 	}
304 	return (dp);
305 }
306 
307 /*
308  * Verify that a directory entry is valid.
309  * This is a superset of the checks made in the kernel.
310  */
311 static int
312 dircheck(struct inodesc *idesc, struct direct *dp)
313 {
314 	int size;
315 	char *cp;
316 	u_char namlen, type;
317 	int spaceleft;
318 
319 	spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
320 	if (iswap32(dp->d_ino) >= maxino ||
321 	    dp->d_reclen == 0 ||
322 	    iswap16(dp->d_reclen) > spaceleft ||
323 	    (iswap16(dp->d_reclen) & 0x3) != 0)
324 		return (0);
325 	if (dp->d_ino == 0)
326 		return (1);
327 	size = UFS_DIRSIZ(!newinofmt, dp, needswap);
328 #	if (BYTE_ORDER == LITTLE_ENDIAN)
329 		if (!newinofmt && !needswap) {
330 #	else
331 		if (!newinofmt && needswap) {
332 #	endif
333 			type = dp->d_namlen;
334 			namlen = dp->d_type;
335 		} else {
336 			namlen = dp->d_namlen;
337 			type = dp->d_type;
338 		}
339 	if (iswap16(dp->d_reclen) < size ||
340 	    idesc->id_filesize < size ||
341 	    /* namlen > MAXNAMLEN || */
342 	    type > 15)
343 		return (0);
344 	for (cp = dp->d_name, size = 0; size < namlen; size++)
345 		if (*cp == '\0' || (*cp++ == '/'))
346 			return (0);
347 	if (*cp != '\0')
348 		return (0);
349 	return (1);
350 }
351 
352 void
353 direrror(ino_t ino, const char *errmesg)
354 {
355 
356 	fileerror(ino, ino, errmesg);
357 }
358 
359 void
360 fileerror(ino_t cwd, ino_t ino, const char *errmesg)
361 {
362 	union dinode *dp;
363 	char pathbuf[MAXPATHLEN + 1];
364 	uint16_t mode;
365 
366 	pwarn("%s ", errmesg);
367 	pinode(ino);
368 	printf("\n");
369 	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
370 	if (ino < UFS_ROOTINO || ino > maxino) {
371 		pfatal("NAME=%s\n", pathbuf);
372 		return;
373 	}
374 	dp = ginode(ino);
375 	if (ftypeok(dp)) {
376 		mode = DIP(dp, mode);
377 		pfatal("%s=%s\n",
378 		    (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
379 	}
380 	else
381 		pfatal("NAME=%s\n", pathbuf);
382 }
383 
384 void
385 adjust(struct inodesc *idesc, int 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_SET(dp, nlink, iswap16(nlink - lcnt));
439 			inodirty();
440 		} else
441 			markclean = 0;
442 	}
443 }
444 
445 static int
446 mkentry(struct inodesc *idesc)
447 {
448 	struct direct *dirp = idesc->id_dirp;
449 	struct direct newent;
450 	int newlen, oldlen;
451 
452 	newent.d_namlen = strlen(idesc->id_name);
453 	newlen = UFS_DIRSIZ(0, &newent, 0);
454 	if (dirp->d_ino != 0)
455 		oldlen = UFS_DIRSIZ(0, dirp, 0);
456 	else
457 		oldlen = 0;
458 	if (iswap16(dirp->d_reclen) - oldlen < newlen)
459 		return (KEEPON);
460 	newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
461 	dirp->d_reclen = iswap16(oldlen);
462 	dirp = (struct direct *)(((char *)dirp) + oldlen);
463 	/* ino to be entered is in id_parent */
464 	dirp->d_ino = iswap32(idesc->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(struct inodesc *idesc)
494 {
495 	struct direct *dirp = idesc->id_dirp;
496 
497 	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
498 		return (KEEPON);
499 	dirp->d_ino = iswap32(idesc->id_parent);
500 	if (newinofmt)
501 		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
502 	else
503 		dirp->d_type = 0;
504 	return (ALTERED|STOP);
505 }
506 
507 int
508 linkup(ino_t orphan, ino_t parentdir, char *name)
509 {
510 	union dinode *dp;
511 	int lostdir;
512 	ino_t oldlfdir;
513 	struct inodesc idesc;
514 	char tempname[BUFSIZ];
515 	int16_t nlink;
516 	uint16_t mode;
517 
518 	memset(&idesc, 0, sizeof(struct inodesc));
519 	dp = ginode(orphan);
520 	mode = iswap16(DIP(dp, mode));
521 	nlink = iswap16(DIP(dp, nlink));
522 	lostdir = (mode & IFMT) == IFDIR;
523 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
524 	pinode(orphan);
525 	if (preen  && DIP(dp, size) == 0)
526 		return (0);
527 	if (preen)
528 		printf(" (RECONNECTED)\n");
529 	else
530 		if (reply("RECONNECT") == 0) {
531 			markclean = 0;
532 			return (0);
533 		}
534 	if (parentdir != 0)
535 		inoinfo(parentdir)->ino_linkcnt++;
536 	if (lfdir == 0) {
537 		dp = ginode(UFS_ROOTINO);
538 		idesc.id_name = lfname;
539 		idesc.id_type = DATA;
540 		idesc.id_func = findino;
541 		idesc.id_number = UFS_ROOTINO;
542 		idesc.id_uid = iswap32(DIP(dp, uid));
543 		idesc.id_gid = iswap32(DIP(dp, gid));
544 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
545 			lfdir = idesc.id_parent;
546 		} else {
547 			pwarn("NO lost+found DIRECTORY");
548 			if (preen || reply("CREATE")) {
549 				lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode);
550 				if (lfdir != 0) {
551 					if (makeentry(UFS_ROOTINO, lfdir, lfname) != 0) {
552 						numdirs++;
553 						if (preen)
554 							printf(" (CREATED)\n");
555 					} else {
556 						freedir(lfdir, UFS_ROOTINO);
557 						lfdir = 0;
558 						if (preen)
559 							printf("\n");
560 					}
561 				}
562 				if (lfdir != 0) {
563 					reparent(lfdir, UFS_ROOTINO);
564 				}
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(UFS_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(UFS_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, UFS_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_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
623 		inodirty();
624 		inoinfo(lfdir)->ino_linkcnt++;
625 		reparent(orphan, lfdir);
626 		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
627 		if (parentdir != (ino_t)-1)
628 			printf("PARENT WAS I=%llu\n",
629 			    (unsigned long long)parentdir);
630 		if (preen == 0)
631 			printf("\n");
632 	}
633 	return (1);
634 }
635 
636 /*
637  * fix an entry in a directory.
638  */
639 int
640 changeino(ino_t dir, const char *name, ino_t newnum)
641 {
642 	struct inodesc idesc;
643 	union dinode *dp;
644 
645 	dp = ginode(dir);
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 	idesc.id_uid = iswap32(DIP(dp, uid));
654 	idesc.id_gid = iswap32(DIP(dp, gid));
655 	return (ckinode(dp, &idesc));
656 }
657 
658 /*
659  * make an entry in a directory
660  */
661 int
662 makeentry(ino_t parent, ino_t ino, const char *name)
663 {
664 	union dinode *dp;
665 	struct inodesc idesc;
666 	char pathbuf[MAXPATHLEN + 1];
667 
668 	if (parent < UFS_ROOTINO || parent >= maxino ||
669 	    ino < UFS_ROOTINO || ino >= maxino)
670 		return (0);
671 	dp = ginode(parent);
672 	memset(&idesc, 0, sizeof(struct inodesc));
673 	idesc.id_type = DATA;
674 	idesc.id_func = mkentry;
675 	idesc.id_number = parent;
676 	idesc.id_parent = ino;	/* this is the inode to enter */
677 	idesc.id_fix = DONTKNOW;
678 	idesc.id_name = name;
679 	idesc.id_uid = iswap32(DIP(dp, uid));
680 	idesc.id_gid = iswap32(DIP(dp, gid));
681 	if (iswap64(DIP(dp, size)) % dirblksiz) {
682 		DIP_SET(dp, size,
683 		    iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
684 		inodirty();
685 	}
686 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
687 		return (1);
688 	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
689 	dp = ginode(parent);
690 	if (expanddir(dp, pathbuf) == 0)
691 		return (0);
692 	update_uquot(idesc.id_number, idesc.id_uid, idesc.id_gid,
693 	    btodb(sblock->fs_bsize), 0);
694 	return (ckinode(dp, &idesc) & ALTERED);
695 }
696 
697 /*
698  * Attempt to expand the size of a directory
699  */
700 static int
701 expanddir(union dinode *dp, char *name)
702 {
703 	daddr_t lastbn, newblk, dirblk;
704 	struct bufarea *bp;
705 	char *cp;
706 #if !defined(NO_APPLE_UFS) && UFS_DIRBLKSIZ < APPLEUFS_DIRBLKSIZ
707 	char firstblk[APPLEUFS_DIRBLKSIZ];
708 #else
709 	char firstblk[UFS_DIRBLKSIZ];
710 #endif
711 	struct ufs1_dinode *dp1 = NULL;
712 	struct ufs2_dinode *dp2 = NULL;
713 
714 	if (is_ufs2)
715 		dp2 = &dp->dp2;
716 	else
717 		dp1 = &dp->dp1;
718 
719 	lastbn = ffs_lblkno(sblock, iswap64(DIP(dp, size)));
720 	if (lastbn >= UFS_NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
721 	    DIP(dp, size) == 0)
722 		return (0);
723 	if ((newblk = allocblk(sblock->fs_frag)) == 0)
724 		return (0);
725 	if (is_ufs2) {
726 		dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
727 		dp2->di_db[lastbn] = iswap64(newblk);
728 		dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
729 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
730 		    btodb(sblock->fs_bsize));
731 		dirblk = iswap64(dp2->di_db[lastbn + 1]);
732 	} else {
733 		dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
734 		dp1->di_db[lastbn] = iswap32((int32_t)newblk);
735 		dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
736 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
737 		    btodb(sblock->fs_bsize));
738 		dirblk = iswap32(dp1->di_db[lastbn + 1]);
739 	}
740 	bp = getdirblk(dirblk, ffs_sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
741 	if (bp->b_errs)
742 		goto bad;
743 	memmove(firstblk, bp->b_un.b_buf, dirblksiz);
744 	bp = getdirblk(newblk, sblock->fs_bsize);
745 	if (bp->b_errs)
746 		goto bad;
747 	memmove(bp->b_un.b_buf, firstblk, dirblksiz);
748 	emptydir.dot_reclen = iswap16(dirblksiz);
749 	for (cp = &bp->b_un.b_buf[dirblksiz];
750 	     cp < &bp->b_un.b_buf[sblock->fs_bsize];
751 	     cp += dirblksiz)
752 		memmove(cp, &emptydir, sizeof emptydir);
753 	dirty(bp);
754 	bp = getdirblk(dirblk, ffs_sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
755 	if (bp->b_errs)
756 		goto bad;
757 	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
758 	pwarn("NO SPACE LEFT IN %s", name);
759 	if (preen)
760 		printf(" (EXPANDED)\n");
761 	else if (reply("EXPAND") == 0)
762 		goto bad;
763 	dirty(bp);
764 	inodirty();
765 	return (1);
766 bad:
767 	if (is_ufs2) {
768 		dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
769 		dp2->di_db[lastbn + 1] = 0;
770 		dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
771 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
772 		    btodb(sblock->fs_bsize));
773 	} else {
774 		dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
775 		dp1->di_db[lastbn + 1] = 0;
776 		dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
777 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
778 		    btodb(sblock->fs_bsize));
779 	}
780 	freeblk(newblk, sblock->fs_frag);
781 	markclean = 0;
782 	return (0);
783 }
784 
785 /*
786  * allocate a new directory
787  */
788 ino_t
789 allocdir(ino_t parent, ino_t request, 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 	if (ino < UFS_ROOTINO)
801 		return 0;
802 	update_uquot(ino, 0, 0, btodb(sblock->fs_fsize), 1);
803 	dirhead.dot_reclen = iswap16(12);
804 	dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
805 	odirhead.dot_reclen = iswap16(12);
806 	odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
807 	odirhead.dot_namlen = iswap16(1);
808 	odirhead.dotdot_namlen = iswap16(2);
809 	if (newinofmt)
810 		dirp = &dirhead;
811 	else
812 		dirp = (struct dirtemplate *)&odirhead;
813 	dirp->dot_ino = iswap32(ino);
814 	dirp->dotdot_ino = iswap32(parent);
815 	dp = ginode(ino);
816 	dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
817 		    : iswap32(dp->dp1.di_db[0]);
818 	bp = getdirblk(dirblk, sblock->fs_fsize);
819 	if (bp->b_errs) {
820 		freeino(ino);
821 		return (0);
822 	}
823 	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
824 	emptydir.dot_reclen = iswap16(dirblksiz);
825 	for (cp = &bp->b_un.b_buf[dirblksiz];
826 	     cp < &bp->b_un.b_buf[sblock->fs_fsize];
827 	     cp += dirblksiz)
828 		memmove(cp, &emptydir, sizeof emptydir);
829 	dirty(bp);
830 	DIP_SET(dp, nlink, iswap16(2));
831 	inodirty();
832 	if (ino == UFS_ROOTINO) {
833 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
834 		cacheino(dp, ino);
835 		return(ino);
836 	}
837 	if (inoinfo(parent)->ino_state != DSTATE &&
838 	    inoinfo(parent)->ino_state != DFOUND) {
839 		freeino(ino);
840 		return (0);
841 	}
842 	cacheino(dp, ino);
843 	inp = getinoinfo(ino);
844 	inp->i_parent = parent;
845 	inp->i_dotdot = parent;
846 	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
847 	if (inoinfo(ino)->ino_state == DSTATE) {
848 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
849 		inoinfo(parent)->ino_linkcnt++;
850 	}
851 	dp = ginode(parent);
852 	DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
853 	inodirty();
854 	return (ino);
855 }
856 
857 /*
858  * free a directory inode
859  */
860 static void
861 freedir(ino_t ino, ino_t parent)
862 {
863 	union dinode *dp;
864 
865 	if (ino != parent) {
866 		dp = ginode(parent);
867 		DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1));
868 		inodirty();
869 	}
870 	freeino(ino);
871 }
872 
873 /*
874  * generate a temporary name for the lost+found directory.
875  */
876 static int
877 lftempname(char *bufp, 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(daddr_t blkno, long size)
903 {
904 
905 	if (pdirbp != 0)
906 		pdirbp->b_flags &= ~B_INUSE;
907 	pdirbp = getdatablk(blkno, size);
908 	return (pdirbp);
909 }
910