pass2.c revision 221110
1344957Smarcel/* 2344957Smarcel * Copyright (c) 1980, 1986, 1993 3344957Smarcel * The Regents of the University of California. All rights reserved. 4344957Smarcel * 5344957Smarcel * Redistribution and use in source and binary forms, with or without 6344957Smarcel * modification, are permitted provided that the following conditions 7344957Smarcel * are met: 8344957Smarcel * 1. Redistributions of source code must retain the above copyright 9344957Smarcel * notice, this list of conditions and the following disclaimer. 10344957Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11344957Smarcel * notice, this list of conditions and the following disclaimer in the 12344957Smarcel * documentation and/or other materials provided with the distribution. 13344957Smarcel * 4. Neither the name of the University nor the names of its contributors 14344957Smarcel * may be used to endorse or promote products derived from this software 15344957Smarcel * without specific prior written permission. 16344957Smarcel * 17344957Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18344957Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19344957Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20344957Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21344957Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22344957Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23344957Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24344957Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25344957Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26344957Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27344957Smarcel * SUCH DAMAGE. 28344957Smarcel */ 29344957Smarcel 30344957Smarcel#if 0 31344957Smarcel#ifndef lint 32344957Smarcelstatic const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 33344957Smarcel#endif /* not lint */ 34344957Smarcel#endif 35344957Smarcel#include <sys/cdefs.h> 36344957Smarcel__FBSDID("$FreeBSD: head/sbin/fsck_ffs/pass2.c 221110 2011-04-27 02:55:03Z des $"); 37344957Smarcel 38344957Smarcel#include <sys/param.h> 39344957Smarcel#include <sys/sysctl.h> 40344957Smarcel 41344957Smarcel#include <ufs/ufs/dinode.h> 42344957Smarcel#include <ufs/ufs/dir.h> 43344957Smarcel#include <ufs/ffs/fs.h> 44344957Smarcel 45344957Smarcel#include <err.h> 46344957Smarcel#include <errno.h> 47344957Smarcel#include <stdint.h> 48344957Smarcel#include <string.h> 49344957Smarcel 50344957Smarcel#include "fsck.h" 51344957Smarcel 52344957Smarcel#define MINDIRSIZE (sizeof (struct dirtemplate)) 53344957Smarcel 54344957Smarcelstatic int fix_extraneous(struct inoinfo *, struct inodesc *); 55344957Smarcelstatic int deleteentry(struct inodesc *); 56344957Smarcelstatic int blksort(const void *, const void *); 57344957Smarcelstatic int pass2check(struct inodesc *); 58344957Smarcel 59344957Smarcelvoid 60344957Smarcelpass2(void) 61344957Smarcel{ 62344957Smarcel union dinode *dp; 63344957Smarcel struct inoinfo **inpp, *inp; 64344957Smarcel struct inoinfo **inpend; 65344957Smarcel struct inodesc curino; 66344957Smarcel union dinode dino; 67344957Smarcel int i; 68344957Smarcel char pathbuf[MAXPATHLEN + 1]; 69344957Smarcel 70344957Smarcel switch (inoinfo(ROOTINO)->ino_state) { 71344957Smarcel 72344957Smarcel case USTATE: 73344957Smarcel pfatal("ROOT INODE UNALLOCATED"); 74344957Smarcel if (reply("ALLOCATE") == 0) { 75344957Smarcel ckfini(0); 76344957Smarcel exit(EEXIT); 77344957Smarcel } 78344957Smarcel if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 79344957Smarcel errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 80344957Smarcel break; 81344957Smarcel 82344957Smarcel case DCLEAR: 83344957Smarcel pfatal("DUPS/BAD IN ROOT INODE"); 84344957Smarcel if (reply("REALLOCATE")) { 85344957Smarcel freeino(ROOTINO); 86344957Smarcel if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 87344957Smarcel errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 88344957Smarcel break; 89344957Smarcel } 90344957Smarcel if (reply("CONTINUE") == 0) { 91344957Smarcel ckfini(0); 92344957Smarcel exit(EEXIT); 93344957Smarcel } 94344957Smarcel break; 95344957Smarcel 96344957Smarcel case FSTATE: 97344957Smarcel case FCLEAR: 98344957Smarcel case FZLINK: 99344957Smarcel pfatal("ROOT INODE NOT DIRECTORY"); 100344957Smarcel if (reply("REALLOCATE")) { 101344957Smarcel freeino(ROOTINO); 102344957Smarcel if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 103344957Smarcel errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 104344957Smarcel break; 105344957Smarcel } 106344957Smarcel if (reply("FIX") == 0) { 107344957Smarcel ckfini(0); 108344957Smarcel exit(EEXIT); 109344957Smarcel } 110344957Smarcel dp = ginode(ROOTINO); 111344957Smarcel DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT); 112344957Smarcel DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR); 113344957Smarcel inodirty(); 114344957Smarcel break; 115344957Smarcel 116344957Smarcel case DSTATE: 117344957Smarcel case DZLINK: 118344957Smarcel break; 119344957Smarcel 120344957Smarcel default: 121344957Smarcel errx(EEXIT, "BAD STATE %d FOR ROOT INODE", 122344957Smarcel inoinfo(ROOTINO)->ino_state); 123344957Smarcel } 124344957Smarcel inoinfo(ROOTINO)->ino_state = DFOUND; 125344957Smarcel inoinfo(WINO)->ino_state = FSTATE; 126344957Smarcel inoinfo(WINO)->ino_type = DT_WHT; 127344957Smarcel /* 128344957Smarcel * Sort the directory list into disk block order. 129344957Smarcel */ 130344957Smarcel qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 131344957Smarcel /* 132344957Smarcel * Check the integrity of each directory. 133344957Smarcel */ 134344957Smarcel memset(&curino, 0, sizeof(struct inodesc)); 135344957Smarcel curino.id_type = DATA; 136344957Smarcel curino.id_func = pass2check; 137344957Smarcel inpend = &inpsort[inplast]; 138344957Smarcel for (inpp = inpsort; inpp < inpend; inpp++) { 139344957Smarcel if (got_siginfo) { 140344957Smarcel printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname, 141344957Smarcel inpp - inpsort, (int)inplast, 142344957Smarcel (int)((inpp - inpsort) * 100 / inplast)); 143344957Smarcel got_siginfo = 0; 144344957Smarcel } 145344957Smarcel if (got_sigalarm) { 146344957Smarcel setproctitle("%s p2 %d%%", cdevname, 147344957Smarcel (int)((inpp - inpsort) * 100 / inplast)); 148344957Smarcel got_sigalarm = 0; 149344957Smarcel } 150344957Smarcel inp = *inpp; 151344957Smarcel if (inp->i_isize == 0) 152344957Smarcel continue; 153344957Smarcel if (inp->i_isize < MINDIRSIZE) { 154344957Smarcel direrror(inp->i_number, "DIRECTORY TOO SHORT"); 155344957Smarcel inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 156344957Smarcel if (reply("FIX") == 1) { 157344957Smarcel dp = ginode(inp->i_number); 158344957Smarcel DIP_SET(dp, di_size, inp->i_isize); 159344957Smarcel inodirty(); 160344957Smarcel } 161344957Smarcel } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 162344957Smarcel getpathname(pathbuf, inp->i_number, inp->i_number); 163344957Smarcel if (usedsoftdep) 164344957Smarcel pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d", 165344957Smarcel "DIRECTORY", pathbuf, 166344957Smarcel (intmax_t)inp->i_isize, DIRBLKSIZ); 167344957Smarcel else 168344957Smarcel pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d", 169344957Smarcel "DIRECTORY", pathbuf, 170344957Smarcel (intmax_t)inp->i_isize, DIRBLKSIZ); 171344957Smarcel if (preen) 172344957Smarcel printf(" (ADJUSTED)\n"); 173344957Smarcel inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 174344957Smarcel if (preen || reply("ADJUST") == 1) { 175344957Smarcel dp = ginode(inp->i_number); 176344957Smarcel DIP_SET(dp, di_size, 177344957Smarcel roundup(inp->i_isize, DIRBLKSIZ)); 178344957Smarcel inodirty(); 179344957Smarcel } 180344957Smarcel } 181344957Smarcel dp = &dino; 182344957Smarcel memset(dp, 0, sizeof(struct ufs2_dinode)); 183344957Smarcel DIP_SET(dp, di_mode, IFDIR); 184344957Smarcel DIP_SET(dp, di_size, inp->i_isize); 185344957Smarcel for (i = 0; 186344957Smarcel i < (inp->i_numblks<NDADDR ? inp->i_numblks : NDADDR); 187344957Smarcel i++) 188344957Smarcel DIP_SET(dp, di_db[i], inp->i_blks[i]); 189344957Smarcel if (inp->i_numblks > NDADDR) 190344957Smarcel for (i = 0; i < NIADDR; i++) 191344957Smarcel DIP_SET(dp, di_ib[i], inp->i_blks[NDADDR + i]); 192344957Smarcel curino.id_number = inp->i_number; 193344957Smarcel curino.id_parent = inp->i_parent; 194344957Smarcel (void)ckinode(dp, &curino); 195344957Smarcel } 196344957Smarcel /* 197344957Smarcel * Now that the parents of all directories have been found, 198344957Smarcel * make another pass to verify the value of `..' 199344957Smarcel */ 200344957Smarcel for (inpp = inpsort; inpp < inpend; inpp++) { 201344957Smarcel inp = *inpp; 202344957Smarcel if (inp->i_parent == 0 || inp->i_isize == 0) 203344957Smarcel continue; 204344957Smarcel if (inoinfo(inp->i_parent)->ino_state == DFOUND && 205344957Smarcel INO_IS_DUNFOUND(inp->i_number)) 206344957Smarcel inoinfo(inp->i_number)->ino_state = DFOUND; 207344957Smarcel if (inp->i_dotdot == inp->i_parent || 208344957Smarcel inp->i_dotdot == (ino_t)-1) 209344957Smarcel continue; 210344957Smarcel if (inp->i_dotdot == 0) { 211344957Smarcel inp->i_dotdot = inp->i_parent; 212344957Smarcel fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 213344957Smarcel if (reply("FIX") == 0) 214344957Smarcel continue; 215344957Smarcel (void)makeentry(inp->i_number, inp->i_parent, ".."); 216344957Smarcel inoinfo(inp->i_parent)->ino_linkcnt--; 217344957Smarcel continue; 218344957Smarcel } 219344957Smarcel /* 220344957Smarcel * Here we have: 221344957Smarcel * inp->i_number is directory with bad ".." in it. 222344957Smarcel * inp->i_dotdot is current value of "..". 223344957Smarcel * inp->i_parent is directory to which ".." should point. 224344957Smarcel */ 225344957Smarcel getpathname(pathbuf, inp->i_parent, inp->i_number); 226344957Smarcel printf("BAD INODE NUMBER FOR '..' in DIR I=%d (%s)\n", 227344957Smarcel inp->i_number, pathbuf); 228344957Smarcel getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot); 229344957Smarcel printf("CURRENTLY POINTS TO I=%d (%s), ", inp->i_dotdot, 230344957Smarcel pathbuf); 231344957Smarcel getpathname(pathbuf, inp->i_parent, inp->i_parent); 232344957Smarcel printf("SHOULD POINT TO I=%d (%s)", inp->i_parent, pathbuf); 233344957Smarcel if (cursnapshot != 0) { 234344957Smarcel /* 235344957Smarcel * We need to: 236344957Smarcel * setcwd(inp->i_number); 237344957Smarcel * setdotdot(inp->i_dotdot, inp->i_parent); 238344957Smarcel */ 239344957Smarcel cmd.value = inp->i_number; 240344957Smarcel if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 241344957Smarcel &cmd, sizeof cmd) == -1) { 242344957Smarcel /* kernel lacks support for these functions */ 243344957Smarcel printf(" (IGNORED)\n"); 244344957Smarcel continue; 245344957Smarcel } 246344957Smarcel cmd.value = inp->i_dotdot; /* verify same value */ 247344957Smarcel cmd.size = inp->i_parent; /* new parent */ 248344957Smarcel if (sysctlbyname("vfs.ffs.setdotdot", 0, 0, 249344957Smarcel &cmd, sizeof cmd) == -1) { 250344957Smarcel printf(" (FIX FAILED: %s)\n", strerror(errno)); 251344957Smarcel continue; 252344957Smarcel } 253344957Smarcel printf(" (FIXED)\n"); 254344957Smarcel inoinfo(inp->i_parent)->ino_linkcnt--; 255344957Smarcel inp->i_dotdot = inp->i_parent; 256344957Smarcel continue; 257344957Smarcel } 258344957Smarcel if (preen) 259344957Smarcel printf(" (FIXED)\n"); 260344957Smarcel else if (reply("FIX") == 0) 261344957Smarcel continue; 262344957Smarcel inoinfo(inp->i_dotdot)->ino_linkcnt++; 263344957Smarcel inoinfo(inp->i_parent)->ino_linkcnt--; 264344957Smarcel inp->i_dotdot = inp->i_parent; 265344957Smarcel (void)changeino(inp->i_number, "..", inp->i_parent); 266344957Smarcel } 267344957Smarcel /* 268344957Smarcel * Mark all the directories that can be found from the root. 269344957Smarcel */ 270344957Smarcel propagate(); 271344957Smarcel} 272344957Smarcel 273344957Smarcelstatic int 274344957Smarcelpass2check(struct inodesc *idesc) 275344957Smarcel{ 276344957Smarcel struct direct *dirp = idesc->id_dirp; 277344957Smarcel char dirname[MAXPATHLEN + 1]; 278344957Smarcel struct inoinfo *inp; 279344957Smarcel int n, entrysize, ret = 0; 280344957Smarcel union dinode *dp; 281344957Smarcel const char *errmsg; 282344957Smarcel struct direct proto; 283344957Smarcel 284344957Smarcel /* 285344957Smarcel * check for "." 286344957Smarcel */ 287344957Smarcel if (dirp->d_ino > maxino) 288344957Smarcel goto chk2; 289344957Smarcel if (idesc->id_entryno != 0) 290344957Smarcel goto chk1; 291344957Smarcel if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 292344957Smarcel if (dirp->d_ino != idesc->id_number) { 293344957Smarcel direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 294344957Smarcel dirp->d_ino = idesc->id_number; 295344957Smarcel if (reply("FIX") == 1) 296344957Smarcel ret |= ALTERED; 297344957Smarcel } 298344957Smarcel if (dirp->d_type != DT_DIR) { 299344957Smarcel direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 300344957Smarcel dirp->d_type = DT_DIR; 301344957Smarcel if (reply("FIX") == 1) 302344957Smarcel ret |= ALTERED; 303344957Smarcel } 304344957Smarcel goto chk1; 305344957Smarcel } 306344957Smarcel direrror(idesc->id_number, "MISSING '.'"); 307344957Smarcel proto.d_ino = idesc->id_number; 308344957Smarcel proto.d_type = DT_DIR; 309344957Smarcel proto.d_namlen = 1; 310344957Smarcel (void)strcpy(proto.d_name, "."); 311344957Smarcel entrysize = DIRSIZ(0, &proto); 312344957Smarcel if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 313344957Smarcel pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 314344957Smarcel dirp->d_name); 315344957Smarcel } else if (dirp->d_reclen < entrysize) { 316344957Smarcel pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 317344957Smarcel } else if (dirp->d_reclen < 2 * entrysize) { 318344957Smarcel proto.d_reclen = dirp->d_reclen; 319344957Smarcel memmove(dirp, &proto, (size_t)entrysize); 320344957Smarcel if (reply("FIX") == 1) 321344957Smarcel ret |= ALTERED; 322344957Smarcel } else { 323 n = dirp->d_reclen - entrysize; 324 proto.d_reclen = entrysize; 325 memmove(dirp, &proto, (size_t)entrysize); 326 idesc->id_entryno++; 327 inoinfo(dirp->d_ino)->ino_linkcnt--; 328 dirp = (struct direct *)((char *)(dirp) + entrysize); 329 memset(dirp, 0, (size_t)n); 330 dirp->d_reclen = n; 331 if (reply("FIX") == 1) 332 ret |= ALTERED; 333 } 334chk1: 335 if (idesc->id_entryno > 1) 336 goto chk2; 337 inp = getinoinfo(idesc->id_number); 338 proto.d_ino = inp->i_parent; 339 proto.d_type = DT_DIR; 340 proto.d_namlen = 2; 341 (void)strcpy(proto.d_name, ".."); 342 entrysize = DIRSIZ(0, &proto); 343 if (idesc->id_entryno == 0) { 344 n = DIRSIZ(0, dirp); 345 if (dirp->d_reclen < n + entrysize) 346 goto chk2; 347 proto.d_reclen = dirp->d_reclen - n; 348 dirp->d_reclen = n; 349 idesc->id_entryno++; 350 inoinfo(dirp->d_ino)->ino_linkcnt--; 351 dirp = (struct direct *)((char *)(dirp) + n); 352 memset(dirp, 0, (size_t)proto.d_reclen); 353 dirp->d_reclen = proto.d_reclen; 354 } 355 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 356 inp->i_dotdot = dirp->d_ino; 357 if (dirp->d_type != DT_DIR) { 358 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 359 dirp->d_type = DT_DIR; 360 if (reply("FIX") == 1) 361 ret |= ALTERED; 362 } 363 goto chk2; 364 } 365 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { 366 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 367 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 368 dirp->d_name); 369 inp->i_dotdot = (ino_t)-1; 370 } else if (dirp->d_reclen < entrysize) { 371 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 372 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 373 inp->i_dotdot = (ino_t)-1; 374 } else if (inp->i_parent != 0) { 375 /* 376 * We know the parent, so fix now. 377 */ 378 inp->i_dotdot = inp->i_parent; 379 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 380 proto.d_reclen = dirp->d_reclen; 381 memmove(dirp, &proto, (size_t)entrysize); 382 if (reply("FIX") == 1) 383 ret |= ALTERED; 384 } 385 idesc->id_entryno++; 386 if (dirp->d_ino != 0) 387 inoinfo(dirp->d_ino)->ino_linkcnt--; 388 return (ret|KEEPON); 389chk2: 390 if (dirp->d_ino == 0) 391 return (ret|KEEPON); 392 if (dirp->d_namlen <= 2 && 393 dirp->d_name[0] == '.' && 394 idesc->id_entryno >= 2) { 395 if (dirp->d_namlen == 1) { 396 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 397 dirp->d_ino = 0; 398 if (reply("FIX") == 1) 399 ret |= ALTERED; 400 return (KEEPON | ret); 401 } 402 if (dirp->d_name[1] == '.') { 403 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 404 dirp->d_ino = 0; 405 if (reply("FIX") == 1) 406 ret |= ALTERED; 407 return (KEEPON | ret); 408 } 409 } 410 idesc->id_entryno++; 411 n = 0; 412 if (dirp->d_ino > maxino) { 413 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 414 n = reply("REMOVE"); 415 } else if (((dirp->d_ino == WINO && dirp->d_type != DT_WHT) || 416 (dirp->d_ino != WINO && dirp->d_type == DT_WHT))) { 417 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 418 dirp->d_ino = WINO; 419 dirp->d_type = DT_WHT; 420 if (reply("FIX") == 1) 421 ret |= ALTERED; 422 } else { 423again: 424 switch (inoinfo(dirp->d_ino)->ino_state) { 425 case USTATE: 426 if (idesc->id_entryno <= 2) 427 break; 428 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 429 n = reply("REMOVE"); 430 break; 431 432 case DCLEAR: 433 case FCLEAR: 434 if (idesc->id_entryno <= 2) 435 break; 436 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR) 437 errmsg = "DUP/BAD"; 438 else if (!preen && !usedsoftdep) 439 errmsg = "ZERO LENGTH DIRECTORY"; 440 else if (cursnapshot == 0) { 441 n = 1; 442 break; 443 } else { 444 getpathname(dirname, idesc->id_number, 445 dirp->d_ino); 446 pwarn("ZERO LENGTH DIRECTORY %s I=%d", 447 dirname, dirp->d_ino); 448 /* 449 * We need to: 450 * setcwd(idesc->id_parent); 451 * rmdir(dirp->d_name); 452 */ 453 cmd.value = idesc->id_number; 454 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 455 &cmd, sizeof cmd) == -1) { 456 /* kernel lacks support */ 457 printf(" (IGNORED)\n"); 458 n = 1; 459 break; 460 } 461 if (rmdir(dirp->d_name) == -1) { 462 printf(" (REMOVAL FAILED: %s)\n", 463 strerror(errno)); 464 n = 1; 465 break; 466 } 467 /* ".." reference to parent is removed */ 468 inoinfo(idesc->id_number)->ino_linkcnt--; 469 printf(" (REMOVED)\n"); 470 break; 471 } 472 fileerror(idesc->id_number, dirp->d_ino, errmsg); 473 if ((n = reply("REMOVE")) == 1) 474 break; 475 dp = ginode(dirp->d_ino); 476 inoinfo(dirp->d_ino)->ino_state = 477 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 478 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 479 goto again; 480 481 case DSTATE: 482 case DZLINK: 483 if (inoinfo(idesc->id_number)->ino_state == DFOUND) 484 inoinfo(dirp->d_ino)->ino_state = DFOUND; 485 /* FALLTHROUGH */ 486 487 case DFOUND: 488 inp = getinoinfo(dirp->d_ino); 489 if (idesc->id_entryno > 2) { 490 if (inp->i_parent == 0) 491 inp->i_parent = idesc->id_number; 492 else if ((n = fix_extraneous(inp, idesc)) == 1) 493 break; 494 } 495 /* FALLTHROUGH */ 496 497 case FSTATE: 498 case FZLINK: 499 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 500 fileerror(idesc->id_number, dirp->d_ino, 501 "BAD TYPE VALUE"); 502 dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 503 if (reply("FIX") == 1) 504 ret |= ALTERED; 505 } 506 inoinfo(dirp->d_ino)->ino_linkcnt--; 507 break; 508 509 default: 510 errx(EEXIT, "BAD STATE %d FOR INODE I=%d", 511 inoinfo(dirp->d_ino)->ino_state, dirp->d_ino); 512 } 513 } 514 if (n == 0) 515 return (ret|KEEPON); 516 dirp->d_ino = 0; 517 return (ret|KEEPON|ALTERED); 518} 519 520static int 521fix_extraneous(struct inoinfo *inp, struct inodesc *idesc) 522{ 523 char *cp; 524 struct inodesc dotdesc; 525 char oldname[MAXPATHLEN + 1]; 526 char newname[MAXPATHLEN + 1]; 527 528 /* 529 * If we have not yet found "..", look it up now so we know 530 * which inode the directory itself believes is its parent. 531 */ 532 if (inp->i_dotdot == 0) { 533 memset(&dotdesc, 0, sizeof(struct inodesc)); 534 dotdesc.id_type = DATA; 535 dotdesc.id_number = idesc->id_dirp->d_ino; 536 dotdesc.id_func = findino; 537 dotdesc.id_name = strdup(".."); 538 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND)) 539 inp->i_dotdot = dotdesc.id_parent; 540 } 541 /* 542 * We have the previously found old name (inp->i_parent) and the 543 * just found new name (idesc->id_number). We have five cases: 544 * 1) ".." is missing - can remove either name, choose to delete 545 * new one and let fsck create ".." pointing to old name. 546 * 2) Both new and old are in same directory, choose to delete 547 * the new name and let fsck fix ".." if it is wrong. 548 * 3) ".." does not point to the new name, so delete it and let 549 * fsck fix ".." to point to the old one if it is wrong. 550 * 4) ".." points to the old name only, so delete the new one. 551 * 5) ".." points to the new name only, so delete the old one. 552 * 553 * For cases 1-4 we eliminate the new name; 554 * for case 5 we eliminate the old name. 555 */ 556 if (inp->i_dotdot == 0 || /* Case 1 */ 557 idesc->id_number == inp->i_parent || /* Case 2 */ 558 inp->i_dotdot != idesc->id_number || /* Case 3 */ 559 inp->i_dotdot == inp->i_parent) { /* Case 4 */ 560 getpathname(newname, idesc->id_number, idesc->id_number); 561 if (strcmp(newname, "/") != 0) 562 strcat (newname, "/"); 563 strcat(newname, idesc->id_dirp->d_name); 564 getpathname(oldname, inp->i_number, inp->i_number); 565 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", 566 newname, oldname); 567 if (cursnapshot != 0) { 568 /* 569 * We need to 570 * setcwd(idesc->id_number); 571 * unlink(idesc->id_dirp->d_name); 572 */ 573 cmd.value = idesc->id_number; 574 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 575 &cmd, sizeof cmd) == -1) { 576 printf(" (IGNORED)\n"); 577 return (0); 578 } 579 cmd.value = (intptr_t)idesc->id_dirp->d_name; 580 cmd.size = inp->i_number; /* verify same name */ 581 if (sysctlbyname("vfs.ffs.unlink", 0, 0, 582 &cmd, sizeof cmd) == -1) { 583 printf(" (UNLINK FAILED: %s)\n", 584 strerror(errno)); 585 return (0); 586 } 587 printf(" (REMOVED)\n"); 588 return (0); 589 } 590 if (preen) { 591 printf(" (REMOVED)\n"); 592 return (1); 593 } 594 return (reply("REMOVE")); 595 } 596 /* 597 * None of the first four cases above, so must be case (5). 598 * Eliminate the old name and make the new the name the parent. 599 */ 600 getpathname(oldname, inp->i_parent, inp->i_number); 601 getpathname(newname, inp->i_number, inp->i_number); 602 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname, 603 newname); 604 if (cursnapshot != 0) { 605 /* 606 * We need to 607 * setcwd(inp->i_parent); 608 * unlink(last component of oldname pathname); 609 */ 610 cmd.value = inp->i_parent; 611 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 612 &cmd, sizeof cmd) == -1) { 613 printf(" (IGNORED)\n"); 614 return (0); 615 } 616 if ((cp = rindex(oldname, '/')) == NULL) { 617 printf(" (IGNORED)\n"); 618 return (0); 619 } 620 cmd.value = (intptr_t)(cp + 1); 621 cmd.size = inp->i_number; /* verify same name */ 622 if (sysctlbyname("vfs.ffs.unlink", 0, 0, 623 &cmd, sizeof cmd) == -1) { 624 printf(" (UNLINK FAILED: %s)\n", 625 strerror(errno)); 626 return (0); 627 } 628 printf(" (REMOVED)\n"); 629 inp->i_parent = idesc->id_number; /* reparent to correct dir */ 630 return (0); 631 } 632 if (!preen && !reply("REMOVE")) 633 return (0); 634 memset(&dotdesc, 0, sizeof(struct inodesc)); 635 dotdesc.id_type = DATA; 636 dotdesc.id_number = inp->i_parent; /* directory in which name appears */ 637 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */ 638 dotdesc.id_func = deleteentry; 639 if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen) 640 printf(" (REMOVED)\n"); 641 inp->i_parent = idesc->id_number; /* reparent to correct directory */ 642 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */ 643 return (0); 644} 645 646static int 647deleteentry(struct inodesc *idesc) 648{ 649 struct direct *dirp = idesc->id_dirp; 650 651 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent) 652 return (KEEPON); 653 dirp->d_ino = 0; 654 return (ALTERED|STOP|FOUND); 655} 656 657/* 658 * Routine to sort disk blocks. 659 */ 660static int 661blksort(const void *arg1, const void *arg2) 662{ 663 664 return ((*(struct inoinfo * const *)arg1)->i_blks[0] - 665 (*(struct inoinfo * const *)arg2)->i_blks[0]); 666} 667