Home | History | Annotate | Line # | Download | only in fsck_lfs
dir.c revision 1.45
      1 /* $NetBSD: dir.c,v 1.45 2015/09/21 01:22:18 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 #if 0
     58 struct lfs_dirtemplate emptydir = {
     59 	.dot_ino = 0,
     60 	.dot_reclen = LFS_DIRBLKSIZ,
     61 };
     62 struct lfs_dirtemplate dirhead = {
     63 	.dot_ino = 0,
     64 	.dot_reclen = 12,
     65 	.dot_type = LFS_DT_DIR,
     66 	.dot_namlen = 1,
     67 	.dot_name = ".",
     68 	.dotdot_ino = 0,
     69 	.dotdot_reclen = LFS_DIRBLKSIZ - 12,
     70 	.dotdot_type = LFS_DT_DIR,
     71 	.dotdot_namlen = 2,
     72 	.dotdot_name = ".."
     73 };
     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_dirheader *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_dirheader *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 = lfs_dir_getreclen(fs, dp);
    158 		memcpy(dbuf, dp, (size_t) dsize);
    159 		idesc->id_dirp = (struct lfs_dirheader *) 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_dirheader *
    177 fsck_readdir(struct uvnode *vp, struct inodesc *idesc)
    178 {
    179 	struct lfs_dirheader *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_dirheader *) (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_dirheader *) (bp->b_data + idesc->id_loc);
    196 		lfs_dir_setino(fs, dp, 0);
    197 		lfs_dir_settype(fs, dp, LFS_DT_UNKNOWN);
    198 		lfs_dir_setnamlen(fs, dp, 0);
    199 		lfs_dir_setreclen(fs, dp, LFS_DIRBLKSIZ);
    200 		/* for now at least, don't zero the old contents */
    201 		/*lfs_copydirname(fs, lfs_dir_nameptr(fs, dp), "", 0, LFS_DIRBLKSIZ);*/
    202 		lfs_dir_nameptr(fs, dp)[0] = '\0';
    203 		if (fix)
    204 			VOP_BWRITE(bp);
    205 		else
    206 			brelse(bp, 0);
    207 		idesc->id_loc += LFS_DIRBLKSIZ;
    208 		idesc->id_filesize -= LFS_DIRBLKSIZ;
    209 		return (dp);
    210 	}
    211 dpok:
    212 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) {
    213 		brelse(bp, 0);
    214 		return NULL;
    215 	}
    216 	dploc = idesc->id_loc;
    217 	dp = (struct lfs_dirheader *) (bp->b_data + dploc);
    218 	idesc->id_loc += lfs_dir_getreclen(fs, dp);
    219 	idesc->id_filesize -= lfs_dir_getreclen(fs, dp);
    220 	if ((idesc->id_loc % LFS_DIRBLKSIZ) == 0) {
    221 		brelse(bp, 0);
    222 		return dp;
    223 	}
    224 	ndp = (struct lfs_dirheader *) (bp->b_data + idesc->id_loc);
    225 	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
    226 	    dircheck(idesc, ndp) == 0) {
    227 		brelse(bp, 0);
    228 		size = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ);
    229 		idesc->id_loc += size;
    230 		idesc->id_filesize -= size;
    231 		if (idesc->id_fix == IGNORE)
    232 			return 0;
    233 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
    234 		bread(vp, idesc->id_lblkno, blksiz, 0, &bp);
    235 		dp = (struct lfs_dirheader *) (bp->b_data + dploc);
    236 		lfs_dir_setreclen(fs, dp, lfs_dir_getreclen(fs, dp) + size);
    237 		if (fix)
    238 			VOP_BWRITE(bp);
    239 		else
    240 			brelse(bp, 0);
    241 	} else
    242 		brelse(bp, 0);
    243 
    244 	return (dp);
    245 }
    246 
    247 /*
    248  * Verify that a directory entry is valid.
    249  * This is a superset of the checks made in the kernel.
    250  */
    251 int
    252 dircheck(struct inodesc *idesc, struct lfs_dirheader *dp)
    253 {
    254 	int size;
    255 	const char *cp;
    256 	u_char namlen, type;
    257 	int spaceleft;
    258 
    259 	spaceleft = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ);
    260 	if (lfs_dir_getino(fs, dp) >= maxino ||
    261 	    lfs_dir_getreclen(fs, dp) == 0 ||
    262 	    lfs_dir_getreclen(fs, dp) > spaceleft ||
    263 	    (lfs_dir_getreclen(fs, dp) & 0x3) != 0) {
    264 		pwarn("ino too large, reclen=0, reclen>space, or reclen&3!=0\n");
    265 		pwarn("dp->d_ino = 0x%jx\tdp->d_reclen = 0x%x\n",
    266 		    (uintmax_t)lfs_dir_getino(fs, dp),
    267 		    lfs_dir_getreclen(fs, dp));
    268 		pwarn("maxino = %ju\tspaceleft = 0x%x\n",
    269 		    (uintmax_t)maxino, spaceleft);
    270 		return (0);
    271 	}
    272 	if (lfs_dir_getino(fs, dp) == 0)
    273 		return (1);
    274 	size = LFS_DIRSIZ(fs, dp);
    275 	namlen = lfs_dir_getnamlen(fs, dp);
    276 	type = lfs_dir_gettype(fs, dp);
    277 	if (lfs_dir_getreclen(fs, dp) < size ||
    278 	    idesc->id_filesize < size ||
    279 	/* namlen > MAXNAMLEN || */
    280 	    type > 15) {
    281 		printf("reclen<size, filesize<size, namlen too large, or type>15\n");
    282 		return (0);
    283 	}
    284 	cp = lfs_dir_nameptr(fs, dp);
    285 	for (size = 0; size < namlen; size++)
    286 		if (*cp == '\0' || (*cp++ == '/')) {
    287 			printf("name contains NUL or /\n");
    288 			return (0);
    289 		}
    290 	if (*cp != '\0') {
    291 		printf("name size misstated\n");
    292 		return (0);
    293 	}
    294 	return (1);
    295 }
    296 
    297 void
    298 direrror(ino_t ino, const char *errmesg)
    299 {
    300 
    301 	fileerror(ino, ino, errmesg);
    302 }
    303 
    304 void
    305 fileerror(ino_t cwd, ino_t ino, const char *errmesg)
    306 {
    307 	char pathbuf[MAXPATHLEN + 1];
    308 	struct uvnode *vp;
    309 
    310 	pwarn("%s ", errmesg);
    311 	pinode(ino);
    312 	printf("\n");
    313 	pwarn("PARENT=%lld\n", (long long)cwd);
    314 	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
    315 	if (ino < ULFS_ROOTINO || ino >= maxino) {
    316 		pfatal("NAME=%s\n", pathbuf);
    317 		return;
    318 	}
    319 	vp = vget(fs, ino);
    320 	if (vp == NULL)
    321 		pfatal("INO is NULL\n");
    322 	else {
    323 		if (ftypeok(VTOD(vp)))
    324 			pfatal("%s=%s\n",
    325 			    (lfs_dino_getmode(fs, VTOI(vp)->i_din) & LFS_IFMT) == LFS_IFDIR ?
    326 			    "DIR" : "FILE", pathbuf);
    327 		else
    328 			pfatal("NAME=%s\n", pathbuf);
    329 	}
    330 }
    331 
    332 void
    333 adjust(struct inodesc *idesc, short lcnt)
    334 {
    335 	struct uvnode *vp;
    336 	union lfs_dinode *dp;
    337 
    338 	/*
    339 	 * XXX: (1) since lcnt is apparently a delta, rename it; (2)
    340 	 * why is it a value to *subtract*? that is unnecessarily
    341 	 * confusing.
    342 	 */
    343 
    344 	vp = vget(fs, idesc->id_number);
    345 	dp = VTOD(vp);
    346 	if (lfs_dino_getnlink(fs, dp) == lcnt) {
    347 		if (linkup(idesc->id_number, (ino_t) 0) == 0)
    348 			clri(idesc, "UNREF", 0);
    349 	} else {
    350 		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
    351 		    ((lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR ? "DIR" : "FILE"));
    352 		pinode(idesc->id_number);
    353 		printf(" COUNT %d SHOULD BE %d",
    354 		    lfs_dino_getnlink(fs, dp), lfs_dino_getnlink(fs, dp) - lcnt);
    355 		if (preen) {
    356 			if (lcnt < 0) {
    357 				printf("\n");
    358 				pfatal("LINK COUNT INCREASING");
    359 			}
    360 			printf(" (ADJUSTED)\n");
    361 		}
    362 		if (preen || reply("ADJUST") == 1) {
    363 			lfs_dino_setnlink(fs, dp,
    364 			    lfs_dino_getnlink(fs, dp) - lcnt);
    365 			inodirty(VTOI(vp));
    366 		}
    367 	}
    368 }
    369 
    370 static int
    371 mkentry(struct inodesc *idesc)
    372 {
    373 	struct lfs_dirheader *dirp = idesc->id_dirp;
    374 	unsigned namlen;
    375 	unsigned newreclen, oldreclen;
    376 
    377 	/* figure the length needed for id_name */
    378 	namlen = strlen(idesc->id_name);
    379 	newreclen = LFS_DIRECTSIZ(fs, namlen);
    380 
    381 	/* find the minimum record length for the existing name */
    382 	if (lfs_dir_getino(fs, dirp) != 0)
    383 		oldreclen = LFS_DIRSIZ(fs, dirp);
    384 	else
    385 		oldreclen = 0;
    386 
    387 	/* Can we insert here? */
    388 	if (lfs_dir_getreclen(fs, dirp) - oldreclen < newreclen)
    389 		return (KEEPON);
    390 
    391 	/* Divide the record; all but oldreclen goes to the new record */
    392 	newreclen = lfs_dir_getreclen(fs, dirp) - oldreclen;
    393 	lfs_dir_setreclen(fs, dirp, oldreclen);
    394 
    395 	/* advance the pointer to the new record */
    396 	dirp = LFS_NEXTDIR(fs, dirp);
    397 
    398 	/* write record; ino to be entered is in id_parent */
    399 	lfs_dir_setino(fs, dirp, idesc->id_parent);
    400 	lfs_dir_setreclen(fs, dirp, newreclen);
    401 	lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]);
    402 	lfs_dir_setnamlen(fs, dirp, namlen);
    403 	lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), idesc->id_name,
    404 			namlen, newreclen);
    405 
    406 	return (ALTERED | STOP);
    407 }
    408 
    409 static int
    410 chgino(struct inodesc *idesc)
    411 {
    412 	struct lfs_dirheader *dirp = idesc->id_dirp;
    413 	int namlen;
    414 
    415 	namlen = lfs_dir_getnamlen(fs, dirp);
    416 	if (memcmp(lfs_dir_nameptr(fs, dirp), idesc->id_name, namlen + 1))
    417 		return (KEEPON);
    418 	lfs_dir_setino(fs, dirp, idesc->id_parent);
    419 	lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]);
    420 	return (ALTERED | STOP);
    421 }
    422 
    423 int
    424 linkup(ino_t orphan, ino_t parentdir)
    425 {
    426 	union lfs_dinode *dp;
    427 	int lostdir;
    428 	ino_t oldlfdir;
    429 	struct inodesc idesc;
    430 	char tempname[BUFSIZ];
    431 	struct uvnode *vp;
    432 
    433 	memset(&idesc, 0, sizeof(struct inodesc));
    434 	vp = vget(fs, orphan);
    435 	dp = VTOD(vp);
    436 	lostdir = (lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR;
    437 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
    438 	pinode(orphan);
    439 	if (preen && lfs_dino_getsize(fs, dp) == 0)
    440 		return (0);
    441 	if (preen)
    442 		printf(" (RECONNECTED)\n");
    443 	else if (reply("RECONNECT") == 0)
    444 		return (0);
    445 	if (lfdir == 0) {
    446 		dp = ginode(ULFS_ROOTINO);
    447 		idesc.id_name = lfname;
    448 		idesc.id_type = DATA;
    449 		idesc.id_func = findino;
    450 		idesc.id_number = ULFS_ROOTINO;
    451 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
    452 			lfdir = idesc.id_parent;
    453 		} else {
    454 			pwarn("NO lost+found DIRECTORY");
    455 			if (preen || reply("CREATE")) {
    456 				lfdir = allocdir(ULFS_ROOTINO, (ino_t) 0, lfmode);
    457 				if (lfdir != 0) {
    458 					if (makeentry(ULFS_ROOTINO, lfdir, lfname) != 0) {
    459 						if (preen)
    460 							printf(" (CREATED)\n");
    461 					} else {
    462 						freedir(lfdir, ULFS_ROOTINO);
    463 						lfdir = 0;
    464 						if (preen)
    465 							printf("\n");
    466 					}
    467 				}
    468 			}
    469 		}
    470 		if (lfdir == 0) {
    471 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
    472 			printf("\n\n");
    473 			return (0);
    474 		}
    475 	}
    476 	vp = vget(fs, lfdir);
    477 	dp = VTOD(vp);
    478 	if ((lfs_dino_getmode(fs, dp) & LFS_IFMT) != LFS_IFDIR) {
    479 		pfatal("lost+found IS NOT A DIRECTORY");
    480 		if (reply("REALLOCATE") == 0)
    481 			return (0);
    482 		oldlfdir = lfdir;
    483 		if ((lfdir = allocdir(ULFS_ROOTINO, (ino_t) 0, lfmode)) == 0) {
    484 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    485 			return (0);
    486 		}
    487 		if ((changeino(ULFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
    488 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
    489 			return (0);
    490 		}
    491 		inodirty(VTOI(vp));
    492 		idesc.id_type = ADDR;
    493 		idesc.id_func = pass4check;
    494 		idesc.id_number = oldlfdir;
    495 		adjust(&idesc, lncntp[oldlfdir] + 1);
    496 		lncntp[oldlfdir] = 0;
    497 		vp = vget(fs, lfdir);
    498 		dp = VTOD(vp);
    499 	}
    500 	if (statemap[lfdir] != DFOUND) {
    501 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
    502 		return (0);
    503 	}
    504 	(void) lftempname(tempname, orphan);
    505 	if (makeentry(lfdir, orphan, tempname) == 0) {
    506 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
    507 		printf("\n\n");
    508 		return (0);
    509 	}
    510 	lncntp[orphan]--;
    511 	if (lostdir) {
    512 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
    513 		    parentdir != (ino_t) - 1)
    514 			(void) makeentry(orphan, lfdir, "..");
    515 		vp = vget(fs, lfdir);
    516 		lfs_dino_setnlink(fs, VTOI(vp)->i_din,
    517 		    lfs_dino_getnlink(fs, VTOI(vp)->i_din) + 1);
    518 		inodirty(VTOI(vp));
    519 		lncntp[lfdir]++;
    520 		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
    521 		if (parentdir != (ino_t) - 1)
    522 			printf("PARENT WAS I=%llu\n",
    523 			    (unsigned long long)parentdir);
    524 		if (preen == 0)
    525 			printf("\n");
    526 	}
    527 	return (1);
    528 }
    529 
    530 /*
    531  * fix an entry in a directory.
    532  */
    533 int
    534 changeino(ino_t dir, const char *name, ino_t newnum)
    535 {
    536 	struct inodesc idesc;
    537 
    538 	memset(&idesc, 0, sizeof(struct inodesc));
    539 	idesc.id_type = DATA;
    540 	idesc.id_func = chgino;
    541 	idesc.id_number = dir;
    542 	idesc.id_fix = DONTKNOW;
    543 	idesc.id_name = name;
    544 	idesc.id_parent = newnum;	/* new value for name */
    545 
    546 	return (ckinode(ginode(dir), &idesc));
    547 }
    548 
    549 /*
    550  * make an entry in a directory
    551  */
    552 int
    553 makeentry(ino_t parent, ino_t ino, const char *name)
    554 {
    555 	union lfs_dinode *dp;
    556 	struct inodesc idesc;
    557 	char pathbuf[MAXPATHLEN + 1];
    558 	struct uvnode *vp;
    559 	uint64_t size;
    560 
    561 	if (parent < ULFS_ROOTINO || parent >= maxino ||
    562 	    ino < ULFS_ROOTINO || ino >= maxino)
    563 		return (0);
    564 	memset(&idesc, 0, sizeof(struct inodesc));
    565 	idesc.id_type = DATA;
    566 	idesc.id_func = mkentry;
    567 	idesc.id_number = parent;
    568 	idesc.id_parent = ino;	/* this is the inode to enter */
    569 	idesc.id_fix = DONTKNOW;
    570 	idesc.id_name = name;
    571 	vp = vget(fs, parent);
    572 	dp = VTOD(vp);
    573 	size = lfs_dino_getsize(fs, dp);
    574 	if (size % LFS_DIRBLKSIZ) {
    575 		size = roundup(size, LFS_DIRBLKSIZ);
    576 		lfs_dino_setsize(fs, dp, size);
    577 		inodirty(VTOI(vp));
    578 	}
    579 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
    580 		return (1);
    581 	getpathname(pathbuf, sizeof(pathbuf), parent, parent);
    582 	vp = vget(fs, parent);
    583 	dp = VTOD(vp);
    584 	if (expanddir(vp, dp, pathbuf) == 0)
    585 		return (0);
    586 	return (ckinode(dp, &idesc) & ALTERED);
    587 }
    588 
    589 /*
    590  * Initialize a completely empty directory block.
    591  * (block size is LFS_DIRBLKSIZ)
    592  */
    593 static void
    594 zerodirblk(void *buf)
    595 {
    596 	struct lfs_dirheader *dirp;
    597 
    598 	dirp = buf;
    599 	lfs_dir_setino(fs, dirp, 0);
    600 	lfs_dir_setreclen(fs, dirp, LFS_DIRBLKSIZ);
    601 	lfs_dir_settype(fs, dirp, LFS_DT_UNKNOWN);
    602 	lfs_dir_setnamlen(fs, dirp, 0);
    603 	lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "", 0,
    604 			LFS_DIRBLKSIZ);
    605 }
    606 
    607 /*
    608  * Attempt to expand the size of a directory
    609  */
    610 static int
    611 expanddir(struct uvnode *vp, union lfs_dinode *dp, char *name)
    612 {
    613 	daddr_t lastbn;
    614 	struct ubuf *bp;
    615 	char *cp, firstblk[LFS_DIRBLKSIZ];
    616 
    617 	lastbn = lfs_lblkno(fs, lfs_dino_getsize(fs, dp));
    618 	if (lastbn >= ULFS_NDADDR - 1 || lfs_dino_getdb(fs, dp, lastbn) == 0 ||
    619 	    lfs_dino_getsize(fs, dp) == 0)
    620 		return (0);
    621 	lfs_dino_setdb(fs, dp, lastbn + 1, lfs_dino_getdb(fs, dp, lastbn));
    622 	lfs_dino_setdb(fs, dp, lastbn, 0);
    623 	bp = getblk(vp, lastbn, lfs_sb_getbsize(fs));
    624 	VOP_BWRITE(bp);
    625 	lfs_dino_setsize(fs, dp,
    626 	    lfs_dino_getsize(fs, dp) + lfs_sb_getbsize(fs));
    627 	lfs_dino_setblocks(fs, dp,
    628 	    lfs_dino_getblocks(fs, dp) + lfs_btofsb(fs, lfs_sb_getbsize(fs)));
    629 	bread(vp, lfs_dino_getdb(fs, dp, lastbn + 1),
    630 	    (long) lfs_dblksize(fs, dp, lastbn + 1), 0, &bp);
    631 	if (bp->b_flags & B_ERROR)
    632 		goto bad;
    633 	memcpy(firstblk, bp->b_data, LFS_DIRBLKSIZ);
    634 	bread(vp, lastbn, lfs_sb_getbsize(fs), 0, &bp);
    635 	if (bp->b_flags & B_ERROR)
    636 		goto bad;
    637 	memcpy(bp->b_data, firstblk, LFS_DIRBLKSIZ);
    638 	for (cp = &bp->b_data[LFS_DIRBLKSIZ];
    639 	    cp < &bp->b_data[lfs_sb_getbsize(fs)];
    640 	    cp += LFS_DIRBLKSIZ)
    641 		zerodirblk(cp);
    642 	VOP_BWRITE(bp);
    643 	bread(vp, lfs_dino_getdb(fs, dp, lastbn + 1),
    644 	    (long) lfs_dblksize(fs, dp, lastbn + 1), 0, &bp);
    645 	if (bp->b_flags & B_ERROR)
    646 		goto bad;
    647 	zerodirblk(bp->b_data);
    648 	pwarn("NO SPACE LEFT IN %s", name);
    649 	if (preen)
    650 		printf(" (EXPANDED)\n");
    651 	else if (reply("EXPAND") == 0)
    652 		goto bad;
    653 	VOP_BWRITE(bp);
    654 	inodirty(VTOI(vp));
    655 	return (1);
    656 bad:
    657 	lfs_dino_setdb(fs, dp, lastbn, lfs_dino_getdb(fs, dp, lastbn + 1));
    658 	lfs_dino_setdb(fs, dp, lastbn + 1, 0);
    659 	lfs_dino_setsize(fs, dp,
    660 	    lfs_dino_getsize(fs, dp) - lfs_sb_getbsize(fs));
    661 	lfs_dino_setblocks(fs, dp,
    662 	    lfs_dino_getblocks(fs, dp) - lfs_btofsb(fs, lfs_sb_getbsize(fs)));
    663 	return (0);
    664 }
    665 
    666 /*
    667  * allocate a new directory
    668  */
    669 int
    670 allocdir(ino_t parent, ino_t request, int mode)
    671 {
    672 	ino_t ino;
    673 	char *cp;
    674 	union lfs_dinode *dp;
    675 	struct ubuf *bp;
    676 	struct lfs_dirheader *dirp;
    677 	struct uvnode *vp;
    678 
    679 	ino = allocino(request, LFS_IFDIR | mode);
    680 	vp = vget(fs, ino);
    681 	dp = VTOD(vp);
    682 	bread(vp, lfs_dino_getdb(fs, dp, 0), lfs_sb_getfsize(fs), 0, &bp);
    683 	if (bp->b_flags & B_ERROR) {
    684 		brelse(bp, 0);
    685 		freeino(ino);
    686 		return (0);
    687 	}
    688 	dirp = (struct lfs_dirheader *)bp->b_data;
    689 	/* . */
    690 	lfs_dir_setino(fs, dirp, ino);
    691 	lfs_dir_setreclen(fs, dirp, LFS_DIRECTSIZ(fs, 1));
    692 	lfs_dir_settype(fs, dirp, LFS_DT_DIR);
    693 	lfs_dir_setnamlen(fs, dirp, 1);
    694 	lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1,
    695 			LFS_DIRECTSIZ(fs, 1));
    696 	/* .. */
    697 	dirp = LFS_NEXTDIR(fs, dirp);
    698 	lfs_dir_setino(fs, dirp, parent);
    699 	lfs_dir_setreclen(fs, dirp, LFS_DIRBLKSIZ - LFS_DIRECTSIZ(fs, 1));
    700 	lfs_dir_settype(fs, dirp, LFS_DT_DIR);
    701 	lfs_dir_setnamlen(fs, dirp, 2);
    702 	lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "..", 2,
    703 			LFS_DIRBLKSIZ - LFS_DIRECTSIZ(fs, 1));
    704 	for (cp = &bp->b_data[LFS_DIRBLKSIZ];
    705 	    cp < &bp->b_data[lfs_sb_getfsize(fs)];
    706 	    cp += LFS_DIRBLKSIZ) {
    707 		zerodirblk(cp);
    708 	}
    709 	VOP_BWRITE(bp);
    710 	lfs_dino_setnlink(fs, dp, 2);
    711 	inodirty(VTOI(vp));
    712 	if (ino == ULFS_ROOTINO) {
    713 		lncntp[ino] = lfs_dino_getnlink(fs, dp);
    714 		cacheino(dp, ino);
    715 		return (ino);
    716 	}
    717 	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
    718 		freeino(ino);
    719 		return (0);
    720 	}
    721 	cacheino(dp, ino);
    722 	statemap[ino] = statemap[parent];
    723 	if (statemap[ino] == DSTATE) {
    724 		lncntp[ino] = lfs_dino_getnlink(fs, dp);
    725 		lncntp[parent]++;
    726 	}
    727 	vp = vget(fs, parent);
    728 	dp = VTOD(vp);
    729 	lfs_dino_setnlink(fs, dp, lfs_dino_getnlink(fs, dp) + 1);
    730 	inodirty(VTOI(vp));
    731 	return (ino);
    732 }
    733 
    734 /*
    735  * free a directory inode
    736  */
    737 static void
    738 freedir(ino_t ino, ino_t parent)
    739 {
    740 	struct uvnode *vp;
    741 
    742 	if (ino != parent) {
    743 		vp = vget(fs, parent);
    744 		lfs_dino_setnlink(fs, VTOI(vp)->i_din,
    745 		    lfs_dino_getnlink(fs, VTOI(vp)->i_din) - 1);
    746 		inodirty(VTOI(vp));
    747 	}
    748 	freeino(ino);
    749 }
    750 
    751 /*
    752  * generate a temporary name for the lost+found directory.
    753  */
    754 static int
    755 lftempname(char *bufp, ino_t ino)
    756 {
    757 	ino_t in;
    758 	char *cp;
    759 	int namlen;
    760 
    761 	cp = bufp + 2;
    762 	for (in = maxino; in > 0; in /= 10)
    763 		cp++;
    764 	*--cp = 0;
    765 	namlen = cp - bufp;
    766 	in = ino;
    767 	while (cp > bufp) {
    768 		*--cp = (in % 10) + '0';
    769 		in /= 10;
    770 	}
    771 	*cp = '#';
    772 	return (namlen);
    773 }
    774