xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/fsck/clusters.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
24*7c478bd9Sstevel@tonic-gate  * All rights reserved.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * fsck_pcfs -- routines for manipulating clusters.
31*7c478bd9Sstevel@tonic-gate  */
32*7c478bd9Sstevel@tonic-gate #include <stdio.h>
33*7c478bd9Sstevel@tonic-gate #include <string.h>
34*7c478bd9Sstevel@tonic-gate #include <unistd.h>
35*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
36*7c478bd9Sstevel@tonic-gate #include <libintl.h>
37*7c478bd9Sstevel@tonic-gate #include <errno.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
42*7c478bd9Sstevel@tonic-gate #include "pcfs_common.h"
43*7c478bd9Sstevel@tonic-gate #include "fsck_pcfs.h"
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate extern	ClusterContents	TheRootDir;
46*7c478bd9Sstevel@tonic-gate extern	off64_t		FirstClusterOffset;
47*7c478bd9Sstevel@tonic-gate extern	off64_t		PartitionOffset;
48*7c478bd9Sstevel@tonic-gate extern	int32_t		BytesPerCluster;
49*7c478bd9Sstevel@tonic-gate extern	int32_t		TotalClusters;
50*7c478bd9Sstevel@tonic-gate extern	int32_t		LastCluster;
51*7c478bd9Sstevel@tonic-gate extern	int32_t		RootDirSize;
52*7c478bd9Sstevel@tonic-gate extern	int32_t		FATSize;
53*7c478bd9Sstevel@tonic-gate extern	bpb_t		TheBIOSParameterBlock;
54*7c478bd9Sstevel@tonic-gate extern	short		FATEntrySize;
55*7c478bd9Sstevel@tonic-gate extern	int		RootDirModified;
56*7c478bd9Sstevel@tonic-gate extern	int		OkayToRelink;
57*7c478bd9Sstevel@tonic-gate extern	int		ReadOnly;
58*7c478bd9Sstevel@tonic-gate extern	int		IsFAT32;
59*7c478bd9Sstevel@tonic-gate extern	int		Verbose;
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate static	struct pcdir	BlankPCDIR;
62*7c478bd9Sstevel@tonic-gate static	CachedCluster	*ClusterCache;
63*7c478bd9Sstevel@tonic-gate static	ClusterInfo	**InUse;
64*7c478bd9Sstevel@tonic-gate static	int32_t		ReservedClusterCount;
65*7c478bd9Sstevel@tonic-gate static	int32_t		AllocedClusterCount;
66*7c478bd9Sstevel@tonic-gate static	int32_t		FreeClusterCount;
67*7c478bd9Sstevel@tonic-gate static	int32_t		BadClusterCount;
68*7c478bd9Sstevel@tonic-gate 
69*7c478bd9Sstevel@tonic-gate /*
70*7c478bd9Sstevel@tonic-gate  * Internal statistics
71*7c478bd9Sstevel@tonic-gate  */
72*7c478bd9Sstevel@tonic-gate static	int32_t		CachedClusterCount;
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate int32_t	HiddenClusterCount;
75*7c478bd9Sstevel@tonic-gate int32_t	FileClusterCount;
76*7c478bd9Sstevel@tonic-gate int32_t	DirClusterCount;
77*7c478bd9Sstevel@tonic-gate int32_t	HiddenFileCount;
78*7c478bd9Sstevel@tonic-gate int32_t	FileCount;
79*7c478bd9Sstevel@tonic-gate int32_t	DirCount;
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate static int32_t orphanSizeLookup(int32_t clusterNum);
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate static void
84*7c478bd9Sstevel@tonic-gate freeNameInfo(int32_t clusterNum)
85*7c478bd9Sstevel@tonic-gate {
86*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
87*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
88*7c478bd9Sstevel@tonic-gate 		return;
89*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
90*7c478bd9Sstevel@tonic-gate 		if (InUse[clusterNum - FIRST_CLUSTER]->path->references > 1) {
91*7c478bd9Sstevel@tonic-gate 			InUse[clusterNum - FIRST_CLUSTER]->path->references--;
92*7c478bd9Sstevel@tonic-gate 		} else {
93*7c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
94*7c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->path);
95*7c478bd9Sstevel@tonic-gate 		}
96*7c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER]->path = NULL;
97*7c478bd9Sstevel@tonic-gate 	}
98*7c478bd9Sstevel@tonic-gate }
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate static void
101*7c478bd9Sstevel@tonic-gate printOrphanPath(int32_t clusterNum)
102*7c478bd9Sstevel@tonic-gate {
103*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
104*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
105*7c478bd9Sstevel@tonic-gate 		return;
106*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
107*7c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\nOrphaned allocation units originally "
108*7c478bd9Sstevel@tonic-gate 		    "allocated to:\n"));
109*7c478bd9Sstevel@tonic-gate 		(void) printf("%s\n",
110*7c478bd9Sstevel@tonic-gate 		    InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
111*7c478bd9Sstevel@tonic-gate 		freeNameInfo(clusterNum);
112*7c478bd9Sstevel@tonic-gate 	} else {
113*7c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\nOrphaned allocation units originally "
114*7c478bd9Sstevel@tonic-gate 		    "allocated to an unknown file or directory:\n"));
115*7c478bd9Sstevel@tonic-gate 		(void) printf(gettext("Orphaned chain begins with allocation "
116*7c478bd9Sstevel@tonic-gate 		    "unit %d.\n"), clusterNum);
117*7c478bd9Sstevel@tonic-gate 	}
118*7c478bd9Sstevel@tonic-gate }
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate static void
121*7c478bd9Sstevel@tonic-gate printOrphanSize(int32_t clusterNum)
122*7c478bd9Sstevel@tonic-gate {
123*7c478bd9Sstevel@tonic-gate 	int32_t size = orphanSizeLookup(clusterNum);
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate 	if (size > 0) {
126*7c478bd9Sstevel@tonic-gate 		(void) printf(gettext("%d bytes in the orphaned chain of "
127*7c478bd9Sstevel@tonic-gate 		    "allocation units.\n"), size);
128*7c478bd9Sstevel@tonic-gate 		if (Verbose) {
129*7c478bd9Sstevel@tonic-gate 			(void) printf(gettext("[Starting at allocation "
130*7c478bd9Sstevel@tonic-gate 			    "unit %d]\n"), clusterNum);
131*7c478bd9Sstevel@tonic-gate 		}
132*7c478bd9Sstevel@tonic-gate 	}
133*7c478bd9Sstevel@tonic-gate }
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate static void
136*7c478bd9Sstevel@tonic-gate printOrphanInfo(int32_t clusterNum)
137*7c478bd9Sstevel@tonic-gate {
138*7c478bd9Sstevel@tonic-gate 	printOrphanPath(clusterNum);
139*7c478bd9Sstevel@tonic-gate 	printOrphanSize(clusterNum);
140*7c478bd9Sstevel@tonic-gate }
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate static int
143*7c478bd9Sstevel@tonic-gate askAboutFreeing(int32_t clusterNum)
144*7c478bd9Sstevel@tonic-gate {
145*7c478bd9Sstevel@tonic-gate 	/*
146*7c478bd9Sstevel@tonic-gate 	 * If it is not OkayToRelink, we haven't already printed the size
147*7c478bd9Sstevel@tonic-gate 	 * of the orphaned chain.
148*7c478bd9Sstevel@tonic-gate 	 */
149*7c478bd9Sstevel@tonic-gate 	if (!OkayToRelink)
150*7c478bd9Sstevel@tonic-gate 		printOrphanInfo(clusterNum);
151*7c478bd9Sstevel@tonic-gate 	/*
152*7c478bd9Sstevel@tonic-gate 	 *  If we are in preen mode, preenBail won't return.
153*7c478bd9Sstevel@tonic-gate 	 */
154*7c478bd9Sstevel@tonic-gate 	preenBail("Need user confirmation to free orphaned chain.\n");
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate 	(void) printf(
157*7c478bd9Sstevel@tonic-gate 	    gettext("Free the allocation units in the orphaned chain ? "
158*7c478bd9Sstevel@tonic-gate 	    "(y/n) "));
159*7c478bd9Sstevel@tonic-gate 	return (yes());
160*7c478bd9Sstevel@tonic-gate }
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate static int
163*7c478bd9Sstevel@tonic-gate askAboutRelink(int32_t clusterNum)
164*7c478bd9Sstevel@tonic-gate {
165*7c478bd9Sstevel@tonic-gate 	/*
166*7c478bd9Sstevel@tonic-gate 	 * Display the size of the chain for the user to consider.
167*7c478bd9Sstevel@tonic-gate 	 */
168*7c478bd9Sstevel@tonic-gate 	printOrphanInfo(clusterNum);
169*7c478bd9Sstevel@tonic-gate 	/*
170*7c478bd9Sstevel@tonic-gate 	 *  If we are in preen mode, preenBail won't return.
171*7c478bd9Sstevel@tonic-gate 	 */
172*7c478bd9Sstevel@tonic-gate 	preenBail("Need user confirmation to re-link orphaned chain.\n");
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate 	(void) printf(gettext("Re-link orphaned chain into file system ? "
175*7c478bd9Sstevel@tonic-gate 	    "(y/n) "));
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate 	return (yes());
178*7c478bd9Sstevel@tonic-gate }
179*7c478bd9Sstevel@tonic-gate 
180*7c478bd9Sstevel@tonic-gate static int
181*7c478bd9Sstevel@tonic-gate isHidden(int32_t clusterNum)
182*7c478bd9Sstevel@tonic-gate {
183*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
184*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
185*7c478bd9Sstevel@tonic-gate 		return (0);
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
188*7c478bd9Sstevel@tonic-gate 		return (0);
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_HIDDEN);
191*7c478bd9Sstevel@tonic-gate }
192*7c478bd9Sstevel@tonic-gate 
193*7c478bd9Sstevel@tonic-gate static int
194*7c478bd9Sstevel@tonic-gate isInUse(int32_t clusterNum)
195*7c478bd9Sstevel@tonic-gate {
196*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
197*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
198*7c478bd9Sstevel@tonic-gate 		return (0);
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate 	return ((InUse[clusterNum - FIRST_CLUSTER] != NULL) &&
201*7c478bd9Sstevel@tonic-gate 		(InUse[clusterNum - FIRST_CLUSTER]->dirent != NULL));
202*7c478bd9Sstevel@tonic-gate }
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate /*
205*7c478bd9Sstevel@tonic-gate  *  Caller's may request that we cache the data from a readCluster.
206*7c478bd9Sstevel@tonic-gate  *  The xxxClusterxxxCachexxx routines handle looking for cached data
207*7c478bd9Sstevel@tonic-gate  *  or initially caching the data.
208*7c478bd9Sstevel@tonic-gate  *
209*7c478bd9Sstevel@tonic-gate  *  XXX - facilitate releasing cached data for low memory situations.
210*7c478bd9Sstevel@tonic-gate  */
211*7c478bd9Sstevel@tonic-gate static CachedCluster *
212*7c478bd9Sstevel@tonic-gate findClusterCacheEntry(int32_t clusterNum)
213*7c478bd9Sstevel@tonic-gate {
214*7c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
215*7c478bd9Sstevel@tonic-gate 
216*7c478bd9Sstevel@tonic-gate 	while (loop != NULL) {
217*7c478bd9Sstevel@tonic-gate 		if (loop->clusterNum == clusterNum)
218*7c478bd9Sstevel@tonic-gate 			return (loop);
219*7c478bd9Sstevel@tonic-gate 		loop = loop->next;
220*7c478bd9Sstevel@tonic-gate 	}
221*7c478bd9Sstevel@tonic-gate 	return (NULL);
222*7c478bd9Sstevel@tonic-gate }
223*7c478bd9Sstevel@tonic-gate 
224*7c478bd9Sstevel@tonic-gate static uchar_t *
225*7c478bd9Sstevel@tonic-gate findClusterDataInTheCache(int32_t clusterNum)
226*7c478bd9Sstevel@tonic-gate {
227*7c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	while (loop) {
230*7c478bd9Sstevel@tonic-gate 		if (loop->clusterNum == clusterNum)
231*7c478bd9Sstevel@tonic-gate 			return (loop->clusterData.bytes);
232*7c478bd9Sstevel@tonic-gate 		loop = loop->next;
233*7c478bd9Sstevel@tonic-gate 	}
234*7c478bd9Sstevel@tonic-gate 	return (NULL);
235*7c478bd9Sstevel@tonic-gate }
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate static uchar_t *
238*7c478bd9Sstevel@tonic-gate addToCache(int32_t clusterNum, uchar_t *buf, int32_t *datasize)
239*7c478bd9Sstevel@tonic-gate {
240*7c478bd9Sstevel@tonic-gate 	CachedCluster *new;
241*7c478bd9Sstevel@tonic-gate 	uchar_t *cp;
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate 	if ((new = (CachedCluster *)malloc(sizeof (CachedCluster))) == NULL) {
244*7c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for cached cluster info"));
245*7c478bd9Sstevel@tonic-gate 		return (buf);
246*7c478bd9Sstevel@tonic-gate 	}
247*7c478bd9Sstevel@tonic-gate 	new->clusterNum = clusterNum;
248*7c478bd9Sstevel@tonic-gate 	new->modified = 0;
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate 	if ((cp = (uchar_t *)calloc(1, BytesPerCluster)) == NULL) {
251*7c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for cached copy of cluster"));
252*7c478bd9Sstevel@tonic-gate 		free(new);
253*7c478bd9Sstevel@tonic-gate 		return (buf);
254*7c478bd9Sstevel@tonic-gate 	}
255*7c478bd9Sstevel@tonic-gate 	(void) memcpy(cp, buf, *datasize);
256*7c478bd9Sstevel@tonic-gate 	new->clusterData.bytes = cp;
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate 	if (Verbose) {
259*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
260*7c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d cached.\n"), clusterNum);
261*7c478bd9Sstevel@tonic-gate 	}
262*7c478bd9Sstevel@tonic-gate 	if (ClusterCache == NULL) {
263*7c478bd9Sstevel@tonic-gate 		ClusterCache = new;
264*7c478bd9Sstevel@tonic-gate 		new->next = NULL;
265*7c478bd9Sstevel@tonic-gate 	} else if (new->clusterNum < ClusterCache->clusterNum) {
266*7c478bd9Sstevel@tonic-gate 		new->next = ClusterCache;
267*7c478bd9Sstevel@tonic-gate 		ClusterCache = new;
268*7c478bd9Sstevel@tonic-gate 	} else {
269*7c478bd9Sstevel@tonic-gate 		CachedCluster *loop = ClusterCache;
270*7c478bd9Sstevel@tonic-gate 		CachedCluster *trailer = NULL;
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate 		while (loop && new->clusterNum > loop->clusterNum) {
273*7c478bd9Sstevel@tonic-gate 			trailer = loop;
274*7c478bd9Sstevel@tonic-gate 			loop = loop->next;
275*7c478bd9Sstevel@tonic-gate 		}
276*7c478bd9Sstevel@tonic-gate 		trailer->next = new;
277*7c478bd9Sstevel@tonic-gate 		if (loop) {
278*7c478bd9Sstevel@tonic-gate 			new->next = loop;
279*7c478bd9Sstevel@tonic-gate 		} else {
280*7c478bd9Sstevel@tonic-gate 			new->next = NULL;
281*7c478bd9Sstevel@tonic-gate 		}
282*7c478bd9Sstevel@tonic-gate 	}
283*7c478bd9Sstevel@tonic-gate 	CachedClusterCount++;
284*7c478bd9Sstevel@tonic-gate 	return (new->clusterData.bytes);
285*7c478bd9Sstevel@tonic-gate }
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate static int
288*7c478bd9Sstevel@tonic-gate seekCluster(int fd, int32_t clusterNum)
289*7c478bd9Sstevel@tonic-gate {
290*7c478bd9Sstevel@tonic-gate 	off64_t seekto;
291*7c478bd9Sstevel@tonic-gate 	int saveError;
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate 	seekto = FirstClusterOffset +
294*7c478bd9Sstevel@tonic-gate 	    ((off64_t)clusterNum - FIRST_CLUSTER) * BytesPerCluster;
295*7c478bd9Sstevel@tonic-gate 	if (lseek64(fd, seekto, SEEK_SET) != seekto) {
296*7c478bd9Sstevel@tonic-gate 		saveError = errno;
297*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
298*7c478bd9Sstevel@tonic-gate 		    gettext("Seek to Allocation unit #%d failed: "),
299*7c478bd9Sstevel@tonic-gate 		    clusterNum);
300*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, strerror(saveError));
301*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
302*7c478bd9Sstevel@tonic-gate 		return (0);
303*7c478bd9Sstevel@tonic-gate 	}
304*7c478bd9Sstevel@tonic-gate 	return (1);
305*7c478bd9Sstevel@tonic-gate }
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate /*
308*7c478bd9Sstevel@tonic-gate  *  getcluster
309*7c478bd9Sstevel@tonic-gate  *	Get cluster bytes off the disk.  We always read those bytes into
310*7c478bd9Sstevel@tonic-gate  *	the same static buffer.  If the caller wants his own copy of the
311*7c478bd9Sstevel@tonic-gate  *	data he'll have to make his own copy.  We'll return all the data
312*7c478bd9Sstevel@tonic-gate  *	read, even if it's short of a full cluster.  This is for future use
313*7c478bd9Sstevel@tonic-gate  *	when we might want to relocate any salvagable data from bad clusters.
314*7c478bd9Sstevel@tonic-gate  */
315*7c478bd9Sstevel@tonic-gate static int
316*7c478bd9Sstevel@tonic-gate getCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize)
317*7c478bd9Sstevel@tonic-gate {
318*7c478bd9Sstevel@tonic-gate 	static uchar_t *clusterBuffer = NULL;
319*7c478bd9Sstevel@tonic-gate 	int saveError;
320*7c478bd9Sstevel@tonic-gate 	int try;
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 	*datasize = 0;
323*7c478bd9Sstevel@tonic-gate 	*data = NULL;
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
326*7c478bd9Sstevel@tonic-gate 		return (RDCLUST_BADINPUT);
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 	if (clusterBuffer == NULL &&
329*7c478bd9Sstevel@tonic-gate 	    (clusterBuffer = (uchar_t *)malloc(BytesPerCluster)) == NULL) {
330*7c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for a cluster data buffer"));
331*7c478bd9Sstevel@tonic-gate 		return (RDCLUST_MEMERR);
332*7c478bd9Sstevel@tonic-gate 	}
333*7c478bd9Sstevel@tonic-gate 
334*7c478bd9Sstevel@tonic-gate 	for (try = 0; try < RDCLUST_MAX_RETRY; try++) {
335*7c478bd9Sstevel@tonic-gate 		if (!seekCluster(fd, clusterNum))
336*7c478bd9Sstevel@tonic-gate 			return (RDCLUST_FAIL);
337*7c478bd9Sstevel@tonic-gate 		if ((*datasize = read(fd, clusterBuffer, BytesPerCluster)) ==
338*7c478bd9Sstevel@tonic-gate 		    BytesPerCluster) {
339*7c478bd9Sstevel@tonic-gate 			*data = clusterBuffer;
340*7c478bd9Sstevel@tonic-gate 			return (RDCLUST_GOOD);
341*7c478bd9Sstevel@tonic-gate 		}
342*7c478bd9Sstevel@tonic-gate 	}
343*7c478bd9Sstevel@tonic-gate 	if (*datasize >= 0) {
344*7c478bd9Sstevel@tonic-gate 		*data = clusterBuffer;
345*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
346*7c478bd9Sstevel@tonic-gate 		    gettext("Short read of allocation unit #%d\n"), clusterNum);
347*7c478bd9Sstevel@tonic-gate 	} else {
348*7c478bd9Sstevel@tonic-gate 		saveError = errno;
349*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "Allocation unit %d:", clusterNum);
350*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, strerror(saveError));
351*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
352*7c478bd9Sstevel@tonic-gate 	}
353*7c478bd9Sstevel@tonic-gate 	return (RDCLUST_FAIL);
354*7c478bd9Sstevel@tonic-gate }
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate static void
357*7c478bd9Sstevel@tonic-gate writeCachedCluster(int fd, CachedCluster *clustInfo)
358*7c478bd9Sstevel@tonic-gate {
359*7c478bd9Sstevel@tonic-gate 	ssize_t bytesWritten;
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate 	if (ReadOnly)
362*7c478bd9Sstevel@tonic-gate 		return;
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 	if (Verbose)
365*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
366*7c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d modified.\n"),
367*7c478bd9Sstevel@tonic-gate 		    clustInfo->clusterNum);
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	if (seekCluster(fd, clustInfo->clusterNum) == NULL)
370*7c478bd9Sstevel@tonic-gate 		return;
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 	if ((bytesWritten = write(fd, clustInfo->clusterData.bytes,
373*7c478bd9Sstevel@tonic-gate 	    BytesPerCluster)) != BytesPerCluster) {
374*7c478bd9Sstevel@tonic-gate 		if (bytesWritten < 0) {
375*7c478bd9Sstevel@tonic-gate 			perror(gettext("Failed to write modified "
376*7c478bd9Sstevel@tonic-gate 			    "allocation unit"));
377*7c478bd9Sstevel@tonic-gate 		} else {
378*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
379*7c478bd9Sstevel@tonic-gate 			    gettext("Short write of allocation unit %d\n"),
380*7c478bd9Sstevel@tonic-gate 			    clustInfo->clusterNum);
381*7c478bd9Sstevel@tonic-gate 		}
382*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
383*7c478bd9Sstevel@tonic-gate 		exit(13);
384*7c478bd9Sstevel@tonic-gate 	}
385*7c478bd9Sstevel@tonic-gate }
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate /*
388*7c478bd9Sstevel@tonic-gate  * It's cheaper to allocate a lot at a time; malloc overhead pushes
389*7c478bd9Sstevel@tonic-gate  * you over the brink much more quickly if you don't.
390*7c478bd9Sstevel@tonic-gate  * This numbers seems to be a fair trade-off between reduced malloc overhead
391*7c478bd9Sstevel@tonic-gate  * and additional overhead by over-allocating.
392*7c478bd9Sstevel@tonic-gate  */
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate #define	CHUNKSIZE	1024
395*7c478bd9Sstevel@tonic-gate 
396*7c478bd9Sstevel@tonic-gate static ClusterInfo *pool;
397*7c478bd9Sstevel@tonic-gate 
398*7c478bd9Sstevel@tonic-gate static ClusterInfo *
399*7c478bd9Sstevel@tonic-gate newClusterInfo(void)
400*7c478bd9Sstevel@tonic-gate {
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate 	ClusterInfo *ret;
403*7c478bd9Sstevel@tonic-gate 
404*7c478bd9Sstevel@tonic-gate 	if (pool == NULL) {
405*7c478bd9Sstevel@tonic-gate 		int i;
406*7c478bd9Sstevel@tonic-gate 
407*7c478bd9Sstevel@tonic-gate 		pool = (ClusterInfo *)malloc(sizeof (ClusterInfo) * CHUNKSIZE);
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 		if (pool == NULL) {
410*7c478bd9Sstevel@tonic-gate 			perror(
411*7c478bd9Sstevel@tonic-gate 			    gettext("Out of memory for cluster information"));
412*7c478bd9Sstevel@tonic-gate 			exit(9);
413*7c478bd9Sstevel@tonic-gate 		}
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < CHUNKSIZE - 1; i++)
416*7c478bd9Sstevel@tonic-gate 			pool[i].nextfree = &pool[i+1];
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 		pool[CHUNKSIZE-1].nextfree = NULL;
419*7c478bd9Sstevel@tonic-gate 	}
420*7c478bd9Sstevel@tonic-gate 	ret = pool;
421*7c478bd9Sstevel@tonic-gate 	pool = pool->nextfree;
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate 	memset(ret, 0, sizeof (*ret));
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 	return (ret);
426*7c478bd9Sstevel@tonic-gate }
427*7c478bd9Sstevel@tonic-gate 
428*7c478bd9Sstevel@tonic-gate /* Should be called with verified arguments */
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate static ClusterInfo *
431*7c478bd9Sstevel@tonic-gate cloneClusterInfo(int32_t clusterNum)
432*7c478bd9Sstevel@tonic-gate {
433*7c478bd9Sstevel@tonic-gate 	ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 	if (cl->refcnt > 1) {
436*7c478bd9Sstevel@tonic-gate 		ClusterInfo *newCl = newClusterInfo();
437*7c478bd9Sstevel@tonic-gate 		cl->refcnt--;
438*7c478bd9Sstevel@tonic-gate 		*newCl = *cl;
439*7c478bd9Sstevel@tonic-gate 		newCl->refcnt = 1;
440*7c478bd9Sstevel@tonic-gate 		if (newCl->path)
441*7c478bd9Sstevel@tonic-gate 			newCl->path->references++;
442*7c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER] = newCl;
443*7c478bd9Sstevel@tonic-gate 	}
444*7c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]);
445*7c478bd9Sstevel@tonic-gate }
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate static void
448*7c478bd9Sstevel@tonic-gate updateFlags(int32_t clusterNum, int newflags)
449*7c478bd9Sstevel@tonic-gate {
450*7c478bd9Sstevel@tonic-gate 	ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 	if (cl->flags != newflags && cl->refcnt > 1)
453*7c478bd9Sstevel@tonic-gate 		cl = cloneClusterInfo(clusterNum);
454*7c478bd9Sstevel@tonic-gate 
455*7c478bd9Sstevel@tonic-gate 	cl->flags = newflags;
456*7c478bd9Sstevel@tonic-gate }
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate static void
459*7c478bd9Sstevel@tonic-gate freeClusterInfo(ClusterInfo *old)
460*7c478bd9Sstevel@tonic-gate {
461*7c478bd9Sstevel@tonic-gate 	if (--old->refcnt <= 0) {
462*7c478bd9Sstevel@tonic-gate 		if (old->path && --old->path->references <= 0) {
463*7c478bd9Sstevel@tonic-gate 			free(old->path->fullName);
464*7c478bd9Sstevel@tonic-gate 			free(old->path);
465*7c478bd9Sstevel@tonic-gate 		}
466*7c478bd9Sstevel@tonic-gate 		old->nextfree = pool;
467*7c478bd9Sstevel@tonic-gate 		pool = old;
468*7c478bd9Sstevel@tonic-gate 	}
469*7c478bd9Sstevel@tonic-gate }
470*7c478bd9Sstevel@tonic-gate 
471*7c478bd9Sstevel@tonic-gate /*
472*7c478bd9Sstevel@tonic-gate  * Allocate entries in our sparse array of cluster information.
473*7c478bd9Sstevel@tonic-gate  * Returns non-zero if the structure already has been allocated
474*7c478bd9Sstevel@tonic-gate  * (for those keeping score at home).
475*7c478bd9Sstevel@tonic-gate  *
476*7c478bd9Sstevel@tonic-gate  * The template parameter, if non-NULL, is used to facilitate sharing
477*7c478bd9Sstevel@tonic-gate  * the ClusterInfo nodes for the clusters belonging to the same file.
478*7c478bd9Sstevel@tonic-gate  * The first call to allocInUse for a new file should have *template
479*7c478bd9Sstevel@tonic-gate  * set to 0; on return, *template then points to the newly allocated
480*7c478bd9Sstevel@tonic-gate  * ClusterInfo.  Second and further calls keep the same value
481*7c478bd9Sstevel@tonic-gate  * in *template and that ClusterInfo ndoe is then used for all
482*7c478bd9Sstevel@tonic-gate  * entries in the file.  Code that modifies the ClusterInfo nodes
483*7c478bd9Sstevel@tonic-gate  * should take care proper sharing semantics are maintained (i.e.,
484*7c478bd9Sstevel@tonic-gate  * copy-on-write using cloneClusterInfo())
485*7c478bd9Sstevel@tonic-gate  *
486*7c478bd9Sstevel@tonic-gate  * The ClusterInfo used in the template is guaranted to be in use in
487*7c478bd9Sstevel@tonic-gate  * at least one other cluster as we never return a value if we didn't
488*7c478bd9Sstevel@tonic-gate  * set it first.  So we can overwrite it without the possibility of a leak.
489*7c478bd9Sstevel@tonic-gate  */
490*7c478bd9Sstevel@tonic-gate static int
491*7c478bd9Sstevel@tonic-gate allocInUse(int32_t clusterNum, ClusterInfo **template)
492*7c478bd9Sstevel@tonic-gate {
493*7c478bd9Sstevel@tonic-gate 	ClusterInfo *newCl;
494*7c478bd9Sstevel@tonic-gate 
495*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
496*7c478bd9Sstevel@tonic-gate 		return (CLINFO_PREVIOUSLY_ALLOCED);
497*7c478bd9Sstevel@tonic-gate 
498*7c478bd9Sstevel@tonic-gate 	if (template != NULL && *template != NULL)
499*7c478bd9Sstevel@tonic-gate 		newCl = *template;
500*7c478bd9Sstevel@tonic-gate 	else {
501*7c478bd9Sstevel@tonic-gate 		newCl = newClusterInfo();
502*7c478bd9Sstevel@tonic-gate 		if (template)
503*7c478bd9Sstevel@tonic-gate 			*template = newCl;
504*7c478bd9Sstevel@tonic-gate 	}
505*7c478bd9Sstevel@tonic-gate 
506*7c478bd9Sstevel@tonic-gate 	InUse[clusterNum - FIRST_CLUSTER] = newCl;
507*7c478bd9Sstevel@tonic-gate 	newCl->refcnt++;
508*7c478bd9Sstevel@tonic-gate 
509*7c478bd9Sstevel@tonic-gate 	return (CLINFO_NEWLY_ALLOCED);
510*7c478bd9Sstevel@tonic-gate }
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate static void
513*7c478bd9Sstevel@tonic-gate markFree(int32_t clusterNum)
514*7c478bd9Sstevel@tonic-gate {
515*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
516*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
517*7c478bd9Sstevel@tonic-gate 		return;
518*7c478bd9Sstevel@tonic-gate 
519*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER]) {
520*7c478bd9Sstevel@tonic-gate 		if (InUse[clusterNum - FIRST_CLUSTER]->saved)
521*7c478bd9Sstevel@tonic-gate 			free(InUse[clusterNum - FIRST_CLUSTER]->saved);
522*7c478bd9Sstevel@tonic-gate 		freeClusterInfo(InUse[clusterNum - FIRST_CLUSTER]);
523*7c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER] = NULL;
524*7c478bd9Sstevel@tonic-gate 	}
525*7c478bd9Sstevel@tonic-gate }
526*7c478bd9Sstevel@tonic-gate 
527*7c478bd9Sstevel@tonic-gate static void
528*7c478bd9Sstevel@tonic-gate markOrphan(int fd, int32_t clusterNum, struct pcdir *dp)
529*7c478bd9Sstevel@tonic-gate {
530*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
531*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
532*7c478bd9Sstevel@tonic-gate 		return;
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate 	(void) markInUse(fd, clusterNum, dp, NULL, 0, VISIBLE, NULL);
535*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
536*7c478bd9Sstevel@tonic-gate 		updateFlags(clusterNum,
537*7c478bd9Sstevel@tonic-gate 		    InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_ORPHAN);
538*7c478bd9Sstevel@tonic-gate }
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate static void
541*7c478bd9Sstevel@tonic-gate markBad(int32_t clusterNum, uchar_t *recovered, int32_t recoveredLen)
542*7c478bd9Sstevel@tonic-gate {
543*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
544*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
545*7c478bd9Sstevel@tonic-gate 		return;
546*7c478bd9Sstevel@tonic-gate 
547*7c478bd9Sstevel@tonic-gate 	(void) allocInUse(clusterNum, NULL);
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate 	if (recoveredLen) {
550*7c478bd9Sstevel@tonic-gate 		(void) cloneClusterInfo(clusterNum);
551*7c478bd9Sstevel@tonic-gate 		InUse[clusterNum - FIRST_CLUSTER]->saved = recovered;
552*7c478bd9Sstevel@tonic-gate 	}
553*7c478bd9Sstevel@tonic-gate 	updateFlags(clusterNum,
554*7c478bd9Sstevel@tonic-gate 	    InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_BAD);
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 	BadClusterCount++;
557*7c478bd9Sstevel@tonic-gate 	if (Verbose)
558*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
559*7c478bd9Sstevel@tonic-gate 		    gettext("Allocation unit %d marked bad.\n"), clusterNum);
560*7c478bd9Sstevel@tonic-gate }
561*7c478bd9Sstevel@tonic-gate 
562*7c478bd9Sstevel@tonic-gate static void
563*7c478bd9Sstevel@tonic-gate clearOrphan(int32_t c)
564*7c478bd9Sstevel@tonic-gate {
565*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
566*7c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
567*7c478bd9Sstevel@tonic-gate 		return;
568*7c478bd9Sstevel@tonic-gate 	if (InUse[c - FIRST_CLUSTER] != NULL)
569*7c478bd9Sstevel@tonic-gate 		updateFlags(c,
570*7c478bd9Sstevel@tonic-gate 		    InUse[c - FIRST_CLUSTER]->flags & ~CLINFO_ORPHAN);
571*7c478bd9Sstevel@tonic-gate }
572*7c478bd9Sstevel@tonic-gate 
573*7c478bd9Sstevel@tonic-gate static void
574*7c478bd9Sstevel@tonic-gate clearInUse(int32_t c)
575*7c478bd9Sstevel@tonic-gate {
576*7c478bd9Sstevel@tonic-gate 	ClusterInfo **clp;
577*7c478bd9Sstevel@tonic-gate 
578*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
579*7c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
580*7c478bd9Sstevel@tonic-gate 		return;
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 	clp = &InUse[c - FIRST_CLUSTER];
583*7c478bd9Sstevel@tonic-gate 	if (*clp != NULL) {
584*7c478bd9Sstevel@tonic-gate 		freeClusterInfo(*clp);
585*7c478bd9Sstevel@tonic-gate 		*clp = NULL;
586*7c478bd9Sstevel@tonic-gate 	}
587*7c478bd9Sstevel@tonic-gate }
588*7c478bd9Sstevel@tonic-gate 
589*7c478bd9Sstevel@tonic-gate static void
590*7c478bd9Sstevel@tonic-gate clearAllClusters_InUse()
591*7c478bd9Sstevel@tonic-gate {
592*7c478bd9Sstevel@tonic-gate 	int32_t cc;
593*7c478bd9Sstevel@tonic-gate 	for (cc = FIRST_CLUSTER; cc < LastCluster; cc++) {
594*7c478bd9Sstevel@tonic-gate 		clearInUse(cc);
595*7c478bd9Sstevel@tonic-gate 	}
596*7c478bd9Sstevel@tonic-gate }
597*7c478bd9Sstevel@tonic-gate 
598*7c478bd9Sstevel@tonic-gate static void
599*7c478bd9Sstevel@tonic-gate makeUseTable(void)
600*7c478bd9Sstevel@tonic-gate {
601*7c478bd9Sstevel@tonic-gate 	if (InUse != NULL) {
602*7c478bd9Sstevel@tonic-gate 		clearAllClusters_InUse();
603*7c478bd9Sstevel@tonic-gate 		return;
604*7c478bd9Sstevel@tonic-gate 	}
605*7c478bd9Sstevel@tonic-gate 	if ((InUse = (ClusterInfo **)
606*7c478bd9Sstevel@tonic-gate 	    calloc(TotalClusters, sizeof (ClusterInfo *))) == NULL) {
607*7c478bd9Sstevel@tonic-gate 		perror(gettext("No memory for internal table"));
608*7c478bd9Sstevel@tonic-gate 		exit(9);
609*7c478bd9Sstevel@tonic-gate 	}
610*7c478bd9Sstevel@tonic-gate }
611*7c478bd9Sstevel@tonic-gate 
612*7c478bd9Sstevel@tonic-gate static void
613*7c478bd9Sstevel@tonic-gate countClusters(void)
614*7c478bd9Sstevel@tonic-gate {
615*7c478bd9Sstevel@tonic-gate 	int32_t c;
616*7c478bd9Sstevel@tonic-gate 
617*7c478bd9Sstevel@tonic-gate 	BadClusterCount = HiddenClusterCount =
618*7c478bd9Sstevel@tonic-gate 	    AllocedClusterCount = FreeClusterCount = 0;
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
621*7c478bd9Sstevel@tonic-gate 		if (badInFAT(c)) {
622*7c478bd9Sstevel@tonic-gate 			BadClusterCount++;
623*7c478bd9Sstevel@tonic-gate 		} else if (isMarkedBad(c)) {
624*7c478bd9Sstevel@tonic-gate 			/*
625*7c478bd9Sstevel@tonic-gate 			 * This catches the bad sectors found
626*7c478bd9Sstevel@tonic-gate 			 * during thorough verify that have never been
627*7c478bd9Sstevel@tonic-gate 			 * allocated to a file.  Without this check, we
628*7c478bd9Sstevel@tonic-gate 			 * count these guys as free.
629*7c478bd9Sstevel@tonic-gate 			 */
630*7c478bd9Sstevel@tonic-gate 			BadClusterCount++;
631*7c478bd9Sstevel@tonic-gate 			markBadInFAT(c);
632*7c478bd9Sstevel@tonic-gate 		} else if (isHidden(c)) {
633*7c478bd9Sstevel@tonic-gate 			HiddenClusterCount++;
634*7c478bd9Sstevel@tonic-gate 		} else if (isInUse(c)) {
635*7c478bd9Sstevel@tonic-gate 			AllocedClusterCount++;
636*7c478bd9Sstevel@tonic-gate 		} else {
637*7c478bd9Sstevel@tonic-gate 			FreeClusterCount++;
638*7c478bd9Sstevel@tonic-gate 		}
639*7c478bd9Sstevel@tonic-gate 	}
640*7c478bd9Sstevel@tonic-gate }
641*7c478bd9Sstevel@tonic-gate 
642*7c478bd9Sstevel@tonic-gate /*
643*7c478bd9Sstevel@tonic-gate  * summarizeFAT
644*7c478bd9Sstevel@tonic-gate  *	Mark orphans without directory entries as allocated.
645*7c478bd9Sstevel@tonic-gate  *	XXX - these chains should be reclaimed!
646*7c478bd9Sstevel@tonic-gate  *	XXX - merge this routine with countClusters (same loop, duh.)
647*7c478bd9Sstevel@tonic-gate  */
648*7c478bd9Sstevel@tonic-gate static void
649*7c478bd9Sstevel@tonic-gate summarizeFAT(int fd)
650*7c478bd9Sstevel@tonic-gate {
651*7c478bd9Sstevel@tonic-gate 	int32_t c;
652*7c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
653*7c478bd9Sstevel@tonic-gate 
654*7c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
655*7c478bd9Sstevel@tonic-gate 		if (!freeInFAT(c) && !badInFAT(c) && !reservedInFAT(c) &&
656*7c478bd9Sstevel@tonic-gate 		    !isInUse(c)) {
657*7c478bd9Sstevel@tonic-gate 			(void) markInUse(fd, c, &BlankPCDIR, NULL, 0, VISIBLE,
658*7c478bd9Sstevel@tonic-gate 				&tmpl);
659*7c478bd9Sstevel@tonic-gate 		}
660*7c478bd9Sstevel@tonic-gate 	}
661*7c478bd9Sstevel@tonic-gate }
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate static void
664*7c478bd9Sstevel@tonic-gate getReadyToSearch(int fd)
665*7c478bd9Sstevel@tonic-gate {
666*7c478bd9Sstevel@tonic-gate 	getFAT(fd);
667*7c478bd9Sstevel@tonic-gate 	if (!IsFAT32)
668*7c478bd9Sstevel@tonic-gate 		getRootDirectory(fd);
669*7c478bd9Sstevel@tonic-gate }
670*7c478bd9Sstevel@tonic-gate 
671*7c478bd9Sstevel@tonic-gate 
672*7c478bd9Sstevel@tonic-gate static char PathName[MAXPATHLEN];
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate static void
675*7c478bd9Sstevel@tonic-gate summarize(int fd, int includeFAT)
676*7c478bd9Sstevel@tonic-gate {
677*7c478bd9Sstevel@tonic-gate 	struct pcdir *ignorep1, *ignorep2 = NULL;
678*7c478bd9Sstevel@tonic-gate 	int32_t ignore32;
679*7c478bd9Sstevel@tonic-gate 	char ignore;
680*7c478bd9Sstevel@tonic-gate 	int pathlen;
681*7c478bd9Sstevel@tonic-gate 
682*7c478bd9Sstevel@tonic-gate 	ReservedClusterCount = 0;
683*7c478bd9Sstevel@tonic-gate 	AllocedClusterCount = 0;
684*7c478bd9Sstevel@tonic-gate 	HiddenClusterCount = 0;
685*7c478bd9Sstevel@tonic-gate 	FileClusterCount = 0;
686*7c478bd9Sstevel@tonic-gate 	FreeClusterCount = 0;
687*7c478bd9Sstevel@tonic-gate 	DirClusterCount = 0;
688*7c478bd9Sstevel@tonic-gate 	BadClusterCount = 0;
689*7c478bd9Sstevel@tonic-gate 	HiddenFileCount = 0;
690*7c478bd9Sstevel@tonic-gate 	FileCount = 0;
691*7c478bd9Sstevel@tonic-gate 	DirCount = 0;
692*7c478bd9Sstevel@tonic-gate 	ignorep1 = ignorep2 = NULL;
693*7c478bd9Sstevel@tonic-gate 	ignore = '\0';
694*7c478bd9Sstevel@tonic-gate 
695*7c478bd9Sstevel@tonic-gate 	PathName[0] = '\0';
696*7c478bd9Sstevel@tonic-gate 	pathlen = 0;
697*7c478bd9Sstevel@tonic-gate 
698*7c478bd9Sstevel@tonic-gate 	getReadyToSearch(fd);
699*7c478bd9Sstevel@tonic-gate 	/*
700*7c478bd9Sstevel@tonic-gate 	 *  Traverse the full meta-data tree to talley what clusters
701*7c478bd9Sstevel@tonic-gate 	 * are in use.  The root directory is an area outside of the
702*7c478bd9Sstevel@tonic-gate 	 * file space on FAT12 and FAT16 file systems.  On FAT32 file
703*7c478bd9Sstevel@tonic-gate 	 * systems, the root directory is in a file area cluster just
704*7c478bd9Sstevel@tonic-gate 	 * like any other directory.
705*7c478bd9Sstevel@tonic-gate 	 */
706*7c478bd9Sstevel@tonic-gate 	if (!IsFAT32) {
707*7c478bd9Sstevel@tonic-gate 		traverseFromRoot(fd, 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL,
708*7c478bd9Sstevel@tonic-gate 		    ignore, &ignorep1, &ignore32, &ignorep2, PathName,
709*7c478bd9Sstevel@tonic-gate 		    &pathlen);
710*7c478bd9Sstevel@tonic-gate 	} else {
711*7c478bd9Sstevel@tonic-gate 		DirCount++;
712*7c478bd9Sstevel@tonic-gate 		traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
713*7c478bd9Sstevel@tonic-gate 		    0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL, ignore,
714*7c478bd9Sstevel@tonic-gate 		    &ignorep1, &ignore32, &ignorep2, PathName, &pathlen);
715*7c478bd9Sstevel@tonic-gate 	}
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 	if (includeFAT)
718*7c478bd9Sstevel@tonic-gate 		summarizeFAT(fd);
719*7c478bd9Sstevel@tonic-gate 	countClusters();
720*7c478bd9Sstevel@tonic-gate }
721*7c478bd9Sstevel@tonic-gate 
722*7c478bd9Sstevel@tonic-gate int
723*7c478bd9Sstevel@tonic-gate isMarkedBad(int32_t clusterNum)
724*7c478bd9Sstevel@tonic-gate {
725*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
726*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
727*7c478bd9Sstevel@tonic-gate 		return (0);
728*7c478bd9Sstevel@tonic-gate 
729*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
730*7c478bd9Sstevel@tonic-gate 		return (0);
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_BAD);
733*7c478bd9Sstevel@tonic-gate }
734*7c478bd9Sstevel@tonic-gate 
735*7c478bd9Sstevel@tonic-gate static int
736*7c478bd9Sstevel@tonic-gate isMarkedOrphan(int32_t clusterNum)
737*7c478bd9Sstevel@tonic-gate {
738*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
739*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
740*7c478bd9Sstevel@tonic-gate 		return (0);
741*7c478bd9Sstevel@tonic-gate 
742*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
743*7c478bd9Sstevel@tonic-gate 		return (0);
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate 	return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_ORPHAN);
746*7c478bd9Sstevel@tonic-gate }
747*7c478bd9Sstevel@tonic-gate 
748*7c478bd9Sstevel@tonic-gate static void
749*7c478bd9Sstevel@tonic-gate orphanChain(int fd, int32_t c, struct pcdir *ndp)
750*7c478bd9Sstevel@tonic-gate {
751*7c478bd9Sstevel@tonic-gate 	ClusterInfo *tmpl = NULL;
752*7c478bd9Sstevel@tonic-gate 
753*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
754*7c478bd9Sstevel@tonic-gate 	if (c < FIRST_CLUSTER || c > LastCluster)
755*7c478bd9Sstevel@tonic-gate 		return;
756*7c478bd9Sstevel@tonic-gate 	clearInUse(c);
757*7c478bd9Sstevel@tonic-gate 	markOrphan(fd, c, ndp);
758*7c478bd9Sstevel@tonic-gate 	c = nextInChain(c);
759*7c478bd9Sstevel@tonic-gate 	while (c != 0) {
760*7c478bd9Sstevel@tonic-gate 		clearInUse(c);
761*7c478bd9Sstevel@tonic-gate 		clearOrphan(c);
762*7c478bd9Sstevel@tonic-gate 		(void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, &tmpl);
763*7c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
764*7c478bd9Sstevel@tonic-gate 	}
765*7c478bd9Sstevel@tonic-gate }
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate static int32_t
768*7c478bd9Sstevel@tonic-gate findAFreeCluster(int32_t startAt)
769*7c478bd9Sstevel@tonic-gate {
770*7c478bd9Sstevel@tonic-gate 	int32_t look = startAt;
771*7c478bd9Sstevel@tonic-gate 
772*7c478bd9Sstevel@tonic-gate 	for (;;) {
773*7c478bd9Sstevel@tonic-gate 		if (freeInFAT(look)) {
774*7c478bd9Sstevel@tonic-gate 			break;
775*7c478bd9Sstevel@tonic-gate 		}
776*7c478bd9Sstevel@tonic-gate 		if (look == LastCluster)
777*7c478bd9Sstevel@tonic-gate 			look = FIRST_CLUSTER;
778*7c478bd9Sstevel@tonic-gate 		else
779*7c478bd9Sstevel@tonic-gate 			look++;
780*7c478bd9Sstevel@tonic-gate 		if (look == startAt)
781*7c478bd9Sstevel@tonic-gate 			break;
782*7c478bd9Sstevel@tonic-gate 	}
783*7c478bd9Sstevel@tonic-gate 	if (look != startAt)
784*7c478bd9Sstevel@tonic-gate 		return (look);
785*7c478bd9Sstevel@tonic-gate 	else
786*7c478bd9Sstevel@tonic-gate 		return (0);
787*7c478bd9Sstevel@tonic-gate }
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate static void
790*7c478bd9Sstevel@tonic-gate setEndOfDirectory(struct pcdir *dp)
791*7c478bd9Sstevel@tonic-gate {
792*7c478bd9Sstevel@tonic-gate 	dp->pcd_filename[0] = PCD_UNUSED;
793*7c478bd9Sstevel@tonic-gate }
794*7c478bd9Sstevel@tonic-gate 
795*7c478bd9Sstevel@tonic-gate static void
796*7c478bd9Sstevel@tonic-gate emergencyEndOfDirectory(int fd, int32_t secondToLast)
797*7c478bd9Sstevel@tonic-gate {
798*7c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
799*7c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
800*7c478bd9Sstevel@tonic-gate 
801*7c478bd9Sstevel@tonic-gate 	if (readCluster(fd, secondToLast, &(dirdata.bytes), &dirdatasize,
802*7c478bd9Sstevel@tonic-gate 	    RDCLUST_DO_CACHE) != RDCLUST_GOOD) {
803*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
804*7c478bd9Sstevel@tonic-gate 		    gettext("Unable to read allocation unit %d.\n"),
805*7c478bd9Sstevel@tonic-gate 		    secondToLast);
806*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
807*7c478bd9Sstevel@tonic-gate 		    gettext("Cannot allocate a new allocation unit to hold an"
808*7c478bd9Sstevel@tonic-gate 		    " end-of-directory marker.\nCannot access allocation unit"
809*7c478bd9Sstevel@tonic-gate 		    " to overwrite existing directory entry with\nthe marker."
810*7c478bd9Sstevel@tonic-gate 		    "  Needed directory truncation has failed.  Giving up.\n"));
811*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
812*7c478bd9Sstevel@tonic-gate 		exit(11);
813*7c478bd9Sstevel@tonic-gate 	}
814*7c478bd9Sstevel@tonic-gate 	setEndOfDirectory(dirdata.dirp);
815*7c478bd9Sstevel@tonic-gate 	markClusterModified(secondToLast);
816*7c478bd9Sstevel@tonic-gate }
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate static void
819*7c478bd9Sstevel@tonic-gate makeNewEndOfDirectory(struct pcdir *entry, int32_t secondToLast,
820*7c478bd9Sstevel@tonic-gate     int32_t newCluster, ClusterContents *newData)
821*7c478bd9Sstevel@tonic-gate {
822*7c478bd9Sstevel@tonic-gate 	setEndOfDirectory(newData->dirp);
823*7c478bd9Sstevel@tonic-gate 	markClusterModified(newCluster);
824*7c478bd9Sstevel@tonic-gate 	/*
825*7c478bd9Sstevel@tonic-gate 	 *  There are two scenarios.  One is that we truncated the
826*7c478bd9Sstevel@tonic-gate 	 *  directory in the very beginning.  The other is that we
827*7c478bd9Sstevel@tonic-gate 	 *  truncated it in the middle or at the end.  In the first
828*7c478bd9Sstevel@tonic-gate 	 *  scenario, the secondToLast argument is not a valid cluster
829*7c478bd9Sstevel@tonic-gate 	 *  (it's zero), and so we actually need to change the start
830*7c478bd9Sstevel@tonic-gate 	 *  cluster for the directory to this new start cluster.  In
831*7c478bd9Sstevel@tonic-gate 	 *  the second scenario, the secondToLast cluster we received
832*7c478bd9Sstevel@tonic-gate 	 *  as an argument needs to be pointed at the new end of
833*7c478bd9Sstevel@tonic-gate 	 *  directory.
834*7c478bd9Sstevel@tonic-gate 	 */
835*7c478bd9Sstevel@tonic-gate 	if (secondToLast == 0) {
836*7c478bd9Sstevel@tonic-gate 		updateDirEnt_Start(entry, newCluster);
837*7c478bd9Sstevel@tonic-gate 	} else {
838*7c478bd9Sstevel@tonic-gate 		writeFATEntry(secondToLast, newCluster);
839*7c478bd9Sstevel@tonic-gate 	}
840*7c478bd9Sstevel@tonic-gate 	markLastInFAT(newCluster);
841*7c478bd9Sstevel@tonic-gate }
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate static void
844*7c478bd9Sstevel@tonic-gate createNewEndOfDirectory(int fd, struct pcdir *entry, int32_t secondToLast)
845*7c478bd9Sstevel@tonic-gate {
846*7c478bd9Sstevel@tonic-gate 	ClusterContents dirdata;
847*7c478bd9Sstevel@tonic-gate 	int32_t dirdatasize = 0;
848*7c478bd9Sstevel@tonic-gate 	int32_t freeCluster;
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 	if (((freeCluster = findAFreeCluster(secondToLast)) != 0)) {
851*7c478bd9Sstevel@tonic-gate 		if (readCluster(fd, freeCluster, &(dirdata.bytes),
852*7c478bd9Sstevel@tonic-gate 		    &dirdatasize, RDCLUST_DO_CACHE) == RDCLUST_GOOD) {
853*7c478bd9Sstevel@tonic-gate 			if (Verbose) {
854*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
855*7c478bd9Sstevel@tonic-gate 				    gettext("Grabbed allocation unit #%d "
856*7c478bd9Sstevel@tonic-gate 				    "for truncated\ndirectory's new end "
857*7c478bd9Sstevel@tonic-gate 				    "of directory.\n"), freeCluster);
858*7c478bd9Sstevel@tonic-gate 			}
859*7c478bd9Sstevel@tonic-gate 			makeNewEndOfDirectory(entry, secondToLast,
860*7c478bd9Sstevel@tonic-gate 			    freeCluster, &dirdata);
861*7c478bd9Sstevel@tonic-gate 			return;
862*7c478bd9Sstevel@tonic-gate 		}
863*7c478bd9Sstevel@tonic-gate 	}
864*7c478bd9Sstevel@tonic-gate 	if (secondToLast == 0) {
865*7c478bd9Sstevel@tonic-gate 		if (freeCluster == 0) {
866*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("File system full.\n"));
867*7c478bd9Sstevel@tonic-gate 		} else {
868*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
869*7c478bd9Sstevel@tonic-gate 			    gettext("Unable to read allocation unit %d.\n"),
870*7c478bd9Sstevel@tonic-gate 			    freeCluster);
871*7c478bd9Sstevel@tonic-gate 		}
872*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
873*7c478bd9Sstevel@tonic-gate 		    gettext("Cannot allocate a new allocation unit to hold "
874*7c478bd9Sstevel@tonic-gate 		    "an end-of-directory marker.\nNo existing directory "
875*7c478bd9Sstevel@tonic-gate 		    "entries can be overwritten with the marker,\n"
876*7c478bd9Sstevel@tonic-gate 		    "the only unit allocated to the directory is "
877*7c478bd9Sstevel@tonic-gate 		    "inaccessible.\nNeeded directory truncation has failed.  "
878*7c478bd9Sstevel@tonic-gate 		    "Giving up.\n"));
879*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
880*7c478bd9Sstevel@tonic-gate 		exit(11);
881*7c478bd9Sstevel@tonic-gate 	}
882*7c478bd9Sstevel@tonic-gate 	emergencyEndOfDirectory(fd, secondToLast);
883*7c478bd9Sstevel@tonic-gate }
884*7c478bd9Sstevel@tonic-gate 
885*7c478bd9Sstevel@tonic-gate /*
886*7c478bd9Sstevel@tonic-gate  * truncAtCluster
887*7c478bd9Sstevel@tonic-gate  *	Given a directory entry and a cluster number, search through
888*7c478bd9Sstevel@tonic-gate  *	the cluster chain for the entry and make the cluster previous
889*7c478bd9Sstevel@tonic-gate  *	to the given cluster in the chain the last cluster in the file.
890*7c478bd9Sstevel@tonic-gate  *	The number of orphaned bytes is returned.  For a chain that's
891*7c478bd9Sstevel@tonic-gate  *	a directory we need to do some special handling, since we'll be
892*7c478bd9Sstevel@tonic-gate  *	getting rid of the end of directory notice by truncating.
893*7c478bd9Sstevel@tonic-gate  */
894*7c478bd9Sstevel@tonic-gate static int64_t
895*7c478bd9Sstevel@tonic-gate truncAtCluster(int fd, struct pcdir *entry, int32_t cluster)
896*7c478bd9Sstevel@tonic-gate {
897*7c478bd9Sstevel@tonic-gate 	uint32_t oldSize, newSize;
898*7c478bd9Sstevel@tonic-gate 	int32_t prev, count, follow;
899*7c478bd9Sstevel@tonic-gate 	int dir = (entry->pcd_attr & PCA_DIR);
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate 	prev = 0; count = 0;
902*7c478bd9Sstevel@tonic-gate 	follow = extractStartCluster(entry);
903*7c478bd9Sstevel@tonic-gate 	while (follow != cluster && follow >= FIRST_CLUSTER &&
904*7c478bd9Sstevel@tonic-gate 	    follow <= LastCluster) {
905*7c478bd9Sstevel@tonic-gate 		prev = follow;
906*7c478bd9Sstevel@tonic-gate 		count++;
907*7c478bd9Sstevel@tonic-gate 		follow = nextInChain(follow);
908*7c478bd9Sstevel@tonic-gate 	}
909*7c478bd9Sstevel@tonic-gate 	if (follow != cluster) {
910*7c478bd9Sstevel@tonic-gate 		/*
911*7c478bd9Sstevel@tonic-gate 		 *  We didn't find the cluster they wanted to trunc at
912*7c478bd9Sstevel@tonic-gate 		 *  anywhere in the entry's chain.  So we'll leave the
913*7c478bd9Sstevel@tonic-gate 		 *  entry alone, and return a negative value so they
914*7c478bd9Sstevel@tonic-gate 		 *  can know something is wrong.
915*7c478bd9Sstevel@tonic-gate 		 */
916*7c478bd9Sstevel@tonic-gate 		return (-1);
917*7c478bd9Sstevel@tonic-gate 	}
918*7c478bd9Sstevel@tonic-gate 	if (Verbose) {
919*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
920*7c478bd9Sstevel@tonic-gate 		    gettext("Chain truncation at unit #%d\n"), cluster);
921*7c478bd9Sstevel@tonic-gate 	}
922*7c478bd9Sstevel@tonic-gate 	if (!dir) {
923*7c478bd9Sstevel@tonic-gate 		oldSize = extractSize(entry);
924*7c478bd9Sstevel@tonic-gate 		newSize = count *
925*7c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.sectors_per_cluster *
926*7c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
927*7c478bd9Sstevel@tonic-gate 		if (newSize == 0)
928*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(entry, 0);
929*7c478bd9Sstevel@tonic-gate 	} else {
930*7c478bd9Sstevel@tonic-gate 		newSize = 0;
931*7c478bd9Sstevel@tonic-gate 	}
932*7c478bd9Sstevel@tonic-gate 	updateDirEnt_Size(entry, newSize);
933*7c478bd9Sstevel@tonic-gate 	if (dir) {
934*7c478bd9Sstevel@tonic-gate 		createNewEndOfDirectory(fd, entry, prev);
935*7c478bd9Sstevel@tonic-gate 	} else if (prev != 0) {
936*7c478bd9Sstevel@tonic-gate 		markLastInFAT(prev);
937*7c478bd9Sstevel@tonic-gate 	}
938*7c478bd9Sstevel@tonic-gate 	if (dir) {
939*7c478bd9Sstevel@tonic-gate 		/*
940*7c478bd9Sstevel@tonic-gate 		 * We don't really know what the size of a directory is
941*7c478bd9Sstevel@tonic-gate 		 * but it is important for us to know if this truncation
942*7c478bd9Sstevel@tonic-gate 		 * results in an orphan with any size.  The value we
943*7c478bd9Sstevel@tonic-gate 		 * return from this routine for a normal file is the
944*7c478bd9Sstevel@tonic-gate 		 * number of bytes left in the chain.  For a directory
945*7c478bd9Sstevel@tonic-gate 		 * we can't be exact, and the caller doesn't really
946*7c478bd9Sstevel@tonic-gate 		 * expect us to be.  For a directory the caller only
947*7c478bd9Sstevel@tonic-gate 		 * cares if there are zero bytes left or more than
948*7c478bd9Sstevel@tonic-gate 		 * zero bytes left.  We'll return 1 to indicate
949*7c478bd9Sstevel@tonic-gate 		 * more than zero.
950*7c478bd9Sstevel@tonic-gate 		 */
951*7c478bd9Sstevel@tonic-gate 		if ((follow = nextInChain(follow)) != 0)
952*7c478bd9Sstevel@tonic-gate 			return (1);
953*7c478bd9Sstevel@tonic-gate 		else
954*7c478bd9Sstevel@tonic-gate 			return (0);
955*7c478bd9Sstevel@tonic-gate 	}
956*7c478bd9Sstevel@tonic-gate 	/*
957*7c478bd9Sstevel@tonic-gate 	 * newSize should always be smaller than the old one, since
958*7c478bd9Sstevel@tonic-gate 	 * we are decreasing the number of clusters allocated to the file.
959*7c478bd9Sstevel@tonic-gate 	 */
960*7c478bd9Sstevel@tonic-gate 	return ((int64_t)oldSize - (int64_t)newSize);
961*7c478bd9Sstevel@tonic-gate }
962*7c478bd9Sstevel@tonic-gate 
963*7c478bd9Sstevel@tonic-gate static struct pcdir *
964*7c478bd9Sstevel@tonic-gate updateOrphanedChainMetadata(int fd, struct pcdir *dp, int32_t endCluster,
965*7c478bd9Sstevel@tonic-gate     int isBad)
966*7c478bd9Sstevel@tonic-gate {
967*7c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
968*7c478bd9Sstevel@tonic-gate 	int64_t remainder;
969*7c478bd9Sstevel@tonic-gate 	char *newName = NULL;
970*7c478bd9Sstevel@tonic-gate 	int chosenName;
971*7c478bd9Sstevel@tonic-gate 	int dir = (dp->pcd_attr & PCA_DIR);
972*7c478bd9Sstevel@tonic-gate 
973*7c478bd9Sstevel@tonic-gate 	/*
974*7c478bd9Sstevel@tonic-gate 	 *  If the truncation fails, (which ought not to happen),
975*7c478bd9Sstevel@tonic-gate 	 *  there's no need to go any further, we just return
976*7c478bd9Sstevel@tonic-gate 	 *  a null value for the new directory entry pointer.
977*7c478bd9Sstevel@tonic-gate 	 */
978*7c478bd9Sstevel@tonic-gate 	remainder = truncAtCluster(fd, dp, endCluster);
979*7c478bd9Sstevel@tonic-gate 	if (remainder < 0)
980*7c478bd9Sstevel@tonic-gate 		return (ndp);
981*7c478bd9Sstevel@tonic-gate 	if (!dir && isBad) {
982*7c478bd9Sstevel@tonic-gate 		/*
983*7c478bd9Sstevel@tonic-gate 		 *  Subtract out the bad cluster from the remaining size
984*7c478bd9Sstevel@tonic-gate 		 *  We always assume the cluster being deleted from the
985*7c478bd9Sstevel@tonic-gate 		 *  file is full size, but that might not be the case
986*7c478bd9Sstevel@tonic-gate 		 *  for the last cluster of the file, so that is why
987*7c478bd9Sstevel@tonic-gate 		 *  we check for negative remainder value.
988*7c478bd9Sstevel@tonic-gate 		 */
989*7c478bd9Sstevel@tonic-gate 		remainder -= TheBIOSParameterBlock.bpb.sectors_per_cluster *
990*7c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
991*7c478bd9Sstevel@tonic-gate 		if (remainder < 0)
992*7c478bd9Sstevel@tonic-gate 			remainder = 0;
993*7c478bd9Sstevel@tonic-gate 	}
994*7c478bd9Sstevel@tonic-gate 	/*
995*7c478bd9Sstevel@tonic-gate 	 * Build a new directory entry for the rest of the chain.
996*7c478bd9Sstevel@tonic-gate 	 * Later, if the user okays it, we'll link this entry into the
997*7c478bd9Sstevel@tonic-gate 	 * root directory.  The new entry will start out as a
998*7c478bd9Sstevel@tonic-gate 	 * copy of the truncated entry.
999*7c478bd9Sstevel@tonic-gate 	 */
1000*7c478bd9Sstevel@tonic-gate 	if ((remainder != 0) &&
1001*7c478bd9Sstevel@tonic-gate 	    ((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
1002*7c478bd9Sstevel@tonic-gate 	    ((ndp = newDirEnt(dp)) != NULL)) {
1003*7c478bd9Sstevel@tonic-gate 		if (Verbose) {
1004*7c478bd9Sstevel@tonic-gate 			if (dir)
1005*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1006*7c478bd9Sstevel@tonic-gate 				    gettext("Orphaned directory chain.\n"));
1007*7c478bd9Sstevel@tonic-gate 			else
1008*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1009*7c478bd9Sstevel@tonic-gate 				    gettext("Orphaned chain, %u bytes.\n"),
1010*7c478bd9Sstevel@tonic-gate 				    (uint32_t)remainder);
1011*7c478bd9Sstevel@tonic-gate 		}
1012*7c478bd9Sstevel@tonic-gate 		if (!dir)
1013*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Size(ndp, (uint32_t)remainder);
1014*7c478bd9Sstevel@tonic-gate 		if (isBad)
1015*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, nextInChain(endCluster));
1016*7c478bd9Sstevel@tonic-gate 		else
1017*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, endCluster);
1018*7c478bd9Sstevel@tonic-gate 		updateDirEnt_Name(ndp, newName);
1019*7c478bd9Sstevel@tonic-gate 		addEntryToCHKList(chosenName);
1020*7c478bd9Sstevel@tonic-gate 	}
1021*7c478bd9Sstevel@tonic-gate 	return (ndp);
1022*7c478bd9Sstevel@tonic-gate }
1023*7c478bd9Sstevel@tonic-gate 
1024*7c478bd9Sstevel@tonic-gate /*
1025*7c478bd9Sstevel@tonic-gate  *  splitChain()
1026*7c478bd9Sstevel@tonic-gate  *
1027*7c478bd9Sstevel@tonic-gate  *	split a cluster allocation chain into two cluster chains
1028*7c478bd9Sstevel@tonic-gate  *	around a given cluster (problemCluster).  This results in two
1029*7c478bd9Sstevel@tonic-gate  *	separate directory entries; the original (dp), and one we hope
1030*7c478bd9Sstevel@tonic-gate  *	to create and return a pointer to to the caller (*newdp).
1031*7c478bd9Sstevel@tonic-gate  *	This second entry is the orphan chain, and it may end up in
1032*7c478bd9Sstevel@tonic-gate  *	the root directory as a FILEnnnn.CHK file.  We also return the
1033*7c478bd9Sstevel@tonic-gate  *	starting cluster of the orphan chain to the caller (*orphanStart).
1034*7c478bd9Sstevel@tonic-gate  */
1035*7c478bd9Sstevel@tonic-gate void
1036*7c478bd9Sstevel@tonic-gate splitChain(int fd, struct pcdir *dp, int32_t problemCluster,
1037*7c478bd9Sstevel@tonic-gate     struct pcdir **newdp, int32_t *orphanStart)
1038*7c478bd9Sstevel@tonic-gate {
1039*7c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
1040*7c478bd9Sstevel@tonic-gate 	int isBad = isMarkedBad(problemCluster);
1041*7c478bd9Sstevel@tonic-gate 
1042*7c478bd9Sstevel@tonic-gate 	ndp = updateOrphanedChainMetadata(fd, dp, problemCluster, isBad);
1043*7c478bd9Sstevel@tonic-gate 	*newdp = ndp;
1044*7c478bd9Sstevel@tonic-gate 	clearInUse(problemCluster);
1045*7c478bd9Sstevel@tonic-gate 	if (isBad) {
1046*7c478bd9Sstevel@tonic-gate 		clearOrphan(problemCluster);
1047*7c478bd9Sstevel@tonic-gate 		*orphanStart = nextInChain(problemCluster);
1048*7c478bd9Sstevel@tonic-gate 		orphanChain(fd, *orphanStart, ndp);
1049*7c478bd9Sstevel@tonic-gate 		markBadInFAT(problemCluster);
1050*7c478bd9Sstevel@tonic-gate 	} else {
1051*7c478bd9Sstevel@tonic-gate 		*orphanStart = problemCluster;
1052*7c478bd9Sstevel@tonic-gate 		orphanChain(fd, problemCluster, ndp);
1053*7c478bd9Sstevel@tonic-gate 	}
1054*7c478bd9Sstevel@tonic-gate }
1055*7c478bd9Sstevel@tonic-gate 
1056*7c478bd9Sstevel@tonic-gate /*
1057*7c478bd9Sstevel@tonic-gate  *  freeOrphan
1058*7c478bd9Sstevel@tonic-gate  *
1059*7c478bd9Sstevel@tonic-gate  *  User has requested that an orphaned cluster chain be freed back
1060*7c478bd9Sstevel@tonic-gate  *  into the file area.
1061*7c478bd9Sstevel@tonic-gate  */
1062*7c478bd9Sstevel@tonic-gate static void
1063*7c478bd9Sstevel@tonic-gate freeOrphan(int32_t c)
1064*7c478bd9Sstevel@tonic-gate {
1065*7c478bd9Sstevel@tonic-gate 	int32_t n;
1066*7c478bd9Sstevel@tonic-gate 
1067*7c478bd9Sstevel@tonic-gate 	/*
1068*7c478bd9Sstevel@tonic-gate 	 * Free the directory entry we explicitly created for
1069*7c478bd9Sstevel@tonic-gate 	 * the orphaned clusters.
1070*7c478bd9Sstevel@tonic-gate 	 */
1071*7c478bd9Sstevel@tonic-gate 	if (InUse[c - FIRST_CLUSTER]->dirent != NULL)
1072*7c478bd9Sstevel@tonic-gate 		free(InUse[c - FIRST_CLUSTER]->dirent);
1073*7c478bd9Sstevel@tonic-gate 	/*
1074*7c478bd9Sstevel@tonic-gate 	 * Then mark the clusters themselves as available.
1075*7c478bd9Sstevel@tonic-gate 	 */
1076*7c478bd9Sstevel@tonic-gate 	do {
1077*7c478bd9Sstevel@tonic-gate 		n = nextInChain(c);
1078*7c478bd9Sstevel@tonic-gate 		markFreeInFAT(c);
1079*7c478bd9Sstevel@tonic-gate 		markFree(c);
1080*7c478bd9Sstevel@tonic-gate 		c = n;
1081*7c478bd9Sstevel@tonic-gate 	} while (c != 0);
1082*7c478bd9Sstevel@tonic-gate }
1083*7c478bd9Sstevel@tonic-gate 
1084*7c478bd9Sstevel@tonic-gate /*
1085*7c478bd9Sstevel@tonic-gate  *  Rewrite the InUse field for a cluster chain.  Can be used on a partial
1086*7c478bd9Sstevel@tonic-gate  *  chain if provided with a stopAtCluster.
1087*7c478bd9Sstevel@tonic-gate  */
1088*7c478bd9Sstevel@tonic-gate static void
1089*7c478bd9Sstevel@tonic-gate redoInUse(int fd, int32_t c, struct pcdir *ndp, int32_t stopAtCluster)
1090*7c478bd9Sstevel@tonic-gate {
1091*7c478bd9Sstevel@tonic-gate 	while (c && c != stopAtCluster) {
1092*7c478bd9Sstevel@tonic-gate 		clearInUse(c);
1093*7c478bd9Sstevel@tonic-gate 		(void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, NULL);
1094*7c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
1095*7c478bd9Sstevel@tonic-gate 	}
1096*7c478bd9Sstevel@tonic-gate }
1097*7c478bd9Sstevel@tonic-gate 
1098*7c478bd9Sstevel@tonic-gate static struct pcdir *
1099*7c478bd9Sstevel@tonic-gate orphanDirEntLookup(int32_t clusterNum)
1100*7c478bd9Sstevel@tonic-gate {
1101*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1102*7c478bd9Sstevel@tonic-gate 		return (NULL);
1103*7c478bd9Sstevel@tonic-gate 
1104*7c478bd9Sstevel@tonic-gate 	if (isInUse(clusterNum)) {
1105*7c478bd9Sstevel@tonic-gate 		return (InUse[clusterNum - FIRST_CLUSTER]->dirent);
1106*7c478bd9Sstevel@tonic-gate 	} else {
1107*7c478bd9Sstevel@tonic-gate 		return (NULL);
1108*7c478bd9Sstevel@tonic-gate 	}
1109*7c478bd9Sstevel@tonic-gate }
1110*7c478bd9Sstevel@tonic-gate 
1111*7c478bd9Sstevel@tonic-gate static int32_t
1112*7c478bd9Sstevel@tonic-gate orphanSizeLookup(int32_t clusterNum)
1113*7c478bd9Sstevel@tonic-gate {
1114*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1115*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1116*7c478bd9Sstevel@tonic-gate 		return (-1);
1117*7c478bd9Sstevel@tonic-gate 
1118*7c478bd9Sstevel@tonic-gate 	if (isInUse(clusterNum)) {
1119*7c478bd9Sstevel@tonic-gate 		return (extractSize(InUse[clusterNum - FIRST_CLUSTER]->dirent));
1120*7c478bd9Sstevel@tonic-gate 	} else {
1121*7c478bd9Sstevel@tonic-gate 		return (-1);
1122*7c478bd9Sstevel@tonic-gate 	}
1123*7c478bd9Sstevel@tonic-gate }
1124*7c478bd9Sstevel@tonic-gate 
1125*7c478bd9Sstevel@tonic-gate /*
1126*7c478bd9Sstevel@tonic-gate  *  linkOrphan
1127*7c478bd9Sstevel@tonic-gate  *
1128*7c478bd9Sstevel@tonic-gate  *  User has requested that an orphaned cluster chain be brought back
1129*7c478bd9Sstevel@tonic-gate  *  into the file system.  So we have to make a new directory entry
1130*7c478bd9Sstevel@tonic-gate  *  in the root directory and point it at the cluster chain.
1131*7c478bd9Sstevel@tonic-gate  */
1132*7c478bd9Sstevel@tonic-gate static void
1133*7c478bd9Sstevel@tonic-gate linkOrphan(int fd, int32_t start)
1134*7c478bd9Sstevel@tonic-gate {
1135*7c478bd9Sstevel@tonic-gate 	struct pcdir *newEnt = NULL;
1136*7c478bd9Sstevel@tonic-gate 	struct pcdir *dp;
1137*7c478bd9Sstevel@tonic-gate 
1138*7c478bd9Sstevel@tonic-gate 	if ((dp = orphanDirEntLookup(start)) != NULL) {
1139*7c478bd9Sstevel@tonic-gate 		newEnt = addRootDirEnt(fd, dp);
1140*7c478bd9Sstevel@tonic-gate 	} else {
1141*7c478bd9Sstevel@tonic-gate 		(void) printf(gettext("Re-link of orphaned chain failed."
1142*7c478bd9Sstevel@tonic-gate 		    "  Allocation units will remain orphaned.\n"));
1143*7c478bd9Sstevel@tonic-gate 	}
1144*7c478bd9Sstevel@tonic-gate 	/*
1145*7c478bd9Sstevel@tonic-gate 	 *  A cluster isn't really InUse() unless it is referenced,
1146*7c478bd9Sstevel@tonic-gate 	 *  so if newEnt is NULL here, we are in effect using markInUse()
1147*7c478bd9Sstevel@tonic-gate 	 *  to note that the cluster is NOT in use.
1148*7c478bd9Sstevel@tonic-gate 	 */
1149*7c478bd9Sstevel@tonic-gate 	redoInUse(fd, start, newEnt, 0);
1150*7c478bd9Sstevel@tonic-gate }
1151*7c478bd9Sstevel@tonic-gate 
1152*7c478bd9Sstevel@tonic-gate /*
1153*7c478bd9Sstevel@tonic-gate  *  relinkCreatedOrphans
1154*7c478bd9Sstevel@tonic-gate  *
1155*7c478bd9Sstevel@tonic-gate  *  While marking clusters as bad, we can create orphan cluster
1156*7c478bd9Sstevel@tonic-gate  *  chains.  Since we were the ones doing the marking, we were able to
1157*7c478bd9Sstevel@tonic-gate  *  keep track of the orphans we created.  Now we want to go through
1158*7c478bd9Sstevel@tonic-gate  *  all those chains and either get them back into the file system or
1159*7c478bd9Sstevel@tonic-gate  *  free them depending on the user's input.
1160*7c478bd9Sstevel@tonic-gate  */
1161*7c478bd9Sstevel@tonic-gate static void
1162*7c478bd9Sstevel@tonic-gate relinkCreatedOrphans(int fd)
1163*7c478bd9Sstevel@tonic-gate {
1164*7c478bd9Sstevel@tonic-gate 	int32_t c;
1165*7c478bd9Sstevel@tonic-gate 
1166*7c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
1167*7c478bd9Sstevel@tonic-gate 		if (isMarkedOrphan(c)) {
1168*7c478bd9Sstevel@tonic-gate 			if (OkayToRelink && askAboutRelink(c)) {
1169*7c478bd9Sstevel@tonic-gate 				linkOrphan(fd, c);
1170*7c478bd9Sstevel@tonic-gate 			} else if (askAboutFreeing(c)) {
1171*7c478bd9Sstevel@tonic-gate 				freeOrphan(c);
1172*7c478bd9Sstevel@tonic-gate 			}
1173*7c478bd9Sstevel@tonic-gate 			clearOrphan(c);
1174*7c478bd9Sstevel@tonic-gate 		}
1175*7c478bd9Sstevel@tonic-gate 	}
1176*7c478bd9Sstevel@tonic-gate }
1177*7c478bd9Sstevel@tonic-gate 
1178*7c478bd9Sstevel@tonic-gate /*
1179*7c478bd9Sstevel@tonic-gate  *  relinkFATOrphans
1180*7c478bd9Sstevel@tonic-gate  *
1181*7c478bd9Sstevel@tonic-gate  *  We want to find orphans not represented in the meta-data.
1182*7c478bd9Sstevel@tonic-gate  *  These are chains marked in the FAT as being in use but
1183*7c478bd9Sstevel@tonic-gate  *  not referenced anywhere by any directory entries.
1184*7c478bd9Sstevel@tonic-gate  *  We'll go through the whole FAT and mark the first cluster
1185*7c478bd9Sstevel@tonic-gate  *  in any such chain as an orphan.  Then we can just use
1186*7c478bd9Sstevel@tonic-gate  *  the relinkCreatedOrphans routine to get them back into the
1187*7c478bd9Sstevel@tonic-gate  *  file system or free'ed depending on the user's input.
1188*7c478bd9Sstevel@tonic-gate  */
1189*7c478bd9Sstevel@tonic-gate static void
1190*7c478bd9Sstevel@tonic-gate relinkFATOrphans(int fd)
1191*7c478bd9Sstevel@tonic-gate {
1192*7c478bd9Sstevel@tonic-gate 	struct pcdir *ndp = NULL;
1193*7c478bd9Sstevel@tonic-gate 	int32_t cc, c, n;
1194*7c478bd9Sstevel@tonic-gate 	int32_t bpc, newSize;
1195*7c478bd9Sstevel@tonic-gate 	char *newName;
1196*7c478bd9Sstevel@tonic-gate 	int chosenName;
1197*7c478bd9Sstevel@tonic-gate 
1198*7c478bd9Sstevel@tonic-gate 	for (c = FIRST_CLUSTER; c < LastCluster; c++) {
1199*7c478bd9Sstevel@tonic-gate 		if (freeInFAT(c) || badInFAT(c) ||
1200*7c478bd9Sstevel@tonic-gate 		    reservedInFAT(c) || isInUse(c))
1201*7c478bd9Sstevel@tonic-gate 			continue;
1202*7c478bd9Sstevel@tonic-gate 		cc = 1;
1203*7c478bd9Sstevel@tonic-gate 		n = c;
1204*7c478bd9Sstevel@tonic-gate 		while (n = nextInChain(n))
1205*7c478bd9Sstevel@tonic-gate 			cc++;
1206*7c478bd9Sstevel@tonic-gate 		bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster *
1207*7c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector;
1208*7c478bd9Sstevel@tonic-gate 		newSize = cc * bpc;
1209*7c478bd9Sstevel@tonic-gate 		if (((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
1210*7c478bd9Sstevel@tonic-gate 		    ((ndp = newDirEnt(NULL)) != NULL)) {
1211*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Size(ndp, newSize);
1212*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Start(ndp, c);
1213*7c478bd9Sstevel@tonic-gate 			updateDirEnt_Name(ndp, newName);
1214*7c478bd9Sstevel@tonic-gate 			addEntryToCHKList(chosenName);
1215*7c478bd9Sstevel@tonic-gate 		}
1216*7c478bd9Sstevel@tonic-gate 		orphanChain(fd, c, ndp);
1217*7c478bd9Sstevel@tonic-gate 	}
1218*7c478bd9Sstevel@tonic-gate 	relinkCreatedOrphans(fd);
1219*7c478bd9Sstevel@tonic-gate }
1220*7c478bd9Sstevel@tonic-gate 
1221*7c478bd9Sstevel@tonic-gate static void
1222*7c478bd9Sstevel@tonic-gate relinkOrphans(int fd)
1223*7c478bd9Sstevel@tonic-gate {
1224*7c478bd9Sstevel@tonic-gate 	relinkCreatedOrphans(fd);
1225*7c478bd9Sstevel@tonic-gate 	relinkFATOrphans(fd);
1226*7c478bd9Sstevel@tonic-gate }
1227*7c478bd9Sstevel@tonic-gate 
1228*7c478bd9Sstevel@tonic-gate static void
1229*7c478bd9Sstevel@tonic-gate checkForFATLoop(int32_t clusterNum)
1230*7c478bd9Sstevel@tonic-gate {
1231*7c478bd9Sstevel@tonic-gate 	int32_t prev = clusterNum;
1232*7c478bd9Sstevel@tonic-gate 	int32_t follow;
1233*7c478bd9Sstevel@tonic-gate 
1234*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1235*7c478bd9Sstevel@tonic-gate 		return;
1236*7c478bd9Sstevel@tonic-gate 
1237*7c478bd9Sstevel@tonic-gate 	follow = nextInChain(clusterNum);
1238*7c478bd9Sstevel@tonic-gate 	while (follow != clusterNum && follow >= FIRST_CLUSTER &&
1239*7c478bd9Sstevel@tonic-gate 	    follow <= LastCluster) {
1240*7c478bd9Sstevel@tonic-gate 		prev = follow;
1241*7c478bd9Sstevel@tonic-gate 		follow = nextInChain(follow);
1242*7c478bd9Sstevel@tonic-gate 	}
1243*7c478bd9Sstevel@tonic-gate 	if (follow == clusterNum) {
1244*7c478bd9Sstevel@tonic-gate 		/*
1245*7c478bd9Sstevel@tonic-gate 		 * We found a loop.  Eradicate it by changing
1246*7c478bd9Sstevel@tonic-gate 		 * the last cluster in the loop to be last
1247*7c478bd9Sstevel@tonic-gate 		 * in the chain instead instead of pointing
1248*7c478bd9Sstevel@tonic-gate 		 * back to the first cluster.
1249*7c478bd9Sstevel@tonic-gate 		 */
1250*7c478bd9Sstevel@tonic-gate 		markLastInFAT(prev);
1251*7c478bd9Sstevel@tonic-gate 	}
1252*7c478bd9Sstevel@tonic-gate }
1253*7c478bd9Sstevel@tonic-gate 
1254*7c478bd9Sstevel@tonic-gate static void
1255*7c478bd9Sstevel@tonic-gate sharedChainError(int fd, int32_t clusterNum, struct pcdir *badEntry)
1256*7c478bd9Sstevel@tonic-gate {
1257*7c478bd9Sstevel@tonic-gate 	/*
1258*7c478bd9Sstevel@tonic-gate 	 * If we have shared clusters, it is either because the
1259*7c478bd9Sstevel@tonic-gate 	 * cluster somehow got assigned to multiple files and/or
1260*7c478bd9Sstevel@tonic-gate 	 * because of a loop in the cluster chain.  In either
1261*7c478bd9Sstevel@tonic-gate 	 * case we want to truncate the offending file at the
1262*7c478bd9Sstevel@tonic-gate 	 * cluster of contention.  Then, we will want to run
1263*7c478bd9Sstevel@tonic-gate 	 * through the remainder of the chain. If we find ourselves
1264*7c478bd9Sstevel@tonic-gate 	 * back at the top, we will know there is a loop in the
1265*7c478bd9Sstevel@tonic-gate 	 * FAT we need to remove.
1266*7c478bd9Sstevel@tonic-gate 	 */
1267*7c478bd9Sstevel@tonic-gate 	if (Verbose)
1268*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1269*7c478bd9Sstevel@tonic-gate 		    gettext("Truncating chain due to duplicate allocation of "
1270*7c478bd9Sstevel@tonic-gate 		    "unit %d.\n"), clusterNum);
1271*7c478bd9Sstevel@tonic-gate 	/*
1272*7c478bd9Sstevel@tonic-gate 	 * Note that we don't orphan anything here, because the duplicate
1273*7c478bd9Sstevel@tonic-gate 	 * part of the chain may be part of another valid chain.
1274*7c478bd9Sstevel@tonic-gate 	 */
1275*7c478bd9Sstevel@tonic-gate 	(void) truncAtCluster(fd, badEntry, clusterNum);
1276*7c478bd9Sstevel@tonic-gate 	checkForFATLoop(clusterNum);
1277*7c478bd9Sstevel@tonic-gate }
1278*7c478bd9Sstevel@tonic-gate 
1279*7c478bd9Sstevel@tonic-gate void
1280*7c478bd9Sstevel@tonic-gate truncChainWithBadCluster(int fd, struct pcdir *dp, int32_t startCluster)
1281*7c478bd9Sstevel@tonic-gate {
1282*7c478bd9Sstevel@tonic-gate 	struct pcdir *orphanEntry;
1283*7c478bd9Sstevel@tonic-gate 	int32_t orphanStartCluster;
1284*7c478bd9Sstevel@tonic-gate 	int32_t c = startCluster;
1285*7c478bd9Sstevel@tonic-gate 
1286*7c478bd9Sstevel@tonic-gate 	while (c != 0) {
1287*7c478bd9Sstevel@tonic-gate 		if (isMarkedBad(c)) {
1288*7c478bd9Sstevel@tonic-gate 			/*
1289*7c478bd9Sstevel@tonic-gate 			 *  splitChain() truncates the current guy and
1290*7c478bd9Sstevel@tonic-gate 			 *  then makes an orphan chain out of the remaining
1291*7c478bd9Sstevel@tonic-gate 			 *  clusters.  When we come back from the split
1292*7c478bd9Sstevel@tonic-gate 			 *  we'll want to continue looking for bad clusters
1293*7c478bd9Sstevel@tonic-gate 			 *  in the orphan chain.
1294*7c478bd9Sstevel@tonic-gate 			 */
1295*7c478bd9Sstevel@tonic-gate 			splitChain(fd, dp, c,
1296*7c478bd9Sstevel@tonic-gate 			    &orphanEntry, &orphanStartCluster);
1297*7c478bd9Sstevel@tonic-gate 			/*
1298*7c478bd9Sstevel@tonic-gate 			 *  There is a chance that we weren't able or weren't
1299*7c478bd9Sstevel@tonic-gate 			 *  required to make a directory entry for the
1300*7c478bd9Sstevel@tonic-gate 			 *  remaining clusters.  In that case we won't go
1301*7c478bd9Sstevel@tonic-gate 			 *  on, because we couldn't make any more splits
1302*7c478bd9Sstevel@tonic-gate 			 *  anyway.
1303*7c478bd9Sstevel@tonic-gate 			 */
1304*7c478bd9Sstevel@tonic-gate 			if (orphanEntry == NULL)
1305*7c478bd9Sstevel@tonic-gate 				break;
1306*7c478bd9Sstevel@tonic-gate 			c = orphanStartCluster;
1307*7c478bd9Sstevel@tonic-gate 			dp = orphanEntry;
1308*7c478bd9Sstevel@tonic-gate 			continue;
1309*7c478bd9Sstevel@tonic-gate 		}
1310*7c478bd9Sstevel@tonic-gate 		c = nextInChain(c);
1311*7c478bd9Sstevel@tonic-gate 	}
1312*7c478bd9Sstevel@tonic-gate }
1313*7c478bd9Sstevel@tonic-gate 
1314*7c478bd9Sstevel@tonic-gate int32_t
1315*7c478bd9Sstevel@tonic-gate nextInChain(int32_t currentCluster)
1316*7c478bd9Sstevel@tonic-gate {
1317*7c478bd9Sstevel@tonic-gate 	int32_t nextCluster;
1318*7c478bd9Sstevel@tonic-gate 
1319*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1320*7c478bd9Sstevel@tonic-gate 	if (currentCluster < FIRST_CLUSTER || currentCluster > LastCluster)
1321*7c478bd9Sstevel@tonic-gate 		return (0);
1322*7c478bd9Sstevel@tonic-gate 
1323*7c478bd9Sstevel@tonic-gate 	/*
1324*7c478bd9Sstevel@tonic-gate 	 * Look up FAT entry of next link in cluster chain,
1325*7c478bd9Sstevel@tonic-gate 	 * if this one is the last one return 0 as the next link.
1326*7c478bd9Sstevel@tonic-gate 	 */
1327*7c478bd9Sstevel@tonic-gate 	nextCluster = readFATEntry(currentCluster);
1328*7c478bd9Sstevel@tonic-gate 	if (nextCluster < FIRST_CLUSTER || nextCluster > LastCluster)
1329*7c478bd9Sstevel@tonic-gate 		return (0);
1330*7c478bd9Sstevel@tonic-gate 
1331*7c478bd9Sstevel@tonic-gate 	return (nextCluster);
1332*7c478bd9Sstevel@tonic-gate }
1333*7c478bd9Sstevel@tonic-gate 
1334*7c478bd9Sstevel@tonic-gate /*
1335*7c478bd9Sstevel@tonic-gate  * findImpactedCluster
1336*7c478bd9Sstevel@tonic-gate  *
1337*7c478bd9Sstevel@tonic-gate  *	Called when someone modifies what they believe might be a cached
1338*7c478bd9Sstevel@tonic-gate  *	cluster entry, but when	they only have a directory entry pointer
1339*7c478bd9Sstevel@tonic-gate  *	and not the cluster number.  We have to go dig up what cluster
1340*7c478bd9Sstevel@tonic-gate  *	they are modifying.
1341*7c478bd9Sstevel@tonic-gate  */
1342*7c478bd9Sstevel@tonic-gate int32_t
1343*7c478bd9Sstevel@tonic-gate findImpactedCluster(struct pcdir *modified)
1344*7c478bd9Sstevel@tonic-gate {
1345*7c478bd9Sstevel@tonic-gate 	CachedCluster *loop;
1346*7c478bd9Sstevel@tonic-gate 	/*
1347*7c478bd9Sstevel@tonic-gate 	 * Check to see if it's in the root directory first
1348*7c478bd9Sstevel@tonic-gate 	 */
1349*7c478bd9Sstevel@tonic-gate 	if (!IsFAT32 && ((uchar_t *)modified >= TheRootDir.bytes) &&
1350*7c478bd9Sstevel@tonic-gate 	    ((uchar_t *)modified < TheRootDir.bytes + RootDirSize))
1351*7c478bd9Sstevel@tonic-gate 		return (FAKE_ROOTDIR_CLUST);
1352*7c478bd9Sstevel@tonic-gate 
1353*7c478bd9Sstevel@tonic-gate 	loop = ClusterCache;
1354*7c478bd9Sstevel@tonic-gate 	while (loop) {
1355*7c478bd9Sstevel@tonic-gate 		if (((uchar_t *)modified >= loop->clusterData.bytes) &&
1356*7c478bd9Sstevel@tonic-gate 		    ((uchar_t *)modified <
1357*7c478bd9Sstevel@tonic-gate 		    (loop->clusterData.bytes + BytesPerCluster))) {
1358*7c478bd9Sstevel@tonic-gate 			return (loop->clusterNum);
1359*7c478bd9Sstevel@tonic-gate 		}
1360*7c478bd9Sstevel@tonic-gate 		loop = loop->next;
1361*7c478bd9Sstevel@tonic-gate 	}
1362*7c478bd9Sstevel@tonic-gate 	/*
1363*7c478bd9Sstevel@tonic-gate 	 *  Guess it wasn't cached after all...
1364*7c478bd9Sstevel@tonic-gate 	 */
1365*7c478bd9Sstevel@tonic-gate 	return (0);
1366*7c478bd9Sstevel@tonic-gate }
1367*7c478bd9Sstevel@tonic-gate 
1368*7c478bd9Sstevel@tonic-gate void
1369*7c478bd9Sstevel@tonic-gate writeClusterMods(int fd)
1370*7c478bd9Sstevel@tonic-gate {
1371*7c478bd9Sstevel@tonic-gate 	CachedCluster *loop = ClusterCache;
1372*7c478bd9Sstevel@tonic-gate 
1373*7c478bd9Sstevel@tonic-gate 	while (loop) {
1374*7c478bd9Sstevel@tonic-gate 		if (loop->modified)
1375*7c478bd9Sstevel@tonic-gate 			writeCachedCluster(fd, loop);
1376*7c478bd9Sstevel@tonic-gate 		loop = loop->next;
1377*7c478bd9Sstevel@tonic-gate 	}
1378*7c478bd9Sstevel@tonic-gate }
1379*7c478bd9Sstevel@tonic-gate 
1380*7c478bd9Sstevel@tonic-gate void
1381*7c478bd9Sstevel@tonic-gate squirrelPath(struct nameinfo *pathInfo, int32_t clusterNum)
1382*7c478bd9Sstevel@tonic-gate {
1383*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1384*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1385*7c478bd9Sstevel@tonic-gate 		return;
1386*7c478bd9Sstevel@tonic-gate 	if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
1387*7c478bd9Sstevel@tonic-gate 		return;
1388*7c478bd9Sstevel@tonic-gate 	InUse[clusterNum - FIRST_CLUSTER]->path = pathInfo;
1389*7c478bd9Sstevel@tonic-gate }
1390*7c478bd9Sstevel@tonic-gate 
1391*7c478bd9Sstevel@tonic-gate int
1392*7c478bd9Sstevel@tonic-gate markInUse(int fd, int32_t clusterNum, struct pcdir *referencer, struct
1393*7c478bd9Sstevel@tonic-gate     pcdir *longRef, int32_t longStartCluster, int isHiddenFile,
1394*7c478bd9Sstevel@tonic-gate     ClusterInfo **template)
1395*7c478bd9Sstevel@tonic-gate {
1396*7c478bd9Sstevel@tonic-gate 	int alreadyMarked;
1397*7c478bd9Sstevel@tonic-gate 	ClusterInfo *cl;
1398*7c478bd9Sstevel@tonic-gate 
1399*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1400*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1401*7c478bd9Sstevel@tonic-gate 		return (CLINFO_NEWLY_ALLOCED);
1402*7c478bd9Sstevel@tonic-gate 
1403*7c478bd9Sstevel@tonic-gate 	alreadyMarked = allocInUse(clusterNum, template);
1404*7c478bd9Sstevel@tonic-gate 	if ((alreadyMarked == CLINFO_PREVIOUSLY_ALLOCED) &&
1405*7c478bd9Sstevel@tonic-gate 	    (isInUse(clusterNum))) {
1406*7c478bd9Sstevel@tonic-gate 		sharedChainError(fd, clusterNum, referencer);
1407*7c478bd9Sstevel@tonic-gate 		return (CLINFO_PREVIOUSLY_ALLOCED);
1408*7c478bd9Sstevel@tonic-gate 	}
1409*7c478bd9Sstevel@tonic-gate 	cl = InUse[clusterNum - FIRST_CLUSTER];
1410*7c478bd9Sstevel@tonic-gate 	/*
1411*7c478bd9Sstevel@tonic-gate 	 * If Cl is newly allocated (refcnt <= 1) we must fill in the fields.
1412*7c478bd9Sstevel@tonic-gate 	 * If Cl has different fields, we must clone it.
1413*7c478bd9Sstevel@tonic-gate 	 */
1414*7c478bd9Sstevel@tonic-gate 
1415*7c478bd9Sstevel@tonic-gate 	if (cl->refcnt <= 1 || cl->dirent != referencer ||
1416*7c478bd9Sstevel@tonic-gate 	    cl->longent != longRef ||
1417*7c478bd9Sstevel@tonic-gate 	    cl->longEntStartClust != longStartCluster) {
1418*7c478bd9Sstevel@tonic-gate 		if (cl->refcnt > 1)
1419*7c478bd9Sstevel@tonic-gate 			cl = cloneClusterInfo(clusterNum);
1420*7c478bd9Sstevel@tonic-gate 		cl->dirent = referencer;
1421*7c478bd9Sstevel@tonic-gate 		cl->longent = longRef;
1422*7c478bd9Sstevel@tonic-gate 		cl->longEntStartClust = longStartCluster;
1423*7c478bd9Sstevel@tonic-gate 		if (isHiddenFile)
1424*7c478bd9Sstevel@tonic-gate 			cl->flags |= CLINFO_HIDDEN;
1425*7c478bd9Sstevel@tonic-gate 
1426*7c478bd9Sstevel@tonic-gate 		/*
1427*7c478bd9Sstevel@tonic-gate 		 * Return cl as the template to use for other clusters in
1428*7c478bd9Sstevel@tonic-gate 		 * this file
1429*7c478bd9Sstevel@tonic-gate 		 */
1430*7c478bd9Sstevel@tonic-gate 		if (template)
1431*7c478bd9Sstevel@tonic-gate 			*template = cl;
1432*7c478bd9Sstevel@tonic-gate 	}
1433*7c478bd9Sstevel@tonic-gate 	return (CLINFO_NEWLY_ALLOCED);
1434*7c478bd9Sstevel@tonic-gate }
1435*7c478bd9Sstevel@tonic-gate 
1436*7c478bd9Sstevel@tonic-gate void
1437*7c478bd9Sstevel@tonic-gate markClusterModified(int32_t clusterNum)
1438*7c478bd9Sstevel@tonic-gate {
1439*7c478bd9Sstevel@tonic-gate 	CachedCluster *c;
1440*7c478bd9Sstevel@tonic-gate 
1441*7c478bd9Sstevel@tonic-gate 	if (clusterNum == FAKE_ROOTDIR_CLUST) {
1442*7c478bd9Sstevel@tonic-gate 		RootDirModified = 1;
1443*7c478bd9Sstevel@tonic-gate 		return;
1444*7c478bd9Sstevel@tonic-gate 	}
1445*7c478bd9Sstevel@tonic-gate 
1446*7c478bd9Sstevel@tonic-gate 	/* silent failure for bogus clusters */
1447*7c478bd9Sstevel@tonic-gate 	if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1448*7c478bd9Sstevel@tonic-gate 		return;
1449*7c478bd9Sstevel@tonic-gate 
1450*7c478bd9Sstevel@tonic-gate 	if (c = findClusterCacheEntry(clusterNum)) {
1451*7c478bd9Sstevel@tonic-gate 		c->modified = 1;
1452*7c478bd9Sstevel@tonic-gate 	} else {
1453*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1454*7c478bd9Sstevel@tonic-gate 		    gettext("Unexpected internal error: "
1455*7c478bd9Sstevel@tonic-gate 		    "Missing cache entry [%d]\n"), clusterNum);
1456*7c478bd9Sstevel@tonic-gate 		exit(10);
1457*7c478bd9Sstevel@tonic-gate 	}
1458*7c478bd9Sstevel@tonic-gate }
1459*7c478bd9Sstevel@tonic-gate 
1460*7c478bd9Sstevel@tonic-gate /*
1461*7c478bd9Sstevel@tonic-gate  *  readCluster
1462*7c478bd9Sstevel@tonic-gate  *	caller wants to read cluster clusterNum.  We should return
1463*7c478bd9Sstevel@tonic-gate  *	a pointer to the read data in "data", and fill in the number
1464*7c478bd9Sstevel@tonic-gate  *	of bytes read in "datasize".  If shouldCache is non-zero
1465*7c478bd9Sstevel@tonic-gate  *	we should allocate cache space to the cluster, otherwise we
1466*7c478bd9Sstevel@tonic-gate  *	just return a pointer to a buffer we re-use whenever cacheing
1467*7c478bd9Sstevel@tonic-gate  *	is not requested.
1468*7c478bd9Sstevel@tonic-gate  */
1469*7c478bd9Sstevel@tonic-gate int
1470*7c478bd9Sstevel@tonic-gate readCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize,
1471*7c478bd9Sstevel@tonic-gate     int shouldCache)
1472*7c478bd9Sstevel@tonic-gate {
1473*7c478bd9Sstevel@tonic-gate 	uchar_t *newBuf;
1474*7c478bd9Sstevel@tonic-gate 	int rv;
1475*7c478bd9Sstevel@tonic-gate 
1476*7c478bd9Sstevel@tonic-gate 	*data = NULL;
1477*7c478bd9Sstevel@tonic-gate 	if ((*data = findClusterDataInTheCache(clusterNum)) != NULL) {
1478*7c478bd9Sstevel@tonic-gate 		*datasize = BytesPerCluster;
1479*7c478bd9Sstevel@tonic-gate 		return (RDCLUST_GOOD);
1480*7c478bd9Sstevel@tonic-gate 	}
1481*7c478bd9Sstevel@tonic-gate 
1482*7c478bd9Sstevel@tonic-gate 	rv = getCluster(fd, clusterNum, &newBuf, datasize);
1483*7c478bd9Sstevel@tonic-gate 	if (rv != RDCLUST_GOOD)
1484*7c478bd9Sstevel@tonic-gate 		return (rv);
1485*7c478bd9Sstevel@tonic-gate 
1486*7c478bd9Sstevel@tonic-gate 	/*
1487*7c478bd9Sstevel@tonic-gate 	 * Caller requested we NOT cache the data from this read.
1488*7c478bd9Sstevel@tonic-gate 	 * So, we just return a pointer to the common data buffer.
1489*7c478bd9Sstevel@tonic-gate 	 */
1490*7c478bd9Sstevel@tonic-gate 	if (shouldCache == 0) {
1491*7c478bd9Sstevel@tonic-gate 		*data = newBuf;
1492*7c478bd9Sstevel@tonic-gate 		return (rv);
1493*7c478bd9Sstevel@tonic-gate 	}
1494*7c478bd9Sstevel@tonic-gate 
1495*7c478bd9Sstevel@tonic-gate 	/*
1496*7c478bd9Sstevel@tonic-gate 	 * Caller requested we cache the data from this read.
1497*7c478bd9Sstevel@tonic-gate 	 * So, if we have some data, add it to the cache by
1498*7c478bd9Sstevel@tonic-gate 	 * copying it out of the common buffer into new storage.
1499*7c478bd9Sstevel@tonic-gate 	 */
1500*7c478bd9Sstevel@tonic-gate 	if (*datasize > 0)
1501*7c478bd9Sstevel@tonic-gate 		*data = addToCache(clusterNum, newBuf, datasize);
1502*7c478bd9Sstevel@tonic-gate 	return (rv);
1503*7c478bd9Sstevel@tonic-gate }
1504*7c478bd9Sstevel@tonic-gate 
1505*7c478bd9Sstevel@tonic-gate void
1506*7c478bd9Sstevel@tonic-gate findBadClusters(int fd)
1507*7c478bd9Sstevel@tonic-gate {
1508*7c478bd9Sstevel@tonic-gate 	int32_t clusterCount;
1509*7c478bd9Sstevel@tonic-gate 	int32_t datasize;
1510*7c478bd9Sstevel@tonic-gate 	uchar_t *data;
1511*7c478bd9Sstevel@tonic-gate 
1512*7c478bd9Sstevel@tonic-gate 	BadClusterCount = 0;
1513*7c478bd9Sstevel@tonic-gate 	makeUseTable();
1514*7c478bd9Sstevel@tonic-gate 	(void) printf(gettext("** Scanning allocation units\n"));
1515*7c478bd9Sstevel@tonic-gate 	for (clusterCount = FIRST_CLUSTER;
1516*7c478bd9Sstevel@tonic-gate 	    clusterCount < LastCluster; clusterCount++) {
1517*7c478bd9Sstevel@tonic-gate 		if (readCluster(fd, clusterCount,
1518*7c478bd9Sstevel@tonic-gate 		    &data, &datasize, RDCLUST_DONT_CACHE) < 0) {
1519*7c478bd9Sstevel@tonic-gate 			if (Verbose)
1520*7c478bd9Sstevel@tonic-gate 			    (void) fprintf(stderr,
1521*7c478bd9Sstevel@tonic-gate 				gettext("\nUnreadable allocation unit %d.\n"),
1522*7c478bd9Sstevel@tonic-gate 				clusterCount);
1523*7c478bd9Sstevel@tonic-gate 			markBad(clusterCount, data, datasize);
1524*7c478bd9Sstevel@tonic-gate 		}
1525*7c478bd9Sstevel@tonic-gate 		/*
1526*7c478bd9Sstevel@tonic-gate 		 *  Progress meter, display a '.' for every 1000 clusters
1527*7c478bd9Sstevel@tonic-gate 		 *  processed.  We don't want to display this when
1528*7c478bd9Sstevel@tonic-gate 		 *  we are in verbose mode; verbose mode progress is
1529*7c478bd9Sstevel@tonic-gate 		 *  shown by displaying each file name as it is found.
1530*7c478bd9Sstevel@tonic-gate 		 */
1531*7c478bd9Sstevel@tonic-gate 		if (!Verbose && clusterCount % 1000 == 0)
1532*7c478bd9Sstevel@tonic-gate 			(void) printf(".");
1533*7c478bd9Sstevel@tonic-gate 	}
1534*7c478bd9Sstevel@tonic-gate 	(void) printf(gettext("..done\n"));
1535*7c478bd9Sstevel@tonic-gate }
1536*7c478bd9Sstevel@tonic-gate 
1537*7c478bd9Sstevel@tonic-gate void
1538*7c478bd9Sstevel@tonic-gate scanAndFixMetadata(int fd)
1539*7c478bd9Sstevel@tonic-gate {
1540*7c478bd9Sstevel@tonic-gate 	/*
1541*7c478bd9Sstevel@tonic-gate 	 * First we initialize a few things.
1542*7c478bd9Sstevel@tonic-gate 	 */
1543*7c478bd9Sstevel@tonic-gate 	makeUseTable();
1544*7c478bd9Sstevel@tonic-gate 	getReadyToSearch(fd);
1545*7c478bd9Sstevel@tonic-gate 	createCHKNameList(fd);
1546*7c478bd9Sstevel@tonic-gate 
1547*7c478bd9Sstevel@tonic-gate 	/*
1548*7c478bd9Sstevel@tonic-gate 	 * Make initial scan, taking into account any effect that
1549*7c478bd9Sstevel@tonic-gate 	 * the bad clusters we may have already discovered have
1550*7c478bd9Sstevel@tonic-gate 	 * on meta-data.  We may break up some cluster chains
1551*7c478bd9Sstevel@tonic-gate 	 * during this period.  The relinkCreatedOrphans() call
1552*7c478bd9Sstevel@tonic-gate 	 * will then give the user the chance to recover stuff
1553*7c478bd9Sstevel@tonic-gate 	 * we've created.
1554*7c478bd9Sstevel@tonic-gate 	 */
1555*7c478bd9Sstevel@tonic-gate 	(void) printf(gettext("** Scanning file system meta-data\n"));
1556*7c478bd9Sstevel@tonic-gate 	summarize(fd, NO_FAT_IN_SUMMARY);
1557*7c478bd9Sstevel@tonic-gate 	if (Verbose)
1558*7c478bd9Sstevel@tonic-gate 		printSummary(stderr);
1559*7c478bd9Sstevel@tonic-gate 	(void) printf(gettext("** Correcting any meta-data discrepancies\n"));
1560*7c478bd9Sstevel@tonic-gate 	relinkCreatedOrphans(fd);
1561*7c478bd9Sstevel@tonic-gate 
1562*7c478bd9Sstevel@tonic-gate 	/*
1563*7c478bd9Sstevel@tonic-gate 	 * Clear our usage table and go back over everything, this
1564*7c478bd9Sstevel@tonic-gate 	 * time including looking for clusters floating free in the FAT.
1565*7c478bd9Sstevel@tonic-gate 	 * This may include clusters the user chose to free during the
1566*7c478bd9Sstevel@tonic-gate 	 * relink phase.
1567*7c478bd9Sstevel@tonic-gate 	 */
1568*7c478bd9Sstevel@tonic-gate 	makeUseTable();
1569*7c478bd9Sstevel@tonic-gate 	summarize(fd, INCLUDE_FAT_IN_SUMMARY);
1570*7c478bd9Sstevel@tonic-gate 	relinkOrphans(fd);
1571*7c478bd9Sstevel@tonic-gate }
1572*7c478bd9Sstevel@tonic-gate 
1573*7c478bd9Sstevel@tonic-gate void
1574*7c478bd9Sstevel@tonic-gate printSummary(FILE *outDest)
1575*7c478bd9Sstevel@tonic-gate {
1576*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1577*7c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes.\n"),
1578*7c478bd9Sstevel@tonic-gate 	    (uint64_t)
1579*7c478bd9Sstevel@tonic-gate 	    TotalClusters * TheBIOSParameterBlock.bpb.sectors_per_cluster *
1580*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
1581*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1582*7c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes in bad sectors.\n"),
1583*7c478bd9Sstevel@tonic-gate 	    (uint64_t)
1584*7c478bd9Sstevel@tonic-gate 	    BadClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
1585*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
1586*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1587*7c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes in %d directories.\n"),
1588*7c478bd9Sstevel@tonic-gate 	    (uint64_t)
1589*7c478bd9Sstevel@tonic-gate 	    DirClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
1590*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector, DirCount);
1591*7c478bd9Sstevel@tonic-gate 	if (HiddenClusterCount) {
1592*7c478bd9Sstevel@tonic-gate 		(void) fprintf(outDest,
1593*7c478bd9Sstevel@tonic-gate 		    gettext("%llu bytes in %d hidden files.\n"),
1594*7c478bd9Sstevel@tonic-gate 		    (uint64_t)HiddenClusterCount *
1595*7c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.sectors_per_cluster *
1596*7c478bd9Sstevel@tonic-gate 		    TheBIOSParameterBlock.bpb.bytes_per_sector,
1597*7c478bd9Sstevel@tonic-gate 		    HiddenFileCount);
1598*7c478bd9Sstevel@tonic-gate 	}
1599*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1600*7c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes in %d files.\n"),
1601*7c478bd9Sstevel@tonic-gate 	    (uint64_t)
1602*7c478bd9Sstevel@tonic-gate 	    FileClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
1603*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector, FileCount);
1604*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1605*7c478bd9Sstevel@tonic-gate 	    gettext("%llu bytes free.\n"), (uint64_t)FreeClusterCount *
1606*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.sectors_per_cluster *
1607*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
1608*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1609*7c478bd9Sstevel@tonic-gate 	    gettext("%d bytes per allocation unit.\n"),
1610*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.sectors_per_cluster *
1611*7c478bd9Sstevel@tonic-gate 	    TheBIOSParameterBlock.bpb.bytes_per_sector);
1612*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1613*7c478bd9Sstevel@tonic-gate 	    gettext("%d total allocation units.\n"), TotalClusters);
1614*7c478bd9Sstevel@tonic-gate 	if (ReservedClusterCount)
1615*7c478bd9Sstevel@tonic-gate 	    (void) fprintf(outDest, gettext("%d reserved allocation units.\n"),
1616*7c478bd9Sstevel@tonic-gate 		ReservedClusterCount);
1617*7c478bd9Sstevel@tonic-gate 	(void) fprintf(outDest,
1618*7c478bd9Sstevel@tonic-gate 	    gettext("%d available allocation units.\n"), FreeClusterCount);
1619*7c478bd9Sstevel@tonic-gate }
1620