Home | History | Annotate | Line # | Download | only in fsck_ffs
      1 /*	$NetBSD: dir.c,v 1.62 2023/07/04 20:40:53 riastradh 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.62 2023/07/04 20:40:53 riastradh 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 *, struct bufarea *);
     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 #if (BYTE_ORDER == LITTLE_ENDIAN)
    148 # define NEEDSWAP	(!needswap)
    149 #else
    150 # define NEEDSWAP	(needswap)
    151 #endif
    152 
    153 static void
    154 dirswap(void *dbuf)
    155 {
    156 	struct direct *tdp = (struct direct *)dbuf;
    157 	u_char tmp;
    158 
    159 	tmp = tdp->d_namlen;
    160 	tdp->d_namlen = tdp->d_type;
    161 	tdp->d_type = tmp;
    162 }
    163 
    164 /*
    165  * Scan each entry in a directory block.
    166  */
    167 int
    168 dirscan(struct inodesc *idesc)
    169 {
    170 	struct direct *dp;
    171 	struct bufarea *bp;
    172 	int dsize, n;
    173 	long blksiz;
    174 #if !defined(NO_APPLE_UFS) && UFS_DIRBLKSIZ < APPLEUFS_DIRBLKSIZ
    175 	char dbuf[APPLEUFS_DIRBLKSIZ];
    176 #else
    177 	char dbuf[UFS_DIRBLKSIZ];
    178 #endif
    179 
    180 	if (idesc->id_type != DATA)
    181 		errexit("wrong type to dirscan %d", idesc->id_type);
    182 	if (idesc->id_entryno == 0 &&
    183 	    (idesc->id_filesize & (dirblksiz - 1)) != 0)
    184 		idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz);
    185 	blksiz = idesc->id_numfrags * sblock->fs_fsize;
    186 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
    187 		idesc->id_filesize -= blksiz;
    188 		return (SKIP);
    189 	}
    190 
    191 	/*
    192 	 * If we are swapping byte order in directory entries, just swap
    193 	 * this block and return.
    194 	 */
    195 	if (do_dirswap) {
    196 		int off;
    197 		bp = getdirblk(idesc->id_blkno, blksiz);
    198 		for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) {
    199 			dp = (struct direct *)(bp->b_un.b_buf + off);
    200 			dp->d_ino = bswap32(dp->d_ino);
    201 			dp->d_reclen = bswap16(dp->d_reclen);
    202 			if (!newinofmt) {
    203 				u_int8_t tmp = dp->d_namlen;
    204 				dp->d_namlen = dp->d_type;
    205 				dp->d_type = tmp;
    206 			}
    207 			if (dp->d_reclen == 0)
    208 				break;
    209 		}
    210 		dirty(bp);
    211 		idesc->id_filesize -= blksiz;
    212 		return (idesc->id_filesize > 0 ? KEEPON : STOP);
    213 	}
    214 
    215 	idesc->id_loc = 0;
    216 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
    217 		dsize = iswap16(dp->d_reclen);
    218 		if (dsize > (int)sizeof dbuf)
    219 			dsize = sizeof dbuf;
    220 		memmove(dbuf, dp, (size_t)dsize);
    221 		if (!newinofmt && NEEDSWAP)
    222 			dirswap(dbuf);
    223 		idesc->id_dirp = (struct direct *)dbuf;
    224 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
    225 			if (!newinofmt && !doinglevel2 && NEEDSWAP)
    226 				dirswap(dbuf);
    227 			bp = getdirblk(idesc->id_blkno, blksiz);
    228 			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
    229 			    (size_t)dsize);
    230 			dirty(bp);
    231 			sbdirty();
    232 		}
    233 		if (n & STOP)
    234 			return (n);
    235 	}
    236 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
    237 }
    238 
    239 /*
    240  * get next entry in a directory.
    241  */
    242 static struct direct *
    243 fsck_readdir(struct inodesc *idesc)
    244 {
    245 	struct direct *dp, *ndp;
    246 	struct bufarea *bp;
    247 	long size, blksiz, fix, dploc;
    248 
    249 	blksiz = idesc->id_numfrags * sblock->fs_fsize;
    250 	bp = getdirblk(idesc->id_blkno, blksiz);
    251 	if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
    252 	    idesc->id_loc < blksiz) {
    253 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
    254 		if (dircheck(idesc, dp, bp))
    255 			goto dpok;
    256 		if (idesc->id_fix == IGNORE)
    257 			return (0);
    258 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
    259 		bp = getdirblk(idesc->id_blkno, blksiz);
    260 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
    261 		dp->d_reclen = iswap16(dirblksiz);
    262 		dp->d_ino = 0;
    263 		dp->d_type = 0;
    264 		dp->d_namlen = 0;
    265 		dp->d_name[0] = '\0';
    266 		if (fix)
    267 			dirty(bp);
    268 		else
    269 			markclean = 0;
    270 		idesc->id_loc += dirblksiz;
    271 		idesc->id_filesize -= dirblksiz;
    272 		return (dp);
    273 	}
    274 dpok:
    275 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
    276 		return NULL;
    277 	dploc = idesc->id_loc;
    278 	dp = (struct direct *)(bp->b_un.b_buf + dploc);
    279 	idesc->id_loc += iswap16(dp->d_reclen);
    280 	idesc->id_filesize -= iswap16(dp->d_reclen);
    281 	if ((idesc->id_loc % dirblksiz) == 0)
    282 		return (dp);
    283 	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
    284 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
    285 	    dircheck(idesc, ndp, bp) == 0) {
    286 		size = dirblksiz - (idesc->id_loc % dirblksiz);
    287 		idesc->id_loc += size;
    288 		idesc->id_filesize -= size;
    289 		if (idesc->id_fix == IGNORE)
    290 			return (0);
    291 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
    292 		bp = getdirblk(idesc->id_blkno, blksiz);
    293 		dp = (struct direct *)(bp->b_un.b_buf + dploc);
    294 		dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
    295 		if (fix)
    296 			dirty(bp);
    297 		else
    298 			markclean = 0;
    299 	}
    300 	return (dp);
    301 }
    302 
    303 /*
    304  * Verify that a directory entry is valid.
    305  * This is a superset of the checks made in the kernel.
    306  * Returns:
    307  *	1: good
    308  *	0: bad
    309  */
    310 static int
    311 dircheck(struct inodesc *idesc, struct direct *dp, struct bufarea *bp)
    312 {
    313 	uint8_t namlen, type;
    314 	uint16_t reclen;
    315 	uint32_t ino;
    316 	char *cp;
    317 	int size, spaceleft, modified, unused, i;
    318 
    319 	modified = 0;
    320 	spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
    321 
    322 	/* fill in the correct info for our fields */
    323 	ino = iswap32(dp->d_ino);
    324 	reclen = iswap16(dp->d_reclen);
    325 	if (!newinofmt && NEEDSWAP) {
    326 		type = dp->d_namlen;
    327 		namlen = dp->d_type;
    328 	} else {
    329 		namlen = dp->d_namlen;
    330 		type = dp->d_type;
    331 	}
    332 
    333 	if (ino >= maxino ||
    334 	    reclen == 0 || reclen > spaceleft || (reclen & 0x3) != 0)
    335 		goto bad;
    336 
    337 	size = UFS_DIRSIZ(!newinofmt, dp, needswap);
    338 	if (ino == 0) {
    339 		/*
    340 		 * Special case of an unused directory entry. Normally
    341 		 * the kernel would coalesce unused space with the previous
    342 		 * entry by extending its d_reclen, but there are situations
    343 		 * (e.g. fsck) where that doesn't occur.
    344 		 * If we're clearing out directory cruft (-z flag), then make
    345 		 * sure this entry gets fully cleared as well.
    346 		 */
    347 		if (!zflag || fswritefd < 0)
    348 			return 1;
    349 
    350 		if (dp->d_type != 0) {
    351 			dp->d_type = 0;
    352 			modified = 1;
    353 		}
    354 		if (dp->d_namlen != 0) {
    355 			dp->d_namlen = 0;
    356 			modified = 1;
    357 		}
    358 		if (dp->d_name[0] != '\0') {
    359 			dp->d_name[0] = '\0';
    360 			modified = 1;
    361 		}
    362 		goto good;
    363 	}
    364 
    365 	if (reclen < size || idesc->id_filesize < size ||
    366 	    /* namlen > MAXNAMLEN || */
    367 	    type > 15)
    368 		goto bad;
    369 
    370 	for (cp = dp->d_name, i = 0; i < namlen; i++)
    371 		if (*cp == '\0' || (*cp++ == '/'))
    372 			goto bad;
    373 
    374 	if (*cp != '\0')
    375 		goto bad;
    376 
    377 	if (!zflag || fswritefd < 0)
    378 		return 1;
    379 good:
    380 	/*
    381 	 * Clear unused directory entry space, including the d_name
    382 	 * padding.
    383 	 */
    384 	/* First figure the number of pad bytes. */
    385 	unused = UFS_NAMEPAD(namlen);
    386 
    387 	/* Add in the free space to the end of the record. */
    388 	unused += iswap16(dp->d_reclen) - size;
    389 
    390 	/*
    391 	 * Now clear out the unused space, keeping track if we actually
    392 	 * changed anything.
    393 	 */
    394 	for (cp = &dp->d_name[namlen]; unused > 0; unused--, cp++) {
    395 		if (*cp == '\0')
    396 			continue;
    397 		*cp = '\0';
    398 		modified = 1;
    399 	}
    400 
    401 	/* mark dirty so we update the zeroed space */
    402 	if (modified)
    403 		dirty(bp);
    404 	return 1;
    405 bad:
    406 	if (debug)
    407 		printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
    408 		    ino, reclen, namlen, type, dp->d_name);
    409 	return 0;
    410 }
    411 
    412 void
    413 direrror(ino_t ino, const char *errmesg)
    414 {
    415 
    416 	fileerror(ino, ino, errmesg);
    417 }
    418 
    419 void
    420 fileerror(ino_t cwd, ino_t ino, const char *errmesg)
    421 {
    422 	union dinode *dp;
    423 	char pathbuf[MAXPATHLEN + 1];
    424 	uint16_t mode;
    425 
    426 	pwarn("%s ", errmesg);
    427 	pinode(ino);
    428 	printf("\n");
    429 	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
    430 	if (ino < UFS_ROOTINO || ino > maxino) {
    431 		pfatal("NAME=%s\n", pathbuf);
    432 		return;
    433 	}
    434 	dp = ginode(ino);
    435 	if (ftypeok(dp)) {
    436 		mode = DIP(dp, mode);
    437 		pfatal("%s=%s\n",
    438 		    (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
    439 	}
    440 	else
    441 		pfatal("NAME=%s\n", pathbuf);
    442 }
    443 
    444 void
    445 adjust(struct inodesc *idesc, int lcnt)
    446 {
    447 	union dinode *dp;
    448 	int16_t nlink;
    449 	int saveresolved;
    450 
    451 	dp = ginode(idesc->id_number);
    452 	nlink = iswap16(DIP(dp, nlink));
    453 	if (nlink == lcnt) {
    454 		/*
    455 		 * If we have not hit any unresolved problems, are running
    456 		 * in preen mode, and are on a file system using soft updates,
    457 		 * then just toss any partially allocated files.
    458 		 */
    459 		if (resolved && preen && usedsoftdep) {
    460 			clri(idesc, "UNREF", 1);
    461 			return;
    462 		} else {
    463 			/*
    464 			 * The file system can be marked clean even if
    465 			 * a file is not linked up, but is cleared.
    466 			 * Hence, resolved should not be cleared when
    467 			 * linkup is answered no, but clri is answered yes.
    468 			 */
    469 			saveresolved = resolved;
    470 			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
    471 				resolved = saveresolved;
    472 				clri(idesc, "UNREF", 0);
    473 				return;
    474 			}
    475 			/*
    476 			 * Account for the new reference created by linkup().
    477 			 */
    478 			dp = ginode(idesc->id_number);
    479 			lcnt--;
    480 		}
    481 	}
    482 	if (lcnt != 0) {
    483 		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
    484 			((iswap16(DIP(dp, mode)) & IFMT) == IFDIR ?
    485 			"DIR" : "FILE"));
    486 		pinode(idesc->id_number);
    487 		printf(" COUNT %d SHOULD BE %d",
    488 			nlink, nlink - lcnt);
    489 		if (preen || usedsoftdep) {
    490 			if (lcnt < 0) {
    491 				printf("\n");
    492 				pfatal("LINK COUNT INCREASING");
    493 			}
    494 			if (preen)
    495 				printf(" (ADJUSTED)\n");
    496 		}
    497 		if (preen || reply("ADJUST") == 1) {
    498 			DIP_SET(dp, nlink, iswap16(nlink - lcnt));
    499 			inodirty();
    500 		} else
    501 			markclean = 0;
    502 	}
    503 }
    504 
    505 static int
    506 mkentry(struct inodesc *idesc)
    507 {
    508 	struct direct *dirp = idesc->id_dirp;
    509 	struct direct newent;
    510 	int newlen, oldlen;
    511 
    512 	newent.d_namlen = strlen(idesc->id_name);
    513 	newlen = UFS_DIRSIZ(0, &newent, 0);
    514 	if (dirp->d_ino != 0)
    515 		oldlen = UFS_DIRSIZ(0, dirp, 0);
    516 	else
    517 		oldlen = 0;
    518 	if (iswap16(dirp->d_reclen) - oldlen < newlen)
    519 		return (KEEPON);
    520 	newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen);
    521 	dirp->d_reclen = iswap16(oldlen);
    522 	dirp = (struct direct *)(((char *)dirp) + oldlen);
    523 	/* ino to be entered is in id_parent */
    524 	dirp->d_ino = iswap32(idesc->id_parent);
    525 	dirp->d_reclen = newent.d_reclen;
    526 	if (newinofmt)
    527 		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
    528 	else
    529 		dirp->d_type = 0;
    530 	dirp->d_namlen = newent.d_namlen;
    531 	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
    532 	/*
    533 	 * If the entry was split, dirscan() will only reverse the byte
    534 	 * order of the original entry, and not the new one, before
    535 	 * writing it back out.  So, we reverse the byte order here if
    536 	 * necessary.
    537 	 */
    538 	if (oldlen != 0 && !newinofmt && !doinglevel2 && NEEDSWAP)
    539 		dirswap(dirp);
    540 	return (ALTERED|STOP);
    541 }
    542 
    543 static int
    544 chgino(struct inodesc *idesc)
    545 {
    546 	struct direct *dirp = idesc->id_dirp;
    547 
    548 	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
    549 		return (KEEPON);
    550 	dirp->d_ino = iswap32(idesc->id_parent);
    551 	if (newinofmt)
    552 		dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
    553 	else
    554 		dirp->d_type = 0;
    555 	return (ALTERED|STOP);
    556 }
    557 
    558 int
    559 linkup(ino_t orphan, ino_t parentdir, char *name)
    560 {
    561 	union dinode *dp;
    562 	int lostdir;
    563 	ino_t oldlfdir;
    564 	struct inodesc idesc;
    565 	char tempname[BUFSIZ];
    566 	int16_t nlink;
    567 	uint16_t mode;
    568 
    569 	memset(&idesc, 0, sizeof(struct inodesc));
    570 	dp = ginode(orphan);
    571 	mode = iswap16(DIP(dp, mode));
    572 	nlink = iswap16(DIP(dp, nlink));
    573 	lostdir = (mode & IFMT) == IFDIR;
    574 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
    575 	pinode(orphan);
    576 	if (preen  && DIP(dp, size) == 0)
    577 		return (0);
    578 	if (preen)
    579 		printf(" (RECONNECTED)\n");
    580 	else
    581 		if (reply("RECONNECT") == 0) {
    582 			markclean = 0;
    583 			return (0);
    584 		}
    585 	if (parentdir != 0)
    586 		inoinfo(parentdir)->ino_linkcnt++;
    587 	if (lfdir == 0) {
    588 		dp = ginode(UFS_ROOTINO);
    589 		idesc.id_name = lfname;
    590 		idesc.id_type = DATA;
    591 		idesc.id_func = findino;
    592 		idesc.id_number = UFS_ROOTINO;
    593 		idesc.id_uid = iswap32(DIP(dp, uid));
    594 		idesc.id_gid = iswap32(DIP(dp, gid));
    595 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
    596 			lfdir = idesc.id_parent;
    597 		} else {
    598 			pwarn("NO lost+found DIRECTORY");
    599 			if (preen || reply("CREATE")) {
    600 				lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode);
    601 				if (lfdir != 0) {
    602 					if (makeentry(UFS_ROOTINO, lfdir, lfname) != 0) {
    603 						numdirs++;
    604 						if (preen)
    605 							printf(" (CREATED)\n");
    606 					} else {
    607 						freedir(lfdir, UFS_ROOTINO);
    608 						lfdir = 0;
    609 						if (preen)
    610 							printf("\n");
    611 					}
    612 				}
    613 				if (lfdir != 0) {
    614 					reparent(lfdir, UFS_ROOTINO);
    615 				}
    616 			}
    617 		}
    618 		if (lfdir == 0) {
    619 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    620 			markclean = 0;
    621 			return (0);
    622 		}
    623 	}
    624 	dp = ginode(lfdir);
    625 	mode = DIP(dp, mode);
    626 	mode = iswap16(mode);
    627 	if ((mode & IFMT) != IFDIR) {
    628 		pfatal("lost+found IS NOT A DIRECTORY");
    629 		if (reply("REALLOCATE") == 0) {
    630 			markclean = 0;
    631 			return (0);
    632 		}
    633 		oldlfdir = lfdir;
    634 		lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode);
    635 		if (lfdir == 0) {
    636 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    637 			markclean = 0;
    638 			return (0);
    639 		}
    640 		if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
    641 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    642 			markclean = 0;
    643 			return (0);
    644 		}
    645 		inodirty();
    646 		reparent(lfdir, UFS_ROOTINO);
    647 		idesc.id_type = ADDR;
    648 		idesc.id_func = pass4check;
    649 		idesc.id_number = oldlfdir;
    650 		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
    651 		inoinfo(oldlfdir)->ino_linkcnt = 0;
    652 		dp = ginode(lfdir);
    653 	}
    654 	if (inoinfo(lfdir)->ino_state != DFOUND) {
    655 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
    656 		markclean = 0;
    657 		return (0);
    658 	}
    659 	(void)lftempname(tempname, orphan);
    660 	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
    661 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
    662 		printf("\n\n");
    663 		markclean = 0;
    664 		return (0);
    665 	}
    666 	inoinfo(orphan)->ino_linkcnt--;
    667 	if (lostdir) {
    668 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
    669 		    parentdir != (ino_t)-1)
    670 			(void)makeentry(orphan, lfdir, "..");
    671 		dp = ginode(lfdir);
    672 		nlink = DIP(dp, nlink);
    673 		DIP_SET(dp, nlink, iswap16(iswap16(nlink) + 1));
    674 		inodirty();
    675 		inoinfo(lfdir)->ino_linkcnt++;
    676 		reparent(orphan, lfdir);
    677 		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
    678 		if (parentdir != (ino_t)-1)
    679 			printf("PARENT WAS I=%llu\n",
    680 			    (unsigned long long)parentdir);
    681 		if (preen == 0)
    682 			printf("\n");
    683 	}
    684 	return (1);
    685 }
    686 
    687 /*
    688  * fix an entry in a directory.
    689  */
    690 int
    691 changeino(ino_t dir, const char *name, ino_t newnum)
    692 {
    693 	struct inodesc idesc;
    694 	union dinode *dp;
    695 
    696 	dp = ginode(dir);
    697 	memset(&idesc, 0, sizeof(struct inodesc));
    698 	idesc.id_type = DATA;
    699 	idesc.id_func = chgino;
    700 	idesc.id_number = dir;
    701 	idesc.id_fix = DONTKNOW;
    702 	idesc.id_name = name;
    703 	idesc.id_parent = newnum;	/* new value for name */
    704 	idesc.id_uid = iswap32(DIP(dp, uid));
    705 	idesc.id_gid = iswap32(DIP(dp, gid));
    706 	return (ckinode(dp, &idesc));
    707 }
    708 
    709 /*
    710  * make an entry in a directory
    711  */
    712 int
    713 makeentry(ino_t parent, ino_t ino, const char *name)
    714 {
    715 	union dinode *dp;
    716 	struct inodesc idesc;
    717 	char pathbuf[MAXPATHLEN + 1];
    718 
    719 	if (parent < UFS_ROOTINO || parent >= maxino ||
    720 	    ino < UFS_ROOTINO || ino >= maxino)
    721 		return (0);
    722 	dp = ginode(parent);
    723 	memset(&idesc, 0, sizeof(struct inodesc));
    724 	idesc.id_type = DATA;
    725 	idesc.id_func = mkentry;
    726 	idesc.id_number = parent;
    727 	idesc.id_parent = ino;	/* this is the inode to enter */
    728 	idesc.id_fix = DONTKNOW;
    729 	idesc.id_name = name;
    730 	idesc.id_uid = iswap32(DIP(dp, uid));
    731 	idesc.id_gid = iswap32(DIP(dp, gid));
    732 	if (iswap64(DIP(dp, size)) % dirblksiz) {
    733 		DIP_SET(dp, size,
    734 		    iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz)));
    735 		inodirty();
    736 	}
    737 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
    738 		return (1);
    739 	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
    740 	dp = ginode(parent);
    741 	if (expanddir(dp, pathbuf) == 0)
    742 		return (0);
    743 	update_uquot(idesc.id_number, idesc.id_uid, idesc.id_gid,
    744 	    btodb(sblock->fs_bsize), 0);
    745 	return (ckinode(dp, &idesc) & ALTERED);
    746 }
    747 
    748 /*
    749  * Attempt to expand the size of a directory
    750  */
    751 static int
    752 expanddir(union dinode *dp, char *name)
    753 {
    754 	daddr_t lastbn, newblk, dirblk;
    755 	struct bufarea *bp;
    756 	char *cp;
    757 #if !defined(NO_APPLE_UFS) && UFS_DIRBLKSIZ < APPLEUFS_DIRBLKSIZ
    758 	char firstblk[APPLEUFS_DIRBLKSIZ];
    759 #else
    760 	char firstblk[UFS_DIRBLKSIZ];
    761 #endif
    762 	struct ufs1_dinode *dp1 = NULL;
    763 	struct ufs2_dinode *dp2 = NULL;
    764 
    765 	if (is_ufs2)
    766 		dp2 = &dp->dp2;
    767 	else
    768 		dp1 = &dp->dp1;
    769 
    770 	lastbn = ffs_lblkno(sblock, iswap64(DIP(dp, size)));
    771 	if (lastbn >= UFS_NDADDR - 1 || DIP(dp, db[lastbn]) == 0 ||
    772 	    DIP(dp, size) == 0)
    773 		return (0);
    774 	if ((newblk = allocblk(sblock->fs_frag)) == 0)
    775 		return (0);
    776 	if (is_ufs2) {
    777 		dp2->di_db[lastbn + 1] = dp2->di_db[lastbn];
    778 		dp2->di_db[lastbn] = iswap64(newblk);
    779 		dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize);
    780 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) +
    781 		    btodb(sblock->fs_bsize));
    782 		dirblk = iswap64(dp2->di_db[lastbn + 1]);
    783 	} else {
    784 		dp1->di_db[lastbn + 1] = dp1->di_db[lastbn];
    785 		dp1->di_db[lastbn] = iswap32((int32_t)newblk);
    786 		dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize);
    787 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) +
    788 		    btodb(sblock->fs_bsize));
    789 		dirblk = iswap32(dp1->di_db[lastbn + 1]);
    790 	}
    791 	bp = getdirblk(dirblk, ffs_sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
    792 	if (bp->b_errs)
    793 		goto bad;
    794 	memmove(firstblk, bp->b_un.b_buf, dirblksiz);
    795 	bp = getdirblk(newblk, sblock->fs_bsize);
    796 	if (bp->b_errs)
    797 		goto bad;
    798 	memmove(bp->b_un.b_buf, firstblk, dirblksiz);
    799 	emptydir.dot_reclen = iswap16(dirblksiz);
    800 	for (cp = &bp->b_un.b_buf[dirblksiz];
    801 	     cp < &bp->b_un.b_buf[sblock->fs_bsize];
    802 	     cp += dirblksiz)
    803 		memmove(cp, &emptydir, sizeof emptydir);
    804 	dirty(bp);
    805 	bp = getdirblk(dirblk, ffs_sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1));
    806 	if (bp->b_errs)
    807 		goto bad;
    808 	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
    809 	pwarn("NO SPACE LEFT IN %s", name);
    810 	if (preen)
    811 		printf(" (EXPANDED)\n");
    812 	else if (reply("EXPAND") == 0)
    813 		goto bad;
    814 	dirty(bp);
    815 	inodirty();
    816 	return (1);
    817 bad:
    818 	if (is_ufs2) {
    819 		dp2->di_db[lastbn] = dp2->di_db[lastbn + 1];
    820 		dp2->di_db[lastbn + 1] = 0;
    821 		dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize);
    822 		dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) -
    823 		    btodb(sblock->fs_bsize));
    824 	} else {
    825 		dp1->di_db[lastbn] = dp1->di_db[lastbn + 1];
    826 		dp1->di_db[lastbn + 1] = 0;
    827 		dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize);
    828 		dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) -
    829 		    btodb(sblock->fs_bsize));
    830 	}
    831 	freeblk(newblk, sblock->fs_frag);
    832 	markclean = 0;
    833 	return (0);
    834 }
    835 
    836 /*
    837  * allocate a new directory
    838  */
    839 ino_t
    840 allocdir(ino_t parent, ino_t request, int mode)
    841 {
    842 	ino_t ino;
    843 	char *cp;
    844 	union dinode *dp;
    845 	struct bufarea *bp;
    846 	struct inoinfo *inp;
    847 	struct dirtemplate *dirp;
    848 	daddr_t dirblk;
    849 
    850 	ino = allocino(request, IFDIR|mode);
    851 	if (ino < UFS_ROOTINO)
    852 		return 0;
    853 	update_uquot(ino, 0, 0, btodb(sblock->fs_fsize), 1);
    854 	dirhead.dot_reclen = iswap16(12);
    855 	dirhead.dotdot_reclen = iswap16(dirblksiz - 12);
    856 	odirhead.dot_reclen = iswap16(12);
    857 	odirhead.dotdot_reclen = iswap16(dirblksiz - 12);
    858 	odirhead.dot_namlen = iswap16(1);
    859 	odirhead.dotdot_namlen = iswap16(2);
    860 	if (newinofmt)
    861 		dirp = &dirhead;
    862 	else
    863 		dirp = (struct dirtemplate *)&odirhead;
    864 	dirp->dot_ino = iswap32(ino);
    865 	dirp->dotdot_ino = iswap32(parent);
    866 	dp = ginode(ino);
    867 	dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0])
    868 		    : iswap32(dp->dp1.di_db[0]);
    869 	bp = getdirblk(dirblk, sblock->fs_fsize);
    870 	if (bp->b_errs) {
    871 		freeino(ino);
    872 		return (0);
    873 	}
    874 	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
    875 	emptydir.dot_reclen = iswap16(dirblksiz);
    876 	for (cp = &bp->b_un.b_buf[dirblksiz];
    877 	     cp < &bp->b_un.b_buf[sblock->fs_fsize];
    878 	     cp += dirblksiz)
    879 		memmove(cp, &emptydir, sizeof emptydir);
    880 	dirty(bp);
    881 	DIP_SET(dp, nlink, iswap16(2));
    882 	inodirty();
    883 	if (ino == UFS_ROOTINO) {
    884 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
    885 		cacheino(dp, ino);
    886 		return(ino);
    887 	}
    888 	if (inoinfo(parent)->ino_state != DSTATE &&
    889 	    inoinfo(parent)->ino_state != DFOUND) {
    890 		freeino(ino);
    891 		return (0);
    892 	}
    893 	cacheino(dp, ino);
    894 	inp = getinoinfo(ino);
    895 	inp->i_parent = parent;
    896 	inp->i_dotdot = parent;
    897 	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
    898 	if (inoinfo(ino)->ino_state == DSTATE) {
    899 		inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink));
    900 		inoinfo(parent)->ino_linkcnt++;
    901 	}
    902 	dp = ginode(parent);
    903 	DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1));
    904 	inodirty();
    905 	return (ino);
    906 }
    907 
    908 /*
    909  * free a directory inode
    910  */
    911 static void
    912 freedir(ino_t ino, ino_t parent)
    913 {
    914 	union dinode *dp;
    915 
    916 	if (ino != parent) {
    917 		dp = ginode(parent);
    918 		DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1));
    919 		inodirty();
    920 	}
    921 	freeino(ino);
    922 }
    923 
    924 /*
    925  * generate a temporary name for the lost+found directory.
    926  */
    927 static int
    928 lftempname(char *bufp, ino_t ino)
    929 {
    930 	ino_t in;
    931 	char *cp;
    932 	int namlen;
    933 
    934 	cp = bufp + 2;
    935 	for (in = maxino; in > 0; in /= 10)
    936 		cp++;
    937 	*--cp = 0;
    938 	namlen = cp - bufp;
    939 	in = ino;
    940 	while (cp > bufp) {
    941 		*--cp = (in % 10) + '0';
    942 		in /= 10;
    943 	}
    944 	*cp = '#';
    945 	return (namlen);
    946 }
    947 
    948 /*
    949  * Get a directory block.
    950  * Insure that it is held until another is requested.
    951  */
    952 static struct bufarea *
    953 getdirblk(daddr_t blkno, long size)
    954 {
    955 
    956 	if (pdirbp != 0)
    957 		pdirbp->b_flags &= ~B_INUSE;
    958 	pdirbp = getdatablk(blkno, size);
    959 	return (pdirbp);
    960 }
    961