10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*2720Sfrankho * Common Development and Distribution License (the "License").
6*2720Sfrankho * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*2720Sfrankho * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23*2720Sfrankho * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
270Sstevel@tonic-gate
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate * fsck_pcfs -- routines for manipulating directories.
300Sstevel@tonic-gate */
310Sstevel@tonic-gate #include <stdio.h>
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <unistd.h>
340Sstevel@tonic-gate #include <stdlib.h>
350Sstevel@tonic-gate #include <libintl.h>
360Sstevel@tonic-gate #include <ctype.h>
37*2720Sfrankho #include <time.h>
380Sstevel@tonic-gate #include <sys/param.h>
390Sstevel@tonic-gate #include <sys/time.h>
40*2720Sfrankho #include <sys/byteorder.h>
410Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
420Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
430Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
440Sstevel@tonic-gate #include <sys/fs/pc_label.h>
450Sstevel@tonic-gate #include "pcfs_common.h"
460Sstevel@tonic-gate #include "fsck_pcfs.h"
470Sstevel@tonic-gate
480Sstevel@tonic-gate extern int32_t HiddenClusterCount;
490Sstevel@tonic-gate extern int32_t FileClusterCount;
500Sstevel@tonic-gate extern int32_t DirClusterCount;
510Sstevel@tonic-gate extern int32_t HiddenFileCount;
520Sstevel@tonic-gate extern int32_t LastCluster;
530Sstevel@tonic-gate extern int32_t FileCount;
540Sstevel@tonic-gate extern int32_t BadCount;
550Sstevel@tonic-gate extern int32_t DirCount;
560Sstevel@tonic-gate extern int32_t FATSize;
570Sstevel@tonic-gate extern off64_t PartitionOffset;
580Sstevel@tonic-gate extern bpb_t TheBIOSParameterBlock;
590Sstevel@tonic-gate extern int ReadOnly;
600Sstevel@tonic-gate extern int IsFAT32;
610Sstevel@tonic-gate extern int Verbose;
620Sstevel@tonic-gate
630Sstevel@tonic-gate static uchar_t *CHKsList = NULL;
640Sstevel@tonic-gate
650Sstevel@tonic-gate ClusterContents TheRootDir;
660Sstevel@tonic-gate int32_t RootDirSize;
670Sstevel@tonic-gate int RootDirModified;
680Sstevel@tonic-gate int OkayToRelink = 1;
690Sstevel@tonic-gate
700Sstevel@tonic-gate /*
710Sstevel@tonic-gate * We have a bunch of routines for handling CHK names. A CHK name is
720Sstevel@tonic-gate * simply a file name of the form "FILEnnnn.CHK", where the n's are the
730Sstevel@tonic-gate * digits in the numbers from 1 to 9999. There are always four digits
740Sstevel@tonic-gate * used, leading zeros are added as necessary.
750Sstevel@tonic-gate *
760Sstevel@tonic-gate * We use CHK names to link orphaned cluster chains back into the file
770Sstevel@tonic-gate * system's root directory under an auspicious name so that the user
780Sstevel@tonic-gate * may be able to recover some of their data.
790Sstevel@tonic-gate *
800Sstevel@tonic-gate * We use these routines to ensure CHK names we use don't conflict
810Sstevel@tonic-gate * with any already present in the file system.
820Sstevel@tonic-gate */
830Sstevel@tonic-gate static int
hasCHKName(struct pcdir * dp)840Sstevel@tonic-gate hasCHKName(struct pcdir *dp)
850Sstevel@tonic-gate {
860Sstevel@tonic-gate return (dp->pcd_filename[CHKNAME_F] == 'F' &&
870Sstevel@tonic-gate dp->pcd_filename[CHKNAME_I] == 'I' &&
880Sstevel@tonic-gate dp->pcd_filename[CHKNAME_L] == 'L' &&
890Sstevel@tonic-gate dp->pcd_filename[CHKNAME_E] == 'E' &&
900Sstevel@tonic-gate isdigit(dp->pcd_filename[CHKNAME_THOUSANDS]) &&
910Sstevel@tonic-gate isdigit(dp->pcd_filename[CHKNAME_HUNDREDS]) &&
920Sstevel@tonic-gate isdigit(dp->pcd_filename[CHKNAME_TENS]) &&
930Sstevel@tonic-gate isdigit(dp->pcd_filename[CHKNAME_ONES]) &&
940Sstevel@tonic-gate dp->pcd_ext[CHKNAME_C] == 'C' &&
950Sstevel@tonic-gate dp->pcd_ext[CHKNAME_H] == 'H' &&
960Sstevel@tonic-gate dp->pcd_ext[CHKNAME_K] == 'K');
970Sstevel@tonic-gate }
980Sstevel@tonic-gate
990Sstevel@tonic-gate void
addEntryToCHKList(int chkNumber)1000Sstevel@tonic-gate addEntryToCHKList(int chkNumber)
1010Sstevel@tonic-gate {
1020Sstevel@tonic-gate /* silent failure on bogus value */
1030Sstevel@tonic-gate if (chkNumber < 0 || chkNumber > MAXCHKVAL)
1040Sstevel@tonic-gate return;
1050Sstevel@tonic-gate CHKsList[chkNumber / NBBY] |= (1 << (chkNumber % NBBY));
1060Sstevel@tonic-gate }
1070Sstevel@tonic-gate
1080Sstevel@tonic-gate static void
addToCHKList(struct pcdir * dp)1090Sstevel@tonic-gate addToCHKList(struct pcdir *dp)
1100Sstevel@tonic-gate {
1110Sstevel@tonic-gate int chknum;
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate chknum = 1000 * (dp->pcd_filename[CHKNAME_THOUSANDS] - '0');
1140Sstevel@tonic-gate chknum += 100 * (dp->pcd_filename[CHKNAME_HUNDREDS] - '0');
1150Sstevel@tonic-gate chknum += 10 * (dp->pcd_filename[CHKNAME_TENS] - '0');
1160Sstevel@tonic-gate chknum += (dp->pcd_filename[CHKNAME_ONES] - '0');
1170Sstevel@tonic-gate addEntryToCHKList(chknum);
1180Sstevel@tonic-gate }
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate static int
inUseCHKName(int chkNumber)1210Sstevel@tonic-gate inUseCHKName(int chkNumber)
1220Sstevel@tonic-gate {
1230Sstevel@tonic-gate return (CHKsList[chkNumber / NBBY] & (1 << (chkNumber % NBBY)));
1240Sstevel@tonic-gate }
1250Sstevel@tonic-gate
1260Sstevel@tonic-gate static void
appendToPath(struct pcdir * dp,char * thePath,int * theLen)1270Sstevel@tonic-gate appendToPath(struct pcdir *dp, char *thePath, int *theLen)
1280Sstevel@tonic-gate {
1290Sstevel@tonic-gate int i = 0;
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate /*
1320Sstevel@tonic-gate * Sometimes caller doesn't care about keeping track of the path
1330Sstevel@tonic-gate */
1340Sstevel@tonic-gate if (thePath == NULL)
1350Sstevel@tonic-gate return;
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate /*
1380Sstevel@tonic-gate * Prepend /
1390Sstevel@tonic-gate */
1400Sstevel@tonic-gate if (*theLen < MAXPATHLEN)
1410Sstevel@tonic-gate *(thePath + (*theLen)++) = '/';
1420Sstevel@tonic-gate /*
1430Sstevel@tonic-gate * Print out the file name part, but only up to the first
1440Sstevel@tonic-gate * space.
1450Sstevel@tonic-gate */
1460Sstevel@tonic-gate while (*theLen < MAXPATHLEN && i < PCFNAMESIZE) {
1470Sstevel@tonic-gate /*
1480Sstevel@tonic-gate * When we start seeing spaces we assume that's the
1490Sstevel@tonic-gate * end of the interesting characters in the name.
1500Sstevel@tonic-gate */
1510Sstevel@tonic-gate if ((dp->pcd_filename[i] == ' ') ||
1520Sstevel@tonic-gate !(pc_validchar(dp->pcd_filename[i])))
1530Sstevel@tonic-gate break;
1540Sstevel@tonic-gate *(thePath + (*theLen)++) = dp->pcd_filename[i++];
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate * Leave now, if we don't have an extension (or room for one)
1580Sstevel@tonic-gate */
1590Sstevel@tonic-gate if ((dp->pcd_ext[i] == ' ') || ((*theLen) >= MAXPATHLEN) ||
1600Sstevel@tonic-gate (!(pc_validchar(dp->pcd_ext[i]))))
1610Sstevel@tonic-gate return;
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate * Tack on the extension
1640Sstevel@tonic-gate */
1650Sstevel@tonic-gate *(thePath + (*theLen)++) = '.';
1660Sstevel@tonic-gate i = 0;
1670Sstevel@tonic-gate while ((*theLen < MAXPATHLEN) && (i < PCFEXTSIZE)) {
1680Sstevel@tonic-gate if ((dp->pcd_ext[i] == ' ') || !(pc_validchar(dp->pcd_ext[i])))
1690Sstevel@tonic-gate break;
1700Sstevel@tonic-gate *(thePath + (*theLen)++) = dp->pcd_ext[i++];
1710Sstevel@tonic-gate }
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate static void
printName(FILE * outDest,struct pcdir * dp)1750Sstevel@tonic-gate printName(FILE *outDest, struct pcdir *dp)
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate int i;
1780Sstevel@tonic-gate for (i = 0; i < PCFNAMESIZE; i++) {
1790Sstevel@tonic-gate if ((dp->pcd_filename[i] == ' ') ||
1800Sstevel@tonic-gate !(pc_validchar(dp->pcd_filename[i])))
1810Sstevel@tonic-gate break;
1820Sstevel@tonic-gate (void) fprintf(outDest, "%c", dp->pcd_filename[i]);
1830Sstevel@tonic-gate }
1840Sstevel@tonic-gate (void) fprintf(outDest, ".");
1850Sstevel@tonic-gate for (i = 0; i < PCFEXTSIZE; i++) {
1860Sstevel@tonic-gate if (!(pc_validchar(dp->pcd_ext[i])))
1870Sstevel@tonic-gate break;
1880Sstevel@tonic-gate (void) fprintf(outDest, "%c", dp->pcd_ext[i]);
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate * sanityCheckSize
1940Sstevel@tonic-gate * Make sure the size in the directory entry matches what is
1950Sstevel@tonic-gate * actually allocated. If there is a mismatch, orphan all
1960Sstevel@tonic-gate * the allocated clusters. Returns SIZE_MATCHED if everything matches
1970Sstevel@tonic-gate * up, TRUNCATED to indicate truncation was necessary.
1980Sstevel@tonic-gate */
1990Sstevel@tonic-gate static int
sanityCheckSize(int fd,struct pcdir * dp,int32_t actualClusterCount,int isDir,int32_t startCluster,struct nameinfo * fullPathName,struct pcdir ** orphanEntry)2000Sstevel@tonic-gate sanityCheckSize(int fd, struct pcdir *dp, int32_t actualClusterCount,
2010Sstevel@tonic-gate int isDir, int32_t startCluster, struct nameinfo *fullPathName,
2020Sstevel@tonic-gate struct pcdir **orphanEntry)
2030Sstevel@tonic-gate {
2040Sstevel@tonic-gate uint32_t sizeFromDir;
2050Sstevel@tonic-gate int32_t ignorei = 0;
2060Sstevel@tonic-gate int64_t bpc;
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster *
2090Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector;
2100Sstevel@tonic-gate sizeFromDir = extractSize(dp);
2110Sstevel@tonic-gate if (isDir) {
2120Sstevel@tonic-gate if (sizeFromDir == 0)
2130Sstevel@tonic-gate return (SIZE_MATCHED);
2140Sstevel@tonic-gate } else {
2150Sstevel@tonic-gate if ((sizeFromDir > ((actualClusterCount - 1) * bpc)) &&
2160Sstevel@tonic-gate (sizeFromDir <= (actualClusterCount * bpc)))
2170Sstevel@tonic-gate return (SIZE_MATCHED);
2180Sstevel@tonic-gate }
2190Sstevel@tonic-gate if (fullPathName != NULL) {
2200Sstevel@tonic-gate fullPathName->references++;
2210Sstevel@tonic-gate (void) fprintf(stderr, "%s\n", fullPathName->fullName);
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate squirrelPath(fullPathName, startCluster);
2240Sstevel@tonic-gate (void) fprintf(stderr,
2250Sstevel@tonic-gate gettext("Truncating chain due to incorrect size "
2260Sstevel@tonic-gate "in directory. Size from directory = %u bytes,\n"), sizeFromDir);
2270Sstevel@tonic-gate if (actualClusterCount == 0) {
2280Sstevel@tonic-gate (void) fprintf(stderr,
2290Sstevel@tonic-gate gettext("Zero bytes are allocated to the file.\n"));
2300Sstevel@tonic-gate } else {
2310Sstevel@tonic-gate (void) fprintf(stderr,
2320Sstevel@tonic-gate gettext("Allocated size in range %llu - %llu bytes.\n"),
2330Sstevel@tonic-gate ((actualClusterCount - 1) * bpc) + 1,
2340Sstevel@tonic-gate (actualClusterCount * bpc));
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate /*
2370Sstevel@tonic-gate * Use splitChain() to make an orphan that is the entire allocation
2380Sstevel@tonic-gate * chain.
2390Sstevel@tonic-gate */
2400Sstevel@tonic-gate splitChain(fd, dp, startCluster, orphanEntry, &ignorei);
2410Sstevel@tonic-gate return (TRUNCATED);
2420Sstevel@tonic-gate }
2430Sstevel@tonic-gate
2440Sstevel@tonic-gate static int
noteUsage(int fd,int32_t startAt,struct pcdir * dp,struct pcdir * lp,int32_t longEntryStartCluster,int isHidden,int isDir,struct nameinfo * fullPathName)2450Sstevel@tonic-gate noteUsage(int fd, int32_t startAt, struct pcdir *dp, struct pcdir *lp,
2460Sstevel@tonic-gate int32_t longEntryStartCluster, int isHidden, int isDir,
2470Sstevel@tonic-gate struct nameinfo *fullPathName)
2480Sstevel@tonic-gate {
2490Sstevel@tonic-gate struct pcdir *orphanEntry;
2500Sstevel@tonic-gate int32_t chain = startAt;
2510Sstevel@tonic-gate int32_t count = 0;
2520Sstevel@tonic-gate int savePathNextIteration = 0;
2530Sstevel@tonic-gate int haveBad = 0;
2540Sstevel@tonic-gate ClusterInfo *tmpl = NULL;
2550Sstevel@tonic-gate
2560Sstevel@tonic-gate while ((chain >= FIRST_CLUSTER) && (chain <= LastCluster)) {
2570Sstevel@tonic-gate if ((markInUse(fd, chain, dp, lp, longEntryStartCluster,
2580Sstevel@tonic-gate isHidden ? HIDDEN : VISIBLE, &tmpl))
2590Sstevel@tonic-gate != CLINFO_NEWLY_ALLOCED)
2600Sstevel@tonic-gate break;
2610Sstevel@tonic-gate count++;
2620Sstevel@tonic-gate if (savePathNextIteration == 1) {
2630Sstevel@tonic-gate savePathNextIteration = 0;
2640Sstevel@tonic-gate if (fullPathName != NULL)
2650Sstevel@tonic-gate fullPathName->references++;
2660Sstevel@tonic-gate squirrelPath(fullPathName, chain);
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate if (isMarkedBad(chain)) {
2690Sstevel@tonic-gate haveBad = 1;
2700Sstevel@tonic-gate savePathNextIteration = 1;
2710Sstevel@tonic-gate }
2720Sstevel@tonic-gate if (isHidden)
2730Sstevel@tonic-gate HiddenClusterCount++;
2740Sstevel@tonic-gate else if (isDir)
2750Sstevel@tonic-gate DirClusterCount++;
2760Sstevel@tonic-gate else
2770Sstevel@tonic-gate FileClusterCount++;
2780Sstevel@tonic-gate chain = nextInChain(chain);
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate /*
2810Sstevel@tonic-gate * Do a sanity check on the file size in the directory entry.
2820Sstevel@tonic-gate * This may create an orphaned cluster chain.
2830Sstevel@tonic-gate */
2840Sstevel@tonic-gate if (sanityCheckSize(fd, dp, count, isDir, startAt,
2850Sstevel@tonic-gate fullPathName, &orphanEntry) == TRUNCATED) {
2860Sstevel@tonic-gate /*
2870Sstevel@tonic-gate * The pre-existing directory entry has been truncated,
2880Sstevel@tonic-gate * so the chain associated with it no longer has any
2890Sstevel@tonic-gate * bad clusters. Instead, the new orphan has them.
2900Sstevel@tonic-gate */
2910Sstevel@tonic-gate if (haveBad > 0) {
2920Sstevel@tonic-gate truncChainWithBadCluster(fd, orphanEntry, startAt);
2930Sstevel@tonic-gate }
2940Sstevel@tonic-gate haveBad = 0;
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate return (haveBad);
2970Sstevel@tonic-gate }
2980Sstevel@tonic-gate
2990Sstevel@tonic-gate static void
storeInfoAboutEntry(int fd,struct pcdir * dp,struct pcdir * ldp,int depth,int32_t longEntryStartCluster,char * fullPath,int * fullLen)3000Sstevel@tonic-gate storeInfoAboutEntry(int fd, struct pcdir *dp, struct pcdir *ldp, int depth,
3010Sstevel@tonic-gate int32_t longEntryStartCluster, char *fullPath, int *fullLen)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate struct nameinfo *pathCopy;
3040Sstevel@tonic-gate int32_t start;
3050Sstevel@tonic-gate int haveBad;
3060Sstevel@tonic-gate int hidden = (dp->pcd_attr & PCA_HIDDEN || dp->pcd_attr & PCA_SYSTEM);
3070Sstevel@tonic-gate int dir = (dp->pcd_attr & PCA_DIR);
3080Sstevel@tonic-gate int i;
3090Sstevel@tonic-gate
3100Sstevel@tonic-gate if (hidden)
3110Sstevel@tonic-gate HiddenFileCount++;
3120Sstevel@tonic-gate else if (dir)
3130Sstevel@tonic-gate DirCount++;
3140Sstevel@tonic-gate else
3150Sstevel@tonic-gate FileCount++;
3160Sstevel@tonic-gate appendToPath(dp, fullPath, fullLen);
3170Sstevel@tonic-gate
3180Sstevel@tonic-gate /*
3190Sstevel@tonic-gate * Make a copy of the name at this point. We may want it to
3200Sstevel@tonic-gate * note the original source of an orphaned cluster.
3210Sstevel@tonic-gate */
3220Sstevel@tonic-gate if ((pathCopy =
3230Sstevel@tonic-gate (struct nameinfo *)malloc(sizeof (struct nameinfo))) != NULL) {
3240Sstevel@tonic-gate if ((pathCopy->fullName =
3250Sstevel@tonic-gate (char *)malloc(*fullLen + 1)) != NULL) {
3260Sstevel@tonic-gate pathCopy->references = 0;
3270Sstevel@tonic-gate (void) strncpy(pathCopy->fullName, fullPath, *fullLen);
3280Sstevel@tonic-gate pathCopy->fullName[*fullLen] = '\0';
3290Sstevel@tonic-gate } else {
3300Sstevel@tonic-gate free(pathCopy);
3310Sstevel@tonic-gate pathCopy = NULL;
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate }
3340Sstevel@tonic-gate if (Verbose) {
3350Sstevel@tonic-gate for (i = 0; i < depth; i++)
3360Sstevel@tonic-gate (void) fprintf(stderr, " ");
3370Sstevel@tonic-gate if (hidden)
3380Sstevel@tonic-gate (void) fprintf(stderr, "[");
3390Sstevel@tonic-gate else if (dir)
3400Sstevel@tonic-gate (void) fprintf(stderr, "|_");
3410Sstevel@tonic-gate else
3420Sstevel@tonic-gate (void) fprintf(stderr, gettext("(%06d) "), FileCount);
3430Sstevel@tonic-gate printName(stderr, dp);
3440Sstevel@tonic-gate if (hidden)
3450Sstevel@tonic-gate (void) fprintf(stderr, "]");
3460Sstevel@tonic-gate (void) fprintf(stderr,
3470Sstevel@tonic-gate gettext(", %u bytes, start cluster %d"),
3480Sstevel@tonic-gate extractSize(dp), extractStartCluster(dp));
3490Sstevel@tonic-gate (void) fprintf(stderr, "\n");
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate start = extractStartCluster(dp);
3520Sstevel@tonic-gate haveBad = noteUsage(fd, start, dp, ldp, longEntryStartCluster,
3530Sstevel@tonic-gate hidden, dir, pathCopy);
3540Sstevel@tonic-gate if (haveBad > 0) {
3550Sstevel@tonic-gate if (dir && pathCopy->fullName != NULL) {
3560Sstevel@tonic-gate (void) fprintf(stderr,
3570Sstevel@tonic-gate gettext("Adjusting for bad allocation units in "
3580Sstevel@tonic-gate "the meta-data of:\n "));
3590Sstevel@tonic-gate (void) fprintf(stderr, pathCopy->fullName);
3600Sstevel@tonic-gate (void) fprintf(stderr, "\n");
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate truncChainWithBadCluster(fd, dp, start);
3630Sstevel@tonic-gate }
3640Sstevel@tonic-gate if ((pathCopy != NULL) && (pathCopy->references == 0)) {
3650Sstevel@tonic-gate free(pathCopy->fullName);
3660Sstevel@tonic-gate free(pathCopy);
3670Sstevel@tonic-gate }
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate static void
storeInfoAboutLabel(struct pcdir * dp)3710Sstevel@tonic-gate storeInfoAboutLabel(struct pcdir *dp)
3720Sstevel@tonic-gate {
3730Sstevel@tonic-gate /*
3740Sstevel@tonic-gate * XXX eventually depth should be passed to this routine just
3750Sstevel@tonic-gate * as it is with storeInfoAboutEntry(). If it isn't zero, then
3760Sstevel@tonic-gate * we've got a bogus directory entry.
3770Sstevel@tonic-gate */
3780Sstevel@tonic-gate if (Verbose) {
3790Sstevel@tonic-gate (void) fprintf(stderr, gettext("** "));
3800Sstevel@tonic-gate printName(stderr, dp);
3810Sstevel@tonic-gate (void) fprintf(stderr, gettext(" **\n"));
3820Sstevel@tonic-gate }
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate
3850Sstevel@tonic-gate static void
searchChecks(struct pcdir * dp,int operation,char matchRequired,struct pcdir ** found)3860Sstevel@tonic-gate searchChecks(struct pcdir *dp, int operation, char matchRequired,
3870Sstevel@tonic-gate struct pcdir **found)
3880Sstevel@tonic-gate {
3890Sstevel@tonic-gate /*
3900Sstevel@tonic-gate * We support these searching operations:
3910Sstevel@tonic-gate *
3920Sstevel@tonic-gate * PCFS_FIND_ATTR
3930Sstevel@tonic-gate * look for the first file with a certain attribute
3940Sstevel@tonic-gate * (e.g, find all hidden files)
3950Sstevel@tonic-gate * PCFS_FIND_STATUS
3960Sstevel@tonic-gate * look for the first file with a certain status
3970Sstevel@tonic-gate * (e.g., the file has been marked deleted; making
3980Sstevel@tonic-gate * its directory entry reusable)
3990Sstevel@tonic-gate * PCFS_FIND_CHKS
4000Sstevel@tonic-gate * look for all files with short names of the form
4010Sstevel@tonic-gate * FILENNNN.CHK. These are the file names we give
4020Sstevel@tonic-gate * to chains of orphaned clusters we relink into the
4030Sstevel@tonic-gate * file system. This find facility allows us to seek
4040Sstevel@tonic-gate * out all existing files of this naming form so that
4050Sstevel@tonic-gate * we may create unique file names for new orphans.
4060Sstevel@tonic-gate */
4070Sstevel@tonic-gate if (operation == PCFS_FIND_ATTR && dp->pcd_attr == matchRequired) {
4080Sstevel@tonic-gate *found = dp;
4090Sstevel@tonic-gate } else if (operation == PCFS_FIND_STATUS &&
4100Sstevel@tonic-gate dp->pcd_filename[0] == matchRequired) {
4110Sstevel@tonic-gate *found = dp;
4120Sstevel@tonic-gate } else if (operation == PCFS_FIND_CHKS && hasCHKName(dp)) {
4130Sstevel@tonic-gate addToCHKList(dp);
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate static void
catalogEntry(int fd,struct pcdir * dp,struct pcdir * longdp,int32_t currentCluster,int depth,char * recordPath,int * pathLen)4180Sstevel@tonic-gate catalogEntry(int fd, struct pcdir *dp, struct pcdir *longdp,
4190Sstevel@tonic-gate int32_t currentCluster, int depth, char *recordPath, int *pathLen)
4200Sstevel@tonic-gate {
4210Sstevel@tonic-gate if (dp->pcd_attr & PCA_LABEL) {
4220Sstevel@tonic-gate storeInfoAboutLabel(dp);
4230Sstevel@tonic-gate } else {
4240Sstevel@tonic-gate storeInfoAboutEntry(fd, dp, longdp, depth, currentCluster,
4250Sstevel@tonic-gate recordPath, pathLen);
4260Sstevel@tonic-gate }
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate /*
4300Sstevel@tonic-gate * visitNodes()
4310Sstevel@tonic-gate *
4320Sstevel@tonic-gate * This is the main workhouse routine for traversing pcfs metadata.
4330Sstevel@tonic-gate * There isn't a lot to the metadata. Basically there is a root
4340Sstevel@tonic-gate * directory somewhere (either in its own special place outside the
4350Sstevel@tonic-gate * data area or in a data cluster). The root directory (and all other
4360Sstevel@tonic-gate * directories) are filled with a number of fixed size entries. An
4370Sstevel@tonic-gate * entry has the filename and extension, the file's attributes, the
4380Sstevel@tonic-gate * file's size, and the starting data cluster of the storage allocated
4390Sstevel@tonic-gate * to the file. To determine which clusters are assigned to the file,
4400Sstevel@tonic-gate * you start at the starting cluster entry in the FAT, and follow the
4410Sstevel@tonic-gate * chain of entries in the FAT.
4420Sstevel@tonic-gate *
4430Sstevel@tonic-gate * Arguments are:
4440Sstevel@tonic-gate * fd
4450Sstevel@tonic-gate * descriptor for accessing the raw file system data
4460Sstevel@tonic-gate * currentCluster
4470Sstevel@tonic-gate * original caller supplies the initial starting cluster,
4480Sstevel@tonic-gate * subsequent recursive calls are made with updated
4490Sstevel@tonic-gate * cluster numbers for the sub-directories.
4500Sstevel@tonic-gate * dirData
4510Sstevel@tonic-gate * pointer to the directory data bytes
4520Sstevel@tonic-gate * dirDataLen
4530Sstevel@tonic-gate * size of the whole buffer of data bytes (usually it is
4540Sstevel@tonic-gate * the size of a cluster, but the root directory on
4550Sstevel@tonic-gate * FAT12/16 is not necessarily the same size as a cluster).
4560Sstevel@tonic-gate * depth
4570Sstevel@tonic-gate * original caller should set it to zero (assuming they are
4580Sstevel@tonic-gate * starting from the root directory). This number is used to
4590Sstevel@tonic-gate * change the indentation of file names presented as debug info.
4600Sstevel@tonic-gate * descend
4610Sstevel@tonic-gate * boolean indicates if we should descend into subdirectories.
4620Sstevel@tonic-gate * operation
4630Sstevel@tonic-gate * what, if any, matching should be performed.
4640Sstevel@tonic-gate * The PCFS_TRAVERSE_ALL operation is a depth first traversal
4650Sstevel@tonic-gate * of all nodes in the metadata tree, that tracks all the
4660Sstevel@tonic-gate * clusters in use (according to the meta-data, at least)
4670Sstevel@tonic-gate * matchRequired
4680Sstevel@tonic-gate * value to be matched (if any)
4690Sstevel@tonic-gate * found
4700Sstevel@tonic-gate * output parameter
4710Sstevel@tonic-gate * used to return pointer to a directory entry that matches
4720Sstevel@tonic-gate * the search requirement
4730Sstevel@tonic-gate * original caller should pass in a pointer to a NULL pointer.
4740Sstevel@tonic-gate * lastDirCluster
4750Sstevel@tonic-gate * output parameter
4760Sstevel@tonic-gate * if no match found, last cluster num of starting directory
4770Sstevel@tonic-gate * dirEnd
4780Sstevel@tonic-gate * output parameter
4790Sstevel@tonic-gate * if no match found, return parameter stores pointer to where
4800Sstevel@tonic-gate * new directory entry could be appended to existing directory
4810Sstevel@tonic-gate * recordPath
4820Sstevel@tonic-gate * output parameter
4830Sstevel@tonic-gate * as files are discovered, and directories traversed, this
4840Sstevel@tonic-gate * buffer is used to store the current full path name.
4850Sstevel@tonic-gate * pathLen
4860Sstevel@tonic-gate * output parameter
4870Sstevel@tonic-gate * this is in the integer length of the current full path name.
4880Sstevel@tonic-gate */
4890Sstevel@tonic-gate static void
visitNodes(int fd,int32_t currentCluster,ClusterContents * dirData,int32_t dirDataLen,int depth,int descend,int operation,char matchRequired,struct pcdir ** found,int32_t * lastDirCluster,struct pcdir ** dirEnd,char * recordPath,int * pathLen)4900Sstevel@tonic-gate visitNodes(int fd, int32_t currentCluster, ClusterContents *dirData,
4910Sstevel@tonic-gate int32_t dirDataLen, int depth, int descend, int operation,
4920Sstevel@tonic-gate char matchRequired, struct pcdir **found, int32_t *lastDirCluster,
4930Sstevel@tonic-gate struct pcdir **dirEnd, char *recordPath, int *pathLen)
4940Sstevel@tonic-gate {
4950Sstevel@tonic-gate struct pcdir *longdp = NULL;
4960Sstevel@tonic-gate struct pcdir *dp;
4970Sstevel@tonic-gate int32_t longStart;
4980Sstevel@tonic-gate int withinLongName = 0;
4990Sstevel@tonic-gate int saveLen = *pathLen;
5000Sstevel@tonic-gate
5010Sstevel@tonic-gate dp = dirData->dirp;
5020Sstevel@tonic-gate
5030Sstevel@tonic-gate /*
5040Sstevel@tonic-gate * A directory entry where the first character of the name is
5050Sstevel@tonic-gate * PCD_UNUSED indicates the end of the directory.
5060Sstevel@tonic-gate */
5070Sstevel@tonic-gate while ((uchar_t *)dp < dirData->bytes + dirDataLen &&
5080Sstevel@tonic-gate dp->pcd_filename[0] != PCD_UNUSED) {
5090Sstevel@tonic-gate /*
5100Sstevel@tonic-gate * Handle the special case find operations.
5110Sstevel@tonic-gate */
5120Sstevel@tonic-gate searchChecks(dp, operation, matchRequired, found);
5130Sstevel@tonic-gate if (*found)
5140Sstevel@tonic-gate break;
5150Sstevel@tonic-gate /*
5160Sstevel@tonic-gate * Are we looking at part of a long file name entry?
5170Sstevel@tonic-gate * If so, we may need to note the start of the name.
5180Sstevel@tonic-gate * We don't do any further processing of long file
5190Sstevel@tonic-gate * name entries.
5200Sstevel@tonic-gate *
5210Sstevel@tonic-gate * We also skip deleted entries and the '.' and '..'
5220Sstevel@tonic-gate * entries.
5230Sstevel@tonic-gate */
5240Sstevel@tonic-gate if ((dp->pcd_attr & PCDL_LFN_BITS) == PCDL_LFN_BITS) {
5250Sstevel@tonic-gate if (!withinLongName) {
5260Sstevel@tonic-gate withinLongName++;
5270Sstevel@tonic-gate longStart = currentCluster;
5280Sstevel@tonic-gate longdp = dp;
5290Sstevel@tonic-gate }
5300Sstevel@tonic-gate dp++;
5310Sstevel@tonic-gate continue;
5320Sstevel@tonic-gate } else if ((dp->pcd_filename[0] == PCD_ERASED) ||
5330Sstevel@tonic-gate (dp->pcd_filename[0] == '.')) {
5340Sstevel@tonic-gate /*
5350Sstevel@tonic-gate * XXX - if we were within a long name, then
5360Sstevel@tonic-gate * its existence is bogus, because it is not
5370Sstevel@tonic-gate * attached to any real file.
5380Sstevel@tonic-gate */
5390Sstevel@tonic-gate withinLongName = 0;
5400Sstevel@tonic-gate dp++;
5410Sstevel@tonic-gate continue;
5420Sstevel@tonic-gate }
5430Sstevel@tonic-gate withinLongName = 0;
5440Sstevel@tonic-gate if (operation == PCFS_TRAVERSE_ALL)
5450Sstevel@tonic-gate catalogEntry(fd, dp, longdp, longStart, depth,
5460Sstevel@tonic-gate recordPath, pathLen);
5470Sstevel@tonic-gate longdp = NULL;
5480Sstevel@tonic-gate longStart = 0;
5490Sstevel@tonic-gate if (dp->pcd_attr & PCA_DIR && descend == PCFS_VISIT_SUBDIRS) {
5500Sstevel@tonic-gate traverseDir(fd, extractStartCluster(dp), depth + 1,
5510Sstevel@tonic-gate descend, operation, matchRequired, found,
5520Sstevel@tonic-gate lastDirCluster, dirEnd, recordPath, pathLen);
5530Sstevel@tonic-gate if (*found)
5540Sstevel@tonic-gate break;
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate dp++;
5570Sstevel@tonic-gate *pathLen = saveLen;
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate if (*found)
5600Sstevel@tonic-gate return;
5610Sstevel@tonic-gate if ((uchar_t *)dp < dirData->bytes + dirDataLen) {
5620Sstevel@tonic-gate /*
5630Sstevel@tonic-gate * We reached the end of directory before the end of
5640Sstevel@tonic-gate * our provided data (a cluster). That means this cluster
5650Sstevel@tonic-gate * is the last one in this directory's chain. It also
5660Sstevel@tonic-gate * means we've just looked at the last directory entry.
5670Sstevel@tonic-gate */
5680Sstevel@tonic-gate *lastDirCluster = currentCluster;
5690Sstevel@tonic-gate *dirEnd = dp;
5700Sstevel@tonic-gate return;
5710Sstevel@tonic-gate }
5720Sstevel@tonic-gate /*
5730Sstevel@tonic-gate * If there is more to the directory we'll go get it otherwise we
5740Sstevel@tonic-gate * are done traversing this directory.
5750Sstevel@tonic-gate */
5760Sstevel@tonic-gate if ((currentCluster == FAKE_ROOTDIR_CLUST) ||
5770Sstevel@tonic-gate (lastInFAT(currentCluster))) {
5780Sstevel@tonic-gate *lastDirCluster = currentCluster;
5790Sstevel@tonic-gate return;
5800Sstevel@tonic-gate } else {
5810Sstevel@tonic-gate traverseDir(fd, nextInChain(currentCluster),
5820Sstevel@tonic-gate depth, descend, operation, matchRequired,
5830Sstevel@tonic-gate found, lastDirCluster, dirEnd, recordPath, pathLen);
5840Sstevel@tonic-gate *pathLen = saveLen;
5850Sstevel@tonic-gate }
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate
5880Sstevel@tonic-gate /*
5890Sstevel@tonic-gate * traverseFromRoot()
5900Sstevel@tonic-gate * For use with 12 and 16 bit FATs that have a root directory outside
5910Sstevel@tonic-gate * of the file system. This is a general purpose routine that
5920Sstevel@tonic-gate * can be used simply to visit all of the nodes in the metadata or
5930Sstevel@tonic-gate * to find the first instance of something, e.g., the first directory
5940Sstevel@tonic-gate * entry where the file is marked deleted.
5950Sstevel@tonic-gate *
5960Sstevel@tonic-gate * Inputs are described in the commentary for visitNodes() above.
5970Sstevel@tonic-gate */
5980Sstevel@tonic-gate void
traverseFromRoot(int fd,int depth,int descend,int operation,char matchRequired,struct pcdir ** found,int32_t * lastDirCluster,struct pcdir ** dirEnd,char * recordPath,int * pathLen)5990Sstevel@tonic-gate traverseFromRoot(int fd, int depth, int descend, int operation,
6000Sstevel@tonic-gate char matchRequired, struct pcdir **found, int32_t *lastDirCluster,
6010Sstevel@tonic-gate struct pcdir **dirEnd, char *recordPath, int *pathLen)
6020Sstevel@tonic-gate {
6030Sstevel@tonic-gate visitNodes(fd, FAKE_ROOTDIR_CLUST, &TheRootDir, RootDirSize, depth,
6040Sstevel@tonic-gate descend, operation, matchRequired, found, lastDirCluster, dirEnd,
6050Sstevel@tonic-gate recordPath, pathLen);
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate
6080Sstevel@tonic-gate /*
6090Sstevel@tonic-gate * traverseDir()
6100Sstevel@tonic-gate * For use with all FATs outside of the initial root directory on
6110Sstevel@tonic-gate * 12 and 16 bit FAT file systems. This is a general purpose routine
6120Sstevel@tonic-gate * that can be used simply to visit all of the nodes in the metadata or
6130Sstevel@tonic-gate * to find the first instance of something, e.g., the first directory
6140Sstevel@tonic-gate * entry where the file is marked deleted.
6150Sstevel@tonic-gate *
6160Sstevel@tonic-gate * Unique Input is:
6170Sstevel@tonic-gate * startAt
6180Sstevel@tonic-gate * starting cluster of the directory
6190Sstevel@tonic-gate *
6200Sstevel@tonic-gate * This is the cluster that is the first one in this directory.
6210Sstevel@tonic-gate * We read it right away, so we can provide it as data to visitNodes().
6220Sstevel@tonic-gate * Note that we cache this cluster as we read it, because it is
6230Sstevel@tonic-gate * metadata and we cache all metadata. By doing so, we can
6240Sstevel@tonic-gate * keep pointers to directory entries for quickly moving around and
6250Sstevel@tonic-gate * fixing up any problems we find. Of course if we get a big
6260Sstevel@tonic-gate * filesystem with a huge amount of metadata we may be hosed, as
6270Sstevel@tonic-gate * we'll likely run out of memory.
6280Sstevel@tonic-gate *
6290Sstevel@tonic-gate * I believe in the future this will have to be addressed. It
6300Sstevel@tonic-gate * may be possible to do more of the processing of problems
6310Sstevel@tonic-gate * within directories as they are cached, so that when memory
6320Sstevel@tonic-gate * runs short we can free cached directories we are already
6330Sstevel@tonic-gate * finished visiting.
6340Sstevel@tonic-gate *
6350Sstevel@tonic-gate * The remainder of inputs are described in visitNodes() comments.
6360Sstevel@tonic-gate */
6370Sstevel@tonic-gate void
traverseDir(int fd,int32_t startAt,int depth,int descend,int operation,char matchRequired,struct pcdir ** found,int32_t * lastDirCluster,struct pcdir ** dirEnd,char * recordPath,int * pathLen)6380Sstevel@tonic-gate traverseDir(int fd, int32_t startAt, int depth, int descend, int operation,
6390Sstevel@tonic-gate char matchRequired, struct pcdir **found, int32_t *lastDirCluster,
6400Sstevel@tonic-gate struct pcdir **dirEnd, char *recordPath, int *pathLen)
6410Sstevel@tonic-gate {
6420Sstevel@tonic-gate ClusterContents dirdata;
6430Sstevel@tonic-gate int32_t dirdatasize = 0;
6440Sstevel@tonic-gate
6450Sstevel@tonic-gate if (startAt < FIRST_CLUSTER || startAt > LastCluster)
6460Sstevel@tonic-gate return;
6470Sstevel@tonic-gate
6480Sstevel@tonic-gate if (readCluster(fd, startAt, &(dirdata.bytes), &dirdatasize,
6490Sstevel@tonic-gate RDCLUST_DO_CACHE) != RDCLUST_GOOD) {
6500Sstevel@tonic-gate (void) fprintf(stderr,
6510Sstevel@tonic-gate gettext("Unable to get more directory entries!\n"));
6520Sstevel@tonic-gate return;
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate
6550Sstevel@tonic-gate if (operation == PCFS_TRAVERSE_ALL) {
6560Sstevel@tonic-gate if (Verbose)
6570Sstevel@tonic-gate (void) fprintf(stderr,
6580Sstevel@tonic-gate gettext("Directory traversal enters "
6590Sstevel@tonic-gate "allocation unit %d.\n"), startAt);
6600Sstevel@tonic-gate }
6610Sstevel@tonic-gate visitNodes(fd, startAt, &dirdata, dirdatasize, depth, descend,
6620Sstevel@tonic-gate operation, matchRequired, found, lastDirCluster, dirEnd,
6630Sstevel@tonic-gate recordPath, pathLen);
6640Sstevel@tonic-gate }
6650Sstevel@tonic-gate
6660Sstevel@tonic-gate void
createCHKNameList(int fd)6670Sstevel@tonic-gate createCHKNameList(int fd)
6680Sstevel@tonic-gate {
6690Sstevel@tonic-gate struct pcdir *ignorep1, *ignorep2;
6700Sstevel@tonic-gate int32_t ignore32;
6710Sstevel@tonic-gate char *ignorecp = NULL;
6720Sstevel@tonic-gate char ignore = '\0';
6730Sstevel@tonic-gate int ignoreint = 0;
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate ignorep1 = ignorep2 = NULL;
6760Sstevel@tonic-gate if (!OkayToRelink || CHKsList != NULL)
6770Sstevel@tonic-gate return;
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate /*
6800Sstevel@tonic-gate * Allocate an array to keep a bit map of the integer
6810Sstevel@tonic-gate * values used in CHK names.
6820Sstevel@tonic-gate */
6830Sstevel@tonic-gate if ((CHKsList =
6840Sstevel@tonic-gate (uchar_t *)calloc(1, idivceil(MAXCHKVAL, NBBY))) == NULL) {
6850Sstevel@tonic-gate OkayToRelink = 0;
6860Sstevel@tonic-gate return;
6870Sstevel@tonic-gate }
6880Sstevel@tonic-gate
6890Sstevel@tonic-gate /*
6900Sstevel@tonic-gate * Search the root directory for all the files with names of
6910Sstevel@tonic-gate * the form FILEXXXX.CHK. The root directory is an area
6920Sstevel@tonic-gate * outside of the file space on FAT12 and FAT16 file systems.
6930Sstevel@tonic-gate * On FAT32 file systems, the root directory is in a file
6940Sstevel@tonic-gate * area cluster just like any other directory.
6950Sstevel@tonic-gate */
6960Sstevel@tonic-gate if (!IsFAT32) {
6970Sstevel@tonic-gate traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS,
6980Sstevel@tonic-gate ignore, &ignorep1, &ignore32, &ignorep2, ignorecp,
6990Sstevel@tonic-gate &ignoreint);
7000Sstevel@tonic-gate } else {
7010Sstevel@tonic-gate DirCount++;
7020Sstevel@tonic-gate traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7030Sstevel@tonic-gate 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS, ignore,
7040Sstevel@tonic-gate &ignorep1, &ignore32, &ignorep2, ignorecp, &ignoreint);
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate
7080Sstevel@tonic-gate
7090Sstevel@tonic-gate char *
nextAvailableCHKName(int * chosen)7100Sstevel@tonic-gate nextAvailableCHKName(int *chosen)
7110Sstevel@tonic-gate {
7120Sstevel@tonic-gate static char nameBuf[PCFNAMESIZE];
7130Sstevel@tonic-gate int i;
7140Sstevel@tonic-gate
7150Sstevel@tonic-gate if (!OkayToRelink)
7160Sstevel@tonic-gate return (NULL);
7170Sstevel@tonic-gate
7180Sstevel@tonic-gate nameBuf[CHKNAME_F] = 'F';
7190Sstevel@tonic-gate nameBuf[CHKNAME_I] = 'I';
7200Sstevel@tonic-gate nameBuf[CHKNAME_L] = 'L';
7210Sstevel@tonic-gate nameBuf[CHKNAME_E] = 'E';
7220Sstevel@tonic-gate
7230Sstevel@tonic-gate for (i = 1; i <= MAXCHKVAL; i++) {
7240Sstevel@tonic-gate if (!inUseCHKName(i))
7250Sstevel@tonic-gate break;
7260Sstevel@tonic-gate }
7270Sstevel@tonic-gate if (i <= MAXCHKVAL) {
7280Sstevel@tonic-gate nameBuf[CHKNAME_THOUSANDS] = '0' + (i / 1000);
7290Sstevel@tonic-gate nameBuf[CHKNAME_HUNDREDS] = '0' + ((i % 1000) / 100);
7300Sstevel@tonic-gate nameBuf[CHKNAME_TENS] = '0' + ((i % 100) / 10);
7310Sstevel@tonic-gate nameBuf[CHKNAME_ONES] = '0' + (i % 10);
7320Sstevel@tonic-gate *chosen = i;
7330Sstevel@tonic-gate return (nameBuf);
7340Sstevel@tonic-gate } else {
7350Sstevel@tonic-gate (void) fprintf(stderr,
7360Sstevel@tonic-gate gettext("Sorry, no names available for "
7370Sstevel@tonic-gate "relinking orphan chains!\n"));
7380Sstevel@tonic-gate OkayToRelink = 0;
7390Sstevel@tonic-gate return (NULL);
7400Sstevel@tonic-gate }
7410Sstevel@tonic-gate }
7420Sstevel@tonic-gate
7430Sstevel@tonic-gate uint32_t
extractSize(struct pcdir * dp)7440Sstevel@tonic-gate extractSize(struct pcdir *dp)
7450Sstevel@tonic-gate {
7460Sstevel@tonic-gate uint32_t returnMe;
7470Sstevel@tonic-gate
7480Sstevel@tonic-gate read_32_bits((uchar_t *)&(dp->pcd_size), &returnMe);
7490Sstevel@tonic-gate return (returnMe);
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate int32_t
extractStartCluster(struct pcdir * dp)7530Sstevel@tonic-gate extractStartCluster(struct pcdir *dp)
7540Sstevel@tonic-gate {
7550Sstevel@tonic-gate uint32_t lo, hi;
7560Sstevel@tonic-gate
7570Sstevel@tonic-gate if (IsFAT32) {
7580Sstevel@tonic-gate read_16_bits((uchar_t *)&(dp->un.pcd_scluster_hi), &hi);
7590Sstevel@tonic-gate read_16_bits((uchar_t *)&(dp->pcd_scluster_lo), &lo);
7600Sstevel@tonic-gate return ((int32_t)((hi << 16) | lo));
7610Sstevel@tonic-gate } else {
7620Sstevel@tonic-gate read_16_bits((uchar_t *)&(dp->pcd_scluster_lo), &lo);
7630Sstevel@tonic-gate return ((int32_t)lo);
7640Sstevel@tonic-gate }
7650Sstevel@tonic-gate }
7660Sstevel@tonic-gate
7670Sstevel@tonic-gate static struct pcdir *
findAvailableRootDirEntSlot(int fd,int32_t * clusterWithSlot)7680Sstevel@tonic-gate findAvailableRootDirEntSlot(int fd, int32_t *clusterWithSlot)
7690Sstevel@tonic-gate {
7700Sstevel@tonic-gate struct pcdir *deletedEntry = NULL;
7710Sstevel@tonic-gate struct pcdir *appendPoint = NULL;
7720Sstevel@tonic-gate char *ignorecp = NULL;
7730Sstevel@tonic-gate int ignore = 0;
7740Sstevel@tonic-gate
7750Sstevel@tonic-gate *clusterWithSlot = 0;
7760Sstevel@tonic-gate
7770Sstevel@tonic-gate /*
7780Sstevel@tonic-gate * First off, try to find an erased entry in the root
7790Sstevel@tonic-gate * directory. The root directory is an area outside of the
7800Sstevel@tonic-gate * file space on FAT12 and FAT16 file systems. On FAT32 file
7810Sstevel@tonic-gate * systems, the root directory is in a file area cluster just
7820Sstevel@tonic-gate * like any other directory.
7830Sstevel@tonic-gate */
7840Sstevel@tonic-gate if (!IsFAT32) {
7850Sstevel@tonic-gate traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS,
7860Sstevel@tonic-gate PCD_ERASED, &deletedEntry, clusterWithSlot,
7870Sstevel@tonic-gate &appendPoint, ignorecp, &ignore);
7880Sstevel@tonic-gate } else {
7890Sstevel@tonic-gate DirCount++;
7900Sstevel@tonic-gate traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7910Sstevel@tonic-gate 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS, PCD_ERASED,
7920Sstevel@tonic-gate &deletedEntry, clusterWithSlot, &appendPoint, ignorecp,
7930Sstevel@tonic-gate &ignore);
7940Sstevel@tonic-gate }
7950Sstevel@tonic-gate /*
7960Sstevel@tonic-gate * If we found a deleted file in the directory we'll overwrite
7970Sstevel@tonic-gate * that entry.
7980Sstevel@tonic-gate */
7990Sstevel@tonic-gate if (deletedEntry)
8000Sstevel@tonic-gate return (deletedEntry);
8010Sstevel@tonic-gate /*
8020Sstevel@tonic-gate * If there is room at the end of the existing directory, we
8030Sstevel@tonic-gate * should place the new entry there.
8040Sstevel@tonic-gate */
8050Sstevel@tonic-gate if (appendPoint)
8060Sstevel@tonic-gate return (appendPoint);
8070Sstevel@tonic-gate /*
8080Sstevel@tonic-gate * XXX need to grow the directory
8090Sstevel@tonic-gate */
8100Sstevel@tonic-gate return (NULL);
8110Sstevel@tonic-gate }
8120Sstevel@tonic-gate
8130Sstevel@tonic-gate static void
insertDirEnt(struct pcdir * slot,struct pcdir * entry,int32_t clusterWithSlot)8140Sstevel@tonic-gate insertDirEnt(struct pcdir *slot, struct pcdir *entry, int32_t clusterWithSlot)
8150Sstevel@tonic-gate {
8160Sstevel@tonic-gate (void) memcpy(slot, entry, sizeof (struct pcdir));
8170Sstevel@tonic-gate markClusterModified(clusterWithSlot);
8180Sstevel@tonic-gate }
8190Sstevel@tonic-gate
820*2720Sfrankho /*
821*2720Sfrankho * Convert current UNIX time into a PCFS timestamp (which is in local time).
822*2720Sfrankho *
823*2720Sfrankho * Since the "seconds" field of that is only accurate to 2sec precision,
824*2720Sfrankho * we allow for the optional (used only for creation times on FAT) "msec"
825*2720Sfrankho * parameter that takes the fractional part.
826*2720Sfrankho */
8270Sstevel@tonic-gate static void
getNow(struct pctime * pctp,uchar_t * msec)828*2720Sfrankho getNow(struct pctime *pctp, uchar_t *msec)
8290Sstevel@tonic-gate {
830*2720Sfrankho time_t now;
831*2720Sfrankho struct tm tm;
832*2720Sfrankho ushort_t tim, dat;
833*2720Sfrankho
834*2720Sfrankho /*
835*2720Sfrankho * Disable daylight savings corrections - Solaris PCFS doesn't
836*2720Sfrankho * support such conversions yet. Save timestamps in local time.
837*2720Sfrankho */
838*2720Sfrankho daylight = 0;
8390Sstevel@tonic-gate
840*2720Sfrankho (void) time(&now);
841*2720Sfrankho (void) localtime_r(&now, &tm);
842*2720Sfrankho
843*2720Sfrankho dat = (tm.tm_year - 80) << YEARSHIFT;
844*2720Sfrankho dat |= tm.tm_mon << MONSHIFT;
845*2720Sfrankho dat |= tm.tm_mday << DAYSHIFT;
846*2720Sfrankho tim = tm.tm_hour << HOURSHIFT;
847*2720Sfrankho tim |= tm.tm_min << MINSHIFT;
848*2720Sfrankho tim |= (tm.tm_sec / 2) << SECSHIFT;
849*2720Sfrankho
850*2720Sfrankho /*
851*2720Sfrankho * Sanity check. If we overflow the PCFS timestamp range
852*2720Sfrankho * we set the time to 01/01/1980, 00:00:00
853*2720Sfrankho */
854*2720Sfrankho if (dat < 80 || dat > 227)
855*2720Sfrankho dat = tim = 0;
856*2720Sfrankho
857*2720Sfrankho pctp->pct_date = LE_16(dat);
858*2720Sfrankho pctp->pct_time = LE_16(tim);
859*2720Sfrankho if (msec)
860*2720Sfrankho *msec = (tm.tm_sec & 1) ? 100 : 0;
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate
8630Sstevel@tonic-gate /*
8640Sstevel@tonic-gate * FAT file systems store the following time information in a directory
8650Sstevel@tonic-gate * entry:
866*2720Sfrankho * timestamp member of "struct pcdir"
867*2720Sfrankho * ======================================================================
868*2720Sfrankho * creation time pcd_crtime.pct_time
869*2720Sfrankho * creation date pcd_crtime.pct_date
870*2720Sfrankho * last access date pcd_ladate
871*2720Sfrankho * last modify time pcd_mtime.pct_time
872*2720Sfrankho * last modify date pcd_mtime.pct_date
8730Sstevel@tonic-gate *
8740Sstevel@tonic-gate * No access time is kept.
8750Sstevel@tonic-gate */
8760Sstevel@tonic-gate static void
updateDirEnt_CreatTime(struct pcdir * dp)8770Sstevel@tonic-gate updateDirEnt_CreatTime(struct pcdir *dp)
8780Sstevel@tonic-gate {
879*2720Sfrankho getNow(&dp->pcd_crtime, &dp->pcd_crtime_msec);
8800Sstevel@tonic-gate markClusterModified(findImpactedCluster(dp));
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate
8830Sstevel@tonic-gate static void
updateDirEnt_ModTimes(struct pcdir * dp)8840Sstevel@tonic-gate updateDirEnt_ModTimes(struct pcdir *dp)
8850Sstevel@tonic-gate {
8860Sstevel@tonic-gate timestruc_t ts;
8870Sstevel@tonic-gate
888*2720Sfrankho getNow(&dp->pcd_mtime, NULL);
889*2720Sfrankho dp->pcd_ladate = dp->pcd_mtime.pct_date;
8900Sstevel@tonic-gate dp->pcd_attr |= PCA_ARCH;
8910Sstevel@tonic-gate markClusterModified(findImpactedCluster(dp));
8920Sstevel@tonic-gate }
8930Sstevel@tonic-gate
8940Sstevel@tonic-gate struct pcdir *
addRootDirEnt(int fd,struct pcdir * new)8950Sstevel@tonic-gate addRootDirEnt(int fd, struct pcdir *new)
8960Sstevel@tonic-gate {
8970Sstevel@tonic-gate struct pcdir *added;
8980Sstevel@tonic-gate int32_t inCluster;
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate if ((added = findAvailableRootDirEntSlot(fd, &inCluster)) != NULL) {
9010Sstevel@tonic-gate insertDirEnt(added, new, inCluster);
9020Sstevel@tonic-gate return (added);
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate return (NULL);
9050Sstevel@tonic-gate }
9060Sstevel@tonic-gate
9070Sstevel@tonic-gate /*
9080Sstevel@tonic-gate * FAT12 and FAT16 have a root directory outside the normal file space,
9090Sstevel@tonic-gate * so we have separate routines for finding and reading the root directory.
9100Sstevel@tonic-gate */
9110Sstevel@tonic-gate static off64_t
seekRootDirectory(int fd)9120Sstevel@tonic-gate seekRootDirectory(int fd)
9130Sstevel@tonic-gate {
9140Sstevel@tonic-gate off64_t seekto;
9150Sstevel@tonic-gate
9160Sstevel@tonic-gate /*
9170Sstevel@tonic-gate * The RootDir immediately follows the FATs, which in
9180Sstevel@tonic-gate * turn immediately follow the reserved sectors.
9190Sstevel@tonic-gate */
9200Sstevel@tonic-gate seekto = (off64_t)TheBIOSParameterBlock.bpb.resv_sectors *
9210Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector +
9220Sstevel@tonic-gate (off64_t)FATSize * TheBIOSParameterBlock.bpb.num_fats +
9230Sstevel@tonic-gate (off64_t)PartitionOffset;
9240Sstevel@tonic-gate if (Verbose)
9250Sstevel@tonic-gate (void) fprintf(stderr,
9260Sstevel@tonic-gate gettext("Seeking root directory @%lld.\n"), seekto);
9270Sstevel@tonic-gate return (lseek64(fd, seekto, SEEK_SET));
9280Sstevel@tonic-gate }
9290Sstevel@tonic-gate
9300Sstevel@tonic-gate void
getRootDirectory(int fd)9310Sstevel@tonic-gate getRootDirectory(int fd)
9320Sstevel@tonic-gate {
9330Sstevel@tonic-gate ssize_t bytesRead;
9340Sstevel@tonic-gate
9350Sstevel@tonic-gate if (TheRootDir.bytes != NULL)
9360Sstevel@tonic-gate return;
9370Sstevel@tonic-gate else if ((TheRootDir.bytes = (uchar_t *)malloc(RootDirSize)) == NULL) {
9380Sstevel@tonic-gate mountSanityCheckFails();
9390Sstevel@tonic-gate perror(gettext("No memory for a copy of the root directory"));
9400Sstevel@tonic-gate (void) close(fd);
9410Sstevel@tonic-gate exit(8);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate
9440Sstevel@tonic-gate if (seekRootDirectory(fd) < 0) {
9450Sstevel@tonic-gate mountSanityCheckFails();
9460Sstevel@tonic-gate perror(gettext("Cannot seek to RootDir"));
9470Sstevel@tonic-gate (void) close(fd);
9480Sstevel@tonic-gate exit(8);
9490Sstevel@tonic-gate }
9500Sstevel@tonic-gate
9510Sstevel@tonic-gate if (Verbose)
9520Sstevel@tonic-gate (void) fprintf(stderr,
9530Sstevel@tonic-gate gettext("Reading root directory.\n"));
9540Sstevel@tonic-gate if ((bytesRead = read(fd, TheRootDir.bytes, RootDirSize)) !=
9550Sstevel@tonic-gate RootDirSize) {
9560Sstevel@tonic-gate mountSanityCheckFails();
9570Sstevel@tonic-gate if (bytesRead < 0) {
9580Sstevel@tonic-gate perror(gettext("Cannot read a RootDir"));
9590Sstevel@tonic-gate } else {
9600Sstevel@tonic-gate (void) fprintf(stderr,
9610Sstevel@tonic-gate gettext("Short read of RootDir\n"));
9620Sstevel@tonic-gate }
9630Sstevel@tonic-gate (void) close(fd);
9640Sstevel@tonic-gate exit(8);
9650Sstevel@tonic-gate }
9660Sstevel@tonic-gate if (Verbose) {
9670Sstevel@tonic-gate (void) fprintf(stderr,
9680Sstevel@tonic-gate gettext("Dump of root dir's first 256 bytes.\n"));
9690Sstevel@tonic-gate header_for_dump();
9700Sstevel@tonic-gate dump_bytes(TheRootDir.bytes, 256);
9710Sstevel@tonic-gate }
9720Sstevel@tonic-gate }
9730Sstevel@tonic-gate
9740Sstevel@tonic-gate void
writeRootDirMods(int fd)9750Sstevel@tonic-gate writeRootDirMods(int fd)
9760Sstevel@tonic-gate {
9770Sstevel@tonic-gate ssize_t bytesWritten;
9780Sstevel@tonic-gate
9790Sstevel@tonic-gate if (!TheRootDir.bytes) {
9800Sstevel@tonic-gate (void) fprintf(stderr,
9810Sstevel@tonic-gate gettext("Internal error: No Root directory to write\n"));
9820Sstevel@tonic-gate (void) close(fd);
9830Sstevel@tonic-gate exit(12);
9840Sstevel@tonic-gate }
9850Sstevel@tonic-gate if (!RootDirModified) {
9860Sstevel@tonic-gate if (Verbose) {
9870Sstevel@tonic-gate (void) fprintf(stderr,
9880Sstevel@tonic-gate gettext("No root directory changes need to "
9890Sstevel@tonic-gate "be written.\n"));
9900Sstevel@tonic-gate }
9910Sstevel@tonic-gate return;
9920Sstevel@tonic-gate }
9930Sstevel@tonic-gate if (ReadOnly)
9940Sstevel@tonic-gate return;
9950Sstevel@tonic-gate if (Verbose)
9960Sstevel@tonic-gate (void) fprintf(stderr,
9970Sstevel@tonic-gate gettext("Writing root directory.\n"));
9980Sstevel@tonic-gate if (seekRootDirectory(fd) < 0) {
9990Sstevel@tonic-gate perror(gettext("Cannot write the RootDir (seek failed)"));
10000Sstevel@tonic-gate (void) close(fd);
10010Sstevel@tonic-gate exit(12);
10020Sstevel@tonic-gate }
10030Sstevel@tonic-gate if ((bytesWritten = write(fd, TheRootDir.bytes, RootDirSize)) !=
10040Sstevel@tonic-gate RootDirSize) {
10050Sstevel@tonic-gate if (bytesWritten < 0) {
10060Sstevel@tonic-gate perror(gettext("Cannot write the RootDir"));
10070Sstevel@tonic-gate } else {
10080Sstevel@tonic-gate (void) fprintf(stderr,
10090Sstevel@tonic-gate gettext("Short write of root directory\n"));
10100Sstevel@tonic-gate }
10110Sstevel@tonic-gate (void) close(fd);
10120Sstevel@tonic-gate exit(12);
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate RootDirModified = 0;
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate
10170Sstevel@tonic-gate struct pcdir *
newDirEnt(struct pcdir * copyme)10180Sstevel@tonic-gate newDirEnt(struct pcdir *copyme)
10190Sstevel@tonic-gate {
10200Sstevel@tonic-gate struct pcdir *ndp;
10210Sstevel@tonic-gate
10220Sstevel@tonic-gate if ((ndp = (struct pcdir *)calloc(1, sizeof (struct pcdir))) == NULL) {
10230Sstevel@tonic-gate (void) fprintf(stderr, gettext("Out of memory to create a "
10240Sstevel@tonic-gate "new directory entry!\n"));
10250Sstevel@tonic-gate return (ndp);
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate if (copyme)
10280Sstevel@tonic-gate (void) memcpy(ndp, copyme, sizeof (struct pcdir));
10290Sstevel@tonic-gate ndp->pcd_ext[CHKNAME_C] = 'C';
10300Sstevel@tonic-gate ndp->pcd_ext[CHKNAME_H] = 'H';
10310Sstevel@tonic-gate ndp->pcd_ext[CHKNAME_K] = 'K';
10320Sstevel@tonic-gate updateDirEnt_CreatTime(ndp);
10330Sstevel@tonic-gate updateDirEnt_ModTimes(ndp);
10340Sstevel@tonic-gate return (ndp);
10350Sstevel@tonic-gate }
10360Sstevel@tonic-gate
10370Sstevel@tonic-gate void
updateDirEnt_Size(struct pcdir * dp,uint32_t newSize)10380Sstevel@tonic-gate updateDirEnt_Size(struct pcdir *dp, uint32_t newSize)
10390Sstevel@tonic-gate {
10400Sstevel@tonic-gate uchar_t *p = (uchar_t *)&(dp->pcd_size);
10410Sstevel@tonic-gate store_32_bits(&p, newSize);
10420Sstevel@tonic-gate markClusterModified(findImpactedCluster(dp));
10430Sstevel@tonic-gate }
10440Sstevel@tonic-gate
10450Sstevel@tonic-gate void
updateDirEnt_Start(struct pcdir * dp,int32_t newStart)10460Sstevel@tonic-gate updateDirEnt_Start(struct pcdir *dp, int32_t newStart)
10470Sstevel@tonic-gate {
10480Sstevel@tonic-gate uchar_t *p = (uchar_t *)&(dp->pcd_scluster_lo);
10490Sstevel@tonic-gate store_16_bits(&p, newStart & 0xffff);
10500Sstevel@tonic-gate if (IsFAT32) {
10510Sstevel@tonic-gate p = (uchar_t *)&(dp->un.pcd_scluster_hi);
10520Sstevel@tonic-gate store_16_bits(&p, newStart >> 16);
10530Sstevel@tonic-gate }
10540Sstevel@tonic-gate markClusterModified(findImpactedCluster(dp));
10550Sstevel@tonic-gate }
10560Sstevel@tonic-gate
10570Sstevel@tonic-gate void
updateDirEnt_Name(struct pcdir * dp,char * newName)10580Sstevel@tonic-gate updateDirEnt_Name(struct pcdir *dp, char *newName)
10590Sstevel@tonic-gate {
10600Sstevel@tonic-gate int i;
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate for (i = 0; i < PCFNAMESIZE; i++) {
10630Sstevel@tonic-gate if (*newName)
10640Sstevel@tonic-gate dp->pcd_filename[i] = *newName++;
10650Sstevel@tonic-gate else
10660Sstevel@tonic-gate dp->pcd_filename[i] = ' ';
10670Sstevel@tonic-gate }
10680Sstevel@tonic-gate markClusterModified(findImpactedCluster(dp));
10690Sstevel@tonic-gate }
1070