1331722Seadler/*
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
30102411Scharnier#if 0
311558Srgrimes#ifndef lint
3223675Speterstatic const char sccsid[] = "@(#)pass2.c	8.9 (Berkeley) 4/28/95";
33102411Scharnier#endif /* not lint */
3441477Sjulian#endif
35102411Scharnier#include <sys/cdefs.h>
36102411Scharnier__FBSDID("$FreeBSD: stable/11/sbin/fsck_ffs/pass2.c 344887 2019-03-07 13:53:59Z kib $");
37102411Scharnier
381558Srgrimes#include <sys/param.h>
39202109Smckusick#include <sys/sysctl.h>
4023675Speter
411558Srgrimes#include <ufs/ufs/dinode.h>
421558Srgrimes#include <ufs/ufs/dir.h>
4374556Smckusick#include <ufs/ffs/fs.h>
4423799Sbde
4523675Speter#include <err.h>
46202109Smckusick#include <errno.h>
47101037Smux#include <stdint.h>
481558Srgrimes#include <string.h>
4923675Speter
501558Srgrimes#include "fsck.h"
511558Srgrimes
521558Srgrimes#define MINDIRSIZE	(sizeof (struct dirtemplate))
531558Srgrimes
54202107Smckusickstatic int fix_extraneous(struct inoinfo *, struct inodesc *);
55202107Smckusickstatic int deleteentry(struct inodesc *);
5692839Simpstatic int blksort(const void *, const void *);
5792839Simpstatic int pass2check(struct inodesc *);
581558Srgrimes
597585Sbdevoid
6092839Simppass2(void)
611558Srgrimes{
6298542Smckusick	union dinode *dp;
6392806Sobrien	struct inoinfo **inpp, *inp;
641558Srgrimes	struct inoinfo **inpend;
651558Srgrimes	struct inodesc curino;
6698542Smckusick	union dinode dino;
6798542Smckusick	int i;
681558Srgrimes	char pathbuf[MAXPATHLEN + 1];
691558Srgrimes
7041474Sjulian	switch (inoinfo(ROOTINO)->ino_state) {
711558Srgrimes
721558Srgrimes	case USTATE:
731558Srgrimes		pfatal("ROOT INODE UNALLOCATED");
7434266Sjulian		if (reply("ALLOCATE") == 0) {
7534266Sjulian			ckfini(0);
7623675Speter			exit(EEXIT);
7734266Sjulian		}
781558Srgrimes		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
7923675Speter			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
801558Srgrimes		break;
811558Srgrimes
821558Srgrimes	case DCLEAR:
831558Srgrimes		pfatal("DUPS/BAD IN ROOT INODE");
841558Srgrimes		if (reply("REALLOCATE")) {
851558Srgrimes			freeino(ROOTINO);
861558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
8723675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
881558Srgrimes			break;
891558Srgrimes		}
9034266Sjulian		if (reply("CONTINUE") == 0) {
9134266Sjulian			ckfini(0);
9223675Speter			exit(EEXIT);
9334266Sjulian		}
941558Srgrimes		break;
951558Srgrimes
961558Srgrimes	case FSTATE:
971558Srgrimes	case FCLEAR:
98136281Struckman	case FZLINK:
991558Srgrimes		pfatal("ROOT INODE NOT DIRECTORY");
1001558Srgrimes		if (reply("REALLOCATE")) {
1011558Srgrimes			freeino(ROOTINO);
1021558Srgrimes			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
10323675Speter				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
1041558Srgrimes			break;
1051558Srgrimes		}
10634266Sjulian		if (reply("FIX") == 0) {
10734266Sjulian			ckfini(0);
10823675Speter			exit(EEXIT);
10934266Sjulian		}
1101558Srgrimes		dp = ginode(ROOTINO);
111134589Sscottl		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
112134589Sscottl		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
113344887Skib		inodirty(dp);
1141558Srgrimes		break;
1151558Srgrimes
1161558Srgrimes	case DSTATE:
117136281Struckman	case DZLINK:
1181558Srgrimes		break;
1191558Srgrimes
1201558Srgrimes	default:
12141474Sjulian		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
12241474Sjulian		    inoinfo(ROOTINO)->ino_state);
1231558Srgrimes	}
12441474Sjulian	inoinfo(ROOTINO)->ino_state = DFOUND;
12596483Sphk	inoinfo(WINO)->ino_state = FSTATE;
12696483Sphk	inoinfo(WINO)->ino_type = DT_WHT;
1271558Srgrimes	/*
1281558Srgrimes	 * Sort the directory list into disk block order.
1291558Srgrimes	 */
1301558Srgrimes	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
1311558Srgrimes	/*
1321558Srgrimes	 * Check the integrity of each directory.
1331558Srgrimes	 */
13423675Speter	memset(&curino, 0, sizeof(struct inodesc));
1351558Srgrimes	curino.id_type = DATA;
1361558Srgrimes	curino.id_func = pass2check;
1371558Srgrimes	inpend = &inpsort[inplast];
1381558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
13970050Siedowse		if (got_siginfo) {
140101037Smux			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
14186514Siedowse			    inpp - inpsort, (int)inplast,
14286514Siedowse			    (int)((inpp - inpsort) * 100 / inplast));
14370050Siedowse			got_siginfo = 0;
14470050Siedowse		}
145126345Sscottl		if (got_sigalarm) {
146126345Sscottl			setproctitle("%s p2 %d%%", cdevname,
147126345Sscottl			    (int)((inpp - inpsort) * 100 / inplast));
148126345Sscottl			got_sigalarm = 0;
149126345Sscottl		}
1501558Srgrimes		inp = *inpp;
1511558Srgrimes		if (inp->i_isize == 0)
1521558Srgrimes			continue;
1531558Srgrimes		if (inp->i_isize < MINDIRSIZE) {
1541558Srgrimes			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1551558Srgrimes			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1561558Srgrimes			if (reply("FIX") == 1) {
1571558Srgrimes				dp = ginode(inp->i_number);
158134589Sscottl				DIP_SET(dp, di_size, inp->i_isize);
159344887Skib				inodirty(dp);
1601558Srgrimes			}
1611558Srgrimes		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
1621558Srgrimes			getpathname(pathbuf, inp->i_number, inp->i_number);
16334266Sjulian			if (usedsoftdep)
164101037Smux				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
165101037Smux					"DIRECTORY", pathbuf,
166101037Smux					(intmax_t)inp->i_isize, DIRBLKSIZ);
16734266Sjulian			else
168101037Smux				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
169101037Smux					"DIRECTORY", pathbuf,
170101037Smux					(intmax_t)inp->i_isize, DIRBLKSIZ);
1711558Srgrimes			if (preen)
1721558Srgrimes				printf(" (ADJUSTED)\n");
1731558Srgrimes			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
1741558Srgrimes			if (preen || reply("ADJUST") == 1) {
1751558Srgrimes				dp = ginode(inp->i_number);
176134589Sscottl				DIP_SET(dp, di_size,
177134589Sscottl				    roundup(inp->i_isize, DIRBLKSIZ));
178344887Skib				inodirty(dp);
1791558Srgrimes			}
1801558Srgrimes		}
18198542Smckusick		dp = &dino;
18298542Smckusick		memset(dp, 0, sizeof(struct ufs2_dinode));
183134589Sscottl		DIP_SET(dp, di_mode, IFDIR);
184134589Sscottl		DIP_SET(dp, di_size, inp->i_isize);
185298907Saraujo		for (i = 0; i < MIN(inp->i_numblks, NDADDR); i++)
186134589Sscottl			DIP_SET(dp, di_db[i], inp->i_blks[i]);
18798542Smckusick		if (inp->i_numblks > NDADDR)
18898542Smckusick			for (i = 0; i < NIADDR; i++)
189134589Sscottl				DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]);
1901558Srgrimes		curino.id_number = inp->i_number;
1911558Srgrimes		curino.id_parent = inp->i_parent;
1921558Srgrimes		(void)ckinode(dp, &curino);
1931558Srgrimes	}
1941558Srgrimes	/*
1951558Srgrimes	 * Now that the parents of all directories have been found,
1961558Srgrimes	 * make another pass to verify the value of `..'
1971558Srgrimes	 */
1981558Srgrimes	for (inpp = inpsort; inpp < inpend; inpp++) {
1991558Srgrimes		inp = *inpp;
2001558Srgrimes		if (inp->i_parent == 0 || inp->i_isize == 0)
2011558Srgrimes			continue;
20241474Sjulian		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
203136281Struckman		    INO_IS_DUNFOUND(inp->i_number))
20441474Sjulian			inoinfo(inp->i_number)->ino_state = DFOUND;
2051558Srgrimes		if (inp->i_dotdot == inp->i_parent ||
2061558Srgrimes		    inp->i_dotdot == (ino_t)-1)
2071558Srgrimes			continue;
2081558Srgrimes		if (inp->i_dotdot == 0) {
2091558Srgrimes			inp->i_dotdot = inp->i_parent;
2101558Srgrimes			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
2111558Srgrimes			if (reply("FIX") == 0)
2121558Srgrimes				continue;
2131558Srgrimes			(void)makeentry(inp->i_number, inp->i_parent, "..");
21441474Sjulian			inoinfo(inp->i_parent)->ino_linkcnt--;
2151558Srgrimes			continue;
2161558Srgrimes		}
217202109Smckusick		/*
218202109Smckusick		 * Here we have:
219202109Smckusick		 *    inp->i_number is directory with bad ".." in it.
220202109Smckusick		 *    inp->i_dotdot is current value of "..".
221202109Smckusick		 *    inp->i_parent is directory to which ".." should point.
222202109Smckusick		 */
223202109Smckusick		getpathname(pathbuf, inp->i_parent, inp->i_number);
224241012Smdf		printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
225241012Smdf		    (uintmax_t)inp->i_number, pathbuf);
226202109Smckusick		getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
227241012Smdf		printf("CURRENTLY POINTS TO I=%ju (%s), ",
228241012Smdf		    (uintmax_t)inp->i_dotdot, pathbuf);
229202109Smckusick		getpathname(pathbuf, inp->i_parent, inp->i_parent);
230241012Smdf		printf("SHOULD POINT TO I=%ju (%s)",
231241012Smdf		    (uintmax_t)inp->i_parent, pathbuf);
232202109Smckusick		if (cursnapshot != 0) {
233202109Smckusick			/*
234202109Smckusick			 * We need to:
235202109Smckusick			 *    setcwd(inp->i_number);
236202109Smckusick			 *    setdotdot(inp->i_dotdot, inp->i_parent);
237202109Smckusick			 */
238202109Smckusick			cmd.value = inp->i_number;
239202109Smckusick			if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
240202109Smckusick			    &cmd, sizeof cmd) == -1) {
241202109Smckusick				/* kernel lacks support for these functions */
242202109Smckusick				printf(" (IGNORED)\n");
243202109Smckusick				continue;
244202109Smckusick			}
245202109Smckusick			cmd.value = inp->i_dotdot; /* verify same value */
246202109Smckusick			cmd.size = inp->i_parent;  /* new parent */
247202109Smckusick			if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
248202109Smckusick			    &cmd, sizeof cmd) == -1) {
249202109Smckusick				printf(" (FIX FAILED: %s)\n", strerror(errno));
250202109Smckusick				continue;
251202109Smckusick			}
252202109Smckusick			printf(" (FIXED)\n");
253202109Smckusick			inoinfo(inp->i_parent)->ino_linkcnt--;
254202109Smckusick			inp->i_dotdot = inp->i_parent;
2551558Srgrimes			continue;
256202109Smckusick		}
257202109Smckusick		if (preen)
258202109Smckusick			printf(" (FIXED)\n");
259202109Smckusick		else if (reply("FIX") == 0)
260202109Smckusick			continue;
26141474Sjulian		inoinfo(inp->i_dotdot)->ino_linkcnt++;
26241474Sjulian		inoinfo(inp->i_parent)->ino_linkcnt--;
2631558Srgrimes		inp->i_dotdot = inp->i_parent;
2641558Srgrimes		(void)changeino(inp->i_number, "..", inp->i_parent);
2651558Srgrimes	}
2661558Srgrimes	/*
2671558Srgrimes	 * Mark all the directories that can be found from the root.
2681558Srgrimes	 */
2691558Srgrimes	propagate();
2701558Srgrimes}
2711558Srgrimes
27223675Speterstatic int
27392839Simppass2check(struct inodesc *idesc)
2741558Srgrimes{
27592806Sobrien	struct direct *dirp = idesc->id_dirp;
276208330Smckusick	char dirname[MAXPATHLEN + 1];
27792806Sobrien	struct inoinfo *inp;
2781558Srgrimes	int n, entrysize, ret = 0;
27998542Smckusick	union dinode *dp;
280100935Sphk	const char *errmsg;
2811558Srgrimes	struct direct proto;
2821558Srgrimes
2831558Srgrimes	/*
2841558Srgrimes	 * check for "."
2851558Srgrimes	 */
286176574Sdelphij	if (dirp->d_ino > maxino)
287176574Sdelphij		goto chk2;
2881558Srgrimes	if (idesc->id_entryno != 0)
2891558Srgrimes		goto chk1;
2901558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2911558Srgrimes		if (dirp->d_ino != idesc->id_number) {
2921558Srgrimes			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2931558Srgrimes			dirp->d_ino = idesc->id_number;
2941558Srgrimes			if (reply("FIX") == 1)
2951558Srgrimes				ret |= ALTERED;
2961558Srgrimes		}
29796483Sphk		if (dirp->d_type != DT_DIR) {
2981558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
2991558Srgrimes			dirp->d_type = DT_DIR;
3001558Srgrimes			if (reply("FIX") == 1)
3011558Srgrimes				ret |= ALTERED;
3021558Srgrimes		}
3031558Srgrimes		goto chk1;
3041558Srgrimes	}
3051558Srgrimes	direrror(idesc->id_number, "MISSING '.'");
3061558Srgrimes	proto.d_ino = idesc->id_number;
30796483Sphk	proto.d_type = DT_DIR;
3081558Srgrimes	proto.d_namlen = 1;
3091558Srgrimes	(void)strcpy(proto.d_name, ".");
3101558Srgrimes	entrysize = DIRSIZ(0, &proto);
3111558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
3121558Srgrimes		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
3131558Srgrimes			dirp->d_name);
3141558Srgrimes	} else if (dirp->d_reclen < entrysize) {
3151558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
3161558Srgrimes	} else if (dirp->d_reclen < 2 * entrysize) {
3171558Srgrimes		proto.d_reclen = dirp->d_reclen;
31823675Speter		memmove(dirp, &proto, (size_t)entrysize);
3191558Srgrimes		if (reply("FIX") == 1)
3201558Srgrimes			ret |= ALTERED;
3211558Srgrimes	} else {
3221558Srgrimes		n = dirp->d_reclen - entrysize;
3231558Srgrimes		proto.d_reclen = entrysize;
32423675Speter		memmove(dirp, &proto, (size_t)entrysize);
3251558Srgrimes		idesc->id_entryno++;
32641474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3271558Srgrimes		dirp = (struct direct *)((char *)(dirp) + entrysize);
32823675Speter		memset(dirp, 0, (size_t)n);
3291558Srgrimes		dirp->d_reclen = n;
3301558Srgrimes		if (reply("FIX") == 1)
3311558Srgrimes			ret |= ALTERED;
3321558Srgrimes	}
3331558Srgrimeschk1:
3341558Srgrimes	if (idesc->id_entryno > 1)
3351558Srgrimes		goto chk2;
3361558Srgrimes	inp = getinoinfo(idesc->id_number);
3371558Srgrimes	proto.d_ino = inp->i_parent;
33896483Sphk	proto.d_type = DT_DIR;
3391558Srgrimes	proto.d_namlen = 2;
3401558Srgrimes	(void)strcpy(proto.d_name, "..");
3411558Srgrimes	entrysize = DIRSIZ(0, &proto);
3421558Srgrimes	if (idesc->id_entryno == 0) {
3431558Srgrimes		n = DIRSIZ(0, dirp);
3441558Srgrimes		if (dirp->d_reclen < n + entrysize)
3451558Srgrimes			goto chk2;
3461558Srgrimes		proto.d_reclen = dirp->d_reclen - n;
3471558Srgrimes		dirp->d_reclen = n;
3481558Srgrimes		idesc->id_entryno++;
34941474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3501558Srgrimes		dirp = (struct direct *)((char *)(dirp) + n);
35123675Speter		memset(dirp, 0, (size_t)proto.d_reclen);
3521558Srgrimes		dirp->d_reclen = proto.d_reclen;
3531558Srgrimes	}
3541558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
3551558Srgrimes		inp->i_dotdot = dirp->d_ino;
35696483Sphk		if (dirp->d_type != DT_DIR) {
3571558Srgrimes			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
3581558Srgrimes			dirp->d_type = DT_DIR;
3591558Srgrimes			if (reply("FIX") == 1)
3601558Srgrimes				ret |= ALTERED;
3611558Srgrimes		}
3621558Srgrimes		goto chk2;
3631558Srgrimes	}
3641558Srgrimes	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
3651558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3661558Srgrimes		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
3671558Srgrimes			dirp->d_name);
3681558Srgrimes		inp->i_dotdot = (ino_t)-1;
3691558Srgrimes	} else if (dirp->d_reclen < entrysize) {
3701558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3711558Srgrimes		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
3721558Srgrimes		inp->i_dotdot = (ino_t)-1;
3731558Srgrimes	} else if (inp->i_parent != 0) {
3741558Srgrimes		/*
3751558Srgrimes		 * We know the parent, so fix now.
3761558Srgrimes		 */
3771558Srgrimes		inp->i_dotdot = inp->i_parent;
3781558Srgrimes		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
3791558Srgrimes		proto.d_reclen = dirp->d_reclen;
38023675Speter		memmove(dirp, &proto, (size_t)entrysize);
3811558Srgrimes		if (reply("FIX") == 1)
3821558Srgrimes			ret |= ALTERED;
3831558Srgrimes	}
3841558Srgrimes	idesc->id_entryno++;
3851558Srgrimes	if (dirp->d_ino != 0)
38641474Sjulian		inoinfo(dirp->d_ino)->ino_linkcnt--;
3871558Srgrimes	return (ret|KEEPON);
3881558Srgrimeschk2:
3891558Srgrimes	if (dirp->d_ino == 0)
3901558Srgrimes		return (ret|KEEPON);
3911558Srgrimes	if (dirp->d_namlen <= 2 &&
3921558Srgrimes	    dirp->d_name[0] == '.' &&
3931558Srgrimes	    idesc->id_entryno >= 2) {
3941558Srgrimes		if (dirp->d_namlen == 1) {
3951558Srgrimes			direrror(idesc->id_number, "EXTRA '.' ENTRY");
3961558Srgrimes			dirp->d_ino = 0;
3971558Srgrimes			if (reply("FIX") == 1)
3981558Srgrimes				ret |= ALTERED;
3991558Srgrimes			return (KEEPON | ret);
4001558Srgrimes		}
4011558Srgrimes		if (dirp->d_name[1] == '.') {
4021558Srgrimes			direrror(idesc->id_number, "EXTRA '..' ENTRY");
4031558Srgrimes			dirp->d_ino = 0;
4041558Srgrimes			if (reply("FIX") == 1)
4051558Srgrimes				ret |= ALTERED;
4061558Srgrimes			return (KEEPON | ret);
4071558Srgrimes		}
4081558Srgrimes	}
4091558Srgrimes	idesc->id_entryno++;
4101558Srgrimes	n = 0;
4111558Srgrimes	if (dirp->d_ino > maxino) {
4121558Srgrimes		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
4131558Srgrimes		n = reply("REMOVE");
41496483Sphk	} else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) ||
41523675Speter		    (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) {
41623675Speter		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
41723675Speter		dirp->d_ino = WINO;
41823675Speter		dirp->d_type = DT_WHT;
41923675Speter		if (reply("FIX") == 1)
42023675Speter			ret |= ALTERED;
4211558Srgrimes	} else {
4221558Srgrimesagain:
42341474Sjulian		switch (inoinfo(dirp->d_ino)->ino_state) {
4241558Srgrimes		case USTATE:
4251558Srgrimes			if (idesc->id_entryno <= 2)
4261558Srgrimes				break;
4271558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
4281558Srgrimes			n = reply("REMOVE");
4291558Srgrimes			break;
4301558Srgrimes
4311558Srgrimes		case DCLEAR:
4321558Srgrimes		case FCLEAR:
4331558Srgrimes			if (idesc->id_entryno <= 2)
4341558Srgrimes				break;
43541474Sjulian			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
4361558Srgrimes				errmsg = "DUP/BAD";
43734266Sjulian			else if (!preen && !usedsoftdep)
4381558Srgrimes				errmsg = "ZERO LENGTH DIRECTORY";
439208330Smckusick			else if (cursnapshot == 0) {
4401558Srgrimes				n = 1;
4411558Srgrimes				break;
442208330Smckusick			} else {
443208330Smckusick				getpathname(dirname, idesc->id_number,
444208330Smckusick				    dirp->d_ino);
445241012Smdf				pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
446241012Smdf				    dirname, (uintmax_t)dirp->d_ino);
447208330Smckusick				/*
448208330Smckusick				 * We need to:
449208330Smckusick				 *    setcwd(idesc->id_parent);
450208330Smckusick				 *    rmdir(dirp->d_name);
451208330Smckusick				 */
452208330Smckusick				cmd.value = idesc->id_number;
453208330Smckusick				if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
454208330Smckusick				    &cmd, sizeof cmd) == -1) {
455208330Smckusick					/* kernel lacks support */
456208330Smckusick					printf(" (IGNORED)\n");
457208330Smckusick					n = 1;
458208330Smckusick					break;
459208330Smckusick				}
460208330Smckusick				if (rmdir(dirp->d_name) == -1) {
461208330Smckusick					printf(" (REMOVAL FAILED: %s)\n",
462208330Smckusick					    strerror(errno));
463208330Smckusick					n = 1;
464208330Smckusick					break;
465208330Smckusick				}
466208330Smckusick				/* ".." reference to parent is removed */
467208330Smckusick				inoinfo(idesc->id_number)->ino_linkcnt--;
468208330Smckusick				printf(" (REMOVED)\n");
469208330Smckusick				break;
4701558Srgrimes			}
4711558Srgrimes			fileerror(idesc->id_number, dirp->d_ino, errmsg);
4721558Srgrimes			if ((n = reply("REMOVE")) == 1)
4731558Srgrimes				break;
4741558Srgrimes			dp = ginode(dirp->d_ino);
47541474Sjulian			inoinfo(dirp->d_ino)->ino_state =
47698542Smckusick			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
47798542Smckusick			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
4781558Srgrimes			goto again;
4791558Srgrimes
4801558Srgrimes		case DSTATE:
481136281Struckman		case DZLINK:
48241474Sjulian			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
48341474Sjulian				inoinfo(dirp->d_ino)->ino_state = DFOUND;
484102411Scharnier			/* FALLTHROUGH */
4851558Srgrimes
4861558Srgrimes		case DFOUND:
4871558Srgrimes			inp = getinoinfo(dirp->d_ino);
488202107Smckusick			if (idesc->id_entryno > 2) {
489202107Smckusick				if (inp->i_parent == 0)
490202107Smckusick					inp->i_parent = idesc->id_number;
491202107Smckusick				else if ((n = fix_extraneous(inp, idesc)) == 1)
49274556Smckusick					break;
4931558Srgrimes			}
494102411Scharnier			/* FALLTHROUGH */
4951558Srgrimes
4961558Srgrimes		case FSTATE:
497136281Struckman		case FZLINK:
49896483Sphk			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
4991558Srgrimes				fileerror(idesc->id_number, dirp->d_ino,
5001558Srgrimes				    "BAD TYPE VALUE");
50141474Sjulian				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
5021558Srgrimes				if (reply("FIX") == 1)
5031558Srgrimes					ret |= ALTERED;
5041558Srgrimes			}
50541474Sjulian			inoinfo(dirp->d_ino)->ino_linkcnt--;
5061558Srgrimes			break;
5071558Srgrimes
5081558Srgrimes		default:
509241012Smdf			errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
510241012Smdf			    inoinfo(dirp->d_ino)->ino_state,
511241012Smdf			    (uintmax_t)dirp->d_ino);
5121558Srgrimes		}
5131558Srgrimes	}
5141558Srgrimes	if (n == 0)
5151558Srgrimes		return (ret|KEEPON);
5161558Srgrimes	dirp->d_ino = 0;
5171558Srgrimes	return (ret|KEEPON|ALTERED);
5181558Srgrimes}
5191558Srgrimes
520202107Smckusickstatic int
521202107Smckusickfix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
522202107Smckusick{
523202109Smckusick	char *cp;
524202107Smckusick	struct inodesc dotdesc;
525202107Smckusick	char oldname[MAXPATHLEN + 1];
526202107Smckusick	char newname[MAXPATHLEN + 1];
527221110Sdes
528202107Smckusick	/*
529202107Smckusick	 * If we have not yet found "..", look it up now so we know
530202107Smckusick	 * which inode the directory itself believes is its parent.
531202107Smckusick	 */
532202107Smckusick	if (inp->i_dotdot == 0) {
533202107Smckusick		memset(&dotdesc, 0, sizeof(struct inodesc));
534202107Smckusick		dotdesc.id_type = DATA;
535202107Smckusick		dotdesc.id_number = idesc->id_dirp->d_ino;
536202107Smckusick		dotdesc.id_func = findino;
537202107Smckusick		dotdesc.id_name = strdup("..");
538202107Smckusick		if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
539202107Smckusick			inp->i_dotdot = dotdesc.id_parent;
540202107Smckusick	}
541202107Smckusick	/*
542202107Smckusick	 * We have the previously found old name (inp->i_parent) and the
543202107Smckusick	 * just found new name (idesc->id_number). We have five cases:
544202107Smckusick	 * 1)  ".." is missing - can remove either name, choose to delete
545202107Smckusick	 *     new one and let fsck create ".." pointing to old name.
546202107Smckusick	 * 2) Both new and old are in same directory, choose to delete
547202107Smckusick	 *    the new name and let fsck fix ".." if it is wrong.
548202107Smckusick	 * 3) ".." does not point to the new name, so delete it and let
549202107Smckusick	 *    fsck fix ".." to point to the old one if it is wrong.
550202107Smckusick	 * 4) ".." points to the old name only, so delete the new one.
551202107Smckusick	 * 5) ".." points to the new name only, so delete the old one.
552202107Smckusick	 *
553202107Smckusick	 * For cases 1-4 we eliminate the new name;
554202107Smckusick	 * for case 5 we eliminate the old name.
555202107Smckusick	 */
556202107Smckusick	if (inp->i_dotdot == 0 ||		    /* Case 1 */
557202107Smckusick	    idesc->id_number == inp->i_parent ||    /* Case 2 */
558202107Smckusick	    inp->i_dotdot != idesc->id_number ||    /* Case 3 */
559202107Smckusick	    inp->i_dotdot == inp->i_parent) {	    /* Case 4 */
560202107Smckusick		getpathname(newname, idesc->id_number, idesc->id_number);
561202107Smckusick		if (strcmp(newname, "/") != 0)
562202107Smckusick			strcat (newname, "/");
563202107Smckusick		strcat(newname, idesc->id_dirp->d_name);
564202107Smckusick		getpathname(oldname, inp->i_number, inp->i_number);
565202109Smckusick		pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
566202107Smckusick		    newname, oldname);
567202107Smckusick		if (cursnapshot != 0) {
568202107Smckusick			/*
569202107Smckusick			 * We need to
570202107Smckusick			 *    setcwd(idesc->id_number);
571202107Smckusick			 *    unlink(idesc->id_dirp->d_name);
572202107Smckusick			 */
573202109Smckusick			cmd.value = idesc->id_number;
574202109Smckusick			if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
575202109Smckusick			    &cmd, sizeof cmd) == -1) {
576202109Smckusick				printf(" (IGNORED)\n");
577202109Smckusick				return (0);
578202109Smckusick			}
579202131Smckusick			cmd.value = (intptr_t)idesc->id_dirp->d_name;
580202109Smckusick			cmd.size = inp->i_number; /* verify same name */
581202109Smckusick			if (sysctlbyname("vfs.ffs.unlink", 0, 0,
582202109Smckusick			    &cmd, sizeof cmd) == -1) {
583202109Smckusick				printf(" (UNLINK FAILED: %s)\n",
584202109Smckusick				    strerror(errno));
585202109Smckusick				return (0);
586202109Smckusick			}
587202109Smckusick			printf(" (REMOVED)\n");
588202107Smckusick			return (0);
589202107Smckusick		}
590202107Smckusick		if (preen) {
591202107Smckusick			printf(" (REMOVED)\n");
592202107Smckusick			return (1);
593202107Smckusick		}
594202107Smckusick		return (reply("REMOVE"));
595202107Smckusick	}
596202107Smckusick	/*
597202107Smckusick	 * None of the first four cases above, so must be case (5).
598202107Smckusick	 * Eliminate the old name and make the new the name the parent.
599202107Smckusick	 */
600202107Smckusick	getpathname(oldname, inp->i_parent, inp->i_number);
601202107Smckusick	getpathname(newname, inp->i_number, inp->i_number);
602202109Smckusick	pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
603202107Smckusick	    newname);
604202107Smckusick	if (cursnapshot != 0) {
605202107Smckusick		/*
606202107Smckusick		 * We need to
607202107Smckusick		 *    setcwd(inp->i_parent);
608202107Smckusick		 *    unlink(last component of oldname pathname);
609202107Smckusick		 */
610202109Smckusick		cmd.value = inp->i_parent;
611202109Smckusick		if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
612202109Smckusick		    &cmd, sizeof cmd) == -1) {
613202109Smckusick			printf(" (IGNORED)\n");
614202109Smckusick			return (0);
615202109Smckusick		}
616229403Sed		if ((cp = strchr(oldname, '/')) == NULL) {
617202109Smckusick			printf(" (IGNORED)\n");
618202109Smckusick			return (0);
619202109Smckusick		}
620202131Smckusick		cmd.value = (intptr_t)(cp + 1);
621202109Smckusick		cmd.size = inp->i_number; /* verify same name */
622202109Smckusick		if (sysctlbyname("vfs.ffs.unlink", 0, 0,
623202109Smckusick		    &cmd, sizeof cmd) == -1) {
624202109Smckusick			printf(" (UNLINK FAILED: %s)\n",
625202109Smckusick			    strerror(errno));
626202109Smckusick			return (0);
627202109Smckusick		}
628202109Smckusick		printf(" (REMOVED)\n");
629202109Smckusick		inp->i_parent = idesc->id_number;  /* reparent to correct dir */
630202107Smckusick		return (0);
631202107Smckusick	}
632202107Smckusick	if (!preen && !reply("REMOVE"))
633202107Smckusick		return (0);
634202107Smckusick	memset(&dotdesc, 0, sizeof(struct inodesc));
635202107Smckusick	dotdesc.id_type = DATA;
636202107Smckusick	dotdesc.id_number = inp->i_parent; /* directory in which name appears */
637202107Smckusick	dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
638202107Smckusick	dotdesc.id_func = deleteentry;
639202107Smckusick	if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
640202107Smckusick		printf(" (REMOVED)\n");
641202107Smckusick	inp->i_parent = idesc->id_number;  /* reparent to correct directory */
642202107Smckusick	inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
643202107Smckusick	return (0);
644202107Smckusick}
645202107Smckusick
646202107Smckusickstatic int
647202107Smckusickdeleteentry(struct inodesc *idesc)
648202107Smckusick{
649202107Smckusick	struct direct *dirp = idesc->id_dirp;
650202107Smckusick
651202107Smckusick	if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
652202107Smckusick		return (KEEPON);
653202107Smckusick	dirp->d_ino = 0;
654202107Smckusick	return (ALTERED|STOP|FOUND);
655202107Smckusick}
656202107Smckusick
6571558Srgrimes/*
6581558Srgrimes * Routine to sort disk blocks.
6591558Srgrimes */
66023675Speterstatic int
66192839Simpblksort(const void *arg1, const void *arg2)
6621558Srgrimes{
6631558Srgrimes
664100935Sphk	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
665100935Sphk		(*(struct inoinfo * const *)arg2)->i_blks[0]);
6661558Srgrimes}
667