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