19111Stwisti/*-
212886Shseigel * SPDX-License-Identifier: BSD-3-Clause
39111Stwisti *
49111Stwisti * Copyright (c) 1980, 1986, 1993
59111Stwisti *	The Regents of the University of California.  All rights reserved.
69111Stwisti *
79111Stwisti * Redistribution and use in source and binary forms, with or without
89111Stwisti * modification, are permitted provided that the following conditions
99111Stwisti * are met:
109111Stwisti * 1. Redistributions of source code must retain the above copyright
119111Stwisti *    notice, this list of conditions and the following disclaimer.
129111Stwisti * 2. Redistributions in binary form must reproduce the above copyright
139111Stwisti *    notice, this list of conditions and the following disclaimer in the
149111Stwisti *    documentation and/or other materials provided with the distribution.
159111Stwisti * 3. Neither the name of the University nor the names of its contributors
169111Stwisti *    may be used to endorse or promote products derived from this software
179111Stwisti *    without specific prior written permission.
189111Stwisti *
199111Stwisti * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209111Stwisti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219111Stwisti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229111Stwisti * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239111Stwisti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249111Stwisti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259111Stwisti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269111Stwisti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279111Stwisti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289111Stwisti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299111Stwisti * SUCH DAMAGE.
309111Stwisti */
319111Stwisti
329111Stwisti#include <sys/param.h>
339111Stwisti#include <sys/sysctl.h>
349111Stwisti
359111Stwisti#include <ufs/ufs/dinode.h>
369111Stwisti#include <ufs/ufs/dir.h>
379111Stwisti#include <ufs/ffs/fs.h>
389111Stwisti
3910762Sjprovino#include <err.h>
409111Stwisti#include <errno.h>
419111Stwisti#include <stdint.h>
429111Stwisti#include <string.h>
439111Stwisti
449111Stwisti#include "fsck.h"
459111Stwisti
469111Stwisti#define MINDIRSIZE	(sizeof (struct dirtemplate))
479111Stwisti
489111Stwististatic int fix_extraneous(struct inoinfo *, struct inodesc *);
499111Stwististatic int deleteentry(struct inodesc *);
509111Stwististatic int blksort(const void *, const void *);
519111Stwististatic int pass2check(struct inodesc *);
5210047Snever
5310047Snevervoid
5410047Sneverpass2(void)
5510047Snever{
5610047Snever	struct inode ip;
5710047Snever	union dinode *dp;
5810047Snever	struct inoinfo **inpp, *inp;
5910047Snever	struct inoinfo **inpend;
6010047Snever	struct inodesc curino;
6110047Snever	union dinode dino;
6210047Snever	int i;
639111Stwisti	char pathbuf[MAXPATHLEN + 1];
649111Stwisti
659111Stwisti	switch (inoinfo(UFS_ROOTINO)->ino_state) {
669111Stwisti
679111Stwisti	case USTATE:
6812953Scoleenp		pfatal("ROOT INODE UNALLOCATED");
699266Scoleenp		if (reply("ALLOCATE") == 0) {
7012953Scoleenp			ckfini(0);
719111Stwisti			exit(EEXIT);
729266Scoleenp		}
739111Stwisti		if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
749111Stwisti			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
759111Stwisti		break;
769266Scoleenp
779111Stwisti	case DCLEAR:
7812953Scoleenp		pfatal("DUPS/BAD IN ROOT INODE");
799111Stwisti		if (reply("REALLOCATE")) {
809266Scoleenp			freedirino(UFS_ROOTINO, UFS_ROOTINO);
8110420Salanb			if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
8212953Scoleenp			    UFS_ROOTINO)
8310420Salanb				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
849111Stwisti			break;
859111Stwisti		}
869111Stwisti		if (reply("CONTINUE") == 0) {
879111Stwisti			ckfini(0);
889111Stwisti			exit(EEXIT);
8912953Scoleenp		}
9012953Scoleenp		break;
9112953Scoleenp
9212953Scoleenp	case FSTATE:
939111Stwisti	case FCLEAR:
949111Stwisti	case FZLINK:
959111Stwisti		pfatal("ROOT INODE NOT DIRECTORY");
969111Stwisti		if (reply("REALLOCATE")) {
979111Stwisti			freeino(UFS_ROOTINO);
989111Stwisti			if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
999111Stwisti			    UFS_ROOTINO)
1009111Stwisti				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
1019111Stwisti			break;
10212953Scoleenp		}
1039111Stwisti		if (reply("FIX") == 0) {
1049111Stwisti			ckfini(0);
1059111Stwisti			exit(EEXIT);
1069111Stwisti		}
1079111Stwisti		ginode(UFS_ROOTINO, &ip);
10812953Scoleenp		dp = ip.i_dp;
1099111Stwisti		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
1109111Stwisti		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
1119111Stwisti		inodirty(&ip);
1129111Stwisti		irelse(&ip);
11312953Scoleenp		break;
1149111Stwisti
1159111Stwisti	case DSTATE:
1169111Stwisti	case DZLINK:
1179111Stwisti		break;
11812953Scoleenp
1199111Stwisti	default:
12012953Scoleenp		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
1219111Stwisti		    inoinfo(UFS_ROOTINO)->ino_state);
1229111Stwisti	}
1239111Stwisti	inoinfo(UFS_ROOTINO)->ino_state = DFOUND;
1249111Stwisti	inoinfo(UFS_WINO)->ino_state = FSTATE;
1259111Stwisti	inoinfo(UFS_WINO)->ino_type = DT_WHT;
1269111Stwisti	/*
1279111Stwisti	 * Sort the directory list into disk block order.
1289111Stwisti	 */
1299111Stwisti	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
1309111Stwisti	/*
1319111Stwisti	 * Check the integrity of each directory.
1329111Stwisti	 */
1339111Stwisti	memset(&curino, 0, sizeof(struct inodesc));
1349111Stwisti	curino.id_type = DATA;
1359111Stwisti	curino.id_func = pass2check;
13612953Scoleenp	inpend = &inpsort[inplast];
1379111Stwisti	for (inpp = inpsort; inpp < inpend; inpp++) {
1389111Stwisti		if (got_siginfo) {
13912953Scoleenp			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
1409111Stwisti			    inpp - inpsort, (int)inplast,
1419111Stwisti			    (int)((inpp - inpsort) * 100 / inplast));
1429111Stwisti			got_siginfo = 0;
1439111Stwisti		}
14412953Scoleenp		if (got_sigalarm) {
1459111Stwisti			setproctitle("%s p2 %d%%", cdevname,
14612953Scoleenp			    (int)((inpp - inpsort) * 100 / inplast));
1479111Stwisti			got_sigalarm = 0;
1489111Stwisti		}
1499111Stwisti		inp = *inpp;
15012953Scoleenp		if (inp->i_isize == 0)
1519111Stwisti			continue;
1529111Stwisti		if (inp->i_isize < MINDIRSIZE) {
1539111Stwisti			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1549111Stwisti			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1559111Stwisti			if (reply("FIX") == 1) {
1569111Stwisti				ginode(inp->i_number, &ip);
1579111Stwisti				DIP_SET(ip.i_dp, di_size, inp->i_isize);
1589111Stwisti				inodirty(&ip);
1599111Stwisti				irelse(&ip);
1609111Stwisti			}
1619111Stwisti		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
16212953Scoleenp			getpathname(pathbuf, inp->i_number, inp->i_number);
1639111Stwisti			if (usedsoftdep)
1649111Stwisti				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
1659111Stwisti					"DIRECTORY", pathbuf,
16612953Scoleenp					(intmax_t)inp->i_isize, DIRBLKSIZ);
1679111Stwisti			else
1689111Stwisti				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
1699111Stwisti					"DIRECTORY", pathbuf,
1709111Stwisti					(intmax_t)inp->i_isize, DIRBLKSIZ);
1719111Stwisti			if (preen)
17212953Scoleenp				printf(" (ADJUSTED)\n");
17312953Scoleenp			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
17412953Scoleenp			if (preen || reply("ADJUST") == 1) {
1759111Stwisti				ginode(inp->i_number, &ip);
1769111Stwisti				DIP_SET(ip.i_dp, di_size,
1779111Stwisti				    roundup(inp->i_isize, DIRBLKSIZ));
1789111Stwisti				inodirty(&ip);
17912953Scoleenp				irelse(&ip);
1809111Stwisti			}
1819111Stwisti		}
18212953Scoleenp		dp = &dino;
1839111Stwisti		memset(dp, 0, sizeof(struct ufs2_dinode));
18412953Scoleenp		DIP_SET(dp, di_mode, IFDIR);
1859111Stwisti		DIP_SET(dp, di_size, inp->i_isize);
18612953Scoleenp		for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++)
1879111Stwisti			DIP_SET(dp, di_db[i], inp->i_blks[i]);
1889111Stwisti		if (inp->i_numblks > UFS_NDADDR)
1899111Stwisti			for (i = 0; i < UFS_NIADDR; i++)
19012953Scoleenp				DIP_SET(dp, di_ib[i],
1919111Stwisti				    inp->i_blks[UFS_NDADDR + i]);
19212953Scoleenp		curino.id_number = inp->i_number;
19312953Scoleenp		curino.id_parent = inp->i_parent;
19412953Scoleenp		(void)ckinode(dp, &curino);
19512953Scoleenp	}
1969111Stwisti	/*
19712953Scoleenp	 * Now that the parents of all directories have been found,
1989111Stwisti	 * make another pass to verify the value of `..'
1999111Stwisti	 */
20012953Scoleenp	for (inpp = inpsort; inpp < inpend; inpp++) {
2019111Stwisti		inp = *inpp;
2029111Stwisti		if (inp->i_parent == 0 || inp->i_isize == 0)
2039111Stwisti			continue;
2049111Stwisti		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
2059111Stwisti		    INO_IS_DUNFOUND(inp->i_number)) {
2069111Stwisti			inoinfo(inp->i_number)->ino_state = DFOUND;
2079111Stwisti			check_dirdepth(inp);
20812953Scoleenp		}
2099111Stwisti		if (inp->i_dotdot == inp->i_parent ||
2109111Stwisti		    inp->i_dotdot == (ino_t)-1)
2119111Stwisti			continue;
2129111Stwisti		if (inp->i_dotdot == 0) {
2139111Stwisti			inp->i_dotdot = inp->i_parent;
2149111Stwisti			if (debug)
2159111Stwisti				fileerror(inp->i_parent, inp->i_number,
2169111Stwisti				    "DEFERRED MISSING '..' FIX");
2179111Stwisti			(void)makeentry(inp->i_number, inp->i_parent, "..");
2189111Stwisti			inoinfo(inp->i_parent)->ino_linkcnt--;
2199111Stwisti			continue;
22012953Scoleenp		}
22112953Scoleenp		/*
22212953Scoleenp		 * Here we have:
22312953Scoleenp		 *    inp->i_number is directory with bad ".." in it.
2249111Stwisti		 *    inp->i_dotdot is current value of "..".
22512953Scoleenp		 *    inp->i_parent is directory to which ".." should point.
2269111Stwisti		 */
2279111Stwisti		getpathname(pathbuf, inp->i_parent, inp->i_number);
2289111Stwisti		printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
2299111Stwisti		    (uintmax_t)inp->i_number, pathbuf);
2309111Stwisti		getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
2319111Stwisti		printf("CURRENTLY POINTS TO I=%ju (%s), ",
2329111Stwisti		    (uintmax_t)inp->i_dotdot, pathbuf);
23312953Scoleenp		getpathname(pathbuf, inp->i_parent, inp->i_parent);
2349111Stwisti		printf("SHOULD POINT TO I=%ju (%s)",
2359111Stwisti		    (uintmax_t)inp->i_parent, pathbuf);
2369111Stwisti		if (cursnapshot != 0) {
2379111Stwisti			/*
2389111Stwisti			 * We need to:
2399111Stwisti			 *    setcwd(inp->i_number);
2409111Stwisti			 *    setdotdot(inp->i_dotdot, inp->i_parent);
2419111Stwisti			 */
2429111Stwisti			cmd.value = inp->i_number;
2439111Stwisti			if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
2449111Stwisti			    &cmd, sizeof cmd) == -1) {
2459111Stwisti				/* kernel lacks support for these functions */
2469111Stwisti				printf(" (IGNORED)\n");
2479111Stwisti				continue;
2489111Stwisti			}
2499111Stwisti			cmd.value = inp->i_dotdot; /* verify same value */
2509111Stwisti			cmd.size = inp->i_parent;  /* new parent */
25112953Scoleenp			if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
25212953Scoleenp			    &cmd, sizeof cmd) == -1) {
25312953Scoleenp				printf(" (FIX FAILED: %s)\n", strerror(errno));
2549111Stwisti				continue;
2559111Stwisti			}
2569111Stwisti			printf(" (FIXED)\n");
2579111Stwisti			inoinfo(inp->i_parent)->ino_linkcnt--;
2589111Stwisti			inp->i_dotdot = inp->i_parent;
2599111Stwisti			continue;
2609111Stwisti		}
2619111Stwisti		if (preen)
2629111Stwisti			printf(" (FIXED)\n");
2639111Stwisti		else if (reply("FIX") == 0)
26412953Scoleenp			continue;
2659111Stwisti		inoinfo(inp->i_dotdot)->ino_linkcnt++;
2669111Stwisti		inoinfo(inp->i_parent)->ino_linkcnt--;
2679111Stwisti		inp->i_dotdot = inp->i_parent;
2689111Stwisti		(void)changeino(inp->i_number, "..", inp->i_parent,
2699111Stwisti		    getinoinfo(inp->i_parent)->i_depth  + 1);
2709111Stwisti	}
2719111Stwisti	/*
2729111Stwisti	 * Mark all the directories that can be found from the root.
2739111Stwisti	 */
27412953Scoleenp	propagate();
2759111Stwisti}
2769111Stwisti
2779111Stwististatic int
2789111Stwistipass2check(struct inodesc *idesc)
2799111Stwisti{
2809111Stwisti	struct direct *dirp = idesc->id_dirp;
2819111Stwisti	char dirname[MAXPATHLEN + 1];
28212953Scoleenp	struct inoinfo *inp;
28312953Scoleenp	int n, entrysize, ret = 0;
28412953Scoleenp	struct inode ip;
28512953Scoleenp	union dinode *dp;
28611352Scoleenp	const char *errmsg;
28711352Scoleenp	struct direct proto, *newdirp;
28812623Sjcm
28912953Scoleenp	/*
29012623Sjcm	 * check for "."
2919111Stwisti	 */
29212953Scoleenp	if (idesc->id_entryno != 0)
2939111Stwisti		goto chk1;
2949111Stwisti	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2959111Stwisti		if (dirp->d_ino != idesc->id_number) {
2969111Stwisti			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2979111Stwisti			if (reply("FIX") == 1) {
2989111Stwisti				dirp->d_ino = idesc->id_number;
2999111Stwisti				ret |= ALTERED;
3009111Stwisti			}
3019111Stwisti		}
3029111Stwisti		if (dirp->d_type != DT_DIR) {
3039111Stwisti			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
3049111Stwisti			if (reply("FIX") == 1) {
3059111Stwisti				dirp->d_type = DT_DIR;
3069111Stwisti				ret |= ALTERED;
3079111Stwisti			}
3089111Stwisti		}
3099111Stwisti		goto chk1;
3109111Stwisti	}
3119111Stwisti	proto.d_ino = idesc->id_number;
3129111Stwisti	proto.d_type = DT_DIR;
3139111Stwisti	proto.d_namlen = 1;
3149111Stwisti	(void)strcpy(proto.d_name, ".");
3159111Stwisti	entrysize = DIRSIZ(0, &proto);
3169111Stwisti	direrror(idesc->id_number, "MISSING '.'");
3179111Stwisti	errmsg = "ADD '.' ENTRY";
3189248Scoleenp	if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
3199111Stwisti		/* Not enough space to add '.', replace first entry with '.' */
32012953Scoleenp		if (dirp->d_ino != 0) {
3219111Stwisti			pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n",
3229111Stwisti			     dirp->d_name);
3239111Stwisti			errmsg = "REPLACE WITH '.'";
3249111Stwisti		}
3259111Stwisti		if (reply(errmsg) == 0)
3269111Stwisti			goto chk1;
3279111Stwisti		proto.d_reclen = dirp->d_reclen;
3289111Stwisti		memmove(dirp, &proto, (size_t)entrysize);
3299111Stwisti		ret |= ALTERED;
3309111Stwisti	} else {
3319111Stwisti		/* Move over first entry and add '.' entry */
3329111Stwisti		if (reply(errmsg) == 0)
3339111Stwisti			goto chk1;
3349111Stwisti		newdirp = (struct direct *)((char *)(dirp) + entrysize);
3359111Stwisti		dirp->d_reclen -= entrysize;
33612953Scoleenp		memmove(newdirp, dirp, dirp->d_reclen);
3379111Stwisti		proto.d_reclen = entrysize;
3389111Stwisti		memmove(dirp, &proto, (size_t)entrysize);
3399111Stwisti		idesc->id_entryno++;
3409111Stwisti		inoinfo(idesc->id_number)->ino_linkcnt--;
3419111Stwisti		dirp = newdirp;
3429111Stwisti		ret |= ALTERED;
34312953Scoleenp	}
34412953Scoleenpchk1:
3459111Stwisti	if (idesc->id_entryno > 1)
3469111Stwisti		goto chk2;
3479111Stwisti	inp = getinoinfo(idesc->id_number);
3489111Stwisti	proto.d_ino = inp->i_parent;
3499111Stwisti	proto.d_type = DT_DIR;
3509111Stwisti	proto.d_namlen = 2;
3519111Stwisti	(void)strcpy(proto.d_name, "..");
3529111Stwisti	entrysize = DIRSIZ(0, &proto);
3539111Stwisti	if (idesc->id_entryno == 0) {
3549111Stwisti		n = DIRSIZ(0, dirp);
3559111Stwisti		if (dirp->d_reclen < n + entrysize)
3569111Stwisti			goto chk2;
3579111Stwisti		proto.d_reclen = dirp->d_reclen - n;
3589111Stwisti		dirp->d_reclen = n;
3599111Stwisti		idesc->id_entryno++;
36013254Sjwilhelm		inoinfo(dirp->d_ino)->ino_linkcnt--;
36113254Sjwilhelm		dirp = (struct direct *)((char *)(dirp) + n);
3629111Stwisti		memset(dirp, 0, (size_t)proto.d_reclen);
3639111Stwisti		dirp->d_reclen = proto.d_reclen;
3649111Stwisti	}
3659111Stwisti	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
36611352Scoleenp		if (dirp->d_ino >= maxino) {
36712623Sjcm			direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
3689111Stwisti			/*
3699111Stwisti			 * If we know parent set it now, otherwise let it
3709111Stwisti			 * point to the root inode and it will get cleaned
3719111Stwisti			 * up later if that is not correct.
3729111Stwisti			 */
3739111Stwisti			if (inp->i_parent != 0)
3749111Stwisti				dirp->d_ino = inp->i_parent;
3759111Stwisti			else
3769111Stwisti				dirp->d_ino = UFS_ROOTINO;
3779111Stwisti			if (reply("FIX") == 1)
3789111Stwisti				ret |= ALTERED;
3799111Stwisti		}
3809111Stwisti		inp->i_dotdot = dirp->d_ino;
3819111Stwisti		if (dirp->d_type != DT_DIR) {
3829111Stwisti			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
3839111Stwisti			dirp->d_type = DT_DIR;
3849111Stwisti			if (reply("FIX") == 1)
3859111Stwisti				ret |= ALTERED;
3869111Stwisti		}
38712953Scoleenp		goto chk2;
3889111Stwisti	}
3899111Stwisti	fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number,
3909111Stwisti	    idesc->id_number, "MISSING '..'");
3919266Scoleenp	errmsg = "ADD '..' ENTRY";
39212953Scoleenp	if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
3939266Scoleenp		/* No space to add '..', replace second entry with '..' */
39412953Scoleenp		if (dirp->d_ino != 0) {
3959111Stwisti			pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
3969111Stwisti			    dirp->d_name);
3979111Stwisti			errmsg = "REPLACE WITH '..'";
3989111Stwisti		}
3999111Stwisti		if (reply(errmsg) == 0) {
4009111Stwisti			inp->i_dotdot = (ino_t)-1;
4019111Stwisti			goto chk2;
4029111Stwisti		}
4039248Scoleenp		if (proto.d_ino == 0) {
4049111Stwisti			/* Defer processing until parent known */
40512953Scoleenp			idesc->id_entryno++;
4069111Stwisti			if (debug)
4079111Stwisti				printf("(FIX DEFERRED)\n");
4089111Stwisti		}
4099111Stwisti		inp->i_dotdot = proto.d_ino;
4109111Stwisti		proto.d_reclen = dirp->d_reclen;
4119111Stwisti		memmove(dirp, &proto, (size_t)entrysize);
4129111Stwisti		ret |= ALTERED;
4139111Stwisti	} else {
4149111Stwisti		/* Move over second entry and add '..' entry */
4159111Stwisti		if (reply(errmsg) == 0) {
4169111Stwisti			inp->i_dotdot = (ino_t)-1;
4179111Stwisti			goto chk2;
4189111Stwisti		}
4199111Stwisti		if (proto.d_ino == 0) {
4209111Stwisti			/* Defer processing until parent known */
4219111Stwisti			idesc->id_entryno++;
4229111Stwisti			if (debug)
4239111Stwisti				printf("(FIX DEFERRED)\n");
4249111Stwisti		}
4259111Stwisti		inp->i_dotdot = proto.d_ino;
4269111Stwisti		if (dirp->d_ino == 0) {
4279111Stwisti			proto.d_reclen = dirp->d_reclen;
4289111Stwisti			memmove(dirp, &proto, (size_t)entrysize);
4299111Stwisti		} else {
4309111Stwisti			newdirp = (struct direct *)((char *)(dirp) + entrysize);
4319111Stwisti			dirp->d_reclen -= entrysize;
4329111Stwisti			memmove(newdirp, dirp, dirp->d_reclen);
4339111Stwisti			proto.d_reclen = entrysize;
4349111Stwisti			memmove(dirp, &proto, (size_t)entrysize);
4359111Stwisti			if (dirp->d_ino != 0) {
4369111Stwisti				idesc->id_entryno++;
4379111Stwisti				inoinfo(dirp->d_ino)->ino_linkcnt--;
4389111Stwisti			}
4399111Stwisti			dirp = newdirp;
4409111Stwisti		}
44110800Snever		ret |= ALTERED;
4429111Stwisti	}
4439111Stwistichk2:
4449111Stwisti	if (dirp->d_ino == 0)
4459111Stwisti		return (ret|KEEPON);
4469111Stwisti	if (dirp->d_namlen <= 2 &&
4479111Stwisti	    dirp->d_name[0] == '.' &&
4489111Stwisti	    idesc->id_entryno >= 2) {
4499111Stwisti		if (dirp->d_namlen == 1) {
4509111Stwisti			direrror(idesc->id_number, "EXTRA '.' ENTRY");
4519111Stwisti			dirp->d_ino = 0;
4529111Stwisti			if (reply("FIX") == 1)
4539111Stwisti				ret |= ALTERED;
4549111Stwisti			return (KEEPON | ret);
4559111Stwisti		}
4569111Stwisti		if (dirp->d_name[1] == '.') {
4579111Stwisti			direrror(idesc->id_number, "EXTRA '..' ENTRY");
4589111Stwisti			dirp->d_ino = 0;
4599111Stwisti			if (reply("FIX") == 1)
4609453Stwisti				ret |= ALTERED;
4619111Stwisti			return (KEEPON | ret);
4629111Stwisti		}
4639111Stwisti	}
4649111Stwisti	idesc->id_entryno++;
4659111Stwisti	n = 0;
4669111Stwisti	if (dirp->d_ino >= maxino) {
4679111Stwisti		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
4689111Stwisti		n = reply("REMOVE");
4699111Stwisti	} else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
4709111Stwisti		    (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
4719111Stwisti		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
4729111Stwisti		dirp->d_ino = UFS_WINO;
4739111Stwisti		dirp->d_type = DT_WHT;
4749111Stwisti		if (reply("FIX") == 1)
4759111Stwisti			ret |= ALTERED;
4769111Stwisti	} else {
4779111Stwistiagain:
4789111Stwisti		switch (inoinfo(dirp->d_ino)->ino_state) {
4799111Stwisti		case USTATE:
4809111Stwisti			if (idesc->id_entryno <= 2)
4819111Stwisti				break;
4829111Stwisti			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
4839111Stwisti			n = reply("REMOVE");
4849111Stwisti			break;
4859111Stwisti
4869111Stwisti		case DCLEAR:
4879111Stwisti		case FCLEAR:
4889111Stwisti			if (idesc->id_entryno <= 2)
4899111Stwisti				break;
4909111Stwisti			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
4919111Stwisti				errmsg = "DUP/BAD";
4929111Stwisti			else if (!preen && !usedsoftdep)
4939111Stwisti				errmsg = "ZERO LENGTH DIRECTORY";
4949111Stwisti			else if (cursnapshot == 0) {
4959111Stwisti				n = 1;
4969111Stwisti				break;
4979111Stwisti			} else {
4989111Stwisti				getpathname(dirname, idesc->id_number,
4999111Stwisti				    dirp->d_ino);
5009111Stwisti				pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
5019111Stwisti				    dirname, (uintmax_t)dirp->d_ino);
5029287Stwisti				/*
5039111Stwisti				 * We need to:
5049111Stwisti				 *    setcwd(idesc->id_parent);
5059111Stwisti				 *    rmdir(dirp->d_name);
5069111Stwisti				 */
5079287Stwisti				cmd.value = idesc->id_number;
5089111Stwisti				if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
5099111Stwisti				    &cmd, sizeof cmd) == -1) {
5109111Stwisti					/* kernel lacks support */
5119111Stwisti					printf(" (IGNORED)\n");
5129111Stwisti					n = 1;
5139111Stwisti					break;
5149111Stwisti				}
5159111Stwisti				if (rmdir(dirp->d_name) == -1) {
5169111Stwisti					printf(" (REMOVAL FAILED: %s)\n",
5179111Stwisti					    strerror(errno));
5189111Stwisti					n = 1;
5199111Stwisti					break;
5209111Stwisti				}
5219111Stwisti				/* ".." reference to parent is removed */
5229111Stwisti				inoinfo(idesc->id_number)->ino_linkcnt--;
5239111Stwisti				printf(" (REMOVED)\n");
5249111Stwisti				break;
5259111Stwisti			}
5269111Stwisti			fileerror(idesc->id_number, dirp->d_ino, errmsg);
5279111Stwisti			if ((n = reply("REMOVE")) == 1)
5289111Stwisti				break;
5299111Stwisti			ginode(dirp->d_ino, &ip);
5309111Stwisti			dp = ip.i_dp;
5319111Stwisti			inoinfo(dirp->d_ino)->ino_state =
5329111Stwisti			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
5339111Stwisti			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
5349111Stwisti			irelse(&ip);
5359111Stwisti			goto again;
5369111Stwisti
5379111Stwisti		case DSTATE:
5389111Stwisti		case DZLINK:
5399111Stwisti			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
5409111Stwisti				inoinfo(dirp->d_ino)->ino_state = DFOUND;
5419111Stwisti			/* FALLTHROUGH */
54210047Snever
54310047Snever		case DFOUND:
54410047Snever			inp = getinoinfo(dirp->d_ino);
5459111Stwisti			if (idesc->id_entryno > 2) {
5469111Stwisti				if (inp->i_parent == 0) {
5479111Stwisti					inp->i_parent = idesc->id_number;
5489111Stwisti					check_dirdepth(inp);
5499111Stwisti				} else if ((n = fix_extraneous(inp, idesc))) {
55011079Srbackman					break;
5519111Stwisti				}
5529111Stwisti			}
5539111Stwisti			/* FALLTHROUGH */
5549111Stwisti
5559111Stwisti		case FSTATE:
5569111Stwisti		case FZLINK:
5579111Stwisti			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
5589111Stwisti				fileerror(idesc->id_number, dirp->d_ino,
5599111Stwisti				    "BAD TYPE VALUE");
5609111Stwisti				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
5619111Stwisti				if (reply("FIX") == 1)
5629111Stwisti					ret |= ALTERED;
5639111Stwisti			}
5649111Stwisti			inoinfo(dirp->d_ino)->ino_linkcnt--;
5659111Stwisti			break;
5669111Stwisti
5679111Stwisti		default:
5689111Stwisti			errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
5699111Stwisti			    inoinfo(dirp->d_ino)->ino_state,
5709111Stwisti			    (uintmax_t)dirp->d_ino);
5719111Stwisti		}
5729111Stwisti	}
5739111Stwisti	if (n == 0)
5749111Stwisti		return (ret|KEEPON);
5759111Stwisti	dirp->d_ino = 0;
5769111Stwisti	return (ret|KEEPON|ALTERED);
5779111Stwisti}
5789111Stwisti
5799111Stwististatic int
5809111Stwistifix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
5819111Stwisti{
5829111Stwisti	char *cp;
5839111Stwisti	struct inode ip;
5849111Stwisti	struct inodesc dotdesc;
5859111Stwisti	char oldname[MAXPATHLEN + 1];
5869111Stwisti	char newname[MAXPATHLEN + 1];
5879111Stwisti
5889111Stwisti	/*
5899111Stwisti	 * If we have not yet found "..", look it up now so we know
5909111Stwisti	 * which inode the directory itself believes is its parent.
5919111Stwisti	 */
5929111Stwisti	if (inp->i_dotdot == 0) {
5939111Stwisti		memset(&dotdesc, 0, sizeof(struct inodesc));
5949111Stwisti		dotdesc.id_type = DATA;
5959111Stwisti		dotdesc.id_number = idesc->id_dirp->d_ino;
5969111Stwisti		dotdesc.id_func = findino;
5979111Stwisti		dotdesc.id_name = strdup("..");
59810240Snever		ginode(dotdesc.id_number, &ip);
59910240Snever		if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
60010240Snever			inp->i_dotdot = dotdesc.id_parent;
60110240Snever		irelse(&ip);
60210240Snever		free(dotdesc.id_name);
60310240Snever	}
60410240Snever	/*
6059111Stwisti	 * We have the previously found old name (inp->i_parent) and the
6069111Stwisti	 * just found new name (idesc->id_number). We have five cases:
6079111Stwisti	 * 1)  ".." is missing - can remove either name, choose to delete
6089111Stwisti	 *     new one and let fsck create ".." pointing to old name.
609	 * 2) Both new and old are in same directory, choose to delete
610	 *    the new name and let fsck fix ".." if it is wrong.
611	 * 3) ".." does not point to the new name, so delete it and let
612	 *    fsck fix ".." to point to the old one if it is wrong.
613	 * 4) ".." points to the old name only, so delete the new one.
614	 * 5) ".." points to the new name only, so delete the old one.
615	 *
616	 * For cases 1-4 we eliminate the new name;
617	 * for case 5 we eliminate the old name.
618	 */
619	if (inp->i_dotdot == 0 ||		    /* Case 1 */
620	    idesc->id_number == inp->i_parent ||    /* Case 2 */
621	    inp->i_dotdot != idesc->id_number ||    /* Case 3 */
622	    inp->i_dotdot == inp->i_parent) {	    /* Case 4 */
623		getpathname(newname, idesc->id_number, idesc->id_number);
624		if (strcmp(newname, "/") != 0)
625			strcat (newname, "/");
626		strcat(newname, idesc->id_dirp->d_name);
627		getpathname(oldname, inp->i_number, inp->i_number);
628		pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
629		    newname, oldname);
630		if (cursnapshot != 0) {
631			/*
632			 * We need to
633			 *    setcwd(idesc->id_number);
634			 *    unlink(idesc->id_dirp->d_name);
635			 */
636			cmd.value = idesc->id_number;
637			if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
638			    &cmd, sizeof cmd) == -1) {
639				printf(" (IGNORED)\n");
640				return (0);
641			}
642			cmd.value = (intptr_t)idesc->id_dirp->d_name;
643			cmd.size = inp->i_number; /* verify same name */
644			if (sysctlbyname("vfs.ffs.unlink", 0, 0,
645			    &cmd, sizeof cmd) == -1) {
646				printf(" (UNLINK FAILED: %s)\n",
647				    strerror(errno));
648				return (0);
649			}
650			printf(" (REMOVED)\n");
651			return (0);
652		}
653		if (preen) {
654			printf(" (REMOVED)\n");
655			return (1);
656		}
657		return (reply("REMOVE"));
658	}
659	/*
660	 * None of the first four cases above, so must be case (5).
661	 * Eliminate the old name and make the new the name the parent.
662	 */
663	getpathname(oldname, inp->i_parent, inp->i_number);
664	getpathname(newname, inp->i_number, inp->i_number);
665	pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
666	    newname);
667	if (cursnapshot != 0) {
668		/*
669		 * We need to
670		 *    setcwd(inp->i_parent);
671		 *    unlink(last component of oldname pathname);
672		 */
673		cmd.value = inp->i_parent;
674		if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
675		    &cmd, sizeof cmd) == -1) {
676			printf(" (IGNORED)\n");
677			return (0);
678		}
679		if ((cp = strchr(oldname, '/')) == NULL) {
680			printf(" (IGNORED)\n");
681			return (0);
682		}
683		cmd.value = (intptr_t)(cp + 1);
684		cmd.size = inp->i_number; /* verify same name */
685		if (sysctlbyname("vfs.ffs.unlink", 0, 0,
686		    &cmd, sizeof cmd) == -1) {
687			printf(" (UNLINK FAILED: %s)\n",
688			    strerror(errno));
689			return (0);
690		}
691		printf(" (REMOVED)\n");
692		inp->i_parent = idesc->id_number;  /* reparent to correct dir */
693		return (0);
694	}
695	if (!preen && !reply("REMOVE"))
696		return (0);
697	memset(&dotdesc, 0, sizeof(struct inodesc));
698	dotdesc.id_type = DATA;
699	dotdesc.id_number = inp->i_parent; /* directory in which name appears */
700	dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
701	dotdesc.id_func = deleteentry;
702	ginode(dotdesc.id_number, &ip);
703	if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen)
704		printf(" (REMOVED)\n");
705	irelse(&ip);
706	inp->i_parent = idesc->id_number;  /* reparent to correct directory */
707	inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
708	return (0);
709}
710
711static int
712deleteentry(struct inodesc *idesc)
713{
714	struct direct *dirp = idesc->id_dirp;
715
716	if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
717		return (KEEPON);
718	dirp->d_ino = 0;
719	return (ALTERED|STOP|FOUND);
720}
721
722/*
723 * Routine to sort disk blocks.
724 */
725static int
726blksort(const void *arg1, const void *arg2)
727{
728
729	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
730		(*(struct inoinfo * const *)arg2)->i_blks[0]);
731}
732