1 /* $OpenBSD: pass2.c,v 1.17 2003/06/02 20:06:15 millert 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.17 2003/06/02 20:06:15 millert 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, int buflen) 65 { 66 return snprintf(buf, buflen, "phase 2, directory %d/%d", 67 info_pos, info_max); 68 } 69 70 static int 71 pass2_info2(char *buf, int buflen) 72 { 73 return snprintf(buf, buflen, "phase 2, parent directory %d/%d", 74 info_pos, info_max); 75 } 76 77 void 78 pass2(void) 79 { 80 struct dinode *dp; 81 struct inoinfo **inpp, *inp, *pinp; 82 struct inoinfo **inpend; 83 struct inodesc curino; 84 struct dinode dino; 85 char pathbuf[MAXPATHLEN + 1]; 86 87 switch (statemap[ROOTINO]) { 88 89 case USTATE: 90 pfatal("ROOT INODE UNALLOCATED"); 91 if (reply("ALLOCATE") == 0) { 92 ckfini(0); 93 errexit("%s", ""); 94 } 95 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 96 errexit("CANNOT ALLOCATE ROOT INODE\n"); 97 break; 98 99 case DCLEAR: 100 pfatal("DUPS/BAD IN ROOT INODE"); 101 if (reply("REALLOCATE")) { 102 freeino(ROOTINO); 103 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 104 errexit("CANNOT ALLOCATE ROOT INODE\n"); 105 break; 106 } 107 if (reply("CONTINUE") == 0) { 108 ckfini(0); 109 errexit("%s", ""); 110 } 111 break; 112 113 case FSTATE: 114 case FCLEAR: 115 pfatal("ROOT INODE NOT DIRECTORY"); 116 if (reply("REALLOCATE")) { 117 freeino(ROOTINO); 118 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 119 errexit("CANNOT ALLOCATE ROOT INODE\n"); 120 break; 121 } 122 if (reply("FIX") == 0) { 123 ckfini(0); 124 errexit("%s", ""); 125 } 126 dp = ginode(ROOTINO); 127 dp->di_mode &= ~IFMT; 128 dp->di_mode |= IFDIR; 129 inodirty(); 130 break; 131 132 case DSTATE: 133 break; 134 135 default: 136 errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); 137 } 138 statemap[ROOTINO] = DFOUND; 139 if (newinofmt) { 140 statemap[WINO] = FSTATE; 141 typemap[WINO] = DT_WHT; 142 } 143 /* 144 * Sort the directory list into disk block order. 145 */ 146 qsort(inpsort, (size_t)inplast, sizeof *inpsort, blksort); 147 /* 148 * Check the integrity of each directory. 149 */ 150 memset(&curino, 0, sizeof(struct inodesc)); 151 curino.id_type = DATA; 152 curino.id_func = pass2check; 153 inpend = &inpsort[inplast]; 154 info_pos = 0; 155 info_max = inpend - inpsort; 156 info_fn = pass2_info1; 157 for (inpp = inpsort; inpp < inpend; inpp++) { 158 inp = *inpp; 159 info_pos ++; 160 if (inp->i_isize == 0) 161 continue; 162 if (inp->i_isize < MINDIRSIZE) { 163 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 164 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 165 if (reply("FIX") == 1) { 166 dp = ginode(inp->i_number); 167 dp->di_size = inp->i_isize; 168 inodirty(); 169 } 170 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 171 getpathname(pathbuf, sizeof pathbuf, 172 inp->i_number, inp->i_number); 173 if (usedsoftdep) 174 pfatal("%s %s: LENGTH %ld NOT MULTIPLE of %d", 175 "DIRECTORY", pathbuf, (long)inp->i_isize, 176 DIRBLKSIZ); 177 else 178 pwarn("%s %s: LENGTH %ld NOT MULTIPLE OF %d", 179 "DIRECTORY", pathbuf, (long)inp->i_isize, 180 DIRBLKSIZ); 181 if (preen) 182 printf(" (ADJUSTED)\n"); 183 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 184 if (preen || reply("ADJUST") == 1) { 185 dp = ginode(inp->i_number); 186 dp->di_size = inp->i_isize; 187 inodirty(); 188 } 189 } 190 memset(&dino, 0, sizeof(struct dinode)); 191 dino.di_mode = IFDIR; 192 dino.di_size = inp->i_isize; 193 memcpy(&dino.di_db[0], &inp->i_blks[0], (size_t)inp->i_numblks); 194 curino.id_number = inp->i_number; 195 curino.id_parent = inp->i_parent; 196 (void)ckinode(&dino, &curino); 197 } 198 /* 199 * Now that the parents of all directories have been found, 200 * make another pass to verify the value of `..' 201 */ 202 info_pos = 0; 203 info_fn = pass2_info2; 204 for (inpp = inpsort; inpp < inpend; inpp++) { 205 inp = *inpp; 206 info_pos++; 207 if (inp->i_parent == 0 || inp->i_isize == 0) 208 continue; 209 if (inp->i_dotdot == inp->i_parent || 210 inp->i_dotdot == (ino_t)-1) 211 continue; 212 if (inp->i_dotdot == 0) { 213 inp->i_dotdot = inp->i_parent; 214 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 215 if (reply("FIX") == 0) 216 continue; 217 (void)makeentry(inp->i_number, inp->i_parent, ".."); 218 lncntp[inp->i_parent]--; 219 continue; 220 } 221 fileerror(inp->i_parent, inp->i_number, 222 "BAD INODE NUMBER FOR '..'"); 223 if (reply("FIX") == 0) 224 continue; 225 lncntp[inp->i_dotdot]++; 226 lncntp[inp->i_parent]--; 227 inp->i_dotdot = inp->i_parent; 228 (void)changeino(inp->i_number, "..", inp->i_parent); 229 } 230 info_fn = NULL; 231 /* 232 * Create a list of children for each directory. 233 */ 234 inpend = &inpsort[inplast]; 235 for (inpp = inpsort; inpp < inpend; inpp++) { 236 inp = *inpp; 237 if (inp->i_parent == 0 || 238 inp->i_number == ROOTINO) 239 continue; 240 pinp = getinoinfo(inp->i_parent); 241 inp->i_parentp = pinp; 242 inp->i_sibling = pinp->i_child; 243 pinp->i_child = inp; 244 } 245 /* 246 * Mark all the directories that can be found from the root. 247 */ 248 propagate(ROOTINO); 249 } 250 251 static int 252 pass2check(struct inodesc *idesc) 253 { 254 struct direct *dirp = idesc->id_dirp; 255 struct inoinfo *inp; 256 int n, entrysize, ret = 0; 257 struct dinode *dp; 258 char *errmsg; 259 struct direct proto; 260 char namebuf[MAXPATHLEN + 1]; 261 char pathbuf[MAXPATHLEN + 1]; 262 263 /* 264 * If converting, set directory entry type. 265 */ 266 if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { 267 dirp->d_type = typemap[dirp->d_ino]; 268 ret |= ALTERED; 269 } 270 /* 271 * check for "." 272 */ 273 if (idesc->id_entryno != 0) 274 goto chk1; 275 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 276 if (dirp->d_ino != idesc->id_number) { 277 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 278 dirp->d_ino = idesc->id_number; 279 if (reply("FIX") == 1) 280 ret |= ALTERED; 281 } 282 if (newinofmt && dirp->d_type != DT_DIR) { 283 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 284 dirp->d_type = DT_DIR; 285 if (reply("FIX") == 1) 286 ret |= ALTERED; 287 } 288 goto chk1; 289 } 290 direrror(idesc->id_number, "MISSING '.'"); 291 proto.d_ino = idesc->id_number; 292 if (newinofmt) 293 proto.d_type = DT_DIR; 294 else 295 proto.d_type = 0; 296 proto.d_namlen = 1; 297 (void)strlcpy(proto.d_name, ".", sizeof proto.d_name); 298 # if BYTE_ORDER == LITTLE_ENDIAN 299 if (!newinofmt) { 300 u_char tmp; 301 302 tmp = proto.d_type; 303 proto.d_type = proto.d_namlen; 304 proto.d_namlen = tmp; 305 } 306 # endif 307 entrysize = DIRSIZ(0, &proto); 308 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 309 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 310 dirp->d_name); 311 } else if (dirp->d_reclen < entrysize) { 312 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 313 } else if (dirp->d_reclen < 2 * entrysize) { 314 proto.d_reclen = dirp->d_reclen; 315 memcpy(dirp, &proto, (size_t)entrysize); 316 if (reply("FIX") == 1) 317 ret |= ALTERED; 318 } else { 319 n = dirp->d_reclen - entrysize; 320 proto.d_reclen = entrysize; 321 memcpy(dirp, &proto, (size_t)entrysize); 322 idesc->id_entryno++; 323 lncntp[dirp->d_ino]--; 324 dirp = (struct direct *)((char *)(dirp) + entrysize); 325 memset(dirp, 0, (size_t)n); 326 dirp->d_reclen = n; 327 if (reply("FIX") == 1) 328 ret |= ALTERED; 329 } 330 chk1: 331 if (idesc->id_entryno > 1) 332 goto chk2; 333 inp = getinoinfo(idesc->id_number); 334 proto.d_ino = inp->i_parent; 335 if (newinofmt) 336 proto.d_type = DT_DIR; 337 else 338 proto.d_type = 0; 339 proto.d_namlen = 2; 340 (void)strlcpy(proto.d_name, "..", sizeof proto.d_name); 341 # if BYTE_ORDER == LITTLE_ENDIAN 342 if (!newinofmt) { 343 u_char tmp; 344 345 tmp = proto.d_type; 346 proto.d_type = proto.d_namlen; 347 proto.d_namlen = tmp; 348 } 349 # endif 350 entrysize = DIRSIZ(0, &proto); 351 if (idesc->id_entryno == 0) { 352 n = DIRSIZ(0, dirp); 353 if (dirp->d_reclen < n + entrysize) 354 goto chk2; 355 proto.d_reclen = dirp->d_reclen - n; 356 dirp->d_reclen = n; 357 idesc->id_entryno++; 358 lncntp[dirp->d_ino]--; 359 dirp = (struct direct *)((char *)(dirp) + n); 360 memset(dirp, 0, (size_t)proto.d_reclen); 361 dirp->d_reclen = proto.d_reclen; 362 } 363 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 364 inp->i_dotdot = dirp->d_ino; 365 if (newinofmt && dirp->d_type != DT_DIR) { 366 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 367 dirp->d_type = DT_DIR; 368 if (reply("FIX") == 1) 369 ret |= ALTERED; 370 } 371 goto chk2; 372 } 373 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 374 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 375 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 376 dirp->d_name); 377 inp->i_dotdot = -1; 378 } else if (dirp->d_reclen < entrysize) { 379 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 380 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 381 inp->i_dotdot = -1; 382 } else if (inp->i_parent != 0) { 383 /* 384 * We know the parent, so fix now. 385 */ 386 inp->i_dotdot = inp->i_parent; 387 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 388 proto.d_reclen = dirp->d_reclen; 389 memcpy(dirp, &proto, (size_t)entrysize); 390 if (reply("FIX") == 1) 391 ret |= ALTERED; 392 } 393 idesc->id_entryno++; 394 if (dirp->d_ino != 0) 395 lncntp[dirp->d_ino]--; 396 return (ret|KEEPON); 397 chk2: 398 if (dirp->d_ino == 0) 399 return (ret|KEEPON); 400 if (dirp->d_namlen <= 2 && 401 dirp->d_name[0] == '.' && 402 idesc->id_entryno >= 2) { 403 if (dirp->d_namlen == 1) { 404 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 405 dirp->d_ino = 0; 406 if (reply("FIX") == 1) 407 ret |= ALTERED; 408 return (KEEPON | ret); 409 } 410 if (dirp->d_name[1] == '.') { 411 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 412 dirp->d_ino = 0; 413 if (reply("FIX") == 1) 414 ret |= ALTERED; 415 return (KEEPON | ret); 416 } 417 } 418 idesc->id_entryno++; 419 n = 0; 420 if (dirp->d_ino > maxino) { 421 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 422 n = reply("REMOVE"); 423 } else if (newinofmt && 424 ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 425 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 426 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 427 dirp->d_ino = WINO; 428 dirp->d_type = DT_WHT; 429 if (reply("FIX") == 1) 430 ret |= ALTERED; 431 } else { 432 again: 433 switch (statemap[dirp->d_ino]) { 434 case USTATE: 435 if (idesc->id_entryno <= 2) 436 break; 437 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 438 n = reply("REMOVE"); 439 break; 440 441 case DCLEAR: 442 case FCLEAR: 443 if (idesc->id_entryno <= 2) 444 break; 445 if (statemap[dirp->d_ino] == FCLEAR) 446 errmsg = "DUP/BAD"; 447 else if (!preen && !usedsoftdep) 448 errmsg = "ZERO LENGTH DIRECTORY"; 449 else { 450 n = 1; 451 break; 452 } 453 fileerror(idesc->id_number, dirp->d_ino, errmsg); 454 if ((n = reply("REMOVE")) == 1) 455 break; 456 dp = ginode(dirp->d_ino); 457 statemap[dirp->d_ino] = 458 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 459 lncntp[dirp->d_ino] = dp->di_nlink; 460 goto again; 461 462 case DSTATE: 463 case DFOUND: 464 inp = getinoinfo(dirp->d_ino); 465 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 466 getpathname(pathbuf, sizeof pathbuf, 467 idesc->id_number, idesc->id_number); 468 getpathname(namebuf, sizeof namebuf, 469 dirp->d_ino, dirp->d_ino); 470 pwarn("%s %s %s\n", pathbuf, 471 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 472 namebuf); 473 if (preen) { 474 printf (" (REMOVED)\n"); 475 n = 1; 476 break; 477 } 478 if ((n = reply("REMOVE")) == 1) 479 break; 480 } 481 if (idesc->id_entryno > 2) 482 inp->i_parent = idesc->id_number; 483 /* fall through */ 484 485 case FSTATE: 486 if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { 487 fileerror(idesc->id_number, dirp->d_ino, 488 "BAD TYPE VALUE"); 489 dirp->d_type = typemap[dirp->d_ino]; 490 if (reply("FIX") == 1) 491 ret |= ALTERED; 492 } 493 lncntp[dirp->d_ino]--; 494 break; 495 496 default: 497 errexit("BAD STATE %d FOR INODE I=%d", 498 statemap[dirp->d_ino], dirp->d_ino); 499 } 500 } 501 if (n == 0) 502 return (ret|KEEPON); 503 dirp->d_ino = 0; 504 return (ret|KEEPON|ALTERED); 505 } 506 507 /* 508 * Routine to sort disk blocks. 509 */ 510 static int 511 blksort(const void *inpp1, const void *inpp2) 512 { 513 return ((* (struct inoinfo **) inpp1)->i_blks[0] - 514 (* (struct inoinfo **) inpp2)->i_blks[0]); 515 } 516