xref: /titanic_41/usr/src/cmd/fs.d/pcfs/fsck/dir.c (revision 264a6e7478846334593be7663fb6b1a8f37784a0)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*264a6e74Sfrankho  * Common Development and Distribution License (the "License").
6*264a6e74Sfrankho  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*264a6e74Sfrankho  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*264a6e74Sfrankho  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * fsck_pcfs -- routines for manipulating directories.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate #include <stdio.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <unistd.h>
347c478bd9Sstevel@tonic-gate #include <stdlib.h>
357c478bd9Sstevel@tonic-gate #include <libintl.h>
367c478bd9Sstevel@tonic-gate #include <ctype.h>
37*264a6e74Sfrankho #include <time.h>
387c478bd9Sstevel@tonic-gate #include <sys/param.h>
397c478bd9Sstevel@tonic-gate #include <sys/time.h>
40*264a6e74Sfrankho #include <sys/byteorder.h>
417c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
427c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
437c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
447c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
457c478bd9Sstevel@tonic-gate #include "pcfs_common.h"
467c478bd9Sstevel@tonic-gate #include "fsck_pcfs.h"
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate extern	int32_t	HiddenClusterCount;
497c478bd9Sstevel@tonic-gate extern	int32_t	FileClusterCount;
507c478bd9Sstevel@tonic-gate extern	int32_t	DirClusterCount;
517c478bd9Sstevel@tonic-gate extern	int32_t	HiddenFileCount;
527c478bd9Sstevel@tonic-gate extern	int32_t	LastCluster;
537c478bd9Sstevel@tonic-gate extern	int32_t	FileCount;
547c478bd9Sstevel@tonic-gate extern	int32_t	BadCount;
557c478bd9Sstevel@tonic-gate extern	int32_t	DirCount;
567c478bd9Sstevel@tonic-gate extern	int32_t	FATSize;
577c478bd9Sstevel@tonic-gate extern	off64_t	PartitionOffset;
587c478bd9Sstevel@tonic-gate extern	bpb_t	TheBIOSParameterBlock;
597c478bd9Sstevel@tonic-gate extern	int	ReadOnly;
607c478bd9Sstevel@tonic-gate extern	int	IsFAT32;
617c478bd9Sstevel@tonic-gate extern	int	Verbose;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate static uchar_t *CHKsList = NULL;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate ClusterContents	TheRootDir;
667c478bd9Sstevel@tonic-gate int32_t	RootDirSize;
677c478bd9Sstevel@tonic-gate int	RootDirModified;
687c478bd9Sstevel@tonic-gate int	OkayToRelink = 1;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * We have a bunch of routines for handling CHK names.  A CHK name is
727c478bd9Sstevel@tonic-gate  * simply a file name of the form "FILEnnnn.CHK", where the n's are the
737c478bd9Sstevel@tonic-gate  * digits in the numbers from 1 to 9999.  There are always four digits
747c478bd9Sstevel@tonic-gate  * used, leading zeros are added as necessary.
757c478bd9Sstevel@tonic-gate  *
767c478bd9Sstevel@tonic-gate  * We use CHK names to link orphaned cluster chains back into the file
777c478bd9Sstevel@tonic-gate  * system's root directory under an auspicious name so that the user
787c478bd9Sstevel@tonic-gate  * may be able to recover some of their data.
797c478bd9Sstevel@tonic-gate  *
807c478bd9Sstevel@tonic-gate  * We use these routines to ensure CHK names we use don't conflict
817c478bd9Sstevel@tonic-gate  * with any already present in the file system.
827c478bd9Sstevel@tonic-gate  */
837c478bd9Sstevel@tonic-gate static int
hasCHKName(struct pcdir * dp)847c478bd9Sstevel@tonic-gate hasCHKName(struct pcdir *dp)
857c478bd9Sstevel@tonic-gate {
867c478bd9Sstevel@tonic-gate 	return (dp->pcd_filename[CHKNAME_F] == 'F' &&
877c478bd9Sstevel@tonic-gate 	    dp->pcd_filename[CHKNAME_I] == 'I' &&
887c478bd9Sstevel@tonic-gate 	    dp->pcd_filename[CHKNAME_L] == 'L' &&
897c478bd9Sstevel@tonic-gate 	    dp->pcd_filename[CHKNAME_E] == 'E' &&
907c478bd9Sstevel@tonic-gate 	    isdigit(dp->pcd_filename[CHKNAME_THOUSANDS]) &&
917c478bd9Sstevel@tonic-gate 	    isdigit(dp->pcd_filename[CHKNAME_HUNDREDS]) &&
927c478bd9Sstevel@tonic-gate 	    isdigit(dp->pcd_filename[CHKNAME_TENS]) &&
937c478bd9Sstevel@tonic-gate 	    isdigit(dp->pcd_filename[CHKNAME_ONES]) &&
947c478bd9Sstevel@tonic-gate 	    dp->pcd_ext[CHKNAME_C] == 'C' &&
957c478bd9Sstevel@tonic-gate 	    dp->pcd_ext[CHKNAME_H] == 'H' &&
967c478bd9Sstevel@tonic-gate 	    dp->pcd_ext[CHKNAME_K] == 'K');
977c478bd9Sstevel@tonic-gate }
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate void
addEntryToCHKList(int chkNumber)1007c478bd9Sstevel@tonic-gate addEntryToCHKList(int chkNumber)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	/* silent failure on bogus value */
1037c478bd9Sstevel@tonic-gate 	if (chkNumber < 0 || chkNumber > MAXCHKVAL)
1047c478bd9Sstevel@tonic-gate 		return;
1057c478bd9Sstevel@tonic-gate 	CHKsList[chkNumber / NBBY] |= (1 << (chkNumber % NBBY));
1067c478bd9Sstevel@tonic-gate }
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate static void
addToCHKList(struct pcdir * dp)1097c478bd9Sstevel@tonic-gate addToCHKList(struct pcdir *dp)
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate 	int chknum;
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	chknum = 1000 * (dp->pcd_filename[CHKNAME_THOUSANDS] - '0');
1147c478bd9Sstevel@tonic-gate 	chknum += 100 * (dp->pcd_filename[CHKNAME_HUNDREDS] - '0');
1157c478bd9Sstevel@tonic-gate 	chknum += 10 * (dp->pcd_filename[CHKNAME_TENS] - '0');
1167c478bd9Sstevel@tonic-gate 	chknum += (dp->pcd_filename[CHKNAME_ONES] - '0');
1177c478bd9Sstevel@tonic-gate 	addEntryToCHKList(chknum);
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate static int
inUseCHKName(int chkNumber)1217c478bd9Sstevel@tonic-gate inUseCHKName(int chkNumber)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	return (CHKsList[chkNumber / NBBY] & (1 << (chkNumber % NBBY)));
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate static void
appendToPath(struct pcdir * dp,char * thePath,int * theLen)1277c478bd9Sstevel@tonic-gate appendToPath(struct pcdir *dp, char *thePath, int *theLen)
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate 	int i = 0;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	/*
1327c478bd9Sstevel@tonic-gate 	 * Sometimes caller doesn't care about keeping track of the path
1337c478bd9Sstevel@tonic-gate 	 */
1347c478bd9Sstevel@tonic-gate 	if (thePath == NULL)
1357c478bd9Sstevel@tonic-gate 		return;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	/*
1387c478bd9Sstevel@tonic-gate 	 *  Prepend /
1397c478bd9Sstevel@tonic-gate 	 */
1407c478bd9Sstevel@tonic-gate 	if (*theLen < MAXPATHLEN)
1417c478bd9Sstevel@tonic-gate 		*(thePath + (*theLen)++) = '/';
1427c478bd9Sstevel@tonic-gate 	/*
1437c478bd9Sstevel@tonic-gate 	 *  Print out the file name part, but only up to the first
1447c478bd9Sstevel@tonic-gate 	 *  space.
1457c478bd9Sstevel@tonic-gate 	 */
1467c478bd9Sstevel@tonic-gate 	while (*theLen < MAXPATHLEN && i < PCFNAMESIZE) {
1477c478bd9Sstevel@tonic-gate 		/*
1487c478bd9Sstevel@tonic-gate 		 *  When we start seeing spaces we assume that's the
1497c478bd9Sstevel@tonic-gate 		 *  end of the interesting characters in the name.
1507c478bd9Sstevel@tonic-gate 		 */
1517c478bd9Sstevel@tonic-gate 		if ((dp->pcd_filename[i] == ' ') ||
1527c478bd9Sstevel@tonic-gate 		    !(pc_validchar(dp->pcd_filename[i])))
1537c478bd9Sstevel@tonic-gate 			break;
1547c478bd9Sstevel@tonic-gate 		*(thePath + (*theLen)++) = dp->pcd_filename[i++];
1557c478bd9Sstevel@tonic-gate 	}
1567c478bd9Sstevel@tonic-gate 	/*
1577c478bd9Sstevel@tonic-gate 	 *  Leave now, if we don't have an extension (or room for one)
1587c478bd9Sstevel@tonic-gate 	 */
1597c478bd9Sstevel@tonic-gate 	if ((dp->pcd_ext[i] == ' ') || ((*theLen) >= MAXPATHLEN) ||
1607c478bd9Sstevel@tonic-gate 	    (!(pc_validchar(dp->pcd_ext[i]))))
1617c478bd9Sstevel@tonic-gate 		return;
1627c478bd9Sstevel@tonic-gate 	/*
1637c478bd9Sstevel@tonic-gate 	 *  Tack on the extension
1647c478bd9Sstevel@tonic-gate 	 */
1657c478bd9Sstevel@tonic-gate 	*(thePath + (*theLen)++) = '.';
1667c478bd9Sstevel@tonic-gate 	i = 0;
1677c478bd9Sstevel@tonic-gate 	while ((*theLen < MAXPATHLEN) && (i < PCFEXTSIZE)) {
1687c478bd9Sstevel@tonic-gate 		if ((dp->pcd_ext[i] == ' ') || !(pc_validchar(dp->pcd_ext[i])))
1697c478bd9Sstevel@tonic-gate 			break;
1707c478bd9Sstevel@tonic-gate 		*(thePath + (*theLen)++) = dp->pcd_ext[i++];
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate static void
printName(FILE * outDest,struct pcdir * dp)1757c478bd9Sstevel@tonic-gate printName(FILE *outDest, struct pcdir *dp)
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate 	int i;
1787c478bd9Sstevel@tonic-gate 	for (i = 0; i < PCFNAMESIZE; i++) {
1797c478bd9Sstevel@tonic-gate 		if ((dp->pcd_filename[i] == ' ') ||
1807c478bd9Sstevel@tonic-gate 		    !(pc_validchar(dp->pcd_filename[i])))
1817c478bd9Sstevel@tonic-gate 			break;
1827c478bd9Sstevel@tonic-gate 		(void) fprintf(outDest, "%c", dp->pcd_filename[i]);
1837c478bd9Sstevel@tonic-gate 	}
1847c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest, ".");
1857c478bd9Sstevel@tonic-gate 	for (i = 0; i < PCFEXTSIZE; i++) {
1867c478bd9Sstevel@tonic-gate 		if (!(pc_validchar(dp->pcd_ext[i])))
1877c478bd9Sstevel@tonic-gate 			break;
1887c478bd9Sstevel@tonic-gate 		(void) fprintf(outDest, "%c", dp->pcd_ext[i]);
1897c478bd9Sstevel@tonic-gate 	}
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate  *  sanityCheckSize
1947c478bd9Sstevel@tonic-gate  *	Make sure the size in the directory entry matches what is
1957c478bd9Sstevel@tonic-gate  *	actually allocated.  If there is a mismatch, orphan all
1967c478bd9Sstevel@tonic-gate  *	the allocated clusters.  Returns SIZE_MATCHED if everything matches
1977c478bd9Sstevel@tonic-gate  *	up, TRUNCATED to indicate truncation was necessary.
1987c478bd9Sstevel@tonic-gate  */
1997c478bd9Sstevel@tonic-gate static int
sanityCheckSize(int fd,struct pcdir * dp,int32_t actualClusterCount,int isDir,int32_t startCluster,struct nameinfo * fullPathName,struct pcdir ** orphanEntry)2007c478bd9Sstevel@tonic-gate sanityCheckSize(int fd, struct pcdir *dp, int32_t actualClusterCount,
2017c478bd9Sstevel@tonic-gate     int isDir, int32_t startCluster, struct nameinfo *fullPathName,
2027c478bd9Sstevel@tonic-gate     struct pcdir **orphanEntry)
2037c478bd9Sstevel@tonic-gate {
2047c478bd9Sstevel@tonic-gate 	uint32_t sizeFromDir;
2057c478bd9Sstevel@tonic-gate 	int32_t ignorei = 0;
2067c478bd9Sstevel@tonic-gate 	int64_t bpc;
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster *
2097c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector;
2107c478bd9Sstevel@tonic-gate 	sizeFromDir = extractSize(dp);
2117c478bd9Sstevel@tonic-gate 	if (isDir) {
2127c478bd9Sstevel@tonic-gate 		if (sizeFromDir == 0)
2137c478bd9Sstevel@tonic-gate 			return (SIZE_MATCHED);
2147c478bd9Sstevel@tonic-gate 	} else {
2157c478bd9Sstevel@tonic-gate 		if ((sizeFromDir > ((actualClusterCount - 1) * bpc)) &&
2167c478bd9Sstevel@tonic-gate 		    (sizeFromDir <= (actualClusterCount * bpc)))
2177c478bd9Sstevel@tonic-gate 			return (SIZE_MATCHED);
2187c478bd9Sstevel@tonic-gate 	}
2197c478bd9Sstevel@tonic-gate 	if (fullPathName != NULL) {
2207c478bd9Sstevel@tonic-gate 		fullPathName->references++;
2217c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s\n", fullPathName->fullName);
2227c478bd9Sstevel@tonic-gate 	}
2237c478bd9Sstevel@tonic-gate 	squirrelPath(fullPathName, startCluster);
2247c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
2257c478bd9Sstevel@tonic-gate 	    gettext("Truncating chain due to incorrect size "
2267c478bd9Sstevel@tonic-gate 	    "in directory.  Size from directory = %u bytes,\n"), sizeFromDir);
2277c478bd9Sstevel@tonic-gate 	if (actualClusterCount == 0) {
2287c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2297c478bd9Sstevel@tonic-gate 		    gettext("Zero bytes are allocated to the file.\n"));
2307c478bd9Sstevel@tonic-gate 	} else {
2317c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2327c478bd9Sstevel@tonic-gate 		    gettext("Allocated size in range %llu - %llu bytes.\n"),
2337c478bd9Sstevel@tonic-gate 		    ((actualClusterCount - 1) * bpc) + 1,
2347c478bd9Sstevel@tonic-gate 		    (actualClusterCount * bpc));
2357c478bd9Sstevel@tonic-gate 	}
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * Use splitChain() to make an orphan that is the entire allocation
2387c478bd9Sstevel@tonic-gate 	 * chain.
2397c478bd9Sstevel@tonic-gate 	 */
2407c478bd9Sstevel@tonic-gate 	splitChain(fd, dp, startCluster, orphanEntry, &ignorei);
2417c478bd9Sstevel@tonic-gate 	return (TRUNCATED);
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@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)2457c478bd9Sstevel@tonic-gate noteUsage(int fd, int32_t startAt, struct pcdir *dp, struct pcdir *lp,
2467c478bd9Sstevel@tonic-gate     int32_t longEntryStartCluster, int isHidden, int isDir,
2477c478bd9Sstevel@tonic-gate     struct nameinfo *fullPathName)
2487c478bd9Sstevel@tonic-gate {
2497c478bd9Sstevel@tonic-gate 	struct pcdir *orphanEntry;
2507c478bd9Sstevel@tonic-gate 	int32_t chain = startAt;
2517c478bd9Sstevel@tonic-gate 	int32_t count = 0;
2527c478bd9Sstevel@tonic-gate 	int savePathNextIteration = 0;
2537c478bd9Sstevel@tonic-gate 	int haveBad = 0;
2547c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	while ((chain >= FIRST_CLUSTER) && (chain <= LastCluster)) {
2577c478bd9Sstevel@tonic-gate 		if ((markInUse(fd, chain, dp, lp, longEntryStartCluster,
2587c478bd9Sstevel@tonic-gate 		    isHidden ? HIDDEN : VISIBLE, &tmpl))
2597c478bd9Sstevel@tonic-gate 			!= CLINFO_NEWLY_ALLOCED)
2607c478bd9Sstevel@tonic-gate 			break;
2617c478bd9Sstevel@tonic-gate 		count++;
2627c478bd9Sstevel@tonic-gate 		if (savePathNextIteration == 1) {
2637c478bd9Sstevel@tonic-gate 			savePathNextIteration = 0;
2647c478bd9Sstevel@tonic-gate 			if (fullPathName != NULL)
2657c478bd9Sstevel@tonic-gate 				fullPathName->references++;
2667c478bd9Sstevel@tonic-gate 			squirrelPath(fullPathName, chain);
2677c478bd9Sstevel@tonic-gate 		}
2687c478bd9Sstevel@tonic-gate 		if (isMarkedBad(chain)) {
2697c478bd9Sstevel@tonic-gate 			haveBad = 1;
2707c478bd9Sstevel@tonic-gate 			savePathNextIteration = 1;
2717c478bd9Sstevel@tonic-gate 		}
2727c478bd9Sstevel@tonic-gate 		if (isHidden)
2737c478bd9Sstevel@tonic-gate 			HiddenClusterCount++;
2747c478bd9Sstevel@tonic-gate 		else if (isDir)
2757c478bd9Sstevel@tonic-gate 			DirClusterCount++;
2767c478bd9Sstevel@tonic-gate 		else
2777c478bd9Sstevel@tonic-gate 			FileClusterCount++;
2787c478bd9Sstevel@tonic-gate 		chain = nextInChain(chain);
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 	/*
2817c478bd9Sstevel@tonic-gate 	 * Do a sanity check on the file size in the directory entry.
2827c478bd9Sstevel@tonic-gate 	 * This may create an orphaned cluster chain.
2837c478bd9Sstevel@tonic-gate 	 */
2847c478bd9Sstevel@tonic-gate 	if (sanityCheckSize(fd, dp, count, isDir, startAt,
2857c478bd9Sstevel@tonic-gate 	    fullPathName, &orphanEntry) == TRUNCATED) {
2867c478bd9Sstevel@tonic-gate 		/*
2877c478bd9Sstevel@tonic-gate 		 * The pre-existing directory entry has been truncated,
2887c478bd9Sstevel@tonic-gate 		 * so the chain associated with it no longer has any
2897c478bd9Sstevel@tonic-gate 		 * bad clusters.  Instead, the new orphan has them.
2907c478bd9Sstevel@tonic-gate 		 */
2917c478bd9Sstevel@tonic-gate 		if (haveBad > 0) {
2927c478bd9Sstevel@tonic-gate 			truncChainWithBadCluster(fd, orphanEntry, startAt);
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 		haveBad = 0;
2957c478bd9Sstevel@tonic-gate 	}
2967c478bd9Sstevel@tonic-gate 	return (haveBad);
2977c478bd9Sstevel@tonic-gate }
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate static void
storeInfoAboutEntry(int fd,struct pcdir * dp,struct pcdir * ldp,int depth,int32_t longEntryStartCluster,char * fullPath,int * fullLen)3007c478bd9Sstevel@tonic-gate storeInfoAboutEntry(int fd, struct pcdir *dp, struct pcdir *ldp, int depth,
3017c478bd9Sstevel@tonic-gate     int32_t longEntryStartCluster, char *fullPath, int *fullLen)
3027c478bd9Sstevel@tonic-gate {
3037c478bd9Sstevel@tonic-gate 	struct nameinfo *pathCopy;
3047c478bd9Sstevel@tonic-gate 	int32_t start;
3057c478bd9Sstevel@tonic-gate 	int haveBad;
3067c478bd9Sstevel@tonic-gate 	int hidden = (dp->pcd_attr & PCA_HIDDEN || dp->pcd_attr & PCA_SYSTEM);
3077c478bd9Sstevel@tonic-gate 	int dir = (dp->pcd_attr & PCA_DIR);
3087c478bd9Sstevel@tonic-gate 	int i;
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	if (hidden)
3117c478bd9Sstevel@tonic-gate 		HiddenFileCount++;
3127c478bd9Sstevel@tonic-gate 	else if (dir)
3137c478bd9Sstevel@tonic-gate 		DirCount++;
3147c478bd9Sstevel@tonic-gate 	else
3157c478bd9Sstevel@tonic-gate 		FileCount++;
3167c478bd9Sstevel@tonic-gate 	appendToPath(dp, fullPath, fullLen);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	/*
3197c478bd9Sstevel@tonic-gate 	 * Make a copy of the name at this point.  We may want it to
3207c478bd9Sstevel@tonic-gate 	 * note the original source of an orphaned cluster.
3217c478bd9Sstevel@tonic-gate 	 */
3227c478bd9Sstevel@tonic-gate 	if ((pathCopy =
3237c478bd9Sstevel@tonic-gate 	    (struct nameinfo *)malloc(sizeof (struct nameinfo))) != NULL) {
3247c478bd9Sstevel@tonic-gate 		if ((pathCopy->fullName =
3257c478bd9Sstevel@tonic-gate 		    (char *)malloc(*fullLen + 1)) != NULL) {
3267c478bd9Sstevel@tonic-gate 			pathCopy->references = 0;
3277c478bd9Sstevel@tonic-gate 			(void) strncpy(pathCopy->fullName, fullPath, *fullLen);
3287c478bd9Sstevel@tonic-gate 			pathCopy->fullName[*fullLen] = '\0';
3297c478bd9Sstevel@tonic-gate 		} else {
3307c478bd9Sstevel@tonic-gate 			free(pathCopy);
3317c478bd9Sstevel@tonic-gate 			pathCopy = NULL;
3327c478bd9Sstevel@tonic-gate 		}
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 	if (Verbose) {
3357c478bd9Sstevel@tonic-gate 		for (i = 0; i < depth; i++)
3367c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "  ");
3377c478bd9Sstevel@tonic-gate 		if (hidden)
3387c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "[");
3397c478bd9Sstevel@tonic-gate 		else if (dir)
3407c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "|_");
3417c478bd9Sstevel@tonic-gate 		else
3427c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("(%06d) "), FileCount);
3437c478bd9Sstevel@tonic-gate 		printName(stderr, dp);
3447c478bd9Sstevel@tonic-gate 		if (hidden)
3457c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "]");
3467c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3477c478bd9Sstevel@tonic-gate 		    gettext(", %u bytes, start cluster %d"),
3487c478bd9Sstevel@tonic-gate 		    extractSize(dp), extractStartCluster(dp));
3497c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 	start = extractStartCluster(dp);
3527c478bd9Sstevel@tonic-gate 	haveBad = noteUsage(fd, start, dp, ldp, longEntryStartCluster,
3537c478bd9Sstevel@tonic-gate 	    hidden, dir, pathCopy);
3547c478bd9Sstevel@tonic-gate 	if (haveBad > 0) {
3557c478bd9Sstevel@tonic-gate 		if (dir && pathCopy->fullName != NULL) {
3567c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3577c478bd9Sstevel@tonic-gate 			    gettext("Adjusting for bad allocation units in "
3587c478bd9Sstevel@tonic-gate 			    "the meta-data of:\n  "));
3597c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, pathCopy->fullName);
3607c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\n");
3617c478bd9Sstevel@tonic-gate 		}
3627c478bd9Sstevel@tonic-gate 		truncChainWithBadCluster(fd, dp, start);
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 	if ((pathCopy != NULL) && (pathCopy->references == 0)) {
3657c478bd9Sstevel@tonic-gate 		free(pathCopy->fullName);
3667c478bd9Sstevel@tonic-gate 		free(pathCopy);
3677c478bd9Sstevel@tonic-gate 	}
3687c478bd9Sstevel@tonic-gate }
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate static void
storeInfoAboutLabel(struct pcdir * dp)3717c478bd9Sstevel@tonic-gate storeInfoAboutLabel(struct pcdir *dp)
3727c478bd9Sstevel@tonic-gate {
3737c478bd9Sstevel@tonic-gate 	/*
3747c478bd9Sstevel@tonic-gate 	 * XXX eventually depth should be passed to this routine just
3757c478bd9Sstevel@tonic-gate 	 * as it is with storeInfoAboutEntry().  If it isn't zero, then
3767c478bd9Sstevel@tonic-gate 	 * we've got a bogus directory entry.
3777c478bd9Sstevel@tonic-gate 	 */
3787c478bd9Sstevel@tonic-gate 	if (Verbose) {
3797c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("** "));
3807c478bd9Sstevel@tonic-gate 		printName(stderr, dp);
3817c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(" **\n"));
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate static void
searchChecks(struct pcdir * dp,int operation,char matchRequired,struct pcdir ** found)3867c478bd9Sstevel@tonic-gate searchChecks(struct pcdir *dp, int operation, char matchRequired,
3877c478bd9Sstevel@tonic-gate     struct pcdir **found)
3887c478bd9Sstevel@tonic-gate {
3897c478bd9Sstevel@tonic-gate 	/*
3907c478bd9Sstevel@tonic-gate 	 *  We support these searching operations:
3917c478bd9Sstevel@tonic-gate 	 *
3927c478bd9Sstevel@tonic-gate 	 *  PCFS_FIND_ATTR
3937c478bd9Sstevel@tonic-gate 	 *	look for the first file with a certain attribute
3947c478bd9Sstevel@tonic-gate 	 *	(e.g, find all hidden files)
3957c478bd9Sstevel@tonic-gate 	 *  PCFS_FIND_STATUS
3967c478bd9Sstevel@tonic-gate 	 *	look for the first file with a certain status
3977c478bd9Sstevel@tonic-gate 	 *	(e.g., the file has been marked deleted; making
3987c478bd9Sstevel@tonic-gate 	 *	its directory entry reusable)
3997c478bd9Sstevel@tonic-gate 	 *  PCFS_FIND_CHKS
4007c478bd9Sstevel@tonic-gate 	 *	look for all files with short names of the form
4017c478bd9Sstevel@tonic-gate 	 *	FILENNNN.CHK.  These are the file names we give
4027c478bd9Sstevel@tonic-gate 	 *	to chains of orphaned clusters we relink into the
4037c478bd9Sstevel@tonic-gate 	 *	file system.  This find facility allows us to seek
4047c478bd9Sstevel@tonic-gate 	 *	out all existing files of this naming form so that
4057c478bd9Sstevel@tonic-gate 	 *	we may create unique file names for new orphans.
4067c478bd9Sstevel@tonic-gate 	 */
4077c478bd9Sstevel@tonic-gate 	if (operation == PCFS_FIND_ATTR && dp->pcd_attr == matchRequired) {
4087c478bd9Sstevel@tonic-gate 		*found = dp;
4097c478bd9Sstevel@tonic-gate 	} else if (operation == PCFS_FIND_STATUS &&
4107c478bd9Sstevel@tonic-gate 	    dp->pcd_filename[0] == matchRequired) {
4117c478bd9Sstevel@tonic-gate 		*found = dp;
4127c478bd9Sstevel@tonic-gate 	} else if (operation == PCFS_FIND_CHKS && hasCHKName(dp)) {
4137c478bd9Sstevel@tonic-gate 		addToCHKList(dp);
4147c478bd9Sstevel@tonic-gate 	}
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate static void
catalogEntry(int fd,struct pcdir * dp,struct pcdir * longdp,int32_t currentCluster,int depth,char * recordPath,int * pathLen)4187c478bd9Sstevel@tonic-gate catalogEntry(int fd, struct pcdir *dp, struct pcdir *longdp,
4197c478bd9Sstevel@tonic-gate     int32_t currentCluster, int depth, char *recordPath, int *pathLen)
4207c478bd9Sstevel@tonic-gate {
4217c478bd9Sstevel@tonic-gate 	if (dp->pcd_attr & PCA_LABEL) {
4227c478bd9Sstevel@tonic-gate 		storeInfoAboutLabel(dp);
4237c478bd9Sstevel@tonic-gate 	} else {
4247c478bd9Sstevel@tonic-gate 		storeInfoAboutEntry(fd, dp, longdp, depth, currentCluster,
4257c478bd9Sstevel@tonic-gate 		    recordPath, pathLen);
4267c478bd9Sstevel@tonic-gate 	}
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /*
4307c478bd9Sstevel@tonic-gate  * visitNodes()
4317c478bd9Sstevel@tonic-gate  *
4327c478bd9Sstevel@tonic-gate  * This is the main workhouse routine for traversing pcfs metadata.
4337c478bd9Sstevel@tonic-gate  * There isn't a lot to the metadata.  Basically there is a root
4347c478bd9Sstevel@tonic-gate  * directory somewhere (either in its own special place outside the
4357c478bd9Sstevel@tonic-gate  * data area or in a data cluster).  The root directory (and all other
4367c478bd9Sstevel@tonic-gate  * directories) are filled with a number of fixed size entries.  An
4377c478bd9Sstevel@tonic-gate  * entry has the filename and extension, the file's attributes, the
4387c478bd9Sstevel@tonic-gate  * file's size, and the starting data cluster of the storage allocated
4397c478bd9Sstevel@tonic-gate  * to the file.  To determine which clusters are assigned to the file,
4407c478bd9Sstevel@tonic-gate  * you start at the starting cluster entry in the FAT, and follow the
4417c478bd9Sstevel@tonic-gate  * chain of entries in the FAT.
4427c478bd9Sstevel@tonic-gate  *
4437c478bd9Sstevel@tonic-gate  *	Arguments are:
4447c478bd9Sstevel@tonic-gate  *	fd
4457c478bd9Sstevel@tonic-gate  *		descriptor for accessing the raw file system data
4467c478bd9Sstevel@tonic-gate  *	currentCluster
4477c478bd9Sstevel@tonic-gate  *		original caller supplies the initial starting cluster,
4487c478bd9Sstevel@tonic-gate  *		subsequent recursive calls are made with updated
4497c478bd9Sstevel@tonic-gate  *		cluster numbers for the sub-directories.
4507c478bd9Sstevel@tonic-gate  *	dirData
4517c478bd9Sstevel@tonic-gate  *		pointer to the directory data bytes
4527c478bd9Sstevel@tonic-gate  *	dirDataLen
4537c478bd9Sstevel@tonic-gate  *		size of the whole buffer of data bytes (usually it is
4547c478bd9Sstevel@tonic-gate  *		the size of a cluster, but the root directory on
4557c478bd9Sstevel@tonic-gate  *		FAT12/16 is not necessarily the same size as a cluster).
4567c478bd9Sstevel@tonic-gate  *	depth
4577c478bd9Sstevel@tonic-gate  *		original caller should set it to zero (assuming they are
4587c478bd9Sstevel@tonic-gate  *		starting from the root directory).  This number is used to
4597c478bd9Sstevel@tonic-gate  *		change the indentation of file names presented as debug info.
4607c478bd9Sstevel@tonic-gate  *	descend
4617c478bd9Sstevel@tonic-gate  *		boolean indicates if we should descend into subdirectories.
4627c478bd9Sstevel@tonic-gate  *	operation
4637c478bd9Sstevel@tonic-gate  *		what, if any, matching should be performed.
4647c478bd9Sstevel@tonic-gate  *		The PCFS_TRAVERSE_ALL operation is a depth first traversal
4657c478bd9Sstevel@tonic-gate  *		of all nodes in the metadata tree, that tracks all the
4667c478bd9Sstevel@tonic-gate  *		clusters in use (according to the meta-data, at least)
4677c478bd9Sstevel@tonic-gate  *	matchRequired
4687c478bd9Sstevel@tonic-gate  *		value to be matched (if any)
4697c478bd9Sstevel@tonic-gate  *	found
4707c478bd9Sstevel@tonic-gate  *		output parameter
4717c478bd9Sstevel@tonic-gate  *		used to return pointer to a directory entry that matches
4727c478bd9Sstevel@tonic-gate  *		the search requirement
4737c478bd9Sstevel@tonic-gate  *		original caller should pass in a pointer to a NULL pointer.
4747c478bd9Sstevel@tonic-gate  *	lastDirCluster
4757c478bd9Sstevel@tonic-gate  *		output parameter
4767c478bd9Sstevel@tonic-gate  *		if no match found, last cluster num of starting directory
4777c478bd9Sstevel@tonic-gate  *	dirEnd
4787c478bd9Sstevel@tonic-gate  *		output parameter
4797c478bd9Sstevel@tonic-gate  *		if no match found, return parameter stores pointer to where
4807c478bd9Sstevel@tonic-gate  *		new directory entry could be appended to existing directory
4817c478bd9Sstevel@tonic-gate  *	recordPath
4827c478bd9Sstevel@tonic-gate  *		output parameter
4837c478bd9Sstevel@tonic-gate  *		as files are discovered, and directories traversed, this
4847c478bd9Sstevel@tonic-gate  *		buffer is used to store the current full path name.
4857c478bd9Sstevel@tonic-gate  *	pathLen
4867c478bd9Sstevel@tonic-gate  *		output parameter
4877c478bd9Sstevel@tonic-gate  *		this is in the integer length of the current full path name.
4887c478bd9Sstevel@tonic-gate  */
4897c478bd9Sstevel@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)4907c478bd9Sstevel@tonic-gate visitNodes(int fd, int32_t currentCluster, ClusterContents *dirData,
4917c478bd9Sstevel@tonic-gate     int32_t dirDataLen, int depth, int descend, int operation,
4927c478bd9Sstevel@tonic-gate     char matchRequired,  struct pcdir **found, int32_t *lastDirCluster,
4937c478bd9Sstevel@tonic-gate     struct pcdir **dirEnd, char *recordPath, int *pathLen)
4947c478bd9Sstevel@tonic-gate {
4957c478bd9Sstevel@tonic-gate 	struct pcdir *longdp = NULL;
4967c478bd9Sstevel@tonic-gate 	struct pcdir *dp;
4977c478bd9Sstevel@tonic-gate 	int32_t longStart;
4987c478bd9Sstevel@tonic-gate 	int withinLongName = 0;
4997c478bd9Sstevel@tonic-gate 	int saveLen = *pathLen;
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	dp = dirData->dirp;
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	/*
5047c478bd9Sstevel@tonic-gate 	 *  A directory entry where the first character of the name is
5057c478bd9Sstevel@tonic-gate 	 *  PCD_UNUSED indicates the end of the directory.
5067c478bd9Sstevel@tonic-gate 	 */
5077c478bd9Sstevel@tonic-gate 	while ((uchar_t *)dp < dirData->bytes + dirDataLen &&
5087c478bd9Sstevel@tonic-gate 	    dp->pcd_filename[0] != PCD_UNUSED) {
5097c478bd9Sstevel@tonic-gate 		/*
5107c478bd9Sstevel@tonic-gate 		 *  Handle the special case find operations.
5117c478bd9Sstevel@tonic-gate 		 */
5127c478bd9Sstevel@tonic-gate 		searchChecks(dp, operation, matchRequired, found);
5137c478bd9Sstevel@tonic-gate 		if (*found)
5147c478bd9Sstevel@tonic-gate 			break;
5157c478bd9Sstevel@tonic-gate 		/*
5167c478bd9Sstevel@tonic-gate 		 * Are we looking at part of a long file name entry?
5177c478bd9Sstevel@tonic-gate 		 * If so, we may need to note the start of the name.
5187c478bd9Sstevel@tonic-gate 		 * We don't do any further processing of long file
5197c478bd9Sstevel@tonic-gate 		 * name entries.
5207c478bd9Sstevel@tonic-gate 		 *
5217c478bd9Sstevel@tonic-gate 		 * We also skip deleted entries and the '.' and '..'
5227c478bd9Sstevel@tonic-gate 		 * entries.
5237c478bd9Sstevel@tonic-gate 		 */
5247c478bd9Sstevel@tonic-gate 		if ((dp->pcd_attr & PCDL_LFN_BITS) == PCDL_LFN_BITS) {
5257c478bd9Sstevel@tonic-gate 			if (!withinLongName) {
5267c478bd9Sstevel@tonic-gate 				withinLongName++;
5277c478bd9Sstevel@tonic-gate 				longStart = currentCluster;
5287c478bd9Sstevel@tonic-gate 				longdp = dp;
5297c478bd9Sstevel@tonic-gate 			}
5307c478bd9Sstevel@tonic-gate 			dp++;
5317c478bd9Sstevel@tonic-gate 			continue;
5327c478bd9Sstevel@tonic-gate 		} else if ((dp->pcd_filename[0] == PCD_ERASED) ||
5337c478bd9Sstevel@tonic-gate 		    (dp->pcd_filename[0] == '.')) {
5347c478bd9Sstevel@tonic-gate 			/*
5357c478bd9Sstevel@tonic-gate 			 * XXX - if we were within a long name, then
5367c478bd9Sstevel@tonic-gate 			 * its existence is bogus, because it is not
5377c478bd9Sstevel@tonic-gate 			 * attached to any real file.
5387c478bd9Sstevel@tonic-gate 			 */
5397c478bd9Sstevel@tonic-gate 			withinLongName = 0;
5407c478bd9Sstevel@tonic-gate 			dp++;
5417c478bd9Sstevel@tonic-gate 			continue;
5427c478bd9Sstevel@tonic-gate 		}
5437c478bd9Sstevel@tonic-gate 		withinLongName = 0;
5447c478bd9Sstevel@tonic-gate 		if (operation == PCFS_TRAVERSE_ALL)
5457c478bd9Sstevel@tonic-gate 			catalogEntry(fd, dp, longdp, longStart, depth,
5467c478bd9Sstevel@tonic-gate 			    recordPath, pathLen);
5477c478bd9Sstevel@tonic-gate 		longdp = NULL;
5487c478bd9Sstevel@tonic-gate 		longStart = 0;
5497c478bd9Sstevel@tonic-gate 		if (dp->pcd_attr & PCA_DIR && descend == PCFS_VISIT_SUBDIRS) {
5507c478bd9Sstevel@tonic-gate 			traverseDir(fd, extractStartCluster(dp), depth + 1,
5517c478bd9Sstevel@tonic-gate 			    descend, operation, matchRequired, found,
5527c478bd9Sstevel@tonic-gate 			    lastDirCluster, dirEnd, recordPath, pathLen);
5537c478bd9Sstevel@tonic-gate 			if (*found)
5547c478bd9Sstevel@tonic-gate 				break;
5557c478bd9Sstevel@tonic-gate 		}
5567c478bd9Sstevel@tonic-gate 		dp++;
5577c478bd9Sstevel@tonic-gate 		*pathLen = saveLen;
5587c478bd9Sstevel@tonic-gate 	}
5597c478bd9Sstevel@tonic-gate 	if (*found)
5607c478bd9Sstevel@tonic-gate 		return;
5617c478bd9Sstevel@tonic-gate 	if ((uchar_t *)dp < dirData->bytes + dirDataLen) {
5627c478bd9Sstevel@tonic-gate 		/*
5637c478bd9Sstevel@tonic-gate 		 * We reached the end of directory before the end of
5647c478bd9Sstevel@tonic-gate 		 * our provided data (a cluster).  That means this cluster
5657c478bd9Sstevel@tonic-gate 		 * is the last one in this directory's chain.  It also
5667c478bd9Sstevel@tonic-gate 		 * means we've just looked at the last directory entry.
5677c478bd9Sstevel@tonic-gate 		 */
5687c478bd9Sstevel@tonic-gate 		*lastDirCluster = currentCluster;
5697c478bd9Sstevel@tonic-gate 		*dirEnd = dp;
5707c478bd9Sstevel@tonic-gate 		return;
5717c478bd9Sstevel@tonic-gate 	}
5727c478bd9Sstevel@tonic-gate 	/*
5737c478bd9Sstevel@tonic-gate 	 * If there is more to the directory we'll go get it otherwise we
5747c478bd9Sstevel@tonic-gate 	 * are done traversing this directory.
5757c478bd9Sstevel@tonic-gate 	 */
5767c478bd9Sstevel@tonic-gate 	if ((currentCluster == FAKE_ROOTDIR_CLUST) ||
5777c478bd9Sstevel@tonic-gate 	    (lastInFAT(currentCluster))) {
5787c478bd9Sstevel@tonic-gate 		*lastDirCluster = currentCluster;
5797c478bd9Sstevel@tonic-gate 		return;
5807c478bd9Sstevel@tonic-gate 	} else {
5817c478bd9Sstevel@tonic-gate 		traverseDir(fd, nextInChain(currentCluster),
5827c478bd9Sstevel@tonic-gate 		    depth, descend, operation, matchRequired,
5837c478bd9Sstevel@tonic-gate 		    found, lastDirCluster, dirEnd, recordPath, pathLen);
5847c478bd9Sstevel@tonic-gate 		*pathLen = saveLen;
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate }
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate /*
5897c478bd9Sstevel@tonic-gate  *  traverseFromRoot()
5907c478bd9Sstevel@tonic-gate  *	For use with 12 and 16 bit FATs that have a root directory outside
5917c478bd9Sstevel@tonic-gate  *	of the file system.  This is a general purpose routine that
5927c478bd9Sstevel@tonic-gate  *	can be used simply to visit all of the nodes in the metadata or
5937c478bd9Sstevel@tonic-gate  *	to find the first instance of something, e.g., the first directory
5947c478bd9Sstevel@tonic-gate  *	entry where the file is marked deleted.
5957c478bd9Sstevel@tonic-gate  *
5967c478bd9Sstevel@tonic-gate  *	Inputs are described in the commentary for visitNodes() above.
5977c478bd9Sstevel@tonic-gate  */
5987c478bd9Sstevel@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)5997c478bd9Sstevel@tonic-gate traverseFromRoot(int fd, int depth, int descend, int operation,
6007c478bd9Sstevel@tonic-gate     char matchRequired,  struct pcdir **found, int32_t *lastDirCluster,
6017c478bd9Sstevel@tonic-gate     struct pcdir **dirEnd, char *recordPath, int *pathLen)
6027c478bd9Sstevel@tonic-gate {
6037c478bd9Sstevel@tonic-gate 	visitNodes(fd, FAKE_ROOTDIR_CLUST, &TheRootDir, RootDirSize, depth,
6047c478bd9Sstevel@tonic-gate 	    descend, operation, matchRequired, found, lastDirCluster, dirEnd,
6057c478bd9Sstevel@tonic-gate 	    recordPath, pathLen);
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate /*
6097c478bd9Sstevel@tonic-gate  *  traverseDir()
6107c478bd9Sstevel@tonic-gate  *	For use with all FATs outside of the initial root directory on
6117c478bd9Sstevel@tonic-gate  *	12 and 16 bit FAT file systems.  This is a general purpose routine
6127c478bd9Sstevel@tonic-gate  *	that can be used simply to visit all of the nodes in the metadata or
6137c478bd9Sstevel@tonic-gate  *	to find the first instance of something, e.g., the first directory
6147c478bd9Sstevel@tonic-gate  *	entry where the file is marked deleted.
6157c478bd9Sstevel@tonic-gate  *
6167c478bd9Sstevel@tonic-gate  *	Unique Input is:
6177c478bd9Sstevel@tonic-gate  *	startAt
6187c478bd9Sstevel@tonic-gate  *		starting cluster of the directory
6197c478bd9Sstevel@tonic-gate  *
6207c478bd9Sstevel@tonic-gate  *	This is the cluster that is the first one in this directory.
6217c478bd9Sstevel@tonic-gate  *	We read it right away, so we can provide it as data to visitNodes().
6227c478bd9Sstevel@tonic-gate  *	Note that we cache this cluster as we read it, because it is
6237c478bd9Sstevel@tonic-gate  *	metadata and we cache all metadata.  By doing so, we can
6247c478bd9Sstevel@tonic-gate  *	keep pointers to directory entries for quickly moving around and
6257c478bd9Sstevel@tonic-gate  *	fixing up any problems we find.  Of course if we get a big
6267c478bd9Sstevel@tonic-gate  *	filesystem with a huge amount of metadata we may be hosed, as
6277c478bd9Sstevel@tonic-gate  *	we'll likely run out of memory.
6287c478bd9Sstevel@tonic-gate  *
6297c478bd9Sstevel@tonic-gate  *	I believe in the future this will have to be addressed.  It
6307c478bd9Sstevel@tonic-gate  *	may be possible to do more of the processing of problems
6317c478bd9Sstevel@tonic-gate  *	within directories as they are cached, so that when memory
6327c478bd9Sstevel@tonic-gate  *	runs short we can free cached directories we are already
6337c478bd9Sstevel@tonic-gate  *	finished visiting.
6347c478bd9Sstevel@tonic-gate  *
6357c478bd9Sstevel@tonic-gate  *	The remainder of inputs are described in visitNodes() comments.
6367c478bd9Sstevel@tonic-gate  */
6377c478bd9Sstevel@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)6387c478bd9Sstevel@tonic-gate traverseDir(int fd, int32_t startAt, int depth, int descend, int operation,
6397c478bd9Sstevel@tonic-gate     char matchRequired,  struct pcdir **found, int32_t *lastDirCluster,
6407c478bd9Sstevel@tonic-gate     struct pcdir **dirEnd, char *recordPath, int *pathLen)
6417c478bd9Sstevel@tonic-gate {
6427c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
6437c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	if (startAt < FIRST_CLUSTER || startAt > LastCluster)
6467c478bd9Sstevel@tonic-gate 		return;
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	if (readCluster(fd, startAt, &(dirdata.bytes), &dirdatasize,
6497c478bd9Sstevel@tonic-gate 	    RDCLUST_DO_CACHE) != RDCLUST_GOOD) {
6507c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
6517c478bd9Sstevel@tonic-gate 		    gettext("Unable to get more directory entries!\n"));
6527c478bd9Sstevel@tonic-gate 		return;
6537c478bd9Sstevel@tonic-gate 	}
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	if (operation == PCFS_TRAVERSE_ALL) {
6567c478bd9Sstevel@tonic-gate 		if (Verbose)
6577c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
6587c478bd9Sstevel@tonic-gate 			    gettext("Directory traversal enters "
6597c478bd9Sstevel@tonic-gate 			    "allocation unit %d.\n"), startAt);
6607c478bd9Sstevel@tonic-gate 	}
6617c478bd9Sstevel@tonic-gate 	visitNodes(fd, startAt, &dirdata, dirdatasize, depth, descend,
6627c478bd9Sstevel@tonic-gate 	    operation, matchRequired, found, lastDirCluster, dirEnd,
6637c478bd9Sstevel@tonic-gate 	    recordPath, pathLen);
6647c478bd9Sstevel@tonic-gate }
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate void
createCHKNameList(int fd)6677c478bd9Sstevel@tonic-gate createCHKNameList(int fd)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	struct pcdir *ignorep1, *ignorep2;
6707c478bd9Sstevel@tonic-gate 	int32_t ignore32;
6717c478bd9Sstevel@tonic-gate 	char *ignorecp = NULL;
6727c478bd9Sstevel@tonic-gate 	char ignore = '\0';
6737c478bd9Sstevel@tonic-gate 	int ignoreint = 0;
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	ignorep1 = ignorep2 = NULL;
6767c478bd9Sstevel@tonic-gate 	if (!OkayToRelink || CHKsList != NULL)
6777c478bd9Sstevel@tonic-gate 		return;
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	/*
6807c478bd9Sstevel@tonic-gate 	 *  Allocate an array to keep a bit map of the integer
6817c478bd9Sstevel@tonic-gate 	 *  values used in CHK names.
6827c478bd9Sstevel@tonic-gate 	 */
6837c478bd9Sstevel@tonic-gate 	if ((CHKsList =
6847c478bd9Sstevel@tonic-gate 	    (uchar_t *)calloc(1, idivceil(MAXCHKVAL, NBBY))) == NULL) {
6857c478bd9Sstevel@tonic-gate 		OkayToRelink = 0;
6867c478bd9Sstevel@tonic-gate 		return;
6877c478bd9Sstevel@tonic-gate 	}
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	/*
6907c478bd9Sstevel@tonic-gate 	 *  Search the root directory for all the files with names of
6917c478bd9Sstevel@tonic-gate 	 *  the form FILEXXXX.CHK.  The root directory is an area
6927c478bd9Sstevel@tonic-gate 	 *  outside of the file space on FAT12 and FAT16 file systems.
6937c478bd9Sstevel@tonic-gate 	 *  On FAT32 file systems, the root directory is in a file
6947c478bd9Sstevel@tonic-gate 	 *  area cluster just like any other directory.
6957c478bd9Sstevel@tonic-gate 	 */
6967c478bd9Sstevel@tonic-gate 	if (!IsFAT32) {
6977c478bd9Sstevel@tonic-gate 		traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS,
6987c478bd9Sstevel@tonic-gate 		    ignore, &ignorep1, &ignore32, &ignorep2, ignorecp,
6997c478bd9Sstevel@tonic-gate 		    &ignoreint);
7007c478bd9Sstevel@tonic-gate 	} else {
7017c478bd9Sstevel@tonic-gate 		DirCount++;
7027c478bd9Sstevel@tonic-gate 		traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7037c478bd9Sstevel@tonic-gate 		    0, PCFS_NO_SUBDIRS, PCFS_FIND_CHKS, ignore,
7047c478bd9Sstevel@tonic-gate 		    &ignorep1, &ignore32, &ignorep2, ignorecp, &ignoreint);
7057c478bd9Sstevel@tonic-gate 	}
7067c478bd9Sstevel@tonic-gate }
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate char *
nextAvailableCHKName(int * chosen)7107c478bd9Sstevel@tonic-gate nextAvailableCHKName(int *chosen)
7117c478bd9Sstevel@tonic-gate {
7127c478bd9Sstevel@tonic-gate 	static char nameBuf[PCFNAMESIZE];
7137c478bd9Sstevel@tonic-gate 	int i;
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	if (!OkayToRelink)
7167c478bd9Sstevel@tonic-gate 		return (NULL);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	nameBuf[CHKNAME_F] = 'F';
7197c478bd9Sstevel@tonic-gate 	nameBuf[CHKNAME_I] = 'I';
7207c478bd9Sstevel@tonic-gate 	nameBuf[CHKNAME_L] = 'L';
7217c478bd9Sstevel@tonic-gate 	nameBuf[CHKNAME_E] = 'E';
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	for (i = 1; i <= MAXCHKVAL; i++) {
7247c478bd9Sstevel@tonic-gate 		if (!inUseCHKName(i))
7257c478bd9Sstevel@tonic-gate 			break;
7267c478bd9Sstevel@tonic-gate 	}
7277c478bd9Sstevel@tonic-gate 	if (i <= MAXCHKVAL) {
7287c478bd9Sstevel@tonic-gate 		nameBuf[CHKNAME_THOUSANDS] = '0' + (i / 1000);
7297c478bd9Sstevel@tonic-gate 		nameBuf[CHKNAME_HUNDREDS] = '0' + ((i % 1000) / 100);
7307c478bd9Sstevel@tonic-gate 		nameBuf[CHKNAME_TENS] = '0' + ((i % 100) / 10);
7317c478bd9Sstevel@tonic-gate 		nameBuf[CHKNAME_ONES] = '0' + (i % 10);
7327c478bd9Sstevel@tonic-gate 		*chosen = i;
7337c478bd9Sstevel@tonic-gate 		return (nameBuf);
7347c478bd9Sstevel@tonic-gate 	} else {
7357c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
7367c478bd9Sstevel@tonic-gate 		    gettext("Sorry, no names available for "
7377c478bd9Sstevel@tonic-gate 		    "relinking orphan chains!\n"));
7387c478bd9Sstevel@tonic-gate 		OkayToRelink = 0;
7397c478bd9Sstevel@tonic-gate 		return (NULL);
7407c478bd9Sstevel@tonic-gate 	}
7417c478bd9Sstevel@tonic-gate }
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate uint32_t
extractSize(struct pcdir * dp)7447c478bd9Sstevel@tonic-gate extractSize(struct pcdir *dp)
7457c478bd9Sstevel@tonic-gate {
7467c478bd9Sstevel@tonic-gate 	uint32_t returnMe;
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	read_32_bits((uchar_t *)&(dp->pcd_size), &returnMe);
7497c478bd9Sstevel@tonic-gate 	return (returnMe);
7507c478bd9Sstevel@tonic-gate }
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate int32_t
extractStartCluster(struct pcdir * dp)7537c478bd9Sstevel@tonic-gate extractStartCluster(struct pcdir *dp)
7547c478bd9Sstevel@tonic-gate {
7557c478bd9Sstevel@tonic-gate 	uint32_t lo, hi;
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	if (IsFAT32) {
7587c478bd9Sstevel@tonic-gate 		read_16_bits((uchar_t *)&(dp->un.pcd_scluster_hi), &hi);
7597c478bd9Sstevel@tonic-gate 		read_16_bits((uchar_t *)&(dp->pcd_scluster_lo), &lo);
7607c478bd9Sstevel@tonic-gate 		return ((int32_t)((hi << 16) | lo));
7617c478bd9Sstevel@tonic-gate 	} else {
7627c478bd9Sstevel@tonic-gate 		read_16_bits((uchar_t *)&(dp->pcd_scluster_lo), &lo);
7637c478bd9Sstevel@tonic-gate 		return ((int32_t)lo);
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate }
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate static struct pcdir *
findAvailableRootDirEntSlot(int fd,int32_t * clusterWithSlot)7687c478bd9Sstevel@tonic-gate findAvailableRootDirEntSlot(int fd, int32_t *clusterWithSlot)
7697c478bd9Sstevel@tonic-gate {
7707c478bd9Sstevel@tonic-gate 	struct pcdir *deletedEntry = NULL;
7717c478bd9Sstevel@tonic-gate 	struct pcdir *appendPoint = NULL;
7727c478bd9Sstevel@tonic-gate 	char *ignorecp = NULL;
7737c478bd9Sstevel@tonic-gate 	int ignore = 0;
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 	*clusterWithSlot = 0;
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	/*
7787c478bd9Sstevel@tonic-gate 	 *  First off, try to find an erased entry in the root
7797c478bd9Sstevel@tonic-gate 	 *  directory.  The root directory is an area outside of the
7807c478bd9Sstevel@tonic-gate 	 *  file space on FAT12 and FAT16 file systems.  On FAT32 file
7817c478bd9Sstevel@tonic-gate 	 *  systems, the root directory is in a file area cluster just
7827c478bd9Sstevel@tonic-gate 	 *  like any other directory.
7837c478bd9Sstevel@tonic-gate 	 */
7847c478bd9Sstevel@tonic-gate 	if (!IsFAT32) {
7857c478bd9Sstevel@tonic-gate 		traverseFromRoot(fd, 0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS,
7867c478bd9Sstevel@tonic-gate 		    PCD_ERASED, &deletedEntry, clusterWithSlot,
7877c478bd9Sstevel@tonic-gate 		    &appendPoint, ignorecp, &ignore);
7887c478bd9Sstevel@tonic-gate 	} else {
7897c478bd9Sstevel@tonic-gate 		DirCount++;
7907c478bd9Sstevel@tonic-gate 		traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7917c478bd9Sstevel@tonic-gate 		    0, PCFS_NO_SUBDIRS, PCFS_FIND_STATUS, PCD_ERASED,
7927c478bd9Sstevel@tonic-gate 		    &deletedEntry, clusterWithSlot, &appendPoint, ignorecp,
7937c478bd9Sstevel@tonic-gate 		    &ignore);
7947c478bd9Sstevel@tonic-gate 	}
7957c478bd9Sstevel@tonic-gate 	/*
7967c478bd9Sstevel@tonic-gate 	 *  If we found a deleted file in the directory we'll overwrite
7977c478bd9Sstevel@tonic-gate 	 *  that entry.
7987c478bd9Sstevel@tonic-gate 	 */
7997c478bd9Sstevel@tonic-gate 	if (deletedEntry)
8007c478bd9Sstevel@tonic-gate 		return (deletedEntry);
8017c478bd9Sstevel@tonic-gate 	/*
8027c478bd9Sstevel@tonic-gate 	 *  If there is room at the end of the existing directory, we
8037c478bd9Sstevel@tonic-gate 	 *  should place the new entry there.
8047c478bd9Sstevel@tonic-gate 	 */
8057c478bd9Sstevel@tonic-gate 	if (appendPoint)
8067c478bd9Sstevel@tonic-gate 		return (appendPoint);
8077c478bd9Sstevel@tonic-gate 	/*
8087c478bd9Sstevel@tonic-gate 	 *  XXX need to grow the directory
8097c478bd9Sstevel@tonic-gate 	 */
8107c478bd9Sstevel@tonic-gate 	return (NULL);
8117c478bd9Sstevel@tonic-gate }
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate static void
insertDirEnt(struct pcdir * slot,struct pcdir * entry,int32_t clusterWithSlot)8147c478bd9Sstevel@tonic-gate insertDirEnt(struct pcdir *slot, struct pcdir *entry, int32_t clusterWithSlot)
8157c478bd9Sstevel@tonic-gate {
8167c478bd9Sstevel@tonic-gate 	(void) memcpy(slot, entry, sizeof (struct pcdir));
8177c478bd9Sstevel@tonic-gate 	markClusterModified(clusterWithSlot);
8187c478bd9Sstevel@tonic-gate }
8197c478bd9Sstevel@tonic-gate 
820*264a6e74Sfrankho /*
821*264a6e74Sfrankho  *  Convert current UNIX time into a PCFS timestamp (which is in local time).
822*264a6e74Sfrankho  *
823*264a6e74Sfrankho  *  Since the "seconds" field of that is only accurate to 2sec precision,
824*264a6e74Sfrankho  *  we allow for the optional (used only for creation times on FAT) "msec"
825*264a6e74Sfrankho  *  parameter that takes the fractional part.
826*264a6e74Sfrankho  */
8277c478bd9Sstevel@tonic-gate static void
getNow(struct pctime * pctp,uchar_t * msec)828*264a6e74Sfrankho getNow(struct pctime *pctp, uchar_t *msec)
8297c478bd9Sstevel@tonic-gate {
830*264a6e74Sfrankho 	time_t		now;
831*264a6e74Sfrankho 	struct tm	tm;
832*264a6e74Sfrankho 	ushort_t	tim, dat;
8337c478bd9Sstevel@tonic-gate 
834*264a6e74Sfrankho 	/*
835*264a6e74Sfrankho 	 * Disable daylight savings corrections - Solaris PCFS doesn't
836*264a6e74Sfrankho 	 * support such conversions yet. Save timestamps in local time.
837*264a6e74Sfrankho 	 */
838*264a6e74Sfrankho 	daylight = 0;
839*264a6e74Sfrankho 
840*264a6e74Sfrankho 	(void) time(&now);
841*264a6e74Sfrankho 	(void) localtime_r(&now, &tm);
842*264a6e74Sfrankho 
843*264a6e74Sfrankho 	dat = (tm.tm_year - 80) << YEARSHIFT;
844*264a6e74Sfrankho 	dat |= tm.tm_mon << MONSHIFT;
845*264a6e74Sfrankho 	dat |= tm.tm_mday << DAYSHIFT;
846*264a6e74Sfrankho 	tim = tm.tm_hour << HOURSHIFT;
847*264a6e74Sfrankho 	tim |= tm.tm_min << MINSHIFT;
848*264a6e74Sfrankho 	tim |= (tm.tm_sec / 2) << SECSHIFT;
849*264a6e74Sfrankho 
850*264a6e74Sfrankho 	/*
851*264a6e74Sfrankho 	 * Sanity check. If we overflow the PCFS timestamp range
852*264a6e74Sfrankho 	 * we set the time to 01/01/1980, 00:00:00
853*264a6e74Sfrankho 	 */
854*264a6e74Sfrankho 	if (dat < 80 || dat > 227)
855*264a6e74Sfrankho 		dat = tim = 0;
856*264a6e74Sfrankho 
857*264a6e74Sfrankho 	pctp->pct_date = LE_16(dat);
858*264a6e74Sfrankho 	pctp->pct_time = LE_16(tim);
859*264a6e74Sfrankho 	if (msec)
860*264a6e74Sfrankho 		*msec = (tm.tm_sec & 1) ? 100 : 0;
8617c478bd9Sstevel@tonic-gate }
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate /*
8647c478bd9Sstevel@tonic-gate  *  FAT file systems store the following time information in a directory
8657c478bd9Sstevel@tonic-gate  *  entry:
866*264a6e74Sfrankho  *		timestamp		member of "struct pcdir"
867*264a6e74Sfrankho  * ======================================================================
868*264a6e74Sfrankho  *		creation time		pcd_crtime.pct_time
869*264a6e74Sfrankho  *		creation date		pcd_crtime.pct_date
870*264a6e74Sfrankho  *		last access date	pcd_ladate
871*264a6e74Sfrankho  *		last modify time	pcd_mtime.pct_time
872*264a6e74Sfrankho  *		last modify date	pcd_mtime.pct_date
8737c478bd9Sstevel@tonic-gate  *
8747c478bd9Sstevel@tonic-gate  *  No access time is kept.
8757c478bd9Sstevel@tonic-gate  */
8767c478bd9Sstevel@tonic-gate static void
updateDirEnt_CreatTime(struct pcdir * dp)8777c478bd9Sstevel@tonic-gate updateDirEnt_CreatTime(struct pcdir *dp)
8787c478bd9Sstevel@tonic-gate {
879*264a6e74Sfrankho 	getNow(&dp->pcd_crtime, &dp->pcd_crtime_msec);
8807c478bd9Sstevel@tonic-gate 	markClusterModified(findImpactedCluster(dp));
8817c478bd9Sstevel@tonic-gate }
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate static void
updateDirEnt_ModTimes(struct pcdir * dp)8847c478bd9Sstevel@tonic-gate updateDirEnt_ModTimes(struct pcdir *dp)
8857c478bd9Sstevel@tonic-gate {
8867c478bd9Sstevel@tonic-gate 	timestruc_t	ts;
8877c478bd9Sstevel@tonic-gate 
888*264a6e74Sfrankho 	getNow(&dp->pcd_mtime, NULL);
889*264a6e74Sfrankho 	dp->pcd_ladate = dp->pcd_mtime.pct_date;
8907c478bd9Sstevel@tonic-gate 	dp->pcd_attr |= PCA_ARCH;
8917c478bd9Sstevel@tonic-gate 	markClusterModified(findImpactedCluster(dp));
8927c478bd9Sstevel@tonic-gate }
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate struct pcdir *
addRootDirEnt(int fd,struct pcdir * new)8957c478bd9Sstevel@tonic-gate addRootDirEnt(int fd, struct pcdir *new)
8967c478bd9Sstevel@tonic-gate {
8977c478bd9Sstevel@tonic-gate 	struct pcdir *added;
8987c478bd9Sstevel@tonic-gate 	int32_t inCluster;
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	if ((added = findAvailableRootDirEntSlot(fd, &inCluster)) != NULL) {
9017c478bd9Sstevel@tonic-gate 		insertDirEnt(added, new, inCluster);
9027c478bd9Sstevel@tonic-gate 		return (added);
9037c478bd9Sstevel@tonic-gate 	}
9047c478bd9Sstevel@tonic-gate 	return (NULL);
9057c478bd9Sstevel@tonic-gate }
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate /*
9087c478bd9Sstevel@tonic-gate  *  FAT12 and FAT16 have a root directory outside the normal file space,
9097c478bd9Sstevel@tonic-gate  *  so we have separate routines for finding and reading the root directory.
9107c478bd9Sstevel@tonic-gate  */
9117c478bd9Sstevel@tonic-gate static off64_t
seekRootDirectory(int fd)9127c478bd9Sstevel@tonic-gate seekRootDirectory(int fd)
9137c478bd9Sstevel@tonic-gate {
9147c478bd9Sstevel@tonic-gate 	off64_t seekto;
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 	/*
9177c478bd9Sstevel@tonic-gate 	 *  The RootDir immediately follows the FATs, which in
9187c478bd9Sstevel@tonic-gate 	 *  turn immediately follow the reserved sectors.
9197c478bd9Sstevel@tonic-gate 	 */
9207c478bd9Sstevel@tonic-gate 	seekto = (off64_t)TheBIOSParameterBlock.bpb.resv_sectors *
9217c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector +
9227c478bd9Sstevel@tonic-gate 		    (off64_t)FATSize * TheBIOSParameterBlock.bpb.num_fats +
9237c478bd9Sstevel@tonic-gate 		    (off64_t)PartitionOffset;
9247c478bd9Sstevel@tonic-gate 	if (Verbose)
9257c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9267c478bd9Sstevel@tonic-gate 		    gettext("Seeking root directory @%lld.\n"), seekto);
9277c478bd9Sstevel@tonic-gate 	return (lseek64(fd, seekto, SEEK_SET));
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate void
getRootDirectory(int fd)9317c478bd9Sstevel@tonic-gate getRootDirectory(int fd)
9327c478bd9Sstevel@tonic-gate {
9337c478bd9Sstevel@tonic-gate 	ssize_t bytesRead;
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	if (TheRootDir.bytes != NULL)
9367c478bd9Sstevel@tonic-gate 		return;
9377c478bd9Sstevel@tonic-gate 	else if ((TheRootDir.bytes = (uchar_t *)malloc(RootDirSize)) == NULL) {
9387c478bd9Sstevel@tonic-gate 		mountSanityCheckFails();
9397c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for a copy of the root directory"));
9407c478bd9Sstevel@tonic-gate 		(void) close(fd);
9417c478bd9Sstevel@tonic-gate 		exit(8);
9427c478bd9Sstevel@tonic-gate 	}
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 	if (seekRootDirectory(fd) < 0) {
9457c478bd9Sstevel@tonic-gate 		mountSanityCheckFails();
9467c478bd9Sstevel@tonic-gate 		perror(gettext("Cannot seek to RootDir"));
9477c478bd9Sstevel@tonic-gate 		(void) close(fd);
9487c478bd9Sstevel@tonic-gate 		exit(8);
9497c478bd9Sstevel@tonic-gate 	}
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 	if (Verbose)
9527c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9537c478bd9Sstevel@tonic-gate 		    gettext("Reading root directory.\n"));
9547c478bd9Sstevel@tonic-gate 	if ((bytesRead = read(fd, TheRootDir.bytes, RootDirSize)) !=
9557c478bd9Sstevel@tonic-gate 	    RootDirSize) {
9567c478bd9Sstevel@tonic-gate 		mountSanityCheckFails();
9577c478bd9Sstevel@tonic-gate 		if (bytesRead < 0) {
9587c478bd9Sstevel@tonic-gate 			perror(gettext("Cannot read a RootDir"));
9597c478bd9Sstevel@tonic-gate 		} else {
9607c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
9617c478bd9Sstevel@tonic-gate 			    gettext("Short read of RootDir\n"));
9627c478bd9Sstevel@tonic-gate 		}
9637c478bd9Sstevel@tonic-gate 		(void) close(fd);
9647c478bd9Sstevel@tonic-gate 		exit(8);
9657c478bd9Sstevel@tonic-gate 	}
9667c478bd9Sstevel@tonic-gate 	if (Verbose) {
9677c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9687c478bd9Sstevel@tonic-gate 		    gettext("Dump of root dir's first 256 bytes.\n"));
9697c478bd9Sstevel@tonic-gate 		header_for_dump();
9707c478bd9Sstevel@tonic-gate 		dump_bytes(TheRootDir.bytes, 256);
9717c478bd9Sstevel@tonic-gate 	}
9727c478bd9Sstevel@tonic-gate }
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate void
writeRootDirMods(int fd)9757c478bd9Sstevel@tonic-gate writeRootDirMods(int fd)
9767c478bd9Sstevel@tonic-gate {
9777c478bd9Sstevel@tonic-gate 	ssize_t bytesWritten;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	if (!TheRootDir.bytes) {
9807c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9817c478bd9Sstevel@tonic-gate 		    gettext("Internal error: No Root directory to write\n"));
9827c478bd9Sstevel@tonic-gate 		(void) close(fd);
9837c478bd9Sstevel@tonic-gate 		exit(12);
9847c478bd9Sstevel@tonic-gate 	}
9857c478bd9Sstevel@tonic-gate 	if (!RootDirModified) {
9867c478bd9Sstevel@tonic-gate 		if (Verbose) {
9877c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
9887c478bd9Sstevel@tonic-gate 			    gettext("No root directory changes need to "
9897c478bd9Sstevel@tonic-gate 			    "be written.\n"));
9907c478bd9Sstevel@tonic-gate 		}
9917c478bd9Sstevel@tonic-gate 		return;
9927c478bd9Sstevel@tonic-gate 	}
9937c478bd9Sstevel@tonic-gate 	if (ReadOnly)
9947c478bd9Sstevel@tonic-gate 		return;
9957c478bd9Sstevel@tonic-gate 	if (Verbose)
9967c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9977c478bd9Sstevel@tonic-gate 		    gettext("Writing root directory.\n"));
9987c478bd9Sstevel@tonic-gate 	if (seekRootDirectory(fd) < 0) {
9997c478bd9Sstevel@tonic-gate 		perror(gettext("Cannot write the RootDir (seek failed)"));
10007c478bd9Sstevel@tonic-gate 		(void) close(fd);
10017c478bd9Sstevel@tonic-gate 		exit(12);
10027c478bd9Sstevel@tonic-gate 	}
10037c478bd9Sstevel@tonic-gate 	if ((bytesWritten = write(fd, TheRootDir.bytes, RootDirSize)) !=
10047c478bd9Sstevel@tonic-gate 	    RootDirSize) {
10057c478bd9Sstevel@tonic-gate 		if (bytesWritten < 0) {
10067c478bd9Sstevel@tonic-gate 			perror(gettext("Cannot write the RootDir"));
10077c478bd9Sstevel@tonic-gate 		} else {
10087c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
10097c478bd9Sstevel@tonic-gate 			    gettext("Short write of root directory\n"));
10107c478bd9Sstevel@tonic-gate 		}
10117c478bd9Sstevel@tonic-gate 		(void) close(fd);
10127c478bd9Sstevel@tonic-gate 		exit(12);
10137c478bd9Sstevel@tonic-gate 	}
10147c478bd9Sstevel@tonic-gate 	RootDirModified = 0;
10157c478bd9Sstevel@tonic-gate }
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate struct pcdir *
newDirEnt(struct pcdir * copyme)10187c478bd9Sstevel@tonic-gate newDirEnt(struct pcdir *copyme)
10197c478bd9Sstevel@tonic-gate {
10207c478bd9Sstevel@tonic-gate 	struct pcdir *ndp;
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	if ((ndp = (struct pcdir *)calloc(1, sizeof (struct pcdir))) == NULL) {
10237c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Out of memory to create a "
10247c478bd9Sstevel@tonic-gate 		    "new directory entry!\n"));
10257c478bd9Sstevel@tonic-gate 		return (ndp);
10267c478bd9Sstevel@tonic-gate 	}
10277c478bd9Sstevel@tonic-gate 	if (copyme)
10287c478bd9Sstevel@tonic-gate 		(void) memcpy(ndp, copyme, sizeof (struct pcdir));
10297c478bd9Sstevel@tonic-gate 	ndp->pcd_ext[CHKNAME_C] = 'C';
10307c478bd9Sstevel@tonic-gate 	ndp->pcd_ext[CHKNAME_H] = 'H';
10317c478bd9Sstevel@tonic-gate 	ndp->pcd_ext[CHKNAME_K] = 'K';
10327c478bd9Sstevel@tonic-gate 	updateDirEnt_CreatTime(ndp);
10337c478bd9Sstevel@tonic-gate 	updateDirEnt_ModTimes(ndp);
10347c478bd9Sstevel@tonic-gate 	return (ndp);
10357c478bd9Sstevel@tonic-gate }
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate void
updateDirEnt_Size(struct pcdir * dp,uint32_t newSize)10387c478bd9Sstevel@tonic-gate updateDirEnt_Size(struct pcdir *dp, uint32_t newSize)
10397c478bd9Sstevel@tonic-gate {
10407c478bd9Sstevel@tonic-gate 	uchar_t *p = (uchar_t *)&(dp->pcd_size);
10417c478bd9Sstevel@tonic-gate 	store_32_bits(&p, newSize);
10427c478bd9Sstevel@tonic-gate 	markClusterModified(findImpactedCluster(dp));
10437c478bd9Sstevel@tonic-gate }
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate void
updateDirEnt_Start(struct pcdir * dp,int32_t newStart)10467c478bd9Sstevel@tonic-gate updateDirEnt_Start(struct pcdir *dp, int32_t newStart)
10477c478bd9Sstevel@tonic-gate {
10487c478bd9Sstevel@tonic-gate 	uchar_t *p = (uchar_t *)&(dp->pcd_scluster_lo);
10497c478bd9Sstevel@tonic-gate 	store_16_bits(&p, newStart & 0xffff);
10507c478bd9Sstevel@tonic-gate 	if (IsFAT32) {
10517c478bd9Sstevel@tonic-gate 		p = (uchar_t *)&(dp->un.pcd_scluster_hi);
10527c478bd9Sstevel@tonic-gate 		store_16_bits(&p, newStart >> 16);
10537c478bd9Sstevel@tonic-gate 	}
10547c478bd9Sstevel@tonic-gate 	markClusterModified(findImpactedCluster(dp));
10557c478bd9Sstevel@tonic-gate }
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate void
updateDirEnt_Name(struct pcdir * dp,char * newName)10587c478bd9Sstevel@tonic-gate updateDirEnt_Name(struct pcdir *dp, char *newName)
10597c478bd9Sstevel@tonic-gate {
10607c478bd9Sstevel@tonic-gate 	int i;
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	for (i = 0; i < PCFNAMESIZE; i++) {
10637c478bd9Sstevel@tonic-gate 		if (*newName)
10647c478bd9Sstevel@tonic-gate 			dp->pcd_filename[i] = *newName++;
10657c478bd9Sstevel@tonic-gate 		else
10667c478bd9Sstevel@tonic-gate 			dp->pcd_filename[i] = ' ';
10677c478bd9Sstevel@tonic-gate 	}
10687c478bd9Sstevel@tonic-gate 	markClusterModified(findImpactedCluster(dp));
10697c478bd9Sstevel@tonic-gate }
1070