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