xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/fsck/clusters.c (revision 48bbca81)
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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
247c478bd9Sstevel@tonic-gate  * All rights reserved.
25*48bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * fsck_pcfs -- routines for manipulating clusters.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate #include <stdio.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <unistd.h>
367c478bd9Sstevel@tonic-gate #include <stdlib.h>
377c478bd9Sstevel@tonic-gate #include <libintl.h>
387c478bd9Sstevel@tonic-gate #include <errno.h>
397c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
417c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
427c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
437c478bd9Sstevel@tonic-gate #include "pcfs_common.h"
447c478bd9Sstevel@tonic-gate #include "fsck_pcfs.h"
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate extern	ClusterContents	TheRootDir;
477c478bd9Sstevel@tonic-gate extern	off64_t		FirstClusterOffset;
487c478bd9Sstevel@tonic-gate extern	off64_t		PartitionOffset;
497c478bd9Sstevel@tonic-gate extern	int32_t		BytesPerCluster;
507c478bd9Sstevel@tonic-gate extern	int32_t		TotalClusters;
517c478bd9Sstevel@tonic-gate extern	int32_t		LastCluster;
527c478bd9Sstevel@tonic-gate extern	int32_t		RootDirSize;
537c478bd9Sstevel@tonic-gate extern	int32_t		FATSize;
547c478bd9Sstevel@tonic-gate extern	bpb_t		TheBIOSParameterBlock;
557c478bd9Sstevel@tonic-gate extern	short		FATEntrySize;
567c478bd9Sstevel@tonic-gate extern	int		RootDirModified;
577c478bd9Sstevel@tonic-gate extern	int		OkayToRelink;
587c478bd9Sstevel@tonic-gate extern	int		ReadOnly;
597c478bd9Sstevel@tonic-gate extern	int		IsFAT32;
607c478bd9Sstevel@tonic-gate extern	int		Verbose;
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate static	struct pcdir	BlankPCDIR;
637c478bd9Sstevel@tonic-gate static	CachedCluster	*ClusterCache;
647c478bd9Sstevel@tonic-gate static	ClusterInfo	**InUse;
657c478bd9Sstevel@tonic-gate static	int32_t		ReservedClusterCount;
667c478bd9Sstevel@tonic-gate static	int32_t		AllocedClusterCount;
677c478bd9Sstevel@tonic-gate static	int32_t		FreeClusterCount;
687c478bd9Sstevel@tonic-gate static	int32_t		BadClusterCount;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * Internal statistics
727c478bd9Sstevel@tonic-gate  */
737c478bd9Sstevel@tonic-gate static	int32_t		CachedClusterCount;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate int32_t	HiddenClusterCount;
767c478bd9Sstevel@tonic-gate int32_t	FileClusterCount;
777c478bd9Sstevel@tonic-gate int32_t	DirClusterCount;
787c478bd9Sstevel@tonic-gate int32_t	HiddenFileCount;
797c478bd9Sstevel@tonic-gate int32_t	FileCount;
807c478bd9Sstevel@tonic-gate int32_t	DirCount;
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate static int32_t orphanSizeLookup(int32_t clusterNum);
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate static void
857c478bd9Sstevel@tonic-gate freeNameInfo(int32_t clusterNum)
867c478bd9Sstevel@tonic-gate {
877c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
887c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
897c478bd9Sstevel@tonic-gate 		return;
907c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
917c478bd9Sstevel@tonic-gate 		if (InUse[clusterNum - FIRST_CLUSTER]->path->references > 1) {
927c478bd9Sstevel@tonic-gate 			InUse[clusterNum - FIRST_CLUSTER]->path->references--;
937c478bd9Sstevel@tonic-gate 		} else {
947c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
957c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->path);
967c478bd9Sstevel@tonic-gate 		}
977c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER]->path = NULL;
987c478bd9Sstevel@tonic-gate 	}
997c478bd9Sstevel@tonic-gate }
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate static void
1027c478bd9Sstevel@tonic-gate printOrphanPath(int32_t clusterNum)
1037c478bd9Sstevel@tonic-gate {
1047c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1057c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1067c478bd9Sstevel@tonic-gate 		return;
1077c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
1087c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\nOrphaned allocation units originally "
1097c478bd9Sstevel@tonic-gate 		    "allocated to:\n"));
1107c478bd9Sstevel@tonic-gate 		(void) printf("%s\n",
1117c478bd9Sstevel@tonic-gate 		    InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
1127c478bd9Sstevel@tonic-gate 		freeNameInfo(clusterNum);
1137c478bd9Sstevel@tonic-gate 	} else {
1147c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\nOrphaned allocation units originally "
1157c478bd9Sstevel@tonic-gate 		    "allocated to an unknown file or directory:\n"));
1167c478bd9Sstevel@tonic-gate 		(void) printf(gettext("Orphaned chain begins with allocation "
1177c478bd9Sstevel@tonic-gate 		    "unit %d.\n"), clusterNum);
1187c478bd9Sstevel@tonic-gate 	}
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate static void
1227c478bd9Sstevel@tonic-gate printOrphanSize(int32_t clusterNum)
1237c478bd9Sstevel@tonic-gate {
1247c478bd9Sstevel@tonic-gate 	int32_t size = orphanSizeLookup(clusterNum);
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	if (size > 0) {
1277c478bd9Sstevel@tonic-gate 		(void) printf(gettext("%d bytes in the orphaned chain of "
1287c478bd9Sstevel@tonic-gate 		    "allocation units.\n"), size);
1297c478bd9Sstevel@tonic-gate 		if (Verbose) {
1307c478bd9Sstevel@tonic-gate 			(void) printf(gettext("[Starting at allocation "
1317c478bd9Sstevel@tonic-gate 			    "unit %d]\n"), clusterNum);
1327c478bd9Sstevel@tonic-gate 		}
1337c478bd9Sstevel@tonic-gate 	}
1347c478bd9Sstevel@tonic-gate }
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate static void
1377c478bd9Sstevel@tonic-gate printOrphanInfo(int32_t clusterNum)
1387c478bd9Sstevel@tonic-gate {
1397c478bd9Sstevel@tonic-gate 	printOrphanPath(clusterNum);
1407c478bd9Sstevel@tonic-gate 	printOrphanSize(clusterNum);
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate static int
1447c478bd9Sstevel@tonic-gate askAboutFreeing(int32_t clusterNum)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	/*
1477c478bd9Sstevel@tonic-gate 	 * If it is not OkayToRelink, we haven't already printed the size
1487c478bd9Sstevel@tonic-gate 	 * of the orphaned chain.
1497c478bd9Sstevel@tonic-gate 	 */
1507c478bd9Sstevel@tonic-gate 	if (!OkayToRelink)
1517c478bd9Sstevel@tonic-gate 		printOrphanInfo(clusterNum);
1527c478bd9Sstevel@tonic-gate 	/*
1537c478bd9Sstevel@tonic-gate 	 *  If we are in preen mode, preenBail won't return.
1547c478bd9Sstevel@tonic-gate 	 */
1557c478bd9Sstevel@tonic-gate 	preenBail("Need user confirmation to free orphaned chain.\n");
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	(void) printf(
1587c478bd9Sstevel@tonic-gate 	    gettext("Free the allocation units in the orphaned chain ? "
1597c478bd9Sstevel@tonic-gate 	    "(y/n) "));
1607c478bd9Sstevel@tonic-gate 	return (yes());
1617c478bd9Sstevel@tonic-gate }
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate static int
1647c478bd9Sstevel@tonic-gate askAboutRelink(int32_t clusterNum)
1657c478bd9Sstevel@tonic-gate {
1667c478bd9Sstevel@tonic-gate 	/*
1677c478bd9Sstevel@tonic-gate 	 * Display the size of the chain for the user to consider.
1687c478bd9Sstevel@tonic-gate 	 */
1697c478bd9Sstevel@tonic-gate 	printOrphanInfo(clusterNum);
1707c478bd9Sstevel@tonic-gate 	/*
1717c478bd9Sstevel@tonic-gate 	 *  If we are in preen mode, preenBail won't return.
1727c478bd9Sstevel@tonic-gate 	 */
1737c478bd9Sstevel@tonic-gate 	preenBail("Need user confirmation to re-link orphaned chain.\n");
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	(void) printf(gettext("Re-link orphaned chain into file system ? "
1767c478bd9Sstevel@tonic-gate 	    "(y/n) "));
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	return (yes());
1797c478bd9Sstevel@tonic-gate }
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate static int
1827c478bd9Sstevel@tonic-gate isHidden(int32_t clusterNum)
1837c478bd9Sstevel@tonic-gate {
1847c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1857c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1867c478bd9Sstevel@tonic-gate 		return (0);
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
1897c478bd9Sstevel@tonic-gate 		return (0);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_HIDDEN);
1927c478bd9Sstevel@tonic-gate }
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate static int
1957c478bd9Sstevel@tonic-gate isInUse(int32_t clusterNum)
1967c478bd9Sstevel@tonic-gate {
1977c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1987c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1997c478bd9Sstevel@tonic-gate 		return (0);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	return ((InUse[clusterNum - FIRST_CLUSTER] != NULL) &&
2027c478bd9Sstevel@tonic-gate 		(InUse[clusterNum - FIRST_CLUSTER]->dirent != NULL));
2037c478bd9Sstevel@tonic-gate }
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate /*
2067c478bd9Sstevel@tonic-gate  *  Caller's may request that we cache the data from a readCluster.
2077c478bd9Sstevel@tonic-gate  *  The xxxClusterxxxCachexxx routines handle looking for cached data
2087c478bd9Sstevel@tonic-gate  *  or initially caching the data.
2097c478bd9Sstevel@tonic-gate  *
2107c478bd9Sstevel@tonic-gate  *  XXX - facilitate releasing cached data for low memory situations.
2117c478bd9Sstevel@tonic-gate  */
2127c478bd9Sstevel@tonic-gate static CachedCluster *
2137c478bd9Sstevel@tonic-gate findClusterCacheEntry(int32_t clusterNum)
2147c478bd9Sstevel@tonic-gate {
2157c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	while (loop != NULL) {
2187c478bd9Sstevel@tonic-gate 		if (loop->clusterNum == clusterNum)
2197c478bd9Sstevel@tonic-gate 			return (loop);
2207c478bd9Sstevel@tonic-gate 		loop = loop->next;
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate 	return (NULL);
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate static uchar_t *
2267c478bd9Sstevel@tonic-gate findClusterDataInTheCache(int32_t clusterNum)
2277c478bd9Sstevel@tonic-gate {
2287c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	while (loop) {
2317c478bd9Sstevel@tonic-gate 		if (loop->clusterNum == clusterNum)
2327c478bd9Sstevel@tonic-gate 			return (loop->clusterData.bytes);
2337c478bd9Sstevel@tonic-gate 		loop = loop->next;
2347c478bd9Sstevel@tonic-gate 	}
2357c478bd9Sstevel@tonic-gate 	return (NULL);
2367c478bd9Sstevel@tonic-gate }
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate static uchar_t *
2397c478bd9Sstevel@tonic-gate addToCache(int32_t clusterNum, uchar_t *buf, int32_t *datasize)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate 	CachedCluster *new;
2427c478bd9Sstevel@tonic-gate 	uchar_t *cp;
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	if ((new = (CachedCluster *)malloc(sizeof (CachedCluster))) == NULL) {
2457c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for cached cluster info"));
2467c478bd9Sstevel@tonic-gate 		return (buf);
2477c478bd9Sstevel@tonic-gate 	}
2487c478bd9Sstevel@tonic-gate 	new->clusterNum = clusterNum;
2497c478bd9Sstevel@tonic-gate 	new->modified = 0;
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	if ((cp = (uchar_t *)calloc(1, BytesPerCluster)) == NULL) {
2527c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for cached copy of cluster"));
2537c478bd9Sstevel@tonic-gate 		free(new);
2547c478bd9Sstevel@tonic-gate 		return (buf);
2557c478bd9Sstevel@tonic-gate 	}
2567c478bd9Sstevel@tonic-gate 	(void) memcpy(cp, buf, *datasize);
2577c478bd9Sstevel@tonic-gate 	new->clusterData.bytes = cp;
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	if (Verbose) {
2607c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2617c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d cached.\n"), clusterNum);
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 	if (ClusterCache == NULL) {
2647c478bd9Sstevel@tonic-gate 		ClusterCache = new;
2657c478bd9Sstevel@tonic-gate 		new->next = NULL;
2667c478bd9Sstevel@tonic-gate 	} else if (new->clusterNum < ClusterCache->clusterNum) {
2677c478bd9Sstevel@tonic-gate 		new->next = ClusterCache;
2687c478bd9Sstevel@tonic-gate 		ClusterCache = new;
2697c478bd9Sstevel@tonic-gate 	} else {
2707c478bd9Sstevel@tonic-gate 		CachedCluster *loop = ClusterCache;
2717c478bd9Sstevel@tonic-gate 		CachedCluster *trailer = NULL;
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 		while (loop && new->clusterNum > loop->clusterNum) {
2747c478bd9Sstevel@tonic-gate 			trailer = loop;
2757c478bd9Sstevel@tonic-gate 			loop = loop->next;
2767c478bd9Sstevel@tonic-gate 		}
2777c478bd9Sstevel@tonic-gate 		trailer->next = new;
2787c478bd9Sstevel@tonic-gate 		if (loop) {
2797c478bd9Sstevel@tonic-gate 			new->next = loop;
2807c478bd9Sstevel@tonic-gate 		} else {
2817c478bd9Sstevel@tonic-gate 			new->next = NULL;
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 	CachedClusterCount++;
2857c478bd9Sstevel@tonic-gate 	return (new->clusterData.bytes);
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate static int
2897c478bd9Sstevel@tonic-gate seekCluster(int fd, int32_t clusterNum)
2907c478bd9Sstevel@tonic-gate {
2917c478bd9Sstevel@tonic-gate 	off64_t seekto;
2927c478bd9Sstevel@tonic-gate 	int saveError;
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	seekto = FirstClusterOffset +
2957c478bd9Sstevel@tonic-gate 	    ((off64_t)clusterNum - FIRST_CLUSTER) * BytesPerCluster;
2967c478bd9Sstevel@tonic-gate 	if (lseek64(fd, seekto, SEEK_SET) != seekto) {
2977c478bd9Sstevel@tonic-gate 		saveError = errno;
2987c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2997c478bd9Sstevel@tonic-gate 		    gettext("Seek to Allocation unit #%d failed: "),
3007c478bd9Sstevel@tonic-gate 		    clusterNum);
3017c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, strerror(saveError));
3027c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
3037c478bd9Sstevel@tonic-gate 		return (0);
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 	return (1);
3067c478bd9Sstevel@tonic-gate }
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate /*
3097c478bd9Sstevel@tonic-gate  *  getcluster
3107c478bd9Sstevel@tonic-gate  *	Get cluster bytes off the disk.  We always read those bytes into
311*48bbca81SDaniel Hoffman  *	the same static buffer.  If the caller wants its own copy of the
312*48bbca81SDaniel Hoffman  *	data it'll have to make its own copy.  We'll return all the data
3137c478bd9Sstevel@tonic-gate  *	read, even if it's short of a full cluster.  This is for future use
3147c478bd9Sstevel@tonic-gate  *	when we might want to relocate any salvagable data from bad clusters.
3157c478bd9Sstevel@tonic-gate  */
3167c478bd9Sstevel@tonic-gate static int
3177c478bd9Sstevel@tonic-gate getCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize)
3187c478bd9Sstevel@tonic-gate {
3197c478bd9Sstevel@tonic-gate 	static uchar_t *clusterBuffer = NULL;
3207c478bd9Sstevel@tonic-gate 	int saveError;
3217c478bd9Sstevel@tonic-gate 	int try;
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	*datasize = 0;
3247c478bd9Sstevel@tonic-gate 	*data = NULL;
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
3277c478bd9Sstevel@tonic-gate 		return (RDCLUST_BADINPUT);
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	if (clusterBuffer == NULL &&
3307c478bd9Sstevel@tonic-gate 	    (clusterBuffer = (uchar_t *)malloc(BytesPerCluster)) == NULL) {
3317c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for a cluster data buffer"));
3327c478bd9Sstevel@tonic-gate 		return (RDCLUST_MEMERR);
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	for (try = 0; try < RDCLUST_MAX_RETRY; try++) {
3367c478bd9Sstevel@tonic-gate 		if (!seekCluster(fd, clusterNum))
3377c478bd9Sstevel@tonic-gate 			return (RDCLUST_FAIL);
3387c478bd9Sstevel@tonic-gate 		if ((*datasize = read(fd, clusterBuffer, BytesPerCluster)) ==
3397c478bd9Sstevel@tonic-gate 		    BytesPerCluster) {
3407c478bd9Sstevel@tonic-gate 			*data = clusterBuffer;
3417c478bd9Sstevel@tonic-gate 			return (RDCLUST_GOOD);
3427c478bd9Sstevel@tonic-gate 		}
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 	if (*datasize >= 0) {
3457c478bd9Sstevel@tonic-gate 		*data = clusterBuffer;
3467c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3477c478bd9Sstevel@tonic-gate 		    gettext("Short read of allocation unit #%d\n"), clusterNum);
3487c478bd9Sstevel@tonic-gate 	} else {
3497c478bd9Sstevel@tonic-gate 		saveError = errno;
3507c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "Allocation unit %d:", clusterNum);
3517c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, strerror(saveError));
3527c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate 	return (RDCLUST_FAIL);
3557c478bd9Sstevel@tonic-gate }
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate static void
3587c478bd9Sstevel@tonic-gate writeCachedCluster(int fd, CachedCluster *clustInfo)
3597c478bd9Sstevel@tonic-gate {
3607c478bd9Sstevel@tonic-gate 	ssize_t bytesWritten;
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	if (ReadOnly)
3637c478bd9Sstevel@tonic-gate 		return;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	if (Verbose)
3667c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3677c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d modified.\n"),
3687c478bd9Sstevel@tonic-gate 		    clustInfo->clusterNum);
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 	if (seekCluster(fd, clustInfo->clusterNum) == NULL)
3717c478bd9Sstevel@tonic-gate 		return;
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	if ((bytesWritten = write(fd, clustInfo->clusterData.bytes,
3747c478bd9Sstevel@tonic-gate 	    BytesPerCluster)) != BytesPerCluster) {
3757c478bd9Sstevel@tonic-gate 		if (bytesWritten < 0) {
3767c478bd9Sstevel@tonic-gate 			perror(gettext("Failed to write modified "
3777c478bd9Sstevel@tonic-gate 			    "allocation unit"));
3787c478bd9Sstevel@tonic-gate 		} else {
3797c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3807c478bd9Sstevel@tonic-gate 			    gettext("Short write of allocation unit %d\n"),
3817c478bd9Sstevel@tonic-gate 			    clustInfo->clusterNum);
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 		(void) close(fd);
3847c478bd9Sstevel@tonic-gate 		exit(13);
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate }
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate /*
3897c478bd9Sstevel@tonic-gate  * It's cheaper to allocate a lot at a time; malloc overhead pushes
3907c478bd9Sstevel@tonic-gate  * you over the brink much more quickly if you don't.
3917c478bd9Sstevel@tonic-gate  * This numbers seems to be a fair trade-off between reduced malloc overhead
3927c478bd9Sstevel@tonic-gate  * and additional overhead by over-allocating.
3937c478bd9Sstevel@tonic-gate  */
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate #define	CHUNKSIZE	1024
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate static ClusterInfo *pool;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate static ClusterInfo *
4007c478bd9Sstevel@tonic-gate newClusterInfo(void)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	ClusterInfo *ret;
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	if (pool == NULL) {
4067c478bd9Sstevel@tonic-gate 		int i;
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 		pool = (ClusterInfo *)malloc(sizeof (ClusterInfo) * CHUNKSIZE);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 		if (pool == NULL) {
4117c478bd9Sstevel@tonic-gate 			perror(
4127c478bd9Sstevel@tonic-gate 			    gettext("Out of memory for cluster information"));
4137c478bd9Sstevel@tonic-gate 			exit(9);
4147c478bd9Sstevel@tonic-gate 		}
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 		for (i = 0; i < CHUNKSIZE - 1; i++)
4177c478bd9Sstevel@tonic-gate 			pool[i].nextfree = &pool[i+1];
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 		pool[CHUNKSIZE-1].nextfree = NULL;
4207c478bd9Sstevel@tonic-gate 	}
4217c478bd9Sstevel@tonic-gate 	ret = pool;
4227c478bd9Sstevel@tonic-gate 	pool = pool->nextfree;
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	memset(ret, 0, sizeof (*ret));
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	return (ret);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /* Should be called with verified arguments */
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate static ClusterInfo *
4327c478bd9Sstevel@tonic-gate cloneClusterInfo(int32_t clusterNum)
4337c478bd9Sstevel@tonic-gate {
4347c478bd9Sstevel@tonic-gate 	ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	if (cl->refcnt > 1) {
4377c478bd9Sstevel@tonic-gate 		ClusterInfo *newCl = newClusterInfo();
4387c478bd9Sstevel@tonic-gate 		cl->refcnt--;
4397c478bd9Sstevel@tonic-gate 		*newCl = *cl;
4407c478bd9Sstevel@tonic-gate 		newCl->refcnt = 1;
4417c478bd9Sstevel@tonic-gate 		if (newCl->path)
4427c478bd9Sstevel@tonic-gate 			newCl->path->references++;
4437c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER] = newCl;
4447c478bd9Sstevel@tonic-gate 	}
4457c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]);
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate static void
4497c478bd9Sstevel@tonic-gate updateFlags(int32_t clusterNum, int newflags)
4507c478bd9Sstevel@tonic-gate {
4517c478bd9Sstevel@tonic-gate 	ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	if (cl->flags != newflags && cl->refcnt > 1)
4547c478bd9Sstevel@tonic-gate 		cl = cloneClusterInfo(clusterNum);
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	cl->flags = newflags;
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate static void
4607c478bd9Sstevel@tonic-gate freeClusterInfo(ClusterInfo *old)
4617c478bd9Sstevel@tonic-gate {
4627c478bd9Sstevel@tonic-gate 	if (--old->refcnt <= 0) {
4637c478bd9Sstevel@tonic-gate 		if (old->path && --old->path->references <= 0) {
4647c478bd9Sstevel@tonic-gate 			free(old->path->fullName);
4657c478bd9Sstevel@tonic-gate 			free(old->path);
4667c478bd9Sstevel@tonic-gate 		}
4677c478bd9Sstevel@tonic-gate 		old->nextfree = pool;
4687c478bd9Sstevel@tonic-gate 		pool = old;
4697c478bd9Sstevel@tonic-gate 	}
4707c478bd9Sstevel@tonic-gate }
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate /*
4737c478bd9Sstevel@tonic-gate  * Allocate entries in our sparse array of cluster information.
4747c478bd9Sstevel@tonic-gate  * Returns non-zero if the structure already has been allocated
4757c478bd9Sstevel@tonic-gate  * (for those keeping score at home).
4767c478bd9Sstevel@tonic-gate  *
4777c478bd9Sstevel@tonic-gate  * The template parameter, if non-NULL, is used to facilitate sharing
4787c478bd9Sstevel@tonic-gate  * the ClusterInfo nodes for the clusters belonging to the same file.
4797c478bd9Sstevel@tonic-gate  * The first call to allocInUse for a new file should have *template
4807c478bd9Sstevel@tonic-gate  * set to 0; on return, *template then points to the newly allocated
4817c478bd9Sstevel@tonic-gate  * ClusterInfo.  Second and further calls keep the same value
4827c478bd9Sstevel@tonic-gate  * in *template and that ClusterInfo ndoe is then used for all
4837c478bd9Sstevel@tonic-gate  * entries in the file.  Code that modifies the ClusterInfo nodes
4847c478bd9Sstevel@tonic-gate  * should take care proper sharing semantics are maintained (i.e.,
4857c478bd9Sstevel@tonic-gate  * copy-on-write using cloneClusterInfo())
4867c478bd9Sstevel@tonic-gate  *
4877c478bd9Sstevel@tonic-gate  * The ClusterInfo used in the template is guaranted to be in use in
4887c478bd9Sstevel@tonic-gate  * at least one other cluster as we never return a value if we didn't
4897c478bd9Sstevel@tonic-gate  * set it first.  So we can overwrite it without the possibility of a leak.
4907c478bd9Sstevel@tonic-gate  */
4917c478bd9Sstevel@tonic-gate static int
4927c478bd9Sstevel@tonic-gate allocInUse(int32_t clusterNum, ClusterInfo **template)
4937c478bd9Sstevel@tonic-gate {
4947c478bd9Sstevel@tonic-gate 	ClusterInfo *newCl;
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
4977c478bd9Sstevel@tonic-gate 		return (CLINFO_PREVIOUSLY_ALLOCED);
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	if (template != NULL && *template != NULL)
5007c478bd9Sstevel@tonic-gate 		newCl = *template;
5017c478bd9Sstevel@tonic-gate 	else {
5027c478bd9Sstevel@tonic-gate 		newCl = newClusterInfo();
5037c478bd9Sstevel@tonic-gate 		if (template)
5047c478bd9Sstevel@tonic-gate 			*template = newCl;
5057c478bd9Sstevel@tonic-gate 	}
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	InUse[clusterNum - FIRST_CLUSTER] = newCl;
5087c478bd9Sstevel@tonic-gate 	newCl->refcnt++;
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	return (CLINFO_NEWLY_ALLOCED);
5117c478bd9Sstevel@tonic-gate }
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate static void
5147c478bd9Sstevel@tonic-gate markFree(int32_t clusterNum)
5157c478bd9Sstevel@tonic-gate {
5167c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5177c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5187c478bd9Sstevel@tonic-gate 		return;
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]) {
5217c478bd9Sstevel@tonic-gate 		if (InUse[clusterNum - FIRST_CLUSTER]->saved)
5227c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->saved);
5237c478bd9Sstevel@tonic-gate 		freeClusterInfo(InUse[clusterNum - FIRST_CLUSTER]);
5247c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER] = NULL;
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate static void
5297c478bd9Sstevel@tonic-gate markOrphan(int fd, int32_t clusterNum, struct pcdir *dp)
5307c478bd9Sstevel@tonic-gate {
5317c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5327c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5337c478bd9Sstevel@tonic-gate 		return;
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	(void) markInUse(fd, clusterNum, dp, NULL, 0, VISIBLE, NULL);
5367c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
5377c478bd9Sstevel@tonic-gate 		updateFlags(clusterNum,
5387c478bd9Sstevel@tonic-gate 		    InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_ORPHAN);
5397c478bd9Sstevel@tonic-gate }
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate static void
5427c478bd9Sstevel@tonic-gate markBad(int32_t clusterNum, uchar_t *recovered, int32_t recoveredLen)
5437c478bd9Sstevel@tonic-gate {
5447c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5457c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5467c478bd9Sstevel@tonic-gate 		return;
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	(void) allocInUse(clusterNum, NULL);
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	if (recoveredLen) {
5517c478bd9Sstevel@tonic-gate 		(void) cloneClusterInfo(clusterNum);
5527c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER]->saved = recovered;
5537c478bd9Sstevel@tonic-gate 	}
5547c478bd9Sstevel@tonic-gate 	updateFlags(clusterNum,
5557c478bd9Sstevel@tonic-gate 	    InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_BAD);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	BadClusterCount++;
5587c478bd9Sstevel@tonic-gate 	if (Verbose)
5597c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
5607c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d marked bad.\n"), clusterNum);
5617c478bd9Sstevel@tonic-gate }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate static void
5647c478bd9Sstevel@tonic-gate clearOrphan(int32_t c)
5657c478bd9Sstevel@tonic-gate {
5667c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5677c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
5687c478bd9Sstevel@tonic-gate 		return;
5697c478bd9Sstevel@tonic-gate 	if (InUse[c - FIRST_CLUSTER] != NULL)
5707c478bd9Sstevel@tonic-gate 		updateFlags(c,
5717c478bd9Sstevel@tonic-gate 		    InUse[c - FIRST_CLUSTER]->flags & ~CLINFO_ORPHAN);
5727c478bd9Sstevel@tonic-gate }
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate static void
5757c478bd9Sstevel@tonic-gate clearInUse(int32_t c)
5767c478bd9Sstevel@tonic-gate {
5777c478bd9Sstevel@tonic-gate 	ClusterInfo **clp;
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5807c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
5817c478bd9Sstevel@tonic-gate 		return;
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	clp = &InUse[c - FIRST_CLUSTER];
5847c478bd9Sstevel@tonic-gate 	if (*clp != NULL) {
5857c478bd9Sstevel@tonic-gate 		freeClusterInfo(*clp);
5867c478bd9Sstevel@tonic-gate 		*clp = NULL;
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate }
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate static void
5917c478bd9Sstevel@tonic-gate clearAllClusters_InUse()
5927c478bd9Sstevel@tonic-gate {
5937c478bd9Sstevel@tonic-gate 	int32_t cc;
5947c478bd9Sstevel@tonic-gate 	for (cc = FIRST_CLUSTER; cc < LastCluster; cc++) {
5957c478bd9Sstevel@tonic-gate 		clearInUse(cc);
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate static void
6007c478bd9Sstevel@tonic-gate makeUseTable(void)
6017c478bd9Sstevel@tonic-gate {
6027c478bd9Sstevel@tonic-gate 	if (InUse != NULL) {
6037c478bd9Sstevel@tonic-gate 		clearAllClusters_InUse();
6047c478bd9Sstevel@tonic-gate 		return;
6057c478bd9Sstevel@tonic-gate 	}
6067c478bd9Sstevel@tonic-gate 	if ((InUse = (ClusterInfo **)
6077c478bd9Sstevel@tonic-gate 	    calloc(TotalClusters, sizeof (ClusterInfo *))) == NULL) {
6087c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for internal table"));
6097c478bd9Sstevel@tonic-gate 		exit(9);
6107c478bd9Sstevel@tonic-gate 	}
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate static void
6147c478bd9Sstevel@tonic-gate countClusters(void)
6157c478bd9Sstevel@tonic-gate {
6167c478bd9Sstevel@tonic-gate 	int32_t c;
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	BadClusterCount = HiddenClusterCount =
6197c478bd9Sstevel@tonic-gate 	    AllocedClusterCount = FreeClusterCount = 0;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
6227c478bd9Sstevel@tonic-gate 		if (badInFAT(c)) {
6237c478bd9Sstevel@tonic-gate 			BadClusterCount++;
6247c478bd9Sstevel@tonic-gate 		} else if (isMarkedBad(c)) {
6257c478bd9Sstevel@tonic-gate 			/*
6267c478bd9Sstevel@tonic-gate 			 * This catches the bad sectors found
6277c478bd9Sstevel@tonic-gate 			 * during thorough verify that have never been
6287c478bd9Sstevel@tonic-gate 			 * allocated to a file.  Without this check, we
6297c478bd9Sstevel@tonic-gate 			 * count these guys as free.
6307c478bd9Sstevel@tonic-gate 			 */
6317c478bd9Sstevel@tonic-gate 			BadClusterCount++;
6327c478bd9Sstevel@tonic-gate 			markBadInFAT(c);
6337c478bd9Sstevel@tonic-gate 		} else if (isHidden(c)) {
6347c478bd9Sstevel@tonic-gate 			HiddenClusterCount++;
6357c478bd9Sstevel@tonic-gate 		} else if (isInUse(c)) {
6367c478bd9Sstevel@tonic-gate 			AllocedClusterCount++;
6377c478bd9Sstevel@tonic-gate 		} else {
6387c478bd9Sstevel@tonic-gate 			FreeClusterCount++;
6397c478bd9Sstevel@tonic-gate 		}
6407c478bd9Sstevel@tonic-gate 	}
6417c478bd9Sstevel@tonic-gate }
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate /*
6447c478bd9Sstevel@tonic-gate  * summarizeFAT
6457c478bd9Sstevel@tonic-gate  *	Mark orphans without directory entries as allocated.
6467c478bd9Sstevel@tonic-gate  *	XXX - these chains should be reclaimed!
6477c478bd9Sstevel@tonic-gate  *	XXX - merge this routine with countClusters (same loop, duh.)
6487c478bd9Sstevel@tonic-gate  */
6497c478bd9Sstevel@tonic-gate static void
6507c478bd9Sstevel@tonic-gate summarizeFAT(int fd)
6517c478bd9Sstevel@tonic-gate {
6527c478bd9Sstevel@tonic-gate 	int32_t c;
6537c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
6567c478bd9Sstevel@tonic-gate 		if (!freeInFAT(c) && !badInFAT(c) && !reservedInFAT(c) &&
6577c478bd9Sstevel@tonic-gate 		    !isInUse(c)) {
6587c478bd9Sstevel@tonic-gate 			(void) markInUse(fd, c, &BlankPCDIR, NULL, 0, VISIBLE,
6597c478bd9Sstevel@tonic-gate 				&tmpl);
6607c478bd9Sstevel@tonic-gate 		}
6617c478bd9Sstevel@tonic-gate 	}
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate static void
6657c478bd9Sstevel@tonic-gate getReadyToSearch(int fd)
6667c478bd9Sstevel@tonic-gate {
6677c478bd9Sstevel@tonic-gate 	getFAT(fd);
6687c478bd9Sstevel@tonic-gate 	if (!IsFAT32)
6697c478bd9Sstevel@tonic-gate 		getRootDirectory(fd);
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate static char PathName[MAXPATHLEN];
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate static void
6767c478bd9Sstevel@tonic-gate summarize(int fd, int includeFAT)
6777c478bd9Sstevel@tonic-gate {
6787c478bd9Sstevel@tonic-gate 	struct pcdir *ignorep1, *ignorep2 = NULL;
6797c478bd9Sstevel@tonic-gate 	int32_t ignore32;
6807c478bd9Sstevel@tonic-gate 	char ignore;
6817c478bd9Sstevel@tonic-gate 	int pathlen;
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	ReservedClusterCount = 0;
6847c478bd9Sstevel@tonic-gate 	AllocedClusterCount = 0;
6857c478bd9Sstevel@tonic-gate 	HiddenClusterCount = 0;
6867c478bd9Sstevel@tonic-gate 	FileClusterCount = 0;
6877c478bd9Sstevel@tonic-gate 	FreeClusterCount = 0;
6887c478bd9Sstevel@tonic-gate 	DirClusterCount = 0;
6897c478bd9Sstevel@tonic-gate 	BadClusterCount = 0;
6907c478bd9Sstevel@tonic-gate 	HiddenFileCount = 0;
6917c478bd9Sstevel@tonic-gate 	FileCount = 0;
6927c478bd9Sstevel@tonic-gate 	DirCount = 0;
6937c478bd9Sstevel@tonic-gate 	ignorep1 = ignorep2 = NULL;
6947c478bd9Sstevel@tonic-gate 	ignore = '\0';
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	PathName[0] = '\0';
6977c478bd9Sstevel@tonic-gate 	pathlen = 0;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	getReadyToSearch(fd);
7007c478bd9Sstevel@tonic-gate 	/*
7017c478bd9Sstevel@tonic-gate 	 *  Traverse the full meta-data tree to talley what clusters
7027c478bd9Sstevel@tonic-gate 	 * are in use.  The root directory is an area outside of the
7037c478bd9Sstevel@tonic-gate 	 * file space on FAT12 and FAT16 file systems.  On FAT32 file
7047c478bd9Sstevel@tonic-gate 	 * systems, the root directory is in a file area cluster just
7057c478bd9Sstevel@tonic-gate 	 * like any other directory.
7067c478bd9Sstevel@tonic-gate 	 */
7077c478bd9Sstevel@tonic-gate 	if (!IsFAT32) {
7087c478bd9Sstevel@tonic-gate 		traverseFromRoot(fd, 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL,
7097c478bd9Sstevel@tonic-gate 		    ignore, &ignorep1, &ignore32, &ignorep2, PathName,
7107c478bd9Sstevel@tonic-gate 		    &pathlen);
7117c478bd9Sstevel@tonic-gate 	} else {
7127c478bd9Sstevel@tonic-gate 		DirCount++;
7137c478bd9Sstevel@tonic-gate 		traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7147c478bd9Sstevel@tonic-gate 		    0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL, ignore,
7157c478bd9Sstevel@tonic-gate 		    &ignorep1, &ignore32, &ignorep2, PathName, &pathlen);
7167c478bd9Sstevel@tonic-gate 	}
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	if (includeFAT)
7197c478bd9Sstevel@tonic-gate 		summarizeFAT(fd);
7207c478bd9Sstevel@tonic-gate 	countClusters();
7217c478bd9Sstevel@tonic-gate }
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate int
7247c478bd9Sstevel@tonic-gate isMarkedBad(int32_t clusterNum)
7257c478bd9Sstevel@tonic-gate {
7267c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
7277c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
7287c478bd9Sstevel@tonic-gate 		return (0);
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
7317c478bd9Sstevel@tonic-gate 		return (0);
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_BAD);
7347c478bd9Sstevel@tonic-gate }
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate static int
7377c478bd9Sstevel@tonic-gate isMarkedOrphan(int32_t clusterNum)
7387c478bd9Sstevel@tonic-gate {
7397c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
7407c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
7417c478bd9Sstevel@tonic-gate 		return (0);
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
7447c478bd9Sstevel@tonic-gate 		return (0);
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_ORPHAN);
7477c478bd9Sstevel@tonic-gate }
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate static void
7507c478bd9Sstevel@tonic-gate orphanChain(int fd, int32_t c, struct pcdir *ndp)
7517c478bd9Sstevel@tonic-gate {
7527c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
7557c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
7567c478bd9Sstevel@tonic-gate 		return;
7577c478bd9Sstevel@tonic-gate 	clearInUse(c);
7587c478bd9Sstevel@tonic-gate 	markOrphan(fd, c, ndp);
7597c478bd9Sstevel@tonic-gate 	c = nextInChain(c);
7607c478bd9Sstevel@tonic-gate 	while (c != 0) {
7617c478bd9Sstevel@tonic-gate 		clearInUse(c);
7627c478bd9Sstevel@tonic-gate 		clearOrphan(c);
7637c478bd9Sstevel@tonic-gate 		(void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, &tmpl);
7647c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
7657c478bd9Sstevel@tonic-gate 	}
7667c478bd9Sstevel@tonic-gate }
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate static int32_t
7697c478bd9Sstevel@tonic-gate findAFreeCluster(int32_t startAt)
7707c478bd9Sstevel@tonic-gate {
7717c478bd9Sstevel@tonic-gate 	int32_t look = startAt;
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	for (;;) {
7747c478bd9Sstevel@tonic-gate 		if (freeInFAT(look)) {
7757c478bd9Sstevel@tonic-gate 			break;
7767c478bd9Sstevel@tonic-gate 		}
7777c478bd9Sstevel@tonic-gate 		if (look == LastCluster)
7787c478bd9Sstevel@tonic-gate 			look = FIRST_CLUSTER;
7797c478bd9Sstevel@tonic-gate 		else
7807c478bd9Sstevel@tonic-gate 			look++;
7817c478bd9Sstevel@tonic-gate 		if (look == startAt)
7827c478bd9Sstevel@tonic-gate 			break;
7837c478bd9Sstevel@tonic-gate 	}
7847c478bd9Sstevel@tonic-gate 	if (look != startAt)
7857c478bd9Sstevel@tonic-gate 		return (look);
7867c478bd9Sstevel@tonic-gate 	else
7877c478bd9Sstevel@tonic-gate 		return (0);
7887c478bd9Sstevel@tonic-gate }
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate static void
7917c478bd9Sstevel@tonic-gate setEndOfDirectory(struct pcdir *dp)
7927c478bd9Sstevel@tonic-gate {
7937c478bd9Sstevel@tonic-gate 	dp->pcd_filename[0] = PCD_UNUSED;
7947c478bd9Sstevel@tonic-gate }
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate static void
7977c478bd9Sstevel@tonic-gate emergencyEndOfDirectory(int fd, int32_t secondToLast)
7987c478bd9Sstevel@tonic-gate {
7997c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
8007c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	if (readCluster(fd, secondToLast, &(dirdata.bytes), &dirdatasize,
8037c478bd9Sstevel@tonic-gate 	    RDCLUST_DO_CACHE) != RDCLUST_GOOD) {
8047c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8057c478bd9Sstevel@tonic-gate 		    gettext("Unable to read allocation unit %d.\n"),
8067c478bd9Sstevel@tonic-gate 		    secondToLast);
8077c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8087c478bd9Sstevel@tonic-gate 		    gettext("Cannot allocate a new allocation unit to hold an"
8097c478bd9Sstevel@tonic-gate 		    " end-of-directory marker.\nCannot access allocation unit"
8107c478bd9Sstevel@tonic-gate 		    " to overwrite existing directory entry with\nthe marker."
8117c478bd9Sstevel@tonic-gate 		    "  Needed directory truncation has failed.  Giving up.\n"));
8127c478bd9Sstevel@tonic-gate 		(void) close(fd);
8137c478bd9Sstevel@tonic-gate 		exit(11);
8147c478bd9Sstevel@tonic-gate 	}
8157c478bd9Sstevel@tonic-gate 	setEndOfDirectory(dirdata.dirp);
8167c478bd9Sstevel@tonic-gate 	markClusterModified(secondToLast);
8177c478bd9Sstevel@tonic-gate }
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate static void
8207c478bd9Sstevel@tonic-gate makeNewEndOfDirectory(struct pcdir *entry, int32_t secondToLast,
8217c478bd9Sstevel@tonic-gate     int32_t newCluster, ClusterContents *newData)
8227c478bd9Sstevel@tonic-gate {
8237c478bd9Sstevel@tonic-gate 	setEndOfDirectory(newData->dirp);
8247c478bd9Sstevel@tonic-gate 	markClusterModified(newCluster);
8257c478bd9Sstevel@tonic-gate 	/*
8267c478bd9Sstevel@tonic-gate 	 *  There are two scenarios.  One is that we truncated the
8277c478bd9Sstevel@tonic-gate 	 *  directory in the very beginning.  The other is that we
8287c478bd9Sstevel@tonic-gate 	 *  truncated it in the middle or at the end.  In the first
8297c478bd9Sstevel@tonic-gate 	 *  scenario, the secondToLast argument is not a valid cluster
8307c478bd9Sstevel@tonic-gate 	 *  (it's zero), and so we actually need to change the start
8317c478bd9Sstevel@tonic-gate 	 *  cluster for the directory to this new start cluster.  In
8327c478bd9Sstevel@tonic-gate 	 *  the second scenario, the secondToLast cluster we received
8337c478bd9Sstevel@tonic-gate 	 *  as an argument needs to be pointed at the new end of
8347c478bd9Sstevel@tonic-gate 	 *  directory.
8357c478bd9Sstevel@tonic-gate 	 */
8367c478bd9Sstevel@tonic-gate 	if (secondToLast == 0) {
8377c478bd9Sstevel@tonic-gate 		updateDirEnt_Start(entry, newCluster);
8387c478bd9Sstevel@tonic-gate 	} else {
8397c478bd9Sstevel@tonic-gate 		writeFATEntry(secondToLast, newCluster);
8407c478bd9Sstevel@tonic-gate 	}
8417c478bd9Sstevel@tonic-gate 	markLastInFAT(newCluster);
8427c478bd9Sstevel@tonic-gate }
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate static void
8457c478bd9Sstevel@tonic-gate createNewEndOfDirectory(int fd, struct pcdir *entry, int32_t secondToLast)
8467c478bd9Sstevel@tonic-gate {
8477c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
8487c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
8497c478bd9Sstevel@tonic-gate 	int32_t freeCluster;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	if (((freeCluster = findAFreeCluster(secondToLast)) != 0)) {
8527c478bd9Sstevel@tonic-gate 		if (readCluster(fd, freeCluster, &(dirdata.bytes),
8537c478bd9Sstevel@tonic-gate 		    &dirdatasize, RDCLUST_DO_CACHE) == RDCLUST_GOOD) {
8547c478bd9Sstevel@tonic-gate 			if (Verbose) {
8557c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
8567c478bd9Sstevel@tonic-gate 				    gettext("Grabbed allocation unit #%d "
8577c478bd9Sstevel@tonic-gate 				    "for truncated\ndirectory's new end "
8587c478bd9Sstevel@tonic-gate 				    "of directory.\n"), freeCluster);
8597c478bd9Sstevel@tonic-gate 			}
8607c478bd9Sstevel@tonic-gate 			makeNewEndOfDirectory(entry, secondToLast,
8617c478bd9Sstevel@tonic-gate 			    freeCluster, &dirdata);
8627c478bd9Sstevel@tonic-gate 			return;
8637c478bd9Sstevel@tonic-gate 		}
8647c478bd9Sstevel@tonic-gate 	}
8657c478bd9Sstevel@tonic-gate 	if (secondToLast == 0) {
8667c478bd9Sstevel@tonic-gate 		if (freeCluster == 0) {
8677c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("File system full.\n"));
8687c478bd9Sstevel@tonic-gate 		} else {
8697c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
8707c478bd9Sstevel@tonic-gate 			    gettext("Unable to read allocation unit %d.\n"),
8717c478bd9Sstevel@tonic-gate 			    freeCluster);
8727c478bd9Sstevel@tonic-gate 		}
8737c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8747c478bd9Sstevel@tonic-gate 		    gettext("Cannot allocate a new allocation unit to hold "
8757c478bd9Sstevel@tonic-gate 		    "an end-of-directory marker.\nNo existing directory "
8767c478bd9Sstevel@tonic-gate 		    "entries can be overwritten with the marker,\n"
8777c478bd9Sstevel@tonic-gate 		    "the only unit allocated to the directory is "
8787c478bd9Sstevel@tonic-gate 		    "inaccessible.\nNeeded directory truncation has failed.  "
8797c478bd9Sstevel@tonic-gate 		    "Giving up.\n"));
8807c478bd9Sstevel@tonic-gate 		(void) close(fd);
8817c478bd9Sstevel@tonic-gate 		exit(11);
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 	emergencyEndOfDirectory(fd, secondToLast);
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate /*
8877c478bd9Sstevel@tonic-gate  * truncAtCluster
8887c478bd9Sstevel@tonic-gate  *	Given a directory entry and a cluster number, search through
8897c478bd9Sstevel@tonic-gate  *	the cluster chain for the entry and make the cluster previous
8907c478bd9Sstevel@tonic-gate  *	to the given cluster in the chain the last cluster in the file.
8917c478bd9Sstevel@tonic-gate  *	The number of orphaned bytes is returned.  For a chain that's
8927c478bd9Sstevel@tonic-gate  *	a directory we need to do some special handling, since we'll be
8937c478bd9Sstevel@tonic-gate  *	getting rid of the end of directory notice by truncating.
8947c478bd9Sstevel@tonic-gate  */
8957c478bd9Sstevel@tonic-gate static int64_t
8967c478bd9Sstevel@tonic-gate truncAtCluster(int fd, struct pcdir *entry, int32_t cluster)
8977c478bd9Sstevel@tonic-gate {
8987c478bd9Sstevel@tonic-gate 	uint32_t oldSize, newSize;
8997c478bd9Sstevel@tonic-gate 	int32_t prev, count, follow;
9007c478bd9Sstevel@tonic-gate 	int dir = (entry->pcd_attr & PCA_DIR);
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	prev = 0; count = 0;
9037c478bd9Sstevel@tonic-gate 	follow = extractStartCluster(entry);
9047c478bd9Sstevel@tonic-gate 	while (follow != cluster && follow >= FIRST_CLUSTER &&
9057c478bd9Sstevel@tonic-gate 	    follow <= LastCluster) {
9067c478bd9Sstevel@tonic-gate 		prev = follow;
9077c478bd9Sstevel@tonic-gate 		count++;
9087c478bd9Sstevel@tonic-gate 		follow = nextInChain(follow);
9097c478bd9Sstevel@tonic-gate 	}
9107c478bd9Sstevel@tonic-gate 	if (follow != cluster) {
9117c478bd9Sstevel@tonic-gate 		/*
9127c478bd9Sstevel@tonic-gate 		 *  We didn't find the cluster they wanted to trunc at
9137c478bd9Sstevel@tonic-gate 		 *  anywhere in the entry's chain.  So we'll leave the
9147c478bd9Sstevel@tonic-gate 		 *  entry alone, and return a negative value so they
9157c478bd9Sstevel@tonic-gate 		 *  can know something is wrong.
9167c478bd9Sstevel@tonic-gate 		 */
9177c478bd9Sstevel@tonic-gate 		return (-1);
9187c478bd9Sstevel@tonic-gate 	}
9197c478bd9Sstevel@tonic-gate 	if (Verbose) {
9207c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9217c478bd9Sstevel@tonic-gate 		    gettext("Chain truncation at unit #%d\n"), cluster);
9227c478bd9Sstevel@tonic-gate 	}
9237c478bd9Sstevel@tonic-gate 	if (!dir) {
9247c478bd9Sstevel@tonic-gate 		oldSize = extractSize(entry);
9257c478bd9Sstevel@tonic-gate 		newSize = count *
9267c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.sectors_per_cluster *
9277c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
9287c478bd9Sstevel@tonic-gate 		if (newSize == 0)
9297c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(entry, 0);
9307c478bd9Sstevel@tonic-gate 	} else {
9317c478bd9Sstevel@tonic-gate 		newSize = 0;
9327c478bd9Sstevel@tonic-gate 	}
9337c478bd9Sstevel@tonic-gate 	updateDirEnt_Size(entry, newSize);
9347c478bd9Sstevel@tonic-gate 	if (dir) {
9357c478bd9Sstevel@tonic-gate 		createNewEndOfDirectory(fd, entry, prev);
9367c478bd9Sstevel@tonic-gate 	} else if (prev != 0) {
9377c478bd9Sstevel@tonic-gate 		markLastInFAT(prev);
9387c478bd9Sstevel@tonic-gate 	}
9397c478bd9Sstevel@tonic-gate 	if (dir) {
9407c478bd9Sstevel@tonic-gate 		/*
9417c478bd9Sstevel@tonic-gate 		 * We don't really know what the size of a directory is
9427c478bd9Sstevel@tonic-gate 		 * but it is important for us to know if this truncation
9437c478bd9Sstevel@tonic-gate 		 * results in an orphan with any size.  The value we
9447c478bd9Sstevel@tonic-gate 		 * return from this routine for a normal file is the
9457c478bd9Sstevel@tonic-gate 		 * number of bytes left in the chain.  For a directory
9467c478bd9Sstevel@tonic-gate 		 * we can't be exact, and the caller doesn't really
9477c478bd9Sstevel@tonic-gate 		 * expect us to be.  For a directory the caller only
9487c478bd9Sstevel@tonic-gate 		 * cares if there are zero bytes left or more than
9497c478bd9Sstevel@tonic-gate 		 * zero bytes left.  We'll return 1 to indicate
9507c478bd9Sstevel@tonic-gate 		 * more than zero.
9517c478bd9Sstevel@tonic-gate 		 */
9527c478bd9Sstevel@tonic-gate 		if ((follow = nextInChain(follow)) != 0)
9537c478bd9Sstevel@tonic-gate 			return (1);
9547c478bd9Sstevel@tonic-gate 		else
9557c478bd9Sstevel@tonic-gate 			return (0);
9567c478bd9Sstevel@tonic-gate 	}
9577c478bd9Sstevel@tonic-gate 	/*
9587c478bd9Sstevel@tonic-gate 	 * newSize should always be smaller than the old one, since
9597c478bd9Sstevel@tonic-gate 	 * we are decreasing the number of clusters allocated to the file.
9607c478bd9Sstevel@tonic-gate 	 */
9617c478bd9Sstevel@tonic-gate 	return ((int64_t)oldSize - (int64_t)newSize);
9627c478bd9Sstevel@tonic-gate }
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate static struct pcdir *
9657c478bd9Sstevel@tonic-gate updateOrphanedChainMetadata(int fd, struct pcdir *dp, int32_t endCluster,
9667c478bd9Sstevel@tonic-gate     int isBad)
9677c478bd9Sstevel@tonic-gate {
9687c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
9697c478bd9Sstevel@tonic-gate 	int64_t remainder;
9707c478bd9Sstevel@tonic-gate 	char *newName = NULL;
9717c478bd9Sstevel@tonic-gate 	int chosenName;
9727c478bd9Sstevel@tonic-gate 	int dir = (dp->pcd_attr & PCA_DIR);
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	/*
9757c478bd9Sstevel@tonic-gate 	 *  If the truncation fails, (which ought not to happen),
9767c478bd9Sstevel@tonic-gate 	 *  there's no need to go any further, we just return
9777c478bd9Sstevel@tonic-gate 	 *  a null value for the new directory entry pointer.
9787c478bd9Sstevel@tonic-gate 	 */
9797c478bd9Sstevel@tonic-gate 	remainder = truncAtCluster(fd, dp, endCluster);
9807c478bd9Sstevel@tonic-gate 	if (remainder < 0)
9817c478bd9Sstevel@tonic-gate 		return (ndp);
9827c478bd9Sstevel@tonic-gate 	if (!dir && isBad) {
9837c478bd9Sstevel@tonic-gate 		/*
9847c478bd9Sstevel@tonic-gate 		 *  Subtract out the bad cluster from the remaining size
9857c478bd9Sstevel@tonic-gate 		 *  We always assume the cluster being deleted from the
9867c478bd9Sstevel@tonic-gate 		 *  file is full size, but that might not be the case
9877c478bd9Sstevel@tonic-gate 		 *  for the last cluster of the file, so that is why
9887c478bd9Sstevel@tonic-gate 		 *  we check for negative remainder value.
9897c478bd9Sstevel@tonic-gate 		 */
9907c478bd9Sstevel@tonic-gate 		remainder -= TheBIOSParameterBlock.bpb.sectors_per_cluster *
9917c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
9927c478bd9Sstevel@tonic-gate 		if (remainder < 0)
9937c478bd9Sstevel@tonic-gate 			remainder = 0;
9947c478bd9Sstevel@tonic-gate 	}
9957c478bd9Sstevel@tonic-gate 	/*
9967c478bd9Sstevel@tonic-gate 	 * Build a new directory entry for the rest of the chain.
9977c478bd9Sstevel@tonic-gate 	 * Later, if the user okays it, we'll link this entry into the
9987c478bd9Sstevel@tonic-gate 	 * root directory.  The new entry will start out as a
9997c478bd9Sstevel@tonic-gate 	 * copy of the truncated entry.
10007c478bd9Sstevel@tonic-gate 	 */
10017c478bd9Sstevel@tonic-gate 	if ((remainder != 0) &&
10027c478bd9Sstevel@tonic-gate 	    ((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
10037c478bd9Sstevel@tonic-gate 	    ((ndp = newDirEnt(dp)) != NULL)) {
10047c478bd9Sstevel@tonic-gate 		if (Verbose) {
10057c478bd9Sstevel@tonic-gate 			if (dir)
10067c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
10077c478bd9Sstevel@tonic-gate 				    gettext("Orphaned directory chain.\n"));
10087c478bd9Sstevel@tonic-gate 			else
10097c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
10107c478bd9Sstevel@tonic-gate 				    gettext("Orphaned chain, %u bytes.\n"),
10117c478bd9Sstevel@tonic-gate 				    (uint32_t)remainder);
10127c478bd9Sstevel@tonic-gate 		}
10137c478bd9Sstevel@tonic-gate 		if (!dir)
10147c478bd9Sstevel@tonic-gate 			updateDirEnt_Size(ndp, (uint32_t)remainder);
10157c478bd9Sstevel@tonic-gate 		if (isBad)
10167c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, nextInChain(endCluster));
10177c478bd9Sstevel@tonic-gate 		else
10187c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, endCluster);
10197c478bd9Sstevel@tonic-gate 		updateDirEnt_Name(ndp, newName);
10207c478bd9Sstevel@tonic-gate 		addEntryToCHKList(chosenName);
10217c478bd9Sstevel@tonic-gate 	}
10227c478bd9Sstevel@tonic-gate 	return (ndp);
10237c478bd9Sstevel@tonic-gate }
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate /*
10267c478bd9Sstevel@tonic-gate  *  splitChain()
10277c478bd9Sstevel@tonic-gate  *
10287c478bd9Sstevel@tonic-gate  *	split a cluster allocation chain into two cluster chains
10297c478bd9Sstevel@tonic-gate  *	around a given cluster (problemCluster).  This results in two
10307c478bd9Sstevel@tonic-gate  *	separate directory entries; the original (dp), and one we hope
10317c478bd9Sstevel@tonic-gate  *	to create and return a pointer to to the caller (*newdp).
10327c478bd9Sstevel@tonic-gate  *	This second entry is the orphan chain, and it may end up in
10337c478bd9Sstevel@tonic-gate  *	the root directory as a FILEnnnn.CHK file.  We also return the
10347c478bd9Sstevel@tonic-gate  *	starting cluster of the orphan chain to the caller (*orphanStart).
10357c478bd9Sstevel@tonic-gate  */
10367c478bd9Sstevel@tonic-gate void
10377c478bd9Sstevel@tonic-gate splitChain(int fd, struct pcdir *dp, int32_t problemCluster,
10387c478bd9Sstevel@tonic-gate     struct pcdir **newdp, int32_t *orphanStart)
10397c478bd9Sstevel@tonic-gate {
10407c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
10417c478bd9Sstevel@tonic-gate 	int isBad = isMarkedBad(problemCluster);
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	ndp = updateOrphanedChainMetadata(fd, dp, problemCluster, isBad);
10447c478bd9Sstevel@tonic-gate 	*newdp = ndp;
10457c478bd9Sstevel@tonic-gate 	clearInUse(problemCluster);
10467c478bd9Sstevel@tonic-gate 	if (isBad) {
10477c478bd9Sstevel@tonic-gate 		clearOrphan(problemCluster);
10487c478bd9Sstevel@tonic-gate 		*orphanStart = nextInChain(problemCluster);
10497c478bd9Sstevel@tonic-gate 		orphanChain(fd, *orphanStart, ndp);
10507c478bd9Sstevel@tonic-gate 		markBadInFAT(problemCluster);
10517c478bd9Sstevel@tonic-gate 	} else {
10527c478bd9Sstevel@tonic-gate 		*orphanStart = problemCluster;
10537c478bd9Sstevel@tonic-gate 		orphanChain(fd, problemCluster, ndp);
10547c478bd9Sstevel@tonic-gate 	}
10557c478bd9Sstevel@tonic-gate }
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate /*
10587c478bd9Sstevel@tonic-gate  *  freeOrphan
10597c478bd9Sstevel@tonic-gate  *
10607c478bd9Sstevel@tonic-gate  *  User has requested that an orphaned cluster chain be freed back
10617c478bd9Sstevel@tonic-gate  *  into the file area.
10627c478bd9Sstevel@tonic-gate  */
10637c478bd9Sstevel@tonic-gate static void
10647c478bd9Sstevel@tonic-gate freeOrphan(int32_t c)
10657c478bd9Sstevel@tonic-gate {
10667c478bd9Sstevel@tonic-gate 	int32_t n;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	/*
10697c478bd9Sstevel@tonic-gate 	 * Free the directory entry we explicitly created for
10707c478bd9Sstevel@tonic-gate 	 * the orphaned clusters.
10717c478bd9Sstevel@tonic-gate 	 */
10727c478bd9Sstevel@tonic-gate 	if (InUse[c - FIRST_CLUSTER]->dirent != NULL)
10737c478bd9Sstevel@tonic-gate 		free(InUse[c - FIRST_CLUSTER]->dirent);
10747c478bd9Sstevel@tonic-gate 	/*
10757c478bd9Sstevel@tonic-gate 	 * Then mark the clusters themselves as available.
10767c478bd9Sstevel@tonic-gate 	 */
10777c478bd9Sstevel@tonic-gate 	do {
10787c478bd9Sstevel@tonic-gate 		n = nextInChain(c);
10797c478bd9Sstevel@tonic-gate 		markFreeInFAT(c);
10807c478bd9Sstevel@tonic-gate 		markFree(c);
10817c478bd9Sstevel@tonic-gate 		c = n;
10827c478bd9Sstevel@tonic-gate 	} while (c != 0);
10837c478bd9Sstevel@tonic-gate }
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate /*
10867c478bd9Sstevel@tonic-gate  *  Rewrite the InUse field for a cluster chain.  Can be used on a partial
10877c478bd9Sstevel@tonic-gate  *  chain if provided with a stopAtCluster.
10887c478bd9Sstevel@tonic-gate  */
10897c478bd9Sstevel@tonic-gate static void
10907c478bd9Sstevel@tonic-gate redoInUse(int fd, int32_t c, struct pcdir *ndp, int32_t stopAtCluster)
10917c478bd9Sstevel@tonic-gate {
10927c478bd9Sstevel@tonic-gate 	while (c && c != stopAtCluster) {
10937c478bd9Sstevel@tonic-gate 		clearInUse(c);
10947c478bd9Sstevel@tonic-gate 		(void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, NULL);
10957c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
10967c478bd9Sstevel@tonic-gate 	}
10977c478bd9Sstevel@tonic-gate }
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate static struct pcdir *
11007c478bd9Sstevel@tonic-gate orphanDirEntLookup(int32_t clusterNum)
11017c478bd9Sstevel@tonic-gate {
11027c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
11037c478bd9Sstevel@tonic-gate 		return (NULL);
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 	if (isInUse(clusterNum)) {
11067c478bd9Sstevel@tonic-gate 		return (InUse[clusterNum - FIRST_CLUSTER]->dirent);
11077c478bd9Sstevel@tonic-gate 	} else {
11087c478bd9Sstevel@tonic-gate 		return (NULL);
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate }
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate static int32_t
11137c478bd9Sstevel@tonic-gate orphanSizeLookup(int32_t clusterNum)
11147c478bd9Sstevel@tonic-gate {
11157c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
11167c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
11177c478bd9Sstevel@tonic-gate 		return (-1);
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	if (isInUse(clusterNum)) {
11207c478bd9Sstevel@tonic-gate 		return (extractSize(InUse[clusterNum - FIRST_CLUSTER]->dirent));
11217c478bd9Sstevel@tonic-gate 	} else {
11227c478bd9Sstevel@tonic-gate 		return (-1);
11237c478bd9Sstevel@tonic-gate 	}
11247c478bd9Sstevel@tonic-gate }
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate /*
11277c478bd9Sstevel@tonic-gate  *  linkOrphan
11287c478bd9Sstevel@tonic-gate  *
11297c478bd9Sstevel@tonic-gate  *  User has requested that an orphaned cluster chain be brought back
11307c478bd9Sstevel@tonic-gate  *  into the file system.  So we have to make a new directory entry
11317c478bd9Sstevel@tonic-gate  *  in the root directory and point it at the cluster chain.
11327c478bd9Sstevel@tonic-gate  */
11337c478bd9Sstevel@tonic-gate static void
11347c478bd9Sstevel@tonic-gate linkOrphan(int fd, int32_t start)
11357c478bd9Sstevel@tonic-gate {
11367c478bd9Sstevel@tonic-gate 	struct pcdir *newEnt = NULL;
11377c478bd9Sstevel@tonic-gate 	struct pcdir *dp;
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 	if ((dp = orphanDirEntLookup(start)) != NULL) {
11407c478bd9Sstevel@tonic-gate 		newEnt = addRootDirEnt(fd, dp);
11417c478bd9Sstevel@tonic-gate 	} else {
11427c478bd9Sstevel@tonic-gate 		(void) printf(gettext("Re-link of orphaned chain failed."
11437c478bd9Sstevel@tonic-gate 		    "  Allocation units will remain orphaned.\n"));
11447c478bd9Sstevel@tonic-gate 	}
11457c478bd9Sstevel@tonic-gate 	/*
11467c478bd9Sstevel@tonic-gate 	 *  A cluster isn't really InUse() unless it is referenced,
11477c478bd9Sstevel@tonic-gate 	 *  so if newEnt is NULL here, we are in effect using markInUse()
11487c478bd9Sstevel@tonic-gate 	 *  to note that the cluster is NOT in use.
11497c478bd9Sstevel@tonic-gate 	 */
11507c478bd9Sstevel@tonic-gate 	redoInUse(fd, start, newEnt, 0);
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate /*
11547c478bd9Sstevel@tonic-gate  *  relinkCreatedOrphans
11557c478bd9Sstevel@tonic-gate  *
11567c478bd9Sstevel@tonic-gate  *  While marking clusters as bad, we can create orphan cluster
11577c478bd9Sstevel@tonic-gate  *  chains.  Since we were the ones doing the marking, we were able to
11587c478bd9Sstevel@tonic-gate  *  keep track of the orphans we created.  Now we want to go through
11597c478bd9Sstevel@tonic-gate  *  all those chains and either get them back into the file system or
11607c478bd9Sstevel@tonic-gate  *  free them depending on the user's input.
11617c478bd9Sstevel@tonic-gate  */
11627c478bd9Sstevel@tonic-gate static void
11637c478bd9Sstevel@tonic-gate relinkCreatedOrphans(int fd)
11647c478bd9Sstevel@tonic-gate {
11657c478bd9Sstevel@tonic-gate 	int32_t c;
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
11687c478bd9Sstevel@tonic-gate 		if (isMarkedOrphan(c)) {
11697c478bd9Sstevel@tonic-gate 			if (OkayToRelink && askAboutRelink(c)) {
11707c478bd9Sstevel@tonic-gate 				linkOrphan(fd, c);
11717c478bd9Sstevel@tonic-gate 			} else if (askAboutFreeing(c)) {
11727c478bd9Sstevel@tonic-gate 				freeOrphan(c);
11737c478bd9Sstevel@tonic-gate 			}
11747c478bd9Sstevel@tonic-gate 			clearOrphan(c);
11757c478bd9Sstevel@tonic-gate 		}
11767c478bd9Sstevel@tonic-gate 	}
11777c478bd9Sstevel@tonic-gate }
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate /*
11807c478bd9Sstevel@tonic-gate  *  relinkFATOrphans
11817c478bd9Sstevel@tonic-gate  *
11827c478bd9Sstevel@tonic-gate  *  We want to find orphans not represented in the meta-data.
11837c478bd9Sstevel@tonic-gate  *  These are chains marked in the FAT as being in use but
11847c478bd9Sstevel@tonic-gate  *  not referenced anywhere by any directory entries.
11857c478bd9Sstevel@tonic-gate  *  We'll go through the whole FAT and mark the first cluster
11867c478bd9Sstevel@tonic-gate  *  in any such chain as an orphan.  Then we can just use
11877c478bd9Sstevel@tonic-gate  *  the relinkCreatedOrphans routine to get them back into the
11887c478bd9Sstevel@tonic-gate  *  file system or free'ed depending on the user's input.
11897c478bd9Sstevel@tonic-gate  */
11907c478bd9Sstevel@tonic-gate static void
11917c478bd9Sstevel@tonic-gate relinkFATOrphans(int fd)
11927c478bd9Sstevel@tonic-gate {
11937c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
11947c478bd9Sstevel@tonic-gate 	int32_t cc, c, n;
11957c478bd9Sstevel@tonic-gate 	int32_t bpc, newSize;
11967c478bd9Sstevel@tonic-gate 	char *newName;
11977c478bd9Sstevel@tonic-gate 	int chosenName;
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
12007c478bd9Sstevel@tonic-gate 		if (freeInFAT(c) || badInFAT(c) ||
12017c478bd9Sstevel@tonic-gate 		    reservedInFAT(c) || isInUse(c))
12027c478bd9Sstevel@tonic-gate 			continue;
12037c478bd9Sstevel@tonic-gate 		cc = 1;
12047c478bd9Sstevel@tonic-gate 		n = c;
12057c478bd9Sstevel@tonic-gate 		while (n = nextInChain(n))
12067c478bd9Sstevel@tonic-gate 			cc++;
12077c478bd9Sstevel@tonic-gate 		bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster *
12087c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
12097c478bd9Sstevel@tonic-gate 		newSize = cc * bpc;
12107c478bd9Sstevel@tonic-gate 		if (((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
12117c478bd9Sstevel@tonic-gate 		    ((ndp = newDirEnt(NULL)) != NULL)) {
12127c478bd9Sstevel@tonic-gate 			updateDirEnt_Size(ndp, newSize);
12137c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, c);
12147c478bd9Sstevel@tonic-gate 			updateDirEnt_Name(ndp, newName);
12157c478bd9Sstevel@tonic-gate 			addEntryToCHKList(chosenName);
12167c478bd9Sstevel@tonic-gate 		}
12177c478bd9Sstevel@tonic-gate 		orphanChain(fd, c, ndp);
12187c478bd9Sstevel@tonic-gate 	}
12197c478bd9Sstevel@tonic-gate 	relinkCreatedOrphans(fd);
12207c478bd9Sstevel@tonic-gate }
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate static void
12237c478bd9Sstevel@tonic-gate relinkOrphans(int fd)
12247c478bd9Sstevel@tonic-gate {
12257c478bd9Sstevel@tonic-gate 	relinkCreatedOrphans(fd);
12267c478bd9Sstevel@tonic-gate 	relinkFATOrphans(fd);
12277c478bd9Sstevel@tonic-gate }
12287c478bd9Sstevel@tonic-gate 
12297c478bd9Sstevel@tonic-gate static void
12307c478bd9Sstevel@tonic-gate checkForFATLoop(int32_t clusterNum)
12317c478bd9Sstevel@tonic-gate {
12327c478bd9Sstevel@tonic-gate 	int32_t prev = clusterNum;
12337c478bd9Sstevel@tonic-gate 	int32_t follow;
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
12367c478bd9Sstevel@tonic-gate 		return;
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 	follow = nextInChain(clusterNum);
12397c478bd9Sstevel@tonic-gate 	while (follow != clusterNum && follow >= FIRST_CLUSTER &&
12407c478bd9Sstevel@tonic-gate 	    follow <= LastCluster) {
12417c478bd9Sstevel@tonic-gate 		prev = follow;
12427c478bd9Sstevel@tonic-gate 		follow = nextInChain(follow);
12437c478bd9Sstevel@tonic-gate 	}
12447c478bd9Sstevel@tonic-gate 	if (follow == clusterNum) {
12457c478bd9Sstevel@tonic-gate 		/*
12467c478bd9Sstevel@tonic-gate 		 * We found a loop.  Eradicate it by changing
12477c478bd9Sstevel@tonic-gate 		 * the last cluster in the loop to be last
12487c478bd9Sstevel@tonic-gate 		 * in the chain instead instead of pointing
12497c478bd9Sstevel@tonic-gate 		 * back to the first cluster.
12507c478bd9Sstevel@tonic-gate 		 */
12517c478bd9Sstevel@tonic-gate 		markLastInFAT(prev);
12527c478bd9Sstevel@tonic-gate 	}
12537c478bd9Sstevel@tonic-gate }
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate static void
12567c478bd9Sstevel@tonic-gate sharedChainError(int fd, int32_t clusterNum, struct pcdir *badEntry)
12577c478bd9Sstevel@tonic-gate {
12587c478bd9Sstevel@tonic-gate 	/*
12597c478bd9Sstevel@tonic-gate 	 * If we have shared clusters, it is either because the
12607c478bd9Sstevel@tonic-gate 	 * cluster somehow got assigned to multiple files and/or
12617c478bd9Sstevel@tonic-gate 	 * because of a loop in the cluster chain.  In either
12627c478bd9Sstevel@tonic-gate 	 * case we want to truncate the offending file at the
12637c478bd9Sstevel@tonic-gate 	 * cluster of contention.  Then, we will want to run
12647c478bd9Sstevel@tonic-gate 	 * through the remainder of the chain. If we find ourselves
12657c478bd9Sstevel@tonic-gate 	 * back at the top, we will know there is a loop in the
12667c478bd9Sstevel@tonic-gate 	 * FAT we need to remove.
12677c478bd9Sstevel@tonic-gate 	 */
12687c478bd9Sstevel@tonic-gate 	if (Verbose)
12697c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
12707c478bd9Sstevel@tonic-gate 		    gettext("Truncating chain due to duplicate allocation of "
12717c478bd9Sstevel@tonic-gate 		    "unit %d.\n"), clusterNum);
12727c478bd9Sstevel@tonic-gate 	/*
12737c478bd9Sstevel@tonic-gate 	 * Note that we don't orphan anything here, because the duplicate
12747c478bd9Sstevel@tonic-gate 	 * part of the chain may be part of another valid chain.
12757c478bd9Sstevel@tonic-gate 	 */
12767c478bd9Sstevel@tonic-gate 	(void) truncAtCluster(fd, badEntry, clusterNum);
12777c478bd9Sstevel@tonic-gate 	checkForFATLoop(clusterNum);
12787c478bd9Sstevel@tonic-gate }
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate void
12817c478bd9Sstevel@tonic-gate truncChainWithBadCluster(int fd, struct pcdir *dp, int32_t startCluster)
12827c478bd9Sstevel@tonic-gate {
12837c478bd9Sstevel@tonic-gate 	struct pcdir *orphanEntry;
12847c478bd9Sstevel@tonic-gate 	int32_t orphanStartCluster;
12857c478bd9Sstevel@tonic-gate 	int32_t c = startCluster;
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	while (c != 0) {
12887c478bd9Sstevel@tonic-gate 		if (isMarkedBad(c)) {
12897c478bd9Sstevel@tonic-gate 			/*
12907c478bd9Sstevel@tonic-gate 			 *  splitChain() truncates the current guy and
12917c478bd9Sstevel@tonic-gate 			 *  then makes an orphan chain out of the remaining
12927c478bd9Sstevel@tonic-gate 			 *  clusters.  When we come back from the split
12937c478bd9Sstevel@tonic-gate 			 *  we'll want to continue looking for bad clusters
12947c478bd9Sstevel@tonic-gate 			 *  in the orphan chain.
12957c478bd9Sstevel@tonic-gate 			 */
12967c478bd9Sstevel@tonic-gate 			splitChain(fd, dp, c,
12977c478bd9Sstevel@tonic-gate 			    &orphanEntry, &orphanStartCluster);
12987c478bd9Sstevel@tonic-gate 			/*
12997c478bd9Sstevel@tonic-gate 			 *  There is a chance that we weren't able or weren't
13007c478bd9Sstevel@tonic-gate 			 *  required to make a directory entry for the
13017c478bd9Sstevel@tonic-gate 			 *  remaining clusters.  In that case we won't go
13027c478bd9Sstevel@tonic-gate 			 *  on, because we couldn't make any more splits
13037c478bd9Sstevel@tonic-gate 			 *  anyway.
13047c478bd9Sstevel@tonic-gate 			 */
13057c478bd9Sstevel@tonic-gate 			if (orphanEntry == NULL)
13067c478bd9Sstevel@tonic-gate 				break;
13077c478bd9Sstevel@tonic-gate 			c = orphanStartCluster;
13087c478bd9Sstevel@tonic-gate 			dp = orphanEntry;
13097c478bd9Sstevel@tonic-gate 			continue;
13107c478bd9Sstevel@tonic-gate 		}
13117c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
13127c478bd9Sstevel@tonic-gate 	}
13137c478bd9Sstevel@tonic-gate }
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate int32_t
13167c478bd9Sstevel@tonic-gate nextInChain(int32_t currentCluster)
13177c478bd9Sstevel@tonic-gate {
13187c478bd9Sstevel@tonic-gate 	int32_t nextCluster;
13197c478bd9Sstevel@tonic-gate 
13207c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
13217c478bd9Sstevel@tonic-gate 	if (currentCluster < FIRST_CLUSTER || currentCluster > LastCluster)
13227c478bd9Sstevel@tonic-gate 		return (0);
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	/*
13257c478bd9Sstevel@tonic-gate 	 * Look up FAT entry of next link in cluster chain,
13267c478bd9Sstevel@tonic-gate 	 * if this one is the last one return 0 as the next link.
13277c478bd9Sstevel@tonic-gate 	 */
13287c478bd9Sstevel@tonic-gate 	nextCluster = readFATEntry(currentCluster);
13297c478bd9Sstevel@tonic-gate 	if (nextCluster < FIRST_CLUSTER || nextCluster > LastCluster)
13307c478bd9Sstevel@tonic-gate 		return (0);
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 	return (nextCluster);
13337c478bd9Sstevel@tonic-gate }
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate /*
13367c478bd9Sstevel@tonic-gate  * findImpactedCluster
13377c478bd9Sstevel@tonic-gate  *
13387c478bd9Sstevel@tonic-gate  *	Called when someone modifies what they believe might be a cached
13397c478bd9Sstevel@tonic-gate  *	cluster entry, but when	they only have a directory entry pointer
13407c478bd9Sstevel@tonic-gate  *	and not the cluster number.  We have to go dig up what cluster
13417c478bd9Sstevel@tonic-gate  *	they are modifying.
13427c478bd9Sstevel@tonic-gate  */
13437c478bd9Sstevel@tonic-gate int32_t
13447c478bd9Sstevel@tonic-gate findImpactedCluster(struct pcdir *modified)
13457c478bd9Sstevel@tonic-gate {
13467c478bd9Sstevel@tonic-gate 	CachedCluster *loop;
13477c478bd9Sstevel@tonic-gate 	/*
13487c478bd9Sstevel@tonic-gate 	 * Check to see if it's in the root directory first
13497c478bd9Sstevel@tonic-gate 	 */
13507c478bd9Sstevel@tonic-gate 	if (!IsFAT32 && ((uchar_t *)modified >= TheRootDir.bytes) &&
13517c478bd9Sstevel@tonic-gate 	    ((uchar_t *)modified < TheRootDir.bytes + RootDirSize))
13527c478bd9Sstevel@tonic-gate 		return (FAKE_ROOTDIR_CLUST);
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 	loop = ClusterCache;
13557c478bd9Sstevel@tonic-gate 	while (loop) {
13567c478bd9Sstevel@tonic-gate 		if (((uchar_t *)modified >= loop->clusterData.bytes) &&
13577c478bd9Sstevel@tonic-gate 		    ((uchar_t *)modified <
13587c478bd9Sstevel@tonic-gate 		    (loop->clusterData.bytes + BytesPerCluster))) {
13597c478bd9Sstevel@tonic-gate 			return (loop->clusterNum);
13607c478bd9Sstevel@tonic-gate 		}
13617c478bd9Sstevel@tonic-gate 		loop = loop->next;
13627c478bd9Sstevel@tonic-gate 	}
13637c478bd9Sstevel@tonic-gate 	/*
13647c478bd9Sstevel@tonic-gate 	 *  Guess it wasn't cached after all...
13657c478bd9Sstevel@tonic-gate 	 */
13667c478bd9Sstevel@tonic-gate 	return (0);
13677c478bd9Sstevel@tonic-gate }
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate void
13707c478bd9Sstevel@tonic-gate writeClusterMods(int fd)
13717c478bd9Sstevel@tonic-gate {
13727c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	while (loop) {
13757c478bd9Sstevel@tonic-gate 		if (loop->modified)
13767c478bd9Sstevel@tonic-gate 			writeCachedCluster(fd, loop);
13777c478bd9Sstevel@tonic-gate 		loop = loop->next;
13787c478bd9Sstevel@tonic-gate 	}
13797c478bd9Sstevel@tonic-gate }
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate void
13827c478bd9Sstevel@tonic-gate squirrelPath(struct nameinfo *pathInfo, int32_t clusterNum)
13837c478bd9Sstevel@tonic-gate {
13847c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
13857c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
13867c478bd9Sstevel@tonic-gate 		return;
13877c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
13887c478bd9Sstevel@tonic-gate 		return;
13897c478bd9Sstevel@tonic-gate 	InUse[clusterNum - FIRST_CLUSTER]->path = pathInfo;
13907c478bd9Sstevel@tonic-gate }
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate int
13937c478bd9Sstevel@tonic-gate markInUse(int fd, int32_t clusterNum, struct pcdir *referencer, struct
13947c478bd9Sstevel@tonic-gate     pcdir *longRef, int32_t longStartCluster, int isHiddenFile,
13957c478bd9Sstevel@tonic-gate     ClusterInfo **template)
13967c478bd9Sstevel@tonic-gate {
13977c478bd9Sstevel@tonic-gate 	int alreadyMarked;
13987c478bd9Sstevel@tonic-gate 	ClusterInfo *cl;
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
14017c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
14027c478bd9Sstevel@tonic-gate 		return (CLINFO_NEWLY_ALLOCED);
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate 	alreadyMarked = allocInUse(clusterNum, template);
14057c478bd9Sstevel@tonic-gate 	if ((alreadyMarked == CLINFO_PREVIOUSLY_ALLOCED) &&
14067c478bd9Sstevel@tonic-gate 	    (isInUse(clusterNum))) {
14077c478bd9Sstevel@tonic-gate 		sharedChainError(fd, clusterNum, referencer);
14087c478bd9Sstevel@tonic-gate 		return (CLINFO_PREVIOUSLY_ALLOCED);
14097c478bd9Sstevel@tonic-gate 	}
14107c478bd9Sstevel@tonic-gate 	cl = InUse[clusterNum - FIRST_CLUSTER];
14117c478bd9Sstevel@tonic-gate 	/*
14127c478bd9Sstevel@tonic-gate 	 * If Cl is newly allocated (refcnt <= 1) we must fill in the fields.
14137c478bd9Sstevel@tonic-gate 	 * If Cl has different fields, we must clone it.
14147c478bd9Sstevel@tonic-gate 	 */
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	if (cl->refcnt <= 1 || cl->dirent != referencer ||
14177c478bd9Sstevel@tonic-gate 	    cl->longent != longRef ||
14187c478bd9Sstevel@tonic-gate 	    cl->longEntStartClust != longStartCluster) {
14197c478bd9Sstevel@tonic-gate 		if (cl->refcnt > 1)
14207c478bd9Sstevel@tonic-gate 			cl = cloneClusterInfo(clusterNum);
14217c478bd9Sstevel@tonic-gate 		cl->dirent = referencer;
14227c478bd9Sstevel@tonic-gate 		cl->longent = longRef;
14237c478bd9Sstevel@tonic-gate 		cl->longEntStartClust = longStartCluster;
14247c478bd9Sstevel@tonic-gate 		if (isHiddenFile)
14257c478bd9Sstevel@tonic-gate 			cl->flags |= CLINFO_HIDDEN;
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 		/*
14287c478bd9Sstevel@tonic-gate 		 * Return cl as the template to use for other clusters in
14297c478bd9Sstevel@tonic-gate 		 * this file
14307c478bd9Sstevel@tonic-gate 		 */
14317c478bd9Sstevel@tonic-gate 		if (template)
14327c478bd9Sstevel@tonic-gate 			*template = cl;
14337c478bd9Sstevel@tonic-gate 	}
14347c478bd9Sstevel@tonic-gate 	return (CLINFO_NEWLY_ALLOCED);
14357c478bd9Sstevel@tonic-gate }
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate void
14387c478bd9Sstevel@tonic-gate markClusterModified(int32_t clusterNum)
14397c478bd9Sstevel@tonic-gate {
14407c478bd9Sstevel@tonic-gate 	CachedCluster *c;
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	if (clusterNum == FAKE_ROOTDIR_CLUST) {
14437c478bd9Sstevel@tonic-gate 		RootDirModified = 1;
14447c478bd9Sstevel@tonic-gate 		return;
14457c478bd9Sstevel@tonic-gate 	}
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
14487c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
14497c478bd9Sstevel@tonic-gate 		return;
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 	if (c = findClusterCacheEntry(clusterNum)) {
14527c478bd9Sstevel@tonic-gate 		c->modified = 1;
14537c478bd9Sstevel@tonic-gate 	} else {
14547c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
14557c478bd9Sstevel@tonic-gate 		    gettext("Unexpected internal error: "
14567c478bd9Sstevel@tonic-gate 		    "Missing cache entry [%d]\n"), clusterNum);
14577c478bd9Sstevel@tonic-gate 		exit(10);
14587c478bd9Sstevel@tonic-gate 	}
14597c478bd9Sstevel@tonic-gate }
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate /*
14627c478bd9Sstevel@tonic-gate  *  readCluster
14637c478bd9Sstevel@tonic-gate  *	caller wants to read cluster clusterNum.  We should return
14647c478bd9Sstevel@tonic-gate  *	a pointer to the read data in "data", and fill in the number
14657c478bd9Sstevel@tonic-gate  *	of bytes read in "datasize".  If shouldCache is non-zero
14667c478bd9Sstevel@tonic-gate  *	we should allocate cache space to the cluster, otherwise we
14677c478bd9Sstevel@tonic-gate  *	just return a pointer to a buffer we re-use whenever cacheing
14687c478bd9Sstevel@tonic-gate  *	is not requested.
14697c478bd9Sstevel@tonic-gate  */
14707c478bd9Sstevel@tonic-gate int
14717c478bd9Sstevel@tonic-gate readCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize,
14727c478bd9Sstevel@tonic-gate     int shouldCache)
14737c478bd9Sstevel@tonic-gate {
14747c478bd9Sstevel@tonic-gate 	uchar_t *newBuf;
14757c478bd9Sstevel@tonic-gate 	int rv;
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 	*data = NULL;
14787c478bd9Sstevel@tonic-gate 	if ((*data = findClusterDataInTheCache(clusterNum)) != NULL) {
14797c478bd9Sstevel@tonic-gate 		*datasize = BytesPerCluster;
14807c478bd9Sstevel@tonic-gate 		return (RDCLUST_GOOD);
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	rv = getCluster(fd, clusterNum, &newBuf, datasize);
14847c478bd9Sstevel@tonic-gate 	if (rv != RDCLUST_GOOD)
14857c478bd9Sstevel@tonic-gate 		return (rv);
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	/*
14887c478bd9Sstevel@tonic-gate 	 * Caller requested we NOT cache the data from this read.
14897c478bd9Sstevel@tonic-gate 	 * So, we just return a pointer to the common data buffer.
14907c478bd9Sstevel@tonic-gate 	 */
14917c478bd9Sstevel@tonic-gate 	if (shouldCache == 0) {
14927c478bd9Sstevel@tonic-gate 		*data = newBuf;
14937c478bd9Sstevel@tonic-gate 		return (rv);
14947c478bd9Sstevel@tonic-gate 	}
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	/*
14977c478bd9Sstevel@tonic-gate 	 * Caller requested we cache the data from this read.
14987c478bd9Sstevel@tonic-gate 	 * So, if we have some data, add it to the cache by
14997c478bd9Sstevel@tonic-gate 	 * copying it out of the common buffer into new storage.
15007c478bd9Sstevel@tonic-gate 	 */
15017c478bd9Sstevel@tonic-gate 	if (*datasize > 0)
15027c478bd9Sstevel@tonic-gate 		*data = addToCache(clusterNum, newBuf, datasize);
15037c478bd9Sstevel@tonic-gate 	return (rv);
15047c478bd9Sstevel@tonic-gate }
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate void
15077c478bd9Sstevel@tonic-gate findBadClusters(int fd)
15087c478bd9Sstevel@tonic-gate {
15097c478bd9Sstevel@tonic-gate 	int32_t clusterCount;
15107c478bd9Sstevel@tonic-gate 	int32_t datasize;
15117c478bd9Sstevel@tonic-gate 	uchar_t *data;
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 	BadClusterCount = 0;
15147c478bd9Sstevel@tonic-gate 	makeUseTable();
15157c478bd9Sstevel@tonic-gate 	(void) printf(gettext("** Scanning allocation units\n"));
15167c478bd9Sstevel@tonic-gate 	for (clusterCount = FIRST_CLUSTER;
15177c478bd9Sstevel@tonic-gate 	    clusterCount < LastCluster; clusterCount++) {
15187c478bd9Sstevel@tonic-gate 		if (readCluster(fd, clusterCount,
15197c478bd9Sstevel@tonic-gate 		    &data, &datasize, RDCLUST_DONT_CACHE) < 0) {
15207c478bd9Sstevel@tonic-gate 			if (Verbose)
15217c478bd9Sstevel@tonic-gate 			    (void) fprintf(stderr,
15227c478bd9Sstevel@tonic-gate 				gettext("\nUnreadable allocation unit %d.\n"),
15237c478bd9Sstevel@tonic-gate 				clusterCount);
15247c478bd9Sstevel@tonic-gate 			markBad(clusterCount, data, datasize);
15257c478bd9Sstevel@tonic-gate 		}
15267c478bd9Sstevel@tonic-gate 		/*
15277c478bd9Sstevel@tonic-gate 		 *  Progress meter, display a '.' for every 1000 clusters
15287c478bd9Sstevel@tonic-gate 		 *  processed.  We don't want to display this when
15297c478bd9Sstevel@tonic-gate 		 *  we are in verbose mode; verbose mode progress is
15307c478bd9Sstevel@tonic-gate 		 *  shown by displaying each file name as it is found.
15317c478bd9Sstevel@tonic-gate 		 */
15327c478bd9Sstevel@tonic-gate 		if (!Verbose && clusterCount % 1000 == 0)
15337c478bd9Sstevel@tonic-gate 			(void) printf(".");
15347c478bd9Sstevel@tonic-gate 	}
15357c478bd9Sstevel@tonic-gate 	(void) printf(gettext("..done\n"));
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate void
15397c478bd9Sstevel@tonic-gate scanAndFixMetadata(int fd)
15407c478bd9Sstevel@tonic-gate {
15417c478bd9Sstevel@tonic-gate 	/*
15427c478bd9Sstevel@tonic-gate 	 * First we initialize a few things.
15437c478bd9Sstevel@tonic-gate 	 */
15447c478bd9Sstevel@tonic-gate 	makeUseTable();
15457c478bd9Sstevel@tonic-gate 	getReadyToSearch(fd);
15467c478bd9Sstevel@tonic-gate 	createCHKNameList(fd);
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 	/*
15497c478bd9Sstevel@tonic-gate 	 * Make initial scan, taking into account any effect that
15507c478bd9Sstevel@tonic-gate 	 * the bad clusters we may have already discovered have
15517c478bd9Sstevel@tonic-gate 	 * on meta-data.  We may break up some cluster chains
15527c478bd9Sstevel@tonic-gate 	 * during this period.  The relinkCreatedOrphans() call
15537c478bd9Sstevel@tonic-gate 	 * will then give the user the chance to recover stuff
15547c478bd9Sstevel@tonic-gate 	 * we've created.
15557c478bd9Sstevel@tonic-gate 	 */
15567c478bd9Sstevel@tonic-gate 	(void) printf(gettext("** Scanning file system meta-data\n"));
15577c478bd9Sstevel@tonic-gate 	summarize(fd, NO_FAT_IN_SUMMARY);
15587c478bd9Sstevel@tonic-gate 	if (Verbose)
15597c478bd9Sstevel@tonic-gate 		printSummary(stderr);
15607c478bd9Sstevel@tonic-gate 	(void) printf(gettext("** Correcting any meta-data discrepancies\n"));
15617c478bd9Sstevel@tonic-gate 	relinkCreatedOrphans(fd);
15627c478bd9Sstevel@tonic-gate 
15637c478bd9Sstevel@tonic-gate 	/*
15647c478bd9Sstevel@tonic-gate 	 * Clear our usage table and go back over everything, this
15657c478bd9Sstevel@tonic-gate 	 * time including looking for clusters floating free in the FAT.
15667c478bd9Sstevel@tonic-gate 	 * This may include clusters the user chose to free during the
15677c478bd9Sstevel@tonic-gate 	 * relink phase.
15687c478bd9Sstevel@tonic-gate 	 */
15697c478bd9Sstevel@tonic-gate 	makeUseTable();
15707c478bd9Sstevel@tonic-gate 	summarize(fd, INCLUDE_FAT_IN_SUMMARY);
15717c478bd9Sstevel@tonic-gate 	relinkOrphans(fd);
15727c478bd9Sstevel@tonic-gate }
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate void
15757c478bd9Sstevel@tonic-gate printSummary(FILE *outDest)
15767c478bd9Sstevel@tonic-gate {
15777c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
15787c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes.\n"),
15797c478bd9Sstevel@tonic-gate 	    (uint64_t)
15807c478bd9Sstevel@tonic-gate 	    TotalClusters * TheBIOSParameterBlock.bpb.sectors_per_cluster *
15817c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
15827c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
15837c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes in bad sectors.\n"),
15847c478bd9Sstevel@tonic-gate 	    (uint64_t)
15857c478bd9Sstevel@tonic-gate 	    BadClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
15867c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
15877c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
15887c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes in %d directories.\n"),
15897c478bd9Sstevel@tonic-gate 	    (uint64_t)
15907c478bd9Sstevel@tonic-gate 	    DirClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
15917c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector, DirCount);
15927c478bd9Sstevel@tonic-gate 	if (HiddenClusterCount) {
15937c478bd9Sstevel@tonic-gate 		(void) fprintf(outDest,
15947c478bd9Sstevel@tonic-gate 		    gettext("%llu bytes in %d hidden files.\n"),
15957c478bd9Sstevel@tonic-gate 		    (uint64_t)HiddenClusterCount *
15967c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.sectors_per_cluster *
15977c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector,
15987c478bd9Sstevel@tonic-gate 		    HiddenFileCount);
15997c478bd9Sstevel@tonic-gate 	}
16007c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
16017c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes in %d files.\n"),
16027c478bd9Sstevel@tonic-gate 	    (uint64_t)
16037c478bd9Sstevel@tonic-gate 	    FileClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
16047c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector, FileCount);
16057c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
16067c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes free.\n"), (uint64_t)FreeClusterCount *
16077c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.sectors_per_cluster *
16087c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
16097c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
16107c478bd9Sstevel@tonic-gate 	    gettext("%d bytes per allocation unit.\n"),
16117c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.sectors_per_cluster *
16127c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
16137c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
16147c478bd9Sstevel@tonic-gate 	    gettext("%d total allocation units.\n"), TotalClusters);
16157c478bd9Sstevel@tonic-gate 	if (ReservedClusterCount)
16167c478bd9Sstevel@tonic-gate 	    (void) fprintf(outDest, gettext("%d reserved allocation units.\n"),
16177c478bd9Sstevel@tonic-gate 		ReservedClusterCount);
16187c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
16197c478bd9Sstevel@tonic-gate 	    gettext("%d available allocation units.\n"), FreeClusterCount);
16207c478bd9Sstevel@tonic-gate }
1621