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