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