pass2.c revision 208330
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 208330 2010-05-20 06:05:40Z 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; 277208330Smckusick char dirname[MAXPATHLEN + 1]; 27892806Sobrien struct inoinfo *inp; 2791558Srgrimes int n, entrysize, ret = 0; 28098542Smckusick union dinode *dp; 281100935Sphk const char *errmsg; 2821558Srgrimes struct direct proto; 2831558Srgrimes 2841558Srgrimes /* 2851558Srgrimes * check for "." 2861558Srgrimes */ 287176574Sdelphij if (dirp->d_ino > maxino) 288176574Sdelphij goto chk2; 2891558Srgrimes if (idesc->id_entryno != 0) 2901558Srgrimes goto chk1; 2911558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 2921558Srgrimes if (dirp->d_ino != idesc->id_number) { 2931558Srgrimes direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 2941558Srgrimes dirp->d_ino = idesc->id_number; 2951558Srgrimes if (reply("FIX") == 1) 2961558Srgrimes ret |= ALTERED; 2971558Srgrimes } 29896483Sphk if (dirp->d_type != DT_DIR) { 2991558Srgrimes direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 3001558Srgrimes dirp->d_type = DT_DIR; 3011558Srgrimes if (reply("FIX") == 1) 3021558Srgrimes ret |= ALTERED; 3031558Srgrimes } 3041558Srgrimes goto chk1; 3051558Srgrimes } 3061558Srgrimes direrror(idesc->id_number, "MISSING '.'"); 3071558Srgrimes proto.d_ino = idesc->id_number; 30896483Sphk proto.d_type = DT_DIR; 3091558Srgrimes proto.d_namlen = 1; 3101558Srgrimes (void)strcpy(proto.d_name, "."); 3111558Srgrimes entrysize = DIRSIZ(0, &proto); 3121558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 3131558Srgrimes pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 3141558Srgrimes dirp->d_name); 3151558Srgrimes } else if (dirp->d_reclen < entrysize) { 3161558Srgrimes pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 3171558Srgrimes } else if (dirp->d_reclen < 2 * entrysize) { 3181558Srgrimes proto.d_reclen = dirp->d_reclen; 31923675Speter memmove(dirp, &proto, (size_t)entrysize); 3201558Srgrimes if (reply("FIX") == 1) 3211558Srgrimes ret |= ALTERED; 3221558Srgrimes } else { 3231558Srgrimes n = dirp->d_reclen - entrysize; 3241558Srgrimes proto.d_reclen = entrysize; 32523675Speter memmove(dirp, &proto, (size_t)entrysize); 3261558Srgrimes idesc->id_entryno++; 32741474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 3281558Srgrimes dirp = (struct direct *)((char *)(dirp) + entrysize); 32923675Speter memset(dirp, 0, (size_t)n); 3301558Srgrimes dirp->d_reclen = n; 3311558Srgrimes if (reply("FIX") == 1) 3321558Srgrimes ret |= ALTERED; 3331558Srgrimes } 3341558Srgrimeschk1: 3351558Srgrimes if (idesc->id_entryno > 1) 3361558Srgrimes goto chk2; 3371558Srgrimes inp = getinoinfo(idesc->id_number); 3381558Srgrimes proto.d_ino = inp->i_parent; 33996483Sphk proto.d_type = DT_DIR; 3401558Srgrimes proto.d_namlen = 2; 3411558Srgrimes (void)strcpy(proto.d_name, ".."); 3421558Srgrimes entrysize = DIRSIZ(0, &proto); 3431558Srgrimes if (idesc->id_entryno == 0) { 3441558Srgrimes n = DIRSIZ(0, dirp); 3451558Srgrimes if (dirp->d_reclen < n + entrysize) 3461558Srgrimes goto chk2; 3471558Srgrimes proto.d_reclen = dirp->d_reclen - n; 3481558Srgrimes dirp->d_reclen = n; 3491558Srgrimes idesc->id_entryno++; 35041474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 3511558Srgrimes dirp = (struct direct *)((char *)(dirp) + n); 35223675Speter memset(dirp, 0, (size_t)proto.d_reclen); 3531558Srgrimes dirp->d_reclen = proto.d_reclen; 3541558Srgrimes } 3551558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 3561558Srgrimes inp->i_dotdot = dirp->d_ino; 35796483Sphk if (dirp->d_type != DT_DIR) { 3581558Srgrimes direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 3591558Srgrimes dirp->d_type = DT_DIR; 3601558Srgrimes if (reply("FIX") == 1) 3611558Srgrimes ret |= ALTERED; 3621558Srgrimes } 3631558Srgrimes goto chk2; 3641558Srgrimes } 3651558Srgrimes if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 3661558Srgrimes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 3671558Srgrimes pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 3681558Srgrimes dirp->d_name); 3691558Srgrimes inp->i_dotdot = (ino_t)-1; 3701558Srgrimes } else if (dirp->d_reclen < entrysize) { 3711558Srgrimes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 3721558Srgrimes pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 3731558Srgrimes inp->i_dotdot = (ino_t)-1; 3741558Srgrimes } else if (inp->i_parent != 0) { 3751558Srgrimes /* 3761558Srgrimes * We know the parent, so fix now. 3771558Srgrimes */ 3781558Srgrimes inp->i_dotdot = inp->i_parent; 3791558Srgrimes fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 3801558Srgrimes proto.d_reclen = dirp->d_reclen; 38123675Speter memmove(dirp, &proto, (size_t)entrysize); 3821558Srgrimes if (reply("FIX") == 1) 3831558Srgrimes ret |= ALTERED; 3841558Srgrimes } 3851558Srgrimes idesc->id_entryno++; 3861558Srgrimes if (dirp->d_ino != 0) 38741474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 3881558Srgrimes return (ret|KEEPON); 3891558Srgrimeschk2: 3901558Srgrimes if (dirp->d_ino == 0) 3911558Srgrimes return (ret|KEEPON); 3921558Srgrimes if (dirp->d_namlen <= 2 && 3931558Srgrimes dirp->d_name[0] == '.' && 3941558Srgrimes idesc->id_entryno >= 2) { 3951558Srgrimes if (dirp->d_namlen == 1) { 3961558Srgrimes direrror(idesc->id_number, "EXTRA '.' ENTRY"); 3971558Srgrimes dirp->d_ino = 0; 3981558Srgrimes if (reply("FIX") == 1) 3991558Srgrimes ret |= ALTERED; 4001558Srgrimes return (KEEPON | ret); 4011558Srgrimes } 4021558Srgrimes if (dirp->d_name[1] == '.') { 4031558Srgrimes direrror(idesc->id_number, "EXTRA '..' ENTRY"); 4041558Srgrimes dirp->d_ino = 0; 4051558Srgrimes if (reply("FIX") == 1) 4061558Srgrimes ret |= ALTERED; 4071558Srgrimes return (KEEPON | ret); 4081558Srgrimes } 4091558Srgrimes } 4101558Srgrimes idesc->id_entryno++; 4111558Srgrimes n = 0; 4121558Srgrimes if (dirp->d_ino > maxino) { 4131558Srgrimes fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 4141558Srgrimes n = reply("REMOVE"); 41596483Sphk } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 41623675Speter (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 41723675Speter fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 41823675Speter dirp->d_ino = WINO; 41923675Speter dirp->d_type = DT_WHT; 42023675Speter if (reply("FIX") == 1) 42123675Speter ret |= ALTERED; 4221558Srgrimes } else { 4231558Srgrimesagain: 42441474Sjulian switch (inoinfo(dirp->d_ino)->ino_state) { 4251558Srgrimes case USTATE: 4261558Srgrimes if (idesc->id_entryno <= 2) 4271558Srgrimes break; 4281558Srgrimes fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 4291558Srgrimes n = reply("REMOVE"); 4301558Srgrimes break; 4311558Srgrimes 4321558Srgrimes case DCLEAR: 4331558Srgrimes case FCLEAR: 4341558Srgrimes if (idesc->id_entryno <= 2) 4351558Srgrimes break; 43641474Sjulian if (inoinfo(dirp->d_ino)->ino_state == FCLEAR) 4371558Srgrimes errmsg = "DUP/BAD"; 43834266Sjulian else if (!preen && !usedsoftdep) 4391558Srgrimes errmsg = "ZERO LENGTH DIRECTORY"; 440208330Smckusick else if (cursnapshot == 0) { 4411558Srgrimes n = 1; 4421558Srgrimes break; 443208330Smckusick } else { 444208330Smckusick getpathname(dirname, idesc->id_number, 445208330Smckusick dirp->d_ino); 446208330Smckusick pwarn("ZERO LENGTH DIRECTORY %s I=%d", 447208330Smckusick dirname, dirp->d_ino); 448208330Smckusick /* 449208330Smckusick * We need to: 450208330Smckusick * setcwd(idesc->id_parent); 451208330Smckusick * rmdir(dirp->d_name); 452208330Smckusick */ 453208330Smckusick cmd.value = idesc->id_number; 454208330Smckusick if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 455208330Smckusick &cmd, sizeof cmd) == -1) { 456208330Smckusick /* kernel lacks support */ 457208330Smckusick printf(" (IGNORED)\n"); 458208330Smckusick n = 1; 459208330Smckusick break; 460208330Smckusick } 461208330Smckusick if (rmdir(dirp->d_name) == -1) { 462208330Smckusick printf(" (REMOVAL FAILED: %s)\n", 463208330Smckusick strerror(errno)); 464208330Smckusick n = 1; 465208330Smckusick break; 466208330Smckusick } 467208330Smckusick /* ".." reference to parent is removed */ 468208330Smckusick inoinfo(idesc->id_number)->ino_linkcnt--; 469208330Smckusick printf(" (REMOVED)\n"); 470208330Smckusick break; 4711558Srgrimes } 4721558Srgrimes fileerror(idesc->id_number, dirp->d_ino, errmsg); 4731558Srgrimes if ((n = reply("REMOVE")) == 1) 4741558Srgrimes break; 4751558Srgrimes dp = ginode(dirp->d_ino); 47641474Sjulian inoinfo(dirp->d_ino)->ino_state = 47798542Smckusick (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 47898542Smckusick inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 4791558Srgrimes goto again; 4801558Srgrimes 4811558Srgrimes case DSTATE: 482136281Struckman case DZLINK: 48341474Sjulian if (inoinfo(idesc->id_number)->ino_state == DFOUND) 48441474Sjulian inoinfo(dirp->d_ino)->ino_state = DFOUND; 485102411Scharnier /* FALLTHROUGH */ 4861558Srgrimes 4871558Srgrimes case DFOUND: 4881558Srgrimes inp = getinoinfo(dirp->d_ino); 489202107Smckusick if (idesc->id_entryno > 2) { 490202107Smckusick if (inp->i_parent == 0) 491202107Smckusick inp->i_parent = idesc->id_number; 492202107Smckusick else if ((n = fix_extraneous(inp, idesc)) == 1) 49374556Smckusick break; 4941558Srgrimes } 495102411Scharnier /* FALLTHROUGH */ 4961558Srgrimes 4971558Srgrimes case FSTATE: 498136281Struckman case FZLINK: 49996483Sphk if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 5001558Srgrimes fileerror(idesc->id_number, dirp->d_ino, 5011558Srgrimes "BAD TYPE VALUE"); 50241474Sjulian dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 5031558Srgrimes if (reply("FIX") == 1) 5041558Srgrimes ret |= ALTERED; 5051558Srgrimes } 50641474Sjulian inoinfo(dirp->d_ino)->ino_linkcnt--; 5071558Srgrimes break; 5081558Srgrimes 5091558Srgrimes default: 51023675Speter errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 51141474Sjulian inoinfo(dirp->d_ino)->ino_state, 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]; 527202107Smckusick 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 } 616202109Smckusick if ((cp = rindex(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