pass2.c revision 98542
179968Sobrien/* 279968Sobrien * Copyright (c) 1980, 1986, 1993 379968Sobrien * The Regents of the University of California. All rights reserved. 479968Sobrien * 579968Sobrien * Redistribution and use in source and binary forms, with or without 679968Sobrien * modification, are permitted provided that the following conditions 779968Sobrien * are met: 879968Sobrien * 1. Redistributions of source code must retain the above copyright 979968Sobrien * notice, this list of conditions and the following disclaimer. 1079968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1179968Sobrien * notice, this list of conditions and the following disclaimer in the 1279968Sobrien * documentation and/or other materials provided with the distribution. 13133936Sobrien * 3. All advertising materials mentioning features or use of this software 1479968Sobrien * must display the following acknowledgement: 1579968Sobrien * This product includes software developed by the University of 1679968Sobrien * California, Berkeley and its contributors. 1779968Sobrien * 4. Neither the name of the University nor the names of its contributors 1879968Sobrien * may be used to endorse or promote products derived from this software 1979968Sobrien * without specific prior written permission. 2079968Sobrien * 2179968Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2279968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2379968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2479968Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2579968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2679968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2779968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2879968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2979968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30133936Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31133936Sobrien * SUCH DAMAGE. 32133936Sobrien */ 33133936Sobrien 34133936Sobrien#ifndef lint 35133936Sobrien#if 0 36133936Sobrienstatic const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 37133936Sobrien#endif 38133936Sobrienstatic const char rcsid[] = 39133936Sobrien "$FreeBSD: head/sbin/fsck_ffs/pass2.c 98542 2002-06-21 06:18:05Z mckusick $"; 40133936Sobrien#endif /* not lint */ 41133936Sobrien 42133936Sobrien#include <sys/param.h> 43133936Sobrien 44133936Sobrien#include <ufs/ufs/dinode.h> 45133936Sobrien#include <ufs/ufs/dir.h> 46133936Sobrien#include <ufs/ffs/fs.h> 47133936Sobrien 48133936Sobrien#include <err.h> 49133936Sobrien#include <string.h> 50133936Sobrien 51133936Sobrien#include "fsck.h" 52133936Sobrien 53133936Sobrien#define MINDIRSIZE (sizeof (struct dirtemplate)) 54133936Sobrien 55108746Sobrienstatic int blksort(const void *, const void *); 56133936Sobrienstatic int pass2check(struct inodesc *); 5779968Sobrien 58108746Sobrienvoid 59108746Sobrienpass2(void) 60108746Sobrien{ 61108746Sobrien union dinode *dp; 62108746Sobrien struct inoinfo **inpp, *inp; 63108746Sobrien struct inoinfo **inpend; 64108746Sobrien struct inodesc curino; 65133936Sobrien union dinode dino; 66133936Sobrien int i; 67133936Sobrien char pathbuf[MAXPATHLEN + 1]; 68108746Sobrien 69108746Sobrien switch (inoinfo(ROOTINO)->ino_state) { 70133936Sobrien 71133936Sobrien case USTATE: 7279968Sobrien pfatal("ROOT INODE UNALLOCATED"); 7379968Sobrien if (reply("ALLOCATE") == 0) { 7479968Sobrien ckfini(0); 7579968Sobrien exit(EEXIT); 7679968Sobrien } 7779968Sobrien if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 7879968Sobrien errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 7979968Sobrien break; 8079968Sobrien 8179968Sobrien case DCLEAR: 8279968Sobrien pfatal("DUPS/BAD IN ROOT INODE"); 83133936Sobrien if (reply("REALLOCATE")) { 8479968Sobrien freeino(ROOTINO); 8579968Sobrien if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 8679968Sobrien errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 8779968Sobrien break; 8879968Sobrien } 8979968Sobrien if (reply("CONTINUE") == 0) { 9079968Sobrien ckfini(0); 9179968Sobrien exit(EEXIT); 9279968Sobrien } 9379968Sobrien break; 9479968Sobrien 9579968Sobrien case FSTATE: 9679968Sobrien case FCLEAR: 9779968Sobrien pfatal("ROOT INODE NOT DIRECTORY"); 98108746Sobrien if (reply("REALLOCATE")) { 9979968Sobrien freeino(ROOTINO); 10079968Sobrien if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 10179968Sobrien errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 10279968Sobrien break; 10379968Sobrien } 10479968Sobrien if (reply("FIX") == 0) { 10579968Sobrien ckfini(0); 10679968Sobrien exit(EEXIT); 10779968Sobrien } 10879968Sobrien dp = ginode(ROOTINO); 10979968Sobrien DIP(dp, di_mode) &= ~IFMT; 11079968Sobrien DIP(dp, di_mode) |= IFDIR; 11179968Sobrien inodirty(); 11279968Sobrien break; 11379968Sobrien 11479968Sobrien case DSTATE: 11579968Sobrien break; 11679968Sobrien 11779968Sobrien default: 11879968Sobrien errx(EEXIT, "BAD STATE %d FOR ROOT INODE", 11979968Sobrien inoinfo(ROOTINO)->ino_state); 12079968Sobrien } 12179968Sobrien inoinfo(ROOTINO)->ino_state = DFOUND; 12279968Sobrien inoinfo(WINO)->ino_state = FSTATE; 123133936Sobrien inoinfo(WINO)->ino_type = DT_WHT; 12479968Sobrien /* 12579968Sobrien * Sort the directory list into disk block order. 12679968Sobrien */ 12779968Sobrien qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 12879968Sobrien /* 12979968Sobrien * Check the integrity of each directory. 13079968Sobrien */ 13179968Sobrien memset(&curino, 0, sizeof(struct inodesc)); 13279968Sobrien curino.id_type = DATA; 13379968Sobrien curino.id_func = pass2check; 13479968Sobrien inpend = &inpsort[inplast]; 13579968Sobrien for (inpp = inpsort; inpp < inpend; inpp++) { 13679968Sobrien if (got_siginfo) { 13779968Sobrien printf("%s: phase 2: dir %d of %d (%d%%)\n", cdevname, 13879968Sobrien inpp - inpsort, (int)inplast, 13979968Sobrien (int)((inpp - inpsort) * 100 / inplast)); 14079968Sobrien got_siginfo = 0; 14179968Sobrien } 14279968Sobrien inp = *inpp; 14379968Sobrien if (inp->i_isize == 0) 14479968Sobrien continue; 14579968Sobrien if (inp->i_isize < MINDIRSIZE) { 14679968Sobrien direrror(inp->i_number, "DIRECTORY TOO SHORT"); 147133936Sobrien inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 148133936Sobrien if (reply("FIX") == 1) { 149133936Sobrien dp = ginode(inp->i_number); 150133936Sobrien DIP(dp, di_size) = inp->i_isize; 151133936Sobrien inodirty(); 152133936Sobrien } 153133936Sobrien } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 154133936Sobrien getpathname(pathbuf, inp->i_number, inp->i_number); 155133936Sobrien if (usedsoftdep) 156133936Sobrien pfatal("%s %s: LENGTH %d NOT MULTIPLE OF %d", 157161764Sobrien "DIRECTORY", pathbuf, inp->i_isize, 158161764Sobrien DIRBLKSIZ); 159161764Sobrien else 160161764Sobrien pwarn("%s %s: LENGTH %d NOT MULTIPLE OF %d", 161161764Sobrien "DIRECTORY", pathbuf, inp->i_isize, 162161764Sobrien DIRBLKSIZ); 163133936Sobrien if (preen) 164 printf(" (ADJUSTED)\n"); 165 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 166 if (preen || reply("ADJUST") == 1) { 167 dp = ginode(inp->i_number); 168 DIP(dp, di_size) = 169 roundup(inp->i_isize, DIRBLKSIZ); 170 inodirty(); 171 } 172 } 173 dp = &dino; 174 memset(dp, 0, sizeof(struct ufs2_dinode)); 175 DIP(dp, di_mode) = IFDIR; 176 DIP(dp, di_size) = inp->i_isize; 177 for (i = 0; 178 i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR); 179 i++) 180 DIP(dp, di_db[i]) = inp->i_blks[i]; 181 if (inp->i_numblks > NDADDR) 182 for (i = 0; i < NIADDR; i++) 183 DIP(dp, di_ib[i]) = inp->i_blks[NDADDR + i]; 184 curino.id_number = inp->i_number; 185 curino.id_parent = inp->i_parent; 186 (void)ckinode(dp, &curino); 187 } 188 /* 189 * Now that the parents of all directories have been found, 190 * make another pass to verify the value of `..' 191 */ 192 for (inpp = inpsort; inpp < inpend; inpp++) { 193 inp = *inpp; 194 if (inp->i_parent == 0 || inp->i_isize == 0) 195 continue; 196 if (inoinfo(inp->i_parent)->ino_state == DFOUND && 197 inoinfo(inp->i_number)->ino_state == DSTATE) 198 inoinfo(inp->i_number)->ino_state = DFOUND; 199 if (inp->i_dotdot == inp->i_parent || 200 inp->i_dotdot == (ino_t)-1) 201 continue; 202 if (inp->i_dotdot == 0) { 203 inp->i_dotdot = inp->i_parent; 204 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 205 if (reply("FIX") == 0) 206 continue; 207 (void)makeentry(inp->i_number, inp->i_parent, ".."); 208 inoinfo(inp->i_parent)->ino_linkcnt--; 209 continue; 210 } 211 fileerror(inp->i_parent, inp->i_number, 212 "BAD INODE NUMBER FOR '..'"); 213 if (reply("FIX") == 0) 214 continue; 215 inoinfo(inp->i_dotdot)->ino_linkcnt++; 216 inoinfo(inp->i_parent)->ino_linkcnt--; 217 inp->i_dotdot = inp->i_parent; 218 (void)changeino(inp->i_number, "..", inp->i_parent); 219 } 220 /* 221 * Mark all the directories that can be found from the root. 222 */ 223 propagate(); 224} 225 226static int 227pass2check(struct inodesc *idesc) 228{ 229 struct direct *dirp = idesc->id_dirp; 230 struct inoinfo *inp; 231 int n, entrysize, ret = 0; 232 union dinode *dp; 233 char *errmsg; 234 struct direct proto; 235 char namebuf[MAXPATHLEN + 1]; 236 char pathbuf[MAXPATHLEN + 1]; 237 238 /* 239 * check for "." 240 */ 241 if (idesc->id_entryno != 0) 242 goto chk1; 243 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 244 if (dirp->d_ino != idesc->id_number) { 245 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 246 dirp->d_ino = idesc->id_number; 247 if (reply("FIX") == 1) 248 ret |= ALTERED; 249 } 250 if (dirp->d_type != DT_DIR) { 251 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 252 dirp->d_type = DT_DIR; 253 if (reply("FIX") == 1) 254 ret |= ALTERED; 255 } 256 goto chk1; 257 } 258 direrror(idesc->id_number, "MISSING '.'"); 259 proto.d_ino = idesc->id_number; 260 proto.d_type = DT_DIR; 261 proto.d_namlen = 1; 262 (void)strcpy(proto.d_name, "."); 263 entrysize = DIRSIZ(0, &proto); 264 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 265 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 266 dirp->d_name); 267 } else if (dirp->d_reclen < entrysize) { 268 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 269 } else if (dirp->d_reclen < 2 * entrysize) { 270 proto.d_reclen = dirp->d_reclen; 271 memmove(dirp, &proto, (size_t)entrysize); 272 if (reply("FIX") == 1) 273 ret |= ALTERED; 274 } else { 275 n = dirp->d_reclen - entrysize; 276 proto.d_reclen = entrysize; 277 memmove(dirp, &proto, (size_t)entrysize); 278 idesc->id_entryno++; 279 inoinfo(dirp->d_ino)->ino_linkcnt--; 280 dirp = (struct direct *)((char *)(dirp) + entrysize); 281 memset(dirp, 0, (size_t)n); 282 dirp->d_reclen = n; 283 if (reply("FIX") == 1) 284 ret |= ALTERED; 285 } 286chk1: 287 if (idesc->id_entryno > 1) 288 goto chk2; 289 inp = getinoinfo(idesc->id_number); 290 proto.d_ino = inp->i_parent; 291 proto.d_type = DT_DIR; 292 proto.d_namlen = 2; 293 (void)strcpy(proto.d_name, ".."); 294 entrysize = DIRSIZ(0, &proto); 295 if (idesc->id_entryno == 0) { 296 n = DIRSIZ(0, dirp); 297 if (dirp->d_reclen < n + entrysize) 298 goto chk2; 299 proto.d_reclen = dirp->d_reclen - n; 300 dirp->d_reclen = n; 301 idesc->id_entryno++; 302 inoinfo(dirp->d_ino)->ino_linkcnt--; 303 dirp = (struct direct *)((char *)(dirp) + n); 304 memset(dirp, 0, (size_t)proto.d_reclen); 305 dirp->d_reclen = proto.d_reclen; 306 } 307 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 308 inp->i_dotdot = dirp->d_ino; 309 if (dirp->d_type != DT_DIR) { 310 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 311 dirp->d_type = DT_DIR; 312 if (reply("FIX") == 1) 313 ret |= ALTERED; 314 } 315 goto chk2; 316 } 317 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 318 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 319 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 320 dirp->d_name); 321 inp->i_dotdot = (ino_t)-1; 322 } else if (dirp->d_reclen < entrysize) { 323 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 324 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 325 inp->i_dotdot = (ino_t)-1; 326 } else if (inp->i_parent != 0) { 327 /* 328 * We know the parent, so fix now. 329 */ 330 inp->i_dotdot = inp->i_parent; 331 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 332 proto.d_reclen = dirp->d_reclen; 333 memmove(dirp, &proto, (size_t)entrysize); 334 if (reply("FIX") == 1) 335 ret |= ALTERED; 336 } 337 idesc->id_entryno++; 338 if (dirp->d_ino != 0) 339 inoinfo(dirp->d_ino)->ino_linkcnt--; 340 return (ret|KEEPON); 341chk2: 342 if (dirp->d_ino == 0) 343 return (ret|KEEPON); 344 if (dirp->d_namlen <= 2 && 345 dirp->d_name[0] == '.' && 346 idesc->id_entryno >= 2) { 347 if (dirp->d_namlen == 1) { 348 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 349 dirp->d_ino = 0; 350 if (reply("FIX") == 1) 351 ret |= ALTERED; 352 return (KEEPON | ret); 353 } 354 if (dirp->d_name[1] == '.') { 355 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 356 dirp->d_ino = 0; 357 if (reply("FIX") == 1) 358 ret |= ALTERED; 359 return (KEEPON | ret); 360 } 361 } 362 idesc->id_entryno++; 363 n = 0; 364 if (dirp->d_ino > maxino) { 365 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 366 n = reply("REMOVE"); 367 } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 368 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 369 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 370 dirp->d_ino = WINO; 371 dirp->d_type = DT_WHT; 372 if (reply("FIX") == 1) 373 ret |= ALTERED; 374 } else { 375again: 376 switch (inoinfo(dirp->d_ino)->ino_state) { 377 case USTATE: 378 if (idesc->id_entryno <= 2) 379 break; 380 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 381 n = reply("REMOVE"); 382 break; 383 384 case DCLEAR: 385 case FCLEAR: 386 if (idesc->id_entryno <= 2) 387 break; 388 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR) 389 errmsg = "DUP/BAD"; 390 else if (!preen && !usedsoftdep) 391 errmsg = "ZERO LENGTH DIRECTORY"; 392 else { 393 n = 1; 394 break; 395 } 396 fileerror(idesc->id_number, dirp->d_ino, errmsg); 397 if ((n = reply("REMOVE")) == 1) 398 break; 399 dp = ginode(dirp->d_ino); 400 inoinfo(dirp->d_ino)->ino_state = 401 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 402 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 403 goto again; 404 405 case DSTATE: 406 if (inoinfo(idesc->id_number)->ino_state == DFOUND) 407 inoinfo(dirp->d_ino)->ino_state = DFOUND; 408 /* fall through */ 409 410 case DFOUND: 411 inp = getinoinfo(dirp->d_ino); 412 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 413 getpathname(pathbuf, idesc->id_number, 414 idesc->id_number); 415 getpathname(namebuf, dirp->d_ino, dirp->d_ino); 416 pwarn("%s%s%s %s %s\n", pathbuf, 417 (strcmp(pathbuf, "/") == 0 ? "" : "/"), 418 dirp->d_name, 419 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 420 namebuf); 421 if (cursnapshot != 0) 422 break; 423 if (preen) { 424 printf(" (REMOVED)\n"); 425 n = 1; 426 break; 427 } 428 if ((n = reply("REMOVE")) == 1) 429 break; 430 } 431 if (idesc->id_entryno > 2) 432 inp->i_parent = idesc->id_number; 433 /* fall through */ 434 435 case FSTATE: 436 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 437 fileerror(idesc->id_number, dirp->d_ino, 438 "BAD TYPE VALUE"); 439 dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 440 if (reply("FIX") == 1) 441 ret |= ALTERED; 442 } 443 inoinfo(dirp->d_ino)->ino_linkcnt--; 444 break; 445 446 default: 447 errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 448 inoinfo(dirp->d_ino)->ino_state, dirp->d_ino); 449 } 450 } 451 if (n == 0) 452 return (ret|KEEPON); 453 dirp->d_ino = 0; 454 return (ret|KEEPON|ALTERED); 455} 456 457/* 458 * Routine to sort disk blocks. 459 */ 460static int 461blksort(const void *arg1, const void *arg2) 462{ 463 464 return ((*(struct inoinfo **)arg1)->i_blks[0] - 465 (*(struct inoinfo **)arg2)->i_blks[0]); 466} 467