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