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