1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if 0 33 #ifndef lint 34 static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 35 #endif /* not lint */ 36 #endif 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include <sys/param.h> 41 #include <sys/sysctl.h> 42 43 #include <ufs/ufs/dinode.h> 44 #include <ufs/ufs/dir.h> 45 #include <ufs/ffs/fs.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <stdint.h> 50 #include <string.h> 51 52 #include "fsck.h" 53 54 #define MINDIRSIZE (sizeof (struct dirtemplate)) 55 56 static int fix_extraneous(struct inoinfo *, struct inodesc *); 57 static int deleteentry(struct inodesc *); 58 static int blksort(const void *, const void *); 59 static int pass2check(struct inodesc *); 60 61 void 62 pass2(void) 63 { 64 struct inode ip; 65 union dinode *dp; 66 struct inoinfo **inpp, *inp; 67 struct inoinfo **inpend; 68 struct inodesc curino; 69 union dinode dino; 70 int i; 71 char pathbuf[MAXPATHLEN + 1]; 72 73 switch (inoinfo(UFS_ROOTINO)->ino_state) { 74 75 case USTATE: 76 pfatal("ROOT INODE UNALLOCATED"); 77 if (reply("ALLOCATE") == 0) { 78 ckfini(0); 79 exit(EEXIT); 80 } 81 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO) 82 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 83 break; 84 85 case DCLEAR: 86 pfatal("DUPS/BAD IN ROOT INODE"); 87 if (reply("REALLOCATE")) { 88 freedirino(UFS_ROOTINO, UFS_ROOTINO); 89 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != 90 UFS_ROOTINO) 91 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 92 break; 93 } 94 if (reply("CONTINUE") == 0) { 95 ckfini(0); 96 exit(EEXIT); 97 } 98 break; 99 100 case FSTATE: 101 case FCLEAR: 102 case FZLINK: 103 pfatal("ROOT INODE NOT DIRECTORY"); 104 if (reply("REALLOCATE")) { 105 freeino(UFS_ROOTINO); 106 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != 107 UFS_ROOTINO) 108 errx(EEXIT, "CANNOT ALLOCATE ROOT INODE"); 109 break; 110 } 111 if (reply("FIX") == 0) { 112 ckfini(0); 113 exit(EEXIT); 114 } 115 ginode(UFS_ROOTINO, &ip); 116 dp = ip.i_dp; 117 DIP_SET(dp, di_mode, DIP(dp, di_mode) & ~IFMT); 118 DIP_SET(dp, di_mode, DIP(dp, di_mode) | IFDIR); 119 inodirty(&ip); 120 irelse(&ip); 121 break; 122 123 case DSTATE: 124 case DZLINK: 125 break; 126 127 default: 128 errx(EEXIT, "BAD STATE %d FOR ROOT INODE", 129 inoinfo(UFS_ROOTINO)->ino_state); 130 } 131 inoinfo(UFS_ROOTINO)->ino_state = DFOUND; 132 inoinfo(UFS_WINO)->ino_state = FSTATE; 133 inoinfo(UFS_WINO)->ino_type = DT_WHT; 134 /* 135 * Sort the directory list into disk block order. 136 */ 137 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 138 /* 139 * Check the integrity of each directory. 140 */ 141 memset(&curino, 0, sizeof(struct inodesc)); 142 curino.id_type = DATA; 143 curino.id_func = pass2check; 144 inpend = &inpsort[inplast]; 145 for (inpp = inpsort; inpp < inpend; inpp++) { 146 if (got_siginfo) { 147 printf("%s: phase 2: dir %td of %d (%d%%)\n", cdevname, 148 inpp - inpsort, (int)inplast, 149 (int)((inpp - inpsort) * 100 / inplast)); 150 got_siginfo = 0; 151 } 152 if (got_sigalarm) { 153 setproctitle("%s p2 %d%%", cdevname, 154 (int)((inpp - inpsort) * 100 / inplast)); 155 got_sigalarm = 0; 156 } 157 inp = *inpp; 158 if (inp->i_isize == 0) 159 continue; 160 if (inp->i_isize < MINDIRSIZE) { 161 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 162 inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); 163 if (reply("FIX") == 1) { 164 ginode(inp->i_number, &ip); 165 DIP_SET(ip.i_dp, di_size, inp->i_isize); 166 inodirty(&ip); 167 irelse(&ip); 168 } 169 } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { 170 getpathname(pathbuf, inp->i_number, inp->i_number); 171 if (usedsoftdep) 172 pfatal("%s %s: LENGTH %jd NOT MULTIPLE OF %d", 173 "DIRECTORY", pathbuf, 174 (intmax_t)inp->i_isize, DIRBLKSIZ); 175 else 176 pwarn("%s %s: LENGTH %jd NOT MULTIPLE OF %d", 177 "DIRECTORY", pathbuf, 178 (intmax_t)inp->i_isize, DIRBLKSIZ); 179 if (preen) 180 printf(" (ADJUSTED)\n"); 181 inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); 182 if (preen || reply("ADJUST") == 1) { 183 ginode(inp->i_number, &ip); 184 DIP_SET(ip.i_dp, di_size, 185 roundup(inp->i_isize, DIRBLKSIZ)); 186 inodirty(&ip); 187 irelse(&ip); 188 } 189 } 190 dp = &dino; 191 memset(dp, 0, sizeof(struct ufs2_dinode)); 192 DIP_SET(dp, di_mode, IFDIR); 193 DIP_SET(dp, di_size, inp->i_isize); 194 for (i = 0; i < MIN(inp->i_numblks, UFS_NDADDR); i++) 195 DIP_SET(dp, di_db[i], inp->i_blks[i]); 196 if (inp->i_numblks > UFS_NDADDR) 197 for (i = 0; i < UFS_NIADDR; i++) 198 DIP_SET(dp, di_ib[i], 199 inp->i_blks[UFS_NDADDR + i]); 200 curino.id_number = inp->i_number; 201 curino.id_parent = inp->i_parent; 202 (void)ckinode(dp, &curino); 203 } 204 /* 205 * Now that the parents of all directories have been found, 206 * make another pass to verify the value of `..' 207 */ 208 for (inpp = inpsort; inpp < inpend; inpp++) { 209 inp = *inpp; 210 if (inp->i_parent == 0 || inp->i_isize == 0) 211 continue; 212 if (inoinfo(inp->i_parent)->ino_state == DFOUND && 213 INO_IS_DUNFOUND(inp->i_number)) { 214 inoinfo(inp->i_number)->ino_state = DFOUND; 215 check_dirdepth(inp); 216 } 217 if (inp->i_dotdot == inp->i_parent || 218 inp->i_dotdot == (ino_t)-1) 219 continue; 220 if (inp->i_dotdot == 0) { 221 inp->i_dotdot = inp->i_parent; 222 if (debug) 223 fileerror(inp->i_parent, inp->i_number, 224 "DEFERRED MISSING '..' FIX"); 225 (void)makeentry(inp->i_number, inp->i_parent, ".."); 226 inoinfo(inp->i_parent)->ino_linkcnt--; 227 continue; 228 } 229 /* 230 * Here we have: 231 * inp->i_number is directory with bad ".." in it. 232 * inp->i_dotdot is current value of "..". 233 * inp->i_parent is directory to which ".." should point. 234 */ 235 getpathname(pathbuf, inp->i_parent, inp->i_number); 236 printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n", 237 (uintmax_t)inp->i_number, pathbuf); 238 getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot); 239 printf("CURRENTLY POINTS TO I=%ju (%s), ", 240 (uintmax_t)inp->i_dotdot, pathbuf); 241 getpathname(pathbuf, inp->i_parent, inp->i_parent); 242 printf("SHOULD POINT TO I=%ju (%s)", 243 (uintmax_t)inp->i_parent, pathbuf); 244 if (cursnapshot != 0) { 245 /* 246 * We need to: 247 * setcwd(inp->i_number); 248 * setdotdot(inp->i_dotdot, inp->i_parent); 249 */ 250 cmd.value = inp->i_number; 251 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 252 &cmd, sizeof cmd) == -1) { 253 /* kernel lacks support for these functions */ 254 printf(" (IGNORED)\n"); 255 continue; 256 } 257 cmd.value = inp->i_dotdot; /* verify same value */ 258 cmd.size = inp->i_parent; /* new parent */ 259 if (sysctlbyname("vfs.ffs.setdotdot", 0, 0, 260 &cmd, sizeof cmd) == -1) { 261 printf(" (FIX FAILED: %s)\n", strerror(errno)); 262 continue; 263 } 264 printf(" (FIXED)\n"); 265 inoinfo(inp->i_parent)->ino_linkcnt--; 266 inp->i_dotdot = inp->i_parent; 267 continue; 268 } 269 if (preen) 270 printf(" (FIXED)\n"); 271 else if (reply("FIX") == 0) 272 continue; 273 inoinfo(inp->i_dotdot)->ino_linkcnt++; 274 inoinfo(inp->i_parent)->ino_linkcnt--; 275 inp->i_dotdot = inp->i_parent; 276 (void)changeino(inp->i_number, "..", inp->i_parent, 277 getinoinfo(inp->i_parent)->i_depth + 1); 278 } 279 /* 280 * Mark all the directories that can be found from the root. 281 */ 282 propagate(); 283 } 284 285 static int 286 pass2check(struct inodesc *idesc) 287 { 288 struct direct *dirp = idesc->id_dirp; 289 char dirname[MAXPATHLEN + 1]; 290 struct inoinfo *inp; 291 int n, entrysize, ret = 0; 292 struct inode ip; 293 union dinode *dp; 294 const char *errmsg; 295 struct direct proto, *newdirp; 296 297 /* 298 * check for "." 299 */ 300 if (idesc->id_entryno != 0) 301 goto chk1; 302 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 303 if (dirp->d_ino != idesc->id_number) { 304 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 305 if (reply("FIX") == 1) { 306 dirp->d_ino = idesc->id_number; 307 ret |= ALTERED; 308 } 309 } 310 if (dirp->d_type != DT_DIR) { 311 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 312 if (reply("FIX") == 1) { 313 dirp->d_type = DT_DIR; 314 ret |= ALTERED; 315 } 316 } 317 goto chk1; 318 } 319 proto.d_ino = idesc->id_number; 320 proto.d_type = DT_DIR; 321 proto.d_namlen = 1; 322 (void)strcpy(proto.d_name, "."); 323 entrysize = DIRSIZ(0, &proto); 324 direrror(idesc->id_number, "MISSING '.'"); 325 errmsg = "ADD '.' ENTRY"; 326 if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) { 327 /* Not enough space to add '.', replace first entry with '.' */ 328 if (dirp->d_ino != 0) { 329 pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n", 330 dirp->d_name); 331 errmsg = "REPLACE WITH '.'"; 332 } 333 if (reply(errmsg) == 0) 334 goto chk1; 335 proto.d_reclen = dirp->d_reclen; 336 memmove(dirp, &proto, (size_t)entrysize); 337 ret |= ALTERED; 338 } else { 339 /* Move over first entry and add '.' entry */ 340 if (reply(errmsg) == 0) 341 goto chk1; 342 newdirp = (struct direct *)((char *)(dirp) + entrysize); 343 dirp->d_reclen -= entrysize; 344 memmove(newdirp, dirp, dirp->d_reclen); 345 proto.d_reclen = entrysize; 346 memmove(dirp, &proto, (size_t)entrysize); 347 idesc->id_entryno++; 348 inoinfo(idesc->id_number)->ino_linkcnt--; 349 dirp = newdirp; 350 ret |= ALTERED; 351 } 352 chk1: 353 if (idesc->id_entryno > 1) 354 goto chk2; 355 inp = getinoinfo(idesc->id_number); 356 proto.d_ino = inp->i_parent; 357 proto.d_type = DT_DIR; 358 proto.d_namlen = 2; 359 (void)strcpy(proto.d_name, ".."); 360 entrysize = DIRSIZ(0, &proto); 361 if (idesc->id_entryno == 0) { 362 n = DIRSIZ(0, dirp); 363 if (dirp->d_reclen < n + entrysize) 364 goto chk2; 365 proto.d_reclen = dirp->d_reclen - n; 366 dirp->d_reclen = n; 367 idesc->id_entryno++; 368 inoinfo(dirp->d_ino)->ino_linkcnt--; 369 dirp = (struct direct *)((char *)(dirp) + n); 370 memset(dirp, 0, (size_t)proto.d_reclen); 371 dirp->d_reclen = proto.d_reclen; 372 } 373 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 374 if (dirp->d_ino > maxino) { 375 direrror(idesc->id_number, "BAD INODE NUMBER FOR '..'"); 376 /* 377 * If we know parent set it now, otherwise let it 378 * point to the root inode and it will get cleaned 379 * up later if that is not correct. 380 */ 381 if (inp->i_parent != 0) 382 dirp->d_ino = inp->i_parent; 383 else 384 dirp->d_ino = UFS_ROOTINO; 385 if (reply("FIX") == 1) 386 ret |= ALTERED; 387 } 388 inp->i_dotdot = dirp->d_ino; 389 if (dirp->d_type != DT_DIR) { 390 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 391 dirp->d_type = DT_DIR; 392 if (reply("FIX") == 1) 393 ret |= ALTERED; 394 } 395 goto chk2; 396 } 397 fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number, 398 idesc->id_number, "MISSING '..'"); 399 errmsg = "ADD '..' ENTRY"; 400 if (dirp->d_reclen < entrysize + DIRSIZ(0, dirp)) { 401 /* No space to add '..', replace second entry with '..' */ 402 if (dirp->d_ino != 0) { 403 pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 404 dirp->d_name); 405 errmsg = "REPLACE WITH '..'"; 406 } 407 if (reply(errmsg) == 0) { 408 inp->i_dotdot = (ino_t)-1; 409 goto chk2; 410 } 411 if (proto.d_ino == 0) { 412 /* Defer processing until parent known */ 413 idesc->id_entryno++; 414 if (debug) 415 printf("(FIX DEFERRED)\n"); 416 } 417 inp->i_dotdot = proto.d_ino; 418 proto.d_reclen = dirp->d_reclen; 419 memmove(dirp, &proto, (size_t)entrysize); 420 ret |= ALTERED; 421 } else { 422 /* Move over second entry and add '..' entry */ 423 if (reply(errmsg) == 0) { 424 inp->i_dotdot = (ino_t)-1; 425 goto chk2; 426 } 427 if (proto.d_ino == 0) { 428 /* Defer processing until parent known */ 429 idesc->id_entryno++; 430 if (debug) 431 printf("(FIX DEFERRED)\n"); 432 } 433 inp->i_dotdot = proto.d_ino; 434 if (dirp->d_ino == 0) { 435 proto.d_reclen = dirp->d_reclen; 436 memmove(dirp, &proto, (size_t)entrysize); 437 } else { 438 newdirp = (struct direct *)((char *)(dirp) + entrysize); 439 dirp->d_reclen -= entrysize; 440 memmove(newdirp, dirp, dirp->d_reclen); 441 proto.d_reclen = entrysize; 442 memmove(dirp, &proto, (size_t)entrysize); 443 if (dirp->d_ino != 0) { 444 idesc->id_entryno++; 445 inoinfo(dirp->d_ino)->ino_linkcnt--; 446 } 447 dirp = newdirp; 448 } 449 ret |= ALTERED; 450 } 451 chk2: 452 if (dirp->d_ino == 0) 453 return (ret|KEEPON); 454 if (dirp->d_namlen <= 2 && 455 dirp->d_name[0] == '.' && 456 idesc->id_entryno >= 2) { 457 if (dirp->d_namlen == 1) { 458 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 459 dirp->d_ino = 0; 460 if (reply("FIX") == 1) 461 ret |= ALTERED; 462 return (KEEPON | ret); 463 } 464 if (dirp->d_name[1] == '.') { 465 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 466 dirp->d_ino = 0; 467 if (reply("FIX") == 1) 468 ret |= ALTERED; 469 return (KEEPON | ret); 470 } 471 } 472 idesc->id_entryno++; 473 n = 0; 474 if (dirp->d_ino > maxino) { 475 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 476 n = reply("REMOVE"); 477 } else if (((dirp->d_ino == UFS_WINO && dirp->d_type != DT_WHT) || 478 (dirp->d_ino != UFS_WINO && dirp->d_type == DT_WHT))) { 479 fileerror(idesc->id_number, dirp->d_ino, "BAD WHITEOUT ENTRY"); 480 dirp->d_ino = UFS_WINO; 481 dirp->d_type = DT_WHT; 482 if (reply("FIX") == 1) 483 ret |= ALTERED; 484 } else { 485 again: 486 switch (inoinfo(dirp->d_ino)->ino_state) { 487 case USTATE: 488 if (idesc->id_entryno <= 2) 489 break; 490 fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); 491 n = reply("REMOVE"); 492 break; 493 494 case DCLEAR: 495 case FCLEAR: 496 if (idesc->id_entryno <= 2) 497 break; 498 if (inoinfo(dirp->d_ino)->ino_state == FCLEAR) 499 errmsg = "DUP/BAD"; 500 else if (!preen && !usedsoftdep) 501 errmsg = "ZERO LENGTH DIRECTORY"; 502 else if (cursnapshot == 0) { 503 n = 1; 504 break; 505 } else { 506 getpathname(dirname, idesc->id_number, 507 dirp->d_ino); 508 pwarn("ZERO LENGTH DIRECTORY %s I=%ju", 509 dirname, (uintmax_t)dirp->d_ino); 510 /* 511 * We need to: 512 * setcwd(idesc->id_parent); 513 * rmdir(dirp->d_name); 514 */ 515 cmd.value = idesc->id_number; 516 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 517 &cmd, sizeof cmd) == -1) { 518 /* kernel lacks support */ 519 printf(" (IGNORED)\n"); 520 n = 1; 521 break; 522 } 523 if (rmdir(dirp->d_name) == -1) { 524 printf(" (REMOVAL FAILED: %s)\n", 525 strerror(errno)); 526 n = 1; 527 break; 528 } 529 /* ".." reference to parent is removed */ 530 inoinfo(idesc->id_number)->ino_linkcnt--; 531 printf(" (REMOVED)\n"); 532 break; 533 } 534 fileerror(idesc->id_number, dirp->d_ino, errmsg); 535 if ((n = reply("REMOVE")) == 1) 536 break; 537 ginode(dirp->d_ino, &ip); 538 dp = ip.i_dp; 539 inoinfo(dirp->d_ino)->ino_state = 540 (DIP(dp, di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; 541 inoinfo(dirp->d_ino)->ino_linkcnt = DIP(dp, di_nlink); 542 irelse(&ip); 543 goto again; 544 545 case DSTATE: 546 case DZLINK: 547 if (inoinfo(idesc->id_number)->ino_state == DFOUND) 548 inoinfo(dirp->d_ino)->ino_state = DFOUND; 549 /* FALLTHROUGH */ 550 551 case DFOUND: 552 inp = getinoinfo(dirp->d_ino); 553 if (idesc->id_entryno > 2) { 554 if (inp->i_parent == 0) { 555 inp->i_parent = idesc->id_number; 556 check_dirdepth(inp); 557 } else if ((n = fix_extraneous(inp, idesc))) { 558 break; 559 } 560 } 561 /* FALLTHROUGH */ 562 563 case FSTATE: 564 case FZLINK: 565 if (dirp->d_type != inoinfo(dirp->d_ino)->ino_type) { 566 fileerror(idesc->id_number, dirp->d_ino, 567 "BAD TYPE VALUE"); 568 dirp->d_type = inoinfo(dirp->d_ino)->ino_type; 569 if (reply("FIX") == 1) 570 ret |= ALTERED; 571 } 572 inoinfo(dirp->d_ino)->ino_linkcnt--; 573 break; 574 575 default: 576 errx(EEXIT, "BAD STATE %d FOR INODE I=%ju", 577 inoinfo(dirp->d_ino)->ino_state, 578 (uintmax_t)dirp->d_ino); 579 } 580 } 581 if (n == 0) 582 return (ret|KEEPON); 583 dirp->d_ino = 0; 584 return (ret|KEEPON|ALTERED); 585 } 586 587 static int 588 fix_extraneous(struct inoinfo *inp, struct inodesc *idesc) 589 { 590 char *cp; 591 struct inode ip; 592 struct inodesc dotdesc; 593 char oldname[MAXPATHLEN + 1]; 594 char newname[MAXPATHLEN + 1]; 595 596 /* 597 * If we have not yet found "..", look it up now so we know 598 * which inode the directory itself believes is its parent. 599 */ 600 if (inp->i_dotdot == 0) { 601 memset(&dotdesc, 0, sizeof(struct inodesc)); 602 dotdesc.id_type = DATA; 603 dotdesc.id_number = idesc->id_dirp->d_ino; 604 dotdesc.id_func = findino; 605 dotdesc.id_name = strdup(".."); 606 ginode(dotdesc.id_number, &ip); 607 if ((ckinode(ip.i_dp, &dotdesc) & FOUND)) 608 inp->i_dotdot = dotdesc.id_parent; 609 irelse(&ip); 610 free(dotdesc.id_name); 611 } 612 /* 613 * We have the previously found old name (inp->i_parent) and the 614 * just found new name (idesc->id_number). We have five cases: 615 * 1) ".." is missing - can remove either name, choose to delete 616 * new one and let fsck create ".." pointing to old name. 617 * 2) Both new and old are in same directory, choose to delete 618 * the new name and let fsck fix ".." if it is wrong. 619 * 3) ".." does not point to the new name, so delete it and let 620 * fsck fix ".." to point to the old one if it is wrong. 621 * 4) ".." points to the old name only, so delete the new one. 622 * 5) ".." points to the new name only, so delete the old one. 623 * 624 * For cases 1-4 we eliminate the new name; 625 * for case 5 we eliminate the old name. 626 */ 627 if (inp->i_dotdot == 0 || /* Case 1 */ 628 idesc->id_number == inp->i_parent || /* Case 2 */ 629 inp->i_dotdot != idesc->id_number || /* Case 3 */ 630 inp->i_dotdot == inp->i_parent) { /* Case 4 */ 631 getpathname(newname, idesc->id_number, idesc->id_number); 632 if (strcmp(newname, "/") != 0) 633 strcat (newname, "/"); 634 strcat(newname, idesc->id_dirp->d_name); 635 getpathname(oldname, inp->i_number, inp->i_number); 636 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", 637 newname, oldname); 638 if (cursnapshot != 0) { 639 /* 640 * We need to 641 * setcwd(idesc->id_number); 642 * unlink(idesc->id_dirp->d_name); 643 */ 644 cmd.value = idesc->id_number; 645 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 646 &cmd, sizeof cmd) == -1) { 647 printf(" (IGNORED)\n"); 648 return (0); 649 } 650 cmd.value = (intptr_t)idesc->id_dirp->d_name; 651 cmd.size = inp->i_number; /* verify same name */ 652 if (sysctlbyname("vfs.ffs.unlink", 0, 0, 653 &cmd, sizeof cmd) == -1) { 654 printf(" (UNLINK FAILED: %s)\n", 655 strerror(errno)); 656 return (0); 657 } 658 printf(" (REMOVED)\n"); 659 return (0); 660 } 661 if (preen) { 662 printf(" (REMOVED)\n"); 663 return (1); 664 } 665 return (reply("REMOVE")); 666 } 667 /* 668 * None of the first four cases above, so must be case (5). 669 * Eliminate the old name and make the new the name the parent. 670 */ 671 getpathname(oldname, inp->i_parent, inp->i_number); 672 getpathname(newname, inp->i_number, inp->i_number); 673 pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s", oldname, 674 newname); 675 if (cursnapshot != 0) { 676 /* 677 * We need to 678 * setcwd(inp->i_parent); 679 * unlink(last component of oldname pathname); 680 */ 681 cmd.value = inp->i_parent; 682 if (sysctlbyname("vfs.ffs.setcwd", 0, 0, 683 &cmd, sizeof cmd) == -1) { 684 printf(" (IGNORED)\n"); 685 return (0); 686 } 687 if ((cp = strchr(oldname, '/')) == NULL) { 688 printf(" (IGNORED)\n"); 689 return (0); 690 } 691 cmd.value = (intptr_t)(cp + 1); 692 cmd.size = inp->i_number; /* verify same name */ 693 if (sysctlbyname("vfs.ffs.unlink", 0, 0, 694 &cmd, sizeof cmd) == -1) { 695 printf(" (UNLINK FAILED: %s)\n", 696 strerror(errno)); 697 return (0); 698 } 699 printf(" (REMOVED)\n"); 700 inp->i_parent = idesc->id_number; /* reparent to correct dir */ 701 return (0); 702 } 703 if (!preen && !reply("REMOVE")) 704 return (0); 705 memset(&dotdesc, 0, sizeof(struct inodesc)); 706 dotdesc.id_type = DATA; 707 dotdesc.id_number = inp->i_parent; /* directory in which name appears */ 708 dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */ 709 dotdesc.id_func = deleteentry; 710 ginode(dotdesc.id_number, &ip); 711 if ((ckinode(ip.i_dp, &dotdesc) & FOUND) && preen) 712 printf(" (REMOVED)\n"); 713 irelse(&ip); 714 inp->i_parent = idesc->id_number; /* reparent to correct directory */ 715 inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */ 716 return (0); 717 } 718 719 static int 720 deleteentry(struct inodesc *idesc) 721 { 722 struct direct *dirp = idesc->id_dirp; 723 724 if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent) 725 return (KEEPON); 726 dirp->d_ino = 0; 727 return (ALTERED|STOP|FOUND); 728 } 729 730 /* 731 * Routine to sort disk blocks. 732 */ 733 static int 734 blksort(const void *arg1, const void *arg2) 735 { 736 737 return ((*(struct inoinfo * const *)arg1)->i_blks[0] - 738 (*(struct inoinfo * const *)arg2)->i_blks[0]); 739 } 740