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