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