1*b9a41fd3Sswilcox /* 2*b9a41fd3Sswilcox * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3*b9a41fd3Sswilcox * Use is subject to license terms. 4*b9a41fd3Sswilcox */ 5*b9a41fd3Sswilcox 6*b9a41fd3Sswilcox /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7*b9a41fd3Sswilcox /* All Rights Reserved */ 8*b9a41fd3Sswilcox 9*b9a41fd3Sswilcox /* 10*b9a41fd3Sswilcox * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 11*b9a41fd3Sswilcox * All rights reserved. 12*b9a41fd3Sswilcox * 13*b9a41fd3Sswilcox * Redistribution and use in source and binary forms are permitted 14*b9a41fd3Sswilcox * provided that: (1) source distributions retain this entire copyright 15*b9a41fd3Sswilcox * notice and comment, and (2) distributions including binaries display 16*b9a41fd3Sswilcox * the following acknowledgement: ``This product includes software 17*b9a41fd3Sswilcox * developed by the University of California, Berkeley and its contributors'' 18*b9a41fd3Sswilcox * in the documentation or other materials provided with the distribution 19*b9a41fd3Sswilcox * and in all advertising materials mentioning features or use of this 20*b9a41fd3Sswilcox * software. Neither the name of the University nor the names of its 21*b9a41fd3Sswilcox * contributors may be used to endorse or promote products derived 22*b9a41fd3Sswilcox * from this software without specific prior written permission. 23*b9a41fd3Sswilcox * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 24*b9a41fd3Sswilcox * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 25*b9a41fd3Sswilcox * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 26*b9a41fd3Sswilcox */ 27*b9a41fd3Sswilcox 28*b9a41fd3Sswilcox #pragma ident "%Z%%M% %I% %E% SMI" 29*b9a41fd3Sswilcox 30*b9a41fd3Sswilcox #include <stdio.h> 31*b9a41fd3Sswilcox #include <stdlib.h> 32*b9a41fd3Sswilcox #include <sys/param.h> 33*b9a41fd3Sswilcox #include <sys/types.h> 34*b9a41fd3Sswilcox #include <sys/sysmacros.h> 35*b9a41fd3Sswilcox #include <sys/mntent.h> 36*b9a41fd3Sswilcox #include <sys/fs/ufs_fs.h> 37*b9a41fd3Sswilcox #include <sys/vnode.h> 38*b9a41fd3Sswilcox #include <sys/fs/ufs_inode.h> 39*b9a41fd3Sswilcox #define _KERNEL 40*b9a41fd3Sswilcox #include <sys/fs/ufs_fsdir.h> 41*b9a41fd3Sswilcox #undef _KERNEL 42*b9a41fd3Sswilcox #include <string.h> 43*b9a41fd3Sswilcox #include "fsck.h" 44*b9a41fd3Sswilcox 45*b9a41fd3Sswilcox #define MINDIRSIZE (sizeof (struct dirtemplate)) 46*b9a41fd3Sswilcox 47*b9a41fd3Sswilcox static int blksort(const void *, const void *); 48*b9a41fd3Sswilcox static int pass2check(struct inodesc *); 49*b9a41fd3Sswilcox 50*b9a41fd3Sswilcox void 51*b9a41fd3Sswilcox pass2(void) 52*b9a41fd3Sswilcox { 53*b9a41fd3Sswilcox struct dinode *dp, *dp2, *dpattr; 54*b9a41fd3Sswilcox struct inoinfo **inpp, *inp; 55*b9a41fd3Sswilcox struct inoinfo **inpend; 56*b9a41fd3Sswilcox struct inodesc curino; 57*b9a41fd3Sswilcox struct inodesc ldesc; 58*b9a41fd3Sswilcox struct dinode dino; 59*b9a41fd3Sswilcox char pathbuf[MAXPATHLEN + 1]; 60*b9a41fd3Sswilcox int found; 61*b9a41fd3Sswilcox int dirtype; 62*b9a41fd3Sswilcox caddr_t errmsg; 63*b9a41fd3Sswilcox struct shadowclientinfo *sci; 64*b9a41fd3Sswilcox 65*b9a41fd3Sswilcox switch (statemap[UFSROOTINO] & ~INDELAYD) { 66*b9a41fd3Sswilcox case USTATE: 67*b9a41fd3Sswilcox pfatal("ROOT INODE UNALLOCATED"); 68*b9a41fd3Sswilcox if (reply("ALLOCATE") == 0) { 69*b9a41fd3Sswilcox errexit("Program terminated."); 70*b9a41fd3Sswilcox } 71*b9a41fd3Sswilcox if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) != UFSROOTINO) 72*b9a41fd3Sswilcox errexit("CANNOT ALLOCATE ROOT INODE\n"); 73*b9a41fd3Sswilcox break; 74*b9a41fd3Sswilcox 75*b9a41fd3Sswilcox case DCLEAR: 76*b9a41fd3Sswilcox pfatal("DUPS/BAD IN ROOT INODE"); 77*b9a41fd3Sswilcox if (reply("REALLOCATE") == 1) { 78*b9a41fd3Sswilcox freeino(UFSROOTINO, TI_NOPARENT); 79*b9a41fd3Sswilcox if (allocdir(UFSROOTINO, UFSROOTINO, 80*b9a41fd3Sswilcox 0755, 0) != UFSROOTINO) 81*b9a41fd3Sswilcox errexit("CANNOT ALLOCATE ROOT INODE\n"); 82*b9a41fd3Sswilcox break; 83*b9a41fd3Sswilcox } 84*b9a41fd3Sswilcox if (reply("CONTINUE") == 0) { 85*b9a41fd3Sswilcox errexit("Program terminated."); 86*b9a41fd3Sswilcox } 87*b9a41fd3Sswilcox break; 88*b9a41fd3Sswilcox 89*b9a41fd3Sswilcox case FSTATE: 90*b9a41fd3Sswilcox case FCLEAR: 91*b9a41fd3Sswilcox case FZLINK: 92*b9a41fd3Sswilcox case SSTATE: 93*b9a41fd3Sswilcox case SCLEAR: 94*b9a41fd3Sswilcox pfatal("ROOT INODE NOT DIRECTORY"); 95*b9a41fd3Sswilcox if (reply("REALLOCATE") == 1) { 96*b9a41fd3Sswilcox freeino(UFSROOTINO, TI_NOPARENT); 97*b9a41fd3Sswilcox if (allocdir(UFSROOTINO, UFSROOTINO, 0755, 0) != 98*b9a41fd3Sswilcox UFSROOTINO) 99*b9a41fd3Sswilcox errexit("CANNOT ALLOCATE ROOT INODE\n"); 100*b9a41fd3Sswilcox break; 101*b9a41fd3Sswilcox } 102*b9a41fd3Sswilcox if (reply("FIX") == 0) { 103*b9a41fd3Sswilcox ckfini(); 104*b9a41fd3Sswilcox errexit("Program terminated."); 105*b9a41fd3Sswilcox } 106*b9a41fd3Sswilcox dp = ginode(UFSROOTINO); 107*b9a41fd3Sswilcox dp->di_mode &= ~IFMT; 108*b9a41fd3Sswilcox dp->di_mode |= IFDIR; 109*b9a41fd3Sswilcox inodirty(); 110*b9a41fd3Sswilcox break; 111*b9a41fd3Sswilcox 112*b9a41fd3Sswilcox case DSTATE: 113*b9a41fd3Sswilcox case DZLINK: 114*b9a41fd3Sswilcox break; 115*b9a41fd3Sswilcox 116*b9a41fd3Sswilcox default: 117*b9a41fd3Sswilcox errexit("BAD STATE 0x%x FOR ROOT INODE\n", 118*b9a41fd3Sswilcox statemap[UFSROOTINO]); 119*b9a41fd3Sswilcox } 120*b9a41fd3Sswilcox statemap[UFSROOTINO] = DFOUND; 121*b9a41fd3Sswilcox 122*b9a41fd3Sswilcox /* 123*b9a41fd3Sswilcox * Technically, we do know who the parent is. However, 124*b9a41fd3Sswilcox * if this is set, then we'll get confused during the 125*b9a41fd3Sswilcox * second-dir-entry-is-dotdot test for the root inode. 126*b9a41fd3Sswilcox */ 127*b9a41fd3Sswilcox inp = getinoinfo(UFSROOTINO); 128*b9a41fd3Sswilcox if (inp != NULL && inp->i_dotdot != 0) 129*b9a41fd3Sswilcox inp->i_dotdot = 0; 130*b9a41fd3Sswilcox 131*b9a41fd3Sswilcox /* 132*b9a41fd3Sswilcox * Sort the directory list into disk block order. There's no 133*b9a41fd3Sswilcox * requirement to do this, but it may help improve our i/o times 134*b9a41fd3Sswilcox * somewhat. 135*b9a41fd3Sswilcox */ 136*b9a41fd3Sswilcox qsort((void *)inpsort, (size_t)inplast, sizeof (*inpsort), blksort); 137*b9a41fd3Sswilcox /* 138*b9a41fd3Sswilcox * Check the integrity of each directory. In general, we treat 139*b9a41fd3Sswilcox * attribute directories just like normal ones. Only the handling 140*b9a41fd3Sswilcox * of .. is really different. 141*b9a41fd3Sswilcox */ 142*b9a41fd3Sswilcox (void) memset(&dino, 0, sizeof (struct dinode)); 143*b9a41fd3Sswilcox dino.di_mode = IFDIR; 144*b9a41fd3Sswilcox inpend = &inpsort[inplast]; 145*b9a41fd3Sswilcox for (inpp = inpsort; inpp < inpend; inpp++) { 146*b9a41fd3Sswilcox inp = *inpp; 147*b9a41fd3Sswilcox 148*b9a41fd3Sswilcox if (inp->i_isize == 0) 149*b9a41fd3Sswilcox continue; 150*b9a41fd3Sswilcox 151*b9a41fd3Sswilcox /* != DSTATE also covers case of == USTATE */ 152*b9a41fd3Sswilcox if (((statemap[inp->i_number] & STMASK) != DSTATE) || 153*b9a41fd3Sswilcox ((statemap[inp->i_number] & INCLEAR) == INCLEAR)) 154*b9a41fd3Sswilcox continue; 155*b9a41fd3Sswilcox 156*b9a41fd3Sswilcox if (inp->i_isize < (offset_t)MINDIRSIZE) { 157*b9a41fd3Sswilcox direrror(inp->i_number, "DIRECTORY TOO SHORT"); 158*b9a41fd3Sswilcox inp->i_isize = (offset_t)roundup(MINDIRSIZE, DIRBLKSIZ); 159*b9a41fd3Sswilcox if (reply("FIX") == 1) { 160*b9a41fd3Sswilcox dp = ginode(inp->i_number); 161*b9a41fd3Sswilcox dp->di_size = (u_offset_t)inp->i_isize; 162*b9a41fd3Sswilcox inodirty(); 163*b9a41fd3Sswilcox } else { 164*b9a41fd3Sswilcox iscorrupt = 1; 165*b9a41fd3Sswilcox } 166*b9a41fd3Sswilcox } 167*b9a41fd3Sswilcox if ((inp->i_isize & (offset_t)(DIRBLKSIZ - 1)) != 0) { 168*b9a41fd3Sswilcox getpathname(pathbuf, inp->i_number, inp->i_number); 169*b9a41fd3Sswilcox pwarn("DIRECTORY %s: LENGTH %lld NOT MULTIPLE OF %d", 170*b9a41fd3Sswilcox pathbuf, (longlong_t)inp->i_isize, DIRBLKSIZ); 171*b9a41fd3Sswilcox inp->i_isize = roundup(inp->i_isize, 172*b9a41fd3Sswilcox (offset_t)DIRBLKSIZ); 173*b9a41fd3Sswilcox if (preen || reply("ADJUST") == 1) { 174*b9a41fd3Sswilcox dp = ginode(inp->i_number); 175*b9a41fd3Sswilcox dp->di_size = 176*b9a41fd3Sswilcox (u_offset_t)roundup(inp->i_isize, 177*b9a41fd3Sswilcox (offset_t)DIRBLKSIZ); 178*b9a41fd3Sswilcox inodirty(); 179*b9a41fd3Sswilcox if (preen) 180*b9a41fd3Sswilcox (void) printf(" (ADJUSTED)\n"); 181*b9a41fd3Sswilcox } else { 182*b9a41fd3Sswilcox iscorrupt = 1; 183*b9a41fd3Sswilcox } 184*b9a41fd3Sswilcox } 185*b9a41fd3Sswilcox dp = ginode(inp->i_number); 186*b9a41fd3Sswilcox if ((dp->di_mode & IFMT) == IFATTRDIR && 187*b9a41fd3Sswilcox (dp->di_cflags & IXATTR) == 0) { 188*b9a41fd3Sswilcox pwarn("ATTRIBUTE DIRECTORY I=%d MISSING IXATTR FLAG", 189*b9a41fd3Sswilcox inp->i_number); 190*b9a41fd3Sswilcox if (preen || reply("CORRECT") == 1) { 191*b9a41fd3Sswilcox dp->di_cflags |= IXATTR; 192*b9a41fd3Sswilcox inodirty(); 193*b9a41fd3Sswilcox if (preen) 194*b9a41fd3Sswilcox (void) printf(" (CORRECTED)\n"); 195*b9a41fd3Sswilcox } 196*b9a41fd3Sswilcox } 197*b9a41fd3Sswilcox dp = &dino; 198*b9a41fd3Sswilcox dp->di_size = (u_offset_t)inp->i_isize; 199*b9a41fd3Sswilcox (void) memmove((void *)&dp->di_db[0], (void *)&inp->i_blks[0], 200*b9a41fd3Sswilcox inp->i_blkssize); 201*b9a41fd3Sswilcox init_inodesc(&curino); 202*b9a41fd3Sswilcox curino.id_type = DATA; 203*b9a41fd3Sswilcox curino.id_func = pass2check; 204*b9a41fd3Sswilcox curino.id_number = inp->i_number; 205*b9a41fd3Sswilcox curino.id_parent = inp->i_parent; 206*b9a41fd3Sswilcox curino.id_fix = DONTKNOW; 207*b9a41fd3Sswilcox (void) ckinode(dp, &curino, CKI_TRAVERSE); 208*b9a41fd3Sswilcox 209*b9a41fd3Sswilcox /* 210*b9a41fd3Sswilcox * Make sure we mark attrdirs as DFOUND, since they won't 211*b9a41fd3Sswilcox * be located during normal scan of standard directories. 212*b9a41fd3Sswilcox */ 213*b9a41fd3Sswilcox if (curino.id_parent == 0) { 214*b9a41fd3Sswilcox dpattr = ginode(inp->i_number); 215*b9a41fd3Sswilcox if ((dpattr->di_mode & IFMT) == IFATTRDIR) { 216*b9a41fd3Sswilcox for (sci = attrclientinfo; sci != NULL; 217*b9a41fd3Sswilcox sci = sci->next) { 218*b9a41fd3Sswilcox if (sci->shadow == inp->i_number) { 219*b9a41fd3Sswilcox curino.id_parent = 220*b9a41fd3Sswilcox sci->clients->client[0]; 221*b9a41fd3Sswilcox statemap[inp->i_number] = 222*b9a41fd3Sswilcox DFOUND; 223*b9a41fd3Sswilcox inp->i_parent = 224*b9a41fd3Sswilcox curino.id_parent; 225*b9a41fd3Sswilcox } 226*b9a41fd3Sswilcox } 227*b9a41fd3Sswilcox } 228*b9a41fd3Sswilcox } 229*b9a41fd3Sswilcox } 230*b9a41fd3Sswilcox /* 231*b9a41fd3Sswilcox * Now that the parents of all directories have been found, 232*b9a41fd3Sswilcox * make another pass to verify the value of .. 233*b9a41fd3Sswilcox */ 234*b9a41fd3Sswilcox for (inpp = inpsort; inpp < inpend; inpp++) { 235*b9a41fd3Sswilcox inp = *inpp; 236*b9a41fd3Sswilcox if (inp->i_parent == 0 || inp->i_isize == 0) 237*b9a41fd3Sswilcox continue; 238*b9a41fd3Sswilcox /* 239*b9a41fd3Sswilcox * There are only directories in inpsort[], so only 240*b9a41fd3Sswilcox * directory-related states need to be checked. There 241*b9a41fd3Sswilcox * should never be any flags associated with USTATE. 242*b9a41fd3Sswilcox */ 243*b9a41fd3Sswilcox if ((statemap[inp->i_number] & STMASK) == DCLEAR || 244*b9a41fd3Sswilcox statemap[inp->i_number] == USTATE) { 245*b9a41fd3Sswilcox continue; 246*b9a41fd3Sswilcox } 247*b9a41fd3Sswilcox if (statemap[inp->i_parent] == DFOUND && 248*b9a41fd3Sswilcox S_IS_DUNFOUND(statemap[inp->i_number])) { 249*b9a41fd3Sswilcox statemap[inp->i_number] = DFOUND | 250*b9a41fd3Sswilcox (statemap[inp->i_number] & INCLEAR); 251*b9a41fd3Sswilcox } 252*b9a41fd3Sswilcox if (inp->i_dotdot == inp->i_parent || 253*b9a41fd3Sswilcox inp->i_dotdot == (fsck_ino_t)-1) { 254*b9a41fd3Sswilcox continue; 255*b9a41fd3Sswilcox } 256*b9a41fd3Sswilcox if (inp->i_dotdot == 0) { 257*b9a41fd3Sswilcox inp->i_dotdot = inp->i_parent; 258*b9a41fd3Sswilcox fileerror(inp->i_parent, inp->i_number, 259*b9a41fd3Sswilcox "MISSING '..'"); 260*b9a41fd3Sswilcox if (reply("FIX") == 0) { 261*b9a41fd3Sswilcox iscorrupt = 1; 262*b9a41fd3Sswilcox continue; 263*b9a41fd3Sswilcox } 264*b9a41fd3Sswilcox dp = ginode(inp->i_number); 265*b9a41fd3Sswilcox found = 0; 266*b9a41fd3Sswilcox dirtype = (dp->di_mode & IFMT); 267*b9a41fd3Sswilcox 268*b9a41fd3Sswilcox /* 269*b9a41fd3Sswilcox * See if this is an attrdir that we located in pass1. 270*b9a41fd3Sswilcox * i.e. it was on an i_oeftflag of some other inode. 271*b9a41fd3Sswilcox * if it isn't found then we have an orphaned attrdir 272*b9a41fd3Sswilcox * that needs to be tossed into lost+found. 273*b9a41fd3Sswilcox */ 274*b9a41fd3Sswilcox if (dirtype == IFATTRDIR) { 275*b9a41fd3Sswilcox for (sci = attrclientinfo; 276*b9a41fd3Sswilcox sci != NULL; 277*b9a41fd3Sswilcox sci = sci->next) { 278*b9a41fd3Sswilcox if (sci->shadow == inp->i_number) { 279*b9a41fd3Sswilcox inp->i_parent = 280*b9a41fd3Sswilcox sci->clients->client[0]; 281*b9a41fd3Sswilcox found = 1; 282*b9a41fd3Sswilcox } 283*b9a41fd3Sswilcox } 284*b9a41fd3Sswilcox } 285*b9a41fd3Sswilcox 286*b9a41fd3Sswilcox /* 287*b9a41fd3Sswilcox * We've already proven there's no "..", so this 288*b9a41fd3Sswilcox * can't create a duplicate. 289*b9a41fd3Sswilcox */ 290*b9a41fd3Sswilcox if (makeentry(inp->i_number, inp->i_parent, "..")) { 291*b9a41fd3Sswilcox 292*b9a41fd3Sswilcox /* 293*b9a41fd3Sswilcox * is it an orphaned attrdir? 294*b9a41fd3Sswilcox */ 295*b9a41fd3Sswilcox if (dirtype == IFATTRDIR && found == 0) { 296*b9a41fd3Sswilcox /* 297*b9a41fd3Sswilcox * Throw it into lost+found 298*b9a41fd3Sswilcox */ 299*b9a41fd3Sswilcox if (linkup(inp->i_number, lfdir, 300*b9a41fd3Sswilcox NULL) == 0) { 301*b9a41fd3Sswilcox pwarn( 302*b9a41fd3Sswilcox "Unable to move attrdir I=%d to lost+found\n", 303*b9a41fd3Sswilcox inp->i_number); 304*b9a41fd3Sswilcox iscorrupt = 1; 305*b9a41fd3Sswilcox } 306*b9a41fd3Sswilcox maybe_convert_attrdir_to_dir( 307*b9a41fd3Sswilcox inp->i_number); 308*b9a41fd3Sswilcox } 309*b9a41fd3Sswilcox if (dirtype == IFDIR) { 310*b9a41fd3Sswilcox LINK_RANGE(errmsg, 311*b9a41fd3Sswilcox lncntp[inp->i_parent], -1); 312*b9a41fd3Sswilcox if (errmsg != NULL) { 313*b9a41fd3Sswilcox LINK_CLEAR(errmsg, 314*b9a41fd3Sswilcox inp->i_parent, IFDIR, 315*b9a41fd3Sswilcox &ldesc); 316*b9a41fd3Sswilcox if (statemap[inp->i_parent] != 317*b9a41fd3Sswilcox USTATE) { 318*b9a41fd3Sswilcox /* 319*b9a41fd3Sswilcox * iscorrupt is 320*b9a41fd3Sswilcox * already set 321*b9a41fd3Sswilcox */ 322*b9a41fd3Sswilcox continue; 323*b9a41fd3Sswilcox } 324*b9a41fd3Sswilcox } 325*b9a41fd3Sswilcox TRACK_LNCNTP(inp->i_parent, 326*b9a41fd3Sswilcox lncntp[inp->i_parent]--); 327*b9a41fd3Sswilcox } 328*b9a41fd3Sswilcox 329*b9a41fd3Sswilcox continue; 330*b9a41fd3Sswilcox } 331*b9a41fd3Sswilcox pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 332*b9a41fd3Sswilcox iscorrupt = 1; 333*b9a41fd3Sswilcox inp->i_dotdot = (fsck_ino_t)-1; 334*b9a41fd3Sswilcox continue; 335*b9a41fd3Sswilcox } 336*b9a41fd3Sswilcox 337*b9a41fd3Sswilcox dp2 = ginode(inp->i_parent); 338*b9a41fd3Sswilcox 339*b9a41fd3Sswilcox if ((dp2->di_mode & IFMT) == IFATTRDIR) { 340*b9a41fd3Sswilcox continue; 341*b9a41fd3Sswilcox } 342*b9a41fd3Sswilcox fileerror(inp->i_parent, inp->i_number, 343*b9a41fd3Sswilcox "BAD INODE NUMBER FOR '..'"); 344*b9a41fd3Sswilcox if (reply("FIX") == 0) { 345*b9a41fd3Sswilcox iscorrupt = 1; 346*b9a41fd3Sswilcox continue; 347*b9a41fd3Sswilcox } 348*b9a41fd3Sswilcox 349*b9a41fd3Sswilcox LINK_RANGE(errmsg, lncntp[inp->i_dotdot], 1); 350*b9a41fd3Sswilcox if (errmsg != NULL) { 351*b9a41fd3Sswilcox LINK_CLEAR(errmsg, inp->i_dotdot, IFDIR, &ldesc); 352*b9a41fd3Sswilcox if (statemap[inp->i_dotdot] != USTATE) { 353*b9a41fd3Sswilcox /* iscorrupt is already set */ 354*b9a41fd3Sswilcox continue; 355*b9a41fd3Sswilcox } 356*b9a41fd3Sswilcox } 357*b9a41fd3Sswilcox TRACK_LNCNTP(inp->i_dotdot, lncntp[inp->i_dotdot]++); 358*b9a41fd3Sswilcox 359*b9a41fd3Sswilcox LINK_RANGE(errmsg, lncntp[inp->i_parent], -1); 360*b9a41fd3Sswilcox if (errmsg != NULL) { 361*b9a41fd3Sswilcox LINK_CLEAR(errmsg, inp->i_parent, IFDIR, &ldesc); 362*b9a41fd3Sswilcox if (statemap[inp->i_parent] != USTATE) { 363*b9a41fd3Sswilcox /* iscorrupt is already set */ 364*b9a41fd3Sswilcox continue; 365*b9a41fd3Sswilcox } 366*b9a41fd3Sswilcox } 367*b9a41fd3Sswilcox TRACK_LNCNTP(inp->i_parent, lncntp[inp->i_parent]--); 368*b9a41fd3Sswilcox 369*b9a41fd3Sswilcox inp->i_dotdot = inp->i_parent; 370*b9a41fd3Sswilcox (void) changeino(inp->i_number, "..", inp->i_parent); 371*b9a41fd3Sswilcox } 372*b9a41fd3Sswilcox /* 373*b9a41fd3Sswilcox * Mark all the directories that can be found from the root. 374*b9a41fd3Sswilcox */ 375*b9a41fd3Sswilcox propagate(); 376*b9a41fd3Sswilcox } 377*b9a41fd3Sswilcox 378*b9a41fd3Sswilcox /* 379*b9a41fd3Sswilcox * Sanity-check a single directory entry. Which entry is being 380*b9a41fd3Sswilcox * examined is tracked via idesc->id_entryno. There are two 381*b9a41fd3Sswilcox * special ones, 0 (.) and 1 (..). Those have to exist in order 382*b9a41fd3Sswilcox * in the first two locations in the directory, and have the usual 383*b9a41fd3Sswilcox * properties. All other entries have to not be for either of 384*b9a41fd3Sswilcox * the special two, and the inode they reference has to be 385*b9a41fd3Sswilcox * reasonable. 386*b9a41fd3Sswilcox * 387*b9a41fd3Sswilcox * This is only called from dirscan(), which looks for the 388*b9a41fd3Sswilcox * ALTERED flag after each invocation. If it finds it, the 389*b9a41fd3Sswilcox * relevant buffer gets pushed out, so we don't have to worry 390*b9a41fd3Sswilcox * about it here. 391*b9a41fd3Sswilcox */ 392*b9a41fd3Sswilcox #define PASS2B_PROMPT "REMOVE DIRECTORY ENTRY FROM I=%d" 393*b9a41fd3Sswilcox 394*b9a41fd3Sswilcox static int 395*b9a41fd3Sswilcox pass2check(struct inodesc *idesc) 396*b9a41fd3Sswilcox { 397*b9a41fd3Sswilcox struct direct *dirp = idesc->id_dirp; 398*b9a41fd3Sswilcox struct inodesc ldesc; 399*b9a41fd3Sswilcox struct inoinfo *inp; 400*b9a41fd3Sswilcox short reclen, entrysize; 401*b9a41fd3Sswilcox int ret = 0; 402*b9a41fd3Sswilcox int act, update_lncntp; 403*b9a41fd3Sswilcox struct dinode *dp, *pdirp, *attrdirp; 404*b9a41fd3Sswilcox caddr_t errmsg; 405*b9a41fd3Sswilcox struct direct proto; 406*b9a41fd3Sswilcox char namebuf[MAXPATHLEN + 1]; 407*b9a41fd3Sswilcox char pathbuf[MAXPATHLEN + 1]; 408*b9a41fd3Sswilcox int isattr; 409*b9a41fd3Sswilcox int pdirtype; 410*b9a41fd3Sswilcox int breakout = 0; 411*b9a41fd3Sswilcox int dontreconnect; 412*b9a41fd3Sswilcox 413*b9a41fd3Sswilcox if (idesc->id_entryno != 0) 414*b9a41fd3Sswilcox goto chk1; 415*b9a41fd3Sswilcox /* 416*b9a41fd3Sswilcox * check for "." 417*b9a41fd3Sswilcox */ 418*b9a41fd3Sswilcox if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 419*b9a41fd3Sswilcox if (dirp->d_ino != idesc->id_number) { 420*b9a41fd3Sswilcox direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 421*b9a41fd3Sswilcox dirp->d_ino = idesc->id_number; 422*b9a41fd3Sswilcox if (reply("FIX") == 1) { 423*b9a41fd3Sswilcox ret |= ALTERED; 424*b9a41fd3Sswilcox } else { 425*b9a41fd3Sswilcox iscorrupt = 1; 426*b9a41fd3Sswilcox } 427*b9a41fd3Sswilcox } 428*b9a41fd3Sswilcox goto chk1; 429*b9a41fd3Sswilcox } 430*b9a41fd3Sswilcox /* 431*b9a41fd3Sswilcox * Build up a new one, and make sure there's room to put 432*b9a41fd3Sswilcox * it where it belongs. 433*b9a41fd3Sswilcox */ 434*b9a41fd3Sswilcox direrror(idesc->id_number, "MISSING '.'"); 435*b9a41fd3Sswilcox proto.d_ino = idesc->id_number; 436*b9a41fd3Sswilcox proto.d_namlen = 1; 437*b9a41fd3Sswilcox (void) strcpy(proto.d_name, "."); 438*b9a41fd3Sswilcox entrysize = DIRSIZ(&proto); 439*b9a41fd3Sswilcox if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 440*b9a41fd3Sswilcox pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 441*b9a41fd3Sswilcox dirp->d_name); 442*b9a41fd3Sswilcox iscorrupt = 1; 443*b9a41fd3Sswilcox } else if ((int)dirp->d_reclen < entrysize) { 444*b9a41fd3Sswilcox pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 445*b9a41fd3Sswilcox iscorrupt = 1; 446*b9a41fd3Sswilcox } else if ((int)dirp->d_reclen < 2 * entrysize) { 447*b9a41fd3Sswilcox /* 448*b9a41fd3Sswilcox * No room for another entry after us ("." is the 449*b9a41fd3Sswilcox * smallest entry you can have), so just put all 450*b9a41fd3Sswilcox * of the old entry's space into the new entry. 451*b9a41fd3Sswilcox * 452*b9a41fd3Sswilcox * Because we don't touch id_entryno, we end up going 453*b9a41fd3Sswilcox * through the chk2 tests as well. 454*b9a41fd3Sswilcox */ 455*b9a41fd3Sswilcox proto.d_reclen = dirp->d_reclen; 456*b9a41fd3Sswilcox (void) memmove((void *)dirp, (void *)&proto, 457*b9a41fd3Sswilcox (size_t)entrysize); 458*b9a41fd3Sswilcox if (reply("FIX") == 1) { 459*b9a41fd3Sswilcox ret |= ALTERED; 460*b9a41fd3Sswilcox } else { 461*b9a41fd3Sswilcox iscorrupt = 1; 462*b9a41fd3Sswilcox } 463*b9a41fd3Sswilcox } else { 464*b9a41fd3Sswilcox /* 465*b9a41fd3Sswilcox * There's enough room for an entire additional entry 466*b9a41fd3Sswilcox * after this, so create the "." entry and follow it 467*b9a41fd3Sswilcox * with an empty entry that covers the rest of the 468*b9a41fd3Sswilcox * space. 469*b9a41fd3Sswilcox * 470*b9a41fd3Sswilcox * The increment of id_entryno means we'll skip the 471*b9a41fd3Sswilcox * "." case of chk1, doing the ".." tests instead. 472*b9a41fd3Sswilcox * Since we know that there's not a ".." where it 473*b9a41fd3Sswilcox * should be (because we just created an empty entry 474*b9a41fd3Sswilcox * there), that's the best way of getting it recreated 475*b9a41fd3Sswilcox * as well. 476*b9a41fd3Sswilcox */ 477*b9a41fd3Sswilcox reclen = dirp->d_reclen - entrysize; 478*b9a41fd3Sswilcox proto.d_reclen = entrysize; 479*b9a41fd3Sswilcox (void) memmove((void *)dirp, (void *)&proto, 480*b9a41fd3Sswilcox (size_t)entrysize); 481*b9a41fd3Sswilcox idesc->id_entryno++; 482*b9a41fd3Sswilcox /* 483*b9a41fd3Sswilcox * Make sure the link count is in range before updating 484*b9a41fd3Sswilcox * it. This makes the assumption that the link count 485*b9a41fd3Sswilcox * for this inode included one for ".", even though 486*b9a41fd3Sswilcox * there wasn't a "." entry. Even if that's not true, 487*b9a41fd3Sswilcox * it's a reasonable working hypothesis, and the link 488*b9a41fd3Sswilcox * count verification done in pass4 will fix it for 489*b9a41fd3Sswilcox * us anyway. 490*b9a41fd3Sswilcox */ 491*b9a41fd3Sswilcox LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1); 492*b9a41fd3Sswilcox if (errmsg != NULL) { 493*b9a41fd3Sswilcox LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc); 494*b9a41fd3Sswilcox if (statemap[dirp->d_ino] == USTATE) { 495*b9a41fd3Sswilcox /* 496*b9a41fd3Sswilcox * The inode got zapped, so reset the 497*b9a41fd3Sswilcox * directory entry. Extend it to also 498*b9a41fd3Sswilcox * cover the space we were going to make 499*b9a41fd3Sswilcox * into a new entry. 500*b9a41fd3Sswilcox */ 501*b9a41fd3Sswilcox dirp->d_ino = 0; 502*b9a41fd3Sswilcox dirp->d_reclen += reclen; 503*b9a41fd3Sswilcox ret |= ALTERED; 504*b9a41fd3Sswilcox return (ret); 505*b9a41fd3Sswilcox } 506*b9a41fd3Sswilcox } 507*b9a41fd3Sswilcox 508*b9a41fd3Sswilcox /* 509*b9a41fd3Sswilcox * Create the new empty entry. 510*b9a41fd3Sswilcox */ 511*b9a41fd3Sswilcox /* LINTED pointer cast alignment (entrysize is valid) */ 512*b9a41fd3Sswilcox dirp = (struct direct *)((char *)(dirp) + entrysize); 513*b9a41fd3Sswilcox (void) memset((void *)dirp, 0, (size_t)reclen); 514*b9a41fd3Sswilcox dirp->d_reclen = reclen; 515*b9a41fd3Sswilcox 516*b9a41fd3Sswilcox /* 517*b9a41fd3Sswilcox * Did the user want us to create a new "."? This 518*b9a41fd3Sswilcox * query assumes that the direrror(MISSING) was the 519*b9a41fd3Sswilcox * last thing printed, so if the LINK_RANGE() check 520*b9a41fd3Sswilcox * fails, it can't pass through here. 521*b9a41fd3Sswilcox */ 522*b9a41fd3Sswilcox if (reply("FIX") == 1) { 523*b9a41fd3Sswilcox TRACK_LNCNTP(idesc->id_number, 524*b9a41fd3Sswilcox lncntp[idesc->id_number]--); 525*b9a41fd3Sswilcox ret |= ALTERED; 526*b9a41fd3Sswilcox } else { 527*b9a41fd3Sswilcox iscorrupt = 1; 528*b9a41fd3Sswilcox } 529*b9a41fd3Sswilcox } 530*b9a41fd3Sswilcox 531*b9a41fd3Sswilcox /* 532*b9a41fd3Sswilcox * XXX The next few lines are needed whether we're processing "." 533*b9a41fd3Sswilcox * or "..". However, there are some extra steps still needed 534*b9a41fd3Sswilcox * for the former, hence the big block of code for 535*b9a41fd3Sswilcox * id_entryno == 0. Alternatively, there could be a label just 536*b9a41fd3Sswilcox * before this comment, and everything through the end of that 537*b9a41fd3Sswilcox * block moved there. In some ways, that might make the 538*b9a41fd3Sswilcox * control flow more logical (factoring out to separate functions 539*b9a41fd3Sswilcox * would be even better). 540*b9a41fd3Sswilcox */ 541*b9a41fd3Sswilcox 542*b9a41fd3Sswilcox chk1: 543*b9a41fd3Sswilcox if (idesc->id_entryno > 1) 544*b9a41fd3Sswilcox goto chk2; 545*b9a41fd3Sswilcox inp = getinoinfo(idesc->id_number); 546*b9a41fd3Sswilcox if (inp == NULL) { 547*b9a41fd3Sswilcox /* 548*b9a41fd3Sswilcox * This is a can't-happen, since inodes get cached before 549*b9a41fd3Sswilcox * we get called on them. 550*b9a41fd3Sswilcox */ 551*b9a41fd3Sswilcox errexit("pass2check got NULL from getinoinfo at chk1 I=%d\n", 552*b9a41fd3Sswilcox idesc->id_number); 553*b9a41fd3Sswilcox } 554*b9a41fd3Sswilcox proto.d_ino = inp->i_parent; 555*b9a41fd3Sswilcox proto.d_namlen = 2; 556*b9a41fd3Sswilcox (void) strcpy(proto.d_name, ".."); 557*b9a41fd3Sswilcox entrysize = DIRSIZ(&proto); 558*b9a41fd3Sswilcox if (idesc->id_entryno == 0) { 559*b9a41fd3Sswilcox /* 560*b9a41fd3Sswilcox * We may not actually need to split things up, but if 561*b9a41fd3Sswilcox * there's room to do so, we should, as that implies 562*b9a41fd3Sswilcox * that the "." entry is larger than it is supposed 563*b9a41fd3Sswilcox * to be, and therefore there's something wrong, albeit 564*b9a41fd3Sswilcox * possibly harmlessly so. 565*b9a41fd3Sswilcox */ 566*b9a41fd3Sswilcox reclen = DIRSIZ(dirp); 567*b9a41fd3Sswilcox if ((int)dirp->d_reclen < reclen + entrysize) { 568*b9a41fd3Sswilcox /* 569*b9a41fd3Sswilcox * Not enough room for inserting a ".." after 570*b9a41fd3Sswilcox * the "." entry. 571*b9a41fd3Sswilcox */ 572*b9a41fd3Sswilcox goto chk2; 573*b9a41fd3Sswilcox } 574*b9a41fd3Sswilcox /* 575*b9a41fd3Sswilcox * There's enough room for an entire additional entry 576*b9a41fd3Sswilcox * after "."'s, so split it up. There's no reason "." 577*b9a41fd3Sswilcox * should be bigger than the minimum, so shrink it to 578*b9a41fd3Sswilcox * fit, too. Since by the time we're done with this 579*b9a41fd3Sswilcox * part, dirp will be pointing at where ".." should be, 580*b9a41fd3Sswilcox * update id_entryno to show that that's the entry 581*b9a41fd3Sswilcox * we're on. 582*b9a41fd3Sswilcox */ 583*b9a41fd3Sswilcox proto.d_reclen = dirp->d_reclen - reclen; 584*b9a41fd3Sswilcox dirp->d_reclen = reclen; 585*b9a41fd3Sswilcox idesc->id_entryno++; 586*b9a41fd3Sswilcox if (dirp->d_ino > 0 && dirp->d_ino <= maxino) { 587*b9a41fd3Sswilcox /* 588*b9a41fd3Sswilcox * Account for the link to ourselves. 589*b9a41fd3Sswilcox */ 590*b9a41fd3Sswilcox LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1); 591*b9a41fd3Sswilcox if (errmsg != NULL) { 592*b9a41fd3Sswilcox LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc); 593*b9a41fd3Sswilcox if (statemap[dirp->d_ino] == USTATE) { 594*b9a41fd3Sswilcox /* 595*b9a41fd3Sswilcox * We were going to split the entry 596*b9a41fd3Sswilcox * up, but the link count overflowed. 597*b9a41fd3Sswilcox * Since we got rid of the inode, 598*b9a41fd3Sswilcox * we need to also zap the directory 599*b9a41fd3Sswilcox * entry, and restoring the original 600*b9a41fd3Sswilcox * state of things is the least-bad 601*b9a41fd3Sswilcox * result. 602*b9a41fd3Sswilcox */ 603*b9a41fd3Sswilcox dirp->d_ino = 0; 604*b9a41fd3Sswilcox dirp->d_reclen += proto.d_reclen; 605*b9a41fd3Sswilcox ret |= ALTERED; 606*b9a41fd3Sswilcox return (ret); 607*b9a41fd3Sswilcox } 608*b9a41fd3Sswilcox } 609*b9a41fd3Sswilcox TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--); 610*b9a41fd3Sswilcox /* 611*b9a41fd3Sswilcox * Make sure the new entry doesn't get interpreted 612*b9a41fd3Sswilcox * as having actual content. 613*b9a41fd3Sswilcox */ 614*b9a41fd3Sswilcox /* LINTED pointer cast alignment (reclen is valid) */ 615*b9a41fd3Sswilcox dirp = (struct direct *)((char *)(dirp) + reclen); 616*b9a41fd3Sswilcox (void) memset((void *)dirp, 0, (size_t)proto.d_reclen); 617*b9a41fd3Sswilcox dirp->d_reclen = proto.d_reclen; 618*b9a41fd3Sswilcox } else { 619*b9a41fd3Sswilcox /* 620*b9a41fd3Sswilcox * Everything was fine, up until we realized that 621*b9a41fd3Sswilcox * the indicated inode was impossible. By clearing 622*b9a41fd3Sswilcox * d_ino here, we'll trigger the recreation of it 623*b9a41fd3Sswilcox * down below, using i_parent. Unlike the other 624*b9a41fd3Sswilcox * half of this if(), we're everything so it shows 625*b9a41fd3Sswilcox * that we're still on the "." entry. 626*b9a41fd3Sswilcox */ 627*b9a41fd3Sswilcox fileerror(idesc->id_number, dirp->d_ino, 628*b9a41fd3Sswilcox "I OUT OF RANGE"); 629*b9a41fd3Sswilcox dirp->d_ino = 0; 630*b9a41fd3Sswilcox if (reply("FIX") == 1) { 631*b9a41fd3Sswilcox ret |= ALTERED; 632*b9a41fd3Sswilcox } else { 633*b9a41fd3Sswilcox iscorrupt = 1; 634*b9a41fd3Sswilcox } 635*b9a41fd3Sswilcox } 636*b9a41fd3Sswilcox } 637*b9a41fd3Sswilcox /* 638*b9a41fd3Sswilcox * Record this ".." inode, but only if we haven't seen one before. 639*b9a41fd3Sswilcox * If this isn't the first, it'll get cleared below, and so we 640*b9a41fd3Sswilcox * want to remember the entry that'll still be around later. 641*b9a41fd3Sswilcox */ 642*b9a41fd3Sswilcox if (dirp->d_ino != 0 && inp->i_dotdot == 0 && 643*b9a41fd3Sswilcox strcmp(dirp->d_name, "..") == 0) { 644*b9a41fd3Sswilcox inp->i_dotdot = dirp->d_ino; 645*b9a41fd3Sswilcox goto chk2; 646*b9a41fd3Sswilcox } 647*b9a41fd3Sswilcox if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 648*b9a41fd3Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 649*b9a41fd3Sswilcox pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 650*b9a41fd3Sswilcox dirp->d_name); 651*b9a41fd3Sswilcox iscorrupt = 1; 652*b9a41fd3Sswilcox inp->i_dotdot = (fsck_ino_t)-1; 653*b9a41fd3Sswilcox } else if ((int)dirp->d_reclen < entrysize) { 654*b9a41fd3Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 655*b9a41fd3Sswilcox pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 656*b9a41fd3Sswilcox /* XXX Same consideration as immediately above. */ 657*b9a41fd3Sswilcox iscorrupt = 1; 658*b9a41fd3Sswilcox inp->i_dotdot = (fsck_ino_t)-1; 659*b9a41fd3Sswilcox } else if (inp->i_parent != 0) { 660*b9a41fd3Sswilcox /* 661*b9a41fd3Sswilcox * We know the parent, so fix now. 662*b9a41fd3Sswilcox */ 663*b9a41fd3Sswilcox proto.d_ino = inp->i_dotdot = inp->i_parent; 664*b9a41fd3Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 665*b9a41fd3Sswilcox /* 666*b9a41fd3Sswilcox * Lint won't be quiet about d_reclen being set but not 667*b9a41fd3Sswilcox * used. It apparently doesn't understand the implications 668*b9a41fd3Sswilcox * of calling memmove(), and won't believe us that it's ok. 669*b9a41fd3Sswilcox */ 670*b9a41fd3Sswilcox proto.d_reclen = dirp->d_reclen; 671*b9a41fd3Sswilcox (void) memmove((void *)dirp, (void *)&proto, 672*b9a41fd3Sswilcox (size_t)entrysize); 673*b9a41fd3Sswilcox if (reply("FIX") == 1) { 674*b9a41fd3Sswilcox ret |= ALTERED; 675*b9a41fd3Sswilcox } else { 676*b9a41fd3Sswilcox iscorrupt = 1; 677*b9a41fd3Sswilcox } 678*b9a41fd3Sswilcox } else if (inp->i_number == UFSROOTINO) { 679*b9a41fd3Sswilcox /* 680*b9a41fd3Sswilcox * Always know parent of root inode, so fix now. 681*b9a41fd3Sswilcox */ 682*b9a41fd3Sswilcox proto.d_ino = inp->i_dotdot = inp->i_parent = UFSROOTINO; 683*b9a41fd3Sswilcox fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 684*b9a41fd3Sswilcox /* 685*b9a41fd3Sswilcox * Lint won't be quiet about d_reclen being set but not 686*b9a41fd3Sswilcox * used. It apparently doesn't understand the implications 687*b9a41fd3Sswilcox * of calling memmove(), and won't believe us that it's ok. 688*b9a41fd3Sswilcox */ 689*b9a41fd3Sswilcox proto.d_reclen = dirp->d_reclen; 690*b9a41fd3Sswilcox (void) memmove((void *)dirp, (void *)&proto, (size_t)entrysize); 691*b9a41fd3Sswilcox if (reply("FIX") == 1) { 692*b9a41fd3Sswilcox ret |= ALTERED; 693*b9a41fd3Sswilcox } else { 694*b9a41fd3Sswilcox iscorrupt = 1; 695*b9a41fd3Sswilcox } 696*b9a41fd3Sswilcox } 697*b9a41fd3Sswilcox idesc->id_entryno++; 698*b9a41fd3Sswilcox if (dirp->d_ino != 0) { 699*b9a41fd3Sswilcox LINK_RANGE(errmsg, lncntp[dirp->d_ino], -1); 700*b9a41fd3Sswilcox if (errmsg != NULL) { 701*b9a41fd3Sswilcox LINK_CLEAR(errmsg, dirp->d_ino, IFDIR, &ldesc); 702*b9a41fd3Sswilcox if (statemap[dirp->d_ino] == USTATE) { 703*b9a41fd3Sswilcox dirp->d_ino = 0; 704*b9a41fd3Sswilcox ret |= ALTERED; 705*b9a41fd3Sswilcox } 706*b9a41fd3Sswilcox } 707*b9a41fd3Sswilcox TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino]--); 708*b9a41fd3Sswilcox } 709*b9a41fd3Sswilcox return (ret|KEEPON); 710*b9a41fd3Sswilcox chk2: 711*b9a41fd3Sswilcox if (dirp->d_ino == 0) 712*b9a41fd3Sswilcox return (ret|KEEPON); 713*b9a41fd3Sswilcox if (dirp->d_namlen <= 2 && 714*b9a41fd3Sswilcox dirp->d_name[0] == '.' && 715*b9a41fd3Sswilcox idesc->id_entryno >= 2) { 716*b9a41fd3Sswilcox if (dirp->d_namlen == 1) { 717*b9a41fd3Sswilcox direrror(idesc->id_number, "EXTRA '.' ENTRY"); 718*b9a41fd3Sswilcox dirp->d_ino = 0; 719*b9a41fd3Sswilcox if (reply("FIX") == 1) { 720*b9a41fd3Sswilcox ret |= ALTERED; 721*b9a41fd3Sswilcox } else { 722*b9a41fd3Sswilcox iscorrupt = 1; 723*b9a41fd3Sswilcox } 724*b9a41fd3Sswilcox return (KEEPON | ret); 725*b9a41fd3Sswilcox } 726*b9a41fd3Sswilcox if (dirp->d_name[1] == '.') { 727*b9a41fd3Sswilcox direrror(idesc->id_number, "EXTRA '..' ENTRY"); 728*b9a41fd3Sswilcox dirp->d_ino = 0; 729*b9a41fd3Sswilcox if (reply("FIX") == 1) { 730*b9a41fd3Sswilcox ret |= ALTERED; 731*b9a41fd3Sswilcox } else { 732*b9a41fd3Sswilcox iscorrupt = 1; 733*b9a41fd3Sswilcox } 734*b9a41fd3Sswilcox return (KEEPON | ret); 735*b9a41fd3Sswilcox } 736*b9a41fd3Sswilcox } 737*b9a41fd3Sswilcox /* 738*b9a41fd3Sswilcox * Because of this increment, all tests for skipping . and .. 739*b9a41fd3Sswilcox * below are ``> 2'', not ``> 1'' as would logically be expected. 740*b9a41fd3Sswilcox */ 741*b9a41fd3Sswilcox idesc->id_entryno++; 742*b9a41fd3Sswilcox act = -1; 743*b9a41fd3Sswilcox /* 744*b9a41fd3Sswilcox * The obvious check would be for d_ino < UFSROOTINO. However, 745*b9a41fd3Sswilcox * 1 is a valid inode number. Although it isn't currently used, 746*b9a41fd3Sswilcox * as it was once the bad block list, there's nothing to prevent 747*b9a41fd3Sswilcox * it from acquiring a new purpose in the future. So, don't 748*b9a41fd3Sswilcox * arbitrarily disallow it. We don't test for <= zero, because 749*b9a41fd3Sswilcox * d_ino is unsigned. 750*b9a41fd3Sswilcox */ 751*b9a41fd3Sswilcox update_lncntp = 0; 752*b9a41fd3Sswilcox if (dirp->d_ino > maxino || dirp->d_ino == 0) { 753*b9a41fd3Sswilcox fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 754*b9a41fd3Sswilcox act = (reply(PASS2B_PROMPT, idesc->id_number) == 1); 755*b9a41fd3Sswilcox } else { 756*b9a41fd3Sswilcox again: 757*b9a41fd3Sswilcox update_lncntp = 0; 758*b9a41fd3Sswilcox switch (statemap[dirp->d_ino] & ~(INDELAYD)) { 759*b9a41fd3Sswilcox case USTATE: 760*b9a41fd3Sswilcox if (idesc->id_entryno <= 2) 761*b9a41fd3Sswilcox break; 762*b9a41fd3Sswilcox fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 763*b9a41fd3Sswilcox act = (reply(PASS2B_PROMPT, idesc->id_number) == 1); 764*b9a41fd3Sswilcox break; 765*b9a41fd3Sswilcox 766*b9a41fd3Sswilcox case DCLEAR: 767*b9a41fd3Sswilcox case FCLEAR: 768*b9a41fd3Sswilcox case SCLEAR: 769*b9a41fd3Sswilcox if (idesc->id_entryno <= 2) 770*b9a41fd3Sswilcox break; 771*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 772*b9a41fd3Sswilcox if (statemap[dirp->d_ino] == DCLEAR) { 773*b9a41fd3Sswilcox errmsg = ((dp->di_mode & IFMT) == IFATTRDIR) ? 774*b9a41fd3Sswilcox "REFERENCE TO ZERO LENGTH ATTRIBUTE DIRECTORY" : 775*b9a41fd3Sswilcox "REFERENCE TO ZERO LENGTH DIRECTORY"; 776*b9a41fd3Sswilcox inp = getinoinfo(dirp->d_ino); 777*b9a41fd3Sswilcox if (inp == NULL) { 778*b9a41fd3Sswilcox /* 779*b9a41fd3Sswilcox * The inode doesn't exist, as all 780*b9a41fd3Sswilcox * should be cached by now. This 781*b9a41fd3Sswilcox * gets caught by the range check 782*b9a41fd3Sswilcox * above, and so it is a can't-happen 783*b9a41fd3Sswilcox * at this point. 784*b9a41fd3Sswilcox */ 785*b9a41fd3Sswilcox errexit("pass2check found a zero-len " 786*b9a41fd3Sswilcox "reference to bad I=%d\n", 787*b9a41fd3Sswilcox dirp->d_ino); 788*b9a41fd3Sswilcox } 789*b9a41fd3Sswilcox if (inp->i_parent != 0) { 790*b9a41fd3Sswilcox (void) printf( 791*b9a41fd3Sswilcox "Multiple links to I=%d, link counts wrong, rerun fsck\n", 792*b9a41fd3Sswilcox inp->i_number); 793*b9a41fd3Sswilcox iscorrupt = 1; 794*b9a41fd3Sswilcox } 795*b9a41fd3Sswilcox } else if (statemap[dirp->d_ino] == SCLEAR) { 796*b9a41fd3Sswilcox /* 797*b9a41fd3Sswilcox * In theory, this is a can't-happen, 798*b9a41fd3Sswilcox * because shadows don't appear in directory 799*b9a41fd3Sswilcox * entries. However, an inode might've 800*b9a41fd3Sswilcox * been reused without a stale directory 801*b9a41fd3Sswilcox * entry having been cleared, so check 802*b9a41fd3Sswilcox * for it just in case. We'll check for 803*b9a41fd3Sswilcox * the no-dir-entry shadows in pass3b(). 804*b9a41fd3Sswilcox */ 805*b9a41fd3Sswilcox errmsg = "ZERO LENGTH SHADOW"; 806*b9a41fd3Sswilcox } else { 807*b9a41fd3Sswilcox errmsg = "DUP/BAD"; 808*b9a41fd3Sswilcox } 809*b9a41fd3Sswilcox fileerror(idesc->id_number, dirp->d_ino, errmsg); 810*b9a41fd3Sswilcox if ((act = reply(PASS2B_PROMPT, idesc->id_number)) == 1) 811*b9a41fd3Sswilcox break; 812*b9a41fd3Sswilcox /* 813*b9a41fd3Sswilcox * Not doing anything about it, so just try 814*b9a41fd3Sswilcox * again as whatever the base type was. 815*b9a41fd3Sswilcox * 816*b9a41fd3Sswilcox * fileerror() invalidated dp. Lint thinks this 817*b9a41fd3Sswilcox * is unnecessary, but we know better. 818*b9a41fd3Sswilcox */ 819*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 820*b9a41fd3Sswilcox statemap[dirp->d_ino] &= STMASK; 821*b9a41fd3Sswilcox TRACK_LNCNTP(dirp->d_ino, lncntp[dirp->d_ino] = 0); 822*b9a41fd3Sswilcox goto again; 823*b9a41fd3Sswilcox 824*b9a41fd3Sswilcox case DSTATE: 825*b9a41fd3Sswilcox case DZLINK: 826*b9a41fd3Sswilcox if (statemap[idesc->id_number] == DFOUND) { 827*b9a41fd3Sswilcox statemap[dirp->d_ino] = DFOUND; 828*b9a41fd3Sswilcox } 829*b9a41fd3Sswilcox /* FALLTHROUGH */ 830*b9a41fd3Sswilcox 831*b9a41fd3Sswilcox case DFOUND: 832*b9a41fd3Sswilcox /* 833*b9a41fd3Sswilcox * This is encouraging the best-practice of not 834*b9a41fd3Sswilcox * hard-linking directories. It's legal (see POSIX), 835*b9a41fd3Sswilcox * but not a good idea. So, don't consider it an 836*b9a41fd3Sswilcox * instance of corruption, but offer to nuke it. 837*b9a41fd3Sswilcox */ 838*b9a41fd3Sswilcox inp = getinoinfo(dirp->d_ino); 839*b9a41fd3Sswilcox if (inp == NULL) { 840*b9a41fd3Sswilcox /* 841*b9a41fd3Sswilcox * Same can't-happen argument as in the 842*b9a41fd3Sswilcox * zero-len case above. 843*b9a41fd3Sswilcox */ 844*b9a41fd3Sswilcox errexit("pass2check found bad reference to " 845*b9a41fd3Sswilcox "hard-linked directory I=%d\n", 846*b9a41fd3Sswilcox dirp->d_ino); 847*b9a41fd3Sswilcox } 848*b9a41fd3Sswilcox dp = ginode(idesc->id_number); 849*b9a41fd3Sswilcox if (inp->i_parent != 0 && idesc->id_entryno > 2 && 850*b9a41fd3Sswilcox ((dp->di_mode & IFMT) != IFATTRDIR)) { 851*b9a41fd3Sswilcox /* 852*b9a41fd3Sswilcox * XXX For nested dirs, this can report 853*b9a41fd3Sswilcox * the same name for both paths. 854*b9a41fd3Sswilcox */ 855*b9a41fd3Sswilcox getpathname(pathbuf, idesc->id_number, 856*b9a41fd3Sswilcox dirp->d_ino); 857*b9a41fd3Sswilcox getpathname(namebuf, dirp->d_ino, dirp->d_ino); 858*b9a41fd3Sswilcox pwarn( 859*b9a41fd3Sswilcox "%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s\n", 860*b9a41fd3Sswilcox pathbuf, namebuf); 861*b9a41fd3Sswilcox if (preen) 862*b9a41fd3Sswilcox (void) printf(" (IGNORED)\n"); 863*b9a41fd3Sswilcox else if ((act = reply(PASS2B_PROMPT, 864*b9a41fd3Sswilcox idesc->id_number)) == 1) { 865*b9a41fd3Sswilcox update_lncntp = 1; 866*b9a41fd3Sswilcox broke_dir_link = 1; 867*b9a41fd3Sswilcox break; 868*b9a41fd3Sswilcox } 869*b9a41fd3Sswilcox } 870*b9a41fd3Sswilcox 871*b9a41fd3Sswilcox if ((idesc->id_entryno > 2) && 872*b9a41fd3Sswilcox (inp->i_extattr != idesc->id_number)) { 873*b9a41fd3Sswilcox inp->i_parent = idesc->id_number; 874*b9a41fd3Sswilcox } 875*b9a41fd3Sswilcox /* FALLTHROUGH */ 876*b9a41fd3Sswilcox 877*b9a41fd3Sswilcox case FSTATE: 878*b9a41fd3Sswilcox case FZLINK: 879*b9a41fd3Sswilcox /* 880*b9a41fd3Sswilcox * There's nothing to do for normal file-like 881*b9a41fd3Sswilcox * things. Extended attributes come through 882*b9a41fd3Sswilcox * here as well, though, and for them, .. may point 883*b9a41fd3Sswilcox * to a file. In this situation we don't want 884*b9a41fd3Sswilcox * to decrement link count as it was already 885*b9a41fd3Sswilcox * decremented when the entry was seen in the 886*b9a41fd3Sswilcox * directory it actually lives in. 887*b9a41fd3Sswilcox */ 888*b9a41fd3Sswilcox pdirp = ginode(idesc->id_number); 889*b9a41fd3Sswilcox pdirtype = (pdirp->di_mode & IFMT); 890*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 891*b9a41fd3Sswilcox isattr = (dp->di_cflags & IXATTR); 892*b9a41fd3Sswilcox act = -1; 893*b9a41fd3Sswilcox if (pdirtype == IFATTRDIR && 894*b9a41fd3Sswilcox (strcmp(dirp->d_name, "..") == 0)) { 895*b9a41fd3Sswilcox dontreconnect = 0; 896*b9a41fd3Sswilcox if (dp->di_oeftflag != 0) { 897*b9a41fd3Sswilcox attrdirp = ginode(dp->di_oeftflag); 898*b9a41fd3Sswilcox 899*b9a41fd3Sswilcox /* 900*b9a41fd3Sswilcox * is it really an attrdir? 901*b9a41fd3Sswilcox * if so, then don't do anything. 902*b9a41fd3Sswilcox */ 903*b9a41fd3Sswilcox 904*b9a41fd3Sswilcox if ((attrdirp->di_mode & IFMT) == 905*b9a41fd3Sswilcox IFATTRDIR) 906*b9a41fd3Sswilcox dontreconnect = 1; 907*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 908*b9a41fd3Sswilcox } 909*b9a41fd3Sswilcox /* 910*b9a41fd3Sswilcox * Rare corner case - the attrdir's .. 911*b9a41fd3Sswilcox * points to the attrdir itself. 912*b9a41fd3Sswilcox */ 913*b9a41fd3Sswilcox if (dirp->d_ino == idesc->id_number) { 914*b9a41fd3Sswilcox dontreconnect = 1; 915*b9a41fd3Sswilcox TRACK_LNCNTP(idesc->id_number, 916*b9a41fd3Sswilcox lncntp[idesc->id_number]--); 917*b9a41fd3Sswilcox } 918*b9a41fd3Sswilcox /* 919*b9a41fd3Sswilcox * Lets see if we have an orphaned attrdir 920*b9a41fd3Sswilcox * that thinks it belongs to this file. 921*b9a41fd3Sswilcox * Only re-connect it if the current 922*b9a41fd3Sswilcox * attrdir is 0 or not an attrdir. 923*b9a41fd3Sswilcox */ 924*b9a41fd3Sswilcox if ((dp->di_oeftflag != idesc->id_number) && 925*b9a41fd3Sswilcox (dontreconnect == 0)) { 926*b9a41fd3Sswilcox fileerror(idesc->id_number, 927*b9a41fd3Sswilcox dirp->d_ino, 928*b9a41fd3Sswilcox "Attribute directory I=%d not " 929*b9a41fd3Sswilcox "attached to file I=%d\n", 930*b9a41fd3Sswilcox idesc->id_number, dirp->d_ino); 931*b9a41fd3Sswilcox if ((act = reply("FIX")) == 1) { 932*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 933*b9a41fd3Sswilcox if (debug) 934*b9a41fd3Sswilcox (void) printf( 935*b9a41fd3Sswilcox "debug: changing i=%d's oeft from %d ", 936*b9a41fd3Sswilcox dirp->d_ino, 937*b9a41fd3Sswilcox dp->di_oeftflag); 938*b9a41fd3Sswilcox dp->di_oeftflag = 939*b9a41fd3Sswilcox idesc->id_number; 940*b9a41fd3Sswilcox if (debug) 941*b9a41fd3Sswilcox (void) printf("to %d\n", 942*b9a41fd3Sswilcox dp->di_oeftflag); 943*b9a41fd3Sswilcox inodirty(); 944*b9a41fd3Sswilcox registershadowclient( 945*b9a41fd3Sswilcox idesc->id_number, 946*b9a41fd3Sswilcox dirp->d_ino, 947*b9a41fd3Sswilcox &attrclientinfo); 948*b9a41fd3Sswilcox } 949*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 950*b9a41fd3Sswilcox } 951*b9a41fd3Sswilcox 952*b9a41fd3Sswilcox /* 953*b9a41fd3Sswilcox * This can only be true if we've modified 954*b9a41fd3Sswilcox * an inode/xattr connection, and we 955*b9a41fd3Sswilcox * don't keep track of those in the link 956*b9a41fd3Sswilcox * counts. So, skipping the checks just 957*b9a41fd3Sswilcox * after this is not a problem. 958*b9a41fd3Sswilcox */ 959*b9a41fd3Sswilcox if (act > 0) 960*b9a41fd3Sswilcox return (KEEPON | ALTERED); 961*b9a41fd3Sswilcox 962*b9a41fd3Sswilcox /* 963*b9a41fd3Sswilcox * Don't screw up link counts for directories. 964*b9a41fd3Sswilcox * If we aren't careful we can perform 965*b9a41fd3Sswilcox * an extra decrement, since the .. of 966*b9a41fd3Sswilcox * an attrdir could be either a file or a 967*b9a41fd3Sswilcox * directory. If it's a file then its link 968*b9a41fd3Sswilcox * should be correct after it is seen when the 969*b9a41fd3Sswilcox * directory it lives in scanned. 970*b9a41fd3Sswilcox */ 971*b9a41fd3Sswilcox if ((pdirtype == IFATTRDIR) && 972*b9a41fd3Sswilcox ((dp->di_mode & IFMT) == IFDIR)) 973*b9a41fd3Sswilcox breakout = 1; 974*b9a41fd3Sswilcox if ((dp->di_mode & IFMT) != IFDIR) 975*b9a41fd3Sswilcox breakout = 1; 976*b9a41fd3Sswilcox 977*b9a41fd3Sswilcox } else if ((pdirtype != IFATTRDIR) || 978*b9a41fd3Sswilcox (strcmp(dirp->d_name, ".") != 0)) { 979*b9a41fd3Sswilcox if ((pdirtype == IFDIR) && isattr) { 980*b9a41fd3Sswilcox fileerror(idesc->id_number, 981*b9a41fd3Sswilcox dirp->d_ino, 982*b9a41fd3Sswilcox "File should NOT be marked as " 983*b9a41fd3Sswilcox "extended attribute\n"); 984*b9a41fd3Sswilcox if ((act = reply("FIX")) == 1) { 985*b9a41fd3Sswilcox dp = ginode(dirp->d_ino); 986*b9a41fd3Sswilcox if (debug) 987*b9a41fd3Sswilcox (void) printf( 988*b9a41fd3Sswilcox "changing i=%d's cflags from 0x%x to ", 989*b9a41fd3Sswilcox dirp->d_ino, 990*b9a41fd3Sswilcox dp->di_cflags); 991*b9a41fd3Sswilcox 992*b9a41fd3Sswilcox dp->di_cflags &= ~IXATTR; 993*b9a41fd3Sswilcox if (debug) 994*b9a41fd3Sswilcox (void) printf("0x%x\n", 995*b9a41fd3Sswilcox dp->di_cflags); 996*b9a41fd3Sswilcox inodirty(); 997*b9a41fd3Sswilcox if ((dp->di_mode & IFMT) == 998*b9a41fd3Sswilcox IFATTRDIR) { 999*b9a41fd3Sswilcox dp->di_mode &= 1000*b9a41fd3Sswilcox ~IFATTRDIR; 1001*b9a41fd3Sswilcox dp->di_mode |= IFDIR; 1002*b9a41fd3Sswilcox inodirty(); 1003*b9a41fd3Sswilcox pdirp = ginode( 1004*b9a41fd3Sswilcox idesc->id_number); 1005*b9a41fd3Sswilcox if (pdirp->di_oeftflag 1006*b9a41fd3Sswilcox != 0) { 1007*b9a41fd3Sswilcox pdirp->di_oeftflag = 0; 1008*b9a41fd3Sswilcox inodirty(); 1009*b9a41fd3Sswilcox } 1010*b9a41fd3Sswilcox } 1011*b9a41fd3Sswilcox } 1012*b9a41fd3Sswilcox } else { 1013*b9a41fd3Sswilcox if (pdirtype == IFATTRDIR && 1014*b9a41fd3Sswilcox (isattr == 0)) { 1015*b9a41fd3Sswilcox fileerror(idesc->id_number, 1016*b9a41fd3Sswilcox dirp->d_ino, 1017*b9a41fd3Sswilcox "File should BE marked as " 1018*b9a41fd3Sswilcox "extended attribute\n"); 1019*b9a41fd3Sswilcox if ((act = reply("FIX")) == 1) { 1020*b9a41fd3Sswilcox dp = ginode( 1021*b9a41fd3Sswilcox dirp->d_ino); 1022*b9a41fd3Sswilcox dp->di_cflags |= IXATTR; 1023*b9a41fd3Sswilcox /* 1024*b9a41fd3Sswilcox * Make sure it's a file 1025*b9a41fd3Sswilcox * while we're at it. 1026*b9a41fd3Sswilcox */ 1027*b9a41fd3Sswilcox dp->di_mode &= ~IFMT; 1028*b9a41fd3Sswilcox dp->di_mode |= IFREG; 1029*b9a41fd3Sswilcox inodirty(); 1030*b9a41fd3Sswilcox } 1031*b9a41fd3Sswilcox } 1032*b9a41fd3Sswilcox } 1033*b9a41fd3Sswilcox 1034*b9a41fd3Sswilcox } 1035*b9a41fd3Sswilcox if (breakout == 0 || dontreconnect == 0) { 1036*b9a41fd3Sswilcox TRACK_LNCNTP(dirp->d_ino, 1037*b9a41fd3Sswilcox lncntp[dirp->d_ino]--); 1038*b9a41fd3Sswilcox if (act > 0) 1039*b9a41fd3Sswilcox return (KEEPON | ALTERED); 1040*b9a41fd3Sswilcox } 1041*b9a41fd3Sswilcox break; 1042*b9a41fd3Sswilcox 1043*b9a41fd3Sswilcox case SSTATE: 1044*b9a41fd3Sswilcox errmsg = "ACL IN DIRECTORY"; 1045*b9a41fd3Sswilcox fileerror(idesc->id_number, dirp->d_ino, errmsg); 1046*b9a41fd3Sswilcox act = (reply(PASS2B_PROMPT, idesc->id_number) == 1); 1047*b9a41fd3Sswilcox break; 1048*b9a41fd3Sswilcox 1049*b9a41fd3Sswilcox default: 1050*b9a41fd3Sswilcox errexit("BAD STATE 0x%x FOR INODE I=%d", 1051*b9a41fd3Sswilcox statemap[dirp->d_ino], dirp->d_ino); 1052*b9a41fd3Sswilcox } 1053*b9a41fd3Sswilcox } 1054*b9a41fd3Sswilcox 1055*b9a41fd3Sswilcox if (act == 0) { 1056*b9a41fd3Sswilcox iscorrupt = 1; 1057*b9a41fd3Sswilcox } 1058*b9a41fd3Sswilcox 1059*b9a41fd3Sswilcox if (act <= 0) 1060*b9a41fd3Sswilcox return (ret|KEEPON); 1061*b9a41fd3Sswilcox 1062*b9a41fd3Sswilcox if (update_lncntp) { 1063*b9a41fd3Sswilcox LINK_RANGE(errmsg, lncntp[idesc->id_number], 1); 1064*b9a41fd3Sswilcox if (errmsg != NULL) { 1065*b9a41fd3Sswilcox LINK_CLEAR(errmsg, idesc->id_number, IFDIR, &ldesc); 1066*b9a41fd3Sswilcox if (statemap[idesc->id_number] == USTATE) { 1067*b9a41fd3Sswilcox idesc->id_number = 0; 1068*b9a41fd3Sswilcox ret |= ALTERED; 1069*b9a41fd3Sswilcox } 1070*b9a41fd3Sswilcox } 1071*b9a41fd3Sswilcox TRACK_LNCNTP(idesc->id_number, lncntp[idesc->id_number]++); 1072*b9a41fd3Sswilcox } 1073*b9a41fd3Sswilcox 1074*b9a41fd3Sswilcox dirp->d_ino = 0; 1075*b9a41fd3Sswilcox 1076*b9a41fd3Sswilcox return (ret|KEEPON|ALTERED); 1077*b9a41fd3Sswilcox } 1078*b9a41fd3Sswilcox 1079*b9a41fd3Sswilcox #undef PASS2B_PROMPT 1080*b9a41fd3Sswilcox 1081*b9a41fd3Sswilcox /* 1082*b9a41fd3Sswilcox * Routine to sort disk blocks. 1083*b9a41fd3Sswilcox */ 1084*b9a41fd3Sswilcox static int 1085*b9a41fd3Sswilcox blksort(const void *arg1, const void *arg2) 1086*b9a41fd3Sswilcox { 1087*b9a41fd3Sswilcox const struct inoinfo **inpp1 = (const struct inoinfo **)arg1; 1088*b9a41fd3Sswilcox const struct inoinfo **inpp2 = (const struct inoinfo **)arg2; 1089*b9a41fd3Sswilcox 1090*b9a41fd3Sswilcox return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 1091*b9a41fd3Sswilcox } 1092