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