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