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