1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1999,2000 by Sun Microsystems, Inc. 24 * All rights reserved. 25 * Copyright (c) 2016 by Delphix. All rights reserved. 26 * Copyright 2024 MNX Cloud, Inc. 27 */ 28 29 /* 30 * fsck_pcfs -- routines for manipulating clusters. 31 */ 32 #include <stdio.h> 33 #include <stdbool.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <stdlib.h> 37 #include <libintl.h> 38 #include <errno.h> 39 #include <sys/dktp/fdisk.h> 40 #include <sys/fs/pc_fs.h> 41 #include <sys/fs/pc_dir.h> 42 #include <sys/fs/pc_label.h> 43 #include "getresponse.h" 44 #include "pcfs_common.h" 45 #include "fsck_pcfs.h" 46 47 extern ClusterContents TheRootDir; 48 extern off64_t FirstClusterOffset; 49 extern off64_t PartitionOffset; 50 extern int32_t BytesPerCluster; 51 extern int32_t TotalClusters; 52 extern int32_t LastCluster; 53 extern int32_t RootDirSize; 54 extern int32_t FATSize; 55 extern bpb_t TheBIOSParameterBlock; 56 extern short FATEntrySize; 57 extern int RootDirModified; 58 extern int OkayToRelink; 59 extern int ReadOnly; 60 extern int IsFAT32; 61 extern int Verbose; 62 63 static struct pcdir BlankPCDIR; 64 static CachedCluster *ClusterCache; 65 static ClusterInfo **InUse; 66 static int32_t ReservedClusterCount; 67 static int32_t AllocedClusterCount; 68 static int32_t FreeClusterCount; 69 static int32_t BadClusterCount; 70 71 /* 72 * Internal statistics 73 */ 74 static int32_t CachedClusterCount; 75 76 int32_t HiddenClusterCount; 77 int32_t FileClusterCount; 78 int32_t DirClusterCount; 79 int32_t HiddenFileCount; 80 int32_t FileCount; 81 int32_t DirCount; 82 83 static int32_t orphanSizeLookup(int32_t clusterNum); 84 85 static void 86 freeNameInfo(int32_t clusterNum) 87 { 88 /* silent failure for bogus clusters */ 89 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 90 return; 91 if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) { 92 if (InUse[clusterNum - FIRST_CLUSTER]->path->references > 1) { 93 InUse[clusterNum - FIRST_CLUSTER]->path->references--; 94 } else { 95 free(InUse[clusterNum - FIRST_CLUSTER]->path->fullName); 96 free(InUse[clusterNum - FIRST_CLUSTER]->path); 97 } 98 InUse[clusterNum - FIRST_CLUSTER]->path = NULL; 99 } 100 } 101 102 static void 103 printOrphanPath(int32_t clusterNum) 104 { 105 /* silent failure for bogus clusters */ 106 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 107 return; 108 if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) { 109 (void) printf(gettext("\nOrphaned allocation units originally " 110 "allocated to:\n")); 111 (void) printf("%s\n", 112 InUse[clusterNum - FIRST_CLUSTER]->path->fullName); 113 freeNameInfo(clusterNum); 114 } else { 115 (void) printf(gettext("\nOrphaned allocation units originally " 116 "allocated to an unknown file or directory:\n")); 117 (void) printf(gettext("Orphaned chain begins with allocation " 118 "unit %d.\n"), clusterNum); 119 } 120 } 121 122 static void 123 printOrphanSize(int32_t clusterNum) 124 { 125 int32_t size = orphanSizeLookup(clusterNum); 126 127 if (size > 0) { 128 (void) printf(gettext("%d bytes in the orphaned chain of " 129 "allocation units.\n"), size); 130 if (Verbose) { 131 (void) printf(gettext("[Starting at allocation " 132 "unit %d]\n"), clusterNum); 133 } 134 } 135 } 136 137 static void 138 printOrphanInfo(int32_t clusterNum) 139 { 140 printOrphanPath(clusterNum); 141 printOrphanSize(clusterNum); 142 } 143 144 static bool 145 askAboutFreeing(int32_t clusterNum) 146 { 147 /* 148 * If it is not OkayToRelink, we haven't already printed the size 149 * of the orphaned chain. 150 */ 151 if (!OkayToRelink) 152 printOrphanInfo(clusterNum); 153 /* 154 * If we are in preen mode, preenBail won't return. 155 */ 156 preenBail("Need user confirmation to free orphaned chain.\n"); 157 158 (void) printf( 159 gettext("Free the allocation units in the orphaned chain ? " 160 "(y/n) ")); 161 if (AlwaysYes) 162 return (true); 163 if (AlwaysNo) 164 return (false); 165 return (yes()); 166 } 167 168 static bool 169 askAboutRelink(int32_t clusterNum) 170 { 171 /* 172 * Display the size of the chain for the user to consider. 173 */ 174 printOrphanInfo(clusterNum); 175 /* 176 * If we are in preen mode, preenBail won't return. 177 */ 178 preenBail("Need user confirmation to re-link orphaned chain.\n"); 179 180 (void) printf(gettext("Re-link orphaned chain into file system ? " 181 "(y/n) ")); 182 183 if (AlwaysYes) 184 return (true); 185 if (AlwaysNo) 186 return (false); 187 return (yes()); 188 } 189 190 static int 191 isHidden(int32_t clusterNum) 192 { 193 /* silent failure for bogus clusters */ 194 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 195 return (0); 196 197 if (InUse[clusterNum - FIRST_CLUSTER] == NULL) 198 return (0); 199 200 return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_HIDDEN); 201 } 202 203 static int 204 isInUse(int32_t clusterNum) 205 { 206 /* silent failure for bogus clusters */ 207 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 208 return (0); 209 210 return ((InUse[clusterNum - FIRST_CLUSTER] != NULL) && 211 (InUse[clusterNum - FIRST_CLUSTER]->dirent != NULL)); 212 } 213 214 /* 215 * Caller's may request that we cache the data from a readCluster. 216 * The xxxClusterxxxCachexxx routines handle looking for cached data 217 * or initially caching the data. 218 * 219 * XXX - facilitate releasing cached data for low memory situations. 220 */ 221 static CachedCluster * 222 findClusterCacheEntry(int32_t clusterNum) 223 { 224 CachedCluster *loop = ClusterCache; 225 226 while (loop != NULL) { 227 if (loop->clusterNum == clusterNum) 228 return (loop); 229 loop = loop->next; 230 } 231 return (NULL); 232 } 233 234 static uchar_t * 235 findClusterDataInTheCache(int32_t clusterNum) 236 { 237 CachedCluster *loop = ClusterCache; 238 239 while (loop) { 240 if (loop->clusterNum == clusterNum) 241 return (loop->clusterData.bytes); 242 loop = loop->next; 243 } 244 return (NULL); 245 } 246 247 static uchar_t * 248 addToCache(int32_t clusterNum, uchar_t *buf, int32_t *datasize) 249 { 250 CachedCluster *new; 251 uchar_t *cp; 252 253 if ((new = (CachedCluster *)malloc(sizeof (CachedCluster))) == NULL) { 254 perror(gettext("No memory for cached cluster info")); 255 return (buf); 256 } 257 new->clusterNum = clusterNum; 258 new->modified = 0; 259 260 if ((cp = (uchar_t *)calloc(1, BytesPerCluster)) == NULL) { 261 perror(gettext("No memory for cached copy of cluster")); 262 free(new); 263 return (buf); 264 } 265 (void) memcpy(cp, buf, *datasize); 266 new->clusterData.bytes = cp; 267 268 if (Verbose) { 269 (void) fprintf(stderr, 270 gettext("Allocation unit %d cached.\n"), clusterNum); 271 } 272 if (ClusterCache == NULL) { 273 ClusterCache = new; 274 new->next = NULL; 275 } else if (new->clusterNum < ClusterCache->clusterNum) { 276 new->next = ClusterCache; 277 ClusterCache = new; 278 } else { 279 CachedCluster *loop = ClusterCache; 280 CachedCluster *trailer = NULL; 281 282 while (loop && new->clusterNum > loop->clusterNum) { 283 trailer = loop; 284 loop = loop->next; 285 } 286 trailer->next = new; 287 if (loop) { 288 new->next = loop; 289 } else { 290 new->next = NULL; 291 } 292 } 293 CachedClusterCount++; 294 return (new->clusterData.bytes); 295 } 296 297 static int 298 seekCluster(int fd, int32_t clusterNum) 299 { 300 off64_t seekto; 301 int saveError; 302 303 seekto = FirstClusterOffset + 304 ((off64_t)clusterNum - FIRST_CLUSTER) * BytesPerCluster; 305 if (lseek64(fd, seekto, SEEK_SET) != seekto) { 306 saveError = errno; 307 (void) fprintf(stderr, 308 gettext("Seek to Allocation unit #%d failed: "), 309 clusterNum); 310 (void) fprintf(stderr, strerror(saveError)); 311 (void) fprintf(stderr, "\n"); 312 return (0); 313 } 314 return (1); 315 } 316 317 /* 318 * getcluster 319 * Get cluster bytes off the disk. We always read those bytes into 320 * the same static buffer. If the caller wants its own copy of the 321 * data it'll have to make its own copy. We'll return all the data 322 * read, even if it's short of a full cluster. This is for future use 323 * when we might want to relocate any salvagable data from bad clusters. 324 */ 325 static int 326 getCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize) 327 { 328 static uchar_t *clusterBuffer = NULL; 329 int saveError; 330 int try; 331 332 *datasize = 0; 333 *data = NULL; 334 335 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 336 return (RDCLUST_BADINPUT); 337 338 if (clusterBuffer == NULL && 339 (clusterBuffer = (uchar_t *)malloc(BytesPerCluster)) == NULL) { 340 perror(gettext("No memory for a cluster data buffer")); 341 return (RDCLUST_MEMERR); 342 } 343 344 for (try = 0; try < RDCLUST_MAX_RETRY; try++) { 345 if (!seekCluster(fd, clusterNum)) 346 return (RDCLUST_FAIL); 347 if ((*datasize = read(fd, clusterBuffer, BytesPerCluster)) == 348 BytesPerCluster) { 349 *data = clusterBuffer; 350 return (RDCLUST_GOOD); 351 } 352 } 353 if (*datasize >= 0) { 354 *data = clusterBuffer; 355 (void) fprintf(stderr, 356 gettext("Short read of allocation unit #%d\n"), clusterNum); 357 } else { 358 saveError = errno; 359 (void) fprintf(stderr, "Allocation unit %d:", clusterNum); 360 (void) fprintf(stderr, strerror(saveError)); 361 (void) fprintf(stderr, "\n"); 362 } 363 return (RDCLUST_FAIL); 364 } 365 366 static void 367 writeCachedCluster(int fd, CachedCluster *clustInfo) 368 { 369 ssize_t bytesWritten; 370 371 if (ReadOnly) 372 return; 373 374 if (Verbose) 375 (void) fprintf(stderr, 376 gettext("Allocation unit %d modified.\n"), 377 clustInfo->clusterNum); 378 379 if (seekCluster(fd, clustInfo->clusterNum) == 0) 380 return; 381 382 if ((bytesWritten = write(fd, clustInfo->clusterData.bytes, 383 BytesPerCluster)) != BytesPerCluster) { 384 if (bytesWritten < 0) { 385 perror(gettext("Failed to write modified " 386 "allocation unit")); 387 } else { 388 (void) fprintf(stderr, 389 gettext("Short write of allocation unit %d\n"), 390 clustInfo->clusterNum); 391 } 392 (void) close(fd); 393 exit(13); 394 } 395 } 396 397 /* 398 * It's cheaper to allocate a lot at a time; malloc overhead pushes 399 * you over the brink much more quickly if you don't. 400 * This numbers seems to be a fair trade-off between reduced malloc overhead 401 * and additional overhead by over-allocating. 402 */ 403 404 #define CHUNKSIZE 1024 405 406 static ClusterInfo *pool; 407 408 static ClusterInfo * 409 newClusterInfo(void) 410 { 411 412 ClusterInfo *ret; 413 414 if (pool == NULL) { 415 int i; 416 417 pool = (ClusterInfo *)malloc(sizeof (ClusterInfo) * CHUNKSIZE); 418 419 if (pool == NULL) { 420 perror( 421 gettext("Out of memory for cluster information")); 422 exit(9); 423 } 424 425 for (i = 0; i < CHUNKSIZE - 1; i++) 426 pool[i].nextfree = &pool[i+1]; 427 428 pool[CHUNKSIZE-1].nextfree = NULL; 429 } 430 ret = pool; 431 pool = pool->nextfree; 432 433 memset(ret, 0, sizeof (*ret)); 434 435 return (ret); 436 } 437 438 /* Should be called with verified arguments */ 439 440 static ClusterInfo * 441 cloneClusterInfo(int32_t clusterNum) 442 { 443 ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER]; 444 445 if (cl->refcnt > 1) { 446 ClusterInfo *newCl = newClusterInfo(); 447 cl->refcnt--; 448 *newCl = *cl; 449 newCl->refcnt = 1; 450 if (newCl->path) 451 newCl->path->references++; 452 InUse[clusterNum - FIRST_CLUSTER] = newCl; 453 } 454 return (InUse[clusterNum - FIRST_CLUSTER]); 455 } 456 457 static void 458 updateFlags(int32_t clusterNum, int newflags) 459 { 460 ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER]; 461 462 if (cl->flags != newflags && cl->refcnt > 1) 463 cl = cloneClusterInfo(clusterNum); 464 465 cl->flags = newflags; 466 } 467 468 static void 469 freeClusterInfo(ClusterInfo *old) 470 { 471 if (--old->refcnt <= 0) { 472 if (old->path && --old->path->references <= 0) { 473 free(old->path->fullName); 474 free(old->path); 475 } 476 old->nextfree = pool; 477 pool = old; 478 } 479 } 480 481 /* 482 * Allocate entries in our sparse array of cluster information. 483 * Returns non-zero if the structure already has been allocated 484 * (for those keeping score at home). 485 * 486 * The template parameter, if non-NULL, is used to facilitate sharing 487 * the ClusterInfo nodes for the clusters belonging to the same file. 488 * The first call to allocInUse for a new file should have *template 489 * set to 0; on return, *template then points to the newly allocated 490 * ClusterInfo. Second and further calls keep the same value 491 * in *template and that ClusterInfo ndoe is then used for all 492 * entries in the file. Code that modifies the ClusterInfo nodes 493 * should take care proper sharing semantics are maintained (i.e., 494 * copy-on-write using cloneClusterInfo()) 495 * 496 * The ClusterInfo used in the template is guaranted to be in use in 497 * at least one other cluster as we never return a value if we didn't 498 * set it first. So we can overwrite it without the possibility of a leak. 499 */ 500 static int 501 allocInUse(int32_t clusterNum, ClusterInfo **template) 502 { 503 ClusterInfo *newCl; 504 505 if (InUse[clusterNum - FIRST_CLUSTER] != NULL) 506 return (CLINFO_PREVIOUSLY_ALLOCED); 507 508 if (template != NULL && *template != NULL) 509 newCl = *template; 510 else { 511 newCl = newClusterInfo(); 512 if (template) 513 *template = newCl; 514 } 515 516 InUse[clusterNum - FIRST_CLUSTER] = newCl; 517 newCl->refcnt++; 518 519 return (CLINFO_NEWLY_ALLOCED); 520 } 521 522 static void 523 markFree(int32_t clusterNum) 524 { 525 /* silent failure for bogus clusters */ 526 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 527 return; 528 529 if (InUse[clusterNum - FIRST_CLUSTER]) { 530 if (InUse[clusterNum - FIRST_CLUSTER]->saved) 531 free(InUse[clusterNum - FIRST_CLUSTER]->saved); 532 freeClusterInfo(InUse[clusterNum - FIRST_CLUSTER]); 533 InUse[clusterNum - FIRST_CLUSTER] = NULL; 534 } 535 } 536 537 static void 538 markOrphan(int fd, int32_t clusterNum, struct pcdir *dp) 539 { 540 /* silent failure for bogus clusters */ 541 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 542 return; 543 544 (void) markInUse(fd, clusterNum, dp, NULL, 0, VISIBLE, NULL); 545 if (InUse[clusterNum - FIRST_CLUSTER] != NULL) 546 updateFlags(clusterNum, 547 InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_ORPHAN); 548 } 549 550 static void 551 markBad(int32_t clusterNum, uchar_t *recovered, int32_t recoveredLen) 552 { 553 /* silent failure for bogus clusters */ 554 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 555 return; 556 557 (void) allocInUse(clusterNum, NULL); 558 559 if (recoveredLen) { 560 (void) cloneClusterInfo(clusterNum); 561 InUse[clusterNum - FIRST_CLUSTER]->saved = recovered; 562 } 563 updateFlags(clusterNum, 564 InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_BAD); 565 566 BadClusterCount++; 567 if (Verbose) 568 (void) fprintf(stderr, 569 gettext("Allocation unit %d marked bad.\n"), clusterNum); 570 } 571 572 static void 573 clearOrphan(int32_t c) 574 { 575 /* silent failure for bogus clusters */ 576 if (c < FIRST_CLUSTER || c > LastCluster) 577 return; 578 if (InUse[c - FIRST_CLUSTER] != NULL) 579 updateFlags(c, 580 InUse[c - FIRST_CLUSTER]->flags & ~CLINFO_ORPHAN); 581 } 582 583 static void 584 clearInUse(int32_t c) 585 { 586 ClusterInfo **clp; 587 588 /* silent failure for bogus clusters */ 589 if (c < FIRST_CLUSTER || c > LastCluster) 590 return; 591 592 clp = &InUse[c - FIRST_CLUSTER]; 593 if (*clp != NULL) { 594 freeClusterInfo(*clp); 595 *clp = NULL; 596 } 597 } 598 599 static void 600 clearAllClusters_InUse() 601 { 602 int32_t cc; 603 for (cc = FIRST_CLUSTER; cc < LastCluster; cc++) { 604 clearInUse(cc); 605 } 606 } 607 608 static void 609 makeUseTable(void) 610 { 611 if (InUse != NULL) { 612 clearAllClusters_InUse(); 613 return; 614 } 615 if ((InUse = (ClusterInfo **) 616 calloc(TotalClusters, sizeof (ClusterInfo *))) == NULL) { 617 perror(gettext("No memory for internal table")); 618 exit(9); 619 } 620 } 621 622 static void 623 countClusters(void) 624 { 625 int32_t c; 626 627 BadClusterCount = HiddenClusterCount = 628 AllocedClusterCount = FreeClusterCount = 0; 629 630 for (c = FIRST_CLUSTER; c < LastCluster; c++) { 631 if (badInFAT(c)) { 632 BadClusterCount++; 633 } else if (isMarkedBad(c)) { 634 /* 635 * This catches the bad sectors found 636 * during thorough verify that have never been 637 * allocated to a file. Without this check, we 638 * count these guys as free. 639 */ 640 BadClusterCount++; 641 markBadInFAT(c); 642 } else if (isHidden(c)) { 643 HiddenClusterCount++; 644 } else if (isInUse(c)) { 645 AllocedClusterCount++; 646 } else { 647 FreeClusterCount++; 648 } 649 } 650 } 651 652 /* 653 * summarizeFAT 654 * Mark orphans without directory entries as allocated. 655 * XXX - these chains should be reclaimed! 656 * XXX - merge this routine with countClusters (same loop, duh.) 657 */ 658 static void 659 summarizeFAT(int fd) 660 { 661 int32_t c; 662 ClusterInfo *tmpl = NULL; 663 664 for (c = FIRST_CLUSTER; c < LastCluster; c++) { 665 if (!freeInFAT(c) && !badInFAT(c) && !reservedInFAT(c) && 666 !isInUse(c)) { 667 (void) markInUse(fd, c, &BlankPCDIR, NULL, 0, VISIBLE, 668 &tmpl); 669 } 670 } 671 } 672 673 static void 674 getReadyToSearch(int fd) 675 { 676 getFAT(fd); 677 if (!IsFAT32) 678 getRootDirectory(fd); 679 } 680 681 682 static char PathName[MAXPATHLEN]; 683 684 static void 685 summarize(int fd, int includeFAT) 686 { 687 struct pcdir *ignorep1, *ignorep2 = NULL; 688 int32_t ignore32; 689 char ignore; 690 int pathlen; 691 692 ReservedClusterCount = 0; 693 AllocedClusterCount = 0; 694 HiddenClusterCount = 0; 695 FileClusterCount = 0; 696 FreeClusterCount = 0; 697 DirClusterCount = 0; 698 BadClusterCount = 0; 699 HiddenFileCount = 0; 700 FileCount = 0; 701 DirCount = 0; 702 ignorep1 = ignorep2 = NULL; 703 ignore = '\0'; 704 705 PathName[0] = '\0'; 706 pathlen = 0; 707 708 getReadyToSearch(fd); 709 /* 710 * Traverse the full meta-data tree to talley what clusters 711 * are in use. The root directory is an area outside of the 712 * file space on FAT12 and FAT16 file systems. On FAT32 file 713 * systems, the root directory is in a file area cluster just 714 * like any other directory. 715 */ 716 if (!IsFAT32) { 717 traverseFromRoot(fd, 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL, 718 ignore, &ignorep1, &ignore32, &ignorep2, PathName, 719 &pathlen); 720 } else { 721 DirCount++; 722 traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust, 723 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL, ignore, 724 &ignorep1, &ignore32, &ignorep2, PathName, &pathlen); 725 } 726 727 if (includeFAT) 728 summarizeFAT(fd); 729 countClusters(); 730 } 731 732 int 733 isMarkedBad(int32_t clusterNum) 734 { 735 /* silent failure for bogus clusters */ 736 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 737 return (0); 738 739 if (InUse[clusterNum - FIRST_CLUSTER] == NULL) 740 return (0); 741 742 return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_BAD); 743 } 744 745 static int 746 isMarkedOrphan(int32_t clusterNum) 747 { 748 /* silent failure for bogus clusters */ 749 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 750 return (0); 751 752 if (InUse[clusterNum - FIRST_CLUSTER] == NULL) 753 return (0); 754 755 return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_ORPHAN); 756 } 757 758 static void 759 orphanChain(int fd, int32_t c, struct pcdir *ndp) 760 { 761 ClusterInfo *tmpl = NULL; 762 763 /* silent failure for bogus clusters */ 764 if (c < FIRST_CLUSTER || c > LastCluster) 765 return; 766 clearInUse(c); 767 markOrphan(fd, c, ndp); 768 c = nextInChain(c); 769 while (c != 0) { 770 clearInUse(c); 771 clearOrphan(c); 772 (void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, &tmpl); 773 c = nextInChain(c); 774 } 775 } 776 777 static int32_t 778 findAFreeCluster(int32_t startAt) 779 { 780 int32_t look = startAt; 781 782 for (;;) { 783 if (freeInFAT(look)) { 784 break; 785 } 786 if (look == LastCluster) 787 look = FIRST_CLUSTER; 788 else 789 look++; 790 if (look == startAt) 791 break; 792 } 793 if (look != startAt) 794 return (look); 795 else 796 return (0); 797 } 798 799 static void 800 setEndOfDirectory(struct pcdir *dp) 801 { 802 dp->pcd_filename[0] = PCD_UNUSED; 803 } 804 805 static void 806 emergencyEndOfDirectory(int fd, int32_t secondToLast) 807 { 808 ClusterContents dirdata; 809 int32_t dirdatasize = 0; 810 811 if (readCluster(fd, secondToLast, &(dirdata.bytes), &dirdatasize, 812 RDCLUST_DO_CACHE) != RDCLUST_GOOD) { 813 (void) fprintf(stderr, 814 gettext("Unable to read allocation unit %d.\n"), 815 secondToLast); 816 (void) fprintf(stderr, 817 gettext("Cannot allocate a new allocation unit to hold an" 818 " end-of-directory marker.\nCannot access allocation unit" 819 " to overwrite existing directory entry with\nthe marker." 820 " Needed directory truncation has failed. Giving up.\n")); 821 (void) close(fd); 822 exit(11); 823 } 824 setEndOfDirectory(dirdata.dirp); 825 markClusterModified(secondToLast); 826 } 827 828 static void 829 makeNewEndOfDirectory(struct pcdir *entry, int32_t secondToLast, 830 int32_t newCluster, ClusterContents *newData) 831 { 832 setEndOfDirectory(newData->dirp); 833 markClusterModified(newCluster); 834 /* 835 * There are two scenarios. One is that we truncated the 836 * directory in the very beginning. The other is that we 837 * truncated it in the middle or at the end. In the first 838 * scenario, the secondToLast argument is not a valid cluster 839 * (it's zero), and so we actually need to change the start 840 * cluster for the directory to this new start cluster. In 841 * the second scenario, the secondToLast cluster we received 842 * as an argument needs to be pointed at the new end of 843 * directory. 844 */ 845 if (secondToLast == 0) { 846 updateDirEnt_Start(entry, newCluster); 847 } else { 848 writeFATEntry(secondToLast, newCluster); 849 } 850 markLastInFAT(newCluster); 851 } 852 853 static void 854 createNewEndOfDirectory(int fd, struct pcdir *entry, int32_t secondToLast) 855 { 856 ClusterContents dirdata; 857 int32_t dirdatasize = 0; 858 int32_t freeCluster; 859 860 if (((freeCluster = findAFreeCluster(secondToLast)) != 0)) { 861 if (readCluster(fd, freeCluster, &(dirdata.bytes), 862 &dirdatasize, RDCLUST_DO_CACHE) == RDCLUST_GOOD) { 863 if (Verbose) { 864 (void) fprintf(stderr, 865 gettext("Grabbed allocation unit #%d " 866 "for truncated\ndirectory's new end " 867 "of directory.\n"), freeCluster); 868 } 869 makeNewEndOfDirectory(entry, secondToLast, 870 freeCluster, &dirdata); 871 return; 872 } 873 } 874 if (secondToLast == 0) { 875 if (freeCluster == 0) { 876 (void) fprintf(stderr, gettext("File system full.\n")); 877 } else { 878 (void) fprintf(stderr, 879 gettext("Unable to read allocation unit %d.\n"), 880 freeCluster); 881 } 882 (void) fprintf(stderr, 883 gettext("Cannot allocate a new allocation unit to hold " 884 "an end-of-directory marker.\nNo existing directory " 885 "entries can be overwritten with the marker,\n" 886 "the only unit allocated to the directory is " 887 "inaccessible.\nNeeded directory truncation has failed. " 888 "Giving up.\n")); 889 (void) close(fd); 890 exit(11); 891 } 892 emergencyEndOfDirectory(fd, secondToLast); 893 } 894 895 /* 896 * truncAtCluster 897 * Given a directory entry and a cluster number, search through 898 * the cluster chain for the entry and make the cluster previous 899 * to the given cluster in the chain the last cluster in the file. 900 * The number of orphaned bytes is returned. For a chain that's 901 * a directory we need to do some special handling, since we'll be 902 * getting rid of the end of directory notice by truncating. 903 */ 904 static int64_t 905 truncAtCluster(int fd, struct pcdir *entry, int32_t cluster) 906 { 907 uint32_t oldSize, newSize; 908 int32_t prev, count, follow; 909 int dir = (entry->pcd_attr & PCA_DIR); 910 911 prev = 0; count = 0; 912 follow = extractStartCluster(entry); 913 while (follow != cluster && follow >= FIRST_CLUSTER && 914 follow <= LastCluster) { 915 prev = follow; 916 count++; 917 follow = nextInChain(follow); 918 } 919 if (follow != cluster) { 920 /* 921 * We didn't find the cluster they wanted to trunc at 922 * anywhere in the entry's chain. So we'll leave the 923 * entry alone, and return a negative value so they 924 * can know something is wrong. 925 */ 926 return (-1); 927 } 928 if (Verbose) { 929 (void) fprintf(stderr, 930 gettext("Chain truncation at unit #%d\n"), cluster); 931 } 932 if (!dir) { 933 oldSize = extractSize(entry); 934 newSize = count * 935 TheBIOSParameterBlock.bpb.sectors_per_cluster * 936 TheBIOSParameterBlock.bpb.bytes_per_sector; 937 if (newSize == 0) 938 updateDirEnt_Start(entry, 0); 939 } else { 940 newSize = 0; 941 } 942 updateDirEnt_Size(entry, newSize); 943 if (dir) { 944 createNewEndOfDirectory(fd, entry, prev); 945 } else if (prev != 0) { 946 markLastInFAT(prev); 947 } 948 if (dir) { 949 /* 950 * We don't really know what the size of a directory is 951 * but it is important for us to know if this truncation 952 * results in an orphan with any size. The value we 953 * return from this routine for a normal file is the 954 * number of bytes left in the chain. For a directory 955 * we can't be exact, and the caller doesn't really 956 * expect us to be. For a directory the caller only 957 * cares if there are zero bytes left or more than 958 * zero bytes left. We'll return 1 to indicate 959 * more than zero. 960 */ 961 if ((follow = nextInChain(follow)) != 0) 962 return (1); 963 else 964 return (0); 965 } 966 /* 967 * newSize should always be smaller than the old one, since 968 * we are decreasing the number of clusters allocated to the file. 969 */ 970 return ((int64_t)oldSize - (int64_t)newSize); 971 } 972 973 static struct pcdir * 974 updateOrphanedChainMetadata(int fd, struct pcdir *dp, int32_t endCluster, 975 int isBad) 976 { 977 struct pcdir *ndp = NULL; 978 int64_t remainder; 979 char *newName = NULL; 980 int chosenName; 981 int dir = (dp->pcd_attr & PCA_DIR); 982 983 /* 984 * If the truncation fails, (which ought not to happen), 985 * there's no need to go any further, we just return 986 * a null value for the new directory entry pointer. 987 */ 988 remainder = truncAtCluster(fd, dp, endCluster); 989 if (remainder < 0) 990 return (ndp); 991 if (!dir && isBad) { 992 /* 993 * Subtract out the bad cluster from the remaining size 994 * We always assume the cluster being deleted from the 995 * file is full size, but that might not be the case 996 * for the last cluster of the file, so that is why 997 * we check for negative remainder value. 998 */ 999 remainder -= TheBIOSParameterBlock.bpb.sectors_per_cluster * 1000 TheBIOSParameterBlock.bpb.bytes_per_sector; 1001 if (remainder < 0) 1002 remainder = 0; 1003 } 1004 /* 1005 * Build a new directory entry for the rest of the chain. 1006 * Later, if the user okays it, we'll link this entry into the 1007 * root directory. The new entry will start out as a 1008 * copy of the truncated entry. 1009 */ 1010 if ((remainder != 0) && 1011 ((newName = nextAvailableCHKName(&chosenName)) != NULL) && 1012 ((ndp = newDirEnt(dp)) != NULL)) { 1013 if (Verbose) { 1014 if (dir) 1015 (void) fprintf(stderr, 1016 gettext("Orphaned directory chain.\n")); 1017 else 1018 (void) fprintf(stderr, 1019 gettext("Orphaned chain, %u bytes.\n"), 1020 (uint32_t)remainder); 1021 } 1022 if (!dir) 1023 updateDirEnt_Size(ndp, (uint32_t)remainder); 1024 if (isBad) 1025 updateDirEnt_Start(ndp, nextInChain(endCluster)); 1026 else 1027 updateDirEnt_Start(ndp, endCluster); 1028 updateDirEnt_Name(ndp, newName); 1029 addEntryToCHKList(chosenName); 1030 } 1031 return (ndp); 1032 } 1033 1034 /* 1035 * splitChain() 1036 * 1037 * split a cluster allocation chain into two cluster chains 1038 * around a given cluster (problemCluster). This results in two 1039 * separate directory entries; the original (dp), and one we hope 1040 * to create and return a pointer to to the caller (*newdp). 1041 * This second entry is the orphan chain, and it may end up in 1042 * the root directory as a FILEnnnn.CHK file. We also return the 1043 * starting cluster of the orphan chain to the caller (*orphanStart). 1044 */ 1045 void 1046 splitChain(int fd, struct pcdir *dp, int32_t problemCluster, 1047 struct pcdir **newdp, int32_t *orphanStart) 1048 { 1049 struct pcdir *ndp = NULL; 1050 int isBad = isMarkedBad(problemCluster); 1051 1052 ndp = updateOrphanedChainMetadata(fd, dp, problemCluster, isBad); 1053 *newdp = ndp; 1054 clearInUse(problemCluster); 1055 if (isBad) { 1056 clearOrphan(problemCluster); 1057 *orphanStart = nextInChain(problemCluster); 1058 orphanChain(fd, *orphanStart, ndp); 1059 markBadInFAT(problemCluster); 1060 } else { 1061 *orphanStart = problemCluster; 1062 orphanChain(fd, problemCluster, ndp); 1063 } 1064 } 1065 1066 /* 1067 * freeOrphan 1068 * 1069 * User has requested that an orphaned cluster chain be freed back 1070 * into the file area. 1071 */ 1072 static void 1073 freeOrphan(int32_t c) 1074 { 1075 int32_t n; 1076 1077 /* 1078 * Free the directory entry we explicitly created for 1079 * the orphaned clusters. 1080 */ 1081 if (InUse[c - FIRST_CLUSTER]->dirent != NULL) 1082 free(InUse[c - FIRST_CLUSTER]->dirent); 1083 /* 1084 * Then mark the clusters themselves as available. 1085 */ 1086 do { 1087 n = nextInChain(c); 1088 markFreeInFAT(c); 1089 markFree(c); 1090 c = n; 1091 } while (c != 0); 1092 } 1093 1094 /* 1095 * Rewrite the InUse field for a cluster chain. Can be used on a partial 1096 * chain if provided with a stopAtCluster. 1097 */ 1098 static void 1099 redoInUse(int fd, int32_t c, struct pcdir *ndp, int32_t stopAtCluster) 1100 { 1101 while (c && c != stopAtCluster) { 1102 clearInUse(c); 1103 (void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, NULL); 1104 c = nextInChain(c); 1105 } 1106 } 1107 1108 static struct pcdir * 1109 orphanDirEntLookup(int32_t clusterNum) 1110 { 1111 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 1112 return (NULL); 1113 1114 if (isInUse(clusterNum)) { 1115 return (InUse[clusterNum - FIRST_CLUSTER]->dirent); 1116 } else { 1117 return (NULL); 1118 } 1119 } 1120 1121 static int32_t 1122 orphanSizeLookup(int32_t clusterNum) 1123 { 1124 /* silent failure for bogus clusters */ 1125 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 1126 return (-1); 1127 1128 if (isInUse(clusterNum)) { 1129 return (extractSize(InUse[clusterNum - FIRST_CLUSTER]->dirent)); 1130 } else { 1131 return (-1); 1132 } 1133 } 1134 1135 /* 1136 * linkOrphan 1137 * 1138 * User has requested that an orphaned cluster chain be brought back 1139 * into the file system. So we have to make a new directory entry 1140 * in the root directory and point it at the cluster chain. 1141 */ 1142 static void 1143 linkOrphan(int fd, int32_t start) 1144 { 1145 struct pcdir *newEnt = NULL; 1146 struct pcdir *dp; 1147 1148 if ((dp = orphanDirEntLookup(start)) != NULL) { 1149 newEnt = addRootDirEnt(fd, dp); 1150 } else { 1151 (void) printf(gettext("Re-link of orphaned chain failed." 1152 " Allocation units will remain orphaned.\n")); 1153 } 1154 /* 1155 * A cluster isn't really InUse() unless it is referenced, 1156 * so if newEnt is NULL here, we are in effect using markInUse() 1157 * to note that the cluster is NOT in use. 1158 */ 1159 redoInUse(fd, start, newEnt, 0); 1160 } 1161 1162 /* 1163 * relinkCreatedOrphans 1164 * 1165 * While marking clusters as bad, we can create orphan cluster 1166 * chains. Since we were the ones doing the marking, we were able to 1167 * keep track of the orphans we created. Now we want to go through 1168 * all those chains and either get them back into the file system or 1169 * free them depending on the user's input. 1170 */ 1171 static void 1172 relinkCreatedOrphans(int fd) 1173 { 1174 int32_t c; 1175 1176 for (c = FIRST_CLUSTER; c < LastCluster; c++) { 1177 if (isMarkedOrphan(c)) { 1178 if (OkayToRelink && askAboutRelink(c)) { 1179 linkOrphan(fd, c); 1180 } else if (askAboutFreeing(c)) { 1181 freeOrphan(c); 1182 } 1183 clearOrphan(c); 1184 } 1185 } 1186 } 1187 1188 /* 1189 * relinkFATOrphans 1190 * 1191 * We want to find orphans not represented in the meta-data. 1192 * These are chains marked in the FAT as being in use but 1193 * not referenced anywhere by any directory entries. 1194 * We'll go through the whole FAT and mark the first cluster 1195 * in any such chain as an orphan. Then we can just use 1196 * the relinkCreatedOrphans routine to get them back into the 1197 * file system or free'ed depending on the user's input. 1198 */ 1199 static void 1200 relinkFATOrphans(int fd) 1201 { 1202 struct pcdir *ndp = NULL; 1203 int32_t cc, c, n; 1204 int32_t bpc, newSize; 1205 char *newName; 1206 int chosenName; 1207 1208 for (c = FIRST_CLUSTER; c < LastCluster; c++) { 1209 if (freeInFAT(c) || badInFAT(c) || 1210 reservedInFAT(c) || isInUse(c)) 1211 continue; 1212 cc = 1; 1213 n = c; 1214 while (n = nextInChain(n)) 1215 cc++; 1216 bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster * 1217 TheBIOSParameterBlock.bpb.bytes_per_sector; 1218 newSize = cc * bpc; 1219 if (((newName = nextAvailableCHKName(&chosenName)) != NULL) && 1220 ((ndp = newDirEnt(NULL)) != NULL)) { 1221 updateDirEnt_Size(ndp, newSize); 1222 updateDirEnt_Start(ndp, c); 1223 updateDirEnt_Name(ndp, newName); 1224 addEntryToCHKList(chosenName); 1225 } 1226 orphanChain(fd, c, ndp); 1227 } 1228 relinkCreatedOrphans(fd); 1229 } 1230 1231 static void 1232 relinkOrphans(int fd) 1233 { 1234 relinkCreatedOrphans(fd); 1235 relinkFATOrphans(fd); 1236 } 1237 1238 static void 1239 checkForFATLoop(int32_t clusterNum) 1240 { 1241 int32_t prev = clusterNum; 1242 int32_t follow; 1243 1244 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 1245 return; 1246 1247 follow = nextInChain(clusterNum); 1248 while (follow != clusterNum && follow >= FIRST_CLUSTER && 1249 follow <= LastCluster) { 1250 prev = follow; 1251 follow = nextInChain(follow); 1252 } 1253 if (follow == clusterNum) { 1254 /* 1255 * We found a loop. Eradicate it by changing 1256 * the last cluster in the loop to be last 1257 * in the chain instead instead of pointing 1258 * back to the first cluster. 1259 */ 1260 markLastInFAT(prev); 1261 } 1262 } 1263 1264 static void 1265 sharedChainError(int fd, int32_t clusterNum, struct pcdir *badEntry) 1266 { 1267 /* 1268 * If we have shared clusters, it is either because the 1269 * cluster somehow got assigned to multiple files and/or 1270 * because of a loop in the cluster chain. In either 1271 * case we want to truncate the offending file at the 1272 * cluster of contention. Then, we will want to run 1273 * through the remainder of the chain. If we find ourselves 1274 * back at the top, we will know there is a loop in the 1275 * FAT we need to remove. 1276 */ 1277 if (Verbose) 1278 (void) fprintf(stderr, 1279 gettext("Truncating chain due to duplicate allocation of " 1280 "unit %d.\n"), clusterNum); 1281 /* 1282 * Note that we don't orphan anything here, because the duplicate 1283 * part of the chain may be part of another valid chain. 1284 */ 1285 (void) truncAtCluster(fd, badEntry, clusterNum); 1286 checkForFATLoop(clusterNum); 1287 } 1288 1289 void 1290 truncChainWithBadCluster(int fd, struct pcdir *dp, int32_t startCluster) 1291 { 1292 struct pcdir *orphanEntry; 1293 int32_t orphanStartCluster; 1294 int32_t c = startCluster; 1295 1296 while (c != 0) { 1297 if (isMarkedBad(c)) { 1298 /* 1299 * splitChain() truncates the current guy and 1300 * then makes an orphan chain out of the remaining 1301 * clusters. When we come back from the split 1302 * we'll want to continue looking for bad clusters 1303 * in the orphan chain. 1304 */ 1305 splitChain(fd, dp, c, 1306 &orphanEntry, &orphanStartCluster); 1307 /* 1308 * There is a chance that we weren't able or weren't 1309 * required to make a directory entry for the 1310 * remaining clusters. In that case we won't go 1311 * on, because we couldn't make any more splits 1312 * anyway. 1313 */ 1314 if (orphanEntry == NULL) 1315 break; 1316 c = orphanStartCluster; 1317 dp = orphanEntry; 1318 continue; 1319 } 1320 c = nextInChain(c); 1321 } 1322 } 1323 1324 int32_t 1325 nextInChain(int32_t currentCluster) 1326 { 1327 int32_t nextCluster; 1328 1329 /* silent failure for bogus clusters */ 1330 if (currentCluster < FIRST_CLUSTER || currentCluster > LastCluster) 1331 return (0); 1332 1333 /* 1334 * Look up FAT entry of next link in cluster chain, 1335 * if this one is the last one return 0 as the next link. 1336 */ 1337 nextCluster = readFATEntry(currentCluster); 1338 if (nextCluster < FIRST_CLUSTER || nextCluster > LastCluster) 1339 return (0); 1340 1341 return (nextCluster); 1342 } 1343 1344 /* 1345 * findImpactedCluster 1346 * 1347 * Called when someone modifies what they believe might be a cached 1348 * cluster entry, but when they only have a directory entry pointer 1349 * and not the cluster number. We have to go dig up what cluster 1350 * they are modifying. 1351 */ 1352 int32_t 1353 findImpactedCluster(struct pcdir *modified) 1354 { 1355 CachedCluster *loop; 1356 /* 1357 * Check to see if it's in the root directory first 1358 */ 1359 if (!IsFAT32 && ((uchar_t *)modified >= TheRootDir.bytes) && 1360 ((uchar_t *)modified < TheRootDir.bytes + RootDirSize)) 1361 return (FAKE_ROOTDIR_CLUST); 1362 1363 loop = ClusterCache; 1364 while (loop) { 1365 if (((uchar_t *)modified >= loop->clusterData.bytes) && 1366 ((uchar_t *)modified < 1367 (loop->clusterData.bytes + BytesPerCluster))) { 1368 return (loop->clusterNum); 1369 } 1370 loop = loop->next; 1371 } 1372 /* 1373 * Guess it wasn't cached after all... 1374 */ 1375 return (0); 1376 } 1377 1378 void 1379 writeClusterMods(int fd) 1380 { 1381 CachedCluster *loop = ClusterCache; 1382 1383 while (loop) { 1384 if (loop->modified) 1385 writeCachedCluster(fd, loop); 1386 loop = loop->next; 1387 } 1388 } 1389 1390 void 1391 squirrelPath(struct nameinfo *pathInfo, int32_t clusterNum) 1392 { 1393 /* silent failure for bogus clusters */ 1394 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 1395 return; 1396 if (InUse[clusterNum - FIRST_CLUSTER] == NULL) 1397 return; 1398 InUse[clusterNum - FIRST_CLUSTER]->path = pathInfo; 1399 } 1400 1401 int 1402 markInUse(int fd, int32_t clusterNum, struct pcdir *referencer, struct 1403 pcdir *longRef, int32_t longStartCluster, int isHiddenFile, 1404 ClusterInfo **template) 1405 { 1406 int alreadyMarked; 1407 ClusterInfo *cl; 1408 1409 /* silent failure for bogus clusters */ 1410 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 1411 return (CLINFO_NEWLY_ALLOCED); 1412 1413 alreadyMarked = allocInUse(clusterNum, template); 1414 if ((alreadyMarked == CLINFO_PREVIOUSLY_ALLOCED) && 1415 (isInUse(clusterNum))) { 1416 sharedChainError(fd, clusterNum, referencer); 1417 return (CLINFO_PREVIOUSLY_ALLOCED); 1418 } 1419 cl = InUse[clusterNum - FIRST_CLUSTER]; 1420 /* 1421 * If Cl is newly allocated (refcnt <= 1) we must fill in the fields. 1422 * If Cl has different fields, we must clone it. 1423 */ 1424 1425 if (cl->refcnt <= 1 || cl->dirent != referencer || 1426 cl->longent != longRef || 1427 cl->longEntStartClust != longStartCluster) { 1428 if (cl->refcnt > 1) 1429 cl = cloneClusterInfo(clusterNum); 1430 cl->dirent = referencer; 1431 cl->longent = longRef; 1432 cl->longEntStartClust = longStartCluster; 1433 if (isHiddenFile) 1434 cl->flags |= CLINFO_HIDDEN; 1435 1436 /* 1437 * Return cl as the template to use for other clusters in 1438 * this file 1439 */ 1440 if (template) 1441 *template = cl; 1442 } 1443 return (CLINFO_NEWLY_ALLOCED); 1444 } 1445 1446 void 1447 markClusterModified(int32_t clusterNum) 1448 { 1449 CachedCluster *c; 1450 1451 if (clusterNum == FAKE_ROOTDIR_CLUST) { 1452 RootDirModified = 1; 1453 return; 1454 } 1455 1456 /* silent failure for bogus clusters */ 1457 if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster) 1458 return; 1459 1460 if (c = findClusterCacheEntry(clusterNum)) { 1461 c->modified = 1; 1462 } else { 1463 (void) fprintf(stderr, 1464 gettext("Unexpected internal error: " 1465 "Missing cache entry [%d]\n"), clusterNum); 1466 exit(10); 1467 } 1468 } 1469 1470 /* 1471 * readCluster 1472 * caller wants to read cluster clusterNum. We should return 1473 * a pointer to the read data in "data", and fill in the number 1474 * of bytes read in "datasize". If shouldCache is non-zero 1475 * we should allocate cache space to the cluster, otherwise we 1476 * just return a pointer to a buffer we re-use whenever cacheing 1477 * is not requested. 1478 */ 1479 int 1480 readCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize, 1481 int shouldCache) 1482 { 1483 uchar_t *newBuf; 1484 int rv; 1485 1486 *data = NULL; 1487 if ((*data = findClusterDataInTheCache(clusterNum)) != NULL) { 1488 *datasize = BytesPerCluster; 1489 return (RDCLUST_GOOD); 1490 } 1491 1492 rv = getCluster(fd, clusterNum, &newBuf, datasize); 1493 if (rv != RDCLUST_GOOD) 1494 return (rv); 1495 1496 /* 1497 * Caller requested we NOT cache the data from this read. 1498 * So, we just return a pointer to the common data buffer. 1499 */ 1500 if (shouldCache == 0) { 1501 *data = newBuf; 1502 return (rv); 1503 } 1504 1505 /* 1506 * Caller requested we cache the data from this read. 1507 * So, if we have some data, add it to the cache by 1508 * copying it out of the common buffer into new storage. 1509 */ 1510 if (*datasize > 0) 1511 *data = addToCache(clusterNum, newBuf, datasize); 1512 return (rv); 1513 } 1514 1515 void 1516 findBadClusters(int fd) 1517 { 1518 int32_t clusterCount; 1519 int32_t datasize; 1520 uchar_t *data; 1521 1522 BadClusterCount = 0; 1523 makeUseTable(); 1524 (void) printf(gettext("** Scanning allocation units\n")); 1525 for (clusterCount = FIRST_CLUSTER; 1526 clusterCount < LastCluster; clusterCount++) { 1527 if (readCluster(fd, clusterCount, 1528 &data, &datasize, RDCLUST_DONT_CACHE) < 0) { 1529 if (Verbose) 1530 (void) fprintf(stderr, 1531 gettext( 1532 "\nUnreadable allocation unit %d.\n"), 1533 clusterCount); 1534 markBad(clusterCount, data, datasize); 1535 } 1536 /* 1537 * Progress meter, display a '.' for every 1000 clusters 1538 * processed. We don't want to display this when 1539 * we are in verbose mode; verbose mode progress is 1540 * shown by displaying each file name as it is found. 1541 */ 1542 if (!Verbose && clusterCount % 1000 == 0) 1543 (void) printf("."); 1544 } 1545 (void) printf(gettext("..done\n")); 1546 } 1547 1548 void 1549 scanAndFixMetadata(int fd) 1550 { 1551 /* 1552 * First we initialize a few things. 1553 */ 1554 makeUseTable(); 1555 getReadyToSearch(fd); 1556 createCHKNameList(fd); 1557 1558 /* 1559 * Make initial scan, taking into account any effect that 1560 * the bad clusters we may have already discovered have 1561 * on meta-data. We may break up some cluster chains 1562 * during this period. The relinkCreatedOrphans() call 1563 * will then give the user the chance to recover stuff 1564 * we've created. 1565 */ 1566 (void) printf(gettext("** Scanning file system meta-data\n")); 1567 summarize(fd, NO_FAT_IN_SUMMARY); 1568 if (Verbose) 1569 printSummary(stderr); 1570 (void) printf(gettext("** Correcting any meta-data discrepancies\n")); 1571 relinkCreatedOrphans(fd); 1572 1573 /* 1574 * Clear our usage table and go back over everything, this 1575 * time including looking for clusters floating free in the FAT. 1576 * This may include clusters the user chose to free during the 1577 * relink phase. 1578 */ 1579 makeUseTable(); 1580 summarize(fd, INCLUDE_FAT_IN_SUMMARY); 1581 relinkOrphans(fd); 1582 } 1583 1584 void 1585 printSummary(FILE *outDest) 1586 { 1587 (void) fprintf(outDest, 1588 gettext("%llu bytes.\n"), 1589 (uint64_t) 1590 TotalClusters * TheBIOSParameterBlock.bpb.sectors_per_cluster * 1591 TheBIOSParameterBlock.bpb.bytes_per_sector); 1592 (void) fprintf(outDest, 1593 gettext("%llu bytes in bad sectors.\n"), 1594 (uint64_t) 1595 BadClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster * 1596 TheBIOSParameterBlock.bpb.bytes_per_sector); 1597 (void) fprintf(outDest, 1598 gettext("%llu bytes in %d directories.\n"), 1599 (uint64_t) 1600 DirClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster * 1601 TheBIOSParameterBlock.bpb.bytes_per_sector, DirCount); 1602 if (HiddenClusterCount) { 1603 (void) fprintf(outDest, 1604 gettext("%llu bytes in %d hidden files.\n"), 1605 (uint64_t)HiddenClusterCount * 1606 TheBIOSParameterBlock.bpb.sectors_per_cluster * 1607 TheBIOSParameterBlock.bpb.bytes_per_sector, 1608 HiddenFileCount); 1609 } 1610 (void) fprintf(outDest, 1611 gettext("%llu bytes in %d files.\n"), 1612 (uint64_t) 1613 FileClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster * 1614 TheBIOSParameterBlock.bpb.bytes_per_sector, FileCount); 1615 (void) fprintf(outDest, 1616 gettext("%llu bytes free.\n"), (uint64_t)FreeClusterCount * 1617 TheBIOSParameterBlock.bpb.sectors_per_cluster * 1618 TheBIOSParameterBlock.bpb.bytes_per_sector); 1619 (void) fprintf(outDest, 1620 gettext("%d bytes per allocation unit.\n"), 1621 TheBIOSParameterBlock.bpb.sectors_per_cluster * 1622 TheBIOSParameterBlock.bpb.bytes_per_sector); 1623 (void) fprintf(outDest, 1624 gettext("%d total allocation units.\n"), TotalClusters); 1625 if (ReservedClusterCount) 1626 (void) fprintf(outDest, 1627 gettext("%d reserved allocation units.\n"), 1628 ReservedClusterCount); 1629 (void) fprintf(outDest, 1630 gettext("%d available allocation units.\n"), FreeClusterCount); 1631 } 1632