1/*
2 * Copyright (c) 1980, 1986, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char sccsid[] = "@(#)dir.c	8.8 (Berkeley) 4/28/95";
33#endif /* not lint */
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: stable/11/sbin/fsck_ffs/dir.c 348260 2019-05-25 00:22:07Z mckusick $");
37
38#include <sys/param.h>
39#include <sys/time.h>
40#include <sys/types.h>
41#include <sys/sysctl.h>
42
43#include <ufs/ufs/dinode.h>
44#include <ufs/ufs/dir.h>
45#include <ufs/ffs/fs.h>
46
47#include <err.h>
48#include <string.h>
49
50#include "fsck.h"
51
52static struct	dirtemplate emptydir = {
53	0, DIRBLKSIZ, DT_UNKNOWN, 0, "",
54	0, 0, DT_UNKNOWN, 0, ""
55};
56static struct	dirtemplate dirhead = {
57	0, 12, DT_DIR, 1, ".",
58	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
59};
60
61static int chgino(struct inodesc *);
62static int dircheck(struct inodesc *, struct bufarea *, struct direct *);
63static int expanddir(union dinode *dp, char *name);
64static void freedir(ino_t ino, ino_t parent);
65static struct direct *fsck_readdir(struct inodesc *);
66static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size);
67static int lftempname(char *bufp, ino_t ino);
68static int mkentry(struct inodesc *);
69
70/*
71 * Propagate connected state through the tree.
72 */
73void
74propagate(void)
75{
76	struct inoinfo **inpp, *inp;
77	struct inoinfo **inpend;
78	long change;
79
80	inpend = &inpsort[inplast];
81	do {
82		change = 0;
83		for (inpp = inpsort; inpp < inpend; inpp++) {
84			inp = *inpp;
85			if (inp->i_parent == 0)
86				continue;
87			if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
88			    INO_IS_DUNFOUND(inp->i_number)) {
89				inoinfo(inp->i_number)->ino_state = DFOUND;
90				change++;
91			}
92		}
93	} while (change > 0);
94}
95
96/*
97 * Scan each entry in a directory block.
98 */
99int
100dirscan(struct inodesc *idesc)
101{
102	struct direct *dp;
103	struct bufarea *bp;
104	u_int dsize, n;
105	long blksiz;
106	char dbuf[DIRBLKSIZ];
107
108	if (idesc->id_type != DATA)
109		errx(EEXIT, "wrong type to dirscan %d", idesc->id_type);
110	if (idesc->id_entryno == 0 &&
111	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
112		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
113	blksiz = idesc->id_numfrags * sblock.fs_fsize;
114	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
115		idesc->id_filesize -= blksiz;
116		return (SKIP);
117	}
118	idesc->id_loc = 0;
119	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
120		dsize = dp->d_reclen;
121		if (dsize > sizeof(dbuf))
122			dsize = sizeof(dbuf);
123		memmove(dbuf, dp, (size_t)dsize);
124		idesc->id_dirp = (struct direct *)dbuf;
125		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
126			bp = getdirblk(idesc->id_blkno, blksiz);
127			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
128			    (size_t)dsize);
129			dirty(bp);
130			sbdirty();
131			rerun = 1;
132		}
133		if (n & STOP)
134			return (n);
135	}
136	return (idesc->id_filesize > 0 ? KEEPON : STOP);
137}
138
139/*
140 * Get and verify the next entry in a directory.
141 * We also verify that if there is another entry in the block that it is
142 * valid, so if it is not valid it can be subsumed into the current entry.
143 */
144static struct direct *
145fsck_readdir(struct inodesc *idesc)
146{
147	struct direct *dp, *ndp;
148	struct bufarea *bp;
149	long size, blksiz, subsume_ndp;
150
151	subsume_ndp = 0;
152	blksiz = idesc->id_numfrags * sblock.fs_fsize;
153	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
154		return (NULL);
155	bp = getdirblk(idesc->id_blkno, blksiz);
156	dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
157	/*
158	 * Only need to check current entry if it is the first in the
159	 * the block, as later entries will have been checked in the
160	 * previous call to this function.
161	 */
162	if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) {
163		/*
164		 * Current entry is good, update to point at next.
165		 */
166		idesc->id_loc += dp->d_reclen;
167		idesc->id_filesize -= dp->d_reclen;
168		/*
169		 * If at end of directory block, just return this entry.
170		 */
171		if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz ||
172		    idesc->id_loc % DIRBLKSIZ == 0)
173			return (dp);
174		/*
175		 * If the next entry good, return this entry.
176		 */
177		ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
178		if (dircheck(idesc, bp, ndp) != 0)
179			return (dp);
180		/*
181		 * The next entry is bad, so subsume it and the remainder
182		 * of this directory block into this entry.
183		 */
184		subsume_ndp = 1;
185	}
186	/*
187	 * Current or next entry is bad. Zap current entry or
188	 * subsume next entry into current entry as appropriate.
189	 */
190	size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
191	idesc->id_loc += size;
192	idesc->id_filesize -= size;
193	if (idesc->id_fix == IGNORE)
194		return (NULL);
195	if (subsume_ndp) {
196		memset(ndp, 0, size);
197		dp->d_reclen += size;
198	} else {
199		memset(dp, 0, size);
200		dp->d_reclen = size;
201	}
202	if (dofix(idesc, "DIRECTORY CORRUPTED"))
203		dirty(bp);
204	return (dp);
205}
206
207/*
208 * Verify that a directory entry is valid.
209 * This is a superset of the checks made in the kernel.
210 * Also optionally clears padding and unused directory space.
211 *
212 * Returns 0 if the entry is bad, 1 if the entry is good.
213 */
214static int
215dircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp)
216{
217	size_t size;
218	char *cp;
219	u_int8_t namlen;
220	int spaceleft, modified, unused;
221
222	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
223	size = DIRSIZ(0, dp);
224	if (dp->d_reclen == 0 ||
225	    dp->d_reclen > spaceleft ||
226	    dp->d_reclen < size ||
227	    idesc->id_filesize < size ||
228	    (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0)
229		goto bad;
230	modified = 0;
231	if (dp->d_ino == 0) {
232		if (!zflag || fswritefd < 0)
233			return (1);
234		/*
235		 * Special case of an unused directory entry. Normally only
236		 * occurs at the beginning of a directory block when the block
237		 * contains no entries. Other than the first entry in a
238		 * directory block, the kernel coalesces unused space with
239		 * the previous entry by extending its d_reclen. However,
240		 * when cleaning up a directory, fsck may set d_ino to zero
241		 * in the middle of a directory block. If we're clearing out
242		 * directory cruft (-z flag), then make sure that all directory
243		 * space in entries with d_ino == 0 gets fully cleared.
244		 */
245		if (dp->d_type != 0) {
246			dp->d_type = 0;
247			modified = 1;
248		}
249		if (dp->d_namlen != 0) {
250			dp->d_namlen = 0;
251			modified = 1;
252		}
253		unused = dp->d_reclen - __offsetof(struct direct, d_name);
254		for (cp = dp->d_name; unused > 0; unused--, cp++) {
255			if (*cp != '\0') {
256				*cp = '\0';
257				modified = 1;
258			}
259		}
260		if (modified)
261			dirty(bp);
262		return (1);
263	}
264	/*
265	 * The d_type field should not be tested here. A bad type is an error
266	 * in the entry itself but is not a corruption of the directory
267	 * structure itself. So blowing away all the remaining entries in the
268	 * directory block is inappropriate. Rather the type error should be
269	 * checked in pass1 and fixed there.
270	 *
271	 * The name validation should also be done in pass1 although the
272	 * check to see if the name is longer than fits in the space
273	 * allocated for it (i.e., the *cp != '\0' fails after exiting the
274	 * loop below) then it really is a structural error that requires
275	 * the stronger action taken here.
276	 */
277	namlen = dp->d_namlen;
278	if (namlen == 0 || dp->d_type > 15)
279		goto bad;
280	for (cp = dp->d_name, size = 0; size < namlen; size++) {
281		if (*cp == '\0' || *cp++ == '/')
282			goto bad;
283	}
284	if (*cp != '\0')
285		goto bad;
286	if (zflag && fswritefd >= 0) {
287		/*
288		 * Clear unused directory entry space, including the d_name
289		 * padding.
290		 */
291		/* First figure the number of pad bytes. */
292		unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1);
293
294		/* Add in the free space to the end of the record. */
295		unused += dp->d_reclen - DIRSIZ(0, dp);
296
297		/*
298		 * Now clear out the unused space, keeping track if we actually
299		 * changed anything.
300		 */
301		for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) {
302			if (*cp != '\0') {
303				*cp = '\0';
304				modified = 1;
305			}
306		}
307
308		if (modified)
309			dirty(bp);
310	}
311	return (1);
312
313bad:
314	if (debug)
315		printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
316		    dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type,
317		    dp->d_name);
318	return (0);
319}
320
321void
322direrror(ino_t ino, const char *errmesg)
323{
324
325	fileerror(ino, ino, errmesg);
326}
327
328void
329fileerror(ino_t cwd, ino_t ino, const char *errmesg)
330{
331	union dinode *dp;
332	char pathbuf[MAXPATHLEN + 1];
333
334	pwarn("%s ", errmesg);
335	pinode(ino);
336	printf("\n");
337	getpathname(pathbuf, cwd, ino);
338	if (ino < ROOTINO || ino > maxino) {
339		pfatal("NAME=%s\n", pathbuf);
340		return;
341	}
342	dp = ginode(ino);
343	if (ftypeok(dp))
344		pfatal("%s=%s\n",
345		    (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE",
346		    pathbuf);
347	else
348		pfatal("NAME=%s\n", pathbuf);
349}
350
351void
352adjust(struct inodesc *idesc, int lcnt)
353{
354	union dinode *dp;
355	int saveresolved;
356
357	dp = ginode(idesc->id_number);
358	if (DIP(dp, di_nlink) == lcnt) {
359		/*
360		 * If we have not hit any unresolved problems, are running
361		 * in preen mode, and are on a file system using soft updates,
362		 * then just toss any partially allocated files.
363		 */
364		if (resolved && (preen || bkgrdflag) && usedsoftdep) {
365			clri(idesc, "UNREF", 1);
366			return;
367		} else {
368			/*
369			 * The file system can be marked clean even if
370			 * a file is not linked up, but is cleared.
371			 * Hence, resolved should not be cleared when
372			 * linkup is answered no, but clri is answered yes.
373			 */
374			saveresolved = resolved;
375			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
376				resolved = saveresolved;
377				clri(idesc, "UNREF", 0);
378				return;
379			}
380			/*
381			 * Account for the new reference created by linkup().
382			 */
383			dp = ginode(idesc->id_number);
384			lcnt--;
385		}
386	}
387	if (lcnt != 0) {
388		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
389			((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
390		pinode(idesc->id_number);
391		printf(" COUNT %d SHOULD BE %d",
392			DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt);
393		if (preen || usedsoftdep) {
394			if (lcnt < 0) {
395				printf("\n");
396				pfatal("LINK COUNT INCREASING");
397			}
398			if (preen)
399				printf(" (ADJUSTED)\n");
400		}
401		if (preen || reply("ADJUST") == 1) {
402			if (bkgrdflag == 0) {
403				DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt);
404				inodirty(dp);
405			} else {
406				cmd.value = idesc->id_number;
407				cmd.size = -lcnt;
408				if (debug)
409					printf("adjrefcnt ino %ld amt %lld\n",
410					    (long)cmd.value,
411					    (long long)cmd.size);
412				if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
413				    &cmd, sizeof cmd) == -1)
414					rwerror("ADJUST INODE", cmd.value);
415			}
416		}
417	}
418}
419
420static int
421mkentry(struct inodesc *idesc)
422{
423	struct direct *dirp = idesc->id_dirp;
424	struct direct newent;
425	int newlen, oldlen;
426
427	newent.d_namlen = strlen(idesc->id_name);
428	newlen = DIRSIZ(0, &newent);
429	if (dirp->d_ino != 0)
430		oldlen = DIRSIZ(0, dirp);
431	else
432		oldlen = 0;
433	if (dirp->d_reclen - oldlen < newlen)
434		return (KEEPON);
435	newent.d_reclen = dirp->d_reclen - oldlen;
436	dirp->d_reclen = oldlen;
437	dirp = (struct direct *)(((char *)dirp) + oldlen);
438	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
439	dirp->d_reclen = newent.d_reclen;
440	dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
441	dirp->d_namlen = newent.d_namlen;
442	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
443	return (ALTERED|STOP);
444}
445
446static int
447chgino(struct inodesc *idesc)
448{
449	struct direct *dirp = idesc->id_dirp;
450
451	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
452		return (KEEPON);
453	dirp->d_ino = idesc->id_parent;
454	dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
455	return (ALTERED|STOP);
456}
457
458int
459linkup(ino_t orphan, ino_t parentdir, char *name)
460{
461	union dinode *dp;
462	int lostdir;
463	ino_t oldlfdir;
464	struct inodesc idesc;
465	char tempname[BUFSIZ];
466
467	memset(&idesc, 0, sizeof(struct inodesc));
468	dp = ginode(orphan);
469	lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR;
470	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
471	pinode(orphan);
472	if (preen && DIP(dp, di_size) == 0)
473		return (0);
474	if (cursnapshot != 0) {
475		pfatal("FILE LINKUP IN SNAPSHOT");
476		return (0);
477	}
478	if (preen)
479		printf(" (RECONNECTED)\n");
480	else
481		if (reply("RECONNECT") == 0)
482			return (0);
483	if (lfdir == 0) {
484		dp = ginode(ROOTINO);
485		idesc.id_name = strdup(lfname);
486		idesc.id_type = DATA;
487		idesc.id_func = findino;
488		idesc.id_number = ROOTINO;
489		if ((ckinode(dp, &idesc) & FOUND) != 0) {
490			lfdir = idesc.id_parent;
491		} else {
492			pwarn("NO lost+found DIRECTORY");
493			if (preen || reply("CREATE")) {
494				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
495				if (lfdir != 0) {
496					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
497						numdirs++;
498						if (preen)
499							printf(" (CREATED)\n");
500					} else {
501						freedir(lfdir, ROOTINO);
502						lfdir = 0;
503						if (preen)
504							printf("\n");
505					}
506				}
507			}
508		}
509		if (lfdir == 0) {
510			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
511			printf("\n\n");
512			return (0);
513		}
514	}
515	dp = ginode(lfdir);
516	if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
517		pfatal("lost+found IS NOT A DIRECTORY");
518		if (reply("REALLOCATE") == 0)
519			return (0);
520		oldlfdir = lfdir;
521		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
522			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
523			return (0);
524		}
525		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
526			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
527			return (0);
528		}
529		inodirty(dp);
530		idesc.id_type = ADDR;
531		idesc.id_func = pass4check;
532		idesc.id_number = oldlfdir;
533		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
534		inoinfo(oldlfdir)->ino_linkcnt = 0;
535		dp = ginode(lfdir);
536	}
537	if (inoinfo(lfdir)->ino_state != DFOUND) {
538		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
539		return (0);
540	}
541	(void)lftempname(tempname, orphan);
542	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
543		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
544		printf("\n\n");
545		return (0);
546	}
547	inoinfo(orphan)->ino_linkcnt--;
548	if (lostdir) {
549		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
550		    parentdir != (ino_t)-1)
551			(void)makeentry(orphan, lfdir, "..");
552		dp = ginode(lfdir);
553		DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
554		inodirty(dp);
555		inoinfo(lfdir)->ino_linkcnt++;
556		pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
557		if (parentdir != (ino_t)-1) {
558			printf("PARENT WAS I=%lu\n", (u_long)parentdir);
559			/*
560			 * The parent directory, because of the ordering
561			 * guarantees, has had the link count incremented
562			 * for the child, but no entry was made.  This
563			 * fixes the parent link count so that fsck does
564			 * not need to be rerun.
565			 */
566			inoinfo(parentdir)->ino_linkcnt++;
567		}
568		if (preen == 0)
569			printf("\n");
570	}
571	return (1);
572}
573
574/*
575 * fix an entry in a directory.
576 */
577int
578changeino(ino_t dir, const char *name, ino_t newnum)
579{
580	struct inodesc idesc;
581
582	memset(&idesc, 0, sizeof(struct inodesc));
583	idesc.id_type = DATA;
584	idesc.id_func = chgino;
585	idesc.id_number = dir;
586	idesc.id_fix = DONTKNOW;
587	idesc.id_name = strdup(name);
588	idesc.id_parent = newnum;	/* new value for name */
589	return (ckinode(ginode(dir), &idesc));
590}
591
592/*
593 * make an entry in a directory
594 */
595int
596makeentry(ino_t parent, ino_t ino, const char *name)
597{
598	union dinode *dp;
599	struct inodesc idesc;
600	char pathbuf[MAXPATHLEN + 1];
601
602	if (parent < ROOTINO || parent >= maxino ||
603	    ino < ROOTINO || ino >= maxino)
604		return (0);
605	memset(&idesc, 0, sizeof(struct inodesc));
606	idesc.id_type = DATA;
607	idesc.id_func = mkentry;
608	idesc.id_number = parent;
609	idesc.id_parent = ino;	/* this is the inode to enter */
610	idesc.id_fix = DONTKNOW;
611	idesc.id_name = strdup(name);
612	dp = ginode(parent);
613	if (DIP(dp, di_size) % DIRBLKSIZ) {
614		DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ));
615		inodirty(dp);
616	}
617	if ((ckinode(dp, &idesc) & ALTERED) != 0)
618		return (1);
619	getpathname(pathbuf, parent, parent);
620	dp = ginode(parent);
621	if (expanddir(dp, pathbuf) == 0)
622		return (0);
623	return (ckinode(dp, &idesc) & ALTERED);
624}
625
626/*
627 * Attempt to expand the size of a directory
628 */
629static int
630expanddir(union dinode *dp, char *name)
631{
632	ufs2_daddr_t lastbn, newblk;
633	struct bufarea *bp;
634	char *cp, firstblk[DIRBLKSIZ];
635
636	lastbn = lblkno(&sblock, DIP(dp, di_size));
637	if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 ||
638	    DIP(dp, di_size) == 0)
639		return (0);
640	if ((newblk = allocblk(sblock.fs_frag)) == 0)
641		return (0);
642	DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn]));
643	DIP_SET(dp, di_db[lastbn], newblk);
644	DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize);
645	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
646	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
647		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
648	if (bp->b_errs)
649		goto bad;
650	memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
651	bp = getdirblk(newblk, sblock.fs_bsize);
652	if (bp->b_errs)
653		goto bad;
654	memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
655	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
656	     cp < &bp->b_un.b_buf[sblock.fs_bsize];
657	     cp += DIRBLKSIZ)
658		memmove(cp, &emptydir, sizeof emptydir);
659	dirty(bp);
660	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
661		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
662	if (bp->b_errs)
663		goto bad;
664	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
665	pwarn("NO SPACE LEFT IN %s", name);
666	if (preen)
667		printf(" (EXPANDED)\n");
668	else if (reply("EXPAND") == 0)
669		goto bad;
670	dirty(bp);
671	inodirty(dp);
672	return (1);
673bad:
674	DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1]));
675	DIP_SET(dp, di_db[lastbn + 1], 0);
676	DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize);
677	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize));
678	freeblk(newblk, sblock.fs_frag);
679	return (0);
680}
681
682/*
683 * allocate a new directory
684 */
685ino_t
686allocdir(ino_t parent, ino_t request, int mode)
687{
688	ino_t ino;
689	char *cp;
690	union dinode *dp;
691	struct bufarea *bp;
692	struct inoinfo *inp;
693	struct dirtemplate *dirp;
694
695	ino = allocino(request, IFDIR|mode);
696	dirp = &dirhead;
697	dirp->dot_ino = ino;
698	dirp->dotdot_ino = parent;
699	dp = ginode(ino);
700	bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
701	if (bp->b_errs) {
702		freeino(ino);
703		return (0);
704	}
705	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
706	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
707	     cp < &bp->b_un.b_buf[sblock.fs_fsize];
708	     cp += DIRBLKSIZ)
709		memmove(cp, &emptydir, sizeof emptydir);
710	dirty(bp);
711	DIP_SET(dp, di_nlink, 2);
712	inodirty(dp);
713	if (ino == ROOTINO) {
714		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
715		cacheino(dp, ino);
716		return(ino);
717	}
718	if (!INO_IS_DVALID(parent)) {
719		freeino(ino);
720		return (0);
721	}
722	cacheino(dp, ino);
723	inp = getinoinfo(ino);
724	inp->i_parent = parent;
725	inp->i_dotdot = parent;
726	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
727	if (inoinfo(ino)->ino_state == DSTATE) {
728		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
729		inoinfo(parent)->ino_linkcnt++;
730	}
731	dp = ginode(parent);
732	DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
733	inodirty(dp);
734	return (ino);
735}
736
737/*
738 * free a directory inode
739 */
740static void
741freedir(ino_t ino, ino_t parent)
742{
743	union dinode *dp;
744
745	if (ino != parent) {
746		dp = ginode(parent);
747		DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1);
748		inodirty(dp);
749	}
750	freeino(ino);
751}
752
753/*
754 * generate a temporary name for the lost+found directory.
755 */
756static int
757lftempname(char *bufp, ino_t ino)
758{
759	ino_t in;
760	char *cp;
761	int namlen;
762
763	cp = bufp + 2;
764	for (in = maxino; in > 0; in /= 10)
765		cp++;
766	*--cp = 0;
767	namlen = cp - bufp;
768	in = ino;
769	while (cp > bufp) {
770		*--cp = (in % 10) + '0';
771		in /= 10;
772	}
773	*cp = '#';
774	return (namlen);
775}
776
777/*
778 * Get a directory block.
779 * Insure that it is held until another is requested.
780 */
781static struct bufarea *
782getdirblk(ufs2_daddr_t blkno, long size)
783{
784
785	if (pdirbp != NULL)
786		pdirbp->b_flags &= ~B_INUSE;
787	pdirbp = getdatablk(blkno, size, BT_DIRDATA);
788	return (pdirbp);
789}
790