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