pass2.c revision 202109
11558Srgrimes/* 21558Srgrimes * Copyright (c) 1980, 1986, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * Redistribution and use in source and binary forms, with or without 61558Srgrimes * modification, are permitted provided that the following conditions 71558Srgrimes * are met: 81558Srgrimes * 1. Redistributions of source code must retain the above copyright 91558Srgrimes * notice, this list of conditions and the following disclaimer. 101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111558Srgrimes * notice, this list of conditions and the following disclaimer in the 121558Srgrimes * documentation and/or other materials provided with the distribution. 131558Srgrimes * 4. Neither the name of the University nor the names of its contributors 141558Srgrimes * may be used to endorse or promote products derived from this software 151558Srgrimes * without specific prior written permission. 161558Srgrimes * 171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271558Srgrimes * SUCH DAMAGE. 281558Srgrimes */ 291558Srgrimes 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: head/sbin/fsck_ffs/pass2.c 202109 2010-01-11 20:05:38Z mckusick $"); 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); 1131558Srgrimes inodirty(); 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); 1591558Srgrimes inodirty(); 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)); 1781558Srgrimes inodirty(); 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); 18598542Smckusick for (i = 0; 18698542Smckusick i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR); 18798542Smckusick i++) 188134589Sscottl DIP_SET(dp, di_db[i], inp->i_blks[i]); 18998542Smckusick if (inp->i_numblks > NDADDR) 19098542Smckusick for (i = 0; i < NIADDR; i++) 191134589Sscottl DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]); 1921558Srgrimes curino.id_number = inp->i_number; 1931558Srgrimes curino.id_parent = inp->i_parent; 1941558Srgrimes (void)ckinode(dp, &curino); 1951558Srgrimes } 1961558Srgrimes /* 1971558Srgrimes * Now that the parents of all directories have been found, 1981558Srgrimes * make another pass to verify the value of `..' 1991558Srgrimes */ 2001558Srgrimes for (inpp = inpsort; inpp < inpend; inpp++) { 2011558Srgrimes inp = *inpp; 2021558Srgrimes if (inp->i_parent == 0 || inp->i_isize == 0) 2031558Srgrimes continue; 20441474Sjulian if (inoinfo(inp->i_parent)->ino_state == DFOUND && 205136281Struckman INO_IS_DUNFOUND(inp->i_number)) 20641474Sjulian inoinfo(inp->i_number)->ino_state = DFOUND; 2071558Srgrimes if (inp->i_dotdot == inp->i_parent || 2081558Srgrimes inp->i_dotdot == (ino_t)-1) 2091558Srgrimes continue; 2101558Srgrimes if (inp->i_dotdot == 0) { 2111558Srgrimes inp->i_dotdot = inp->i_parent; 2121558Srgrimes fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 2131558Srgrimes if (reply("FIX") == 0) 2141558Srgrimes continue; 2151558Srgrimes (void)makeentry(inp->i_number, inp->i_parent, ".."); 21641474Sjulian inoinfo(inp->i_parent)->ino_linkcnt--; 2171558Srgrimes continue; 2181558Srgrimes } 219202109Smckusick /* 220202109Smckusick * Here we have: 221202109Smckusick * inp->i_number is directory with bad ".." in it. 222202109Smckusick * inp->i_dotdot is current value of "..". 223202109Smckusick * inp->i_parent is directory to which ".." should point. 224202109Smckusick */ 225202109Smckusick getpathname(pathbuf, inp->i_parent, inp->i_number); 226202109Smckusick printf("BAD INODE NUMBER FOR '..' in DIR I=%d (%s)\n", 227202109Smckusick inp->i_number, pathbuf); 228202109Smckusick getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot); 229202109Smckusick printf("CURRENTLY POINTS TO I=%d (%s), ", inp->i_dotdot, 230202109Smckusick pathbuf); 231202109Smckusick getpathname(pathbuf, inp->i_parent, inp->i_parent); 232202109Smckusick printf("SHOULD POINT TO I=%d (%s)", inp->i_parent, pathbuf); 233202109Smckusick if (cursnapshot != 0) { 234202109Smckusick /* 235202109Smckusick * We need to: 236202109Smckusick * setcwd(inp->i_number); 237202109Smckusick * setdotdot(inp->i_dotdot, inp->i_parent); 238202109Smckusick */ 239202109Smckusick cmd.value = inp->i_number; 240202109Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 241202109Smckusick &cmd, sizeof cmd) == -1) { 242202109Smckusick /* kernel lacks support for these functions */ 243202109Smckusick printf(" (IGNORED)\n"); 244202109Smckusick continue; 245202109Smckusick } 246202109Smckusick cmd.value = inp->i_dotdot; /* verify same value */ 247202109Smckusick cmd.size = inp->i_parent; /* new parent */ 248202109Smckusick if (sysctlbyname("vfs.ffs.setdotdot", 0, 0, 249202109Smckusick &cmd, sizeof cmd) == -1) { 250202109Smckusick printf(" (FIX FAILED: %s)\n", strerror(errno)); 251202109Smckusick continue; 252202109Smckusick } 253202109Smckusick printf(" (FIXED)\n"); 254202109Smckusick inoinfo(inp->i_parent)->ino_linkcnt--; 255202109Smckusick inp->i_dotdot = inp->i_parent; 2561558Srgrimes continue; 257202109Smckusick } 258202109Smckusick if (preen) 259202109Smckusick printf(" (FIXED)\n"); 260202109Smckusick else if (reply("FIX") == 0) 261202109Smckusick continue; 26241474Sjulian inoinfo(inp->i_dotdot)->ino_linkcnt++; 26341474Sjulian inoinfo(inp->i_parent)->ino_linkcnt--; 2641558Srgrimes inp->i_dotdot = inp->i_parent; 2651558Srgrimes (void)changeino(inp->i_number, "..", inp->i_parent); 2661558Srgrimes } 2671558Srgrimes /* 2681558Srgrimes * Mark all the directories that can be found from the root. 2691558Srgrimes */ 2701558Srgrimes propagate(); 2711558Srgrimes} 2721558Srgrimes 27323675Speterstatic int 27492839Simppass2check(struct inodesc *idesc) 2751558Srgrimes{ 27692806Sobrien struct direct *dirp = idesc->id_dirp; 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"; 4391558Srgrimes else { 4401558Srgrimes n = 1; 4411558Srgrimes break; 4421558Srgrimes } 4431558Srgrimes fileerror(idesc->id_number, dirp->d_ino, errmsg); 4441558Srgrimes if ((n = reply("REMOVE")) == 1) 4451558Srgrimes break; 4461558Srgrimes dp = ginode(dirp->d_ino); 44741474Sjulian inoinfo(dirp->d_ino)->ino_state = 44898542Smckusick (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 44998542Smckusick inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 4501558Srgrimes goto again; 4511558Srgrimes 4521558Srgrimes case DSTATE: 453136281Struckman case DZLINK: 45441474Sjulian if (inoinfo(idesc->id_number)->ino_state == DFOUND) 45541474Sjulian inoinfo(dirp->d_ino)->ino_state = DFOUND; 456102411Scharnier /* FALLTHROUGH */ 4571558Srgrimes 4581558Srgrimes case DFOUND: 4591558Srgrimes inp = getinoinfo(dirp->d_ino); 460202107Smckusick if (idesc->id_entryno > 2) { 461202107Smckusick if (inp->i_parent == 0) 462202107Smckusick inp->i_parent = idesc->id_number; 463202107Smckusick else if ((n = fix_extraneous(inp, idesc)) == 1) 46474556Smckusick break; 4651558Srgrimes } 466102411Scharnier /* FALLTHROUGH */ 4671558Srgrimes 4681558Srgrimes case FSTATE: 469136281Struckman case FZLINK: 47096483Sphk if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 4711558Srgrimes fileerror(idesc->id_number, dirp->d_ino, 4721558Srgrimes "BAD TYPE VALUE"); 47341474Sjulian dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 4741558Srgrimes if (reply("FIX") == 1) 4751558Srgrimes ret |= ALTERED; 4761558Srgrimes } 47741474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 4781558Srgrimes break; 4791558Srgrimes 4801558Srgrimes default: 48123675Speter errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 48241474Sjulian inoinfo(dirp->d_ino)->ino_state, dirp->d_ino); 4831558Srgrimes } 4841558Srgrimes } 4851558Srgrimes if (n == 0) 4861558Srgrimes return (ret|KEEPON); 4871558Srgrimes dirp->d_ino = 0; 4881558Srgrimes return (ret|KEEPON|ALTERED); 4891558Srgrimes} 4901558Srgrimes 491202107Smckusickstatic int 492202107Smckusickfix_extraneous(struct inoinfo *inp, struct inodesc *idesc) 493202107Smckusick{ 494202109Smckusick char *cp; 495202107Smckusick struct inodesc dotdesc; 496202107Smckusick char oldname[MAXPATHLEN + 1]; 497202107Smckusick char newname[MAXPATHLEN + 1]; 498202107Smckusick 499202107Smckusick /* 500202107Smckusick * If we have not yet found "..", look it up now so we know 501202107Smckusick * which inode the directory itself believes is its parent. 502202107Smckusick */ 503202107Smckusick if (inp->i_dotdot == 0) { 504202107Smckusick memset(&dotdesc, 0, sizeof(struct inodesc)); 505202107Smckusick dotdesc.id_type = DATA; 506202107Smckusick dotdesc.id_number = idesc->id_dirp->d_ino; 507202107Smckusick dotdesc.id_func = findino; 508202107Smckusick dotdesc.id_name = strdup(".."); 509202107Smckusick if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND)) 510202107Smckusick inp->i_dotdot = dotdesc.id_parent; 511202107Smckusick } 512202107Smckusick /* 513202107Smckusick * We have the previously found old name (inp->i_parent) and the 514202107Smckusick * just found new name (idesc->id_number). We have five cases: 515202107Smckusick * 1) ".." is missing - can remove either name, choose to delete 516202107Smckusick * new one and let fsck create ".." pointing to old name. 517202107Smckusick * 2) Both new and old are in same directory, choose to delete 518202107Smckusick * the new name and let fsck fix ".." if it is wrong. 519202107Smckusick * 3) ".." does not point to the new name, so delete it and let 520202107Smckusick * fsck fix ".." to point to the old one if it is wrong. 521202107Smckusick * 4) ".." points to the old name only, so delete the new one. 522202107Smckusick * 5) ".." points to the new name only, so delete the old one. 523202107Smckusick * 524202107Smckusick * For cases 1-4 we eliminate the new name; 525202107Smckusick * for case 5 we eliminate the old name. 526202107Smckusick */ 527202107Smckusick if (inp->i_dotdot == 0 || /* Case 1 */ 528202107Smckusick idesc->id_number == inp->i_parent || /* Case 2 */ 529202107Smckusick inp->i_dotdot != idesc->id_number || /* Case 3 */ 530202107Smckusick inp->i_dotdot == inp->i_parent) { /* Case 4 */ 531202107Smckusick getpathname(newname, idesc->id_number, idesc->id_number); 532202107Smckusick if (strcmp(newname, "/") != 0) 533202107Smckusick strcat (newname, "/"); 534202107Smckusick strcat(newname, idesc->id_dirp->d_name); 535202107Smckusick getpathname(oldname, inp->i_number, inp->i_number); 536202109Smckusick pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", 537202107Smckusick newname, oldname); 538202107Smckusick if (cursnapshot != 0) { 539202107Smckusick /* 540202107Smckusick * We need to 541202107Smckusick * setcwd(idesc->id_number); 542202107Smckusick * unlink(idesc->id_dirp->d_name); 543202107Smckusick */ 544202109Smckusick cmd.value = idesc->id_number; 545202109Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 546202109Smckusick &cmd, sizeof cmd) == -1) { 547202109Smckusick printf(" (IGNORED)\n"); 548202109Smckusick return (0); 549202109Smckusick } 550202109Smckusick cmd.value = (int)idesc->id_dirp->d_name; 551202109Smckusick cmd.size = inp->i_number; /* verify same name */ 552202109Smckusick if (sysctlbyname("vfs.ffs.unlink", 0, 0, 553202109Smckusick &cmd, sizeof cmd) == -1) { 554202109Smckusick printf(" (UNLINK FAILED: %s)\n", 555202109Smckusick strerror(errno)); 556202109Smckusick return (0); 557202109Smckusick } 558202109Smckusick printf(" (REMOVED)\n"); 559202107Smckusick return (0); 560202107Smckusick } 561202107Smckusick if (preen) { 562202107Smckusick printf(" (REMOVED)\n"); 563202107Smckusick return (1); 564202107Smckusick } 565202107Smckusick return (reply("REMOVE")); 566202107Smckusick } 567202107Smckusick /* 568202107Smckusick * None of the first four cases above, so must be case (5). 569202107Smckusick * Eliminate the old name and make the new the name the parent. 570202107Smckusick */ 571202107Smckusick getpathname(oldname, inp->i_parent, inp->i_number); 572202107Smckusick getpathname(newname, inp->i_number, inp->i_number); 573202109Smckusick pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname, 574202107Smckusick newname); 575202107Smckusick if (cursnapshot != 0) { 576202107Smckusick /* 577202107Smckusick * We need to 578202107Smckusick * setcwd(inp->i_parent); 579202107Smckusick * unlink(last component of oldname pathname); 580202107Smckusick */ 581202109Smckusick cmd.value = inp->i_parent; 582202109Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 583202109Smckusick &cmd, sizeof cmd) == -1) { 584202109Smckusick printf(" (IGNORED)\n"); 585202109Smckusick return (0); 586202109Smckusick } 587202109Smckusick if ((cp = rindex(oldname, '/')) == NULL) { 588202109Smckusick printf(" (IGNORED)\n"); 589202109Smckusick return (0); 590202109Smckusick } 591202109Smckusick cmd.value = (int)(cp + 1); 592202109Smckusick cmd.size = inp->i_number; /* verify same name */ 593202109Smckusick if (sysctlbyname("vfs.ffs.unlink", 0, 0, 594202109Smckusick &cmd, sizeof cmd) == -1) { 595202109Smckusick printf(" (UNLINK FAILED: %s)\n", 596202109Smckusick strerror(errno)); 597202109Smckusick return (0); 598202109Smckusick } 599202109Smckusick printf(" (REMOVED)\n"); 600202109Smckusick inp->i_parent = idesc->id_number; /* reparent to correct dir */ 601202107Smckusick return (0); 602202107Smckusick } 603202107Smckusick if (!preen && !reply("REMOVE")) 604202107Smckusick return (0); 605202107Smckusick memset(&dotdesc, 0, sizeof(struct inodesc)); 606202107Smckusick dotdesc.id_type = DATA; 607202107Smckusick dotdesc.id_number = inp->i_parent; /* directory in which name appears */ 608202107Smckusick dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */ 609202107Smckusick dotdesc.id_func = deleteentry; 610202107Smckusick if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen) 611202107Smckusick printf(" (REMOVED)\n"); 612202107Smckusick inp->i_parent = idesc->id_number; /* reparent to correct directory */ 613202107Smckusick inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */ 614202107Smckusick return (0); 615202107Smckusick} 616202107Smckusick 617202107Smckusickstatic int 618202107Smckusickdeleteentry(struct inodesc *idesc) 619202107Smckusick{ 620202107Smckusick struct direct *dirp = idesc->id_dirp; 621202107Smckusick 622202107Smckusick if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent) 623202107Smckusick return (KEEPON); 624202107Smckusick dirp->d_ino = 0; 625202107Smckusick return (ALTERED|STOP|FOUND); 626202107Smckusick} 627202107Smckusick 6281558Srgrimes/* 6291558Srgrimes * Routine to sort disk blocks. 6301558Srgrimes */ 63123675Speterstatic int 63292839Simpblksort(const void *arg1, const void *arg2) 6331558Srgrimes{ 6341558Srgrimes 635100935Sphk return ((*(struct inoinfo * const *)arg1)->i_blks[0] - 636100935Sphk (*(struct inoinfo * const *)arg2)->i_blks[0]); 6371558Srgrimes} 638