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