1 /* $NetBSD: pass2.c,v 1.31 2002/05/06 19:37:51 agc Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 40 #else 41 __RCSID("$NetBSD: pass2.c,v 1.31 2002/05/06 19:37:51 agc Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/time.h> 47 48 #include <ufs/ufs/dinode.h> 49 #include <ufs/ufs/dir.h> 50 #include <ufs/ffs/fs.h> 51 52 #include <err.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 57 #include "fsck.h" 58 #include "fsutil.h" 59 #include "extern.h" 60 61 #define MINDIRSIZE (sizeof (struct dirtemplate)) 62 63 static int blksort __P((const void *, const void *)); 64 static int pass2check __P((struct inodesc *)); 65 66 void 67 pass2() 68 { 69 struct dinode *dp; 70 struct inoinfo **inpp, *inp, *pinp; 71 struct inoinfo **inpend; 72 struct inodesc curino; 73 struct dinode dino; 74 char pathbuf[MAXPATHLEN + 1]; 75 76 switch (statemap[ROOTINO]) { 77 78 case USTATE: 79 pfatal("ROOT INODE UNALLOCATED"); 80 if (reply("ALLOCATE") == 0) { 81 markclean = 0; 82 ckfini(); 83 exit(EEXIT); 84 } 85 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 86 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 87 break; 88 89 case DCLEAR: 90 pfatal("DUPS/BAD IN ROOT INODE"); 91 if (reply("REALLOCATE")) { 92 freeino(ROOTINO); 93 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 94 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 95 break; 96 } 97 markclean = 0; 98 if (reply("CONTINUE") == 0) { 99 ckfini(); 100 exit(EEXIT); 101 } 102 break; 103 104 case FSTATE: 105 case FCLEAR: 106 pfatal("ROOT INODE NOT DIRECTORY"); 107 if (reply("REALLOCATE")) { 108 freeino(ROOTINO); 109 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 110 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 111 break; 112 } 113 if (reply("FIX") == 0) { 114 markclean = 0; 115 ckfini(); 116 exit(EEXIT); 117 } 118 dp = ginode(ROOTINO); 119 dp->di_mode = iswap16((iswap16(dp->di_mode) & ~IFMT) | IFDIR); 120 inodirty(); 121 break; 122 123 case DSTATE: 124 break; 125 126 default: 127 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 128 } 129 if (newinofmt) { 130 statemap[WINO] = FSTATE; 131 typemap[WINO] = DT_WHT; 132 } 133 /* 134 * Sort the directory list into disk block order. 135 */ 136 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 137 /* 138 * Check the integrity of each directory. 139 */ 140 memset(&curino, 0, sizeof(struct inodesc)); 141 curino.id_type = DATA; 142 curino.id_func = pass2check; 143 inpend = &inpsort[inplast]; 144 for (inpp = inpsort; inpp < inpend; inpp++) { 145 if (got_siginfo) { 146 fprintf(stderr, 147 "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(), 148 (long)(inpp - inpsort), (int)inplast, 149 (int)((inpp - inpsort) * 100 / inplast)); 150 got_siginfo = 0; 151 } 152 inp = *inpp; 153 if (inp->i_isize == 0) 154 continue; 155 if (inp->i_isize < MINDIRSIZE) { 156 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 157 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 158 if (reply("FIX") == 1) { 159 dp = ginode(inp->i_number); 160 dp->di_size = iswap64(inp->i_isize); 161 inodirty(); 162 } else 163 markclean = 0; 164 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 165 getpathname(pathbuf, inp->i_number, inp->i_number); 166 if (usedsoftdep) 167 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 168 "DIRECTORY", pathbuf, 169 (long long)inp->i_isize, DIRBLKSIZ); 170 else 171 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 172 "DIRECTORY", pathbuf, 173 (long long)inp->i_isize, DIRBLKSIZ); 174 if (preen) 175 printf(" (ADJUSTED)\n"); 176 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 177 if (preen || reply("ADJUST") == 1) { 178 dp = ginode(inp->i_number); 179 dp->di_size = iswap64(inp->i_isize); 180 inodirty(); 181 } else 182 markclean = 0; 183 } 184 memset(&dino, 0, DINODE_SIZE); 185 dino.di_mode = iswap16(IFDIR); 186 dino.di_size = iswap64(inp->i_isize); 187 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks); 188 curino.id_number = inp->i_number; 189 curino.id_parent = inp->i_parent; 190 (void)ckinode(&dino, &curino); 191 } 192 193 /* byte swapping in direcoties entries, if needed, have been done. 194 * Now rescan dirs for pass2check() 195 */ 196 if (do_dirswap) { 197 do_dirswap = 0; 198 for (inpp = inpsort; inpp < inpend; inpp++) { 199 inp = *inpp; 200 if (inp->i_isize == 0) 201 continue; 202 memset(&dino, 0, DINODE_SIZE); 203 dino.di_mode = iswap16(IFDIR); 204 dino.di_size = iswap64(inp->i_isize); 205 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks); 206 curino.id_number = inp->i_number; 207 curino.id_parent = inp->i_parent; 208 (void)ckinode(&dino, &curino); 209 } 210 } 211 212 /* 213 * Now that the parents of all directories have been found, 214 * make another pass to verify the value of `..' 215 */ 216 for (inpp = inpsort; inpp < inpend; inpp++) { 217 inp = *inpp; 218 if (inp->i_parent == 0 || inp->i_isize == 0) 219 continue; 220 if (inp->i_dotdot == inp->i_parent || 221 inp->i_dotdot == (ino_t)-1) 222 continue; 223 if (inp->i_dotdot == 0) { 224 inp->i_dotdot = inp->i_parent; 225 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 226 if (reply("FIX") == 0) { 227 markclean = 0; 228 continue; 229 } 230 (void)makeentry(inp->i_number, inp->i_parent, ".."); 231 lncntp[inp->i_parent]--; 232 continue; 233 } 234 fileerror(inp->i_parent, inp->i_number, 235 "BAD INODE NUMBER FOR '..'"); 236 if (reply("FIX") == 0) { 237 markclean = 0; 238 continue; 239 } 240 lncntp[inp->i_dotdot]++; 241 lncntp[inp->i_parent]--; 242 inp->i_dotdot = inp->i_parent; 243 (void)changeino(inp->i_number, "..", inp->i_parent); 244 } 245 /* 246 * Create a list of children for each directory. 247 */ 248 inpend = &inpsort[inplast]; 249 for (inpp = inpsort; inpp < inpend; inpp++) { 250 inp = *inpp; 251 inp->i_child = inp->i_sibling = inp->i_parentp = 0; 252 if (statemap[inp->i_number] == DFOUND) 253 statemap[inp->i_number] = DSTATE; 254 } 255 for (inpp = inpsort; inpp < inpend; inpp++) { 256 inp = *inpp; 257 if (inp->i_parent == 0 || 258 inp->i_number == ROOTINO) 259 continue; 260 pinp = getinoinfo(inp->i_parent); 261 inp->i_parentp = pinp; 262 inp->i_sibling = pinp->i_child; 263 pinp->i_child = inp; 264 } 265 /* 266 * Mark all the directories that can be found from the root. 267 */ 268 propagate(ROOTINO); 269 } 270 271 static int 272 pass2check(idesc) 273 struct inodesc *idesc; 274 { 275 struct direct *dirp = idesc->id_dirp; 276 struct inoinfo *inp; 277 int n, entrysize, ret = 0; 278 struct dinode *dp; 279 char *errmsg; 280 struct direct proto; 281 char namebuf[MAXPATHLEN + 1]; 282 char pathbuf[MAXPATHLEN + 1]; 283 284 /* 285 * If converting, set directory entry type. 286 */ 287 if (doinglevel2 && iswap32(dirp->d_ino) > 0 && iswap32(dirp->d_ino) < maxino) { 288 dirp->d_type = typemap[iswap32(dirp->d_ino)]; 289 ret |= ALTERED; 290 } 291 /* 292 * check for "." 293 */ 294 if (idesc->id_entryno != 0) 295 goto chk1; 296 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 297 if (iswap32(dirp->d_ino) != idesc->id_number) { 298 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 299 dirp->d_ino = iswap32(idesc->id_number); 300 if (reply("FIX") == 1) 301 ret |= ALTERED; 302 else 303 markclean = 0; 304 } 305 if (newinofmt && dirp->d_type != DT_DIR) { 306 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 307 dirp->d_type = DT_DIR; 308 if (reply("FIX") == 1) 309 ret |= ALTERED; 310 else 311 markclean = 0; 312 } 313 goto chk1; 314 } 315 direrror(idesc->id_number, "MISSING '.'"); 316 proto.d_ino = iswap32(idesc->id_number); 317 if (newinofmt) 318 proto.d_type = DT_DIR; 319 else 320 proto.d_type = 0; 321 proto.d_namlen = 1; 322 (void)strcpy(proto.d_name, "."); 323 # if BYTE_ORDER == LITTLE_ENDIAN 324 if (!newinofmt && !needswap) { 325 # else 326 if (!newinofmt && needswap) { 327 # endif 328 u_char tmp; 329 330 tmp = proto.d_type; 331 proto.d_type = proto.d_namlen; 332 proto.d_namlen = tmp; 333 } 334 entrysize = DIRSIZ(0, &proto, 0); 335 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 336 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 337 dirp->d_name); 338 markclean = 0; 339 } else if (iswap16(dirp->d_reclen) < entrysize) { 340 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 341 markclean = 0; 342 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) { 343 proto.d_reclen = dirp->d_reclen; 344 memmove(dirp, &proto, (size_t)entrysize); 345 if (reply("FIX") == 1) 346 ret |= ALTERED; 347 else 348 markclean = 0; 349 } else { 350 n = iswap16(dirp->d_reclen) - entrysize; 351 proto.d_reclen = iswap16(entrysize); 352 memmove(dirp, &proto, (size_t)entrysize); 353 idesc->id_entryno++; 354 lncntp[iswap32(dirp->d_ino)]--; 355 dirp = (struct direct *)((char *)(dirp) + entrysize); 356 memset(dirp, 0, (size_t)n); 357 dirp->d_reclen = iswap16(n); 358 if (reply("FIX") == 1) 359 ret |= ALTERED; 360 else 361 markclean = 0; 362 } 363 chk1: 364 if (idesc->id_entryno > 1) 365 goto chk2; 366 inp = getinoinfo(idesc->id_number); 367 proto.d_ino = iswap32(inp->i_parent); 368 if (newinofmt) 369 proto.d_type = DT_DIR; 370 else 371 proto.d_type = 0; 372 proto.d_namlen = 2; 373 (void)strcpy(proto.d_name, ".."); 374 # if BYTE_ORDER == LITTLE_ENDIAN 375 if (!newinofmt && !needswap) { 376 # else 377 if (!newinofmt && needswap) { 378 # endif 379 u_char tmp; 380 381 tmp = proto.d_type; 382 proto.d_type = proto.d_namlen; 383 proto.d_namlen = tmp; 384 } 385 entrysize = DIRSIZ(0, &proto, 0); 386 if (idesc->id_entryno == 0) { 387 n = DIRSIZ(0, dirp, 0); 388 if (iswap16(dirp->d_reclen) < n + entrysize) 389 goto chk2; 390 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); 391 dirp->d_reclen = iswap16(n); 392 idesc->id_entryno++; 393 lncntp[iswap32(dirp->d_ino)]--; 394 dirp = (struct direct *)((char *)(dirp) + n); 395 memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); 396 dirp->d_reclen = proto.d_reclen; 397 } 398 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 399 inp->i_dotdot = iswap32(dirp->d_ino); 400 if (newinofmt && dirp->d_type != DT_DIR) { 401 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 402 dirp->d_type = DT_DIR; 403 if (reply("FIX") == 1) 404 ret |= ALTERED; 405 else 406 markclean = 0; 407 } 408 goto chk2; 409 } 410 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) { 411 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 412 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 413 dirp->d_name); 414 inp->i_dotdot = (ino_t)-1; 415 markclean = 0; 416 } else if (iswap16(dirp->d_reclen) < entrysize) { 417 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 418 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 419 inp->i_dotdot = (ino_t)-1; 420 markclean = 0; 421 } else if (inp->i_parent != 0) { 422 /* 423 * We know the parent, so fix now. 424 */ 425 inp->i_dotdot = inp->i_parent; 426 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 427 proto.d_reclen = dirp->d_reclen; 428 memmove(dirp, &proto, (size_t)entrysize); 429 if (reply("FIX") == 1) 430 ret |= ALTERED; 431 else 432 markclean = 0; 433 } 434 idesc->id_entryno++; 435 if (dirp->d_ino != 0) 436 lncntp[iswap32(dirp->d_ino)]--; 437 return (ret|KEEPON); 438 chk2: 439 if (dirp->d_ino == 0) 440 return (ret|KEEPON); 441 if (dirp->d_namlen <= 2 && 442 dirp->d_name[0] == '.' && 443 idesc->id_entryno >= 2) { 444 if (dirp->d_namlen == 1) { 445 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 446 dirp->d_ino = 0; 447 if (reply("FIX") == 1) 448 ret |= ALTERED; 449 else 450 markclean = 0; 451 return (KEEPON | ret); 452 } 453 if (dirp->d_name[1] == '.') { 454 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 455 dirp->d_ino = 0; 456 if (reply("FIX") == 1) 457 ret |= ALTERED; 458 else 459 markclean = 0; 460 return (KEEPON | ret); 461 } 462 } 463 idesc->id_entryno++; 464 n = 0; 465 if (iswap32(dirp->d_ino) > maxino) { 466 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 467 n = reply("REMOVE"); 468 if (n == 0) 469 markclean = 0; 470 } else if (newinofmt && 471 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) || 472 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) { 473 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); 474 dirp->d_ino = iswap32(WINO); 475 dirp->d_type = DT_WHT; 476 if (reply("FIX") == 1) 477 ret |= ALTERED; 478 else 479 markclean = 0; 480 } else { 481 again: 482 switch (statemap[iswap32(dirp->d_ino)]) { 483 case USTATE: 484 if (idesc->id_entryno <= 2) 485 break; 486 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); 487 n = reply("REMOVE"); 488 if (n == 0) 489 markclean = 0; 490 break; 491 492 case DCLEAR: 493 case FCLEAR: 494 if (idesc->id_entryno <= 2) 495 break; 496 if (statemap[iswap32(dirp->d_ino)] == FCLEAR) 497 errmsg = "DUP/BAD"; 498 else if (!preen && !usedsoftdep) 499 errmsg = "ZERO LENGTH DIRECTORY"; 500 else { 501 n = 1; 502 break; 503 } 504 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); 505 if ((n = reply("REMOVE")) == 1) 506 break; 507 dp = ginode(iswap32(dirp->d_ino)); 508 statemap[iswap32(dirp->d_ino)] = 509 (iswap16(dp->di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 510 lncntp[iswap32(dirp->d_ino)] = iswap16(dp->di_nlink); 511 goto again; 512 513 case DSTATE: 514 case DFOUND: 515 inp = getinoinfo(iswap32(dirp->d_ino)); 516 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 517 getpathname(pathbuf, idesc->id_number, 518 idesc->id_number); 519 getpathname(namebuf, iswap32(dirp->d_ino), 520 iswap32(dirp->d_ino)); 521 pwarn("%s %s %s\n", pathbuf, 522 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 523 namebuf); 524 if (preen) 525 printf(" (IGNORED)\n"); 526 else if ((n = reply("REMOVE")) == 1) 527 break; 528 } 529 if (idesc->id_entryno > 2) 530 inp->i_parent = idesc->id_number; 531 /* fall through */ 532 533 case FSTATE: 534 if (newinofmt && dirp->d_type != typemap[iswap32(dirp->d_ino)]) { 535 fileerror(idesc->id_number, iswap32(dirp->d_ino), 536 "BAD TYPE VALUE"); 537 dirp->d_type = typemap[iswap32(dirp->d_ino)]; 538 if (reply("FIX") == 1) 539 ret |= ALTERED; 540 else 541 markclean = 0; 542 } 543 lncntp[iswap32(dirp->d_ino)]--; 544 break; 545 546 default: 547 errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 548 statemap[iswap32(dirp->d_ino)], iswap32(dirp->d_ino)); 549 } 550 } 551 if (n == 0) 552 return (ret|KEEPON); 553 dirp->d_ino = 0; 554 return (ret|KEEPON|ALTERED); 555 } 556 557 /* 558 * Routine to sort disk blocks. 559 */ 560 static int 561 blksort(arg1, arg2) 562 const void *arg1, *arg2; 563 { 564 565 return ((*(struct inoinfo **)arg1)->i_blks[0] - 566 (*(struct inoinfo **)arg2)->i_blks[0]); 567 } 568