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