xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/fsck/clusters.c (revision 8509e9ca)
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.
2548bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * fsck_pcfs -- routines for manipulating clusters.
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 <errno.h>
377c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
397c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
417c478bd9Sstevel@tonic-gate #include "pcfs_common.h"
427c478bd9Sstevel@tonic-gate #include "fsck_pcfs.h"
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate extern	ClusterContents	TheRootDir;
457c478bd9Sstevel@tonic-gate extern	off64_t		FirstClusterOffset;
467c478bd9Sstevel@tonic-gate extern	off64_t		PartitionOffset;
477c478bd9Sstevel@tonic-gate extern	int32_t		BytesPerCluster;
487c478bd9Sstevel@tonic-gate extern	int32_t		TotalClusters;
497c478bd9Sstevel@tonic-gate extern	int32_t		LastCluster;
507c478bd9Sstevel@tonic-gate extern	int32_t		RootDirSize;
517c478bd9Sstevel@tonic-gate extern	int32_t		FATSize;
527c478bd9Sstevel@tonic-gate extern	bpb_t		TheBIOSParameterBlock;
537c478bd9Sstevel@tonic-gate extern	short		FATEntrySize;
547c478bd9Sstevel@tonic-gate extern	int		RootDirModified;
557c478bd9Sstevel@tonic-gate extern	int		OkayToRelink;
567c478bd9Sstevel@tonic-gate extern	int		ReadOnly;
577c478bd9Sstevel@tonic-gate extern	int		IsFAT32;
587c478bd9Sstevel@tonic-gate extern	int		Verbose;
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate static	struct pcdir	BlankPCDIR;
617c478bd9Sstevel@tonic-gate static	CachedCluster	*ClusterCache;
627c478bd9Sstevel@tonic-gate static	ClusterInfo	**InUse;
637c478bd9Sstevel@tonic-gate static	int32_t		ReservedClusterCount;
647c478bd9Sstevel@tonic-gate static	int32_t		AllocedClusterCount;
657c478bd9Sstevel@tonic-gate static	int32_t		FreeClusterCount;
667c478bd9Sstevel@tonic-gate static	int32_t		BadClusterCount;
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /*
697c478bd9Sstevel@tonic-gate  * Internal statistics
707c478bd9Sstevel@tonic-gate  */
717c478bd9Sstevel@tonic-gate static	int32_t		CachedClusterCount;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate int32_t	HiddenClusterCount;
747c478bd9Sstevel@tonic-gate int32_t	FileClusterCount;
757c478bd9Sstevel@tonic-gate int32_t	DirClusterCount;
767c478bd9Sstevel@tonic-gate int32_t	HiddenFileCount;
777c478bd9Sstevel@tonic-gate int32_t	FileCount;
787c478bd9Sstevel@tonic-gate int32_t	DirCount;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate static int32_t orphanSizeLookup(int32_t clusterNum);
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate static void
freeNameInfo(int32_t clusterNum)837c478bd9Sstevel@tonic-gate freeNameInfo(int32_t clusterNum)
847c478bd9Sstevel@tonic-gate {
857c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
867c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
877c478bd9Sstevel@tonic-gate 		return;
887c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
897c478bd9Sstevel@tonic-gate 		if (InUse[clusterNum - FIRST_CLUSTER]->path->references > 1) {
907c478bd9Sstevel@tonic-gate 			InUse[clusterNum - FIRST_CLUSTER]->path->references--;
917c478bd9Sstevel@tonic-gate 		} else {
927c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
937c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->path);
947c478bd9Sstevel@tonic-gate 		}
957c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER]->path = NULL;
967c478bd9Sstevel@tonic-gate 	}
977c478bd9Sstevel@tonic-gate }
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate static void
printOrphanPath(int32_t clusterNum)1007c478bd9Sstevel@tonic-gate printOrphanPath(int32_t clusterNum)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1037c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1047c478bd9Sstevel@tonic-gate 		return;
1057c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
1067c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\nOrphaned allocation units originally "
1077c478bd9Sstevel@tonic-gate 		    "allocated to:\n"));
1087c478bd9Sstevel@tonic-gate 		(void) printf("%s\n",
1097c478bd9Sstevel@tonic-gate 		    InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
1107c478bd9Sstevel@tonic-gate 		freeNameInfo(clusterNum);
1117c478bd9Sstevel@tonic-gate 	} else {
1127c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\nOrphaned allocation units originally "
1137c478bd9Sstevel@tonic-gate 		    "allocated to an unknown file or directory:\n"));
1147c478bd9Sstevel@tonic-gate 		(void) printf(gettext("Orphaned chain begins with allocation "
1157c478bd9Sstevel@tonic-gate 		    "unit %d.\n"), clusterNum);
1167c478bd9Sstevel@tonic-gate 	}
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate static void
printOrphanSize(int32_t clusterNum)1207c478bd9Sstevel@tonic-gate printOrphanSize(int32_t clusterNum)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate 	int32_t size = orphanSizeLookup(clusterNum);
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	if (size > 0) {
1257c478bd9Sstevel@tonic-gate 		(void) printf(gettext("%d bytes in the orphaned chain of "
1267c478bd9Sstevel@tonic-gate 		    "allocation units.\n"), size);
1277c478bd9Sstevel@tonic-gate 		if (Verbose) {
1287c478bd9Sstevel@tonic-gate 			(void) printf(gettext("[Starting at allocation "
1297c478bd9Sstevel@tonic-gate 			    "unit %d]\n"), clusterNum);
1307c478bd9Sstevel@tonic-gate 		}
1317c478bd9Sstevel@tonic-gate 	}
1327c478bd9Sstevel@tonic-gate }
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate static void
printOrphanInfo(int32_t clusterNum)1357c478bd9Sstevel@tonic-gate printOrphanInfo(int32_t clusterNum)
1367c478bd9Sstevel@tonic-gate {
1377c478bd9Sstevel@tonic-gate 	printOrphanPath(clusterNum);
1387c478bd9Sstevel@tonic-gate 	printOrphanSize(clusterNum);
1397c478bd9Sstevel@tonic-gate }
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate static int
askAboutFreeing(int32_t clusterNum)1427c478bd9Sstevel@tonic-gate askAboutFreeing(int32_t clusterNum)
1437c478bd9Sstevel@tonic-gate {
1447c478bd9Sstevel@tonic-gate 	/*
1457c478bd9Sstevel@tonic-gate 	 * If it is not OkayToRelink, we haven't already printed the size
1467c478bd9Sstevel@tonic-gate 	 * of the orphaned chain.
1477c478bd9Sstevel@tonic-gate 	 */
1487c478bd9Sstevel@tonic-gate 	if (!OkayToRelink)
1497c478bd9Sstevel@tonic-gate 		printOrphanInfo(clusterNum);
1507c478bd9Sstevel@tonic-gate 	/*
1517c478bd9Sstevel@tonic-gate 	 *  If we are in preen mode, preenBail won't return.
1527c478bd9Sstevel@tonic-gate 	 */
1537c478bd9Sstevel@tonic-gate 	preenBail("Need user confirmation to free orphaned chain.\n");
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	(void) printf(
1567c478bd9Sstevel@tonic-gate 	    gettext("Free the allocation units in the orphaned chain ? "
1577c478bd9Sstevel@tonic-gate 	    "(y/n) "));
1587c478bd9Sstevel@tonic-gate 	return (yes());
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate static int
askAboutRelink(int32_t clusterNum)1627c478bd9Sstevel@tonic-gate askAboutRelink(int32_t clusterNum)
1637c478bd9Sstevel@tonic-gate {
1647c478bd9Sstevel@tonic-gate 	/*
1657c478bd9Sstevel@tonic-gate 	 * Display the size of the chain for the user to consider.
1667c478bd9Sstevel@tonic-gate 	 */
1677c478bd9Sstevel@tonic-gate 	printOrphanInfo(clusterNum);
1687c478bd9Sstevel@tonic-gate 	/*
1697c478bd9Sstevel@tonic-gate 	 *  If we are in preen mode, preenBail won't return.
1707c478bd9Sstevel@tonic-gate 	 */
1717c478bd9Sstevel@tonic-gate 	preenBail("Need user confirmation to re-link orphaned chain.\n");
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	(void) printf(gettext("Re-link orphaned chain into file system ? "
1747c478bd9Sstevel@tonic-gate 	    "(y/n) "));
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	return (yes());
1777c478bd9Sstevel@tonic-gate }
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate static int
isHidden(int32_t clusterNum)1807c478bd9Sstevel@tonic-gate isHidden(int32_t clusterNum)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1837c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1847c478bd9Sstevel@tonic-gate 		return (0);
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
1877c478bd9Sstevel@tonic-gate 		return (0);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_HIDDEN);
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate static int
isInUse(int32_t clusterNum)1937c478bd9Sstevel@tonic-gate isInUse(int32_t clusterNum)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1967c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1977c478bd9Sstevel@tonic-gate 		return (0);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	return ((InUse[clusterNum - FIRST_CLUSTER] != NULL) &&
2007c478bd9Sstevel@tonic-gate 		(InUse[clusterNum - FIRST_CLUSTER]->dirent != NULL));
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate /*
2047c478bd9Sstevel@tonic-gate  *  Caller's may request that we cache the data from a readCluster.
2057c478bd9Sstevel@tonic-gate  *  The xxxClusterxxxCachexxx routines handle looking for cached data
2067c478bd9Sstevel@tonic-gate  *  or initially caching the data.
2077c478bd9Sstevel@tonic-gate  *
2087c478bd9Sstevel@tonic-gate  *  XXX - facilitate releasing cached data for low memory situations.
2097c478bd9Sstevel@tonic-gate  */
2107c478bd9Sstevel@tonic-gate static CachedCluster *
findClusterCacheEntry(int32_t clusterNum)2117c478bd9Sstevel@tonic-gate findClusterCacheEntry(int32_t clusterNum)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	while (loop != NULL) {
2167c478bd9Sstevel@tonic-gate 		if (loop->clusterNum == clusterNum)
2177c478bd9Sstevel@tonic-gate 			return (loop);
2187c478bd9Sstevel@tonic-gate 		loop = loop->next;
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 	return (NULL);
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate static uchar_t *
findClusterDataInTheCache(int32_t clusterNum)2247c478bd9Sstevel@tonic-gate findClusterDataInTheCache(int32_t clusterNum)
2257c478bd9Sstevel@tonic-gate {
2267c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	while (loop) {
2297c478bd9Sstevel@tonic-gate 		if (loop->clusterNum == clusterNum)
2307c478bd9Sstevel@tonic-gate 			return (loop->clusterData.bytes);
2317c478bd9Sstevel@tonic-gate 		loop = loop->next;
2327c478bd9Sstevel@tonic-gate 	}
2337c478bd9Sstevel@tonic-gate 	return (NULL);
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate static uchar_t *
addToCache(int32_t clusterNum,uchar_t * buf,int32_t * datasize)2377c478bd9Sstevel@tonic-gate addToCache(int32_t clusterNum, uchar_t *buf, int32_t *datasize)
2387c478bd9Sstevel@tonic-gate {
2397c478bd9Sstevel@tonic-gate 	CachedCluster *new;
2407c478bd9Sstevel@tonic-gate 	uchar_t *cp;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	if ((new = (CachedCluster *)malloc(sizeof (CachedCluster))) == NULL) {
2437c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for cached cluster info"));
2447c478bd9Sstevel@tonic-gate 		return (buf);
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 	new->clusterNum = clusterNum;
2477c478bd9Sstevel@tonic-gate 	new->modified = 0;
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	if ((cp = (uchar_t *)calloc(1, BytesPerCluster)) == NULL) {
2507c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for cached copy of cluster"));
2517c478bd9Sstevel@tonic-gate 		free(new);
2527c478bd9Sstevel@tonic-gate 		return (buf);
2537c478bd9Sstevel@tonic-gate 	}
2547c478bd9Sstevel@tonic-gate 	(void) memcpy(cp, buf, *datasize);
2557c478bd9Sstevel@tonic-gate 	new->clusterData.bytes = cp;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	if (Verbose) {
2587c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2597c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d cached.\n"), clusterNum);
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 	if (ClusterCache == NULL) {
2627c478bd9Sstevel@tonic-gate 		ClusterCache = new;
2637c478bd9Sstevel@tonic-gate 		new->next = NULL;
2647c478bd9Sstevel@tonic-gate 	} else if (new->clusterNum < ClusterCache->clusterNum) {
2657c478bd9Sstevel@tonic-gate 		new->next = ClusterCache;
2667c478bd9Sstevel@tonic-gate 		ClusterCache = new;
2677c478bd9Sstevel@tonic-gate 	} else {
2687c478bd9Sstevel@tonic-gate 		CachedCluster *loop = ClusterCache;
2697c478bd9Sstevel@tonic-gate 		CachedCluster *trailer = NULL;
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 		while (loop && new->clusterNum > loop->clusterNum) {
2727c478bd9Sstevel@tonic-gate 			trailer = loop;
2737c478bd9Sstevel@tonic-gate 			loop = loop->next;
2747c478bd9Sstevel@tonic-gate 		}
2757c478bd9Sstevel@tonic-gate 		trailer->next = new;
2767c478bd9Sstevel@tonic-gate 		if (loop) {
2777c478bd9Sstevel@tonic-gate 			new->next = loop;
2787c478bd9Sstevel@tonic-gate 		} else {
2797c478bd9Sstevel@tonic-gate 			new->next = NULL;
2807c478bd9Sstevel@tonic-gate 		}
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 	CachedClusterCount++;
2837c478bd9Sstevel@tonic-gate 	return (new->clusterData.bytes);
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate static int
seekCluster(int fd,int32_t clusterNum)2877c478bd9Sstevel@tonic-gate seekCluster(int fd, int32_t clusterNum)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	off64_t seekto;
2907c478bd9Sstevel@tonic-gate 	int saveError;
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	seekto = FirstClusterOffset +
2937c478bd9Sstevel@tonic-gate 	    ((off64_t)clusterNum - FIRST_CLUSTER) * BytesPerCluster;
2947c478bd9Sstevel@tonic-gate 	if (lseek64(fd, seekto, SEEK_SET) != seekto) {
2957c478bd9Sstevel@tonic-gate 		saveError = errno;
2967c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2977c478bd9Sstevel@tonic-gate 		    gettext("Seek to Allocation unit #%d failed: "),
2987c478bd9Sstevel@tonic-gate 		    clusterNum);
2997c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, strerror(saveError));
3007c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
3017c478bd9Sstevel@tonic-gate 		return (0);
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 	return (1);
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate /*
3077c478bd9Sstevel@tonic-gate  *  getcluster
3087c478bd9Sstevel@tonic-gate  *	Get cluster bytes off the disk.  We always read those bytes into
30948bbca81SDaniel Hoffman  *	the same static buffer.  If the caller wants its own copy of the
31048bbca81SDaniel Hoffman  *	data it'll have to make its own copy.  We'll return all the data
3117c478bd9Sstevel@tonic-gate  *	read, even if it's short of a full cluster.  This is for future use
3127c478bd9Sstevel@tonic-gate  *	when we might want to relocate any salvagable data from bad clusters.
3137c478bd9Sstevel@tonic-gate  */
3147c478bd9Sstevel@tonic-gate static int
getCluster(int fd,int32_t clusterNum,uchar_t ** data,int32_t * datasize)3157c478bd9Sstevel@tonic-gate getCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize)
3167c478bd9Sstevel@tonic-gate {
3177c478bd9Sstevel@tonic-gate 	static uchar_t *clusterBuffer = NULL;
3187c478bd9Sstevel@tonic-gate 	int saveError;
3197c478bd9Sstevel@tonic-gate 	int try;
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	*datasize = 0;
3227c478bd9Sstevel@tonic-gate 	*data = NULL;
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
3257c478bd9Sstevel@tonic-gate 		return (RDCLUST_BADINPUT);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	if (clusterBuffer == NULL &&
3287c478bd9Sstevel@tonic-gate 	    (clusterBuffer = (uchar_t *)malloc(BytesPerCluster)) == NULL) {
3297c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for a cluster data buffer"));
3307c478bd9Sstevel@tonic-gate 		return (RDCLUST_MEMERR);
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	for (try = 0; try < RDCLUST_MAX_RETRY; try++) {
3347c478bd9Sstevel@tonic-gate 		if (!seekCluster(fd, clusterNum))
3357c478bd9Sstevel@tonic-gate 			return (RDCLUST_FAIL);
3367c478bd9Sstevel@tonic-gate 		if ((*datasize = read(fd, clusterBuffer, BytesPerCluster)) ==
3377c478bd9Sstevel@tonic-gate 		    BytesPerCluster) {
3387c478bd9Sstevel@tonic-gate 			*data = clusterBuffer;
3397c478bd9Sstevel@tonic-gate 			return (RDCLUST_GOOD);
3407c478bd9Sstevel@tonic-gate 		}
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 	if (*datasize >= 0) {
3437c478bd9Sstevel@tonic-gate 		*data = clusterBuffer;
3447c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3457c478bd9Sstevel@tonic-gate 		    gettext("Short read of allocation unit #%d\n"), clusterNum);
3467c478bd9Sstevel@tonic-gate 	} else {
3477c478bd9Sstevel@tonic-gate 		saveError = errno;
3487c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "Allocation unit %d:", clusterNum);
3497c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, strerror(saveError));
3507c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
3517c478bd9Sstevel@tonic-gate 	}
3527c478bd9Sstevel@tonic-gate 	return (RDCLUST_FAIL);
3537c478bd9Sstevel@tonic-gate }
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate static void
writeCachedCluster(int fd,CachedCluster * clustInfo)3567c478bd9Sstevel@tonic-gate writeCachedCluster(int fd, CachedCluster *clustInfo)
3577c478bd9Sstevel@tonic-gate {
3587c478bd9Sstevel@tonic-gate 	ssize_t bytesWritten;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	if (ReadOnly)
3617c478bd9Sstevel@tonic-gate 		return;
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	if (Verbose)
3647c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3657c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d modified.\n"),
3667c478bd9Sstevel@tonic-gate 		    clustInfo->clusterNum);
3677c478bd9Sstevel@tonic-gate 
368*8509e9caSToomas Soome 	if (seekCluster(fd, clustInfo->clusterNum) == 0)
3697c478bd9Sstevel@tonic-gate 		return;
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if ((bytesWritten = write(fd, clustInfo->clusterData.bytes,
3727c478bd9Sstevel@tonic-gate 	    BytesPerCluster)) != BytesPerCluster) {
3737c478bd9Sstevel@tonic-gate 		if (bytesWritten < 0) {
3747c478bd9Sstevel@tonic-gate 			perror(gettext("Failed to write modified "
3757c478bd9Sstevel@tonic-gate 			    "allocation unit"));
3767c478bd9Sstevel@tonic-gate 		} else {
3777c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3787c478bd9Sstevel@tonic-gate 			    gettext("Short write of allocation unit %d\n"),
3797c478bd9Sstevel@tonic-gate 			    clustInfo->clusterNum);
3807c478bd9Sstevel@tonic-gate 		}
3817c478bd9Sstevel@tonic-gate 		(void) close(fd);
3827c478bd9Sstevel@tonic-gate 		exit(13);
3837c478bd9Sstevel@tonic-gate 	}
3847c478bd9Sstevel@tonic-gate }
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate  * It's cheaper to allocate a lot at a time; malloc overhead pushes
3887c478bd9Sstevel@tonic-gate  * you over the brink much more quickly if you don't.
3897c478bd9Sstevel@tonic-gate  * This numbers seems to be a fair trade-off between reduced malloc overhead
3907c478bd9Sstevel@tonic-gate  * and additional overhead by over-allocating.
3917c478bd9Sstevel@tonic-gate  */
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate #define	CHUNKSIZE	1024
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate static ClusterInfo *pool;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate static ClusterInfo *
newClusterInfo(void)3987c478bd9Sstevel@tonic-gate newClusterInfo(void)
3997c478bd9Sstevel@tonic-gate {
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	ClusterInfo *ret;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	if (pool == NULL) {
4047c478bd9Sstevel@tonic-gate 		int i;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 		pool = (ClusterInfo *)malloc(sizeof (ClusterInfo) * CHUNKSIZE);
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 		if (pool == NULL) {
4097c478bd9Sstevel@tonic-gate 			perror(
4107c478bd9Sstevel@tonic-gate 			    gettext("Out of memory for cluster information"));
4117c478bd9Sstevel@tonic-gate 			exit(9);
4127c478bd9Sstevel@tonic-gate 		}
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		for (i = 0; i < CHUNKSIZE - 1; i++)
4157c478bd9Sstevel@tonic-gate 			pool[i].nextfree = &pool[i+1];
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		pool[CHUNKSIZE-1].nextfree = NULL;
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 	ret = pool;
4207c478bd9Sstevel@tonic-gate 	pool = pool->nextfree;
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	memset(ret, 0, sizeof (*ret));
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	return (ret);
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate /* Should be called with verified arguments */
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate static ClusterInfo *
cloneClusterInfo(int32_t clusterNum)4307c478bd9Sstevel@tonic-gate cloneClusterInfo(int32_t clusterNum)
4317c478bd9Sstevel@tonic-gate {
4327c478bd9Sstevel@tonic-gate 	ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	if (cl->refcnt > 1) {
4357c478bd9Sstevel@tonic-gate 		ClusterInfo *newCl = newClusterInfo();
4367c478bd9Sstevel@tonic-gate 		cl->refcnt--;
4377c478bd9Sstevel@tonic-gate 		*newCl = *cl;
4387c478bd9Sstevel@tonic-gate 		newCl->refcnt = 1;
4397c478bd9Sstevel@tonic-gate 		if (newCl->path)
4407c478bd9Sstevel@tonic-gate 			newCl->path->references++;
4417c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER] = newCl;
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]);
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate static void
updateFlags(int32_t clusterNum,int newflags)4477c478bd9Sstevel@tonic-gate updateFlags(int32_t clusterNum, int newflags)
4487c478bd9Sstevel@tonic-gate {
4497c478bd9Sstevel@tonic-gate 	ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	if (cl->flags != newflags && cl->refcnt > 1)
4527c478bd9Sstevel@tonic-gate 		cl = cloneClusterInfo(clusterNum);
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	cl->flags = newflags;
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate static void
freeClusterInfo(ClusterInfo * old)4587c478bd9Sstevel@tonic-gate freeClusterInfo(ClusterInfo *old)
4597c478bd9Sstevel@tonic-gate {
4607c478bd9Sstevel@tonic-gate 	if (--old->refcnt <= 0) {
4617c478bd9Sstevel@tonic-gate 		if (old->path && --old->path->references <= 0) {
4627c478bd9Sstevel@tonic-gate 			free(old->path->fullName);
4637c478bd9Sstevel@tonic-gate 			free(old->path);
4647c478bd9Sstevel@tonic-gate 		}
4657c478bd9Sstevel@tonic-gate 		old->nextfree = pool;
4667c478bd9Sstevel@tonic-gate 		pool = old;
4677c478bd9Sstevel@tonic-gate 	}
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate /*
4717c478bd9Sstevel@tonic-gate  * Allocate entries in our sparse array of cluster information.
4727c478bd9Sstevel@tonic-gate  * Returns non-zero if the structure already has been allocated
4737c478bd9Sstevel@tonic-gate  * (for those keeping score at home).
4747c478bd9Sstevel@tonic-gate  *
4757c478bd9Sstevel@tonic-gate  * The template parameter, if non-NULL, is used to facilitate sharing
4767c478bd9Sstevel@tonic-gate  * the ClusterInfo nodes for the clusters belonging to the same file.
4777c478bd9Sstevel@tonic-gate  * The first call to allocInUse for a new file should have *template
4787c478bd9Sstevel@tonic-gate  * set to 0; on return, *template then points to the newly allocated
4797c478bd9Sstevel@tonic-gate  * ClusterInfo.  Second and further calls keep the same value
4807c478bd9Sstevel@tonic-gate  * in *template and that ClusterInfo ndoe is then used for all
4817c478bd9Sstevel@tonic-gate  * entries in the file.  Code that modifies the ClusterInfo nodes
4827c478bd9Sstevel@tonic-gate  * should take care proper sharing semantics are maintained (i.e.,
4837c478bd9Sstevel@tonic-gate  * copy-on-write using cloneClusterInfo())
4847c478bd9Sstevel@tonic-gate  *
4857c478bd9Sstevel@tonic-gate  * The ClusterInfo used in the template is guaranted to be in use in
4867c478bd9Sstevel@tonic-gate  * at least one other cluster as we never return a value if we didn't
4877c478bd9Sstevel@tonic-gate  * set it first.  So we can overwrite it without the possibility of a leak.
4887c478bd9Sstevel@tonic-gate  */
4897c478bd9Sstevel@tonic-gate static int
allocInUse(int32_t clusterNum,ClusterInfo ** template)4907c478bd9Sstevel@tonic-gate allocInUse(int32_t clusterNum, ClusterInfo **template)
4917c478bd9Sstevel@tonic-gate {
4927c478bd9Sstevel@tonic-gate 	ClusterInfo *newCl;
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
4957c478bd9Sstevel@tonic-gate 		return (CLINFO_PREVIOUSLY_ALLOCED);
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	if (template != NULL && *template != NULL)
4987c478bd9Sstevel@tonic-gate 		newCl = *template;
4997c478bd9Sstevel@tonic-gate 	else {
5007c478bd9Sstevel@tonic-gate 		newCl = newClusterInfo();
5017c478bd9Sstevel@tonic-gate 		if (template)
5027c478bd9Sstevel@tonic-gate 			*template = newCl;
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	InUse[clusterNum - FIRST_CLUSTER] = newCl;
5067c478bd9Sstevel@tonic-gate 	newCl->refcnt++;
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	return (CLINFO_NEWLY_ALLOCED);
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate static void
markFree(int32_t clusterNum)5127c478bd9Sstevel@tonic-gate markFree(int32_t clusterNum)
5137c478bd9Sstevel@tonic-gate {
5147c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5157c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5167c478bd9Sstevel@tonic-gate 		return;
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]) {
5197c478bd9Sstevel@tonic-gate 		if (InUse[clusterNum - FIRST_CLUSTER]->saved)
5207c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->saved);
5217c478bd9Sstevel@tonic-gate 		freeClusterInfo(InUse[clusterNum - FIRST_CLUSTER]);
5227c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER] = NULL;
5237c478bd9Sstevel@tonic-gate 	}
5247c478bd9Sstevel@tonic-gate }
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate static void
markOrphan(int fd,int32_t clusterNum,struct pcdir * dp)5277c478bd9Sstevel@tonic-gate markOrphan(int fd, int32_t clusterNum, struct pcdir *dp)
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5307c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5317c478bd9Sstevel@tonic-gate 		return;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	(void) markInUse(fd, clusterNum, dp, NULL, 0, VISIBLE, NULL);
5347c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
5357c478bd9Sstevel@tonic-gate 		updateFlags(clusterNum,
5367c478bd9Sstevel@tonic-gate 		    InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_ORPHAN);
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate static void
markBad(int32_t clusterNum,uchar_t * recovered,int32_t recoveredLen)5407c478bd9Sstevel@tonic-gate markBad(int32_t clusterNum, uchar_t *recovered, int32_t recoveredLen)
5417c478bd9Sstevel@tonic-gate {
5427c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5437c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5447c478bd9Sstevel@tonic-gate 		return;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	(void) allocInUse(clusterNum, NULL);
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	if (recoveredLen) {
5497c478bd9Sstevel@tonic-gate 		(void) cloneClusterInfo(clusterNum);
5507c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER]->saved = recovered;
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 	updateFlags(clusterNum,
5537c478bd9Sstevel@tonic-gate 	    InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_BAD);
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	BadClusterCount++;
5567c478bd9Sstevel@tonic-gate 	if (Verbose)
5577c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
5587c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d marked bad.\n"), clusterNum);
5597c478bd9Sstevel@tonic-gate }
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate static void
clearOrphan(int32_t c)5627c478bd9Sstevel@tonic-gate clearOrphan(int32_t c)
5637c478bd9Sstevel@tonic-gate {
5647c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5657c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
5667c478bd9Sstevel@tonic-gate 		return;
5677c478bd9Sstevel@tonic-gate 	if (InUse[c - FIRST_CLUSTER] != NULL)
5687c478bd9Sstevel@tonic-gate 		updateFlags(c,
5697c478bd9Sstevel@tonic-gate 		    InUse[c - FIRST_CLUSTER]->flags & ~CLINFO_ORPHAN);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate static void
clearInUse(int32_t c)5737c478bd9Sstevel@tonic-gate clearInUse(int32_t c)
5747c478bd9Sstevel@tonic-gate {
5757c478bd9Sstevel@tonic-gate 	ClusterInfo **clp;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
5787c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
5797c478bd9Sstevel@tonic-gate 		return;
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	clp = &InUse[c - FIRST_CLUSTER];
5827c478bd9Sstevel@tonic-gate 	if (*clp != NULL) {
5837c478bd9Sstevel@tonic-gate 		freeClusterInfo(*clp);
5847c478bd9Sstevel@tonic-gate 		*clp = NULL;
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate }
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate static void
clearAllClusters_InUse()5897c478bd9Sstevel@tonic-gate clearAllClusters_InUse()
5907c478bd9Sstevel@tonic-gate {
5917c478bd9Sstevel@tonic-gate 	int32_t cc;
5927c478bd9Sstevel@tonic-gate 	for (cc = FIRST_CLUSTER; cc < LastCluster; cc++) {
5937c478bd9Sstevel@tonic-gate 		clearInUse(cc);
5947c478bd9Sstevel@tonic-gate 	}
5957c478bd9Sstevel@tonic-gate }
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate static void
makeUseTable(void)5987c478bd9Sstevel@tonic-gate makeUseTable(void)
5997c478bd9Sstevel@tonic-gate {
6007c478bd9Sstevel@tonic-gate 	if (InUse != NULL) {
6017c478bd9Sstevel@tonic-gate 		clearAllClusters_InUse();
6027c478bd9Sstevel@tonic-gate 		return;
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 	if ((InUse = (ClusterInfo **)
6057c478bd9Sstevel@tonic-gate 	    calloc(TotalClusters, sizeof (ClusterInfo *))) == NULL) {
6067c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for internal table"));
6077c478bd9Sstevel@tonic-gate 		exit(9);
6087c478bd9Sstevel@tonic-gate 	}
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate static void
countClusters(void)6127c478bd9Sstevel@tonic-gate countClusters(void)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	int32_t c;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	BadClusterCount = HiddenClusterCount =
6177c478bd9Sstevel@tonic-gate 	    AllocedClusterCount = FreeClusterCount = 0;
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
6207c478bd9Sstevel@tonic-gate 		if (badInFAT(c)) {
6217c478bd9Sstevel@tonic-gate 			BadClusterCount++;
6227c478bd9Sstevel@tonic-gate 		} else if (isMarkedBad(c)) {
6237c478bd9Sstevel@tonic-gate 			/*
6247c478bd9Sstevel@tonic-gate 			 * This catches the bad sectors found
6257c478bd9Sstevel@tonic-gate 			 * during thorough verify that have never been
6267c478bd9Sstevel@tonic-gate 			 * allocated to a file.  Without this check, we
6277c478bd9Sstevel@tonic-gate 			 * count these guys as free.
6287c478bd9Sstevel@tonic-gate 			 */
6297c478bd9Sstevel@tonic-gate 			BadClusterCount++;
6307c478bd9Sstevel@tonic-gate 			markBadInFAT(c);
6317c478bd9Sstevel@tonic-gate 		} else if (isHidden(c)) {
6327c478bd9Sstevel@tonic-gate 			HiddenClusterCount++;
6337c478bd9Sstevel@tonic-gate 		} else if (isInUse(c)) {
6347c478bd9Sstevel@tonic-gate 			AllocedClusterCount++;
6357c478bd9Sstevel@tonic-gate 		} else {
6367c478bd9Sstevel@tonic-gate 			FreeClusterCount++;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate }
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate /*
6427c478bd9Sstevel@tonic-gate  * summarizeFAT
6437c478bd9Sstevel@tonic-gate  *	Mark orphans without directory entries as allocated.
6447c478bd9Sstevel@tonic-gate  *	XXX - these chains should be reclaimed!
6457c478bd9Sstevel@tonic-gate  *	XXX - merge this routine with countClusters (same loop, duh.)
6467c478bd9Sstevel@tonic-gate  */
6477c478bd9Sstevel@tonic-gate static void
summarizeFAT(int fd)6487c478bd9Sstevel@tonic-gate summarizeFAT(int fd)
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate 	int32_t c;
6517c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
6547c478bd9Sstevel@tonic-gate 		if (!freeInFAT(c) && !badInFAT(c) && !reservedInFAT(c) &&
6557c478bd9Sstevel@tonic-gate 		    !isInUse(c)) {
6567c478bd9Sstevel@tonic-gate 			(void) markInUse(fd, c, &BlankPCDIR, NULL, 0, VISIBLE,
6577c478bd9Sstevel@tonic-gate 				&tmpl);
6587c478bd9Sstevel@tonic-gate 		}
6597c478bd9Sstevel@tonic-gate 	}
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate static void
getReadyToSearch(int fd)6637c478bd9Sstevel@tonic-gate getReadyToSearch(int fd)
6647c478bd9Sstevel@tonic-gate {
6657c478bd9Sstevel@tonic-gate 	getFAT(fd);
6667c478bd9Sstevel@tonic-gate 	if (!IsFAT32)
6677c478bd9Sstevel@tonic-gate 		getRootDirectory(fd);
6687c478bd9Sstevel@tonic-gate }
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate static char PathName[MAXPATHLEN];
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate static void
summarize(int fd,int includeFAT)6747c478bd9Sstevel@tonic-gate summarize(int fd, int includeFAT)
6757c478bd9Sstevel@tonic-gate {
6767c478bd9Sstevel@tonic-gate 	struct pcdir *ignorep1, *ignorep2 = NULL;
6777c478bd9Sstevel@tonic-gate 	int32_t ignore32;
6787c478bd9Sstevel@tonic-gate 	char ignore;
6797c478bd9Sstevel@tonic-gate 	int pathlen;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	ReservedClusterCount = 0;
6827c478bd9Sstevel@tonic-gate 	AllocedClusterCount = 0;
6837c478bd9Sstevel@tonic-gate 	HiddenClusterCount = 0;
6847c478bd9Sstevel@tonic-gate 	FileClusterCount = 0;
6857c478bd9Sstevel@tonic-gate 	FreeClusterCount = 0;
6867c478bd9Sstevel@tonic-gate 	DirClusterCount = 0;
6877c478bd9Sstevel@tonic-gate 	BadClusterCount = 0;
6887c478bd9Sstevel@tonic-gate 	HiddenFileCount = 0;
6897c478bd9Sstevel@tonic-gate 	FileCount = 0;
6907c478bd9Sstevel@tonic-gate 	DirCount = 0;
6917c478bd9Sstevel@tonic-gate 	ignorep1 = ignorep2 = NULL;
6927c478bd9Sstevel@tonic-gate 	ignore = '\0';
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	PathName[0] = '\0';
6957c478bd9Sstevel@tonic-gate 	pathlen = 0;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	getReadyToSearch(fd);
6987c478bd9Sstevel@tonic-gate 	/*
6997c478bd9Sstevel@tonic-gate 	 *  Traverse the full meta-data tree to talley what clusters
7007c478bd9Sstevel@tonic-gate 	 * are in use.  The root directory is an area outside of the
7017c478bd9Sstevel@tonic-gate 	 * file space on FAT12 and FAT16 file systems.  On FAT32 file
7027c478bd9Sstevel@tonic-gate 	 * systems, the root directory is in a file area cluster just
7037c478bd9Sstevel@tonic-gate 	 * like any other directory.
7047c478bd9Sstevel@tonic-gate 	 */
7057c478bd9Sstevel@tonic-gate 	if (!IsFAT32) {
7067c478bd9Sstevel@tonic-gate 		traverseFromRoot(fd, 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL,
7077c478bd9Sstevel@tonic-gate 		    ignore, &ignorep1, &ignore32, &ignorep2, PathName,
7087c478bd9Sstevel@tonic-gate 		    &pathlen);
7097c478bd9Sstevel@tonic-gate 	} else {
7107c478bd9Sstevel@tonic-gate 		DirCount++;
7117c478bd9Sstevel@tonic-gate 		traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7127c478bd9Sstevel@tonic-gate 		    0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL, ignore,
7137c478bd9Sstevel@tonic-gate 		    &ignorep1, &ignore32, &ignorep2, PathName, &pathlen);
7147c478bd9Sstevel@tonic-gate 	}
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	if (includeFAT)
7177c478bd9Sstevel@tonic-gate 		summarizeFAT(fd);
7187c478bd9Sstevel@tonic-gate 	countClusters();
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate int
isMarkedBad(int32_t clusterNum)7227c478bd9Sstevel@tonic-gate isMarkedBad(int32_t clusterNum)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
7257c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
7267c478bd9Sstevel@tonic-gate 		return (0);
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
7297c478bd9Sstevel@tonic-gate 		return (0);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_BAD);
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate static int
isMarkedOrphan(int32_t clusterNum)7357c478bd9Sstevel@tonic-gate isMarkedOrphan(int32_t clusterNum)
7367c478bd9Sstevel@tonic-gate {
7377c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
7387c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
7397c478bd9Sstevel@tonic-gate 		return (0);
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
7427c478bd9Sstevel@tonic-gate 		return (0);
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_ORPHAN);
7457c478bd9Sstevel@tonic-gate }
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate static void
orphanChain(int fd,int32_t c,struct pcdir * ndp)7487c478bd9Sstevel@tonic-gate orphanChain(int fd, int32_t c, struct pcdir *ndp)
7497c478bd9Sstevel@tonic-gate {
7507c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
7537c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
7547c478bd9Sstevel@tonic-gate 		return;
7557c478bd9Sstevel@tonic-gate 	clearInUse(c);
7567c478bd9Sstevel@tonic-gate 	markOrphan(fd, c, ndp);
7577c478bd9Sstevel@tonic-gate 	c = nextInChain(c);
7587c478bd9Sstevel@tonic-gate 	while (c != 0) {
7597c478bd9Sstevel@tonic-gate 		clearInUse(c);
7607c478bd9Sstevel@tonic-gate 		clearOrphan(c);
7617c478bd9Sstevel@tonic-gate 		(void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, &tmpl);
7627c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
7637c478bd9Sstevel@tonic-gate 	}
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate static int32_t
findAFreeCluster(int32_t startAt)7677c478bd9Sstevel@tonic-gate findAFreeCluster(int32_t startAt)
7687c478bd9Sstevel@tonic-gate {
7697c478bd9Sstevel@tonic-gate 	int32_t look = startAt;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	for (;;) {
7727c478bd9Sstevel@tonic-gate 		if (freeInFAT(look)) {
7737c478bd9Sstevel@tonic-gate 			break;
7747c478bd9Sstevel@tonic-gate 		}
7757c478bd9Sstevel@tonic-gate 		if (look == LastCluster)
7767c478bd9Sstevel@tonic-gate 			look = FIRST_CLUSTER;
7777c478bd9Sstevel@tonic-gate 		else
7787c478bd9Sstevel@tonic-gate 			look++;
7797c478bd9Sstevel@tonic-gate 		if (look == startAt)
7807c478bd9Sstevel@tonic-gate 			break;
7817c478bd9Sstevel@tonic-gate 	}
7827c478bd9Sstevel@tonic-gate 	if (look != startAt)
7837c478bd9Sstevel@tonic-gate 		return (look);
7847c478bd9Sstevel@tonic-gate 	else
7857c478bd9Sstevel@tonic-gate 		return (0);
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate static void
setEndOfDirectory(struct pcdir * dp)7897c478bd9Sstevel@tonic-gate setEndOfDirectory(struct pcdir *dp)
7907c478bd9Sstevel@tonic-gate {
7917c478bd9Sstevel@tonic-gate 	dp->pcd_filename[0] = PCD_UNUSED;
7927c478bd9Sstevel@tonic-gate }
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate static void
emergencyEndOfDirectory(int fd,int32_t secondToLast)7957c478bd9Sstevel@tonic-gate emergencyEndOfDirectory(int fd, int32_t secondToLast)
7967c478bd9Sstevel@tonic-gate {
7977c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
7987c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	if (readCluster(fd, secondToLast, &(dirdata.bytes), &dirdatasize,
8017c478bd9Sstevel@tonic-gate 	    RDCLUST_DO_CACHE) != RDCLUST_GOOD) {
8027c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8037c478bd9Sstevel@tonic-gate 		    gettext("Unable to read allocation unit %d.\n"),
8047c478bd9Sstevel@tonic-gate 		    secondToLast);
8057c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8067c478bd9Sstevel@tonic-gate 		    gettext("Cannot allocate a new allocation unit to hold an"
8077c478bd9Sstevel@tonic-gate 		    " end-of-directory marker.\nCannot access allocation unit"
8087c478bd9Sstevel@tonic-gate 		    " to overwrite existing directory entry with\nthe marker."
8097c478bd9Sstevel@tonic-gate 		    "  Needed directory truncation has failed.  Giving up.\n"));
8107c478bd9Sstevel@tonic-gate 		(void) close(fd);
8117c478bd9Sstevel@tonic-gate 		exit(11);
8127c478bd9Sstevel@tonic-gate 	}
8137c478bd9Sstevel@tonic-gate 	setEndOfDirectory(dirdata.dirp);
8147c478bd9Sstevel@tonic-gate 	markClusterModified(secondToLast);
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate static void
makeNewEndOfDirectory(struct pcdir * entry,int32_t secondToLast,int32_t newCluster,ClusterContents * newData)8187c478bd9Sstevel@tonic-gate makeNewEndOfDirectory(struct pcdir *entry, int32_t secondToLast,
8197c478bd9Sstevel@tonic-gate     int32_t newCluster, ClusterContents *newData)
8207c478bd9Sstevel@tonic-gate {
8217c478bd9Sstevel@tonic-gate 	setEndOfDirectory(newData->dirp);
8227c478bd9Sstevel@tonic-gate 	markClusterModified(newCluster);
8237c478bd9Sstevel@tonic-gate 	/*
8247c478bd9Sstevel@tonic-gate 	 *  There are two scenarios.  One is that we truncated the
8257c478bd9Sstevel@tonic-gate 	 *  directory in the very beginning.  The other is that we
8267c478bd9Sstevel@tonic-gate 	 *  truncated it in the middle or at the end.  In the first
8277c478bd9Sstevel@tonic-gate 	 *  scenario, the secondToLast argument is not a valid cluster
8287c478bd9Sstevel@tonic-gate 	 *  (it's zero), and so we actually need to change the start
8297c478bd9Sstevel@tonic-gate 	 *  cluster for the directory to this new start cluster.  In
8307c478bd9Sstevel@tonic-gate 	 *  the second scenario, the secondToLast cluster we received
8317c478bd9Sstevel@tonic-gate 	 *  as an argument needs to be pointed at the new end of
8327c478bd9Sstevel@tonic-gate 	 *  directory.
8337c478bd9Sstevel@tonic-gate 	 */
8347c478bd9Sstevel@tonic-gate 	if (secondToLast == 0) {
8357c478bd9Sstevel@tonic-gate 		updateDirEnt_Start(entry, newCluster);
8367c478bd9Sstevel@tonic-gate 	} else {
8377c478bd9Sstevel@tonic-gate 		writeFATEntry(secondToLast, newCluster);
8387c478bd9Sstevel@tonic-gate 	}
8397c478bd9Sstevel@tonic-gate 	markLastInFAT(newCluster);
8407c478bd9Sstevel@tonic-gate }
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate static void
createNewEndOfDirectory(int fd,struct pcdir * entry,int32_t secondToLast)8437c478bd9Sstevel@tonic-gate createNewEndOfDirectory(int fd, struct pcdir *entry, int32_t secondToLast)
8447c478bd9Sstevel@tonic-gate {
8457c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
8467c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
8477c478bd9Sstevel@tonic-gate 	int32_t freeCluster;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	if (((freeCluster = findAFreeCluster(secondToLast)) != 0)) {
8507c478bd9Sstevel@tonic-gate 		if (readCluster(fd, freeCluster, &(dirdata.bytes),
8517c478bd9Sstevel@tonic-gate 		    &dirdatasize, RDCLUST_DO_CACHE) == RDCLUST_GOOD) {
8527c478bd9Sstevel@tonic-gate 			if (Verbose) {
8537c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
8547c478bd9Sstevel@tonic-gate 				    gettext("Grabbed allocation unit #%d "
8557c478bd9Sstevel@tonic-gate 				    "for truncated\ndirectory's new end "
8567c478bd9Sstevel@tonic-gate 				    "of directory.\n"), freeCluster);
8577c478bd9Sstevel@tonic-gate 			}
8587c478bd9Sstevel@tonic-gate 			makeNewEndOfDirectory(entry, secondToLast,
8597c478bd9Sstevel@tonic-gate 			    freeCluster, &dirdata);
8607c478bd9Sstevel@tonic-gate 			return;
8617c478bd9Sstevel@tonic-gate 		}
8627c478bd9Sstevel@tonic-gate 	}
8637c478bd9Sstevel@tonic-gate 	if (secondToLast == 0) {
8647c478bd9Sstevel@tonic-gate 		if (freeCluster == 0) {
8657c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("File system full.\n"));
8667c478bd9Sstevel@tonic-gate 		} else {
8677c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
8687c478bd9Sstevel@tonic-gate 			    gettext("Unable to read allocation unit %d.\n"),
8697c478bd9Sstevel@tonic-gate 			    freeCluster);
8707c478bd9Sstevel@tonic-gate 		}
8717c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8727c478bd9Sstevel@tonic-gate 		    gettext("Cannot allocate a new allocation unit to hold "
8737c478bd9Sstevel@tonic-gate 		    "an end-of-directory marker.\nNo existing directory "
8747c478bd9Sstevel@tonic-gate 		    "entries can be overwritten with the marker,\n"
8757c478bd9Sstevel@tonic-gate 		    "the only unit allocated to the directory is "
8767c478bd9Sstevel@tonic-gate 		    "inaccessible.\nNeeded directory truncation has failed.  "
8777c478bd9Sstevel@tonic-gate 		    "Giving up.\n"));
8787c478bd9Sstevel@tonic-gate 		(void) close(fd);
8797c478bd9Sstevel@tonic-gate 		exit(11);
8807c478bd9Sstevel@tonic-gate 	}
8817c478bd9Sstevel@tonic-gate 	emergencyEndOfDirectory(fd, secondToLast);
8827c478bd9Sstevel@tonic-gate }
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate /*
8857c478bd9Sstevel@tonic-gate  * truncAtCluster
8867c478bd9Sstevel@tonic-gate  *	Given a directory entry and a cluster number, search through
8877c478bd9Sstevel@tonic-gate  *	the cluster chain for the entry and make the cluster previous
8887c478bd9Sstevel@tonic-gate  *	to the given cluster in the chain the last cluster in the file.
8897c478bd9Sstevel@tonic-gate  *	The number of orphaned bytes is returned.  For a chain that's
8907c478bd9Sstevel@tonic-gate  *	a directory we need to do some special handling, since we'll be
8917c478bd9Sstevel@tonic-gate  *	getting rid of the end of directory notice by truncating.
8927c478bd9Sstevel@tonic-gate  */
8937c478bd9Sstevel@tonic-gate static int64_t
truncAtCluster(int fd,struct pcdir * entry,int32_t cluster)8947c478bd9Sstevel@tonic-gate truncAtCluster(int fd, struct pcdir *entry, int32_t cluster)
8957c478bd9Sstevel@tonic-gate {
8967c478bd9Sstevel@tonic-gate 	uint32_t oldSize, newSize;
8977c478bd9Sstevel@tonic-gate 	int32_t prev, count, follow;
8987c478bd9Sstevel@tonic-gate 	int dir = (entry->pcd_attr & PCA_DIR);
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	prev = 0; count = 0;
9017c478bd9Sstevel@tonic-gate 	follow = extractStartCluster(entry);
9027c478bd9Sstevel@tonic-gate 	while (follow != cluster && follow >= FIRST_CLUSTER &&
9037c478bd9Sstevel@tonic-gate 	    follow <= LastCluster) {
9047c478bd9Sstevel@tonic-gate 		prev = follow;
9057c478bd9Sstevel@tonic-gate 		count++;
9067c478bd9Sstevel@tonic-gate 		follow = nextInChain(follow);
9077c478bd9Sstevel@tonic-gate 	}
9087c478bd9Sstevel@tonic-gate 	if (follow != cluster) {
9097c478bd9Sstevel@tonic-gate 		/*
9107c478bd9Sstevel@tonic-gate 		 *  We didn't find the cluster they wanted to trunc at
9117c478bd9Sstevel@tonic-gate 		 *  anywhere in the entry's chain.  So we'll leave the
9127c478bd9Sstevel@tonic-gate 		 *  entry alone, and return a negative value so they
9137c478bd9Sstevel@tonic-gate 		 *  can know something is wrong.
9147c478bd9Sstevel@tonic-gate 		 */
9157c478bd9Sstevel@tonic-gate 		return (-1);
9167c478bd9Sstevel@tonic-gate 	}
9177c478bd9Sstevel@tonic-gate 	if (Verbose) {
9187c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
9197c478bd9Sstevel@tonic-gate 		    gettext("Chain truncation at unit #%d\n"), cluster);
9207c478bd9Sstevel@tonic-gate 	}
9217c478bd9Sstevel@tonic-gate 	if (!dir) {
9227c478bd9Sstevel@tonic-gate 		oldSize = extractSize(entry);
9237c478bd9Sstevel@tonic-gate 		newSize = count *
9247c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.sectors_per_cluster *
9257c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
9267c478bd9Sstevel@tonic-gate 		if (newSize == 0)
9277c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(entry, 0);
9287c478bd9Sstevel@tonic-gate 	} else {
9297c478bd9Sstevel@tonic-gate 		newSize = 0;
9307c478bd9Sstevel@tonic-gate 	}
9317c478bd9Sstevel@tonic-gate 	updateDirEnt_Size(entry, newSize);
9327c478bd9Sstevel@tonic-gate 	if (dir) {
9337c478bd9Sstevel@tonic-gate 		createNewEndOfDirectory(fd, entry, prev);
9347c478bd9Sstevel@tonic-gate 	} else if (prev != 0) {
9357c478bd9Sstevel@tonic-gate 		markLastInFAT(prev);
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 	if (dir) {
9387c478bd9Sstevel@tonic-gate 		/*
9397c478bd9Sstevel@tonic-gate 		 * We don't really know what the size of a directory is
9407c478bd9Sstevel@tonic-gate 		 * but it is important for us to know if this truncation
9417c478bd9Sstevel@tonic-gate 		 * results in an orphan with any size.  The value we
9427c478bd9Sstevel@tonic-gate 		 * return from this routine for a normal file is the
9437c478bd9Sstevel@tonic-gate 		 * number of bytes left in the chain.  For a directory
9447c478bd9Sstevel@tonic-gate 		 * we can't be exact, and the caller doesn't really
9457c478bd9Sstevel@tonic-gate 		 * expect us to be.  For a directory the caller only
9467c478bd9Sstevel@tonic-gate 		 * cares if there are zero bytes left or more than
9477c478bd9Sstevel@tonic-gate 		 * zero bytes left.  We'll return 1 to indicate
9487c478bd9Sstevel@tonic-gate 		 * more than zero.
9497c478bd9Sstevel@tonic-gate 		 */
9507c478bd9Sstevel@tonic-gate 		if ((follow = nextInChain(follow)) != 0)
9517c478bd9Sstevel@tonic-gate 			return (1);
9527c478bd9Sstevel@tonic-gate 		else
9537c478bd9Sstevel@tonic-gate 			return (0);
9547c478bd9Sstevel@tonic-gate 	}
9557c478bd9Sstevel@tonic-gate 	/*
9567c478bd9Sstevel@tonic-gate 	 * newSize should always be smaller than the old one, since
9577c478bd9Sstevel@tonic-gate 	 * we are decreasing the number of clusters allocated to the file.
9587c478bd9Sstevel@tonic-gate 	 */
9597c478bd9Sstevel@tonic-gate 	return ((int64_t)oldSize - (int64_t)newSize);
9607c478bd9Sstevel@tonic-gate }
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate static struct pcdir *
updateOrphanedChainMetadata(int fd,struct pcdir * dp,int32_t endCluster,int isBad)9637c478bd9Sstevel@tonic-gate updateOrphanedChainMetadata(int fd, struct pcdir *dp, int32_t endCluster,
9647c478bd9Sstevel@tonic-gate     int isBad)
9657c478bd9Sstevel@tonic-gate {
9667c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
9677c478bd9Sstevel@tonic-gate 	int64_t remainder;
9687c478bd9Sstevel@tonic-gate 	char *newName = NULL;
9697c478bd9Sstevel@tonic-gate 	int chosenName;
9707c478bd9Sstevel@tonic-gate 	int dir = (dp->pcd_attr & PCA_DIR);
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	/*
9737c478bd9Sstevel@tonic-gate 	 *  If the truncation fails, (which ought not to happen),
9747c478bd9Sstevel@tonic-gate 	 *  there's no need to go any further, we just return
9757c478bd9Sstevel@tonic-gate 	 *  a null value for the new directory entry pointer.
9767c478bd9Sstevel@tonic-gate 	 */
9777c478bd9Sstevel@tonic-gate 	remainder = truncAtCluster(fd, dp, endCluster);
9787c478bd9Sstevel@tonic-gate 	if (remainder < 0)
9797c478bd9Sstevel@tonic-gate 		return (ndp);
9807c478bd9Sstevel@tonic-gate 	if (!dir && isBad) {
9817c478bd9Sstevel@tonic-gate 		/*
9827c478bd9Sstevel@tonic-gate 		 *  Subtract out the bad cluster from the remaining size
9837c478bd9Sstevel@tonic-gate 		 *  We always assume the cluster being deleted from the
9847c478bd9Sstevel@tonic-gate 		 *  file is full size, but that might not be the case
9857c478bd9Sstevel@tonic-gate 		 *  for the last cluster of the file, so that is why
9867c478bd9Sstevel@tonic-gate 		 *  we check for negative remainder value.
9877c478bd9Sstevel@tonic-gate 		 */
9887c478bd9Sstevel@tonic-gate 		remainder -= TheBIOSParameterBlock.bpb.sectors_per_cluster *
9897c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
9907c478bd9Sstevel@tonic-gate 		if (remainder < 0)
9917c478bd9Sstevel@tonic-gate 			remainder = 0;
9927c478bd9Sstevel@tonic-gate 	}
9937c478bd9Sstevel@tonic-gate 	/*
9947c478bd9Sstevel@tonic-gate 	 * Build a new directory entry for the rest of the chain.
9957c478bd9Sstevel@tonic-gate 	 * Later, if the user okays it, we'll link this entry into the
9967c478bd9Sstevel@tonic-gate 	 * root directory.  The new entry will start out as a
9977c478bd9Sstevel@tonic-gate 	 * copy of the truncated entry.
9987c478bd9Sstevel@tonic-gate 	 */
9997c478bd9Sstevel@tonic-gate 	if ((remainder != 0) &&
10007c478bd9Sstevel@tonic-gate 	    ((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
10017c478bd9Sstevel@tonic-gate 	    ((ndp = newDirEnt(dp)) != NULL)) {
10027c478bd9Sstevel@tonic-gate 		if (Verbose) {
10037c478bd9Sstevel@tonic-gate 			if (dir)
10047c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
10057c478bd9Sstevel@tonic-gate 				    gettext("Orphaned directory chain.\n"));
10067c478bd9Sstevel@tonic-gate 			else
10077c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
10087c478bd9Sstevel@tonic-gate 				    gettext("Orphaned chain, %u bytes.\n"),
10097c478bd9Sstevel@tonic-gate 				    (uint32_t)remainder);
10107c478bd9Sstevel@tonic-gate 		}
10117c478bd9Sstevel@tonic-gate 		if (!dir)
10127c478bd9Sstevel@tonic-gate 			updateDirEnt_Size(ndp, (uint32_t)remainder);
10137c478bd9Sstevel@tonic-gate 		if (isBad)
10147c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, nextInChain(endCluster));
10157c478bd9Sstevel@tonic-gate 		else
10167c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, endCluster);
10177c478bd9Sstevel@tonic-gate 		updateDirEnt_Name(ndp, newName);
10187c478bd9Sstevel@tonic-gate 		addEntryToCHKList(chosenName);
10197c478bd9Sstevel@tonic-gate 	}
10207c478bd9Sstevel@tonic-gate 	return (ndp);
10217c478bd9Sstevel@tonic-gate }
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate /*
10247c478bd9Sstevel@tonic-gate  *  splitChain()
10257c478bd9Sstevel@tonic-gate  *
10267c478bd9Sstevel@tonic-gate  *	split a cluster allocation chain into two cluster chains
10277c478bd9Sstevel@tonic-gate  *	around a given cluster (problemCluster).  This results in two
10287c478bd9Sstevel@tonic-gate  *	separate directory entries; the original (dp), and one we hope
10297c478bd9Sstevel@tonic-gate  *	to create and return a pointer to to the caller (*newdp).
10307c478bd9Sstevel@tonic-gate  *	This second entry is the orphan chain, and it may end up in
10317c478bd9Sstevel@tonic-gate  *	the root directory as a FILEnnnn.CHK file.  We also return the
10327c478bd9Sstevel@tonic-gate  *	starting cluster of the orphan chain to the caller (*orphanStart).
10337c478bd9Sstevel@tonic-gate  */
10347c478bd9Sstevel@tonic-gate void
splitChain(int fd,struct pcdir * dp,int32_t problemCluster,struct pcdir ** newdp,int32_t * orphanStart)10357c478bd9Sstevel@tonic-gate splitChain(int fd, struct pcdir *dp, int32_t problemCluster,
10367c478bd9Sstevel@tonic-gate     struct pcdir **newdp, int32_t *orphanStart)
10377c478bd9Sstevel@tonic-gate {
10387c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
10397c478bd9Sstevel@tonic-gate 	int isBad = isMarkedBad(problemCluster);
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	ndp = updateOrphanedChainMetadata(fd, dp, problemCluster, isBad);
10427c478bd9Sstevel@tonic-gate 	*newdp = ndp;
10437c478bd9Sstevel@tonic-gate 	clearInUse(problemCluster);
10447c478bd9Sstevel@tonic-gate 	if (isBad) {
10457c478bd9Sstevel@tonic-gate 		clearOrphan(problemCluster);
10467c478bd9Sstevel@tonic-gate 		*orphanStart = nextInChain(problemCluster);
10477c478bd9Sstevel@tonic-gate 		orphanChain(fd, *orphanStart, ndp);
10487c478bd9Sstevel@tonic-gate 		markBadInFAT(problemCluster);
10497c478bd9Sstevel@tonic-gate 	} else {
10507c478bd9Sstevel@tonic-gate 		*orphanStart = problemCluster;
10517c478bd9Sstevel@tonic-gate 		orphanChain(fd, problemCluster, ndp);
10527c478bd9Sstevel@tonic-gate 	}
10537c478bd9Sstevel@tonic-gate }
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate /*
10567c478bd9Sstevel@tonic-gate  *  freeOrphan
10577c478bd9Sstevel@tonic-gate  *
10587c478bd9Sstevel@tonic-gate  *  User has requested that an orphaned cluster chain be freed back
10597c478bd9Sstevel@tonic-gate  *  into the file area.
10607c478bd9Sstevel@tonic-gate  */
10617c478bd9Sstevel@tonic-gate static void
freeOrphan(int32_t c)10627c478bd9Sstevel@tonic-gate freeOrphan(int32_t c)
10637c478bd9Sstevel@tonic-gate {
10647c478bd9Sstevel@tonic-gate 	int32_t n;
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	/*
10677c478bd9Sstevel@tonic-gate 	 * Free the directory entry we explicitly created for
10687c478bd9Sstevel@tonic-gate 	 * the orphaned clusters.
10697c478bd9Sstevel@tonic-gate 	 */
10707c478bd9Sstevel@tonic-gate 	if (InUse[c - FIRST_CLUSTER]->dirent != NULL)
10717c478bd9Sstevel@tonic-gate 		free(InUse[c - FIRST_CLUSTER]->dirent);
10727c478bd9Sstevel@tonic-gate 	/*
10737c478bd9Sstevel@tonic-gate 	 * Then mark the clusters themselves as available.
10747c478bd9Sstevel@tonic-gate 	 */
10757c478bd9Sstevel@tonic-gate 	do {
10767c478bd9Sstevel@tonic-gate 		n = nextInChain(c);
10777c478bd9Sstevel@tonic-gate 		markFreeInFAT(c);
10787c478bd9Sstevel@tonic-gate 		markFree(c);
10797c478bd9Sstevel@tonic-gate 		c = n;
10807c478bd9Sstevel@tonic-gate 	} while (c != 0);
10817c478bd9Sstevel@tonic-gate }
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate /*
10847c478bd9Sstevel@tonic-gate  *  Rewrite the InUse field for a cluster chain.  Can be used on a partial
10857c478bd9Sstevel@tonic-gate  *  chain if provided with a stopAtCluster.
10867c478bd9Sstevel@tonic-gate  */
10877c478bd9Sstevel@tonic-gate static void
redoInUse(int fd,int32_t c,struct pcdir * ndp,int32_t stopAtCluster)10887c478bd9Sstevel@tonic-gate redoInUse(int fd, int32_t c, struct pcdir *ndp, int32_t stopAtCluster)
10897c478bd9Sstevel@tonic-gate {
10907c478bd9Sstevel@tonic-gate 	while (c && c != stopAtCluster) {
10917c478bd9Sstevel@tonic-gate 		clearInUse(c);
10927c478bd9Sstevel@tonic-gate 		(void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, NULL);
10937c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
10947c478bd9Sstevel@tonic-gate 	}
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate static struct pcdir *
orphanDirEntLookup(int32_t clusterNum)10987c478bd9Sstevel@tonic-gate orphanDirEntLookup(int32_t clusterNum)
10997c478bd9Sstevel@tonic-gate {
11007c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
11017c478bd9Sstevel@tonic-gate 		return (NULL);
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	if (isInUse(clusterNum)) {
11047c478bd9Sstevel@tonic-gate 		return (InUse[clusterNum - FIRST_CLUSTER]->dirent);
11057c478bd9Sstevel@tonic-gate 	} else {
11067c478bd9Sstevel@tonic-gate 		return (NULL);
11077c478bd9Sstevel@tonic-gate 	}
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate static int32_t
orphanSizeLookup(int32_t clusterNum)11117c478bd9Sstevel@tonic-gate orphanSizeLookup(int32_t clusterNum)
11127c478bd9Sstevel@tonic-gate {
11137c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
11147c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
11157c478bd9Sstevel@tonic-gate 		return (-1);
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	if (isInUse(clusterNum)) {
11187c478bd9Sstevel@tonic-gate 		return (extractSize(InUse[clusterNum - FIRST_CLUSTER]->dirent));
11197c478bd9Sstevel@tonic-gate 	} else {
11207c478bd9Sstevel@tonic-gate 		return (-1);
11217c478bd9Sstevel@tonic-gate 	}
11227c478bd9Sstevel@tonic-gate }
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate /*
11257c478bd9Sstevel@tonic-gate  *  linkOrphan
11267c478bd9Sstevel@tonic-gate  *
11277c478bd9Sstevel@tonic-gate  *  User has requested that an orphaned cluster chain be brought back
11287c478bd9Sstevel@tonic-gate  *  into the file system.  So we have to make a new directory entry
11297c478bd9Sstevel@tonic-gate  *  in the root directory and point it at the cluster chain.
11307c478bd9Sstevel@tonic-gate  */
11317c478bd9Sstevel@tonic-gate static void
linkOrphan(int fd,int32_t start)11327c478bd9Sstevel@tonic-gate linkOrphan(int fd, int32_t start)
11337c478bd9Sstevel@tonic-gate {
11347c478bd9Sstevel@tonic-gate 	struct pcdir *newEnt = NULL;
11357c478bd9Sstevel@tonic-gate 	struct pcdir *dp;
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 	if ((dp = orphanDirEntLookup(start)) != NULL) {
11387c478bd9Sstevel@tonic-gate 		newEnt = addRootDirEnt(fd, dp);
11397c478bd9Sstevel@tonic-gate 	} else {
11407c478bd9Sstevel@tonic-gate 		(void) printf(gettext("Re-link of orphaned chain failed."
11417c478bd9Sstevel@tonic-gate 		    "  Allocation units will remain orphaned.\n"));
11427c478bd9Sstevel@tonic-gate 	}
11437c478bd9Sstevel@tonic-gate 	/*
11447c478bd9Sstevel@tonic-gate 	 *  A cluster isn't really InUse() unless it is referenced,
11457c478bd9Sstevel@tonic-gate 	 *  so if newEnt is NULL here, we are in effect using markInUse()
11467c478bd9Sstevel@tonic-gate 	 *  to note that the cluster is NOT in use.
11477c478bd9Sstevel@tonic-gate 	 */
11487c478bd9Sstevel@tonic-gate 	redoInUse(fd, start, newEnt, 0);
11497c478bd9Sstevel@tonic-gate }
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate /*
11527c478bd9Sstevel@tonic-gate  *  relinkCreatedOrphans
11537c478bd9Sstevel@tonic-gate  *
11547c478bd9Sstevel@tonic-gate  *  While marking clusters as bad, we can create orphan cluster
11557c478bd9Sstevel@tonic-gate  *  chains.  Since we were the ones doing the marking, we were able to
11567c478bd9Sstevel@tonic-gate  *  keep track of the orphans we created.  Now we want to go through
11577c478bd9Sstevel@tonic-gate  *  all those chains and either get them back into the file system or
11587c478bd9Sstevel@tonic-gate  *  free them depending on the user's input.
11597c478bd9Sstevel@tonic-gate  */
11607c478bd9Sstevel@tonic-gate static void
relinkCreatedOrphans(int fd)11617c478bd9Sstevel@tonic-gate relinkCreatedOrphans(int fd)
11627c478bd9Sstevel@tonic-gate {
11637c478bd9Sstevel@tonic-gate 	int32_t c;
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
11667c478bd9Sstevel@tonic-gate 		if (isMarkedOrphan(c)) {
11677c478bd9Sstevel@tonic-gate 			if (OkayToRelink && askAboutRelink(c)) {
11687c478bd9Sstevel@tonic-gate 				linkOrphan(fd, c);
11697c478bd9Sstevel@tonic-gate 			} else if (askAboutFreeing(c)) {
11707c478bd9Sstevel@tonic-gate 				freeOrphan(c);
11717c478bd9Sstevel@tonic-gate 			}
11727c478bd9Sstevel@tonic-gate 			clearOrphan(c);
11737c478bd9Sstevel@tonic-gate 		}
11747c478bd9Sstevel@tonic-gate 	}
11757c478bd9Sstevel@tonic-gate }
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate /*
11787c478bd9Sstevel@tonic-gate  *  relinkFATOrphans
11797c478bd9Sstevel@tonic-gate  *
11807c478bd9Sstevel@tonic-gate  *  We want to find orphans not represented in the meta-data.
11817c478bd9Sstevel@tonic-gate  *  These are chains marked in the FAT as being in use but
11827c478bd9Sstevel@tonic-gate  *  not referenced anywhere by any directory entries.
11837c478bd9Sstevel@tonic-gate  *  We'll go through the whole FAT and mark the first cluster
11847c478bd9Sstevel@tonic-gate  *  in any such chain as an orphan.  Then we can just use
11857c478bd9Sstevel@tonic-gate  *  the relinkCreatedOrphans routine to get them back into the
11867c478bd9Sstevel@tonic-gate  *  file system or free'ed depending on the user's input.
11877c478bd9Sstevel@tonic-gate  */
11887c478bd9Sstevel@tonic-gate static void
relinkFATOrphans(int fd)11897c478bd9Sstevel@tonic-gate relinkFATOrphans(int fd)
11907c478bd9Sstevel@tonic-gate {
11917c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
1192