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