xref: /netbsd-src/sbin/fsck_ffs/dir.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: dir.c,v 1.49 2006/10/16 03:09:06 christos 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.49 2006/10/16 03:09:06 christos 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 = 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 = 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 = 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 DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
158 	char dbuf[DIRBLKSIZ];
159 #else
160 	char dbuf[APPLEUFS_DIRBLKSIZ];
161 #endif
162 
163 	if (idesc->id_type != DATA)
164 		errx(EEXIT, "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 > 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 = 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 < 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 = DIRSIZ(0, &newent, 0);
454 	if (dirp->d_ino != 0)
455 		oldlen = 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(ROOTINO);
538 		idesc.id_name = lfname;
539 		idesc.id_type = DATA;
540 		idesc.id_func = findino;
541 		idesc.id_number = ROOTINO;
542 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
543 			lfdir = idesc.id_parent;
544 		} else {
545 			pwarn("NO lost+found DIRECTORY");
546 			if (preen || reply("CREATE")) {
547 				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
548 				if (lfdir != 0) {
549 					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
550 						numdirs++;
551 						if (preen)
552 							printf(" (CREATED)\n");
553 					} else {
554 						freedir(lfdir, ROOTINO);
555 						lfdir = 0;
556 						if (preen)
557 							printf("\n");
558 					}
559 				}
560 				if (lfdir != 0) {
561 					reparent(lfdir, ROOTINO);
562 				}
563 			}
564 		}
565 		if (lfdir == 0) {
566 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
567 			markclean = 0;
568 			return (0);
569 		}
570 	}
571 	dp = ginode(lfdir);
572 	mode = DIP(dp, mode);
573 	mode = iswap16(mode);
574 	if ((mode & IFMT) != IFDIR) {
575 		pfatal("lost+found IS NOT A DIRECTORY");
576 		if (reply("REALLOCATE") == 0) {
577 			markclean = 0;
578 			return (0);
579 		}
580 		oldlfdir = lfdir;
581 		lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
582 		if (lfdir == 0) {
583 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
584 			markclean = 0;
585 			return (0);
586 		}
587 		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
588 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
589 			markclean = 0;
590 			return (0);
591 		}
592 		inodirty();
593 		reparent(lfdir, ROOTINO);
594 		idesc.id_type = ADDR;
595 		idesc.id_func = pass4check;
596 		idesc.id_number = oldlfdir;
597 		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
598 		inoinfo(oldlfdir)->ino_linkcnt = 0;
599 		dp = ginode(lfdir);
600 	}
601 	if (inoinfo(lfdir)->ino_state != DFOUND) {
602 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
603 		markclean = 0;
604 		return (0);
605 	}
606 	(void)lftempname(tempname, orphan);
607 	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
608 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
609 		printf("\n\n");
610 		markclean = 0;
611 		return (0);
612 	}
613 	inoinfo(orphan)->ino_linkcnt--;
614 	if (lostdir) {
615 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
616 		    parentdir != (ino_t)-1)
617 			(void)makeentry(orphan, lfdir, "..");
618 		dp = ginode(lfdir);
619 		nlink = DIP(dp, nlink);
620 		DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
621 		inodirty();
622 		inoinfo(lfdir)->ino_linkcnt++;
623 		reparent(orphan, lfdir);
624 		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
625 		if (parentdir != (ino_t)-1)
626 			printf("PARENT WAS I=%llu\n",
627 			    (unsigned long long)parentdir);
628 		if (preen == 0)
629 			printf("\n");
630 	}
631 	return (1);
632 }
633 
634 /*
635  * fix an entry in a directory.
636  */
637 int
638 changeino(ino_t dir, const char *name, ino_t newnum)
639 {
640 	struct inodesc idesc;
641 
642 	memset(&idesc, 0, sizeof(struct inodesc));
643 	idesc.id_type = DATA;
644 	idesc.id_func = chgino;
645 	idesc.id_number = dir;
646 	idesc.id_fix = DONTKNOW;
647 	idesc.id_name = name;
648 	idesc.id_parent = newnum;	/* new value for name */
649 	return (ckinode(ginode(dir), &idesc));
650 }
651 
652 /*
653  * make an entry in a directory
654  */
655 int
656 makeentry(ino_t parent, ino_t ino, const char *name)
657 {
658 	union dinode *dp;
659 	struct inodesc idesc;
660 	char pathbuf[MAXPATHLEN + 1];
661 
662 	if (parent < ROOTINO || parent >= maxino ||
663 	    ino < ROOTINO || ino >= maxino)
664 		return (0);
665 	memset(&idesc, 0, sizeof(struct inodesc));
666 	idesc.id_type = DATA;
667 	idesc.id_func = mkentry;
668 	idesc.id_number = parent;
669 	idesc.id_parent = ino;	/* this is the inode to enter */
670 	idesc.id_fix = DONTKNOW;
671 	idesc.id_name = name;
672 	dp = ginode(parent);
673 	if (iswap64(DIP(dp, size)) % dirblksiz) {
674 		DIP_SET(dp, size,
675 		    iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
676 		inodirty();
677 	}
678 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
679 		return (1);
680 	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
681 	dp = ginode(parent);
682 	if (expanddir(dp, pathbuf) == 0)
683 		return (0);
684 	return (ckinode(dp, &idesc) & ALTERED);
685 }
686 
687 /*
688  * Attempt to expand the size of a directory
689  */
690 static int
691 expanddir(union dinode *dp, char *name)
692 {
693 	daddr_t lastbn, newblk, dirblk;
694 	struct bufarea *bp;
695 	char *cp;
696 #if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ
697 	char firstblk[DIRBLKSIZ];
698 #else
699 	char firstblk[APPLEUFS_DIRBLKSIZ];
700 #endif
701 	struct ufs1_dinode *dp1 = NULL;
702 	struct ufs2_dinode *dp2 = NULL;
703 
704 	if (is_ufs2)
705 		dp2 = &dp->dp2;
706 	else
707 		dp1 = &dp->dp1;
708 
709 	lastbn = lblkno(sblock, iswap64(DIP(dp, size)));
710 	if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
711 	    DIP(dp, size) == 0)
712 		return (0);
713 	if ((newblk = allocblk(sblock->fs_frag)) == 0)
714 		return (0);
715 	if (is_ufs2) {
716 		dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
717 		dp2->di_db[lastbn] = iswap64(newblk);
718 		dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
719 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
720 		    btodb(sblock->fs_bsize));
721 		dirblk = iswap64(dp2->di_db[lastbn + 1]);
722 	} else {
723 		dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
724 		dp1->di_db[lastbn] = iswap32((int32_t)newblk);
725 		dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
726 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
727 		    btodb(sblock->fs_bsize));
728 		dirblk = iswap32(dp1->di_db[lastbn + 1]);
729 	}
730 	bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1));
731 	if (bp->b_errs)
732 		goto bad;
733 	memmove(firstblk, bp->b_un.b_buf, dirblksiz);
734 	bp = getdirblk(newblk, sblock->fs_bsize);
735 	if (bp->b_errs)
736 		goto bad;
737 	memmove(bp->b_un.b_buf, firstblk, dirblksiz);
738 	emptydir.dot_reclen = iswap16(dirblksiz);
739 	for (cp = &bp->b_un.b_buf[dirblksiz];
740 	     cp < &bp->b_un.b_buf[sblock->fs_bsize];
741 	     cp += dirblksiz)
742 		memmove(cp, &emptydir, sizeof emptydir);
743 	dirty(bp);
744 	bp = getdirblk(dirblk, sblksize(sblock, DIP(dp, size), lastbn + 1));
745 	if (bp->b_errs)
746 		goto bad;
747 	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
748 	pwarn("NO SPACE LEFT IN %s", name);
749 	if (preen)
750 		printf(" (EXPANDED)\n");
751 	else if (reply("EXPAND") == 0)
752 		goto bad;
753 	dirty(bp);
754 	inodirty();
755 	return (1);
756 bad:
757 	if (is_ufs2) {
758 		dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
759 		dp2->di_db[lastbn + 1] = 0;
760 		dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
761 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
762 		    btodb(sblock->fs_bsize));
763 	} else {
764 		dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
765 		dp1->di_db[lastbn + 1] = 0;
766 		dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
767 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
768 		    btodb(sblock->fs_bsize));
769 	}
770 	freeblk(newblk, sblock->fs_frag);
771 	markclean = 0;
772 	return (0);
773 }
774 
775 /*
776  * allocate a new directory
777  */
778 ino_t
779 allocdir(ino_t parent, ino_t request, int mode)
780 {
781 	ino_t ino;
782 	char *cp;
783 	union dinode *dp;
784 	struct bufarea *bp;
785 	struct inoinfo *inp;
786 	struct dirtemplate *dirp;
787 	daddr_t dirblk;
788 
789 	ino = allocino(request, IFDIR|mode);
790 	if (ino < ROOTINO)
791 		return 0;
792 	dirhead.dot_reclen = iswap16(12);
793 	dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
794 	odirhead.dot_reclen = iswap16(12);
795 	odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
796 	odirhead.dot_namlen = iswap16(1);
797 	odirhead.dotdot_namlen = iswap16(2);
798 	if (newinofmt)
799 		dirp = &dirhead;
800 	else
801 		dirp = (struct dirtemplate *)&odirhead;
802 	dirp->dot_ino = iswap32(ino);
803 	dirp->dotdot_ino = iswap32(parent);
804 	dp = ginode(ino);
805 	dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
806 		    : iswap32(dp->dp1.di_db[0]);
807 	bp = getdirblk(dirblk, sblock->fs_fsize);
808 	if (bp->b_errs) {
809 		freeino(ino);
810 		return (0);
811 	}
812 	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
813 	emptydir.dot_reclen = iswap16(dirblksiz);
814 	for (cp = &bp->b_un.b_buf[dirblksiz];
815 	     cp < &bp->b_un.b_buf[sblock->fs_fsize];
816 	     cp += dirblksiz)
817 		memmove(cp, &emptydir, sizeof emptydir);
818 	dirty(bp);
819 	DIP_SET(dp, nlink, iswap16(2));
820 	inodirty();
821 	if (ino == ROOTINO) {
822 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
823 		cacheino(dp, ino);
824 		return(ino);
825 	}
826 	if (inoinfo(parent)->ino_state != DSTATE &&
827 	    inoinfo(parent)->ino_state != DFOUND) {
828 		freeino(ino);
829 		return (0);
830 	}
831 	cacheino(dp, ino);
832 	inp = getinoinfo(ino);
833 	inp->i_parent = parent;
834 	inp->i_dotdot = parent;
835 	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
836 	if (inoinfo(ino)->ino_state == DSTATE) {
837 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
838 		inoinfo(parent)->ino_linkcnt++;
839 	}
840 	dp = ginode(parent);
841 	DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
842 	inodirty();
843 	return (ino);
844 }
845 
846 /*
847  * free a directory inode
848  */
849 static void
850 freedir(ino_t ino, ino_t parent)
851 {
852 	union dinode *dp;
853 
854 	if (ino != parent) {
855 		dp = ginode(parent);
856 		DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1));
857 		inodirty();
858 	}
859 	freeino(ino);
860 }
861 
862 /*
863  * generate a temporary name for the lost+found directory.
864  */
865 static int
866 lftempname(char *bufp, ino_t ino)
867 {
868 	ino_t in;
869 	char *cp;
870 	int namlen;
871 
872 	cp = bufp + 2;
873 	for (in = maxino; in > 0; in /= 10)
874 		cp++;
875 	*--cp = 0;
876 	namlen = cp - bufp;
877 	in = ino;
878 	while (cp > bufp) {
879 		*--cp = (in % 10) + '0';
880 		in /= 10;
881 	}
882 	*cp = '#';
883 	return (namlen);
884 }
885 
886 /*
887  * Get a directory block.
888  * Insure that it is held until another is requested.
889  */
890 static struct bufarea *
891 getdirblk(daddr_t blkno, long size)
892 {
893 
894 	if (pdirbp != 0)
895 		pdirbp->b_flags &= ~B_INUSE;
896 	pdirbp = getdatablk(blkno, size);
897 	return (pdirbp);
898 }
899