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