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