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.8 (Berkeley) 04/27/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 bzero((char *)&curino, 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 bzero((char *)&dino, sizeof(struct dinode)); 129 dino.di_mode = IFDIR; 130 dp->di_size = inp->i_isize; 131 bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 132 (size_t)inp->i_numblks); 133 curino.id_number = inp->i_number; 134 curino.id_parent = inp->i_parent; 135 (void)ckinode(dp, &curino); 136 } 137 /* 138 * Now that the parents of all directories have been found, 139 * make another pass to verify the value of `..' 140 */ 141 for (inpp = inpsort; inpp < inpend; inpp++) { 142 inp = *inpp; 143 if (inp->i_parent == 0 || inp->i_isize == 0) 144 continue; 145 if (statemap[inp->i_parent] == DFOUND && 146 statemap[inp->i_number] == DSTATE) 147 statemap[inp->i_number] = DFOUND; 148 if (inp->i_dotdot == inp->i_parent || 149 inp->i_dotdot == (ino_t)-1) 150 continue; 151 if (inp->i_dotdot == 0) { 152 inp->i_dotdot = inp->i_parent; 153 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 154 if (reply("FIX") == 0) 155 continue; 156 (void)makeentry(inp->i_number, inp->i_parent, ".."); 157 lncntp[inp->i_parent]--; 158 continue; 159 } 160 fileerror(inp->i_parent, inp->i_number, 161 "BAD INODE NUMBER FOR '..'"); 162 if (reply("FIX") == 0) 163 continue; 164 lncntp[inp->i_dotdot]++; 165 lncntp[inp->i_parent]--; 166 inp->i_dotdot = inp->i_parent; 167 (void)changeino(inp->i_number, "..", inp->i_parent); 168 } 169 /* 170 * Mark all the directories that can be found from the root. 171 */ 172 propagate(); 173 } 174 175 static int 176 pass2check(idesc) 177 struct inodesc *idesc; 178 { 179 register struct direct *dirp = idesc->id_dirp; 180 register struct inoinfo *inp; 181 int n, entrysize, ret = 0; 182 struct dinode *dp; 183 char *errmsg; 184 struct direct proto; 185 char namebuf[MAXPATHLEN + 1]; 186 char pathbuf[MAXPATHLEN + 1]; 187 188 /* 189 * If converting, set directory entry type. 190 */ 191 if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { 192 dirp->d_type = typemap[dirp->d_ino]; 193 ret |= ALTERED; 194 } 195 /* 196 * check for "." 197 */ 198 if (idesc->id_entryno != 0) 199 goto chk1; 200 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 201 if (dirp->d_ino != idesc->id_number) { 202 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 203 dirp->d_ino = idesc->id_number; 204 if (reply("FIX") == 1) 205 ret |= ALTERED; 206 } 207 if (newinofmt && dirp->d_type != DT_DIR) { 208 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 209 dirp->d_type = DT_DIR; 210 if (reply("FIX") == 1) 211 ret |= ALTERED; 212 } 213 goto chk1; 214 } 215 direrror(idesc->id_number, "MISSING '.'"); 216 proto.d_ino = idesc->id_number; 217 if (newinofmt) 218 proto.d_type = DT_DIR; 219 else 220 proto.d_type = 0; 221 proto.d_namlen = 1; 222 (void)strcpy(proto.d_name, "."); 223 # if BYTE_ORDER == LITTLE_ENDIAN 224 if (!newinofmt) { 225 u_char tmp; 226 227 tmp = proto.d_type; 228 proto.d_type = proto.d_namlen; 229 proto.d_namlen = tmp; 230 } 231 # endif 232 entrysize = DIRSIZ(0, &proto); 233 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 234 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 235 dirp->d_name); 236 } else if (dirp->d_reclen < entrysize) { 237 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 238 } else if (dirp->d_reclen < 2 * entrysize) { 239 proto.d_reclen = dirp->d_reclen; 240 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 241 if (reply("FIX") == 1) 242 ret |= ALTERED; 243 } else { 244 n = dirp->d_reclen - entrysize; 245 proto.d_reclen = entrysize; 246 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 247 idesc->id_entryno++; 248 lncntp[dirp->d_ino]--; 249 dirp = (struct direct *)((char *)(dirp) + entrysize); 250 bzero((char *)dirp, (size_t)n); 251 dirp->d_reclen = n; 252 if (reply("FIX") == 1) 253 ret |= ALTERED; 254 } 255 chk1: 256 if (idesc->id_entryno > 1) 257 goto chk2; 258 inp = getinoinfo(idesc->id_number); 259 proto.d_ino = inp->i_parent; 260 if (newinofmt) 261 proto.d_type = DT_DIR; 262 else 263 proto.d_type = 0; 264 proto.d_namlen = 2; 265 (void)strcpy(proto.d_name, ".."); 266 # if BYTE_ORDER == LITTLE_ENDIAN 267 if (!newinofmt) { 268 u_char tmp; 269 270 tmp = proto.d_type; 271 proto.d_type = proto.d_namlen; 272 proto.d_namlen = tmp; 273 } 274 # endif 275 entrysize = DIRSIZ(0, &proto); 276 if (idesc->id_entryno == 0) { 277 n = DIRSIZ(0, dirp); 278 if (dirp->d_reclen < n + entrysize) 279 goto chk2; 280 proto.d_reclen = dirp->d_reclen - n; 281 dirp->d_reclen = n; 282 idesc->id_entryno++; 283 lncntp[dirp->d_ino]--; 284 dirp = (struct direct *)((char *)(dirp) + n); 285 bzero((char *)dirp, (size_t)proto.d_reclen); 286 dirp->d_reclen = proto.d_reclen; 287 } 288 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 289 inp->i_dotdot = dirp->d_ino; 290 if (newinofmt && dirp->d_type != DT_DIR) { 291 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 292 dirp->d_type = DT_DIR; 293 if (reply("FIX") == 1) 294 ret |= ALTERED; 295 } 296 goto chk2; 297 } 298 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 299 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 300 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 301 dirp->d_name); 302 inp->i_dotdot = (ino_t)-1; 303 } else if (dirp->d_reclen < entrysize) { 304 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 305 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 306 inp->i_dotdot = (ino_t)-1; 307 } else if (inp->i_parent != 0) { 308 /* 309 * We know the parent, so fix now. 310 */ 311 inp->i_dotdot = inp->i_parent; 312 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 313 proto.d_reclen = dirp->d_reclen; 314 bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); 315 if (reply("FIX") == 1) 316 ret |= ALTERED; 317 } 318 idesc->id_entryno++; 319 if (dirp->d_ino != 0) 320 lncntp[dirp->d_ino]--; 321 return (ret|KEEPON); 322 chk2: 323 if (dirp->d_ino == 0) 324 return (ret|KEEPON); 325 if (dirp->d_namlen <= 2 && 326 dirp->d_name[0] == '.' && 327 idesc->id_entryno >= 2) { 328 if (dirp->d_namlen == 1) { 329 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 330 dirp->d_ino = 0; 331 if (reply("FIX") == 1) 332 ret |= ALTERED; 333 return (KEEPON | ret); 334 } 335 if (dirp->d_name[1] == '.') { 336 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 337 dirp->d_ino = 0; 338 if (reply("FIX") == 1) 339 ret |= ALTERED; 340 return (KEEPON | ret); 341 } 342 } 343 idesc->id_entryno++; 344 n = 0; 345 if (dirp->d_ino > maxino) { 346 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 347 n = reply("REMOVE"); 348 } else if (newinofmt && 349 ((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 350 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 351 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 352 dirp->d_ino = WINO; 353 dirp->d_type = DT_WHT; 354 if (reply("FIX") == 1) 355 ret |= ALTERED; 356 } else { 357 again: 358 switch (statemap[dirp->d_ino]) { 359 case USTATE: 360 if (idesc->id_entryno <= 2) 361 break; 362 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 363 n = reply("REMOVE"); 364 break; 365 366 case DCLEAR: 367 case FCLEAR: 368 if (idesc->id_entryno <= 2) 369 break; 370 if (statemap[dirp->d_ino] == FCLEAR) 371 errmsg = "DUP/BAD"; 372 else if (!preen) 373 errmsg = "ZERO LENGTH DIRECTORY"; 374 else { 375 n = 1; 376 break; 377 } 378 fileerror(idesc->id_number, dirp->d_ino, errmsg); 379 if ((n = reply("REMOVE")) == 1) 380 break; 381 dp = ginode(dirp->d_ino); 382 statemap[dirp->d_ino] = 383 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 384 lncntp[dirp->d_ino] = dp->di_nlink; 385 goto again; 386 387 case DSTATE: 388 if (statemap[idesc->id_number] == DFOUND) 389 statemap[dirp->d_ino] = DFOUND; 390 /* fall through */ 391 392 case DFOUND: 393 inp = getinoinfo(dirp->d_ino); 394 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 395 getpathname(pathbuf, idesc->id_number, 396 idesc->id_number); 397 getpathname(namebuf, dirp->d_ino, dirp->d_ino); 398 pwarn("%s %s %s\n", pathbuf, 399 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 400 namebuf); 401 if (preen) 402 printf(" (IGNORED)\n"); 403 else if ((n = reply("REMOVE")) == 1) 404 break; 405 } 406 if (idesc->id_entryno > 2) 407 inp->i_parent = idesc->id_number; 408 /* fall through */ 409 410 case FSTATE: 411 if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { 412 fileerror(idesc->id_number, dirp->d_ino, 413 "BAD TYPE VALUE"); 414 dirp->d_type = typemap[dirp->d_ino]; 415 if (reply("FIX") == 1) 416 ret |= ALTERED; 417 } 418 lncntp[dirp->d_ino]--; 419 break; 420 421 default: 422 errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 423 statemap[dirp->d_ino], dirp->d_ino); 424 } 425 } 426 if (n == 0) 427 return (ret|KEEPON); 428 dirp->d_ino = 0; 429 return (ret|KEEPON|ALTERED); 430 } 431 432 /* 433 * Routine to sort disk blocks. 434 */ 435 static int 436 blksort(arg1, arg2) 437 const void *arg1, *arg2; 438 { 439 440 return ((*(struct inoinfo **)arg1)->i_blks[0] - 441 (*(struct inoinfo **)arg2)->i_blks[0]); 442 } 443