dir.c revision 297886
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1980, 1986, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30114589Sobrien#if 0
311558Srgrimes#ifndef lint
3223675Speterstatic const char sccsid[] = "@(#)dir.c	8.8 (Berkeley) 4/28/95";
33114589Sobrien#endif /* not lint */
3441477Sjulian#endif
35114589Sobrien#include <sys/cdefs.h>
36114589Sobrien__FBSDID("$FreeBSD: head/sbin/fsck_ffs/dir.c 297886 2016-04-12 22:55:47Z pfg $");
371558Srgrimes
381558Srgrimes#include <sys/param.h>
3941474Sjulian#include <sys/time.h>
40275030Sbapt#include <sys/types.h>
4174556Smckusick#include <sys/sysctl.h>
4223675Speter
431558Srgrimes#include <ufs/ufs/dinode.h>
441558Srgrimes#include <ufs/ufs/dir.h>
451558Srgrimes#include <ufs/ffs/fs.h>
4623796Sbde
4723675Speter#include <err.h>
481558Srgrimes#include <string.h>
4923675Speter
501558Srgrimes#include "fsck.h"
511558Srgrimes
52260068Sscottlstatic struct	dirtemplate emptydir = {
5374556Smckusick	0, DIRBLKSIZ, DT_UNKNOWN, 0, "",
5474556Smckusick	0, 0, DT_UNKNOWN, 0, ""
5574556Smckusick};
56260068Sscottlstatic struct	dirtemplate dirhead = {
571558Srgrimes	0, 12, DT_DIR, 1, ".",
581558Srgrimes	0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
591558Srgrimes};
601558Srgrimes
6192839Simpstatic int chgino(struct inodesc *);
6292839Simpstatic int dircheck(struct inodesc *, struct direct *);
6398542Smckusickstatic int expanddir(union dinode *dp, char *name);
6492839Simpstatic void freedir(ino_t ino, ino_t parent);
6592839Simpstatic struct direct *fsck_readdir(struct inodesc *);
6698542Smckusickstatic struct bufarea *getdirblk(ufs2_daddr_t blkno, long size);
6792839Simpstatic int lftempname(char *bufp, ino_t ino);
6892839Simpstatic int mkentry(struct inodesc *);
691558Srgrimes
701558Srgrimes/*
711558Srgrimes * Propagate connected state through the tree.
721558Srgrimes */
737585Sbdevoid
7492839Simppropagate(void)
751558Srgrimes{
7692806Sobrien	struct inoinfo **inpp, *inp;
771558Srgrimes	struct inoinfo **inpend;
781558Srgrimes	long change;
791558Srgrimes
801558Srgrimes	inpend = &inpsort[inplast];
811558Srgrimes	do {
821558Srgrimes		change = 0;
831558Srgrimes		for (inpp = inpsort; inpp < inpend; inpp++) {
841558Srgrimes			inp = *inpp;
851558Srgrimes			if (inp->i_parent == 0)
861558Srgrimes				continue;
8741474Sjulian			if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
88136281Struckman			    INO_IS_DUNFOUND(inp->i_number)) {
8941474Sjulian				inoinfo(inp->i_number)->ino_state = DFOUND;
901558Srgrimes				change++;
911558Srgrimes			}
921558Srgrimes		}
931558Srgrimes	} while (change > 0);
941558Srgrimes}
951558Srgrimes
961558Srgrimes/*
971558Srgrimes * Scan each entry in a directory block.
981558Srgrimes */
997585Sbdeint
10092839Simpdirscan(struct inodesc *idesc)
1011558Srgrimes{
10292806Sobrien	struct direct *dp;
10392806Sobrien	struct bufarea *bp;
10474556Smckusick	u_int dsize, n;
1051558Srgrimes	long blksiz;
1061558Srgrimes	char dbuf[DIRBLKSIZ];
1071558Srgrimes
1081558Srgrimes	if (idesc->id_type != DATA)
10923675Speter		errx(EEXIT, "wrong type to dirscan %d", idesc->id_type);
1101558Srgrimes	if (idesc->id_entryno == 0 &&
1111558Srgrimes	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
1121558Srgrimes		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
1131558Srgrimes	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1141558Srgrimes	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
1151558Srgrimes		idesc->id_filesize -= blksiz;
1161558Srgrimes		return (SKIP);
1171558Srgrimes	}
1181558Srgrimes	idesc->id_loc = 0;
1191558Srgrimes	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
1201558Srgrimes		dsize = dp->d_reclen;
12141474Sjulian		if (dsize > sizeof(dbuf))
12241474Sjulian			dsize = sizeof(dbuf);
12323675Speter		memmove(dbuf, dp, (size_t)dsize);
1241558Srgrimes		idesc->id_dirp = (struct direct *)dbuf;
1251558Srgrimes		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
1261558Srgrimes			bp = getdirblk(idesc->id_blkno, blksiz);
12723675Speter			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
1281558Srgrimes			    (size_t)dsize);
1291558Srgrimes			dirty(bp);
1301558Srgrimes			sbdirty();
131260068Sscottl			rerun = 1;
1321558Srgrimes		}
1338871Srgrimes		if (n & STOP)
1341558Srgrimes			return (n);
1351558Srgrimes	}
1361558Srgrimes	return (idesc->id_filesize > 0 ? KEEPON : STOP);
1371558Srgrimes}
1381558Srgrimes
1391558Srgrimes/*
1401558Srgrimes * get next entry in a directory.
1411558Srgrimes */
14223675Speterstatic struct direct *
14392839Simpfsck_readdir(struct inodesc *idesc)
1441558Srgrimes{
14592806Sobrien	struct direct *dp, *ndp;
14692806Sobrien	struct bufarea *bp;
1471558Srgrimes	long size, blksiz, fix, dploc;
1481558Srgrimes
1491558Srgrimes	blksiz = idesc->id_numfrags * sblock.fs_fsize;
1501558Srgrimes	bp = getdirblk(idesc->id_blkno, blksiz);
1511558Srgrimes	if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
1521558Srgrimes	    idesc->id_loc < blksiz) {
1531558Srgrimes		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
1541558Srgrimes		if (dircheck(idesc, dp))
1551558Srgrimes			goto dpok;
15623675Speter		if (idesc->id_fix == IGNORE)
15723675Speter			return (0);
1581558Srgrimes		fix = dofix(idesc, "DIRECTORY CORRUPTED");
1591558Srgrimes		bp = getdirblk(idesc->id_blkno, blksiz);
1601558Srgrimes		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
1611558Srgrimes		dp->d_reclen = DIRBLKSIZ;
1621558Srgrimes		dp->d_ino = 0;
1631558Srgrimes		dp->d_type = 0;
1641558Srgrimes		dp->d_namlen = 0;
1651558Srgrimes		dp->d_name[0] = '\0';
1661558Srgrimes		if (fix)
1671558Srgrimes			dirty(bp);
1681558Srgrimes		idesc->id_loc += DIRBLKSIZ;
1691558Srgrimes		idesc->id_filesize -= DIRBLKSIZ;
1701558Srgrimes		return (dp);
1711558Srgrimes	}
1721558Srgrimesdpok:
1731558Srgrimes	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
1741558Srgrimes		return NULL;
1751558Srgrimes	dploc = idesc->id_loc;
1761558Srgrimes	dp = (struct direct *)(bp->b_un.b_buf + dploc);
1771558Srgrimes	idesc->id_loc += dp->d_reclen;
1781558Srgrimes	idesc->id_filesize -= dp->d_reclen;
1791558Srgrimes	if ((idesc->id_loc % DIRBLKSIZ) == 0)
1801558Srgrimes		return (dp);
1811558Srgrimes	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
1821558Srgrimes	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
1831558Srgrimes	    dircheck(idesc, ndp) == 0) {
1841558Srgrimes		size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
1851558Srgrimes		idesc->id_loc += size;
1861558Srgrimes		idesc->id_filesize -= size;
18723675Speter		if (idesc->id_fix == IGNORE)
18823675Speter			return (0);
1891558Srgrimes		fix = dofix(idesc, "DIRECTORY CORRUPTED");
1901558Srgrimes		bp = getdirblk(idesc->id_blkno, blksiz);
1911558Srgrimes		dp = (struct direct *)(bp->b_un.b_buf + dploc);
1921558Srgrimes		dp->d_reclen += size;
1931558Srgrimes		if (fix)
1941558Srgrimes			dirty(bp);
1951558Srgrimes	}
1961558Srgrimes	return (dp);
1971558Srgrimes}
1981558Srgrimes
1991558Srgrimes/*
2001558Srgrimes * Verify that a directory entry is valid.
2011558Srgrimes * This is a superset of the checks made in the kernel.
2021558Srgrimes */
20323675Speterstatic int
20492839Simpdircheck(struct inodesc *idesc, struct direct *dp)
2051558Srgrimes{
206114589Sobrien	size_t size;
20792806Sobrien	char *cp;
208114589Sobrien	u_char type;
209225338Sdelphij	u_int8_t namlen;
2101558Srgrimes	int spaceleft;
2111558Srgrimes
21223675Speter	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
21341474Sjulian	if (dp->d_reclen == 0 ||
21423675Speter	    dp->d_reclen > spaceleft ||
21523675Speter	    (dp->d_reclen & 0x3) != 0)
21662668Smckusick		goto bad;
21723675Speter	if (dp->d_ino == 0)
21823675Speter		return (1);
21996483Sphk	size = DIRSIZ(0, dp);
22096483Sphk	namlen = dp->d_namlen;
22196483Sphk	type = dp->d_type;
22223675Speter	if (dp->d_reclen < size ||
22323675Speter	    idesc->id_filesize < size ||
224225338Sdelphij	    namlen == 0 ||
22523675Speter	    type > 15)
22662668Smckusick		goto bad;
22723675Speter	for (cp = dp->d_name, size = 0; size < namlen; size++)
22823675Speter		if (*cp == '\0' || (*cp++ == '/'))
22962668Smckusick			goto bad;
23023675Speter	if (*cp != '\0')
23162668Smckusick		goto bad;
23223675Speter	return (1);
23362668Smckusickbad:
23462668Smckusick	if (debug)
23562668Smckusick		printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
23662668Smckusick		    dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type,
23762668Smckusick		    dp->d_name);
23862668Smckusick	return (0);
2391558Srgrimes}
2401558Srgrimes
2417585Sbdevoid
242100935Sphkdirerror(ino_t ino, const char *errmesg)
2431558Srgrimes{
2441558Srgrimes
2451558Srgrimes	fileerror(ino, ino, errmesg);
2461558Srgrimes}
2471558Srgrimes
2487585Sbdevoid
249100935Sphkfileerror(ino_t cwd, ino_t ino, const char *errmesg)
2501558Srgrimes{
25198542Smckusick	union dinode *dp;
2521558Srgrimes	char pathbuf[MAXPATHLEN + 1];
2531558Srgrimes
2541558Srgrimes	pwarn("%s ", errmesg);
2551558Srgrimes	pinode(ino);
2561558Srgrimes	printf("\n");
2571558Srgrimes	getpathname(pathbuf, cwd, ino);
2581558Srgrimes	if (ino < ROOTINO || ino > maxino) {
2591558Srgrimes		pfatal("NAME=%s\n", pathbuf);
2601558Srgrimes		return;
2611558Srgrimes	}
2621558Srgrimes	dp = ginode(ino);
2631558Srgrimes	if (ftypeok(dp))
2641558Srgrimes		pfatal("%s=%s\n",
26598542Smckusick		    (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE",
26698542Smckusick		    pathbuf);
2671558Srgrimes	else
2681558Srgrimes		pfatal("NAME=%s\n", pathbuf);
2691558Srgrimes}
2701558Srgrimes
2717585Sbdevoid
27292839Simpadjust(struct inodesc *idesc, int lcnt)
2731558Srgrimes{
27498542Smckusick	union dinode *dp;
27541474Sjulian	int saveresolved;
2761558Srgrimes
2771558Srgrimes	dp = ginode(idesc->id_number);
27898542Smckusick	if (DIP(dp, di_nlink) == lcnt) {
27941474Sjulian		/*
28041474Sjulian		 * If we have not hit any unresolved problems, are running
281102231Strhodes		 * in preen mode, and are on a file system using soft updates,
28241474Sjulian		 * then just toss any partially allocated files.
28341474Sjulian		 */
28474556Smckusick		if (resolved && (preen || bkgrdflag) && usedsoftdep) {
28541474Sjulian			clri(idesc, "UNREF", 1);
28641474Sjulian			return;
28741474Sjulian		} else {
28841474Sjulian			/*
289102231Strhodes			 * The file system can be marked clean even if
29041474Sjulian			 * a file is not linked up, but is cleared.
29141474Sjulian			 * Hence, resolved should not be cleared when
29241474Sjulian			 * linkup is answered no, but clri is answered yes.
29341474Sjulian			 */
29441474Sjulian			saveresolved = resolved;
29541474Sjulian			if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) {
29641474Sjulian				resolved = saveresolved;
29741474Sjulian				clri(idesc, "UNREF", 0);
29841474Sjulian				return;
29941474Sjulian			}
30041474Sjulian			/*
30141474Sjulian			 * Account for the new reference created by linkup().
30241474Sjulian			 */
30341474Sjulian			dp = ginode(idesc->id_number);
30441474Sjulian			lcnt--;
30541474Sjulian		}
30641474Sjulian	}
30741474Sjulian	if (lcnt != 0) {
3081558Srgrimes		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
30998542Smckusick			((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
3101558Srgrimes		pinode(idesc->id_number);
3111558Srgrimes		printf(" COUNT %d SHOULD BE %d",
31298542Smckusick			DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt);
31334266Sjulian		if (preen || usedsoftdep) {
3141558Srgrimes			if (lcnt < 0) {
3151558Srgrimes				printf("\n");
3161558Srgrimes				pfatal("LINK COUNT INCREASING");
3171558Srgrimes			}
31834266Sjulian			if (preen)
31934266Sjulian				printf(" (ADJUSTED)\n");
3201558Srgrimes		}
3211558Srgrimes		if (preen || reply("ADJUST") == 1) {
32274556Smckusick			if (bkgrdflag == 0) {
323134589Sscottl				DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt);
32474556Smckusick				inodirty();
32574556Smckusick			} else {
32674556Smckusick				cmd.value = idesc->id_number;
32774556Smckusick				cmd.size = -lcnt;
32874556Smckusick				if (debug)
329100935Sphk					printf("adjrefcnt ino %ld amt %lld\n",
330100935Sphk					    (long)cmd.value,
331100935Sphk					    (long long)cmd.size);
33274556Smckusick				if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
33374556Smckusick				    &cmd, sizeof cmd) == -1)
33474556Smckusick					rwerror("ADJUST INODE", cmd.value);
33574556Smckusick			}
3361558Srgrimes		}
3371558Srgrimes	}
3381558Srgrimes}
3391558Srgrimes
34023675Speterstatic int
34192839Simpmkentry(struct inodesc *idesc)
3421558Srgrimes{
34392806Sobrien	struct direct *dirp = idesc->id_dirp;
3441558Srgrimes	struct direct newent;
3451558Srgrimes	int newlen, oldlen;
3461558Srgrimes
3471558Srgrimes	newent.d_namlen = strlen(idesc->id_name);
3481558Srgrimes	newlen = DIRSIZ(0, &newent);
3491558Srgrimes	if (dirp->d_ino != 0)
3501558Srgrimes		oldlen = DIRSIZ(0, dirp);
3511558Srgrimes	else
3521558Srgrimes		oldlen = 0;
3531558Srgrimes	if (dirp->d_reclen - oldlen < newlen)
3541558Srgrimes		return (KEEPON);
3551558Srgrimes	newent.d_reclen = dirp->d_reclen - oldlen;
3561558Srgrimes	dirp->d_reclen = oldlen;
3571558Srgrimes	dirp = (struct direct *)(((char *)dirp) + oldlen);
3581558Srgrimes	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */
35923675Speter	dirp->d_reclen = newent.d_reclen;
36096483Sphk	dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
36123675Speter	dirp->d_namlen = newent.d_namlen;
36223675Speter	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
3631558Srgrimes	return (ALTERED|STOP);
3641558Srgrimes}
3651558Srgrimes
36623675Speterstatic int
36792839Simpchgino(struct inodesc *idesc)
3681558Srgrimes{
36992806Sobrien	struct direct *dirp = idesc->id_dirp;
3701558Srgrimes
37123675Speter	if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
3721558Srgrimes		return (KEEPON);
3731558Srgrimes	dirp->d_ino = idesc->id_parent;
37496483Sphk	dirp->d_type = inoinfo(idesc->id_parent)->ino_type;
3751558Srgrimes	return (ALTERED|STOP);
3761558Srgrimes}
3771558Srgrimes
3787585Sbdeint
37992839Simplinkup(ino_t orphan, ino_t parentdir, char *name)
3801558Srgrimes{
38198542Smckusick	union dinode *dp;
3821558Srgrimes	int lostdir;
3831558Srgrimes	ino_t oldlfdir;
3841558Srgrimes	struct inodesc idesc;
3851558Srgrimes	char tempname[BUFSIZ];
3861558Srgrimes
38723675Speter	memset(&idesc, 0, sizeof(struct inodesc));
3881558Srgrimes	dp = ginode(orphan);
38998542Smckusick	lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR;
3901558Srgrimes	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
3911558Srgrimes	pinode(orphan);
39298542Smckusick	if (preen && DIP(dp, di_size) == 0)
3931558Srgrimes		return (0);
39474556Smckusick	if (cursnapshot != 0) {
39574556Smckusick		pfatal("FILE LINKUP IN SNAPSHOT");
39674556Smckusick		return (0);
39774556Smckusick	}
3981558Srgrimes	if (preen)
3991558Srgrimes		printf(" (RECONNECTED)\n");
4001558Srgrimes	else
4011558Srgrimes		if (reply("RECONNECT") == 0)
4021558Srgrimes			return (0);
4031558Srgrimes	if (lfdir == 0) {
4041558Srgrimes		dp = ginode(ROOTINO);
405100935Sphk		idesc.id_name = strdup(lfname);
4061558Srgrimes		idesc.id_type = DATA;
4071558Srgrimes		idesc.id_func = findino;
4081558Srgrimes		idesc.id_number = ROOTINO;
4091558Srgrimes		if ((ckinode(dp, &idesc) & FOUND) != 0) {
4101558Srgrimes			lfdir = idesc.id_parent;
4111558Srgrimes		} else {
4121558Srgrimes			pwarn("NO lost+found DIRECTORY");
4131558Srgrimes			if (preen || reply("CREATE")) {
4141558Srgrimes				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
4151558Srgrimes				if (lfdir != 0) {
4161558Srgrimes					if (makeentry(ROOTINO, lfdir, lfname) != 0) {
41741474Sjulian						numdirs++;
4181558Srgrimes						if (preen)
4191558Srgrimes							printf(" (CREATED)\n");
4201558Srgrimes					} else {
4211558Srgrimes						freedir(lfdir, ROOTINO);
4221558Srgrimes						lfdir = 0;
4231558Srgrimes						if (preen)
4241558Srgrimes							printf("\n");
4251558Srgrimes					}
4261558Srgrimes				}
4271558Srgrimes			}
4281558Srgrimes		}
4291558Srgrimes		if (lfdir == 0) {
4301558Srgrimes			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
4311558Srgrimes			printf("\n\n");
4321558Srgrimes			return (0);
4331558Srgrimes		}
4341558Srgrimes	}
4351558Srgrimes	dp = ginode(lfdir);
43698542Smckusick	if ((DIP(dp, di_mode) & IFMT) != IFDIR) {
4371558Srgrimes		pfatal("lost+found IS NOT A DIRECTORY");
4381558Srgrimes		if (reply("REALLOCATE") == 0)
4391558Srgrimes			return (0);
4401558Srgrimes		oldlfdir = lfdir;
4411558Srgrimes		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
4421558Srgrimes			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
4431558Srgrimes			return (0);
4441558Srgrimes		}
4451558Srgrimes		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
4461558Srgrimes			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
4471558Srgrimes			return (0);
4481558Srgrimes		}
4491558Srgrimes		inodirty();
4501558Srgrimes		idesc.id_type = ADDR;
4511558Srgrimes		idesc.id_func = pass4check;
4521558Srgrimes		idesc.id_number = oldlfdir;
45341474Sjulian		adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1);
45441474Sjulian		inoinfo(oldlfdir)->ino_linkcnt = 0;
4551558Srgrimes		dp = ginode(lfdir);
4561558Srgrimes	}
45741474Sjulian	if (inoinfo(lfdir)->ino_state != DFOUND) {
4581558Srgrimes		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
4591558Srgrimes		return (0);
4601558Srgrimes	}
4611558Srgrimes	(void)lftempname(tempname, orphan);
46241474Sjulian	if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) {
4631558Srgrimes		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
4641558Srgrimes		printf("\n\n");
4651558Srgrimes		return (0);
4661558Srgrimes	}
46741474Sjulian	inoinfo(orphan)->ino_linkcnt--;
4681558Srgrimes	if (lostdir) {
4691558Srgrimes		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
4701558Srgrimes		    parentdir != (ino_t)-1)
4711558Srgrimes			(void)makeentry(orphan, lfdir, "..");
4721558Srgrimes		dp = ginode(lfdir);
473134589Sscottl		DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
4741558Srgrimes		inodirty();
47541474Sjulian		inoinfo(lfdir)->ino_linkcnt++;
47686514Siedowse		pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan);
47715699Snate		if (parentdir != (ino_t)-1) {
47837236Sbde			printf("PARENT WAS I=%lu\n", (u_long)parentdir);
47941477Sjulian			/*
48041477Sjulian			 * The parent directory, because of the ordering
48141477Sjulian			 * guarantees, has had the link count incremented
48241477Sjulian			 * for the child, but no entry was made.  This
48341477Sjulian			 * fixes the parent link count so that fsck does
48441477Sjulian			 * not need to be rerun.
48541477Sjulian			 */
48641474Sjulian			inoinfo(parentdir)->ino_linkcnt++;
48715699Snate		}
4881558Srgrimes		if (preen == 0)
4891558Srgrimes			printf("\n");
4901558Srgrimes	}
4911558Srgrimes	return (1);
4921558Srgrimes}
4931558Srgrimes
4941558Srgrimes/*
4951558Srgrimes * fix an entry in a directory.
4961558Srgrimes */
4977585Sbdeint
498100935Sphkchangeino(ino_t dir, const char *name, ino_t newnum)
4991558Srgrimes{
5001558Srgrimes	struct inodesc idesc;
5011558Srgrimes
50223675Speter	memset(&idesc, 0, sizeof(struct inodesc));
5031558Srgrimes	idesc.id_type = DATA;
5041558Srgrimes	idesc.id_func = chgino;
5051558Srgrimes	idesc.id_number = dir;
5061558Srgrimes	idesc.id_fix = DONTKNOW;
507100935Sphk	idesc.id_name = strdup(name);
5081558Srgrimes	idesc.id_parent = newnum;	/* new value for name */
5091558Srgrimes	return (ckinode(ginode(dir), &idesc));
5101558Srgrimes}
5111558Srgrimes
5121558Srgrimes/*
5131558Srgrimes * make an entry in a directory
5141558Srgrimes */
5157585Sbdeint
516100935Sphkmakeentry(ino_t parent, ino_t ino, const char *name)
5171558Srgrimes{
51898542Smckusick	union dinode *dp;
5191558Srgrimes	struct inodesc idesc;
5201558Srgrimes	char pathbuf[MAXPATHLEN + 1];
5218871Srgrimes
5221558Srgrimes	if (parent < ROOTINO || parent >= maxino ||
5231558Srgrimes	    ino < ROOTINO || ino >= maxino)
5241558Srgrimes		return (0);
52523675Speter	memset(&idesc, 0, sizeof(struct inodesc));
5261558Srgrimes	idesc.id_type = DATA;
5271558Srgrimes	idesc.id_func = mkentry;
5281558Srgrimes	idesc.id_number = parent;
5291558Srgrimes	idesc.id_parent = ino;	/* this is the inode to enter */
5301558Srgrimes	idesc.id_fix = DONTKNOW;
531100935Sphk	idesc.id_name = strdup(name);
5321558Srgrimes	dp = ginode(parent);
53398542Smckusick	if (DIP(dp, di_size) % DIRBLKSIZ) {
534134589Sscottl		DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ));
5351558Srgrimes		inodirty();
5361558Srgrimes	}
5371558Srgrimes	if ((ckinode(dp, &idesc) & ALTERED) != 0)
5381558Srgrimes		return (1);
5391558Srgrimes	getpathname(pathbuf, parent, parent);
5401558Srgrimes	dp = ginode(parent);
5411558Srgrimes	if (expanddir(dp, pathbuf) == 0)
5421558Srgrimes		return (0);
5431558Srgrimes	return (ckinode(dp, &idesc) & ALTERED);
5441558Srgrimes}
5451558Srgrimes
5461558Srgrimes/*
5471558Srgrimes * Attempt to expand the size of a directory
5481558Srgrimes */
54923675Speterstatic int
55098542Smckusickexpanddir(union dinode *dp, char *name)
5511558Srgrimes{
55298542Smckusick	ufs2_daddr_t lastbn, newblk;
55392806Sobrien	struct bufarea *bp;
5541558Srgrimes	char *cp, firstblk[DIRBLKSIZ];
5551558Srgrimes
55698542Smckusick	lastbn = lblkno(&sblock, DIP(dp, di_size));
55798542Smckusick	if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 ||
55898542Smckusick	    DIP(dp, di_size) == 0)
5591558Srgrimes		return (0);
5601558Srgrimes	if ((newblk = allocblk(sblock.fs_frag)) == 0)
5611558Srgrimes		return (0);
562134589Sscottl	DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn]));
563134589Sscottl	DIP_SET(dp, di_db[lastbn], newblk);
564134589Sscottl	DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize);
565134589Sscottl	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize));
56698542Smckusick	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
56798542Smckusick		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
5681558Srgrimes	if (bp->b_errs)
5691558Srgrimes		goto bad;
57023675Speter	memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
5711558Srgrimes	bp = getdirblk(newblk, sblock.fs_bsize);
5721558Srgrimes	if (bp->b_errs)
5731558Srgrimes		goto bad;
57423675Speter	memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
5751558Srgrimes	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
5761558Srgrimes	     cp < &bp->b_un.b_buf[sblock.fs_bsize];
5771558Srgrimes	     cp += DIRBLKSIZ)
57823675Speter		memmove(cp, &emptydir, sizeof emptydir);
5791558Srgrimes	dirty(bp);
58098542Smckusick	bp = getdirblk(DIP(dp, di_db[lastbn + 1]),
58198542Smckusick		sblksize(&sblock, DIP(dp, di_size), lastbn + 1));
5821558Srgrimes	if (bp->b_errs)
5831558Srgrimes		goto bad;
58423675Speter	memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir);
5851558Srgrimes	pwarn("NO SPACE LEFT IN %s", name);
5861558Srgrimes	if (preen)
5871558Srgrimes		printf(" (EXPANDED)\n");
5881558Srgrimes	else if (reply("EXPAND") == 0)
5891558Srgrimes		goto bad;
5901558Srgrimes	dirty(bp);
5911558Srgrimes	inodirty();
5921558Srgrimes	return (1);
5931558Srgrimesbad:
594134589Sscottl	DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1]));
595134589Sscottl	DIP_SET(dp, di_db[lastbn + 1], 0);
596134589Sscottl	DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize);
597134589Sscottl	DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize));
5981558Srgrimes	freeblk(newblk, sblock.fs_frag);
5991558Srgrimes	return (0);
6001558Srgrimes}
6011558Srgrimes
6021558Srgrimes/*
6031558Srgrimes * allocate a new directory
6041558Srgrimes */
6057586Sbdeino_t
60692839Simpallocdir(ino_t parent, ino_t request, int mode)
6071558Srgrimes{
6081558Srgrimes	ino_t ino;
6091558Srgrimes	char *cp;
61098542Smckusick	union dinode *dp;
61163810Smckusick	struct bufarea *bp;
61263810Smckusick	struct inoinfo *inp;
6131558Srgrimes	struct dirtemplate *dirp;
6141558Srgrimes
6151558Srgrimes	ino = allocino(request, IFDIR|mode);
61696483Sphk	dirp = &dirhead;
6171558Srgrimes	dirp->dot_ino = ino;
6181558Srgrimes	dirp->dotdot_ino = parent;
6191558Srgrimes	dp = ginode(ino);
62098542Smckusick	bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize);
6211558Srgrimes	if (bp->b_errs) {
6221558Srgrimes		freeino(ino);
6231558Srgrimes		return (0);
6241558Srgrimes	}
62523675Speter	memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate));
6261558Srgrimes	for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
6271558Srgrimes	     cp < &bp->b_un.b_buf[sblock.fs_fsize];
6281558Srgrimes	     cp += DIRBLKSIZ)
62923675Speter		memmove(cp, &emptydir, sizeof emptydir);
6301558Srgrimes	dirty(bp);
631134589Sscottl	DIP_SET(dp, di_nlink, 2);
6321558Srgrimes	inodirty();
6331558Srgrimes	if (ino == ROOTINO) {
63498542Smckusick		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
6351558Srgrimes		cacheino(dp, ino);
6361558Srgrimes		return(ino);
6371558Srgrimes	}
638136281Struckman	if (!INO_IS_DVALID(parent)) {
6391558Srgrimes		freeino(ino);
6401558Srgrimes		return (0);
6411558Srgrimes	}
6421558Srgrimes	cacheino(dp, ino);
64363810Smckusick	inp = getinoinfo(ino);
64463810Smckusick	inp->i_parent = parent;
64563810Smckusick	inp->i_dotdot = parent;
64641474Sjulian	inoinfo(ino)->ino_state = inoinfo(parent)->ino_state;
64741474Sjulian	if (inoinfo(ino)->ino_state == DSTATE) {
64898542Smckusick		inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink);
64941474Sjulian		inoinfo(parent)->ino_linkcnt++;
6501558Srgrimes	}
6511558Srgrimes	dp = ginode(parent);
652134589Sscottl	DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1);
6531558Srgrimes	inodirty();
6541558Srgrimes	return (ino);
6551558Srgrimes}
6561558Srgrimes
6571558Srgrimes/*
6581558Srgrimes * free a directory inode
6591558Srgrimes */
6607585Sbdestatic void
66192839Simpfreedir(ino_t ino, ino_t parent)
6621558Srgrimes{
66398542Smckusick	union dinode *dp;
6641558Srgrimes
6651558Srgrimes	if (ino != parent) {
6661558Srgrimes		dp = ginode(parent);
667134589Sscottl		DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1);
6681558Srgrimes		inodirty();
6691558Srgrimes	}
6701558Srgrimes	freeino(ino);
6711558Srgrimes}
6721558Srgrimes
6731558Srgrimes/*
6741558Srgrimes * generate a temporary name for the lost+found directory.
6751558Srgrimes */
67623675Speterstatic int
67792839Simplftempname(char *bufp, ino_t ino)
6781558Srgrimes{
67992806Sobrien	ino_t in;
68092806Sobrien	char *cp;
6811558Srgrimes	int namlen;
6821558Srgrimes
6831558Srgrimes	cp = bufp + 2;
6841558Srgrimes	for (in = maxino; in > 0; in /= 10)
6851558Srgrimes		cp++;
6861558Srgrimes	*--cp = 0;
6871558Srgrimes	namlen = cp - bufp;
6881558Srgrimes	in = ino;
6891558Srgrimes	while (cp > bufp) {
6901558Srgrimes		*--cp = (in % 10) + '0';
6911558Srgrimes		in /= 10;
6921558Srgrimes	}
6931558Srgrimes	*cp = '#';
6941558Srgrimes	return (namlen);
6951558Srgrimes}
6961558Srgrimes
6971558Srgrimes/*
6981558Srgrimes * Get a directory block.
6991558Srgrimes * Insure that it is held until another is requested.
7001558Srgrimes */
70123675Speterstatic struct bufarea *
70298542Smckusickgetdirblk(ufs2_daddr_t blkno, long size)
7031558Srgrimes{
7041558Srgrimes
705297886Spfg	if (pdirbp != NULL)
7061558Srgrimes		pdirbp->b_flags &= ~B_INUSE;
707247212Smckusick	pdirbp = getdatablk(blkno, size, BT_DIRDATA);
7081558Srgrimes	return (pdirbp);
7091558Srgrimes}
710