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