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