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$"); 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); 226241012Smdf printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n", 227241012Smdf (uintmax_t)inp->i_number, pathbuf); 228202109Smckusick getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot); 229241012Smdf printf("CURRENTLY POINTS TO I=%ju (%s), ", 230241012Smdf (uintmax_t)inp->i_dotdot, pathbuf); 231202109Smckusick getpathname(pathbuf, inp->i_parent, inp->i_parent); 232241012Smdf printf("SHOULD POINT TO I=%ju (%s)", 233241012Smdf (uintmax_t)inp->i_parent, pathbuf); 234202109Smckusick if (cursnapshot != 0) { 235202109Smckusick /* 236202109Smckusick * We need to: 237202109Smckusick * setcwd(inp->i_number); 238202109Smckusick * setdotdot(inp->i_dotdot, inp->i_parent); 239202109Smckusick */ 240202109Smckusick cmd.value = inp->i_number; 241202109Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 242202109Smckusick &cmd, sizeof cmd) == -1) { 243202109Smckusick /* kernel lacks support for these functions */ 244202109Smckusick printf(" (IGNORED)\n"); 245202109Smckusick continue; 246202109Smckusick } 247202109Smckusick cmd.value = inp->i_dotdot; /* verify same value */ 248202109Smckusick cmd.size = inp->i_parent; /* new parent */ 249202109Smckusick if (sysctlbyname("vfs.ffs.setdotdot", 0, 0, 250202109Smckusick &cmd, sizeof cmd) == -1) { 251202109Smckusick printf(" (FIX FAILED: %s)\n", strerror(errno)); 252202109Smckusick continue; 253202109Smckusick } 254202109Smckusick printf(" (FIXED)\n"); 255202109Smckusick inoinfo(inp->i_parent)->ino_linkcnt--; 256202109Smckusick inp->i_dotdot = inp->i_parent; 2571558Srgrimes continue; 258202109Smckusick } 259202109Smckusick if (preen) 260202109Smckusick printf(" (FIXED)\n"); 261202109Smckusick else if (reply("FIX") == 0) 262202109Smckusick continue; 26341474Sjulian inoinfo(inp->i_dotdot)->ino_linkcnt++; 26441474Sjulian inoinfo(inp->i_parent)->ino_linkcnt--; 2651558Srgrimes inp->i_dotdot = inp->i_parent; 2661558Srgrimes (void)changeino(inp->i_number, "..", inp->i_parent); 2671558Srgrimes } 2681558Srgrimes /* 2691558Srgrimes * Mark all the directories that can be found from the root. 2701558Srgrimes */ 2711558Srgrimes propagate(); 2721558Srgrimes} 2731558Srgrimes 27423675Speterstatic int 27592839Simppass2check(struct inodesc *idesc) 2761558Srgrimes{ 27792806Sobrien struct direct *dirp = idesc->id_dirp; 278208330Smckusick char dirname[MAXPATHLEN + 1]; 27992806Sobrien struct inoinfo *inp; 2801558Srgrimes int n, entrysize, ret = 0; 28198542Smckusick union dinode *dp; 282100935Sphk const char *errmsg; 2831558Srgrimes struct direct proto; 2841558Srgrimes 2851558Srgrimes /* 2861558Srgrimes * check for "." 2871558Srgrimes */ 288176574Sdelphij if (dirp->d_ino > maxino) 289176574Sdelphij goto chk2; 2901558Srgrimes if (idesc->id_entryno != 0) 2911558Srgrimes goto chk1; 2921558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 2931558Srgrimes if (dirp->d_ino != idesc->id_number) { 2941558Srgrimes direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 2951558Srgrimes dirp->d_ino = idesc->id_number; 2961558Srgrimes if (reply("FIX") == 1) 2971558Srgrimes ret |= ALTERED; 2981558Srgrimes } 29996483Sphk if (dirp->d_type != DT_DIR) { 3001558Srgrimes direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 3011558Srgrimes dirp->d_type = DT_DIR; 3021558Srgrimes if (reply("FIX") == 1) 3031558Srgrimes ret |= ALTERED; 3041558Srgrimes } 3051558Srgrimes goto chk1; 3061558Srgrimes } 3071558Srgrimes direrror(idesc->id_number, "MISSING '.'"); 3081558Srgrimes proto.d_ino = idesc->id_number; 30996483Sphk proto.d_type = DT_DIR; 3101558Srgrimes proto.d_namlen = 1; 3111558Srgrimes (void)strcpy(proto.d_name, "."); 3121558Srgrimes entrysize = DIRSIZ(0, &proto); 3131558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 3141558Srgrimes pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 3151558Srgrimes dirp->d_name); 3161558Srgrimes } else if (dirp->d_reclen < entrysize) { 3171558Srgrimes pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 3181558Srgrimes } else if (dirp->d_reclen < 2 * entrysize) { 3191558Srgrimes proto.d_reclen = dirp->d_reclen; 32023675Speter memmove(dirp, &proto, (size_t)entrysize); 3211558Srgrimes if (reply("FIX") == 1) 3221558Srgrimes ret |= ALTERED; 3231558Srgrimes } else { 3241558Srgrimes n = dirp->d_reclen - entrysize; 3251558Srgrimes proto.d_reclen = entrysize; 32623675Speter memmove(dirp, &proto, (size_t)entrysize); 3271558Srgrimes idesc->id_entryno++; 32841474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 3291558Srgrimes dirp = (struct direct *)((char *)(dirp) + entrysize); 33023675Speter memset(dirp, 0, (size_t)n); 3311558Srgrimes dirp->d_reclen = n; 3321558Srgrimes if (reply("FIX") == 1) 3331558Srgrimes ret |= ALTERED; 3341558Srgrimes } 3351558Srgrimeschk1: 3361558Srgrimes if (idesc->id_entryno > 1) 3371558Srgrimes goto chk2; 3381558Srgrimes inp = getinoinfo(idesc->id_number); 3391558Srgrimes proto.d_ino = inp->i_parent; 34096483Sphk proto.d_type = DT_DIR; 3411558Srgrimes proto.d_namlen = 2; 3421558Srgrimes (void)strcpy(proto.d_name, ".."); 3431558Srgrimes entrysize = DIRSIZ(0, &proto); 3441558Srgrimes if (idesc->id_entryno == 0) { 3451558Srgrimes n = DIRSIZ(0, dirp); 3461558Srgrimes if (dirp->d_reclen < n + entrysize) 3471558Srgrimes goto chk2; 3481558Srgrimes proto.d_reclen = dirp->d_reclen - n; 3491558Srgrimes dirp->d_reclen = n; 3501558Srgrimes idesc->id_entryno++; 35141474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 3521558Srgrimes dirp = (struct direct *)((char *)(dirp) + n); 35323675Speter memset(dirp, 0, (size_t)proto.d_reclen); 3541558Srgrimes dirp->d_reclen = proto.d_reclen; 3551558Srgrimes } 3561558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 3571558Srgrimes inp->i_dotdot = dirp->d_ino; 35896483Sphk if (dirp->d_type != DT_DIR) { 3591558Srgrimes direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 3601558Srgrimes dirp->d_type = DT_DIR; 3611558Srgrimes if (reply("FIX") == 1) 3621558Srgrimes ret |= ALTERED; 3631558Srgrimes } 3641558Srgrimes goto chk2; 3651558Srgrimes } 3661558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 3671558Srgrimes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 3681558Srgrimes pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 3691558Srgrimes dirp->d_name); 3701558Srgrimes inp->i_dotdot = (ino_t)-1; 3711558Srgrimes } else if (dirp->d_reclen < entrysize) { 3721558Srgrimes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 3731558Srgrimes pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 3741558Srgrimes inp->i_dotdot = (ino_t)-1; 3751558Srgrimes } else if (inp->i_parent != 0) { 3761558Srgrimes /* 3771558Srgrimes * We know the parent, so fix now. 3781558Srgrimes */ 3791558Srgrimes inp->i_dotdot = inp->i_parent; 3801558Srgrimes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 3811558Srgrimes proto.d_reclen = dirp->d_reclen; 38223675Speter memmove(dirp, &proto, (size_t)entrysize); 3831558Srgrimes if (reply("FIX") == 1) 3841558Srgrimes ret |= ALTERED; 3851558Srgrimes } 3861558Srgrimes idesc->id_entryno++; 3871558Srgrimes if (dirp->d_ino != 0) 38841474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 3891558Srgrimes return (ret|KEEPON); 3901558Srgrimeschk2: 3911558Srgrimes if (dirp->d_ino == 0) 3921558Srgrimes return (ret|KEEPON); 3931558Srgrimes if (dirp->d_namlen <= 2 && 3941558Srgrimes dirp->d_name[0] == '.' && 3951558Srgrimes idesc->id_entryno >= 2) { 3961558Srgrimes if (dirp->d_namlen == 1) { 3971558Srgrimes direrror(idesc->id_number, "EXTRA '.' ENTRY"); 3981558Srgrimes dirp->d_ino = 0; 3991558Srgrimes if (reply("FIX") == 1) 4001558Srgrimes ret |= ALTERED; 4011558Srgrimes return (KEEPON | ret); 4021558Srgrimes } 4031558Srgrimes if (dirp->d_name[1] == '.') { 4041558Srgrimes direrror(idesc->id_number, "EXTRA '..' ENTRY"); 4051558Srgrimes dirp->d_ino = 0; 4061558Srgrimes if (reply("FIX") == 1) 4071558Srgrimes ret |= ALTERED; 4081558Srgrimes return (KEEPON | ret); 4091558Srgrimes } 4101558Srgrimes } 4111558Srgrimes idesc->id_entryno++; 4121558Srgrimes n = 0; 4131558Srgrimes if (dirp->d_ino > maxino) { 4141558Srgrimes fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 4151558Srgrimes n = reply("REMOVE"); 41696483Sphk } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 41723675Speter (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 41823675Speter fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 41923675Speter dirp->d_ino = WINO; 42023675Speter dirp->d_type = DT_WHT; 42123675Speter if (reply("FIX") == 1) 42223675Speter ret |= ALTERED; 4231558Srgrimes } else { 4241558Srgrimesagain: 42541474Sjulian switch (inoinfo(dirp->d_ino)->ino_state) { 4261558Srgrimes case USTATE: 4271558Srgrimes if (idesc->id_entryno <= 2) 4281558Srgrimes break; 4291558Srgrimes fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 4301558Srgrimes n = reply("REMOVE"); 4311558Srgrimes break; 4321558Srgrimes 4331558Srgrimes case DCLEAR: 4341558Srgrimes case FCLEAR: 4351558Srgrimes if (idesc->id_entryno <= 2) 4361558Srgrimes break; 43741474Sjulian if (inoinfo(dirp->d_ino)->ino_state == FCLEAR) 4381558Srgrimes errmsg = "DUP/BAD"; 43934266Sjulian else if (!preen && !usedsoftdep) 4401558Srgrimes errmsg = "ZERO LENGTH DIRECTORY"; 441208330Smckusick else if (cursnapshot == 0) { 4421558Srgrimes n = 1; 4431558Srgrimes break; 444208330Smckusick } else { 445208330Smckusick getpathname(dirname, idesc->id_number, 446208330Smckusick dirp->d_ino); 447241012Smdf pwarn("ZERO LENGTH DIRECTORY %s I=%ju", 448241012Smdf dirname, (uintmax_t)dirp->d_ino); 449208330Smckusick /* 450208330Smckusick * We need to: 451208330Smckusick * setcwd(idesc->id_parent); 452208330Smckusick * rmdir(dirp->d_name); 453208330Smckusick */ 454208330Smckusick cmd.value = idesc->id_number; 455208330Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 456208330Smckusick &cmd, sizeof cmd) == -1) { 457208330Smckusick /* kernel lacks support */ 458208330Smckusick printf(" (IGNORED)\n"); 459208330Smckusick n = 1; 460208330Smckusick break; 461208330Smckusick } 462208330Smckusick if (rmdir(dirp->d_name) == -1) { 463208330Smckusick printf(" (REMOVAL FAILED: %s)\n", 464208330Smckusick strerror(errno)); 465208330Smckusick n = 1; 466208330Smckusick break; 467208330Smckusick } 468208330Smckusick /* ".." reference to parent is removed */ 469208330Smckusick inoinfo(idesc->id_number)->ino_linkcnt--; 470208330Smckusick printf(" (REMOVED)\n"); 471208330Smckusick break; 4721558Srgrimes } 4731558Srgrimes fileerror(idesc->id_number, dirp->d_ino, errmsg); 4741558Srgrimes if ((n = reply("REMOVE")) == 1) 4751558Srgrimes break; 4761558Srgrimes dp = ginode(dirp->d_ino); 47741474Sjulian inoinfo(dirp->d_ino)->ino_state = 47898542Smckusick (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 47998542Smckusick inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 4801558Srgrimes goto again; 4811558Srgrimes 4821558Srgrimes case DSTATE: 483136281Struckman case DZLINK: 48441474Sjulian if (inoinfo(idesc->id_number)->ino_state == DFOUND) 48541474Sjulian inoinfo(dirp->d_ino)->ino_state = DFOUND; 486102411Scharnier /* FALLTHROUGH */ 4871558Srgrimes 4881558Srgrimes case DFOUND: 4891558Srgrimes inp = getinoinfo(dirp->d_ino); 490202107Smckusick if (idesc->id_entryno > 2) { 491202107Smckusick if (inp->i_parent == 0) 492202107Smckusick inp->i_parent = idesc->id_number; 493202107Smckusick else if ((n = fix_extraneous(inp, idesc)) == 1) 49474556Smckusick break; 4951558Srgrimes } 496102411Scharnier /* FALLTHROUGH */ 4971558Srgrimes 4981558Srgrimes case FSTATE: 499136281Struckman case FZLINK: 50096483Sphk if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 5011558Srgrimes fileerror(idesc->id_number, dirp->d_ino, 5021558Srgrimes "BAD TYPE VALUE"); 50341474Sjulian dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 5041558Srgrimes if (reply("FIX") == 1) 5051558Srgrimes ret |= ALTERED; 5061558Srgrimes } 50741474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 5081558Srgrimes break; 5091558Srgrimes 5101558Srgrimes default: 511241012Smdf errx(EEXIT, "BAD STATE %d FOR INODE I=%ju", 512241012Smdf inoinfo(dirp->d_ino)->ino_state, 513241012Smdf (uintmax_t)dirp->d_ino); 5141558Srgrimes } 5151558Srgrimes } 5161558Srgrimes if (n == 0) 5171558Srgrimes return (ret|KEEPON); 5181558Srgrimes dirp->d_ino = 0; 5191558Srgrimes return (ret|KEEPON|ALTERED); 5201558Srgrimes} 5211558Srgrimes 522202107Smckusickstatic int 523202107Smckusickfix_extraneous(struct inoinfo *inp, struct inodesc *idesc) 524202107Smckusick{ 525202109Smckusick char *cp; 526202107Smckusick struct inodesc dotdesc; 527202107Smckusick char oldname[MAXPATHLEN + 1]; 528202107Smckusick char newname[MAXPATHLEN + 1]; 529221110Sdes 530202107Smckusick /* 531202107Smckusick * If we have not yet found "..", look it up now so we know 532202107Smckusick * which inode the directory itself believes is its parent. 533202107Smckusick */ 534202107Smckusick if (inp->i_dotdot == 0) { 535202107Smckusick memset(&dotdesc, 0, sizeof(struct inodesc)); 536202107Smckusick dotdesc.id_type = DATA; 537202107Smckusick dotdesc.id_number = idesc->id_dirp->d_ino; 538202107Smckusick dotdesc.id_func = findino; 539202107Smckusick dotdesc.id_name = strdup(".."); 540202107Smckusick if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND)) 541202107Smckusick inp->i_dotdot = dotdesc.id_parent; 542202107Smckusick } 543202107Smckusick /* 544202107Smckusick * We have the previously found old name (inp->i_parent) and the 545202107Smckusick * just found new name (idesc->id_number). We have five cases: 546202107Smckusick * 1) ".." is missing - can remove either name, choose to delete 547202107Smckusick * new one and let fsck create ".." pointing to old name. 548202107Smckusick * 2) Both new and old are in same directory, choose to delete 549202107Smckusick * the new name and let fsck fix ".." if it is wrong. 550202107Smckusick * 3) ".." does not point to the new name, so delete it and let 551202107Smckusick * fsck fix ".." to point to the old one if it is wrong. 552202107Smckusick * 4) ".." points to the old name only, so delete the new one. 553202107Smckusick * 5) ".." points to the new name only, so delete the old one. 554202107Smckusick * 555202107Smckusick * For cases 1-4 we eliminate the new name; 556202107Smckusick * for case 5 we eliminate the old name. 557202107Smckusick */ 558202107Smckusick if (inp->i_dotdot == 0 || /* Case 1 */ 559202107Smckusick idesc->id_number == inp->i_parent || /* Case 2 */ 560202107Smckusick inp->i_dotdot != idesc->id_number || /* Case 3 */ 561202107Smckusick inp->i_dotdot == inp->i_parent) { /* Case 4 */ 562202107Smckusick getpathname(newname, idesc->id_number, idesc->id_number); 563202107Smckusick if (strcmp(newname, "/") != 0) 564202107Smckusick strcat (newname, "/"); 565202107Smckusick strcat(newname, idesc->id_dirp->d_name); 566202107Smckusick getpathname(oldname, inp->i_number, inp->i_number); 567202109Smckusick pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", 568202107Smckusick newname, oldname); 569202107Smckusick if (cursnapshot != 0) { 570202107Smckusick /* 571202107Smckusick * We need to 572202107Smckusick * setcwd(idesc->id_number); 573202107Smckusick * unlink(idesc->id_dirp->d_name); 574202107Smckusick */ 575202109Smckusick cmd.value = idesc->id_number; 576202109Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 577202109Smckusick &cmd, sizeof cmd) == -1) { 578202109Smckusick printf(" (IGNORED)\n"); 579202109Smckusick return (0); 580202109Smckusick } 581202131Smckusick cmd.value = (intptr_t)idesc->id_dirp->d_name; 582202109Smckusick cmd.size = inp->i_number; /* verify same name */ 583202109Smckusick if (sysctlbyname("vfs.ffs.unlink", 0, 0, 584202109Smckusick &cmd, sizeof cmd) == -1) { 585202109Smckusick printf(" (UNLINK FAILED: %s)\n", 586202109Smckusick strerror(errno)); 587202109Smckusick return (0); 588202109Smckusick } 589202109Smckusick printf(" (REMOVED)\n"); 590202107Smckusick return (0); 591202107Smckusick } 592202107Smckusick if (preen) { 593202107Smckusick printf(" (REMOVED)\n"); 594202107Smckusick return (1); 595202107Smckusick } 596202107Smckusick return (reply("REMOVE")); 597202107Smckusick } 598202107Smckusick /* 599202107Smckusick * None of the first four cases above, so must be case (5). 600202107Smckusick * Eliminate the old name and make the new the name the parent. 601202107Smckusick */ 602202107Smckusick getpathname(oldname, inp->i_parent, inp->i_number); 603202107Smckusick getpathname(newname, inp->i_number, inp->i_number); 604202109Smckusick pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname, 605202107Smckusick newname); 606202107Smckusick if (cursnapshot != 0) { 607202107Smckusick /* 608202107Smckusick * We need to 609202107Smckusick * setcwd(inp->i_parent); 610202107Smckusick * unlink(last component of oldname pathname); 611202107Smckusick */ 612202109Smckusick cmd.value = inp->i_parent; 613202109Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 614202109Smckusick &cmd, sizeof cmd) == -1) { 615202109Smckusick printf(" (IGNORED)\n"); 616202109Smckusick return (0); 617202109Smckusick } 618229403Sed if ((cp = strchr(oldname, '/')) == NULL) { 619202109Smckusick printf(" (IGNORED)\n"); 620202109Smckusick return (0); 621202109Smckusick } 622202131Smckusick cmd.value = (intptr_t)(cp + 1); 623202109Smckusick cmd.size = inp->i_number; /* verify same name */ 624202109Smckusick if (sysctlbyname("vfs.ffs.unlink", 0, 0, 625202109Smckusick &cmd, sizeof cmd) == -1) { 626202109Smckusick printf(" (UNLINK FAILED: %s)\n", 627202109Smckusick strerror(errno)); 628202109Smckusick return (0); 629202109Smckusick } 630202109Smckusick printf(" (REMOVED)\n"); 631202109Smckusick inp->i_parent = idesc->id_number; /* reparent to correct dir */ 632202107Smckusick return (0); 633202107Smckusick } 634202107Smckusick if (!preen && !reply("REMOVE")) 635202107Smckusick return (0); 636202107Smckusick memset(&dotdesc, 0, sizeof(struct inodesc)); 637202107Smckusick dotdesc.id_type = DATA; 638202107Smckusick dotdesc.id_number = inp->i_parent; /* directory in which name appears */ 639202107Smckusick dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */ 640202107Smckusick dotdesc.id_func = deleteentry; 641202107Smckusick if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen) 642202107Smckusick printf(" (REMOVED)\n"); 643202107Smckusick inp->i_parent = idesc->id_number; /* reparent to correct directory */ 644202107Smckusick inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */ 645202107Smckusick return (0); 646202107Smckusick} 647202107Smckusick 648202107Smckusickstatic int 649202107Smckusickdeleteentry(struct inodesc *idesc) 650202107Smckusick{ 651202107Smckusick struct direct *dirp = idesc->id_dirp; 652202107Smckusick 653202107Smckusick if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent) 654202107Smckusick return (KEEPON); 655202107Smckusick dirp->d_ino = 0; 656202107Smckusick return (ALTERED|STOP|FOUND); 657202107Smckusick} 658202107Smckusick 6591558Srgrimes/* 6601558Srgrimes * Routine to sort disk blocks. 6611558Srgrimes */ 66223675Speterstatic int 66392839Simpblksort(const void *arg1, const void *arg2) 6641558Srgrimes{ 6651558Srgrimes 666100935Sphk return ((*(struct inoinfo * const *)arg1)->i_blks[0] - 667100935Sphk (*(struct inoinfo * const *)arg2)->i_blks[0]); 6681558Srgrimes} 669