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