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