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