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