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