Home | History | Annotate | Line # | Download | only in fsck_lfs
dir.c revision 1.38
      1 /* $NetBSD: dir.c,v 1.38 2015/09/01 06:16:58 dholland 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/types.h>
     33 #include <sys/param.h>
     34 #include <sys/time.h>
     35 #include <sys/buf.h>
     36 #include <sys/mount.h>
     37 
     38 #include <ufs/lfs/lfs.h>
     39 #include <ufs/lfs/lfs_accessors.h>
     40 #include <ufs/lfs/lfs_inode.h>
     41 
     42 #include <err.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <string.h>
     46 
     47 #include "bufcache.h"
     48 #include "vnode.h"
     49 #include "lfs_user.h"
     50 
     51 #include "fsck.h"
     52 #include "fsutil.h"
     53 #include "extern.h"
     54 
     55 const char *lfname = "lost+found";
     56 int lfmode = 01700;
     57 struct lfs_dirtemplate emptydir = {
     58 	.dot_ino = 0,
     59 	.dot_reclen = LFS_DIRBLKSIZ,
     60 };
     61 struct lfs_dirtemplate dirhead = {
     62 	.dot_ino = 0,
     63 	.dot_reclen = 12,
     64 	.dot_type = LFS_DT_DIR,
     65 	.dot_namlen = 1,
     66 	.dot_name = ".",
     67 	.dotdot_ino = 0,
     68 	.dotdot_reclen = LFS_DIRBLKSIZ - 12,
     69 	.dotdot_type = LFS_DT_DIR,
     70 	.dotdot_namlen = 2,
     71 	.dotdot_name = ".."
     72 };
     73 #if 0
     74 struct lfs_odirtemplate odirhead = {
     75 	.dot_ino = 0,
     76 	.dot_reclen = 12,
     77 	.dot_namlen = 1,
     78 	.dot_name = ".",
     79 	.dotdot_ino = 0,
     80 	.dotdot_reclen = LFS_DIRBLKSIZ - 12,
     81 	.dotdot_namlen = 2,
     82 	.dotdot_name = ".."
     83 };
     84 #endif
     85 
     86 static int expanddir(struct uvnode *, union lfs_dinode *, char *);
     87 static void freedir(ino_t, ino_t);
     88 static struct lfs_direct *fsck_readdir(struct uvnode *, struct inodesc *);
     89 static int lftempname(char *, ino_t);
     90 static int mkentry(struct inodesc *);
     91 static int chgino(struct inodesc *);
     92 
     93 /*
     94  * Propagate connected state through the tree.
     95  */
     96 void
     97 propagate(void)
     98 {
     99 	struct inoinfo **inpp, *inp, *pinp;
    100 	struct inoinfo **inpend;
    101 
    102 	/*
    103 	 * Create a list of children for each directory.
    104 	 */
    105 	inpend = &inpsort[inplast];
    106 	for (inpp = inpsort; inpp < inpend; inpp++) {
    107 		inp = *inpp;
    108 		if (inp->i_parent == 0 ||
    109 		    inp->i_number == ULFS_ROOTINO)
    110 			continue;
    111 		pinp = getinoinfo(inp->i_parent);
    112 		inp->i_parentp = pinp;
    113 		inp->i_sibling = pinp->i_child;
    114 		pinp->i_child = inp;
    115 	}
    116 	inp = getinoinfo(ULFS_ROOTINO);
    117 	while (inp) {
    118 		statemap[inp->i_number] = DFOUND;
    119 		if (inp->i_child &&
    120 		    statemap[inp->i_child->i_number] == DSTATE)
    121 			inp = inp->i_child;
    122 		else if (inp->i_sibling)
    123 			inp = inp->i_sibling;
    124 		else
    125 			inp = inp->i_parentp;
    126 	}
    127 }
    128 
    129 /*
    130  * Scan each entry in a directory block.
    131  */
    132 int
    133 dirscan(struct inodesc *idesc)
    134 {
    135 	struct lfs_direct *dp;
    136 	struct ubuf *bp;
    137 	int dsize, n;
    138 	long blksiz;
    139 	char dbuf[LFS_DIRBLKSIZ];
    140 	struct uvnode *vp;
    141 
    142 	if (idesc->id_type != DATA)
    143 		errexit("wrong type to dirscan %d", idesc->id_type);
    144 	if (idesc->id_entryno == 0 &&
    145 	    (idesc->id_filesize & (LFS_DIRBLKSIZ - 1)) != 0)
    146 		idesc->id_filesize = roundup(idesc->id_filesize, LFS_DIRBLKSIZ);
    147 	blksiz = idesc->id_numfrags * lfs_sb_getfsize(fs);
    148 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
    149 		idesc->id_filesize -= blksiz;
    150 		return (SKIP);
    151 	}
    152 	idesc->id_loc = 0;
    153 
    154 	vp = vget(fs, idesc->id_number);
    155 	for (dp = fsck_readdir(vp, idesc); dp != NULL;
    156 	    dp = fsck_readdir(vp, idesc)) {
    157 		dsize = dp->d_reclen;
    158 		memcpy(dbuf, dp, (size_t) dsize);
    159 		idesc->id_dirp = (struct lfs_direct *) dbuf;
    160 		if ((n = (*idesc->id_func) (idesc)) & ALTERED) {
    161 			bread(vp, idesc->id_lblkno, blksiz, 0, &bp);
    162 			memcpy(bp->b_data + idesc->id_loc - dsize, dbuf,
    163 			    (size_t) dsize);
    164 			VOP_BWRITE(bp);
    165 			sbdirty();
    166 		}
    167 		if (n & STOP)
    168 			return (n);
    169 	}
    170 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
    171 }
    172 
    173 /*
    174  * get next entry in a directory.
    175  */
    176 static struct lfs_direct *
    177 fsck_readdir(struct uvnode *vp, struct inodesc *idesc)
    178 {
    179 	struct lfs_direct *dp, *ndp;
    180 	struct ubuf *bp;
    181 	long size, blksiz, fix, dploc;
    182 
    183 	blksiz = idesc->id_numfrags * lfs_sb_getfsize(fs);
    184 	bread(vp, idesc->id_lblkno, blksiz, 0, &bp);
    185 	if (idesc->id_loc % LFS_DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
    186 	    idesc->id_loc < blksiz) {
    187 		dp = (struct lfs_direct *) (bp->b_data + idesc->id_loc);
    188 		if (dircheck(idesc, dp))
    189 			goto dpok;
    190 		brelse(bp, 0);
    191 		if (idesc->id_fix == IGNORE)
    192 			return (0);
    193 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
    194 		bread(vp, idesc->id_lblkno, blksiz, 0, &bp);
    195 		dp = (struct lfs_direct *) (bp->b_data + idesc->id_loc);
    196 		dp->d_reclen = LFS_DIRBLKSIZ;
    197 		dp->d_ino = 0;
    198 		lfs_dir_settype(fs, dp, LFS_DT_UNKNOWN);
    199 		lfs_dir_setnamlen(fs, dp, 0);
    200 		dp->d_name[0] = '\0';
    201 		if (fix)
    202 			VOP_BWRITE(bp);
    203 		else
    204 			brelse(bp, 0);
    205 		idesc->id_loc += LFS_DIRBLKSIZ;
    206 		idesc->id_filesize -= LFS_DIRBLKSIZ;
    207 		return (dp);
    208 	}
    209 dpok:
    210 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) {
    211 		brelse(bp, 0);
    212 		return NULL;
    213 	}
    214 	dploc = idesc->id_loc;
    215 	dp = (struct lfs_direct *) (bp->b_data + dploc);
    216 	idesc->id_loc += dp->d_reclen;
    217 	idesc->id_filesize -= dp->d_reclen;
    218 	if ((idesc->id_loc % LFS_DIRBLKSIZ) == 0) {
    219 		brelse(bp, 0);
    220 		return dp;
    221 	}
    222 	ndp = (struct lfs_direct *) (bp->b_data + idesc->id_loc);
    223 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
    224 	    dircheck(idesc, ndp) == 0) {
    225 		brelse(bp, 0);
    226 		size = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ);
    227 		idesc->id_loc += size;
    228 		idesc->id_filesize -= size;
    229 		if (idesc->id_fix == IGNORE)
    230 			return 0;
    231 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
    232 		bread(vp, idesc->id_lblkno, blksiz, 0, &bp);
    233 		dp = (struct lfs_direct *) (bp->b_data + dploc);
    234 		dp->d_reclen += size;
    235 		if (fix)
    236 			VOP_BWRITE(bp);
    237 		else
    238 			brelse(bp, 0);
    239 	} else
    240 		brelse(bp, 0);
    241 
    242 	return (dp);
    243 }
    244 
    245 /*
    246  * Verify that a directory entry is valid.
    247  * This is a superset of the checks made in the kernel.
    248  */
    249 int
    250 dircheck(struct inodesc *idesc, struct lfs_direct *dp)
    251 {
    252 	int size;
    253 	char *cp;
    254 	u_char namlen, type;
    255 	int spaceleft;
    256 
    257 	spaceleft = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ);
    258 	if (dp->d_ino >= maxino ||
    259 	    dp->d_reclen == 0 ||
    260 	    dp->d_reclen > spaceleft ||
    261 	    (dp->d_reclen & 0x3) != 0) {
    262 		pwarn("ino too large, reclen=0, reclen>space, or reclen&3!=0\n");
    263 		pwarn("dp->d_ino = 0x%x\tdp->d_reclen = 0x%x\n",
    264 		    dp->d_ino, dp->d_reclen);
    265 		pwarn("maxino = %llu\tspaceleft = 0x%x\n",
    266 		    (unsigned long long)maxino, spaceleft);
    267 		return (0);
    268 	}
    269 	if (dp->d_ino == 0)
    270 		return (1);
    271 	size = LFS_DIRSIZ(fs, dp);
    272 	namlen = lfs_dir_getnamlen(fs, dp);
    273 	type = lfs_dir_gettype(fs, dp);
    274 	if (dp->d_reclen < size ||
    275 	    idesc->id_filesize < size ||
    276 	/* namlen > MAXNAMLEN || */
    277 	    type > 15) {
    278 		printf("reclen<size, filesize<size, namlen too large, or type>15\n");
    279 		return (0);
    280 	}
    281 	for (cp = dp->d_name, size = 0; size < namlen; size++)
    282 		if (*cp == '\0' || (*cp++ == '/')) {
    283 			printf("name contains NUL or /\n");
    284 			return (0);
    285 		}
    286 	if (*cp != '\0') {
    287 		printf("name size misstated\n");
    288 		return (0);
    289 	}
    290 	return (1);
    291 }
    292 
    293 void
    294 direrror(ino_t ino, const char *errmesg)
    295 {
    296 
    297 	fileerror(ino, ino, errmesg);
    298 }
    299 
    300 void
    301 fileerror(ino_t cwd, ino_t ino, const char *errmesg)
    302 {
    303 	char pathbuf[MAXPATHLEN + 1];
    304 	struct uvnode *vp;
    305 
    306 	pwarn("%s ", errmesg);
    307 	pinode(ino);
    308 	printf("\n");
    309 	pwarn("PARENT=%lld\n", (long long)cwd);
    310 	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
    311 	if (ino < ULFS_ROOTINO || ino >= maxino) {
    312 		pfatal("NAME=%s\n", pathbuf);
    313 		return;
    314 	}
    315 	vp = vget(fs, ino);
    316 	if (vp == NULL)
    317 		pfatal("INO is NULL\n");
    318 	else {
    319 		if (ftypeok(VTOD(vp)))
    320 			pfatal("%s=%s\n",
    321 			    (lfs_dino_getmode(fs, VTOI(vp)->i_din) & LFS_IFMT) == LFS_IFDIR ?
    322 			    "DIR" : "FILE", pathbuf);
    323 		else
    324 			pfatal("NAME=%s\n", pathbuf);
    325 	}
    326 }
    327 
    328 void
    329 adjust(struct inodesc *idesc, short lcnt)
    330 {
    331 	struct uvnode *vp;
    332 	union lfs_dinode *dp;
    333 
    334 	/*
    335 	 * XXX: (1) since lcnt is apparently a delta, rename it; (2)
    336 	 * why is it a value to *subtract*? that is unnecessarily
    337 	 * confusing.
    338 	 */
    339 
    340 	vp = vget(fs, idesc->id_number);
    341 	dp = VTOD(vp);
    342 	if (lfs_dino_getnlink(fs, dp) == lcnt) {
    343 		if (linkup(idesc->id_number, (ino_t) 0) == 0)
    344 			clri(idesc, "UNREF", 0);
    345 	} else {
    346 		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
    347 		    ((lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR ? "DIR" : "FILE"));
    348 		pinode(idesc->id_number);
    349 		printf(" COUNT %d SHOULD BE %d",
    350 		    lfs_dino_getnlink(fs, dp), lfs_dino_getnlink(fs, dp) - lcnt);
    351 		if (preen) {
    352 			if (lcnt < 0) {
    353 				printf("\n");
    354 				pfatal("LINK COUNT INCREASING");
    355 			}
    356 			printf(" (ADJUSTED)\n");
    357 		}
    358 		if (preen || reply("ADJUST") == 1) {
    359 			lfs_dino_setnlink(fs, dp,
    360 			    lfs_dino_getnlink(fs, dp) - lcnt);
    361 			inodirty(VTOI(vp));
    362 		}
    363 	}
    364 }
    365 
    366 static int
    367 mkentry(struct inodesc *idesc)
    368 {
    369 	struct lfs_direct *dirp = idesc->id_dirp;
    370 	struct lfs_direct newent;
    371 	unsigned namlen;
    372 	int newlen, oldlen;
    373 
    374 	namlen = strlen(idesc->id_name);
    375 	lfs_dir_setnamlen(fs, &newent, namlen);
    376 	newlen = LFS_DIRSIZ(fs, &newent);
    377 	if (dirp->d_ino != 0)
    378 		oldlen = LFS_DIRSIZ(fs, dirp);
    379 	else
    380 		oldlen = 0;
    381 	if (dirp->d_reclen - oldlen < newlen)
    382 		return (KEEPON);
    383 	newent.d_reclen = dirp->d_reclen - oldlen;
    384 	dirp->d_reclen = oldlen;
    385 	dirp = (struct lfs_direct *) (((char *) dirp) + oldlen);
    386 	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
    387 	dirp->d_reclen = newent.d_reclen;
    388 	lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]);
    389 	lfs_dir_setnamlen(fs, dirp, namlen);
    390 	memcpy(dirp->d_name, idesc->id_name, (size_t)namlen + 1);
    391 	return (ALTERED | STOP);
    392 }
    393 
    394 static int
    395 chgino(struct inodesc *idesc)
    396 {
    397 	struct lfs_direct *dirp = idesc->id_dirp;
    398 	int namlen;
    399 
    400 	namlen = lfs_dir_getnamlen(fs, dirp);
    401 	if (memcmp(dirp->d_name, idesc->id_name, namlen + 1))
    402 		return (KEEPON);
    403 	dirp->d_ino = idesc->id_parent;
    404 	lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]);
    405 	return (ALTERED | STOP);
    406 }
    407 
    408 int
    409 linkup(ino_t orphan, ino_t parentdir)
    410 {
    411 	union lfs_dinode *dp;
    412 	int lostdir;
    413 	ino_t oldlfdir;
    414 	struct inodesc idesc;
    415 	char tempname[BUFSIZ];
    416 	struct uvnode *vp;
    417 
    418 	memset(&idesc, 0, sizeof(struct inodesc));
    419 	vp = vget(fs, orphan);
    420 	dp = VTOD(vp);
    421 	lostdir = (lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR;
    422 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
    423 	pinode(orphan);
    424 	if (preen && lfs_dino_getsize(fs, dp) == 0)
    425 		return (0);
    426 	if (preen)
    427 		printf(" (RECONNECTED)\n");
    428 	else if (reply("RECONNECT") == 0)
    429 		return (0);
    430 	if (lfdir == 0) {
    431 		dp = ginode(ULFS_ROOTINO);
    432 		idesc.id_name = lfname;
    433 		idesc.id_type = DATA;
    434 		idesc.id_func = findino;
    435 		idesc.id_number = ULFS_ROOTINO;
    436 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
    437 			lfdir = idesc.id_parent;
    438 		} else {
    439 			pwarn("NO lost+found DIRECTORY");
    440 			if (preen || reply("CREATE")) {
    441 				lfdir = allocdir(ULFS_ROOTINO, (ino_t) 0, lfmode);
    442 				if (lfdir != 0) {
    443 					if (makeentry(ULFS_ROOTINO, lfdir, lfname) != 0) {
    444 						if (preen)
    445 							printf(" (CREATED)\n");
    446 					} else {
    447 						freedir(lfdir, ULFS_ROOTINO);
    448 						lfdir = 0;
    449 						if (preen)
    450 							printf("\n");
    451 					}
    452 				}
    453 			}
    454 		}
    455 		if (lfdir == 0) {
    456 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
    457 			printf("\n\n");
    458 			return (0);
    459 		}
    460 	}
    461 	vp = vget(fs, lfdir);
    462 	dp = VTOD(vp);
    463 	if ((lfs_dino_getmode(fs, dp) & LFS_IFMT) != LFS_IFDIR) {
    464 		pfatal("lost+found IS NOT A DIRECTORY");
    465 		if (reply("REALLOCATE") == 0)
    466 			return (0);
    467 		oldlfdir = lfdir;
    468 		if ((lfdir = allocdir(ULFS_ROOTINO, (ino_t) 0, lfmode)) == 0) {
    469 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    470 			return (0);
    471 		}
    472 		if ((changeino(ULFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
    473 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    474 			return (0);
    475 		}
    476 		inodirty(VTOI(vp));
    477 		idesc.id_type = ADDR;
    478 		idesc.id_func = pass4check;
    479 		idesc.id_number = oldlfdir;
    480 		adjust(&idesc, lncntp[oldlfdir] + 1);
    481 		lncntp[oldlfdir] = 0;
    482 		vp = vget(fs, lfdir);
    483 		dp = VTOD(vp);
    484 	}
    485 	if (statemap[lfdir] != DFOUND) {
    486 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
    487 		return (0);
    488 	}
    489 	(void) lftempname(tempname, orphan);
    490 	if (makeentry(lfdir, orphan, tempname) == 0) {
    491 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
    492 		printf("\n\n");
    493 		return (0);
    494 	}
    495 	lncntp[orphan]--;
    496 	if (lostdir) {
    497 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
    498 		    parentdir != (ino_t) - 1)
    499 			(void) makeentry(orphan, lfdir, "..");
    500 		vp = vget(fs, lfdir);
    501 		lfs_dino_setnlink(fs, VTOI(vp)->i_din,
    502 		    lfs_dino_getnlink(fs, VTOI(vp)->i_din) + 1);
    503 		inodirty(VTOI(vp));
    504 		lncntp[lfdir]++;
    505 		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
    506 		if (parentdir != (ino_t) - 1)
    507 			printf("PARENT WAS I=%llu\n",
    508 			    (unsigned long long)parentdir);
    509 		if (preen == 0)
    510 			printf("\n");
    511 	}
    512 	return (1);
    513 }
    514 
    515 /*
    516  * fix an entry in a directory.
    517  */
    518 int
    519 changeino(ino_t dir, const char *name, ino_t newnum)
    520 {
    521 	struct inodesc idesc;
    522 
    523 	memset(&idesc, 0, sizeof(struct inodesc));
    524 	idesc.id_type = DATA;
    525 	idesc.id_func = chgino;
    526 	idesc.id_number = dir;
    527 	idesc.id_fix = DONTKNOW;
    528 	idesc.id_name = name;
    529 	idesc.id_parent = newnum;	/* new value for name */
    530 
    531 	return (ckinode(ginode(dir), &idesc));
    532 }
    533 
    534 /*
    535  * make an entry in a directory
    536  */
    537 int
    538 makeentry(ino_t parent, ino_t ino, const char *name)
    539 {
    540 	union lfs_dinode *dp;
    541 	struct inodesc idesc;
    542 	char pathbuf[MAXPATHLEN + 1];
    543 	struct uvnode *vp;
    544 	uint64_t size;
    545 
    546 	if (parent < ULFS_ROOTINO || parent >= maxino ||
    547 	    ino < ULFS_ROOTINO || ino >= maxino)
    548 		return (0);
    549 	memset(&idesc, 0, sizeof(struct inodesc));
    550 	idesc.id_type = DATA;
    551 	idesc.id_func = mkentry;
    552 	idesc.id_number = parent;
    553 	idesc.id_parent = ino;	/* this is the inode to enter */
    554 	idesc.id_fix = DONTKNOW;
    555 	idesc.id_name = name;
    556 	vp = vget(fs, parent);
    557 	dp = VTOD(vp);
    558 	size = lfs_dino_getsize(fs, dp);
    559 	if (size % LFS_DIRBLKSIZ) {
    560 		size = roundup(size, LFS_DIRBLKSIZ);
    561 		lfs_dino_setsize(fs, dp, size);
    562 		inodirty(VTOI(vp));
    563 	}
    564 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
    565 		return (1);
    566 	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
    567 	vp = vget(fs, parent);
    568 	dp = VTOD(vp);
    569 	if (expanddir(vp, dp, pathbuf) == 0)
    570 		return (0);
    571 	return (ckinode(dp, &idesc) & ALTERED);
    572 }
    573 
    574 /*
    575  * Attempt to expand the size of a directory
    576  */
    577 static int
    578 expanddir(struct uvnode *vp, union lfs_dinode *dp, char *name)
    579 {
    580 	daddr_t lastbn;
    581 	struct ubuf *bp;
    582 	char *cp, firstblk[LFS_DIRBLKSIZ];
    583 
    584 	lastbn = lfs_lblkno(fs, lfs_dino_getsize(fs, dp));
    585 	if (lastbn >= ULFS_NDADDR - 1 || lfs_dino_getdb(fs, dp, lastbn) == 0 ||
    586 	    lfs_dino_getsize(fs, dp) == 0)
    587 		return (0);
    588 	lfs_dino_setdb(fs, dp, lastbn + 1, lfs_dino_getdb(fs, dp, lastbn));
    589 	lfs_dino_setdb(fs, dp, lastbn, 0);
    590 	bp = getblk(vp, lastbn, lfs_sb_getbsize(fs));
    591 	VOP_BWRITE(bp);
    592 	lfs_dino_setsize(fs, dp,
    593 	    lfs_dino_getsize(fs, dp) + lfs_sb_getbsize(fs));
    594 	lfs_dino_setblocks(fs, dp,
    595 	    lfs_dino_getblocks(fs, dp) + lfs_btofsb(fs, lfs_sb_getbsize(fs)));
    596 	bread(vp, lfs_dino_getdb(fs, dp, lastbn + 1),
    597 	    (long) lfs_dblksize(fs, dp, lastbn + 1), 0, &bp);
    598 	if (bp->b_flags & B_ERROR)
    599 		goto bad;
    600 	memcpy(firstblk, bp->b_data, LFS_DIRBLKSIZ);
    601 	bread(vp, lastbn, lfs_sb_getbsize(fs), 0, &bp);
    602 	if (bp->b_flags & B_ERROR)
    603 		goto bad;
    604 	memcpy(bp->b_data, firstblk, LFS_DIRBLKSIZ);
    605 	for (cp = &bp->b_data[LFS_DIRBLKSIZ];
    606 	    cp < &bp->b_data[lfs_sb_getbsize(fs)];
    607 	    cp += LFS_DIRBLKSIZ)
    608 		memcpy(cp, &emptydir, sizeof emptydir);
    609 	VOP_BWRITE(bp);
    610 	bread(vp, lfs_dino_getdb(fs, dp, lastbn + 1),
    611 	    (long) lfs_dblksize(fs, dp, lastbn + 1), 0, &bp);
    612 	if (bp->b_flags & B_ERROR)
    613 		goto bad;
    614 	memcpy(bp->b_data, &emptydir, sizeof emptydir);
    615 	pwarn("NO SPACE LEFT IN %s", name);
    616 	if (preen)
    617 		printf(" (EXPANDED)\n");
    618 	else if (reply("EXPAND") == 0)
    619 		goto bad;
    620 	VOP_BWRITE(bp);
    621 	inodirty(VTOI(vp));
    622 	return (1);
    623 bad:
    624 	lfs_dino_setdb(fs, dp, lastbn, lfs_dino_getdb(fs, dp, lastbn + 1));
    625 	lfs_dino_setdb(fs, dp, lastbn + 1, 0);
    626 	lfs_dino_setsize(fs, dp,
    627 	    lfs_dino_getsize(fs, dp) - lfs_sb_getbsize(fs));
    628 	lfs_dino_setblocks(fs, dp,
    629 	    lfs_dino_getblocks(fs, dp) - lfs_btofsb(fs, lfs_sb_getbsize(fs)));
    630 	return (0);
    631 }
    632 
    633 /*
    634  * allocate a new directory
    635  */
    636 int
    637 allocdir(ino_t parent, ino_t request, int mode)
    638 {
    639 	ino_t ino;
    640 	char *cp;
    641 	union lfs_dinode *dp;
    642 	struct ubuf *bp;
    643 	struct lfs_dirtemplate *dirp;
    644 	struct uvnode *vp;
    645 
    646 	ino = allocino(request, LFS_IFDIR | mode);
    647 	dirp = &dirhead;
    648 	dirp->dot_ino = ino;
    649 	dirp->dotdot_ino = parent;
    650 	vp = vget(fs, ino);
    651 	dp = VTOD(vp);
    652 	bread(vp, lfs_dino_getdb(fs, dp, 0), lfs_sb_getfsize(fs), 0, &bp);
    653 	if (bp->b_flags & B_ERROR) {
    654 		brelse(bp, 0);
    655 		freeino(ino);
    656 		return (0);
    657 	}
    658 	memcpy(bp->b_data, dirp, sizeof(struct lfs_dirtemplate));
    659 	for (cp = &bp->b_data[LFS_DIRBLKSIZ];
    660 	    cp < &bp->b_data[lfs_sb_getfsize(fs)];
    661 	    cp += LFS_DIRBLKSIZ)
    662 		memcpy(cp, &emptydir, sizeof emptydir);
    663 	VOP_BWRITE(bp);
    664 	lfs_dino_setnlink(fs, dp, 2);
    665 	inodirty(VTOI(vp));
    666 	if (ino == ULFS_ROOTINO) {
    667 		lncntp[ino] = lfs_dino_getnlink(fs, dp);
    668 		cacheino(dp, ino);
    669 		return (ino);
    670 	}
    671 	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
    672 		freeino(ino);
    673 		return (0);
    674 	}
    675 	cacheino(dp, ino);
    676 	statemap[ino] = statemap[parent];
    677 	if (statemap[ino] == DSTATE) {
    678 		lncntp[ino] = lfs_dino_getnlink(fs, dp);
    679 		lncntp[parent]++;
    680 	}
    681 	vp = vget(fs, parent);
    682 	dp = VTOD(vp);
    683 	lfs_dino_setnlink(fs, dp, lfs_dino_getnlink(fs, dp) + 1);
    684 	inodirty(VTOI(vp));
    685 	return (ino);
    686 }
    687 
    688 /*
    689  * free a directory inode
    690  */
    691 static void
    692 freedir(ino_t ino, ino_t parent)
    693 {
    694 	struct uvnode *vp;
    695 
    696 	if (ino != parent) {
    697 		vp = vget(fs, parent);
    698 		lfs_dino_setnlink(fs, VTOI(vp)->i_din,
    699 		    lfs_dino_getnlink(fs, VTOI(vp)->i_din) - 1);
    700 		inodirty(VTOI(vp));
    701 	}
    702 	freeino(ino);
    703 }
    704 
    705 /*
    706  * generate a temporary name for the lost+found directory.
    707  */
    708 static int
    709 lftempname(char *bufp, ino_t ino)
    710 {
    711 	ino_t in;
    712 	char *cp;
    713 	int namlen;
    714 
    715 	cp = bufp + 2;
    716 	for (in = maxino; in > 0; in /= 10)
    717 		cp++;
    718 	*--cp = 0;
    719 	namlen = cp - bufp;
    720 	in = ino;
    721 	while (cp > bufp) {
    722 		*--cp = (in % 10) + '0';
    723 		in /= 10;
    724 	}
    725 	*cp = '#';
    726 	return (namlen);
    727 }
    728