xref: /freebsd/sbin/fsck_ffs/pass2.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
48fae3551SRodney W. Grimes  * Copyright (c) 1980, 1986, 1993
58fae3551SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
68fae3551SRodney W. Grimes  *
78fae3551SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
88fae3551SRodney W. Grimes  * modification, are permitted provided that the following conditions
98fae3551SRodney W. Grimes  * are met:
108fae3551SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
118fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
128fae3551SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
138fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
148fae3551SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
168fae3551SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
178fae3551SRodney W. Grimes  *    without specific prior written permission.
188fae3551SRodney W. Grimes  *
198fae3551SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
208fae3551SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
218fae3551SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
228fae3551SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
238fae3551SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
248fae3551SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
258fae3551SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
268fae3551SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
278fae3551SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
288fae3551SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
298fae3551SRodney W. Grimes  * SUCH DAMAGE.
308fae3551SRodney W. Grimes  */
318fae3551SRodney W. Grimes 
328fae3551SRodney W. Grimes #include <sys/param.h>
3377355ce0SKirk McKusick #include <sys/sysctl.h>
34780a5c1eSPeter Wemm 
358fae3551SRodney W. Grimes #include <ufs/ufs/dinode.h>
368fae3551SRodney W. Grimes #include <ufs/ufs/dir.h>
377578c6abSKirk McKusick #include <ufs/ffs/fs.h>
3851a5cf90SBruce Evans 
39780a5c1eSPeter Wemm #include <err.h>
4077355ce0SKirk McKusick #include <errno.h>
4184fc0d7eSMaxime Henrion #include <stdint.h>
428fae3551SRodney W. Grimes #include <string.h>
43780a5c1eSPeter Wemm 
448fae3551SRodney W. Grimes #include "fsck.h"
458fae3551SRodney W. Grimes 
468fae3551SRodney W. Grimes #define MINDIRSIZE	(sizeof (struct dirtemplate))
478fae3551SRodney W. Grimes 
4837776076SKirk McKusick static int fix_extraneous(struct inoinfo *, struct inodesc *);
4937776076SKirk McKusick static int deleteentry(struct inodesc *);
50b70cd7eeSWarner Losh static int blksort(const void *, const void *);
51b70cd7eeSWarner Losh static int pass2check(struct inodesc *);
528fae3551SRodney W. Grimes 
5331f4ab50SBruce Evans void
pass2(void)54b70cd7eeSWarner Losh pass2(void)
558fae3551SRodney W. Grimes {
565cc52631SKirk McKusick 	struct inode ip;
571c85e6a3SKirk McKusick 	union dinode *dp;
583d438ad6SDavid E. O'Brien 	struct inoinfo **inpp, *inp;
598fae3551SRodney W. Grimes 	struct inoinfo **inpend;
608fae3551SRodney W. Grimes 	struct inodesc curino;
611c85e6a3SKirk McKusick 	union dinode dino;
621c85e6a3SKirk McKusick 	int i;
638fae3551SRodney W. Grimes 	char pathbuf[MAXPATHLEN + 1];
648fae3551SRodney W. Grimes 
651dc349abSEd Maste 	switch (inoinfo(UFS_ROOTINO)->ino_state) {
668fae3551SRodney W. Grimes 
678fae3551SRodney W. Grimes 	case USTATE:
688fae3551SRodney W. Grimes 		pfatal("ROOT INODE UNALLOCATED");
69b1897c19SJulian Elischer 		if (reply("ALLOCATE") == 0) {
70b1897c19SJulian Elischer 			ckfini(0);
71780a5c1eSPeter Wemm 			exit(EEXIT);
72b1897c19SJulian Elischer 		}
731dc349abSEd Maste 		if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO)
74780a5c1eSPeter Wemm 			errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
758fae3551SRodney W. Grimes 		break;
768fae3551SRodney W. Grimes 
778fae3551SRodney W. Grimes 	case DCLEAR:
788fae3551SRodney W. Grimes 		pfatal("DUPS/BAD IN ROOT INODE");
798fae3551SRodney W. Grimes 		if (reply("REALLOCATE")) {
8052f97104SKirk McKusick 			freedirino(UFS_ROOTINO, UFS_ROOTINO);
811dc349abSEd Maste 			if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
821dc349abSEd Maste 			    UFS_ROOTINO)
83780a5c1eSPeter Wemm 				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
848fae3551SRodney W. Grimes 			break;
858fae3551SRodney W. Grimes 		}
86b1897c19SJulian Elischer 		if (reply("CONTINUE") == 0) {
87b1897c19SJulian Elischer 			ckfini(0);
88780a5c1eSPeter Wemm 			exit(EEXIT);
89b1897c19SJulian Elischer 		}
908fae3551SRodney W. Grimes 		break;
918fae3551SRodney W. Grimes 
928fae3551SRodney W. Grimes 	case FSTATE:
938fae3551SRodney W. Grimes 	case FCLEAR:
94af6726e6SDon Lewis 	case FZLINK:
958fae3551SRodney W. Grimes 		pfatal("ROOT INODE NOT DIRECTORY");
968fae3551SRodney W. Grimes 		if (reply("REALLOCATE")) {
971dc349abSEd Maste 			freeino(UFS_ROOTINO);
981dc349abSEd Maste 			if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) !=
991dc349abSEd Maste 			    UFS_ROOTINO)
100780a5c1eSPeter Wemm 				errx(EEXIT, "CANNOT ALLOCATE ROOT INODE");
1018fae3551SRodney W. Grimes 			break;
1028fae3551SRodney W. Grimes 		}
103b1897c19SJulian Elischer 		if (reply("FIX") == 0) {
104b1897c19SJulian Elischer 			ckfini(0);
105780a5c1eSPeter Wemm 			exit(EEXIT);
106b1897c19SJulian Elischer 		}
1075cc52631SKirk McKusick 		ginode(UFS_ROOTINO, &ip);
1085cc52631SKirk McKusick 		dp = ip.i_dp;
109d8ba45e2SEd Maste 		DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT);
110d8ba45e2SEd Maste 		DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR);
1115cc52631SKirk McKusick 		inodirty(&ip);
1125cc52631SKirk McKusick 		irelse(&ip);
1138fae3551SRodney W. Grimes 		break;
1148fae3551SRodney W. Grimes 
1158fae3551SRodney W. Grimes 	case DSTATE:
116af6726e6SDon Lewis 	case DZLINK:
1178fae3551SRodney W. Grimes 		break;
1188fae3551SRodney W. Grimes 
1198fae3551SRodney W. Grimes 	default:
120d33e92f9SJulian Elischer 		errx(EEXIT, "BAD STATE %d FOR ROOT INODE",
1211dc349abSEd Maste 		    inoinfo(UFS_ROOTINO)->ino_state);
1228fae3551SRodney W. Grimes 	}
1231dc349abSEd Maste 	inoinfo(UFS_ROOTINO)->ino_state = DFOUND;
1241dc349abSEd Maste 	inoinfo(UFS_WINO)->ino_state = FSTATE;
1251dc349abSEd Maste 	inoinfo(UFS_WINO)->ino_type = DT_WHT;
1268fae3551SRodney W. Grimes 	/*
1278fae3551SRodney W. Grimes 	 * Sort the directory list into disk block order.
1288fae3551SRodney W. Grimes 	 */
1298fae3551SRodney W. Grimes 	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
1308fae3551SRodney W. Grimes 	/*
1318fae3551SRodney W. Grimes 	 * Check the integrity of each directory.
1328fae3551SRodney W. Grimes 	 */
133780a5c1eSPeter Wemm 	memset(&curino, 0, sizeof(struct inodesc));
1348fae3551SRodney W. Grimes 	curino.id_type = DATA;
1358fae3551SRodney W. Grimes 	curino.id_func = pass2check;
1368fae3551SRodney W. Grimes 	inpend = &inpsort[inplast];
1378fae3551SRodney W. Grimes 	for (inpp = inpsort; inpp < inpend; inpp++) {
1386db798caSIan Dowse 		if (got_siginfo) {
13984fc0d7eSMaxime Henrion 			printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname,
140bf58d635SIan Dowse 			    inpp - inpsort, (int)inplast,
141bf58d635SIan Dowse 			    (int)((inpp - inpsort) * 100 / inplast));
1426db798caSIan Dowse 			got_siginfo = 0;
1436db798caSIan Dowse 		}
1441660ae87SScott Long 		if (got_sigalarm) {
1451660ae87SScott Long 			setproctitle("%s p2 %d%%", cdevname,
1461660ae87SScott Long 			    (int)((inpp - inpsort) * 100 / inplast));
1471660ae87SScott Long 			got_sigalarm = 0;
1481660ae87SScott Long 		}
1498fae3551SRodney W. Grimes 		inp = *inpp;
1508fae3551SRodney W. Grimes 		if (inp->i_isize == 0)
1518fae3551SRodney W. Grimes 			continue;
1528fae3551SRodney W. Grimes 		if (inp->i_isize < MINDIRSIZE) {
1538fae3551SRodney W. Grimes 			direrror(inp->i_number, "DIRECTORY TOO SHORT");
1548fae3551SRodney W. Grimes 			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
1558fae3551SRodney W. Grimes 			if (reply("FIX") == 1) {
1565cc52631SKirk McKusick 				ginode(inp->i_number, &ip);
1575cc52631SKirk McKusick 				DIP_SET(ip.i_dp, di_size, inp->i_isize);
1585cc52631SKirk McKusick 				inodirty(&ip);
1595cc52631SKirk McKusick 				irelse(&ip);
1608fae3551SRodney W. Grimes 			}
1618fae3551SRodney W. Grimes 		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
1628fae3551SRodney W. Grimes 			getpathname(pathbuf, inp->i_number, inp->i_number);
163b1897c19SJulian Elischer 			if (usedsoftdep)
16484fc0d7eSMaxime Henrion 				pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
16584fc0d7eSMaxime Henrion 					"DIRECTORY", pathbuf,
16684fc0d7eSMaxime Henrion 					(intmax_t)inp->i_isize, DIRBLKSIZ);
167b1897c19SJulian Elischer 			else
16884fc0d7eSMaxime Henrion 				pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d",
16984fc0d7eSMaxime Henrion 					"DIRECTORY", pathbuf,
17084fc0d7eSMaxime Henrion 					(intmax_t)inp->i_isize, DIRBLKSIZ);
1718fae3551SRodney W. Grimes 			if (preen)
1728fae3551SRodney W. Grimes 				printf(" (ADJUSTED)\n");
1738fae3551SRodney W. Grimes 			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
1748fae3551SRodney W. Grimes 			if (preen || reply("ADJUST") == 1) {
1755cc52631SKirk McKusick 				ginode(inp->i_number, &ip);
1765cc52631SKirk McKusick 				DIP_SET(ip.i_dp, di_size,
177c3b2344bSScott Long 				    roundup(inp->i_isize, DIRBLKSIZ));
1785cc52631SKirk McKusick 				inodirty(&ip);
1795cc52631SKirk McKusick 				irelse(&ip);
1801c85e6a3SKirk McKusick 			}
1811c85e6a3SKirk McKusick 		}
1828fae3551SRodney W. Grimes 		dp = &dino;
1831c85e6a3SKirk McKusick 		memset(dp, 0, sizeof(struct ufs2_dinode));
184d8ba45e2SEd Maste 		DIP_SET(dp, di_mode, IFDIR);
185c3b2344bSScott Long 		DIP_SET(dp, di_size, inp->i_isize);
1861dc349abSEd Maste 		for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++)
187c3b2344bSScott Long 			DIP_SET(dp, di_db[i], inp->i_blks[i]);
1881dc349abSEd Maste 		if (inp->i_numblks > UFS_NDADDR)
1891dc349abSEd Maste 			for (i = 0; i < UFS_NIADDR; i++)
1901dc349abSEd Maste 				DIP_SET(dp, di_ib[i],
1911dc349abSEd Maste 				    inp->i_blks[UFS_NDADDR + i]);
1928fae3551SRodney W. Grimes 		curino.id_number = inp->i_number;
1938fae3551SRodney W. Grimes 		curino.id_parent = inp->i_parent;
1948fae3551SRodney W. Grimes 		(void)ckinode(dp, &curino);
1958fae3551SRodney W. Grimes 	}
1968fae3551SRodney W. Grimes 	/*
1978fae3551SRodney W. Grimes 	 * Now that the parents of all directories have been found,
1988fae3551SRodney W. Grimes 	 * make another pass to verify the value of `..'
1998fae3551SRodney W. Grimes 	 */
2008fae3551SRodney W. Grimes 	for (inpp = inpsort; inpp < inpend; inpp++) {
2018fae3551SRodney W. Grimes 		inp = *inpp;
2028fae3551SRodney W. Grimes 		if (inp->i_parent == 0 || inp->i_isize == 0)
2038fae3551SRodney W. Grimes 			continue;
204d33e92f9SJulian Elischer 		if (inoinfo(inp->i_parent)->ino_state == DFOUND &&
205fe5e6e2cSKirk McKusick 		    INO_IS_DUNFOUND(inp->i_number)) {
206d33e92f9SJulian Elischer 			inoinfo(inp->i_number)->ino_state = DFOUND;
207fe5e6e2cSKirk McKusick 			check_dirdepth(inp);
208fe5e6e2cSKirk McKusick 		}
2098fae3551SRodney W. Grimes 		if (inp->i_dotdot == inp->i_parent ||
2108fae3551SRodney W. Grimes 		    inp->i_dotdot == (ino_t)-1)
2118fae3551SRodney W. Grimes 			continue;
2128fae3551SRodney W. Grimes 		if (inp->i_dotdot == 0) {
2138fae3551SRodney W. Grimes 			inp->i_dotdot = inp->i_parent;
2146bae6625SKirk McKusick 			if (debug)
2156bae6625SKirk McKusick 				fileerror(inp->i_parent, inp->i_number,
2166bae6625SKirk McKusick 				    "DEFERRED MISSING '..' FIX");
2178fae3551SRodney W. Grimes 			(void)makeentry(inp->i_number, inp->i_parent, "..");
218d33e92f9SJulian Elischer 			inoinfo(inp->i_parent)->ino_linkcnt--;
2198fae3551SRodney W. Grimes 			continue;
2208fae3551SRodney W. Grimes 		}
22177355ce0SKirk McKusick 		/*
22277355ce0SKirk McKusick 		 * Here we have:
22377355ce0SKirk McKusick 		 *    inp->i_number is directory with bad ".." in it.
22477355ce0SKirk McKusick 		 *    inp->i_dotdot is current value of "..".
22577355ce0SKirk McKusick 		 *    inp->i_parent is directory to which ".." should point.
22677355ce0SKirk McKusick 		 */
22777355ce0SKirk McKusick 		getpathname(pathbuf, inp->i_parent, inp->i_number);
228623d7cb6SMatthew D Fleming 		printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
229623d7cb6SMatthew D Fleming 		    (uintmax_t)inp->i_number, pathbuf);
23077355ce0SKirk McKusick 		getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
231623d7cb6SMatthew D Fleming 		printf("CURRENTLY POINTS TO I=%ju (%s), ",
232623d7cb6SMatthew D Fleming 		    (uintmax_t)inp->i_dotdot, pathbuf);
23377355ce0SKirk McKusick 		getpathname(pathbuf, inp->i_parent, inp->i_parent);
234623d7cb6SMatthew D Fleming 		printf("SHOULD POINT TO I=%ju (%s)",
235623d7cb6SMatthew D Fleming 		    (uintmax_t)inp->i_parent, pathbuf);
23677355ce0SKirk McKusick 		if (cursnapshot != 0) {
23777355ce0SKirk McKusick 			/*
23877355ce0SKirk McKusick 			 * We need to:
23977355ce0SKirk McKusick 			 *    setcwd(inp->i_number);
24077355ce0SKirk McKusick 			 *    setdotdot(inp->i_dotdot, inp->i_parent);
24177355ce0SKirk McKusick 			 */
24277355ce0SKirk McKusick 			cmd.value = inp->i_number;
24377355ce0SKirk McKusick 			if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
24477355ce0SKirk McKusick 			    &cmd, sizeof cmd) == -1) {
24577355ce0SKirk McKusick 				/* kernel lacks support for these functions */
24677355ce0SKirk McKusick 				printf(" (IGNORED)\n");
24777355ce0SKirk McKusick 				continue;
24877355ce0SKirk McKusick 			}
24977355ce0SKirk McKusick 			cmd.value = inp->i_dotdot; /* verify same value */
25077355ce0SKirk McKusick 			cmd.size = inp->i_parent;  /* new parent */
25177355ce0SKirk McKusick 			if (sysctlbyname("vfs.ffs.setdotdot", 0, 0,
25277355ce0SKirk McKusick 			    &cmd, sizeof cmd) == -1) {
25377355ce0SKirk McKusick 				printf(" (FIX FAILED: %s)\n", strerror(errno));
25477355ce0SKirk McKusick 				continue;
25577355ce0SKirk McKusick 			}
25677355ce0SKirk McKusick 			printf(" (FIXED)\n");
25777355ce0SKirk McKusick 			inoinfo(inp->i_parent)->ino_linkcnt--;
25877355ce0SKirk McKusick 			inp->i_dotdot = inp->i_parent;
25977355ce0SKirk McKusick 			continue;
26077355ce0SKirk McKusick 		}
26177355ce0SKirk McKusick 		if (preen)
26277355ce0SKirk McKusick 			printf(" (FIXED)\n");
26377355ce0SKirk McKusick 		else if (reply("FIX") == 0)
2648fae3551SRodney W. Grimes 			continue;
265d33e92f9SJulian Elischer 		inoinfo(inp->i_dotdot)->ino_linkcnt++;
266d33e92f9SJulian Elischer 		inoinfo(inp->i_parent)->ino_linkcnt--;
2678fae3551SRodney W. Grimes 		inp->i_dotdot = inp->i_parent;
268fe5e6e2cSKirk McKusick 		(void)changeino(inp->i_number, "..", inp->i_parent,
269fe5e6e2cSKirk McKusick 		    getinoinfo(inp->i_parent)->i_depth  + 1);
2708fae3551SRodney W. Grimes 	}
2718fae3551SRodney W. Grimes 	/*
2728fae3551SRodney W. Grimes 	 * Mark all the directories that can be found from the root.
2738fae3551SRodney W. Grimes 	 */
2748fae3551SRodney W. Grimes 	propagate();
2758fae3551SRodney W. Grimes }
2768fae3551SRodney W. Grimes 
277780a5c1eSPeter Wemm static int
pass2check(struct inodesc * idesc)278b70cd7eeSWarner Losh pass2check(struct inodesc *idesc)
2798fae3551SRodney W. Grimes {
2803d438ad6SDavid E. O'Brien 	struct direct *dirp = idesc->id_dirp;
28197fea87bSKirk McKusick 	char dirname[MAXPATHLEN + 1];
2823d438ad6SDavid E. O'Brien 	struct inoinfo *inp;
2838fae3551SRodney W. Grimes 	int n, entrysize, ret = 0;
2845cc52631SKirk McKusick 	struct inode ip;
2851c85e6a3SKirk McKusick 	union dinode *dp;
286599304a4SPoul-Henning Kamp 	const char *errmsg;
2876bae6625SKirk McKusick 	struct direct proto, *newdirp;
2888fae3551SRodney W. Grimes 
2898fae3551SRodney W. Grimes 	/*
2908fae3551SRodney W. Grimes 	 * check for "."
2918fae3551SRodney W. Grimes 	 */
2928fae3551SRodney W. Grimes 	if (idesc->id_entryno != 0)
2938fae3551SRodney W. Grimes 		goto chk1;
2948fae3551SRodney W. Grimes 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
2958fae3551SRodney W. Grimes 		if (dirp->d_ino != idesc->id_number) {
2968fae3551SRodney W. Grimes 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
2976bae6625SKirk McKusick 			if (reply("FIX") == 1) {
2988fae3551SRodney W. Grimes 				dirp->d_ino = idesc->id_number;
2998fae3551SRodney W. Grimes 				ret |= ALTERED;
3008fae3551SRodney W. Grimes 			}
3016bae6625SKirk McKusick 		}
302381ee4c2SPoul-Henning Kamp 		if (dirp->d_type != DT_DIR) {
3038fae3551SRodney W. Grimes 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
3046bae6625SKirk McKusick 			if (reply("FIX") == 1) {
3058fae3551SRodney W. Grimes 				dirp->d_type = DT_DIR;
3068fae3551SRodney W. Grimes 				ret |= ALTERED;
3078fae3551SRodney W. Grimes 			}
3086bae6625SKirk McKusick 		}
3098fae3551SRodney W. Grimes 		goto chk1;
3108fae3551SRodney W. Grimes 	}
3118fae3551SRodney W. Grimes 	proto.d_ino = idesc->id_number;
3128fae3551SRodney W. Grimes 	proto.d_type = DT_DIR;
3138fae3551SRodney W. Grimes 	proto.d_namlen = 1;
3148fae3551SRodney W. Grimes 	(void)strcpy(proto.d_name, ".");
3158fae3551SRodney W. Grimes 	entrysize = DIRSIZ(0, &proto);
3166bae6625SKirk McKusick 	direrror(idesc->id_number, "MISSING '.'");
3176bae6625SKirk McKusick 	errmsg = "ADD '.' ENTRY";
3186bae6625SKirk McKusick 	if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
3196bae6625SKirk McKusick 		/* Not enough space to add '.', replace first entry with '.' */
3206bae6625SKirk McKusick 		if (dirp->d_ino != 0) {
3216bae6625SKirk McKusick 			pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n",
3228fae3551SRodney W. Grimes 			     dirp->d_name);
3236bae6625SKirk McKusick 			errmsg = "REPLACE WITH '.'";
3246bae6625SKirk McKusick 		}
3256bae6625SKirk McKusick 		if (reply(errmsg) == 0)
3266bae6625SKirk McKusick 			goto chk1;
3278fae3551SRodney W. Grimes 		proto.d_reclen = dirp->d_reclen;
328780a5c1eSPeter Wemm 		memmove(dirp, &proto, (size_t)entrysize);
3298fae3551SRodney W. Grimes 		ret |= ALTERED;
3308fae3551SRodney W. Grimes 	} else {
3316bae6625SKirk McKusick 		/* Move over first entry and add '.' entry */
3326bae6625SKirk McKusick 		if (reply(errmsg) == 0)
3336bae6625SKirk McKusick 			goto chk1;
3346bae6625SKirk McKusick 		newdirp = (struct direct *)((char *)(dirp) + entrysize);
3356bae6625SKirk McKusick 		dirp->d_reclen -= entrysize;
3366bae6625SKirk McKusick 		memmove(newdirp, dirp, dirp->d_reclen);
3378fae3551SRodney W. Grimes 		proto.d_reclen = entrysize;
338780a5c1eSPeter Wemm 		memmove(dirp, &proto, (size_t)entrysize);
3398fae3551SRodney W. Grimes 		idesc->id_entryno++;
3406bae6625SKirk McKusick 		inoinfo(idesc->id_number)->ino_linkcnt--;
3416bae6625SKirk McKusick 		dirp = newdirp;
3428fae3551SRodney W. Grimes 		ret |= ALTERED;
3438fae3551SRodney W. Grimes 	}
3448fae3551SRodney W. Grimes chk1:
3458fae3551SRodney W. Grimes 	if (idesc->id_entryno > 1)
3468fae3551SRodney W. Grimes 		goto chk2;
3478fae3551SRodney W. Grimes 	inp = getinoinfo(idesc->id_number);
3488fae3551SRodney W. Grimes 	proto.d_ino = inp->i_parent;
3498fae3551SRodney W. Grimes 	proto.d_type = DT_DIR;
3508fae3551SRodney W. Grimes 	proto.d_namlen = 2;
3518fae3551SRodney W. Grimes 	(void)strcpy(proto.d_name, "..");
3528fae3551SRodney W. Grimes 	entrysize = DIRSIZ(0, &proto);
3538fae3551SRodney W. Grimes 	if (idesc->id_entryno == 0) {
3548fae3551SRodney W. Grimes 		n = DIRSIZ(0, dirp);
3558fae3551SRodney W. Grimes 		if (dirp->d_reclen < n + entrysize)
3568fae3551SRodney W. Grimes 			goto chk2;
3578fae3551SRodney W. Grimes 		proto.d_reclen = dirp->d_reclen - n;
3588fae3551SRodney W. Grimes 		dirp->d_reclen = n;
3598fae3551SRodney W. Grimes 		idesc->id_entryno++;
360d33e92f9SJulian Elischer 		inoinfo(dirp->d_ino)->ino_linkcnt--;
3618fae3551SRodney W. Grimes 		dirp = (struct direct *)((char *)(dirp) + n);
362780a5c1eSPeter Wemm 		memset(dirp, 0, (size_t)proto.d_reclen);
3638fae3551SRodney W. Grimes 		dirp->d_reclen = proto.d_reclen;
3648fae3551SRodney W. Grimes 	}
3658fae3551SRodney W. Grimes 	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
366*11ce203eSKirk McKusick 		if (dirp->d_ino >= maxino) {
36752f97104SKirk McKusick 			direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'");
36852f97104SKirk McKusick 			/*
36952f97104SKirk McKusick 			 * If we know parent set it now, otherwise let it
37052f97104SKirk McKusick 			 * point to the root inode and it will get cleaned
37152f97104SKirk McKusick 			 * up later if that is not correct.
37252f97104SKirk McKusick 			 */
37352f97104SKirk McKusick 			if (inp->i_parent != 0)
37452f97104SKirk McKusick 				dirp->d_ino = inp->i_parent;
37552f97104SKirk McKusick 			else
37652f97104SKirk McKusick 				dirp->d_ino = UFS_ROOTINO;
37752f97104SKirk McKusick 			if (reply("FIX") == 1)
37852f97104SKirk McKusick 				ret |= ALTERED;
37952f97104SKirk McKusick 		}
3808fae3551SRodney W. Grimes 		inp->i_dotdot = dirp->d_ino;
381381ee4c2SPoul-Henning Kamp 		if (dirp->d_type != DT_DIR) {
3828fae3551SRodney W. Grimes 			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
3838fae3551SRodney W. Grimes 			dirp->d_type = DT_DIR;
3848fae3551SRodney W. Grimes 			if (reply("FIX") == 1)
3858fae3551SRodney W. Grimes 				ret |= ALTERED;
3868fae3551SRodney W. Grimes 		}
3878fae3551SRodney W. Grimes 		goto chk2;
3888fae3551SRodney W. Grimes 	}
3896bae6625SKirk McKusick 	fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number,
3906bae6625SKirk McKusick 	    idesc->id_number, "MISSING '..'");
3916bae6625SKirk McKusick 	errmsg = "ADD '..' ENTRY";
3926bae6625SKirk McKusick 	if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) {
3936bae6625SKirk McKusick 		/* No space to add '..', replace second entry with '..' */
3946bae6625SKirk McKusick 		if (dirp->d_ino != 0) {
3956bae6625SKirk McKusick 			pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
3968fae3551SRodney W. Grimes 			    dirp->d_name);
3976bae6625SKirk McKusick 			errmsg = "REPLACE WITH '..'";
3986bae6625SKirk McKusick 		}
3996bae6625SKirk McKusick 		if (reply(errmsg) == 0) {
4008fae3551SRodney W. Grimes 			inp->i_dotdot = (ino_t)-1;
4016bae6625SKirk McKusick 			goto chk2;
4026bae6625SKirk McKusick 		}
4036bae6625SKirk McKusick 		if (proto.d_ino == 0) {
4046bae6625SKirk McKusick 			/* Defer processing until parent known */
4056bae6625SKirk McKusick 			idesc->id_entryno++;
4066bae6625SKirk McKusick 			if (debug)
4076bae6625SKirk McKusick 				printf("(FIX DEFERRED)\n");
4086bae6625SKirk McKusick 		}
4096bae6625SKirk McKusick 		inp->i_dotdot = proto.d_ino;
4108fae3551SRodney W. Grimes 		proto.d_reclen = dirp->d_reclen;
411780a5c1eSPeter Wemm 		memmove(dirp, &proto, (size_t)entrysize);
4126bae6625SKirk McKusick 		ret |= ALTERED;
4136bae6625SKirk McKusick 	} else {
4146bae6625SKirk McKusick 		/* Move over second entry and add '..' entry */
4156bae6625SKirk McKusick 		if (reply(errmsg) == 0) {
4166bae6625SKirk McKusick 			inp->i_dotdot = (ino_t)-1;
4176bae6625SKirk McKusick 			goto chk2;
4186bae6625SKirk McKusick 		}
4196bae6625SKirk McKusick 		if (proto.d_ino == 0) {
4206bae6625SKirk McKusick 			/* Defer processing until parent known */
4216bae6625SKirk McKusick 			idesc->id_entryno++;
4226bae6625SKirk McKusick 			if (debug)
4236bae6625SKirk McKusick 				printf("(FIX DEFERRED)\n");
4246bae6625SKirk McKusick 		}
4256bae6625SKirk McKusick 		inp->i_dotdot = proto.d_ino;
4266bae6625SKirk McKusick 		if (dirp->d_ino == 0) {
4276bae6625SKirk McKusick 			proto.d_reclen = dirp->d_reclen;
4286bae6625SKirk McKusick 			memmove(dirp, &proto, (size_t)entrysize);
4296bae6625SKirk McKusick 		} else {
4306bae6625SKirk McKusick 			newdirp = (struct direct *)((char *)(dirp) + entrysize);
4316bae6625SKirk McKusick 			dirp->d_reclen -= entrysize;
4326bae6625SKirk McKusick 			memmove(newdirp, dirp, dirp->d_reclen);
4336bae6625SKirk McKusick 			proto.d_reclen = entrysize;
4346bae6625SKirk McKusick 			memmove(dirp, &proto, (size_t)entrysize);
4356bae6625SKirk McKusick 			if (dirp->d_ino != 0) {
4366bae6625SKirk McKusick 				idesc->id_entryno++;
4376bae6625SKirk McKusick 				inoinfo(dirp->d_ino)->ino_linkcnt--;
4386bae6625SKirk McKusick 			}
4396bae6625SKirk McKusick 			dirp = newdirp;
4406bae6625SKirk McKusick 		}
4418fae3551SRodney W. Grimes 		ret |= ALTERED;
4428fae3551SRodney W. Grimes 	}
4438fae3551SRodney W. Grimes chk2:
4448fae3551SRodney W. Grimes 	if (dirp->d_ino == 0)
4458fae3551SRodney W. Grimes 		return (ret|KEEPON);
4468fae3551SRodney W. Grimes 	if (dirp->d_namlen <= 2 &&
4478fae3551SRodney W. Grimes 	    dirp->d_name[0] == '.' &&
4488fae3551SRodney W. Grimes 	    idesc->id_entryno >= 2) {
4498fae3551SRodney W. Grimes 		if (dirp->d_namlen == 1) {
4508fae3551SRodney W. Grimes 			direrror(idesc->id_number, "EXTRA '.' ENTRY");
4518fae3551SRodney W. Grimes 			dirp->d_ino = 0;
4528fae3551SRodney W. Grimes 			if (reply("FIX") == 1)
4538fae3551SRodney W. Grimes 				ret |= ALTERED;
4548fae3551SRodney W. Grimes 			return (KEEPON | ret);
4558fae3551SRodney W. Grimes 		}
4568fae3551SRodney W. Grimes 		if (dirp->d_name[1] == '.') {
4578fae3551SRodney W. Grimes 			direrror(idesc->id_number, "EXTRA '..' ENTRY");
4588fae3551SRodney W. Grimes 			dirp->d_ino = 0;
4598fae3551SRodney W. Grimes 			if (reply("FIX") == 1)
4608fae3551SRodney W. Grimes 				ret |= ALTERED;
4618fae3551SRodney W. Grimes 			return (KEEPON | ret);
4628fae3551SRodney W. Grimes 		}
4638fae3551SRodney W. Grimes 	}
4648fae3551SRodney W. Grimes 	idesc->id_entryno++;
4658fae3551SRodney W. Grimes 	n = 0;
466*11ce203eSKirk McKusick 	if (dirp->d_ino >= maxino) {
4678fae3551SRodney W. Grimes 		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
4688fae3551SRodney W. Grimes 		n = reply("REMOVE");
4691dc349abSEd Maste 	} else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) ||
4701dc349abSEd Maste 		    (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) {
471780a5c1eSPeter Wemm 		fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY");
4721dc349abSEd Maste 		dirp->d_ino = UFS_WINO;
473780a5c1eSPeter Wemm 		dirp->d_type = DT_WHT;
474780a5c1eSPeter Wemm 		if (reply("FIX") == 1)
475780a5c1eSPeter Wemm 			ret |= ALTERED;
4768fae3551SRodney W. Grimes 	} else {
4778fae3551SRodney W. Grimes again:
478d33e92f9SJulian Elischer 		switch (inoinfo(dirp->d_ino)->ino_state) {
4798fae3551SRodney W. Grimes 		case USTATE:
4808fae3551SRodney W. Grimes 			if (idesc->id_entryno <= 2)
4818fae3551SRodney W. Grimes 				break;
4828fae3551SRodney W. Grimes 			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
4838fae3551SRodney W. Grimes 			n = reply("REMOVE");
4848fae3551SRodney W. Grimes 			break;
4858fae3551SRodney W. Grimes 
4868fae3551SRodney W. Grimes 		case DCLEAR:
4878fae3551SRodney W. Grimes 		case FCLEAR:
4888fae3551SRodney W. Grimes 			if (idesc->id_entryno <= 2)
4898fae3551SRodney W. Grimes 				break;
490d33e92f9SJulian Elischer 			if (inoinfo(dirp->d_ino)->ino_state == FCLEAR)
4918fae3551SRodney W. Grimes 				errmsg = "DUP/BAD";
492b1897c19SJulian Elischer 			else if (!preen && !usedsoftdep)
4938fae3551SRodney W. Grimes 				errmsg = "ZERO LENGTH DIRECTORY";
49497fea87bSKirk McKusick 			else if (cursnapshot == 0) {
4958fae3551SRodney W. Grimes 				n = 1;
4968fae3551SRodney W. Grimes 				break;
49797fea87bSKirk McKusick 			} else {
49897fea87bSKirk McKusick 				getpathname(dirname, idesc->id_number,
49997fea87bSKirk McKusick 				    dirp->d_ino);
500623d7cb6SMatthew D Fleming 				pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
501623d7cb6SMatthew D Fleming 				    dirname, (uintmax_t)dirp->d_ino);
50297fea87bSKirk McKusick 				/*
50397fea87bSKirk McKusick 				 * We need to:
50497fea87bSKirk McKusick 				 *    setcwd(idesc->id_parent);
50597fea87bSKirk McKusick 				 *    rmdir(dirp->d_name);
50697fea87bSKirk McKusick 				 */
50797fea87bSKirk McKusick 				cmd.value = idesc->id_number;
50897fea87bSKirk McKusick 				if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
50997fea87bSKirk McKusick 				    &cmd, sizeof cmd) == -1) {
51097fea87bSKirk McKusick 					/* kernel lacks support */
51197fea87bSKirk McKusick 					printf(" (IGNORED)\n");
51297fea87bSKirk McKusick 					n = 1;
51397fea87bSKirk McKusick 					break;
51497fea87bSKirk McKusick 				}
51597fea87bSKirk McKusick 				if (rmdir(dirp->d_name) == -1) {
51697fea87bSKirk McKusick 					printf(" (REMOVAL FAILED: %s)\n",
51797fea87bSKirk McKusick 					    strerror(errno));
51897fea87bSKirk McKusick 					n = 1;
51997fea87bSKirk McKusick 					break;
52097fea87bSKirk McKusick 				}
52197fea87bSKirk McKusick 				/* ".." reference to parent is removed */
52297fea87bSKirk McKusick 				inoinfo(idesc->id_number)->ino_linkcnt--;
52397fea87bSKirk McKusick 				printf(" (REMOVED)\n");
52497fea87bSKirk McKusick 				break;
5258fae3551SRodney W. Grimes 			}
5268fae3551SRodney W. Grimes 			fileerror(idesc->id_number, dirp->d_ino, errmsg);
5278fae3551SRodney W. Grimes 			if ((n = reply("REMOVE")) == 1)
5288fae3551SRodney W. Grimes 				break;
5295cc52631SKirk McKusick 			ginode(dirp->d_ino, &ip);
5305cc52631SKirk McKusick 			dp = ip.i_dp;
531d33e92f9SJulian Elischer 			inoinfo(dirp->d_ino)->ino_state =
532d8ba45e2SEd Maste 			   (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
5331c85e6a3SKirk McKusick 			inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink);
5345cc52631SKirk McKusick 			irelse(&ip);
5358fae3551SRodney W. Grimes 			goto again;
5368fae3551SRodney W. Grimes 
5378fae3551SRodney W. Grimes 		case DSTATE:
538af6726e6SDon Lewis 		case DZLINK:
539d33e92f9SJulian Elischer 			if (inoinfo(idesc->id_number)->ino_state == DFOUND)
540d33e92f9SJulian Elischer 				inoinfo(dirp->d_ino)->ino_state = DFOUND;
5417fed38d0SPhilippe Charnier 			/* FALLTHROUGH */
5428fae3551SRodney W. Grimes 
5438fae3551SRodney W. Grimes 		case DFOUND:
5448fae3551SRodney W. Grimes 			inp = getinoinfo(dirp->d_ino);
54537776076SKirk McKusick 			if (idesc->id_entryno > 2) {
546fe5e6e2cSKirk McKusick 				if (inp->i_parent == 0) {
5478fae3551SRodney W. Grimes 					inp->i_parent = idesc->id_number;
548fe5e6e2cSKirk McKusick 					check_dirdepth(inp);
549fe5e6e2cSKirk McKusick 				} else if ((n = fix_extraneous(inp, idesc))) {
55037776076SKirk McKusick 					break;
55137776076SKirk McKusick 				}
552fe5e6e2cSKirk McKusick 			}
5537fed38d0SPhilippe Charnier 			/* FALLTHROUGH */
5548fae3551SRodney W. Grimes 
5558fae3551SRodney W. Grimes 		case FSTATE:
556af6726e6SDon Lewis 		case FZLINK:
557381ee4c2SPoul-Henning Kamp 			if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) {
5588fae3551SRodney W. Grimes 				fileerror(idesc->id_number, dirp->d_ino,
5598fae3551SRodney W. Grimes 				    "BAD TYPE VALUE");
560d33e92f9SJulian Elischer 				dirp->d_type = inoinfo(dirp->d_ino)->ino_type;
5618fae3551SRodney W. Grimes 				if (reply("FIX") == 1)
5628fae3551SRodney W. Grimes 					ret |= ALTERED;
5638fae3551SRodney W. Grimes 			}
564d33e92f9SJulian Elischer 			inoinfo(dirp->d_ino)->ino_linkcnt--;
5658fae3551SRodney W. Grimes 			break;
5668fae3551SRodney W. Grimes 
5678fae3551SRodney W. Grimes 		default:
568623d7cb6SMatthew D Fleming 			errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
569623d7cb6SMatthew D Fleming 			    inoinfo(dirp->d_ino)->ino_state,
570623d7cb6SMatthew D Fleming 			    (uintmax_t)dirp->d_ino);
5718fae3551SRodney W. Grimes 		}
5728fae3551SRodney W. Grimes 	}
5738fae3551SRodney W. Grimes 	if (n == 0)
5748fae3551SRodney W. Grimes 		return (ret|KEEPON);
5758fae3551SRodney W. Grimes 	dirp->d_ino = 0;
5768fae3551SRodney W. Grimes 	return (ret|KEEPON|ALTERED);
5778fae3551SRodney W. Grimes }
5788fae3551SRodney W. Grimes 
57937776076SKirk McKusick static int
fix_extraneous(struct inoinfo * inp,struct inodesc * idesc)58037776076SKirk McKusick fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
58137776076SKirk McKusick {
58277355ce0SKirk McKusick 	char *cp;
5835cc52631SKirk McKusick 	struct inode ip;
58437776076SKirk McKusick 	struct inodesc dotdesc;
58537776076SKirk McKusick 	char oldname[MAXPATHLEN + 1];
58637776076SKirk McKusick 	char newname[MAXPATHLEN + 1];
58737776076SKirk McKusick 
58837776076SKirk McKusick 	/*
58937776076SKirk McKusick 	 * If we have not yet found "..", look it up now so we know
59037776076SKirk McKusick 	 * which inode the directory itself believes is its parent.
59137776076SKirk McKusick 	 */
59237776076SKirk McKusick 	if (inp->i_dotdot == 0) {
59337776076SKirk McKusick 		memset(&dotdesc, 0, sizeof(struct inodesc));
59437776076SKirk McKusick 		dotdesc.id_type = DATA;
59537776076SKirk McKusick 		dotdesc.id_number = idesc->id_dirp->d_ino;
59637776076SKirk McKusick 		dotdesc.id_func = findino;
59737776076SKirk McKusick 		dotdesc.id_name = strdup("..");
5985cc52631SKirk McKusick 		ginode(dotdesc.id_number, &ip);
5995cc52631SKirk McKusick 		if ((ckinode(ip.i_dp, &dotdesc) & FOUND))
60037776076SKirk McKusick 			inp->i_dotdot = dotdesc.id_parent;
6015cc52631SKirk McKusick 		irelse(&ip);
602e5d0d1c5SKirk McKusick 		free(dotdesc.id_name);
60337776076SKirk McKusick 	}
60437776076SKirk McKusick 	/*
60537776076SKirk McKusick 	 * We have the previously found old name (inp->i_parent) and the
60637776076SKirk McKusick 	 * just found new name (idesc->id_number). We have five cases:
60737776076SKirk McKusick 	 * 1)  ".." is missing - can remove either name, choose to delete
60837776076SKirk McKusick 	 *     new one and let fsck create ".." pointing to old name.
60937776076SKirk McKusick 	 * 2) Both new and old are in same directory, choose to delete
61037776076SKirk McKusick 	 *    the new name and let fsck fix ".." if it is wrong.
61137776076SKirk McKusick 	 * 3) ".." does not point to the new name, so delete it and let
61237776076SKirk McKusick 	 *    fsck fix ".." to point to the old one if it is wrong.
61337776076SKirk McKusick 	 * 4) ".." points to the old name only, so delete the new one.
61437776076SKirk McKusick 	 * 5) ".." points to the new name only, so delete the old one.
61537776076SKirk McKusick 	 *
61637776076SKirk McKusick 	 * For cases 1-4 we eliminate the new name;
61737776076SKirk McKusick 	 * for case 5 we eliminate the old name.
61837776076SKirk McKusick 	 */
61937776076SKirk McKusick 	if (inp->i_dotdot == 0 ||		    /* Case 1 */
62037776076SKirk McKusick 	    idesc->id_number == inp->i_parent ||    /* Case 2 */
62137776076SKirk McKusick 	    inp->i_dotdot != idesc->id_number ||    /* Case 3 */
62237776076SKirk McKusick 	    inp->i_dotdot == inp->i_parent) {	    /* Case 4 */
62337776076SKirk McKusick 		getpathname(newname, idesc->id_number, idesc->id_number);
62437776076SKirk McKusick 		if (strcmp(newname, "/") != 0)
62537776076SKirk McKusick 			strcat (newname, "/");
62637776076SKirk McKusick 		strcat(newname, idesc->id_dirp->d_name);
62737776076SKirk McKusick 		getpathname(oldname, inp->i_number, inp->i_number);
62877355ce0SKirk McKusick 		pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s",
62937776076SKirk McKusick 		    newname, oldname);
63037776076SKirk McKusick 		if (cursnapshot != 0) {
63137776076SKirk McKusick 			/*
63237776076SKirk McKusick 			 * We need to
63337776076SKirk McKusick 			 *    setcwd(idesc->id_number);
63437776076SKirk McKusick 			 *    unlink(idesc->id_dirp->d_name);
63537776076SKirk McKusick 			 */
63677355ce0SKirk McKusick 			cmd.value = idesc->id_number;
63777355ce0SKirk McKusick 			if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
63877355ce0SKirk McKusick 			    &cmd, sizeof cmd) == -1) {
63937776076SKirk McKusick 				printf(" (IGNORED)\n");
64037776076SKirk McKusick 				return (0);
64137776076SKirk McKusick 			}
642f83a0037SKirk McKusick 			cmd.value = (intptr_t)idesc->id_dirp->d_name;
64377355ce0SKirk McKusick 			cmd.size = inp->i_number; /* verify same name */
64477355ce0SKirk McKusick 			if (sysctlbyname("vfs.ffs.unlink", 0, 0,
64577355ce0SKirk McKusick 			    &cmd, sizeof cmd) == -1) {
64677355ce0SKirk McKusick 				printf(" (UNLINK FAILED: %s)\n",
64777355ce0SKirk McKusick 				    strerror(errno));
64877355ce0SKirk McKusick 				return (0);
64977355ce0SKirk McKusick 			}
65077355ce0SKirk McKusick 			printf(" (REMOVED)\n");
65177355ce0SKirk McKusick 			return (0);
65277355ce0SKirk McKusick 		}
65337776076SKirk McKusick 		if (preen) {
65437776076SKirk McKusick 			printf(" (REMOVED)\n");
65537776076SKirk McKusick 			return (1);
65637776076SKirk McKusick 		}
65737776076SKirk McKusick 		return (reply("REMOVE"));
65837776076SKirk McKusick 	}
65937776076SKirk McKusick 	/*
66037776076SKirk McKusick 	 * None of the first four cases above, so must be case (5).
66137776076SKirk McKusick 	 * Eliminate the old name and make the new the name the parent.
66237776076SKirk McKusick 	 */
66337776076SKirk McKusick 	getpathname(oldname, inp->i_parent, inp->i_number);
66437776076SKirk McKusick 	getpathname(newname, inp->i_number, inp->i_number);
66577355ce0SKirk McKusick 	pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname,
66637776076SKirk McKusick 	    newname);
66737776076SKirk McKusick 	if (cursnapshot != 0) {
66837776076SKirk McKusick 		/*
66937776076SKirk McKusick 		 * We need to
67037776076SKirk McKusick 		 *    setcwd(inp->i_parent);
67137776076SKirk McKusick 		 *    unlink(last component of oldname pathname);
67237776076SKirk McKusick 		 */
67377355ce0SKirk McKusick 		cmd.value = inp->i_parent;
67477355ce0SKirk McKusick 		if (sysctlbyname("vfs.ffs.setcwd", 0, 0,
67577355ce0SKirk McKusick 		    &cmd, sizeof cmd) == -1) {
67637776076SKirk McKusick 			printf(" (IGNORED)\n");
67737776076SKirk McKusick 			return (0);
67837776076SKirk McKusick 		}
679b3608ae1SEd Schouten 		if ((cp = strchr(oldname, '/')) == NULL) {
68077355ce0SKirk McKusick 			printf(" (IGNORED)\n");
68177355ce0SKirk McKusick 			return (0);
68277355ce0SKirk McKusick 		}
683f83a0037SKirk McKusick 		cmd.value = (intptr_t)(cp + 1);
68477355ce0SKirk McKusick 		cmd.size = inp->i_number; /* verify same name */
68577355ce0SKirk McKusick 		if (sysctlbyname("vfs.ffs.unlink", 0, 0,
68677355ce0SKirk McKusick 		    &cmd, sizeof cmd) == -1) {
68777355ce0SKirk McKusick 			printf(" (UNLINK FAILED: %s)\n",
68877355ce0SKirk McKusick 			    strerror(errno));
68977355ce0SKirk McKusick 			return (0);
69077355ce0SKirk McKusick 		}
69177355ce0SKirk McKusick 		printf(" (REMOVED)\n");
69277355ce0SKirk McKusick 		inp->i_parent = idesc->id_number;  /* reparent to correct dir */
69377355ce0SKirk McKusick 		return (0);
69477355ce0SKirk McKusick 	}
69537776076SKirk McKusick 	if (!preen && !reply("REMOVE"))
69637776076SKirk McKusick 		return (0);
69737776076SKirk McKusick 	memset(&dotdesc, 0, sizeof(struct inodesc));
69837776076SKirk McKusick 	dotdesc.id_type = DATA;
69937776076SKirk McKusick 	dotdesc.id_number = inp->i_parent; /* directory in which name appears */
70037776076SKirk McKusick 	dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
70137776076SKirk McKusick 	dotdesc.id_func = deleteentry;
7025cc52631SKirk McKusick 	ginode(dotdesc.id_number, &ip);
7035cc52631SKirk McKusick 	if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen)
70437776076SKirk McKusick 		printf(" (REMOVED)\n");
7055cc52631SKirk McKusick 	irelse(&ip);
70637776076SKirk McKusick 	inp->i_parent = idesc->id_number;  /* reparent to correct directory */
70737776076SKirk McKusick 	inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
70837776076SKirk McKusick 	return (0);
70937776076SKirk McKusick }
71037776076SKirk McKusick 
71137776076SKirk McKusick static int
deleteentry(struct inodesc * idesc)71237776076SKirk McKusick deleteentry(struct inodesc *idesc)
71337776076SKirk McKusick {
71437776076SKirk McKusick 	struct direct *dirp = idesc->id_dirp;
71537776076SKirk McKusick 
71637776076SKirk McKusick 	if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
71737776076SKirk McKusick 		return (KEEPON);
71837776076SKirk McKusick 	dirp->d_ino = 0;
71937776076SKirk McKusick 	return (ALTERED|STOP|FOUND);
72037776076SKirk McKusick }
72137776076SKirk McKusick 
7228fae3551SRodney W. Grimes /*
7238fae3551SRodney W. Grimes  * Routine to sort disk blocks.
7248fae3551SRodney W. Grimes  */
725780a5c1eSPeter Wemm static int
blksort(const void * arg1,const void * arg2)726b70cd7eeSWarner Losh blksort(const void *arg1, const void *arg2)
7278fae3551SRodney W. Grimes {
7288fae3551SRodney W. Grimes 
729599304a4SPoul-Henning Kamp 	return ((*(struct inoinfo * const *)arg1)->i_blks[0] -
730599304a4SPoul-Henning Kamp 		(*(struct inoinfo * const *)arg2)->i_blks[0]);
7318fae3551SRodney W. Grimes }
732