xref: /titanic_52/usr/src/cmd/fs.d/ufs/fsck/dir.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/sysmacros.h>
33 #include <sys/mntent.h>
34 
35 #define	bcopy(f, t, n)    memcpy(t, f, n)
36 #define	bzero(s, n)	memset(s, 0, n)
37 #define	bcmp(s, d, n)	memcmp(s, d, n)
38 
39 #define	index(s, r)	strchr(s, r)
40 #define	rindex(s, r)	strrchr(s, r)
41 
42 #include <sys/fs/ufs_fs.h>
43 #include <sys/vnode.h>
44 #include <sys/fs/ufs_inode.h>
45 #define	_KERNEL
46 #include <sys/fs/ufs_fsdir.h>
47 #undef _KERNEL
48 #include "fsck.h"
49 
50 char	*lfname = "lost+found";
51 int	lfmode = 01700;
52 struct	dirtemplate emptydir = { 0, DIRBLKSIZ };
53 struct	dirtemplate dirhead = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." };
54 
55 struct direct	*fsck_readdir();
56 struct bufarea	*getdirblk(daddr32_t, int);
57 
58 /*
59  * Propagate connected state through the tree.
60  */
61 propagate()
62 {
63 	struct inoinfo **inpp, *inp;
64 	struct inoinfo **inpend;
65 	int change;
66 
67 	inpend = &inpsort[inplast];
68 	do {
69 		change = 0;
70 		for (inpp = inpsort; inpp < inpend; inpp++) {
71 			inp = *inpp;
72 			if (inp->i_parent == 0)
73 				continue;
74 			if (statemap[inp->i_parent] == DFOUND &&
75 			    statemap[inp->i_number] == DSTATE) {
76 				statemap[inp->i_number] = DFOUND;
77 				change++;
78 			}
79 		}
80 	} while (change > 0);
81 }
82 
83 /*
84  * Scan each entry in a directory block.
85  */
86 dirscan(idesc)
87 	struct inodesc *idesc;
88 {
89 	struct direct *dp;
90 	struct bufarea *bp;
91 	int dsize, n;
92 	int blksiz;
93 	char dbuf[DIRBLKSIZ];
94 
95 	if (idesc->id_type != DATA)
96 		errexit("wrong type to dirscan %d\n", idesc->id_type);
97 	if (idesc->id_entryno == 0 &&
98 	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
99 		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
100 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
101 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
102 		idesc->id_filesize -= (offset_t)blksiz;
103 		return (SKIP);
104 	}
105 	idesc->id_loc = 0;
106 	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
107 		/*
108 		 * If we were just passed a corrupt directory entry with
109 		 * d_reclen > DIRBLKSIZ so we don't bcopy() all over our stack.
110 		 * This directory gets cleaned up later.
111 		 */
112 		dsize = MIN(dp->d_reclen, DIRBLKSIZ);
113 		bcopy((char *)dp, dbuf, dsize);
114 		idesc->id_dirp = (struct direct *)dbuf;
115 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
116 			bp = getdirblk(idesc->id_blkno, blksiz);
117 			bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize,
118 			    dsize);
119 			dirty(bp);
120 			sbdirty();
121 		}
122 		if (n & STOP)
123 			return (n);
124 	}
125 	return (idesc->id_filesize > 0 ? KEEPON : STOP);
126 }
127 
128 /*
129  * Get current entry in a directory (and peek at the next entry).
130  */
131 struct direct *
132 fsck_readdir(idesc)
133 	struct inodesc *idesc;
134 {
135 	struct direct *dp, *ndp = 0;
136 	struct bufarea *bp;
137 	int size, blksiz;
138 	int dofixret;
139 	int origloc	= idesc->id_loc;
140 
141 	blksiz = idesc->id_numfrags * sblock.fs_fsize;
142 	/*
143 	 * Sanity check id_filesize and id_loc fields.
144 	 */
145 	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
146 		return (NULL);
147 
148 	bp = getdirblk(idesc->id_blkno, blksiz);
149 	dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
150 
151 	/*
152 	 * Check the current entry in the directory.
153 	 */
154 	if (dircheck(idesc, dp) == 0) {
155 		/*
156 		 * If we are in here, then either the current directory
157 		 * entry is bad or the next directory entry is bad.
158 		 */
159 next_is_bad:
160 		/*
161 		 * Find the amount of space left to the end of the
162 		 * directory block for either directory entry.
163 		 */
164 		size = DIRBLKSIZ - (idesc->id_loc & (DIRBLKSIZ - 1));
165 
166 		/*
167 		 * Advance to the end of the directory block.
168 		 */
169 		idesc->id_loc += size;
170 		idesc->id_filesize -= (offset_t)size;
171 
172 		/*
173 		 * Ask the question before we fix the in-core directory
174 		 * block because dofix() may reuse the buffer.
175 		 */
176 		dofixret = dofix(idesc, "DIRECTORY CORRUPTED");
177 		bp = getdirblk(idesc->id_blkno, blksiz);
178 		dp = (struct direct *)(bp->b_un.b_buf + origloc);
179 
180 		/*
181 		 * This is the current directory entry and since it is
182 		 * corrupt we cannot trust the rest of the directory
183 		 * block so change the current directory entry to
184 		 * contain nothing and encompass the rest of the block.
185 		 */
186 		if (ndp == NULL) {
187 			dp->d_reclen = size;
188 			dp->d_ino = 0;
189 			dp->d_namlen = 0;
190 			dp->d_name[0] = '\0';
191 		}
192 		/*
193 		 * This is the next directory entry, i.e., we got here
194 		 * via a "goto next_is_bad".  This directory entry is
195 		 * corrupt.  The current directory entry is okay so, if we
196 		 * are in fix mode, extend just its record size to encompass
197 		 * the rest of the block.
198 		 */
199 		else if (dofixret) {
200 			dp->d_reclen += size;
201 		}
202 		/*
203 		 * If the user said to fix the directory corruption, then
204 		 * mark the block as dirty.  Otherwise, our "repairs" only
205 		 * apply to the in-core copy so we don't hand back trash
206 		 * to the caller.
207 		 *
208 		 * Note: It is possible that saying "no" to a change in
209 		 * one part of the I/O buffer and "yes" to a later change
210 		 * in the same I/O buffer may still flush the change to
211 		 * which we said "no". This is the pathalogical case and
212 		 * no fix is planned at this time.
213 		 */
214 		if (dofixret)
215 			dirty(bp);
216 		return (dp);
217 	}
218 	/*
219 	 * The current directory entry checked out so advance past it.
220 	 */
221 	idesc->id_loc += dp->d_reclen;
222 	idesc->id_filesize -= (offset_t)dp->d_reclen;
223 
224 	/*
225 	 * If we are not at the directory block boundary, then peek
226 	 * at the next directory entry and if it is bad we can add
227 	 * its space to the current directory entry (compression).
228 	 * Again, we sanity check the id_loc and id_filesize fields
229 	 * since we modified them above.
230 	 */
231 	if ((idesc->id_loc & (DIRBLKSIZ - 1)) &&
232 	    idesc->id_loc < blksiz && idesc->id_filesize > 0) {
233 		ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
234 		if (dircheck(idesc, ndp) == 0)
235 			goto next_is_bad;
236 	}
237 
238 	return (dp);
239 }
240 
241 /*
242  * Verify that a directory entry is valid.
243  * This is a superset of the checks made in the kernel.
244  */
245 dircheck(idesc, dp)
246 	struct inodesc *idesc;
247 	struct direct *dp;
248 {
249 	int size;
250 	char *cp;
251 	int spaceleft;
252 
253 	size = DIRSIZ(dp);
254 	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
255 	if (dp->d_ino < maxino &&
256 	    dp->d_reclen != 0 &&
257 	    (int)dp->d_reclen <= spaceleft &&
258 	    (dp->d_reclen & 0x3) == 0 &&
259 	    (int)dp->d_reclen >= size &&
260 	    idesc->id_filesize >= (offset_t)size &&
261 	    dp->d_namlen <= MAXNAMLEN) {
262 		if (dp->d_ino == 0)
263 			return (1);
264 		for (cp = dp->d_name, size = 0; size < (int)dp->d_namlen;
265 								size++, cp++)
266 			if ((*cp == 0) || (*cp == '/'))
267 				return (0);
268 		if (*cp == 0)
269 			return (1);
270 	}
271 	return (0);
272 }
273 
274 direrror(ino, errmesg)
275 	ino_t ino;
276 	char *errmesg;
277 {
278 
279 	fileerror(ino, ino, errmesg);
280 }
281 
282 fileerror(cwd, ino, errmesg)
283 	ino_t cwd, ino;
284 	char *errmesg;
285 {
286 	struct dinode *dp;
287 	char pathbuf[MAXPATHLEN + 1];
288 
289 	pwarn("%s ", errmesg);
290 	pinode(ino);
291 	printf("\n");
292 	getpathname(pathbuf, cwd, ino);
293 	if (ino < UFSROOTINO || ino > maxino) {
294 		pfatal("NAME=%s\n", pathbuf);
295 		return;
296 	}
297 	dp = ginode(ino);
298 	if (ftypeok(dp))
299 		pfatal("%s=%s\n",
300 		    (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
301 	else
302 		pfatal("NAME=%s\n", pathbuf);
303 }
304 
305 adjust(idesc, lcnt)
306 	struct inodesc *idesc;
307 	short lcnt;
308 {
309 	struct dinode *dp;
310 
311 	dp = ginode(idesc->id_number);
312 	if (dp->di_nlink == lcnt) {
313 		if (linkup(idesc->id_number, (ino_t)0) == 0) {
314 			clri(idesc, "UNREF", 0);
315 			return;
316 		}
317 		lcnt = (short)lncntp[idesc->id_number];
318 	}
319 	if (lcnt && dp->di_nlink != lcnt) {
320 		pwarn("LINK COUNT %s",
321 		    (lfdir == idesc->id_number) ? lfname :
322 		    (dp->di_mode & IFMT) == IFDIR ? "DIR" :
323 		    (dp->di_mode & IFMT) == IFATTRDIR ? "ATTR DIR" :
324 		    (dp->di_mode & IFMT) == IFSHAD ? "ACL" : "FILE");
325 		pinode(idesc->id_number);
326 		printf(" COUNT %d SHOULD BE %d",
327 		    dp->di_nlink, dp->di_nlink - lcnt);
328 		if (preen) {
329 			if (lcnt < 0) {
330 				printf("\n");
331 				if ((dp->di_mode & IFMT) == IFSHAD)
332 					pwarn("LINK COUNT INCREASING");
333 				else
334 					pfatal("LINK COUNT INCREASING");
335 			}
336 			printf(" (ADJUSTED)\n");
337 		}
338 		if (preen || reply("ADJUST") == 1) {
339 			dp->di_nlink -= lcnt;
340 			inodirty();
341 		}
342 	}
343 }
344 
345 mkentry(idesc)
346 	struct inodesc *idesc;
347 {
348 	struct direct *dirp = idesc->id_dirp;
349 	struct direct newent;
350 	int newlen, oldlen;
351 
352 	newent.d_namlen = strlen(idesc->id_name);
353 	newlen = DIRSIZ(&newent);
354 	if (dirp->d_ino != 0)
355 		oldlen = DIRSIZ(dirp);
356 	else
357 		oldlen = 0;
358 	if ((int)dirp->d_reclen - oldlen < newlen)
359 		return (KEEPON);
360 	newent.d_reclen = dirp->d_reclen - (ushort_t)oldlen;
361 	dirp->d_reclen = (ushort_t)oldlen;
362 	dirp = (struct direct *)(((char *)dirp) + oldlen);
363 	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
364 	dirp->d_reclen = newent.d_reclen;
365 	dirp->d_namlen = newent.d_namlen;
366 	bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
367 	return (ALTERED|STOP);
368 }
369 
370 chgino(idesc)
371 	struct inodesc *idesc;
372 {
373 	struct direct *dirp = idesc->id_dirp;
374 
375 	if (bcmp(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1))
376 		return (KEEPON);
377 	dirp->d_ino = idesc->id_parent;
378 	return (ALTERED|STOP);
379 }
380 
381 linkup(orphan, parentdir)
382 	ino_t orphan;
383 	ino_t parentdir;
384 {
385 	struct dinode *dp;
386 	int lostdir;
387 	int lostshadow;
388 	int lostattrdir;
389 	ino_t oldlfdir;
390 	struct inodesc idesc;
391 	char tempname[BUFSIZ];
392 	extern int pass4check();
393 
394 	bzero((char *)&idesc, sizeof (struct inodesc));
395 	dp = ginode(orphan);
396 	lostdir = (((dp->di_mode & IFMT) == IFDIR) ||
397 	    ((dp->di_mode & IFMT) == IFATTRDIR));
398 	/*
399 	 * reclaim thread will remove this dir at mount
400 	 */
401 	if (lostdir &&
402 	    (willreclaim && dp->di_nlink <= 0 && lncntp[orphan] == -1)) {
403 		if (parentdir && lncntp[parentdir] < 0)
404 			++lncntp[parentdir];
405 		return (0);
406 	}
407 	lostshadow = (dp->di_mode & IFMT) == IFSHAD;
408 	lostattrdir = (dp->di_mode & IFMT) == IFATTRDIR;
409 	pwarn("UNREF %s ", lostattrdir ? "ATTRDIR" : lostdir ? "DIR" :
410 	    lostshadow ? "ACL" : "FILE");
411 	pinode(orphan);
412 	if (lostshadow || (dp->di_size == 0 && dp->di_oeftflag == 0))
413 		return (0);
414 	if (preen)
415 		printf(" (RECONNECTED)\n");
416 	else
417 		if (reply("RECONNECT") == 0)
418 			return (0);
419 	if (lfdir == 0) {
420 		dp = ginode(UFSROOTINO);
421 		idesc.id_name = lfname;
422 		idesc.id_type = DATA;
423 		idesc.id_func = findino;
424 		idesc.id_number = UFSROOTINO;
425 		idesc.id_fix = DONTKNOW;
426 		if ((ckinode(dp, &idesc) & FOUND) != 0) {
427 			lfdir = idesc.id_parent;
428 		} else {
429 			pwarn("NO lost+found DIRECTORY");
430 			if (preen || reply("CREATE")) {
431 				lfdir = allocdir(UFSROOTINO, (ino_t)0, lfmode);
432 				if (lfdir != 0) {
433 					if (makeentry(UFSROOTINO, lfdir,
434 					    lfname) != 0) {
435 						if (preen)
436 							printf(" (CREATED)\n");
437 					} else {
438 						freedir(lfdir, UFSROOTINO);
439 						lfdir = 0;
440 						if (preen)
441 							printf("\n");
442 					}
443 				}
444 			}
445 		}
446 		if (lfdir == 0) {
447 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
448 			printf("\n\n");
449 			return (0);
450 		}
451 	} else {	/* lost+found already created */
452 		idesc.id_name = lfname;
453 	}
454 	dp = ginode(lfdir);
455 	if ((dp->di_mode & IFMT) != IFDIR) {
456 		pfatal("lost+found IS NOT A DIRECTORY");
457 		if (reply("REALLOCATE") == 0)
458 			return (0);
459 		oldlfdir = lfdir;
460 		if ((lfdir = allocdir(UFSROOTINO, (ino_t)0, lfmode)) == 0) {
461 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
462 			return (0);
463 		}
464 		if ((changeino(UFSROOTINO, lfname, lfdir) & ALTERED) == 0) {
465 			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
466 			return (0);
467 		}
468 		inodirty();
469 		idesc.id_type = ADDR;
470 		idesc.id_func = pass4check;
471 		idesc.id_number = oldlfdir;
472 		idesc.id_fix = DONTKNOW;
473 		lncntp[oldlfdir]++;
474 		adjust(&idesc, lncntp[oldlfdir]);
475 		lncntp[oldlfdir] = 0;
476 		dp = ginode(lfdir);
477 	}
478 	if (statemap[lfdir] != DFOUND) {
479 		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
480 		return (0);
481 	}
482 	(void) lftempname(tempname, orphan);
483 	if (makeentry(lfdir, orphan, tempname) == 0) {
484 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
485 		printf("\n\n");
486 		return (0);
487 	}
488 	lncntp[orphan]--;
489 	if (lostdir) {
490 		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
491 		    parentdir != (ino_t)-1)
492 			(void) makeentry(orphan, lfdir, "..");
493 		/*
494 		 * If we were half-detached, don't try to get
495 		 * inode 0 later on.
496 		 */
497 		if (parentdir == 0)
498 			parentdir = -1;
499 		dp = ginode(lfdir);
500 		dp->di_nlink++;
501 		inodirty();
502 		lncntp[lfdir]++;
503 		if (parentdir != lfdir && parentdir != -1) {
504 			dp = ginode(parentdir);
505 			if ((dp->di_mode & IFMT) == IFDIR)
506 				lncntp[parentdir]++;
507 		}
508 		pwarn("DIR I=%u CONNECTED. ", orphan);
509 		if (parentdir != (ino_t)-1)
510 			printf("PARENT WAS I=%u\n", parentdir);
511 		if (preen == 0)
512 			printf("\n");
513 	}
514 	return (1);
515 }
516 
517 /*
518  * fix an entry in a directory.
519  */
520 changeino(dir, name, newnum)
521 	ino_t dir;
522 	char *name;
523 	ino_t newnum;
524 {
525 	struct inodesc idesc;
526 
527 	bzero((char *)&idesc, sizeof (struct inodesc));
528 	idesc.id_type = DATA;
529 	idesc.id_func = chgino;
530 	idesc.id_number = dir;
531 	idesc.id_fix = DONTKNOW;
532 	idesc.id_name = name;
533 	idesc.id_parent = newnum;	/* new value for name */
534 	return (ckinode(ginode(dir), &idesc));
535 }
536 
537 /*
538  * make an entry in a directory
539  */
540 makeentry(parent, ino, name)
541 	ino_t parent, ino;
542 	char *name;
543 {
544 	struct dinode *dp;
545 	struct inodesc idesc;
546 	char pathbuf[MAXPATHLEN + 1];
547 
548 	if (parent < UFSROOTINO || parent >= maxino ||
549 	    ino < UFSROOTINO || ino >= maxino)
550 		return (0);
551 	bzero((char *)&idesc, sizeof (struct inodesc));
552 	idesc.id_type = DATA;
553 	idesc.id_func = mkentry;
554 	idesc.id_number = parent;
555 	idesc.id_parent = ino;	/* this is the inode to enter */
556 	idesc.id_fix = DONTKNOW;
557 	idesc.id_name = name;
558 	dp = ginode(parent);
559 	if (dp->di_size % DIRBLKSIZ) {
560 		dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
561 		inodirty();
562 	}
563 	if ((ckinode(dp, &idesc) & ALTERED) != 0)
564 		return (1);
565 	getpathname(pathbuf, parent, parent);
566 	dp = ginode(parent);
567 	if (expanddir(dp, pathbuf) == 0)
568 		return (0);
569 	idesc.id_fix = DONTKNOW;
570 	return (ckinode(dp, &idesc) & ALTERED);
571 }
572 
573 /*
574  * Attempt to expand the size of a directory
575  */
576 expanddir(dp, name)
577 	struct dinode *dp;
578 	char *name;
579 {
580 	struct bufarea *bpback, *bp[2];
581 	daddr32_t nxtibn, nxtbn;
582 	daddr32_t newblk[2];
583 	char *cp;
584 	int bc, n, f;
585 	int allocIndir;
586 	int frag2blks = 0;
587 	int lffragsz = 0;
588 	int c = 0;
589 
590 	if (dp->di_size == 0)
591 		return (0);
592 
593 	nxtbn = lblkno(&sblock, dp->di_size - 1) + 1;
594 
595 	/*
596 	 *  Check that none of the direct block addresses for
597 	 *  the directory are bogus.
598 	 */
599 	for (bc = 0; ((nxtbn > 0) && (bc < nxtbn) && (bc < NDADDR)); bc++) {
600 		if (dp->di_db[bc] == 0)
601 			return (0);
602 	}
603 
604 	/*
605 	 * Determine our data block allocation needs.  We always need to
606 	 * allocate at least one data block.  We may need a second, the
607 	 * indirect block itself.
608 	 */
609 	allocIndir = 0;
610 	nxtibn = -1;
611 
612 	if (nxtbn <= NDADDR)  {
613 		/*
614 		 * Still in direct blocks.  Check for the unlikely
615 		 * case where the last block is a frag rather than
616 		 * a full block.  This would only happen if someone had
617 		 * created a file in lost+found, and then that caused
618 		 * the dynamic directory shrinking capabilities of ufs
619 		 * to kick in.
620 		 */
621 		lffragsz = dp->di_size % sblock.fs_bsize;
622 	}
623 
624 	if (nxtbn >= NDADDR && !lffragsz) {
625 		n = sblock.fs_bsize/sizeof (daddr32_t);
626 		nxtibn = nxtbn - NDADDR;
627 		/*
628 		 * Only go one level of indirection
629 		 */
630 		if (nxtibn >= n)
631 			return (0);
632 
633 		if (nxtibn == 0)
634 			allocIndir++;
635 	}
636 
637 	/*
638 	 * Allocate all the new blocks we need.
639 	 */
640 	if ((newblk[0] = allocblk(sblock.fs_frag)) == 0)
641 		goto bail;
642 	c++;
643 	if (allocIndir) {
644 		if ((newblk[1] = allocblk(sblock.fs_frag)) == 0)
645 			goto bail;
646 		c++;
647 	}
648 
649 	/*
650 	 * Take care of the block that will hold new directory entries.
651 	 * This one is always allocated.
652 	 */
653 	bp[0] = getdirblk(newblk[0], sblock.fs_bsize);
654 	if (bp[0]->b_errs)
655 		goto bail;
656 	if (lffragsz) {
657 		bpback = getdirblk(dp->di_db[nxtbn - 1],
658 		    dblksize(&sblock, dp, nxtbn - 1));
659 		if (bpback->b_errs)
660 			goto bail;
661 		bcopy(bpback->b_un.b_buf, bp[0]->b_un.b_buf, (size_t)lffragsz);
662 		for (cp = &(bp[0]->b_un.b_buf[lffragsz]);
663 		    cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]);
664 		    cp += DIRBLKSIZ)
665 			bcopy((char *)&emptydir, cp, sizeof (emptydir));
666 	} else {
667 		for (cp = bp[0]->b_un.b_buf;
668 		    cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]);
669 		    cp += DIRBLKSIZ)
670 			bcopy((char *)&emptydir, cp, sizeof (emptydir));
671 	}
672 	dirty(bp[0]);
673 
674 	/*
675 	 * If we allocated the indirect block, zero it out. Otherwise
676 	 * read it in if we're using one.
677 	 */
678 	if (allocIndir) {
679 		bp[1] = getdatablk(newblk[1], sblock.fs_bsize);
680 		if (bp[1]->b_errs)
681 			goto bail;
682 		bzero(bp[1]->b_un.b_buf, sblock.fs_bsize);
683 		dirty(bp[1]);
684 	} else if (nxtibn >= 0) {
685 		/* Check that the indirect block pointer looks okay */
686 		if (dp->di_ib[0] == 0)
687 			goto bail;
688 
689 		bp[1] = getdatablk(dp->di_ib[0], sblock.fs_bsize);
690 		if (bp[1]->b_errs)
691 			goto bail;
692 
693 		for (bc = 0; ((bc < nxtibn) && (bc < n)); bc++) {
694 			if (((daddr32_t *)bp[1]->b_un.b_buf)[bc] == 0)
695 				goto bail;
696 		}
697 	}
698 
699 	pwarn("NO SPACE LEFT IN %s", name);
700 	if (preen)
701 		printf(" (EXPANDED)\n");
702 	else if (reply("EXPAND") == 0)
703 		goto bail;
704 
705 	/*
706 	 * Now that everything we need is gathered up, we
707 	 * do the final assignments
708 	 */
709 
710 	if (lffragsz) {
711 		frag2blks = roundup(lffragsz, sblock.fs_fsize);
712 		freeblk(dp->di_db[nxtbn - 1],
713 		    frag2blks / sblock.fs_fsize);
714 		frag2blks = btodb(frag2blks);
715 		dp->di_size -= (u_offset_t)lffragsz;
716 		dp->di_blocks = dp->di_blocks - frag2blks;
717 		dp->di_db[nxtbn - 1] = newblk[0];
718 		dp->di_size += (u_offset_t)sblock.fs_bsize;
719 		dp->di_blocks += btodb(sblock.fs_bsize);
720 		inodirty();
721 		return (1);
722 	}
723 
724 	dp->di_size += (u_offset_t)sblock.fs_bsize;
725 	dp->di_blocks += btodb(sblock.fs_bsize);
726 	if (allocIndir) {
727 		dp->di_blocks += btodb(sblock.fs_bsize);
728 	}
729 
730 	inodirty();
731 	if (nxtibn < 0) {
732 		/*
733 		 * Still in direct blocks
734 		 */
735 		dp->di_db[nxtbn] = newblk[0];
736 	} else {
737 		/*
738 		 * Last indirect is always going to point at the
739 		 * new directory buffer
740 		 */
741 		if (allocIndir)
742 			dp->di_ib[0] = newblk[1];
743 		((daddr32_t *)bp[1]->b_un.b_buf)[nxtibn] = newblk[0];
744 		dirty(bp[1]);
745 	}
746 	return (1);
747 
748 bail:
749 	for (f = 0; f < c; f++)
750 		freeblk(newblk[f], sblock.fs_frag);
751 	return (0);
752 }
753 
754 /*
755  * allocate a new directory
756  */
757 allocdir(parent, request, mode)
758 	ino_t parent, request;
759 	int mode;
760 {
761 	ino_t ino;
762 	char *cp;
763 	struct dinode *dp;
764 	struct inoinfo *inp;
765 	struct bufarea *bp;
766 
767 	ino = allocino(request, IFDIR|mode);
768 	if (ino == 0)
769 		return (0);
770 	dirhead.dot_ino = ino;
771 	dirhead.dotdot_ino = parent;
772 	dp = ginode(ino);
773 	bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
774 	if (bp->b_errs) {
775 		freeino(ino);
776 		return (0);
777 	}
778 	bcopy((char *)&dirhead, bp->b_un.b_buf, sizeof (dirhead));
779 	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
780 	    cp < &bp->b_un.b_buf[sblock.fs_fsize];
781 	    cp += DIRBLKSIZ)
782 		bcopy((char *)&emptydir, cp, sizeof (emptydir));
783 	dirty(bp);
784 	dp->di_nlink = 2;
785 	inodirty();
786 	if (!inocached(ino)) {
787 		if (debug)
788 			printf("inode %d added to directory cache\n",
789 			    ino);
790 		cacheino(dp, ino);
791 	} else {
792 		/*
793 		 * re-using an old directory inode
794 		 */
795 		inp = getinoinfo(ino);
796 		inp->i_isize = (offset_t)dp->di_size;
797 		inp->i_numblks = dp->di_blocks * sizeof (daddr32_t);
798 		inp->i_parent = parent;
799 		bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
800 		    (size_t)inp->i_numblks);
801 	}
802 	if (ino == UFSROOTINO) {
803 		lncntp[ino] = dp->di_nlink;
804 		return (ino);
805 	}
806 	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
807 		freeino(ino);
808 		return (0);
809 	}
810 	statemap[ino] = statemap[parent];
811 	if (statemap[ino] == DSTATE) {
812 		lncntp[ino] = dp->di_nlink;
813 		lncntp[parent]++;
814 	}
815 	dp = ginode(parent);
816 	dp->di_nlink++;
817 	inodirty();
818 	return (ino);
819 }
820 
821 /*
822  * free a directory inode
823  */
824 freedir(ino, parent)
825 	ino_t ino, parent;
826 {
827 	struct dinode *dp;
828 
829 	if (ino != parent) {
830 		dp = ginode(parent);
831 		dp->di_nlink--;
832 		inodirty();
833 	}
834 	freeino(ino);
835 }
836 
837 /*
838  * generate a temporary name for the lost+found directory.
839  */
840 lftempname(bufp, ino)
841 	char *bufp;
842 	ino_t ino;
843 {
844 	ino_t in;
845 	char *cp;
846 	int namlen;
847 
848 	cp = bufp + 2;
849 	for (in = maxino; in > 0; in /= 10)
850 		cp++;
851 	*--cp = 0;
852 	namlen = cp - bufp;
853 	in = ino;
854 	while (cp > bufp) {
855 		*--cp = (in % 10) + '0';
856 		in /= 10;
857 	}
858 	*cp = '#';
859 	return (namlen);
860 }
861 
862 /*
863  * Get a directory block.
864  * Insure that it is held until another is requested.
865  */
866 struct bufarea *
867 getdirblk(blkno, size)
868 	daddr32_t blkno;
869 	int size;
870 {
871 	if (pdirbp != 0) {
872 		brelse(pdirbp);
873 	}
874 	pdirbp = getdatablk(blkno, size);
875 	return (pdirbp);
876 }
877