1 /* 2 * Copyright (c) 1980, 1986 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)pass2.c 5.13 (Berkeley) 06/01/90"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <ufs/dinode.h> 14 #include <ufs/fs.h> 15 #define KERNEL 16 #include <ufs/dir.h> 17 #undef KERNEL 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 /* 81 * Sort the directory list into disk block order. 82 */ 83 qsort((char *)inpsort, (int)inplast, sizeof *inpsort, blksort); 84 /* 85 * Check the integrity of each directory. 86 */ 87 bzero((char *)&curino, sizeof(struct inodesc)); 88 curino.id_type = DATA; 89 curino.id_func = pass2check; 90 dp = &dino; 91 inpend = &inpsort[inplast]; 92 for (inpp = inpsort; inpp < inpend; inpp++) { 93 inp = *inpp; 94 if (inp->i_isize == 0) 95 continue; 96 if (inp->i_isize < MINDIRSIZE) { 97 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 98 inp->i_isize = MINDIRSIZE; 99 if (reply("FIX") == 1) { 100 dp = ginode(inp->i_number); 101 dp->di_size = MINDIRSIZE; 102 inodirty(); 103 dp = &dino; 104 } 105 } 106 if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 107 getpathname(pathbuf, inp->i_number, inp->i_number); 108 pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", 109 pathbuf, inp->i_isize, DIRBLKSIZ); 110 if (preen) 111 printf(" (ADJUSTED)\n"); 112 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 113 if (preen || reply("ADJUST") == 1) { 114 dp = ginode(inp->i_number); 115 dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); 116 inodirty(); 117 dp = &dino; 118 } 119 } 120 dp->di_size = inp->i_isize; 121 bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], 122 (int)inp->i_numblks); 123 curino.id_number = inp->i_number; 124 curino.id_parent = inp->i_parent; 125 (void)ckinode(dp, &curino); 126 } 127 /* 128 * Now that the parents of all directories have been found, 129 * make another pass to verify the value of `..' 130 */ 131 for (inpp = inpsort; inpp < inpend; inpp++) { 132 inp = *inpp; 133 if (inp->i_parent == 0 || inp->i_isize == 0) 134 continue; 135 if (statemap[inp->i_parent] == DFOUND && 136 statemap[inp->i_number] == DSTATE) 137 statemap[inp->i_number] = DFOUND; 138 if (inp->i_dotdot == inp->i_parent || 139 inp->i_dotdot == (ino_t)-1) 140 continue; 141 if (inp->i_dotdot == 0) { 142 inp->i_dotdot = inp->i_parent; 143 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 144 if (reply("FIX") == 0) 145 continue; 146 makeentry(inp->i_number, inp->i_parent, ".."); 147 lncntp[inp->i_parent]--; 148 continue; 149 } 150 fileerror(inp->i_parent, inp->i_number, 151 "BAD INODE NUMBER FOR '..'"); 152 if (reply("FIX") == 0) 153 continue; 154 lncntp[inp->i_dotdot]++; 155 lncntp[inp->i_parent]--; 156 inp->i_dotdot = inp->i_parent; 157 (void)changeino(inp->i_number, "..", inp->i_parent); 158 } 159 /* 160 * Mark all the directories that can be found from the root. 161 */ 162 propagate(); 163 } 164 165 pass2check(idesc) 166 struct inodesc *idesc; 167 { 168 register struct direct *dirp = idesc->id_dirp; 169 register struct inoinfo *inp; 170 int n, entrysize, ret = 0; 171 struct dinode *dp; 172 char *errmsg; 173 struct direct proto; 174 char namebuf[MAXPATHLEN + 1]; 175 char pathbuf[MAXPATHLEN + 1]; 176 177 /* 178 * check for "." 179 */ 180 if (idesc->id_entryno != 0) 181 goto chk1; 182 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 183 if (dirp->d_ino != idesc->id_number) { 184 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 185 dirp->d_ino = idesc->id_number; 186 if (reply("FIX") == 1) 187 ret |= ALTERED; 188 } 189 goto chk1; 190 } 191 direrror(idesc->id_number, "MISSING '.'"); 192 proto.d_ino = idesc->id_number; 193 proto.d_namlen = 1; 194 (void)strcpy(proto.d_name, "."); 195 entrysize = DIRSIZ(&proto); 196 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 197 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 198 dirp->d_name); 199 } else if (dirp->d_reclen < entrysize) { 200 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 201 } else if (dirp->d_reclen < 2 * entrysize) { 202 proto.d_reclen = dirp->d_reclen; 203 bcopy((char *)&proto, (char *)dirp, entrysize); 204 if (reply("FIX") == 1) 205 ret |= ALTERED; 206 } else { 207 n = dirp->d_reclen - entrysize; 208 proto.d_reclen = entrysize; 209 bcopy((char *)&proto, (char *)dirp, entrysize); 210 idesc->id_entryno++; 211 lncntp[dirp->d_ino]--; 212 dirp = (struct direct *)((char *)(dirp) + entrysize); 213 bzero((char *)dirp, n); 214 dirp->d_reclen = n; 215 if (reply("FIX") == 1) 216 ret |= ALTERED; 217 } 218 chk1: 219 if (idesc->id_entryno > 1) 220 goto chk2; 221 inp = getinoinfo(idesc->id_number); 222 proto.d_ino = inp->i_parent; 223 proto.d_namlen = 2; 224 (void)strcpy(proto.d_name, ".."); 225 entrysize = DIRSIZ(&proto); 226 if (idesc->id_entryno == 0) { 227 n = DIRSIZ(dirp); 228 if (dirp->d_reclen < n + entrysize) 229 goto chk2; 230 proto.d_reclen = dirp->d_reclen - n; 231 dirp->d_reclen = n; 232 idesc->id_entryno++; 233 lncntp[dirp->d_ino]--; 234 dirp = (struct direct *)((char *)(dirp) + n); 235 bzero((char *)dirp, (int)proto.d_reclen); 236 dirp->d_reclen = proto.d_reclen; 237 } 238 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 239 inp->i_dotdot = dirp->d_ino; 240 goto chk2; 241 } 242 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 243 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 244 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 245 dirp->d_name); 246 inp->i_dotdot = (ino_t)-1; 247 } else if (dirp->d_reclen < entrysize) { 248 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 249 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 250 inp->i_dotdot = (ino_t)-1; 251 } else if (inp->i_parent != 0) { 252 /* 253 * We know the parent, so fix now. 254 */ 255 inp->i_dotdot = inp->i_parent; 256 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 257 proto.d_reclen = dirp->d_reclen; 258 bcopy((char *)&proto, (char *)dirp, entrysize); 259 if (reply("FIX") == 1) 260 ret |= ALTERED; 261 } 262 idesc->id_entryno++; 263 if (dirp->d_ino != 0) 264 lncntp[dirp->d_ino]--; 265 return (ret|KEEPON); 266 chk2: 267 if (dirp->d_ino == 0) 268 return (ret|KEEPON); 269 if (dirp->d_namlen <= 2 && 270 dirp->d_name[0] == '.' && 271 idesc->id_entryno >= 2) { 272 if (dirp->d_namlen == 1) { 273 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 274 dirp->d_ino = 0; 275 if (reply("FIX") == 1) 276 ret |= ALTERED; 277 return (KEEPON | ret); 278 } 279 if (dirp->d_name[1] == '.') { 280 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 281 dirp->d_ino = 0; 282 if (reply("FIX") == 1) 283 ret |= ALTERED; 284 return (KEEPON | ret); 285 } 286 } 287 idesc->id_entryno++; 288 n = 0; 289 if (dirp->d_ino > maxino || dirp->d_ino <= 0) { 290 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 291 n = reply("REMOVE"); 292 } else { 293 again: 294 switch (statemap[dirp->d_ino]) { 295 case USTATE: 296 if (idesc->id_entryno <= 2) 297 break; 298 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 299 n = reply("REMOVE"); 300 break; 301 302 case DCLEAR: 303 case FCLEAR: 304 if (idesc->id_entryno <= 2) 305 break; 306 if (statemap[dirp->d_ino] == DCLEAR) 307 errmsg = "ZERO LENGTH DIRECTORY"; 308 else 309 errmsg = "DUP/BAD"; 310 fileerror(idesc->id_number, dirp->d_ino, errmsg); 311 if ((n = reply("REMOVE")) == 1) 312 break; 313 dp = ginode(dirp->d_ino); 314 statemap[dirp->d_ino] = 315 (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; 316 lncntp[dirp->d_ino] = dp->di_nlink; 317 goto again; 318 319 case DSTATE: 320 if (statemap[idesc->id_number] == DFOUND) 321 statemap[dirp->d_ino] = DFOUND; 322 /* fall through */ 323 324 case DFOUND: 325 inp = getinoinfo(dirp->d_ino); 326 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 327 getpathname(pathbuf, idesc->id_number, 328 idesc->id_number); 329 getpathname(namebuf, dirp->d_ino, dirp->d_ino); 330 pwarn("%s %s %s\n", pathbuf, 331 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 332 namebuf); 333 if (preen) 334 printf(" (IGNORED)\n"); 335 else if ((n = reply("REMOVE")) == 1) 336 break; 337 } 338 if (idesc->id_entryno > 2) 339 inp->i_parent = idesc->id_number; 340 /* fall through */ 341 342 case FSTATE: 343 lncntp[dirp->d_ino]--; 344 break; 345 346 default: 347 errexit("BAD STATE %d FOR INODE I=%d", 348 statemap[dirp->d_ino], dirp->d_ino); 349 } 350 } 351 if (n == 0) 352 return (ret|KEEPON); 353 dirp->d_ino = 0; 354 return (ret|KEEPON|ALTERED); 355 } 356 357 /* 358 * Routine to sort disk blocks. 359 */ 360 blksort(inpp1, inpp2) 361 struct inoinfo **inpp1, **inpp2; 362 { 363 364 return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); 365 } 366