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