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