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