1 /* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)pass2.c 8.6 (Berkeley) 10/27/94"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/time.h> 14 #include <sys/stat.h> 15 #include <ufs/ufs/dinode.h> 16 #include <ufs/ufs/dir.h> 17 #include <ufs/ffs/fs.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include "fsck.h" 21 22 #define MINDIRSIZE (sizeof (struct dirtemplate)) 23 24 int pass2check(), blksort(); 25 26 pass2() 27 { 28 register struct dinode *dp; 29 register struct inoinfo **inpp, *inp; 30 struct inoinfo **inpend; 31 struct inodesc curino; 32 struct dinode dino; 33 char pathbuf[MAXPATHLEN + 1]; 34 35 switch (statemap[ROOTINO]) { 36 37 case USTATE: 38 pfatal("ROOT INODE UNALLOCATED"); 39 if (reply("ALLOCATE") == 0) 40 errexit(""); 41 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 42 errexit("CANNOT ALLOCATE ROOT INODE\n"); 43 break; 44 45 case DCLEAR: 46 pfatal("DUPS/BAD IN ROOT INODE"); 47 if (reply("REALLOCATE")) { 48 freeino(ROOTINO); 49 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 50 errexit("CANNOT ALLOCATE ROOT INODE\n"); 51 break; 52 } 53 if (reply("CONTINUE") == 0) 54 errexit(""); 55 break; 56 57 case FSTATE: 58 case FCLEAR: 59 pfatal("ROOT INODE NOT DIRECTORY"); 60 if (reply("REALLOCATE")) { 61 freeino(ROOTINO); 62 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 63 errexit("CANNOT ALLOCATE ROOT INODE\n"); 64 break; 65 } 66 if (reply("FIX") == 0) 67 errexit(""); 68 dp = ginode(ROOTINO); 69 dp->di_mode &= ~S_IFMT; 70 dp->di_mode |= S_IFDIR; 71 inodirty(); 72 break; 73 74 case DSTATE: 75 break; 76 77 default: 78 errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 79 } 80 statemap[ROOTINO] = DFOUND; 81 if (newinofmt) { 82 statemap[WINO] = FSTATE; 83 typemap[WINO] = IFTODT(S_IFWHT); 84 } 85 /* 86 * Sort the directory list into disk block order. 87 */ 88 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 89 /* 90 * Check the integrity of each directory. 91 */ 92 bzero((char *)&curino, sizeof(struct inodesc)); 93 curino.id_type = DATA; 94 curino.id_func = pass2check; 95 dp = &dino; 96 inpend = &inpsort[inplast]; 97 for (inpp = inpsort; inpp < inpend; inpp++) { 98 inp = *inpp; 99 if (inp->i_isize == 0) 100 continue; 101 if (inp->i_isize < MINDIRSIZE) { 102 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 103 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 104 if (reply("FIX") == 1) { 105 dp = ginode(inp->i_number); 106 dp->di_size = inp->i_isize; 107 inodirty(); 108 dp = &dino; 109 } 110 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 111 getpathname(pathbuf, inp->i_number, inp->i_number); 112 pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 113 pathbuf, inp->i_isize, DIRBLKSIZ); 114 if (preen) 115 printf(" (ADJUSTED)\n"); 116 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 117 if (preen || reply("ADJUST") == 1) { 118 dp = ginode(inp->i_number); 119 dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); 120 inodirty(); 121 dp = &dino; 122 } 123 } 124 bzero((char *)&dino, sizeof(struct dinode)); 125 dino.di_mode = S_IFDIR; 126 dp->di_size = inp->i_isize; 127 bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 128 (size_t)inp->i_numblks); 129 curino.id_number = inp->i_number; 130 curino.id_parent = inp->i_parent; 131 (void)ckinode(dp, &curino); 132 } 133 /* 134 * Now that the parents of all directories have been found, 135 * make another pass to verify the value of `..' 136 */ 137 for (inpp = inpsort; inpp < inpend; inpp++) { 138 inp = *inpp; 139 if (inp->i_parent == 0 || inp->i_isize == 0) 140 continue; 141 if (statemap[inp->i_parent] == DFOUND && 142 statemap[inp->i_number] == DSTATE) 143 statemap[inp->i_number] = DFOUND; 144 if (inp->i_dotdot == inp->i_parent || 145 inp->i_dotdot == (ino_t)-1) 146 continue; 147 if (inp->i_dotdot == 0) { 148 inp->i_dotdot = inp->i_parent; 149 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 150 if (reply("FIX") == 0) 151 continue; 152 (void)makeentry(inp->i_number, inp->i_parent, ".."); 153 lncntp[inp->i_parent]--; 154 continue; 155 } 156 fileerror(inp->i_parent, inp->i_number, 157 "BAD INODE NUMBER FOR '..'"); 158 if (reply("FIX") == 0) 159 continue; 160 lncntp[inp->i_dotdot]++; 161 lncntp[inp->i_parent]--; 162 inp->i_dotdot = inp->i_parent; 163 (void)changeino(inp->i_number, "..", inp->i_parent); 164 } 165 /* 166 * Mark all the directories that can be found from the root. 167 */ 168 propagate(); 169 } 170 171 pass2check(idesc) 172 struct inodesc *idesc; 173 { 174 register struct direct *dirp = idesc->id_dirp; 175 register struct inoinfo *inp; 176 int n, entrysize, ret = 0; 177 struct dinode *dp; 178 char *errmsg; 179 struct direct proto; 180 char namebuf[MAXPATHLEN + 1]; 181 char pathbuf[MAXPATHLEN + 1]; 182 183 /* 184 * If converting, set directory entry type. 185 */ 186 if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { 187 dirp->d_type = typemap[dirp->d_ino]; 188 ret |= ALTERED; 189 } 190 /* 191 * check for "." 192 */ 193 if (idesc->id_entryno != 0) 194 goto chk1; 195 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 196 if (dirp->d_ino != idesc->id_number) { 197 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 198 dirp->d_ino = idesc->id_number; 199 if (reply("FIX") == 1) 200 ret |= ALTERED; 201 } 202 if (newinofmt && dirp->d_type != DT_DIR) { 203 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 204 dirp->d_type = DT_DIR; 205 if (reply("FIX") == 1) 206 ret |= ALTERED; 207 } 208 goto chk1; 209 } 210 direrror(idesc->id_number, "MISSING '.'"); 211 proto.d_ino = idesc->id_number; 212 if (newinofmt) 213 proto.d_type = DT_DIR; 214 else 215 proto.d_type = 0; 216 proto.d_namlen = 1; 217 (void)strcpy(proto.d_name, "."); 218 # if BYTE_ORDER == LITTLE_ENDIAN 219 if (!newinofmt) { 220 u_char tmp; 221 222 tmp = proto.d_type; 223 proto.d_type = proto.d_namlen; 224 proto.d_namlen = tmp; 225 } 226 # endif 227 entrysize = DIRSIZ(0, &proto); 228 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 229 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 230 dirp->d_name); 231 } else if (dirp->d_reclen < entrysize) { 232 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 233 } else if (dirp->d_reclen < 2 * entrysize) { 234 proto.d_reclen = dirp->d_reclen; 235 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 236 if (reply("FIX") == 1) 237 ret |= ALTERED; 238 } else { 239 n = dirp->d_reclen - entrysize; 240 proto.d_reclen = entrysize; 241 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 242 idesc->id_entryno++; 243 lncntp[dirp->d_ino]--; 244 dirp = (struct direct *)((char *)(dirp) + entrysize); 245 bzero((char *)dirp, (size_t)n); 246 dirp->d_reclen = n; 247 if (reply("FIX") == 1) 248 ret |= ALTERED; 249 } 250 chk1: 251 if (idesc->id_entryno > 1) 252 goto chk2; 253 inp = getinoinfo(idesc->id_number); 254 proto.d_ino = inp->i_parent; 255 if (newinofmt) 256 proto.d_type = DT_DIR; 257 else 258 proto.d_type = 0; 259 proto.d_namlen = 2; 260 (void)strcpy(proto.d_name, ".."); 261 # if BYTE_ORDER == LITTLE_ENDIAN 262 if (!newinofmt) { 263 u_char tmp; 264 265 tmp = proto.d_type; 266 proto.d_type = proto.d_namlen; 267 proto.d_namlen = tmp; 268 } 269 # endif 270 entrysize = DIRSIZ(0, &proto); 271 if (idesc->id_entryno == 0) { 272 n = DIRSIZ(0, dirp); 273 if (dirp->d_reclen < n + entrysize) 274 goto chk2; 275 proto.d_reclen = dirp->d_reclen - n; 276 dirp->d_reclen = n; 277 idesc->id_entryno++; 278 lncntp[dirp->d_ino]--; 279 dirp = (struct direct *)((char *)(dirp) + n); 280 bzero((char *)dirp, (size_t)proto.d_reclen); 281 dirp->d_reclen = proto.d_reclen; 282 } 283 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 284 inp->i_dotdot = dirp->d_ino; 285 if (newinofmt && dirp->d_type != DT_DIR) { 286 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 287 dirp->d_type = DT_DIR; 288 if (reply("FIX") == 1) 289 ret |= ALTERED; 290 } 291 goto chk2; 292 } 293 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 294 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 295 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 296 dirp->d_name); 297 inp->i_dotdot = (ino_t)-1; 298 } else if (dirp->d_reclen < entrysize) { 299 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 300 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 301 inp->i_dotdot = (ino_t)-1; 302 } else if (inp->i_parent != 0) { 303 /* 304 * We know the parent, so fix now. 305 */ 306 inp->i_dotdot = inp->i_parent; 307 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 308 proto.d_reclen = dirp->d_reclen; 309 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 310 if (reply("FIX") == 1) 311 ret |= ALTERED; 312 } 313 idesc->id_entryno++; 314 if (dirp->d_ino != 0) 315 lncntp[dirp->d_ino]--; 316 return (ret|KEEPON); 317 chk2: 318 if (dirp->d_ino == 0) 319 return (ret|KEEPON); 320 if (dirp->d_namlen <= 2 && 321 dirp->d_name[0] == '.' && 322 idesc->id_entryno >= 2) { 323 if (dirp->d_namlen == 1) { 324 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 325 dirp->d_ino = 0; 326 if (reply("FIX") == 1) 327 ret |= ALTERED; 328 return (KEEPON | ret); 329 } 330 if (dirp->d_name[1] == '.') { 331 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 332 dirp->d_ino = 0; 333 if (reply("FIX") == 1) 334 ret |= ALTERED; 335 return (KEEPON | ret); 336 } 337 } 338 idesc->id_entryno++; 339 n = 0; 340 if (dirp->d_ino > maxino) { 341 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 342 n = reply("REMOVE"); 343 } else if (newinofmt && 344 ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 345 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 346 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 347 dirp->d_ino = WINO; 348 dirp->d_type = DT_WHT; 349 if (reply("FIX") == 1) 350 ret |= ALTERED; 351 } else { 352 again: 353 switch (statemap[dirp->d_ino]) { 354 case USTATE: 355 if (idesc->id_entryno <= 2) 356 break; 357 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 358 n = reply("REMOVE"); 359 break; 360 361 case DCLEAR: 362 case FCLEAR: 363 if (idesc->id_entryno <= 2) 364 break; 365 if (statemap[dirp->d_ino] == FCLEAR) 366 errmsg = "DUP/BAD"; 367 else if (!preen) 368 errmsg = "ZERO LENGTH DIRECTORY"; 369 else { 370 n = 1; 371 break; 372 } 373 fileerror(idesc->id_number, dirp->d_ino, errmsg); 374 if ((n = reply("REMOVE")) == 1) 375 break; 376 dp = ginode(dirp->d_ino); 377 statemap[dirp->d_ino] = 378 (dp->di_mode & S_IFMT) == S_IFDIR ? DSTATE : FSTATE; 379 lncntp[dirp->d_ino] = dp->di_nlink; 380 goto again; 381 382 case DSTATE: 383 if (statemap[idesc->id_number] == DFOUND) 384 statemap[dirp->d_ino] = DFOUND; 385 /* fall through */ 386 387 case DFOUND: 388 inp = getinoinfo(dirp->d_ino); 389 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 390 getpathname(pathbuf, idesc->id_number, 391 idesc->id_number); 392 getpathname(namebuf, dirp->d_ino, dirp->d_ino); 393 pwarn("%s %s %s\n", pathbuf, 394 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 395 namebuf); 396 if (preen) 397 printf(" (IGNORED)\n"); 398 else if ((n = reply("REMOVE")) == 1) 399 break; 400 } 401 if (idesc->id_entryno > 2) 402 inp->i_parent = idesc->id_number; 403 /* fall through */ 404 405 case FSTATE: 406 if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { 407 fileerror(idesc->id_number, dirp->d_ino, 408 "BAD TYPE VALUE"); 409 dirp->d_type = typemap[dirp->d_ino]; 410 if (reply("FIX") == 1) 411 ret |= ALTERED; 412 } 413 lncntp[dirp->d_ino]--; 414 break; 415 416 default: 417 errexit("BAD STATE %d FOR INODE I=%d", 418 statemap[dirp->d_ino], dirp->d_ino); 419 } 420 } 421 if (n == 0) 422 return (ret|KEEPON); 423 dirp->d_ino = 0; 424 return (ret|KEEPON|ALTERED); 425 } 426 427 /* 428 * Routine to sort disk blocks. 429 */ 430 blksort(inpp1, inpp2) 431 struct inoinfo **inpp1, **inpp2; 432 { 433 434 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 435 } 436