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