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