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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * fsck_pcfs -- routines for manipulating directories. 30 */ 31 #include <stdio.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <libintl.h> 36 #include <ctype.h> 37 #include <time.h> 38 #include <sys/param.h> 39 #include <sys/time.h> 40 #include <sys/byteorder.h> 41 #include <sys/dktp/fdisk.h> 42 #include <sys/fs/pc_fs.h> 43 #include <sys/fs/pc_dir.h> 44 #include <sys/fs/pc_label.h> 45 #include "pcfs_common.h" 46 #include "fsck_pcfs.h" 47 48 extern int32_t HiddenClusterCount; 49 extern int32_t FileClusterCount; 50 extern int32_t DirClusterCount; 51 extern int32_t HiddenFileCount; 52 extern int32_t LastCluster; 53 extern int32_t FileCount; 54 extern int32_t BadCount; 55 extern int32_t DirCount; 56 extern int32_t FATSize; 57 extern off64_t PartitionOffset; 58 extern bpb_t TheBIOSParameterBlock; 59 extern int ReadOnly; 60 extern int IsFAT32; 61 extern int Verbose; 62 63 static uchar_t *CHKsList = NULL; 64 65 ClusterContents TheRootDir; 66 int32_t RootDirSize; 67 int RootDirModified; 68 int OkayToRelink = 1; 69 70 /* 71 * We have a bunch of routines for handling CHK names. A CHK name is 72 * simply a file name of the form "FILEnnnn.CHK", where the n's are the 73 * digits in the numbers from 1 to 9999. There are always four digits 74 * used, leading zeros are added as necessary. 75 * 76 * We use CHK names to link orphaned cluster chains back into the file 77 * system's root directory under an auspicious name so that the user 78 * may be able to recover some of their data. 79 * 80 * We use these routines to ensure CHK names we use don't conflict 81 * with any already present in the file system. 82 */ 83 static int 84 hasCHKName(struct pcdir *dp) 85 { 86 return (dp->pcd_filename[CHKNAME_F] == 'F' && 87 dp->pcd_filename[CHKNAME_I] == 'I' && 88 dp->pcd_filename[CHKNAME_L] == 'L' && 89 dp->pcd_filename[CHKNAME_E] == 'E' && 90 isdigit(dp->pcd_filename[CHKNAME_THOUSANDS]) && 91 isdigit(dp->pcd_filename[CHKNAME_HUNDREDS]) && 92 isdigit(dp->pcd_filename[CHKNAME_TENS]) && 93 isdigit(dp->pcd_filename[CHKNAME_ONES]) && 94 dp->pcd_ext[CHKNAME_C] == 'C' && 95 dp->pcd_ext[CHKNAME_H] == 'H' && 96 dp->pcd_ext[CHKNAME_K] == 'K'); 97 } 98 99 void 100 addEntryToCHKList(int chkNumber) 101 { 102 /* silent failure on bogus value */ 103 if (chkNumber < 0 || chkNumber > MAXCHKVAL) 104 return; 105 CHKsList[chkNumber / NBBY] |= (1 << (chkNumber % NBBY)); 106 } 107 108 static void 109 addToCHKList(struct pcdir *dp) 110 { 111 int chknum; 112 113 chknum = 1000 * (dp->pcd_filename[CHKNAME_THOUSANDS] - '0'); 114 chknum += 100 * (dp->pcd_filename[CHKNAME_HUNDREDS] - '0'); 115 chknum += 10 * (dp->pcd_filename[CHKNAME_TENS] - '0'); 116 chknum += (dp->pcd_filename[CHKNAME_ONES] - '0'); 117 addEntryToCHKList(chknum); 118 } 119 120 static int 121 inUseCHKName(int chkNumber) 122 { 123 return (CHKsList[chkNumber / NBBY] & (1 << (chkNumber % NBBY))); 124 } 125 126 static void 127 appendToPath(struct pcdir *dp, char *thePath, int *theLen) 128 { 129 int i = 0; 130 131 /* 132 * Sometimes caller doesn't care about keeping track of the path 133 */ 134 if (thePath == NULL) 135 return; 136 137 /* 138 * Prepend / 139 */ 140 if (*theLen < MAXPATHLEN) 141 *(thePath + (*theLen)++) = '/'; 142 /* 143 * Print out the file name part, but only up to the first 144 * space. 145 */ 146 while (*theLen < MAXPATHLEN && i < PCFNAMESIZE) { 147 /* 148 * When we start seeing spaces we assume that's the 149 * end of the interesting characters in the name. 150 */ 151 if ((dp->pcd_filename[i] == ' ') || 152 !(pc_validchar(dp->pcd_filename[i]))) 153 break; 154 *(thePath + (*theLen)++) = dp->pcd_filename[i++]; 155 } 156 /* 157 * Leave now, if we don't have an extension (or room for one) 158 */ 159 if ((dp->pcd_ext[i] == ' ') || ((*theLen) >= MAXPATHLEN) || 160 (!(pc_validchar(dp->pcd_ext[i])))) 161 return; 162 /* 163 * Tack on the extension 164 */ 165 *(thePath + (*theLen)++) = '.'; 166 i = 0; 167 while ((*theLen < MAXPATHLEN) && (i < PCFEXTSIZE)) { 168 if ((dp->pcd_ext[i] == ' ') || !(pc_validchar(dp->pcd_ext[i]))) 169 break; 170 *(thePath + (*theLen)++) = dp->pcd_ext[i++]; 171 } 172 } 173 174 static void 175 printName(FILE *outDest, struct pcdir *dp) 176 { 177 int i; 178 for (i = 0; i < PCFNAMESIZE; i++) { 179 if ((dp->pcd_filename[i] == ' ') || 180 !(pc_validchar(dp->pcd_filename[i]))) 181 break; 182 (void) fprintf(outDest, "%c", dp->pcd_filename[i]); 183 } 184 (void) fprintf(outDest, "."); 185 for (i = 0; i < PCFEXTSIZE; i++) { 186 if (!(pc_validchar(dp->pcd_ext[i]))) 187 break; 188 (void) fprintf(outDest, "%c", dp->pcd_ext[i]); 189 } 190 } 191 192 /* 193 * sanityCheckSize 194 * Make sure the size in the directory entry matches what is 195 * actually allocated. If there is a mismatch, orphan all 196 * the allocated clusters. Returns SIZE_MATCHED if everything matches 197 * up, TRUNCATED to indicate truncation was necessary. 198 */ 199 static int 200 sanityCheckSize(int fd, struct pcdir *dp, int32_t actualClusterCount, 201 int isDir, int32_t startCluster, struct nameinfo *fullPathName, 202 struct pcdir **orphanEntry) 203 { 204 uint32_t sizeFromDir; 205 int32_t ignorei = 0; 206 int64_t bpc; 207 208 bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster * 209 TheBIOSParameterBlock.bpb.bytes_per_sector; 210 sizeFromDir = extractSize(dp); 211 if (isDir) { 212 if (sizeFromDir == 0) 213 return (SIZE_MATCHED); 214 } else { 215 if ((sizeFromDir > ((actualClusterCount - 1) * bpc)) && 216 (sizeFromDir <= (actualClusterCount * bpc))) 217 return (SIZE_MATCHED); 218 } 219 if (fullPathName != NULL) { 220 fullPathName->references++; 221 (void) fprintf(stderr, "%s\n", fullPathName->fullName); 222 } 223 squirrelPath(fullPathName, startCluster); 224 (void) fprintf(stderr, 225 gettext("Truncating chain due to incorrect size " 226 "in directory. Size from directory = %u bytes,\n"), sizeFromDir); 227 if (actualClusterCount == 0) { 228 (void) fprintf(stderr, 229 gettext("Zero bytes are allocated to the file.\n")); 230 } else { 231 (void) fprintf(stderr, 232 gettext("Allocated size in range %llu - %llu bytes.\n"), 233 ((actualClusterCount - 1) * bpc) + 1, 234 (actualClusterCount * bpc)); 235 } 236 /* 237 * Use splitChain() to make an orphan that is the entire allocation 238 * chain. 239 */ 240 splitChain(fd, dp, startCluster, orphanEntry, &ignorei); 241 return (TRUNCATED); 242 } 243 244 static int 245 noteUsage(int fd, int32_t startAt, struct pcdir *dp, struct pcdir *lp, 246 int32_t longEntryStartCluster, int isHidden, int isDir, 247 struct nameinfo *fullPathName) 248 { 249 struct pcdir *orphanEntry; 250 int32_t chain = startAt; 251 int32_t count = 0; 252 int savePathNextIteration = 0; 253 int haveBad = 0; 254 ClusterInfo *tmpl = NULL; 255 256 while ((chain >= FIRST_CLUSTER) && (chain <= LastCluster)) { 257 if ((markInUse(fd, chain, dp, lp, longEntryStartCluster, 258 isHidden ? HIDDEN : VISIBLE, &tmpl)) 259 != CLINFO_NEWLY_ALLOCED) 260 break; 261 count++; 262 if (savePathNextIteration == 1) { 263 savePathNextIteration = 0; 264 if (fullPathName != NULL) 265 fullPathName->references++; 266 squirrelPath(fullPathName, chain); 267 } 268 if (isMarkedBad(chain)) { 269 haveBad = 1; 270 savePathNextIteration = 1; 271 } 272 if (isHidden) 273 HiddenClusterCount++; 274 else if (isDir) 275 DirClusterCount++; 276 else 277 FileClusterCount++; 278 chain = nextInChain(chain); 279 } 280 /* 281 * Do a sanity check on the file size in the directory entry. 282 * This may create an orphaned cluster chain. 283 */ 284 if (sanityCheckSize(fd, dp, count, isDir, startAt, 285 fullPathName, &orphanEntry) == TRUNCATED) { 286 /* 287 * The pre-existing directory entry has been truncated, 288 * so the chain associated with it no longer has any 289 * bad clusters. Instead, the new orphan has them. 290 */ 291 if (haveBad > 0) { 292 truncChainWithBadCluster(fd, orphanEntry, startAt); 293 } 294 haveBad = 0; 295 } 296 return (haveBad); 297 } 298 299 static void 300 storeInfoAboutEntry(int fd, struct pcdir *dp, struct pcdir *ldp, int depth, 301 int32_t longEntryStartCluster, char *fullPath, int *fullLen) 302 { 303 struct nameinfo *pathCopy; 304 int32_t start; 305 int haveBad; 306 int hidden = (dp->pcd_attr & PCA_HIDDEN || dp->pcd_attr & PCA_SYSTEM); 307 int dir = (dp->pcd_attr & PCA_DIR); 308 int i; 309 310 if (hidden) 311 HiddenFileCount++; 312 else if (dir) 313 DirCount++; 314 else 315 FileCount++; 316 appendToPath(dp, fullPath, fullLen); 317 318 /* 319 * Make a copy of the name at this point. We may want it to 320 * note the original source of an orphaned cluster. 321 */ 322 if ((pathCopy = 323 (struct nameinfo *)malloc(sizeof (struct nameinfo))) != NULL) { 324 if ((pathCopy->fullName = 325 (char *)malloc(*fullLen + 1)) != NULL) { 326 pathCopy->references = 0; 327 (void) strncpy(pathCopy->fullName, fullPath, *fullLen); 328 pathCopy->fullName[*fullLen] = '\0'; 329 } else { 330 free(pathCopy); 331 pathCopy = NULL; 332 } 333 } 334 if (Verbose) { 335 for (i = 0; i < depth; i++) 336 (void) fprintf(stderr, " "); 337 if (hidden) 338 (void) fprintf(stderr, "["); 339 else if (dir) 340 (void) fprintf(stderr, "|_"); 341 else 342 (void) fprintf(stderr, gettext("(%06d) "), FileCount); 343 printName(stderr, dp); 344 if (hidden) 345 (void) fprintf(stderr, "]"); 346 (void) fprintf(stderr, 347 gettext(", %u bytes, start cluster %d"), 348 extractSize(dp), extractStartCluster(dp)); 349 (void) fprintf(stderr, "\n"); 350 } 351 start = extractStartCluster(dp); 352 haveBad = noteUsage(fd, start, dp, ldp, longEntryStartCluster, 353 hidden, dir, pathCopy); 354 if (haveBad > 0) { 355 if (dir && pathCopy->fullName != NULL) { 356 (void) fprintf(stderr, 357 gettext("Adjusting for bad allocation units in " 358 "the meta-data of:\n ")); 359 (void) fprintf(stderr, pathCopy->fullName); 360 (void) fprintf(stderr, "\n"); 361 } 362 truncChainWithBadCluster(fd, dp, start); 363 } 364 if ((pathCopy != NULL) && (pathCopy->references == 0)) { 365 free(pathCopy->fullName); 366 free(pathCopy); 367 } 368 } 369 370 static void 371 storeInfoAboutLabel(struct pcdir *dp) 372 { 373 /* 374 * XXX eventually depth should be passed to this routine just 375 * as it is with storeInfoAboutEntry(). If it isn't zero, then 376 * we've got a bogus directory entry. 377 */ 378 if (Verbose) { 379 (void) fprintf(stderr, gettext("** ")); 380 printName(stderr, dp); 381 (void) fprintf(stderr, gettext(" **\n")); 382 } 383 } 384 385 static void 386 searchChecks(struct pcdir *dp, int operation, char matchRequired, 387 struct pcdir **found) 388 { 389 /* 390 * We support these searching operations: 391 * 392 * PCFS_FIND_ATTR 393 * look for the first file with a certain attribute 394 * (e.g, find all hidden files) 395 * PCFS_FIND_STATUS 396 * look for the first file with a certain status 397 * (e.g., the file has been marked deleted; making 398 * its directory entry reusable) 399 * PCFS_FIND_CHKS 400 * look for all files with short names of the form 401 * FILENNNN.CHK. These are the file names we give 402 * to chains of orphaned clusters we relink into the 403 * file system. This find facility allows us to seek 404 * out all existing files of this naming form so that 405 * we may create unique file names for new orphans. 406 */ 407 if (operation == PCFS_FIND_ATTR && dp->pcd_attr == matchRequired) { 408 *found = dp; 409 } else if (operation == PCFS_FIND_STATUS && 410 dp->pcd_filename[0] == matchRequired) { 411 *found = dp; 412 } else if (operation == PCFS_FIND_CHKS && hasCHKName(dp)) { 413 addToCHKList(dp); 414 } 415 } 416 417 static void 418 catalogEntry(int fd, struct pcdir *dp, struct pcdir *longdp, 419 int32_t currentCluster, int depth, char *recordPath, int *pathLen) 420 { 421 if (dp->pcd_attr & PCA_LABEL) { 422 storeInfoAboutLabel(dp); 423 } else { 424 storeInfoAboutEntry(fd, dp, longdp, depth, currentCluster, 425 recordPath, pathLen); 426 } 427 } 428 429 /* 430 * visitNodes() 431 * 432 * This is the main workhouse routine for traversing pcfs metadata. 433 * There isn't a lot to the metadata. Basically there is a root 434 * directory somewhere (either in its own special place outside the 435 * data area or in a data cluster). The root directory (and all other 436 * directories) are filled with a number of fixed size entries. An 437 * entry has the filename and extension, the file's attributes, the 438 * file's size, and the starting data cluster of the storage allocated 439 * to the file. To determine which clusters are assigned to the file, 440 * you start at the starting cluster entry in the FAT, and follow the 441 * chain of entries in the FAT. 442 * 443 * Arguments are: 444 * fd 445 * descriptor for accessing the raw file system data 446 * currentCluster 447 * original caller supplies the initial starting cluster, 448 * subsequent recursive calls are made with updated 449 * cluster numbers for the sub-directories. 450 * dirData 451 * pointer to the directory data bytes 452 * dirDataLen 453 * size of the whole buffer of data bytes (usually it is 454 * the size of a cluster, but the root directory on 455 * FAT12/16 is not necessarily the same size as a cluster). 456 * depth 457 * original caller should set it to zero (assuming they are 458 * starting from the root directory). This number is used to 459 * change the indentation of file names presented as debug info. 460 * descend 461 * boolean indicates if we should descend into subdirectories. 462 * operation 463 * what, if any, matching should be performed. 464 * The PCFS_TRAVERSE_ALL operation is a depth first traversal 465 * of all nodes in the metadata tree, that tracks all the 466 * clusters in use (according to the meta-data, at least) 467 * matchRequired 468 * value to be matched (if any) 469 * found 470 * output parameter 471 * used to return pointer to a directory entry that matches 472 * the search requirement 473 * original caller should pass in a pointer to a NULL pointer. 474 * lastDirCluster 475 * output parameter 476 * if no match found, last cluster num of starting directory 477 * dirEnd 478 * output parameter 479 * if no match found, return parameter stores pointer to where 480 * new directory entry could be appended to existing directory 481 * recordPath 482 * output parameter 483 * as files are discovered, and directories traversed, this 484 * buffer is used to store the current full path name. 485 * pathLen 486 * output parameter 487 * this is in the integer length of the current full path name. 488 */ 489 static void 490 visitNodes(int fd, int32_t currentCluster, ClusterContents *dirData, 491 int32_t dirDataLen, int depth, int descend, int operation, 492 char matchRequired, struct pcdir **found, int32_t *lastDirCluster, 493 struct pcdir **dirEnd, char *recordPath, int *pathLen) 494 { 495 struct pcdir *longdp = NULL; 496 struct pcdir *dp; 497 int32_t longStart; 498 int withinLongName = 0; 499 int saveLen = *pathLen; 500 501 dp = dirData->dirp; 502 503 /* 504 * A directory entry where the first character of the name is 505 * PCD_UNUSED indicates the end of the directory. 506 */ 507 while ((uchar_t *)dp < dirData->bytes + dirDataLen && 508 dp->pcd_filename[0] != PCD_UNUSED) { 509 /* 510 * Handle the special case find operations. 511 */ 512 searchChecks(dp, operation, matchRequired, found); 513 if (*found) 514 break; 515 /* 516 * Are we looking at part of a long file name entry? 517 * If so, we may need to note the start of the name. 518 * We don't do any further processing of long file 519 * name entries. 520 * 521 * We also skip deleted entries and the '.' and '..' 522 * entries. 523 */ 524 if ((dp->pcd_attr & PCDL_LFN_BITS) == PCDL_LFN_BITS) { 525 if (!withinLongName) { 526 withinLongName++; 527 longStart = currentCluster; 528 longdp = dp; 529 } 530 dp++; 531 continue; 532 } else if ((dp->pcd_filename[0] == PCD_ERASED) || 533 (dp->pcd_filename[0] == '.')) { 534 /* 535 * XXX - if we were within a long name, then 536 * its existence is bogus, because it is not 537 * attached to any real file. 538 */ 539 withinLongName = 0; 540 dp++; 541 continue; 542 } 543 withinLongName = 0; 544 if (operation == PCFS_TRAVERSE_ALL) 545 catalogEntry(fd, dp, longdp, longStart, depth, 546 recordPath, pathLen); 547 longdp = NULL; 548 longStart = 0; 549 if (dp->pcd_attr & PCA_DIR && descend == PCFS_VISIT_SUBDIRS) { 550 traverseDir(fd, extractStartCluster(dp), depth + 1, 551 descend, operation, matchRequired, found, 552 lastDirCluster, dirEnd, recordPath, pathLen); 553 if (*found) 554 break; 555 } 556 dp++; 557 *pathLen = saveLen; 558 } 559 if (*found) 560 return; 561 if ((uchar_t *)dp < dirData->bytes + dirDataLen) { 562 /* 563 * We reached the end of directory before the end of 564 * our provided data (a cluster). That means this cluster 565 * is the last one in this directory's chain. It also 566 * means we've just looked at the last directory entry. 567 */ 568 *lastDirCluster = currentCluster; 569 *dirEnd = dp; 570 return; 571 } 572 /* 573 * If there is more to the directory we'll go get it otherwise we 574 * are done traversing this directory. 575 */ 576 if ((currentCluster == FAKE_ROOTDIR_CLUST) || 577 (lastInFAT(currentCluster))) { 578 *lastDirCluster = currentCluster; 579 return; 580 } else { 581 traverseDir(fd, nextInChain(currentCluster), 582 depth, descend, operation, matchRequired, 583 found, lastDirCluster, dirEnd, recordPath, pathLen); 584 *pathLen = saveLen; 585 } 586 } 587 588 /* 589 * traverseFromRoot() 590 * For use with 12 and 16 bit FATs that have a root directory outside 591 * of the file system. This is a general purpose routine that 592 * can be used simply to visit all of the nodes in the metadata or 593 * to find the first instance of something, e.g., the first directory 594 * entry where the file is marked deleted. 595 * 596 * Inputs are described in the commentary for visitNodes() above. 597 */ 598 void 599 traverseFromRoot(int fd, int depth, int descend, int operation, 600 char matchRequired, struct pcdir **found, int32_t *lastDirCluster, 601 struct pcdir **dirEnd, char *recordPath, int *pathLen) 602 { 603 visitNodes(fd, FAKE_ROOTDIR_CLUST, &TheRootDir, RootDirSize, depth, 604 descend, operation, matchRequired, found, lastDirCluster, dirEnd, 605 recordPath, pathLen); 606 } 607 608 /* 609 * traverseDir() 610 * For use with all FATs outside of the initial root directory on 611 * 12 and 16 bit FAT file systems. This is a general purpose routine 612 * that can be used simply to visit all of the nodes in the metadata or 613 * to find the first instance of something, e.g., the first directory 614 * entry where the file is marked deleted. 615 * 616 * Unique Input is: 617 * startAt 618 * starting cluster of the directory 619 * 620 * This is the cluster that is the first one in this directory. 621 * We read it right away, so we can provide it as data to visitNodes(). 622 * Note that we cache this cluster as we read it, because it is 623 * metadata and we cache all metadata. By doing so, we can 624 * keep pointers to directory entries for quickly moving around and 625 * fixing up any problems we find. Of course if we get a big 626 * filesystem with a huge amount of metadata we may be hosed, as 627 * we'll likely run out of memory. 628 * 629 * I believe in the future this will have to be addressed. It 630 * may be possible to do more of the processing of problems 631 * within directories as they are cached, so that when memory 632 * runs short we can free cached directories we are already 633 * finished visiting. 634 * 635 * The remainder of inputs are described in visitNodes() comments. 636 */ 637 void 638 traverseDir(int fd, int32_t startAt, int depth, int descend, int operation, 639 char matchRequired, struct pcdir **found, int32_t *lastDirCluster, 640 struct pcdir **dirEnd, char *recordPath, int *pathLen) 641 { 642 ClusterContents dirdata; 643 int32_t dirdatasize = 0; 644 645 if (startAt < FIRST_CLUSTER || startAt > LastCluster) 646 return; 647 648 if (readCluster(fd, startAt, &(dirdata.bytes), &dirdatasize, 649 RDCLUST_DO_CACHE) != RDCLUST_GOOD) { 650 (void) fprintf(stderr, 651 gettext("Unable to get more directory entries!\n")); 652 return; 653 } 654 655 if (operation == PCFS_TRAVERSE_ALL) { 656 if (Verbose) 657 (void) fprintf(stderr, 658 gettext("Directory traversal enters " 659 "allocation unit %d.\n"), startAt); 660 } 661 visitNodes(fd, startAt, &dirdata, dirdatasize, depth, descend, 662 operation, matchRequired, found, lastDirCluster, dirEnd, 663 recordPath, pathLen); 664 } 665 666 void 667 createCHKNameList(int fd) 668 { 669 struct pcdir *ignorep1, *ignorep2; 670 int32_t ignore32; 671 char *ignorecp = NULL; 672 char ignore = '\0'; 673 int ignoreint = 0; 674 675 ignorep1 = ignorep2 = NULL; 676 if (!OkayToRelink || CHKsList != NULL) 677 return; 678 679 /* 680 * Allocate an array to keep a bit map of the integer 681 * values used in CHK names. 682 */ 683 if ((CHKsList = 684 (uchar_t *)calloc(1, idivceil(MAXCHKVAL, NBBY))) == NULL) { 685 OkayToRelink = 0; 686 return; 687 } 688 689 /* 690 * Search the root directory for all the files with names of 691 * the form FILEXXXX.CHK. The root directory is an area 692 * outside of the file space on FAT12 and FAT16 file systems. 693 * On FAT32 file systems, the root directory is in a file 694 * area cluster just like any other directory. 695 */ 696 if (!IsFAT32) { 697 traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS, 698 ignore, &ignorep1, &ignore32, &ignorep2, ignorecp, 699 &ignoreint); 700 } else { 701 DirCount++; 702 traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust, 703 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS, ignore, 704 &ignorep1, &ignore32, &ignorep2, ignorecp, &ignoreint); 705 } 706 } 707 708 709 char * 710 nextAvailableCHKName(int *chosen) 711 { 712 static char nameBuf[PCFNAMESIZE]; 713 int i; 714 715 if (!OkayToRelink) 716 return (NULL); 717 718 nameBuf[CHKNAME_F] = 'F'; 719 nameBuf[CHKNAME_I] = 'I'; 720 nameBuf[CHKNAME_L] = 'L'; 721 nameBuf[CHKNAME_E] = 'E'; 722 723 for (i = 1; i <= MAXCHKVAL; i++) { 724 if (!inUseCHKName(i)) 725 break; 726 } 727 if (i <= MAXCHKVAL) { 728 nameBuf[CHKNAME_THOUSANDS] = '0' + (i / 1000); 729 nameBuf[CHKNAME_HUNDREDS] = '0' + ((i % 1000) / 100); 730 nameBuf[CHKNAME_TENS] = '0' + ((i % 100) / 10); 731 nameBuf[CHKNAME_ONES] = '0' + (i % 10); 732 *chosen = i; 733 return (nameBuf); 734 } else { 735 (void) fprintf(stderr, 736 gettext("Sorry, no names available for " 737 "relinking orphan chains!\n")); 738 OkayToRelink = 0; 739 return (NULL); 740 } 741 } 742 743 uint32_t 744 extractSize(struct pcdir *dp) 745 { 746 uint32_t returnMe; 747 748 read_32_bits((uchar_t *)&(dp->pcd_size), &returnMe); 749 return (returnMe); 750 } 751 752 int32_t 753 extractStartCluster(struct pcdir *dp) 754 { 755 uint32_t lo, hi; 756 757 if (IsFAT32) { 758 read_16_bits((uchar_t *)&(dp->un.pcd_scluster_hi), &hi); 759 read_16_bits((uchar_t *)&(dp->pcd_scluster_lo), &lo); 760 return ((int32_t)((hi << 16) | lo)); 761 } else { 762 read_16_bits((uchar_t *)&(dp->pcd_scluster_lo), &lo); 763 return ((int32_t)lo); 764 } 765 } 766 767 static struct pcdir * 768 findAvailableRootDirEntSlot(int fd, int32_t *clusterWithSlot) 769 { 770 struct pcdir *deletedEntry = NULL; 771 struct pcdir *appendPoint = NULL; 772 char *ignorecp = NULL; 773 int ignore = 0; 774 775 *clusterWithSlot = 0; 776 777 /* 778 * First off, try to find an erased entry in the root 779 * directory. The root directory is an area outside of the 780 * file space on FAT12 and FAT16 file systems. On FAT32 file 781 * systems, the root directory is in a file area cluster just 782 * like any other directory. 783 */ 784 if (!IsFAT32) { 785 traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS, 786 PCD_ERASED, &deletedEntry, clusterWithSlot, 787 &appendPoint, ignorecp, &ignore); 788 } else { 789 DirCount++; 790 traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust, 791 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS, PCD_ERASED, 792 &deletedEntry, clusterWithSlot, &appendPoint, ignorecp, 793 &ignore); 794 } 795 /* 796 * If we found a deleted file in the directory we'll overwrite 797 * that entry. 798 */ 799 if (deletedEntry) 800 return (deletedEntry); 801 /* 802 * If there is room at the end of the existing directory, we 803 * should place the new entry there. 804 */ 805 if (appendPoint) 806 return (appendPoint); 807 /* 808 * XXX need to grow the directory 809 */ 810 return (NULL); 811 } 812 813 static void 814 insertDirEnt(struct pcdir *slot, struct pcdir *entry, int32_t clusterWithSlot) 815 { 816 (void) memcpy(slot, entry, sizeof (struct pcdir)); 817 markClusterModified(clusterWithSlot); 818 } 819 820 /* 821 * Convert current UNIX time into a PCFS timestamp (which is in local time). 822 * 823 * Since the "seconds" field of that is only accurate to 2sec precision, 824 * we allow for the optional (used only for creation times on FAT) "msec" 825 * parameter that takes the fractional part. 826 */ 827 static void 828 getNow(struct pctime *pctp, uchar_t *msec) 829 { 830 time_t now; 831 struct tm tm; 832 ushort_t tim, dat; 833 834 /* 835 * Disable daylight savings corrections - Solaris PCFS doesn't 836 * support such conversions yet. Save timestamps in local time. 837 */ 838 daylight = 0; 839 840 (void) time(&now); 841 (void) localtime_r(&now, &tm); 842 843 dat = (tm.tm_year - 80) << YEARSHIFT; 844 dat |= tm.tm_mon << MONSHIFT; 845 dat |= tm.tm_mday << DAYSHIFT; 846 tim = tm.tm_hour << HOURSHIFT; 847 tim |= tm.tm_min << MINSHIFT; 848 tim |= (tm.tm_sec / 2) << SECSHIFT; 849 850 /* 851 * Sanity check. If we overflow the PCFS timestamp range 852 * we set the time to 01/01/1980, 00:00:00 853 */ 854 if (dat < 80 || dat > 227) 855 dat = tim = 0; 856 857 pctp->pct_date = LE_16(dat); 858 pctp->pct_time = LE_16(tim); 859 if (msec) 860 *msec = (tm.tm_sec & 1) ? 100 : 0; 861 } 862 863 /* 864 * FAT file systems store the following time information in a directory 865 * entry: 866 * timestamp member of "struct pcdir" 867 * ====================================================================== 868 * creation time pcd_crtime.pct_time 869 * creation date pcd_crtime.pct_date 870 * last access date pcd_ladate 871 * last modify time pcd_mtime.pct_time 872 * last modify date pcd_mtime.pct_date 873 * 874 * No access time is kept. 875 */ 876 static void 877 updateDirEnt_CreatTime(struct pcdir *dp) 878 { 879 getNow(&dp->pcd_crtime, &dp->pcd_crtime_msec); 880 markClusterModified(findImpactedCluster(dp)); 881 } 882 883 static void 884 updateDirEnt_ModTimes(struct pcdir *dp) 885 { 886 timestruc_t ts; 887 888 getNow(&dp->pcd_mtime, NULL); 889 dp->pcd_ladate = dp->pcd_mtime.pct_date; 890 dp->pcd_attr |= PCA_ARCH; 891 markClusterModified(findImpactedCluster(dp)); 892 } 893 894 struct pcdir * 895 addRootDirEnt(int fd, struct pcdir *new) 896 { 897 struct pcdir *added; 898 int32_t inCluster; 899 900 if ((added = findAvailableRootDirEntSlot(fd, &inCluster)) != NULL) { 901 insertDirEnt(added, new, inCluster); 902 return (added); 903 } 904 return (NULL); 905 } 906 907 /* 908 * FAT12 and FAT16 have a root directory outside the normal file space, 909 * so we have separate routines for finding and reading the root directory. 910 */ 911 static off64_t 912 seekRootDirectory(int fd) 913 { 914 off64_t seekto; 915 916 /* 917 * The RootDir immediately follows the FATs, which in 918 * turn immediately follow the reserved sectors. 919 */ 920 seekto = (off64_t)TheBIOSParameterBlock.bpb.resv_sectors * 921 TheBIOSParameterBlock.bpb.bytes_per_sector + 922 (off64_t)FATSize * TheBIOSParameterBlock.bpb.num_fats + 923 (off64_t)PartitionOffset; 924 if (Verbose) 925 (void) fprintf(stderr, 926 gettext("Seeking root directory @%lld.\n"), seekto); 927 return (lseek64(fd, seekto, SEEK_SET)); 928 } 929 930 void 931 getRootDirectory(int fd) 932 { 933 ssize_t bytesRead; 934 935 if (TheRootDir.bytes != NULL) 936 return; 937 else if ((TheRootDir.bytes = (uchar_t *)malloc(RootDirSize)) == NULL) { 938 mountSanityCheckFails(); 939 perror(gettext("No memory for a copy of the root directory")); 940 (void) close(fd); 941 exit(8); 942 } 943 944 if (seekRootDirectory(fd) < 0) { 945 mountSanityCheckFails(); 946 perror(gettext("Cannot seek to RootDir")); 947 (void) close(fd); 948 exit(8); 949 } 950 951 if (Verbose) 952 (void) fprintf(stderr, 953 gettext("Reading root directory.\n")); 954 if ((bytesRead = read(fd, TheRootDir.bytes, RootDirSize)) != 955 RootDirSize) { 956 mountSanityCheckFails(); 957 if (bytesRead < 0) { 958 perror(gettext("Cannot read a RootDir")); 959 } else { 960 (void) fprintf(stderr, 961 gettext("Short read of RootDir\n")); 962 } 963 (void) close(fd); 964 exit(8); 965 } 966 if (Verbose) { 967 (void) fprintf(stderr, 968 gettext("Dump of root dir's first 256 bytes.\n")); 969 header_for_dump(); 970 dump_bytes(TheRootDir.bytes, 256); 971 } 972 } 973 974 void 975 writeRootDirMods(int fd) 976 { 977 ssize_t bytesWritten; 978 979 if (!TheRootDir.bytes) { 980 (void) fprintf(stderr, 981 gettext("Internal error: No Root directory to write\n")); 982 (void) close(fd); 983 exit(12); 984 } 985 if (!RootDirModified) { 986 if (Verbose) { 987 (void) fprintf(stderr, 988 gettext("No root directory changes need to " 989 "be written.\n")); 990 } 991 return; 992 } 993 if (ReadOnly) 994 return; 995 if (Verbose) 996 (void) fprintf(stderr, 997 gettext("Writing root directory.\n")); 998 if (seekRootDirectory(fd) < 0) { 999 perror(gettext("Cannot write the RootDir (seek failed)")); 1000 (void) close(fd); 1001 exit(12); 1002 } 1003 if ((bytesWritten = write(fd, TheRootDir.bytes, RootDirSize)) != 1004 RootDirSize) { 1005 if (bytesWritten < 0) { 1006 perror(gettext("Cannot write the RootDir")); 1007 } else { 1008 (void) fprintf(stderr, 1009 gettext("Short write of root directory\n")); 1010 } 1011 (void) close(fd); 1012 exit(12); 1013 } 1014 RootDirModified = 0; 1015 } 1016 1017 struct pcdir * 1018 newDirEnt(struct pcdir *copyme) 1019 { 1020 struct pcdir *ndp; 1021 1022 if ((ndp = (struct pcdir *)calloc(1, sizeof (struct pcdir))) == NULL) { 1023 (void) fprintf(stderr, gettext("Out of memory to create a " 1024 "new directory entry!\n")); 1025 return (ndp); 1026 } 1027 if (copyme) 1028 (void) memcpy(ndp, copyme, sizeof (struct pcdir)); 1029 ndp->pcd_ext[CHKNAME_C] = 'C'; 1030 ndp->pcd_ext[CHKNAME_H] = 'H'; 1031 ndp->pcd_ext[CHKNAME_K] = 'K'; 1032 updateDirEnt_CreatTime(ndp); 1033 updateDirEnt_ModTimes(ndp); 1034 return (ndp); 1035 } 1036 1037 void 1038 updateDirEnt_Size(struct pcdir *dp, uint32_t newSize) 1039 { 1040 uchar_t *p = (uchar_t *)&(dp->pcd_size); 1041 store_32_bits(&p, newSize); 1042 markClusterModified(findImpactedCluster(dp)); 1043 } 1044 1045 void 1046 updateDirEnt_Start(struct pcdir *dp, int32_t newStart) 1047 { 1048 uchar_t *p = (uchar_t *)&(dp->pcd_scluster_lo); 1049 store_16_bits(&p, newStart & 0xffff); 1050 if (IsFAT32) { 1051 p = (uchar_t *)&(dp->un.pcd_scluster_hi); 1052 store_16_bits(&p, newStart >> 16); 1053 } 1054 markClusterModified(findImpactedCluster(dp)); 1055 } 1056 1057 void 1058 updateDirEnt_Name(struct pcdir *dp, char *newName) 1059 { 1060 int i; 1061 1062 for (i = 0; i < PCFNAMESIZE; i++) { 1063 if (*newName) 1064 dp->pcd_filename[i] = *newName++; 1065 else 1066 dp->pcd_filename[i] = ' '; 1067 } 1068 markClusterModified(findImpactedCluster(dp)); 1069 } 1070