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