1 /* $NetBSD: pass2.c,v 1.29 2001/01/09 05:51:14 mycroft 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.29 2001/01/09 05:51:14 mycroft 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 inp = *inpp; 146 if (inp->i_isize == 0) 147 continue; 148 if (inp->i_isize < MINDIRSIZE) { 149 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 150 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 151 if (reply("FIX") == 1) { 152 dp = ginode(inp->i_number); 153 dp->di_size = iswap64(inp->i_isize); 154 inodirty(); 155 } else 156 markclean = 0; 157 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 158 getpathname(pathbuf, inp->i_number, inp->i_number); 159 if (usedsoftdep) 160 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 161 "DIRECTORY", pathbuf, 162 (long long)inp->i_isize, DIRBLKSIZ); 163 else 164 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 165 "DIRECTORY", pathbuf, 166 (long long)inp->i_isize, DIRBLKSIZ); 167 if (preen) 168 printf(" (ADJUSTED)\n"); 169 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 170 if (preen || reply("ADJUST") == 1) { 171 dp = ginode(inp->i_number); 172 dp->di_size = iswap64(inp->i_isize); 173 inodirty(); 174 } else 175 markclean = 0; 176 } 177 memset(&dino, 0, DINODE_SIZE); 178 dino.di_mode = iswap16(IFDIR); 179 dino.di_size = iswap64(inp->i_isize); 180 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks); 181 curino.id_number = inp->i_number; 182 curino.id_parent = inp->i_parent; 183 (void)ckinode(&dino, &curino); 184 } 185 186 /* byte swapping in direcoties entries, if needed, have been done. 187 * Now rescan dirs for pass2check() 188 */ 189 if (do_dirswap) { 190 do_dirswap = 0; 191 for (inpp = inpsort; inpp < inpend; inpp++) { 192 inp = *inpp; 193 if (inp->i_isize == 0) 194 continue; 195 memset(&dino, 0, DINODE_SIZE); 196 dino.di_mode = iswap16(IFDIR); 197 dino.di_size = iswap64(inp->i_isize); 198 memmove(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks); 199 curino.id_number = inp->i_number; 200 curino.id_parent = inp->i_parent; 201 (void)ckinode(&dino, &curino); 202 } 203 } 204 205 /* 206 * Now that the parents of all directories have been found, 207 * make another pass to verify the value of `..' 208 */ 209 for (inpp = inpsort; inpp < inpend; inpp++) { 210 inp = *inpp; 211 if (inp->i_parent == 0 || inp->i_isize == 0) 212 continue; 213 if (inp->i_dotdot == inp->i_parent || 214 inp->i_dotdot == (ino_t)-1) 215 continue; 216 if (inp->i_dotdot == 0) { 217 inp->i_dotdot = inp->i_parent; 218 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 219 if (reply("FIX") == 0) { 220 markclean = 0; 221 continue; 222 } 223 (void)makeentry(inp->i_number, inp->i_parent, ".."); 224 lncntp[inp->i_parent]--; 225 continue; 226 } 227 fileerror(inp->i_parent, inp->i_number, 228 "BAD INODE NUMBER FOR '..'"); 229 if (reply("FIX") == 0) { 230 markclean = 0; 231 continue; 232 } 233 lncntp[inp->i_dotdot]++; 234 lncntp[inp->i_parent]--; 235 inp->i_dotdot = inp->i_parent; 236 (void)changeino(inp->i_number, "..", inp->i_parent); 237 } 238 /* 239 * Create a list of children for each directory. 240 */ 241 inpend = &inpsort[inplast]; 242 for (inpp = inpsort; inpp < inpend; inpp++) { 243 inp = *inpp; 244 inp->i_child = inp->i_sibling = inp->i_parentp = 0; 245 if (statemap[inp->i_number] == DFOUND) 246 statemap[inp->i_number] = DSTATE; 247 } 248 for (inpp = inpsort; inpp < inpend; inpp++) { 249 inp = *inpp; 250 if (inp->i_parent == 0 || 251 inp->i_number == ROOTINO) 252 continue; 253 pinp = getinoinfo(inp->i_parent); 254 inp->i_parentp = pinp; 255 inp->i_sibling = pinp->i_child; 256 pinp->i_child = inp; 257 } 258 /* 259 * Mark all the directories that can be found from the root. 260 */ 261 propagate(ROOTINO); 262 } 263 264 static int 265 pass2check(idesc) 266 struct inodesc *idesc; 267 { 268 struct direct *dirp = idesc->id_dirp; 269 struct inoinfo *inp; 270 int n, entrysize, ret = 0; 271 struct dinode *dp; 272 char *errmsg; 273 struct direct proto; 274 char namebuf[MAXPATHLEN + 1]; 275 char pathbuf[MAXPATHLEN + 1]; 276 277 /* 278 * If converting, set directory entry type. 279 */ 280 if (doinglevel2 && iswap32(dirp->d_ino) > 0 && iswap32(dirp->d_ino) < maxino) { 281 dirp->d_type = typemap[iswap32(dirp->d_ino)]; 282 ret |= ALTERED; 283 } 284 /* 285 * check for "." 286 */ 287 if (idesc->id_entryno != 0) 288 goto chk1; 289 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 290 if (iswap32(dirp->d_ino) != idesc->id_number) { 291 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 292 dirp->d_ino = iswap32(idesc->id_number); 293 if (reply("FIX") == 1) 294 ret |= ALTERED; 295 else 296 markclean = 0; 297 } 298 if (newinofmt && dirp->d_type != DT_DIR) { 299 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 300 dirp->d_type = DT_DIR; 301 if (reply("FIX") == 1) 302 ret |= ALTERED; 303 else 304 markclean = 0; 305 } 306 goto chk1; 307 } 308 direrror(idesc->id_number, "MISSING '.'"); 309 proto.d_ino = iswap32(idesc->id_number); 310 if (newinofmt) 311 proto.d_type = DT_DIR; 312 else 313 proto.d_type = 0; 314 proto.d_namlen = 1; 315 (void)strcpy(proto.d_name, "."); 316 # if BYTE_ORDER == LITTLE_ENDIAN 317 if (!newinofmt && !needswap) { 318 # else 319 if (!newinofmt && needswap) { 320 # endif 321 u_char tmp; 322 323 tmp = proto.d_type; 324 proto.d_type = proto.d_namlen; 325 proto.d_namlen = tmp; 326 } 327 entrysize = DIRSIZ(0, &proto, 0); 328 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 329 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 330 dirp->d_name); 331 markclean = 0; 332 } else if (iswap16(dirp->d_reclen) < entrysize) { 333 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 334 markclean = 0; 335 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) { 336 proto.d_reclen = dirp->d_reclen; 337 memmove(dirp, &proto, (size_t)entrysize); 338 if (reply("FIX") == 1) 339 ret |= ALTERED; 340 else 341 markclean = 0; 342 } else { 343 n = iswap16(dirp->d_reclen) - entrysize; 344 proto.d_reclen = iswap16(entrysize); 345 memmove(dirp, &proto, (size_t)entrysize); 346 idesc->id_entryno++; 347 lncntp[iswap32(dirp->d_ino)]--; 348 dirp = (struct direct *)((char *)(dirp) + entrysize); 349 memset(dirp, 0, (size_t)n); 350 dirp->d_reclen = iswap16(n); 351 if (reply("FIX") == 1) 352 ret |= ALTERED; 353 else 354 markclean = 0; 355 } 356 chk1: 357 if (idesc->id_entryno > 1) 358 goto chk2; 359 inp = getinoinfo(idesc->id_number); 360 proto.d_ino = iswap32(inp->i_parent); 361 if (newinofmt) 362 proto.d_type = DT_DIR; 363 else 364 proto.d_type = 0; 365 proto.d_namlen = 2; 366 (void)strcpy(proto.d_name, ".."); 367 # if BYTE_ORDER == LITTLE_ENDIAN 368 if (!newinofmt && !needswap) { 369 # else 370 if (!newinofmt && needswap) { 371 # endif 372 u_char tmp; 373 374 tmp = proto.d_type; 375 proto.d_type = proto.d_namlen; 376 proto.d_namlen = tmp; 377 } 378 entrysize = DIRSIZ(0, &proto, 0); 379 if (idesc->id_entryno == 0) { 380 n = DIRSIZ(0, dirp, 0); 381 if (iswap16(dirp->d_reclen) < n + entrysize) 382 goto chk2; 383 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); 384 dirp->d_reclen = iswap16(n); 385 idesc->id_entryno++; 386 lncntp[iswap32(dirp->d_ino)]--; 387 dirp = (struct direct *)((char *)(dirp) + n); 388 memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); 389 dirp->d_reclen = proto.d_reclen; 390 } 391 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 392 inp->i_dotdot = iswap32(dirp->d_ino); 393 if (newinofmt && dirp->d_type != DT_DIR) { 394 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 395 dirp->d_type = DT_DIR; 396 if (reply("FIX") == 1) 397 ret |= ALTERED; 398 else 399 markclean = 0; 400 } 401 goto chk2; 402 } 403 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) { 404 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 405 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 406 dirp->d_name); 407 inp->i_dotdot = (ino_t)-1; 408 markclean = 0; 409 } else if (iswap16(dirp->d_reclen) < entrysize) { 410 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 411 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 412 inp->i_dotdot = (ino_t)-1; 413 markclean = 0; 414 } else if (inp->i_parent != 0) { 415 /* 416 * We know the parent, so fix now. 417 */ 418 inp->i_dotdot = inp->i_parent; 419 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 420 proto.d_reclen = dirp->d_reclen; 421 memmove(dirp, &proto, (size_t)entrysize); 422 if (reply("FIX") == 1) 423 ret |= ALTERED; 424 else 425 markclean = 0; 426 } 427 idesc->id_entryno++; 428 if (dirp->d_ino != 0) 429 lncntp[iswap32(dirp->d_ino)]--; 430 return (ret|KEEPON); 431 chk2: 432 if (dirp->d_ino == 0) 433 return (ret|KEEPON); 434 if (dirp->d_namlen <= 2 && 435 dirp->d_name[0] == '.' && 436 idesc->id_entryno >= 2) { 437 if (dirp->d_namlen == 1) { 438 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 439 dirp->d_ino = 0; 440 if (reply("FIX") == 1) 441 ret |= ALTERED; 442 else 443 markclean = 0; 444 return (KEEPON | ret); 445 } 446 if (dirp->d_name[1] == '.') { 447 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 448 dirp->d_ino = 0; 449 if (reply("FIX") == 1) 450 ret |= ALTERED; 451 else 452 markclean = 0; 453 return (KEEPON | ret); 454 } 455 } 456 idesc->id_entryno++; 457 n = 0; 458 if (iswap32(dirp->d_ino) > maxino) { 459 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 460 n = reply("REMOVE"); 461 if (n == 0) 462 markclean = 0; 463 } else if (newinofmt && 464 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) || 465 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) { 466 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); 467 dirp->d_ino = iswap32(WINO); 468 dirp->d_type = DT_WHT; 469 if (reply("FIX") == 1) 470 ret |= ALTERED; 471 else 472 markclean = 0; 473 } else { 474 again: 475 switch (statemap[iswap32(dirp->d_ino)]) { 476 case USTATE: 477 if (idesc->id_entryno <= 2) 478 break; 479 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); 480 n = reply("REMOVE"); 481 if (n == 0) 482 markclean = 0; 483 break; 484 485 case DCLEAR: 486 case FCLEAR: 487 if (idesc->id_entryno <= 2) 488 break; 489 if (statemap[iswap32(dirp->d_ino)] == FCLEAR) 490 errmsg = "DUP/BAD"; 491 else if (!preen && !usedsoftdep) 492 errmsg = "ZERO LENGTH DIRECTORY"; 493 else { 494 n = 1; 495 break; 496 } 497 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); 498 if ((n = reply("REMOVE")) == 1) 499 break; 500 dp = ginode(iswap32(dirp->d_ino)); 501 statemap[iswap32(dirp->d_ino)] = 502 (iswap16(dp->di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 503 lncntp[iswap32(dirp->d_ino)] = iswap16(dp->di_nlink); 504 goto again; 505 506 case DSTATE: 507 case DFOUND: 508 inp = getinoinfo(iswap32(dirp->d_ino)); 509 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 510 getpathname(pathbuf, idesc->id_number, 511 idesc->id_number); 512 getpathname(namebuf, iswap32(dirp->d_ino), 513 iswap32(dirp->d_ino)); 514 pwarn("%s %s %s\n", pathbuf, 515 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 516 namebuf); 517 if (preen) 518 printf(" (IGNORED)\n"); 519 else if ((n = reply("REMOVE")) == 1) 520 break; 521 } 522 if (idesc->id_entryno > 2) 523 inp->i_parent = idesc->id_number; 524 /* fall through */ 525 526 case FSTATE: 527 if (newinofmt && dirp->d_type != typemap[iswap32(dirp->d_ino)]) { 528 fileerror(idesc->id_number, iswap32(dirp->d_ino), 529 "BAD TYPE VALUE"); 530 dirp->d_type = typemap[iswap32(dirp->d_ino)]; 531 if (reply("FIX") == 1) 532 ret |= ALTERED; 533 else 534 markclean = 0; 535 } 536 lncntp[iswap32(dirp->d_ino)]--; 537 break; 538 539 default: 540 errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 541 statemap[iswap32(dirp->d_ino)], iswap32(dirp->d_ino)); 542 } 543 } 544 if (n == 0) 545 return (ret|KEEPON); 546 dirp->d_ino = 0; 547 return (ret|KEEPON|ALTERED); 548 } 549 550 /* 551 * Routine to sort disk blocks. 552 */ 553 static int 554 blksort(arg1, arg2) 555 const void *arg1, *arg2; 556 { 557 558 return ((*(struct inoinfo **)arg1)->i_blks[0] - 559 (*(struct inoinfo **)arg2)->i_blks[0]); 560 } 561