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