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
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.
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]
23 * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
24 * All rights reserved.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
28 #pragma ident "%Z%%M% %I% %E% SMI"
31 * fsck_pcfs -- routines for manipulating clusters.
39 #include <sys/dktp/fdisk.h>
40 #include <sys/fs/pc_fs.h>
41 #include <sys/fs/pc_dir.h>
42 #include <sys/fs/pc_label.h>
43 #include "pcfs_common.h"
44 #include "fsck_pcfs.h"
46 extern ClusterContents TheRootDir
;
47 extern off64_t FirstClusterOffset
;
48 extern off64_t PartitionOffset
;
49 extern int32_t BytesPerCluster
;
50 extern int32_t TotalClusters
;
51 extern int32_t LastCluster
;
52 extern int32_t RootDirSize
;
53 extern int32_t FATSize
;
54 extern bpb_t TheBIOSParameterBlock
;
55 extern short FATEntrySize
;
56 extern int RootDirModified
;
57 extern int OkayToRelink
;
62 static struct pcdir BlankPCDIR
;
63 static CachedCluster
*ClusterCache
;
64 static ClusterInfo
**InUse
;
65 static int32_t ReservedClusterCount
;
66 static int32_t AllocedClusterCount
;
67 static int32_t FreeClusterCount
;
68 static int32_t BadClusterCount
;
73 static int32_t CachedClusterCount
;
75 int32_t HiddenClusterCount
;
76 int32_t FileClusterCount
;
77 int32_t DirClusterCount
;
78 int32_t HiddenFileCount
;
82 static int32_t orphanSizeLookup(int32_t clusterNum
);
85 freeNameInfo(int32_t clusterNum
)
87 /* silent failure for bogus clusters */
88 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
90 if (InUse
[clusterNum
- FIRST_CLUSTER
]->path
!= NULL
) {
91 if (InUse
[clusterNum
- FIRST_CLUSTER
]->path
->references
> 1) {
92 InUse
[clusterNum
- FIRST_CLUSTER
]->path
->references
--;
94 free(InUse
[clusterNum
- FIRST_CLUSTER
]->path
->fullName
);
95 free(InUse
[clusterNum
- FIRST_CLUSTER
]->path
);
97 InUse
[clusterNum
- FIRST_CLUSTER
]->path
= NULL
;
102 printOrphanPath(int32_t clusterNum
)
104 /* silent failure for bogus clusters */
105 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
107 if (InUse
[clusterNum
- FIRST_CLUSTER
]->path
!= NULL
) {
108 (void) printf(gettext("\nOrphaned allocation units originally "
110 (void) printf("%s\n",
111 InUse
[clusterNum
- FIRST_CLUSTER
]->path
->fullName
);
112 freeNameInfo(clusterNum
);
114 (void) printf(gettext("\nOrphaned allocation units originally "
115 "allocated to an unknown file or directory:\n"));
116 (void) printf(gettext("Orphaned chain begins with allocation "
117 "unit %d.\n"), clusterNum
);
122 printOrphanSize(int32_t clusterNum
)
124 int32_t size
= orphanSizeLookup(clusterNum
);
127 (void) printf(gettext("%d bytes in the orphaned chain of "
128 "allocation units.\n"), size
);
130 (void) printf(gettext("[Starting at allocation "
131 "unit %d]\n"), clusterNum
);
137 printOrphanInfo(int32_t clusterNum
)
139 printOrphanPath(clusterNum
);
140 printOrphanSize(clusterNum
);
144 askAboutFreeing(int32_t clusterNum
)
147 * If it is not OkayToRelink, we haven't already printed the size
148 * of the orphaned chain.
151 printOrphanInfo(clusterNum
);
153 * If we are in preen mode, preenBail won't return.
155 preenBail("Need user confirmation to free orphaned chain.\n");
158 gettext("Free the allocation units in the orphaned chain ? "
164 askAboutRelink(int32_t clusterNum
)
167 * Display the size of the chain for the user to consider.
169 printOrphanInfo(clusterNum
);
171 * If we are in preen mode, preenBail won't return.
173 preenBail("Need user confirmation to re-link orphaned chain.\n");
175 (void) printf(gettext("Re-link orphaned chain into file system ? "
182 isHidden(int32_t clusterNum
)
184 /* silent failure for bogus clusters */
185 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
188 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
191 return (InUse
[clusterNum
- FIRST_CLUSTER
]->flags
& CLINFO_HIDDEN
);
195 isInUse(int32_t clusterNum
)
197 /* silent failure for bogus clusters */
198 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
201 return ((InUse
[clusterNum
- FIRST_CLUSTER
] != NULL
) &&
202 (InUse
[clusterNum
- FIRST_CLUSTER
]->dirent
!= NULL
));
206 * Caller's may request that we cache the data from a readCluster.
207 * The xxxClusterxxxCachexxx routines handle looking for cached data
208 * or initially caching the data.
210 * XXX - facilitate releasing cached data for low memory situations.
212 static CachedCluster
*
213 findClusterCacheEntry(int32_t clusterNum
)
215 CachedCluster
*loop
= ClusterCache
;
217 while (loop
!= NULL
) {
218 if (loop
->clusterNum
== clusterNum
)
226 findClusterDataInTheCache(int32_t clusterNum
)
228 CachedCluster
*loop
= ClusterCache
;
231 if (loop
->clusterNum
== clusterNum
)
232 return (loop
->clusterData
.bytes
);
239 addToCache(int32_t clusterNum
, uchar_t
*buf
, int32_t *datasize
)
244 if ((new = (CachedCluster
*)malloc(sizeof (CachedCluster
))) == NULL
) {
245 perror(gettext("No memory for cached cluster info"));
248 new->clusterNum
= clusterNum
;
251 if ((cp
= (uchar_t
*)calloc(1, BytesPerCluster
)) == NULL
) {
252 perror(gettext("No memory for cached copy of cluster"));
256 (void) memcpy(cp
, buf
, *datasize
);
257 new->clusterData
.bytes
= cp
;
260 (void) fprintf(stderr
,
261 gettext("Allocation unit %d cached.\n"), clusterNum
);
263 if (ClusterCache
== NULL
) {
266 } else if (new->clusterNum
< ClusterCache
->clusterNum
) {
267 new->next
= ClusterCache
;
270 CachedCluster
*loop
= ClusterCache
;
271 CachedCluster
*trailer
= NULL
;
273 while (loop
&& new->clusterNum
> loop
->clusterNum
) {
284 CachedClusterCount
++;
285 return (new->clusterData
.bytes
);
289 seekCluster(int fd
, int32_t clusterNum
)
294 seekto
= FirstClusterOffset
+
295 ((off64_t
)clusterNum
- FIRST_CLUSTER
) * BytesPerCluster
;
296 if (lseek64(fd
, seekto
, SEEK_SET
) != seekto
) {
298 (void) fprintf(stderr
,
299 gettext("Seek to Allocation unit #%d failed: "),
301 (void) fprintf(stderr
, strerror(saveError
));
302 (void) fprintf(stderr
, "\n");
310 * Get cluster bytes off the disk. We always read those bytes into
311 * the same static buffer. If the caller wants its own copy of the
312 * data it'll have to make its own copy. We'll return all the data
313 * read, even if it's short of a full cluster. This is for future use
314 * when we might want to relocate any salvagable data from bad clusters.
317 getCluster(int fd
, int32_t clusterNum
, uchar_t
**data
, int32_t *datasize
)
319 static uchar_t
*clusterBuffer
= NULL
;
326 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
327 return (RDCLUST_BADINPUT
);
329 if (clusterBuffer
== NULL
&&
330 (clusterBuffer
= (uchar_t
*)malloc(BytesPerCluster
)) == NULL
) {
331 perror(gettext("No memory for a cluster data buffer"));
332 return (RDCLUST_MEMERR
);
335 for (try = 0; try < RDCLUST_MAX_RETRY
; try++) {
336 if (!seekCluster(fd
, clusterNum
))
337 return (RDCLUST_FAIL
);
338 if ((*datasize
= read(fd
, clusterBuffer
, BytesPerCluster
)) ==
340 *data
= clusterBuffer
;
341 return (RDCLUST_GOOD
);
344 if (*datasize
>= 0) {
345 *data
= clusterBuffer
;
346 (void) fprintf(stderr
,
347 gettext("Short read of allocation unit #%d\n"), clusterNum
);
350 (void) fprintf(stderr
, "Allocation unit %d:", clusterNum
);
351 (void) fprintf(stderr
, strerror(saveError
));
352 (void) fprintf(stderr
, "\n");
354 return (RDCLUST_FAIL
);
358 writeCachedCluster(int fd
, CachedCluster
*clustInfo
)
360 ssize_t bytesWritten
;
366 (void) fprintf(stderr
,
367 gettext("Allocation unit %d modified.\n"),
368 clustInfo
->clusterNum
);
370 if (seekCluster(fd
, clustInfo
->clusterNum
) == NULL
)
373 if ((bytesWritten
= write(fd
, clustInfo
->clusterData
.bytes
,
374 BytesPerCluster
)) != BytesPerCluster
) {
375 if (bytesWritten
< 0) {
376 perror(gettext("Failed to write modified "
379 (void) fprintf(stderr
,
380 gettext("Short write of allocation unit %d\n"),
381 clustInfo
->clusterNum
);
389 * It's cheaper to allocate a lot at a time; malloc overhead pushes
390 * you over the brink much more quickly if you don't.
391 * This numbers seems to be a fair trade-off between reduced malloc overhead
392 * and additional overhead by over-allocating.
395 #define CHUNKSIZE 1024
397 static ClusterInfo
*pool
;
408 pool
= (ClusterInfo
*)malloc(sizeof (ClusterInfo
) * CHUNKSIZE
);
412 gettext("Out of memory for cluster information"));
416 for (i
= 0; i
< CHUNKSIZE
- 1; i
++)
417 pool
[i
].nextfree
= &pool
[i
+1];
419 pool
[CHUNKSIZE
-1].nextfree
= NULL
;
422 pool
= pool
->nextfree
;
424 memset(ret
, 0, sizeof (*ret
));
429 /* Should be called with verified arguments */
432 cloneClusterInfo(int32_t clusterNum
)
434 ClusterInfo
*cl
= InUse
[clusterNum
- FIRST_CLUSTER
];
436 if (cl
->refcnt
> 1) {
437 ClusterInfo
*newCl
= newClusterInfo();
442 newCl
->path
->references
++;
443 InUse
[clusterNum
- FIRST_CLUSTER
] = newCl
;
445 return (InUse
[clusterNum
- FIRST_CLUSTER
]);
449 updateFlags(int32_t clusterNum
, int newflags
)
451 ClusterInfo
*cl
= InUse
[clusterNum
- FIRST_CLUSTER
];
453 if (cl
->flags
!= newflags
&& cl
->refcnt
> 1)
454 cl
= cloneClusterInfo(clusterNum
);
456 cl
->flags
= newflags
;
460 freeClusterInfo(ClusterInfo
*old
)
462 if (--old
->refcnt
<= 0) {
463 if (old
->path
&& --old
->path
->references
<= 0) {
464 free(old
->path
->fullName
);
467 old
->nextfree
= pool
;
473 * Allocate entries in our sparse array of cluster information.
474 * Returns non-zero if the structure already has been allocated
475 * (for those keeping score at home).
477 * The template parameter, if non-NULL, is used to facilitate sharing
478 * the ClusterInfo nodes for the clusters belonging to the same file.
479 * The first call to allocInUse for a new file should have *template
480 * set to 0; on return, *template then points to the newly allocated
481 * ClusterInfo. Second and further calls keep the same value
482 * in *template and that ClusterInfo ndoe is then used for all
483 * entries in the file. Code that modifies the ClusterInfo nodes
484 * should take care proper sharing semantics are maintained (i.e.,
485 * copy-on-write using cloneClusterInfo())
487 * The ClusterInfo used in the template is guaranted to be in use in
488 * at least one other cluster as we never return a value if we didn't
489 * set it first. So we can overwrite it without the possibility of a leak.
492 allocInUse(int32_t clusterNum
, ClusterInfo
**template)
496 if (InUse
[clusterNum
- FIRST_CLUSTER
] != NULL
)
497 return (CLINFO_PREVIOUSLY_ALLOCED
);
499 if (template != NULL
&& *template != NULL
)
502 newCl
= newClusterInfo();
507 InUse
[clusterNum
- FIRST_CLUSTER
] = newCl
;
510 return (CLINFO_NEWLY_ALLOCED
);
514 markFree(int32_t clusterNum
)
516 /* silent failure for bogus clusters */
517 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
520 if (InUse
[clusterNum
- FIRST_CLUSTER
]) {
521 if (InUse
[clusterNum
- FIRST_CLUSTER
]->saved
)
522 free(InUse
[clusterNum
- FIRST_CLUSTER
]->saved
);
523 freeClusterInfo(InUse
[clusterNum
- FIRST_CLUSTER
]);
524 InUse
[clusterNum
- FIRST_CLUSTER
] = NULL
;
529 markOrphan(int fd
, int32_t clusterNum
, struct pcdir
*dp
)
531 /* silent failure for bogus clusters */
532 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
535 (void) markInUse(fd
, clusterNum
, dp
, NULL
, 0, VISIBLE
, NULL
);
536 if (InUse
[clusterNum
- FIRST_CLUSTER
] != NULL
)
537 updateFlags(clusterNum
,
538 InUse
[clusterNum
- FIRST_CLUSTER
]->flags
| CLINFO_ORPHAN
);
542 markBad(int32_t clusterNum
, uchar_t
*recovered
, int32_t recoveredLen
)
544 /* silent failure for bogus clusters */
545 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
548 (void) allocInUse(clusterNum
, NULL
);
551 (void) cloneClusterInfo(clusterNum
);
552 InUse
[clusterNum
- FIRST_CLUSTER
]->saved
= recovered
;
554 updateFlags(clusterNum
,
555 InUse
[clusterNum
- FIRST_CLUSTER
]->flags
| CLINFO_BAD
);
559 (void) fprintf(stderr
,
560 gettext("Allocation unit %d marked bad.\n"), clusterNum
);
564 clearOrphan(int32_t c
)
566 /* silent failure for bogus clusters */
567 if (c
< FIRST_CLUSTER
|| c
> LastCluster
)
569 if (InUse
[c
- FIRST_CLUSTER
] != NULL
)
571 InUse
[c
- FIRST_CLUSTER
]->flags
& ~CLINFO_ORPHAN
);
575 clearInUse(int32_t c
)
579 /* silent failure for bogus clusters */
580 if (c
< FIRST_CLUSTER
|| c
> LastCluster
)
583 clp
= &InUse
[c
- FIRST_CLUSTER
];
585 freeClusterInfo(*clp
);
591 clearAllClusters_InUse()
594 for (cc
= FIRST_CLUSTER
; cc
< LastCluster
; cc
++) {
603 clearAllClusters_InUse();
606 if ((InUse
= (ClusterInfo
**)
607 calloc(TotalClusters
, sizeof (ClusterInfo
*))) == NULL
) {
608 perror(gettext("No memory for internal table"));
618 BadClusterCount
= HiddenClusterCount
=
619 AllocedClusterCount
= FreeClusterCount
= 0;
621 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
624 } else if (isMarkedBad(c
)) {
626 * This catches the bad sectors found
627 * during thorough verify that have never been
628 * allocated to a file. Without this check, we
629 * count these guys as free.
633 } else if (isHidden(c
)) {
634 HiddenClusterCount
++;
635 } else if (isInUse(c
)) {
636 AllocedClusterCount
++;
645 * Mark orphans without directory entries as allocated.
646 * XXX - these chains should be reclaimed!
647 * XXX - merge this routine with countClusters (same loop, duh.)
653 ClusterInfo
*tmpl
= NULL
;
655 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
656 if (!freeInFAT(c
) && !badInFAT(c
) && !reservedInFAT(c
) &&
658 (void) markInUse(fd
, c
, &BlankPCDIR
, NULL
, 0, VISIBLE
,
665 getReadyToSearch(int fd
)
669 getRootDirectory(fd
);
673 static char PathName
[MAXPATHLEN
];
676 summarize(int fd
, int includeFAT
)
678 struct pcdir
*ignorep1
, *ignorep2
= NULL
;
683 ReservedClusterCount
= 0;
684 AllocedClusterCount
= 0;
685 HiddenClusterCount
= 0;
686 FileClusterCount
= 0;
687 FreeClusterCount
= 0;
693 ignorep1
= ignorep2
= NULL
;
699 getReadyToSearch(fd
);
701 * Traverse the full meta-data tree to talley what clusters
702 * are in use. The root directory is an area outside of the
703 * file space on FAT12 and FAT16 file systems. On FAT32 file
704 * systems, the root directory is in a file area cluster just
705 * like any other directory.
708 traverseFromRoot(fd
, 0, PCFS_VISIT_SUBDIRS
, PCFS_TRAVERSE_ALL
,
709 ignore
, &ignorep1
, &ignore32
, &ignorep2
, PathName
,
713 traverseDir(fd
, TheBIOSParameterBlock
.bpb32
.root_dir_clust
,
714 0, PCFS_VISIT_SUBDIRS
, PCFS_TRAVERSE_ALL
, ignore
,
715 &ignorep1
, &ignore32
, &ignorep2
, PathName
, &pathlen
);
724 isMarkedBad(int32_t clusterNum
)
726 /* silent failure for bogus clusters */
727 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
730 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
733 return (InUse
[clusterNum
- FIRST_CLUSTER
]->flags
& CLINFO_BAD
);
737 isMarkedOrphan(int32_t clusterNum
)
739 /* silent failure for bogus clusters */
740 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
743 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
746 return (InUse
[clusterNum
- FIRST_CLUSTER
]->flags
& CLINFO_ORPHAN
);
750 orphanChain(int fd
, int32_t c
, struct pcdir
*ndp
)
752 ClusterInfo
*tmpl
= NULL
;
754 /* silent failure for bogus clusters */
755 if (c
< FIRST_CLUSTER
|| c
> LastCluster
)
758 markOrphan(fd
, c
, ndp
);
763 (void) markInUse(fd
, c
, ndp
, NULL
, 0, VISIBLE
, &tmpl
);
769 findAFreeCluster(int32_t startAt
)
771 int32_t look
= startAt
;
774 if (freeInFAT(look
)) {
777 if (look
== LastCluster
)
778 look
= FIRST_CLUSTER
;
791 setEndOfDirectory(struct pcdir
*dp
)
793 dp
->pcd_filename
[0] = PCD_UNUSED
;
797 emergencyEndOfDirectory(int fd
, int32_t secondToLast
)
799 ClusterContents dirdata
;
800 int32_t dirdatasize
= 0;
802 if (readCluster(fd
, secondToLast
, &(dirdata
.bytes
), &dirdatasize
,
803 RDCLUST_DO_CACHE
) != RDCLUST_GOOD
) {
804 (void) fprintf(stderr
,
805 gettext("Unable to read allocation unit %d.\n"),
807 (void) fprintf(stderr
,
808 gettext("Cannot allocate a new allocation unit to hold an"
809 " end-of-directory marker.\nCannot access allocation unit"
810 " to overwrite existing directory entry with\nthe marker."
811 " Needed directory truncation has failed. Giving up.\n"));
815 setEndOfDirectory(dirdata
.dirp
);
816 markClusterModified(secondToLast
);
820 makeNewEndOfDirectory(struct pcdir
*entry
, int32_t secondToLast
,
821 int32_t newCluster
, ClusterContents
*newData
)
823 setEndOfDirectory(newData
->dirp
);
824 markClusterModified(newCluster
);
826 * There are two scenarios. One is that we truncated the
827 * directory in the very beginning. The other is that we
828 * truncated it in the middle or at the end. In the first
829 * scenario, the secondToLast argument is not a valid cluster
830 * (it's zero), and so we actually need to change the start
831 * cluster for the directory to this new start cluster. In
832 * the second scenario, the secondToLast cluster we received
833 * as an argument needs to be pointed at the new end of
836 if (secondToLast
== 0) {
837 updateDirEnt_Start(entry
, newCluster
);
839 writeFATEntry(secondToLast
, newCluster
);
841 markLastInFAT(newCluster
);
845 createNewEndOfDirectory(int fd
, struct pcdir
*entry
, int32_t secondToLast
)
847 ClusterContents dirdata
;
848 int32_t dirdatasize
= 0;
851 if (((freeCluster
= findAFreeCluster(secondToLast
)) != 0)) {
852 if (readCluster(fd
, freeCluster
, &(dirdata
.bytes
),
853 &dirdatasize
, RDCLUST_DO_CACHE
) == RDCLUST_GOOD
) {
855 (void) fprintf(stderr
,
856 gettext("Grabbed allocation unit #%d "
857 "for truncated\ndirectory's new end "
858 "of directory.\n"), freeCluster
);
860 makeNewEndOfDirectory(entry
, secondToLast
,
861 freeCluster
, &dirdata
);
865 if (secondToLast
== 0) {
866 if (freeCluster
== 0) {
867 (void) fprintf(stderr
, gettext("File system full.\n"));
869 (void) fprintf(stderr
,
870 gettext("Unable to read allocation unit %d.\n"),
873 (void) fprintf(stderr
,
874 gettext("Cannot allocate a new allocation unit to hold "
875 "an end-of-directory marker.\nNo existing directory "
876 "entries can be overwritten with the marker,\n"
877 "the only unit allocated to the directory is "
878 "inaccessible.\nNeeded directory truncation has failed. "
883 emergencyEndOfDirectory(fd
, secondToLast
);
888 * Given a directory entry and a cluster number, search through
889 * the cluster chain for the entry and make the cluster previous
890 * to the given cluster in the chain the last cluster in the file.
891 * The number of orphaned bytes is returned. For a chain that's
892 * a directory we need to do some special handling, since we'll be
893 * getting rid of the end of directory notice by truncating.
896 truncAtCluster(int fd
, struct pcdir
*entry
, int32_t cluster
)
898 uint32_t oldSize
, newSize
;
899 int32_t prev
, count
, follow
;
900 int dir
= (entry
->pcd_attr
& PCA_DIR
);
903 follow
= extractStartCluster(entry
);
904 while (follow
!= cluster
&& follow
>= FIRST_CLUSTER
&&
905 follow
<= LastCluster
) {
908 follow
= nextInChain(follow
);
910 if (follow
!= cluster
) {
912 * We didn't find the cluster they wanted to trunc at
913 * anywhere in the entry's chain. So we'll leave the
914 * entry alone, and return a negative value so they
915 * can know something is wrong.
920 (void) fprintf(stderr
,
921 gettext("Chain truncation at unit #%d\n"), cluster
);
924 oldSize
= extractSize(entry
);
926 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
927 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
929 updateDirEnt_Start(entry
, 0);
933 updateDirEnt_Size(entry
, newSize
);
935 createNewEndOfDirectory(fd
, entry
, prev
);
936 } else if (prev
!= 0) {
941 * We don't really know what the size of a directory is
942 * but it is important for us to know if this truncation
943 * results in an orphan with any size. The value we
944 * return from this routine for a normal file is the
945 * number of bytes left in the chain. For a directory
946 * we can't be exact, and the caller doesn't really
947 * expect us to be. For a directory the caller only
948 * cares if there are zero bytes left or more than
949 * zero bytes left. We'll return 1 to indicate
952 if ((follow
= nextInChain(follow
)) != 0)
958 * newSize should always be smaller than the old one, since
959 * we are decreasing the number of clusters allocated to the file.
961 return ((int64_t)oldSize
- (int64_t)newSize
);
964 static struct pcdir
*
965 updateOrphanedChainMetadata(int fd
, struct pcdir
*dp
, int32_t endCluster
,
968 struct pcdir
*ndp
= NULL
;
970 char *newName
= NULL
;
972 int dir
= (dp
->pcd_attr
& PCA_DIR
);
975 * If the truncation fails, (which ought not to happen),
976 * there's no need to go any further, we just return
977 * a null value for the new directory entry pointer.
979 remainder
= truncAtCluster(fd
, dp
, endCluster
);
984 * Subtract out the bad cluster from the remaining size
985 * We always assume the cluster being deleted from the
986 * file is full size, but that might not be the case
987 * for the last cluster of the file, so that is why
988 * we check for negative remainder value.
990 remainder
-= TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
991 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
996 * Build a new directory entry for the rest of the chain.
997 * Later, if the user okays it, we'll link this entry into the
998 * root directory. The new entry will start out as a
999 * copy of the truncated entry.
1001 if ((remainder
!= 0) &&
1002 ((newName
= nextAvailableCHKName(&chosenName
)) != NULL
) &&
1003 ((ndp
= newDirEnt(dp
)) != NULL
)) {
1006 (void) fprintf(stderr
,
1007 gettext("Orphaned directory chain.\n"));
1009 (void) fprintf(stderr
,
1010 gettext("Orphaned chain, %u bytes.\n"),
1011 (uint32_t)remainder
);
1014 updateDirEnt_Size(ndp
, (uint32_t)remainder
);
1016 updateDirEnt_Start(ndp
, nextInChain(endCluster
));
1018 updateDirEnt_Start(ndp
, endCluster
);
1019 updateDirEnt_Name(ndp
, newName
);
1020 addEntryToCHKList(chosenName
);
1028 * split a cluster allocation chain into two cluster chains
1029 * around a given cluster (problemCluster). This results in two
1030 * separate directory entries; the original (dp), and one we hope
1031 * to create and return a pointer to to the caller (*newdp).
1032 * This second entry is the orphan chain, and it may end up in
1033 * the root directory as a FILEnnnn.CHK file. We also return the
1034 * starting cluster of the orphan chain to the caller (*orphanStart).
1037 splitChain(int fd
, struct pcdir
*dp
, int32_t problemCluster
,
1038 struct pcdir
**newdp
, int32_t *orphanStart
)
1040 struct pcdir
*ndp
= NULL
;
1041 int isBad
= isMarkedBad(problemCluster
);
1043 ndp
= updateOrphanedChainMetadata(fd
, dp
, problemCluster
, isBad
);
1045 clearInUse(problemCluster
);
1047 clearOrphan(problemCluster
);
1048 *orphanStart
= nextInChain(problemCluster
);
1049 orphanChain(fd
, *orphanStart
, ndp
);
1050 markBadInFAT(problemCluster
);
1052 *orphanStart
= problemCluster
;
1053 orphanChain(fd
, problemCluster
, ndp
);
1060 * User has requested that an orphaned cluster chain be freed back
1061 * into the file area.
1064 freeOrphan(int32_t c
)
1069 * Free the directory entry we explicitly created for
1070 * the orphaned clusters.
1072 if (InUse
[c
- FIRST_CLUSTER
]->dirent
!= NULL
)
1073 free(InUse
[c
- FIRST_CLUSTER
]->dirent
);
1075 * Then mark the clusters themselves as available.
1086 * Rewrite the InUse field for a cluster chain. Can be used on a partial
1087 * chain if provided with a stopAtCluster.
1090 redoInUse(int fd
, int32_t c
, struct pcdir
*ndp
, int32_t stopAtCluster
)
1092 while (c
&& c
!= stopAtCluster
) {
1094 (void) markInUse(fd
, c
, ndp
, NULL
, 0, VISIBLE
, NULL
);
1099 static struct pcdir
*
1100 orphanDirEntLookup(int32_t clusterNum
)
1102 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1105 if (isInUse(clusterNum
)) {
1106 return (InUse
[clusterNum
- FIRST_CLUSTER
]->dirent
);
1113 orphanSizeLookup(int32_t clusterNum
)
1115 /* silent failure for bogus clusters */
1116 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1119 if (isInUse(clusterNum
)) {
1120 return (extractSize(InUse
[clusterNum
- FIRST_CLUSTER
]->dirent
));
1129 * User has requested that an orphaned cluster chain be brought back
1130 * into the file system. So we have to make a new directory entry
1131 * in the root directory and point it at the cluster chain.
1134 linkOrphan(int fd
, int32_t start
)
1136 struct pcdir
*newEnt
= NULL
;
1139 if ((dp
= orphanDirEntLookup(start
)) != NULL
) {
1140 newEnt
= addRootDirEnt(fd
, dp
);
1142 (void) printf(gettext("Re-link of orphaned chain failed."
1143 " Allocation units will remain orphaned.\n"));
1146 * A cluster isn't really InUse() unless it is referenced,
1147 * so if newEnt is NULL here, we are in effect using markInUse()
1148 * to note that the cluster is NOT in use.
1150 redoInUse(fd
, start
, newEnt
, 0);
1154 * relinkCreatedOrphans
1156 * While marking clusters as bad, we can create orphan cluster
1157 * chains. Since we were the ones doing the marking, we were able to
1158 * keep track of the orphans we created. Now we want to go through
1159 * all those chains and either get them back into the file system or
1160 * free them depending on the user's input.
1163 relinkCreatedOrphans(int fd
)
1167 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
1168 if (isMarkedOrphan(c
)) {
1169 if (OkayToRelink
&& askAboutRelink(c
)) {
1171 } else if (askAboutFreeing(c
)) {
1182 * We want to find orphans not represented in the meta-data.
1183 * These are chains marked in the FAT as being in use but
1184 * not referenced anywhere by any directory entries.
1185 * We'll go through the whole FAT and mark the first cluster
1186 * in any such chain as an orphan. Then we can just use
1187 * the relinkCreatedOrphans routine to get them back into the
1188 * file system or free'ed depending on the user's input.
1191 relinkFATOrphans(int fd
)
1193 struct pcdir
*ndp
= NULL
;
1195 int32_t bpc
, newSize
;
1199 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
1200 if (freeInFAT(c
) || badInFAT(c
) ||
1201 reservedInFAT(c
) || isInUse(c
))
1205 while (n
= nextInChain(n
))
1207 bpc
= TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1208 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
1210 if (((newName
= nextAvailableCHKName(&chosenName
)) != NULL
) &&
1211 ((ndp
= newDirEnt(NULL
)) != NULL
)) {
1212 updateDirEnt_Size(ndp
, newSize
);
1213 updateDirEnt_Start(ndp
, c
);
1214 updateDirEnt_Name(ndp
, newName
);
1215 addEntryToCHKList(chosenName
);
1217 orphanChain(fd
, c
, ndp
);
1219 relinkCreatedOrphans(fd
);
1223 relinkOrphans(int fd
)
1225 relinkCreatedOrphans(fd
);
1226 relinkFATOrphans(fd
);
1230 checkForFATLoop(int32_t clusterNum
)
1232 int32_t prev
= clusterNum
;
1235 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1238 follow
= nextInChain(clusterNum
);
1239 while (follow
!= clusterNum
&& follow
>= FIRST_CLUSTER
&&
1240 follow
<= LastCluster
) {
1242 follow
= nextInChain(follow
);
1244 if (follow
== clusterNum
) {
1246 * We found a loop. Eradicate it by changing
1247 * the last cluster in the loop to be last
1248 * in the chain instead instead of pointing
1249 * back to the first cluster.
1251 markLastInFAT(prev
);
1256 sharedChainError(int fd
, int32_t clusterNum
, struct pcdir
*badEntry
)
1259 * If we have shared clusters, it is either because the
1260 * cluster somehow got assigned to multiple files and/or
1261 * because of a loop in the cluster chain. In either
1262 * case we want to truncate the offending file at the
1263 * cluster of contention. Then, we will want to run
1264 * through the remainder of the chain. If we find ourselves
1265 * back at the top, we will know there is a loop in the
1266 * FAT we need to remove.
1269 (void) fprintf(stderr
,
1270 gettext("Truncating chain due to duplicate allocation of "
1271 "unit %d.\n"), clusterNum
);
1273 * Note that we don't orphan anything here, because the duplicate
1274 * part of the chain may be part of another valid chain.
1276 (void) truncAtCluster(fd
, badEntry
, clusterNum
);
1277 checkForFATLoop(clusterNum
);
1281 truncChainWithBadCluster(int fd
, struct pcdir
*dp
, int32_t startCluster
)
1283 struct pcdir
*orphanEntry
;
1284 int32_t orphanStartCluster
;
1285 int32_t c
= startCluster
;
1288 if (isMarkedBad(c
)) {
1290 * splitChain() truncates the current guy and
1291 * then makes an orphan chain out of the remaining
1292 * clusters. When we come back from the split
1293 * we'll want to continue looking for bad clusters
1294 * in the orphan chain.
1296 splitChain(fd
, dp
, c
,
1297 &orphanEntry
, &orphanStartCluster
);
1299 * There is a chance that we weren't able or weren't
1300 * required to make a directory entry for the
1301 * remaining clusters. In that case we won't go
1302 * on, because we couldn't make any more splits
1305 if (orphanEntry
== NULL
)
1307 c
= orphanStartCluster
;
1316 nextInChain(int32_t currentCluster
)
1318 int32_t nextCluster
;
1320 /* silent failure for bogus clusters */
1321 if (currentCluster
< FIRST_CLUSTER
|| currentCluster
> LastCluster
)
1325 * Look up FAT entry of next link in cluster chain,
1326 * if this one is the last one return 0 as the next link.
1328 nextCluster
= readFATEntry(currentCluster
);
1329 if (nextCluster
< FIRST_CLUSTER
|| nextCluster
> LastCluster
)
1332 return (nextCluster
);
1336 * findImpactedCluster
1338 * Called when someone modifies what they believe might be a cached
1339 * cluster entry, but when they only have a directory entry pointer
1340 * and not the cluster number. We have to go dig up what cluster
1341 * they are modifying.
1344 findImpactedCluster(struct pcdir
*modified
)
1346 CachedCluster
*loop
;
1348 * Check to see if it's in the root directory first
1350 if (!IsFAT32
&& ((uchar_t
*)modified
>= TheRootDir
.bytes
) &&
1351 ((uchar_t
*)modified
< TheRootDir
.bytes
+ RootDirSize
))
1352 return (FAKE_ROOTDIR_CLUST
);
1354 loop
= ClusterCache
;
1356 if (((uchar_t
*)modified
>= loop
->clusterData
.bytes
) &&
1357 ((uchar_t
*)modified
<
1358 (loop
->clusterData
.bytes
+ BytesPerCluster
))) {
1359 return (loop
->clusterNum
);
1364 * Guess it wasn't cached after all...
1370 writeClusterMods(int fd
)
1372 CachedCluster
*loop
= ClusterCache
;
1376 writeCachedCluster(fd
, loop
);
1382 squirrelPath(struct nameinfo
*pathInfo
, int32_t clusterNum
)
1384 /* silent failure for bogus clusters */
1385 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1387 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
1389 InUse
[clusterNum
- FIRST_CLUSTER
]->path
= pathInfo
;
1393 markInUse(int fd
, int32_t clusterNum
, struct pcdir
*referencer
, struct
1394 pcdir
*longRef
, int32_t longStartCluster
, int isHiddenFile
,
1395 ClusterInfo
**template)
1400 /* silent failure for bogus clusters */
1401 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1402 return (CLINFO_NEWLY_ALLOCED
);
1404 alreadyMarked
= allocInUse(clusterNum
, template);
1405 if ((alreadyMarked
== CLINFO_PREVIOUSLY_ALLOCED
) &&
1406 (isInUse(clusterNum
))) {
1407 sharedChainError(fd
, clusterNum
, referencer
);
1408 return (CLINFO_PREVIOUSLY_ALLOCED
);
1410 cl
= InUse
[clusterNum
- FIRST_CLUSTER
];
1412 * If Cl is newly allocated (refcnt <= 1) we must fill in the fields.
1413 * If Cl has different fields, we must clone it.
1416 if (cl
->refcnt
<= 1 || cl
->dirent
!= referencer
||
1417 cl
->longent
!= longRef
||
1418 cl
->longEntStartClust
!= longStartCluster
) {
1420 cl
= cloneClusterInfo(clusterNum
);
1421 cl
->dirent
= referencer
;
1422 cl
->longent
= longRef
;
1423 cl
->longEntStartClust
= longStartCluster
;
1425 cl
->flags
|= CLINFO_HIDDEN
;
1428 * Return cl as the template to use for other clusters in
1434 return (CLINFO_NEWLY_ALLOCED
);
1438 markClusterModified(int32_t clusterNum
)
1442 if (clusterNum
== FAKE_ROOTDIR_CLUST
) {
1443 RootDirModified
= 1;
1447 /* silent failure for bogus clusters */
1448 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1451 if (c
= findClusterCacheEntry(clusterNum
)) {
1454 (void) fprintf(stderr
,
1455 gettext("Unexpected internal error: "
1456 "Missing cache entry [%d]\n"), clusterNum
);
1463 * caller wants to read cluster clusterNum. We should return
1464 * a pointer to the read data in "data", and fill in the number
1465 * of bytes read in "datasize". If shouldCache is non-zero
1466 * we should allocate cache space to the cluster, otherwise we
1467 * just return a pointer to a buffer we re-use whenever cacheing
1471 readCluster(int fd
, int32_t clusterNum
, uchar_t
**data
, int32_t *datasize
,
1478 if ((*data
= findClusterDataInTheCache(clusterNum
)) != NULL
) {
1479 *datasize
= BytesPerCluster
;
1480 return (RDCLUST_GOOD
);
1483 rv
= getCluster(fd
, clusterNum
, &newBuf
, datasize
);
1484 if (rv
!= RDCLUST_GOOD
)
1488 * Caller requested we NOT cache the data from this read.
1489 * So, we just return a pointer to the common data buffer.
1491 if (shouldCache
== 0) {
1497 * Caller requested we cache the data from this read.
1498 * So, if we have some data, add it to the cache by
1499 * copying it out of the common buffer into new storage.
1502 *data
= addToCache(clusterNum
, newBuf
, datasize
);
1507 findBadClusters(int fd
)
1509 int32_t clusterCount
;
1513 BadClusterCount
= 0;
1515 (void) printf(gettext("** Scanning allocation units\n"));
1516 for (clusterCount
= FIRST_CLUSTER
;
1517 clusterCount
< LastCluster
; clusterCount
++) {
1518 if (readCluster(fd
, clusterCount
,
1519 &data
, &datasize
, RDCLUST_DONT_CACHE
) < 0) {
1521 (void) fprintf(stderr
,
1522 gettext("\nUnreadable allocation unit %d.\n"),
1524 markBad(clusterCount
, data
, datasize
);
1527 * Progress meter, display a '.' for every 1000 clusters
1528 * processed. We don't want to display this when
1529 * we are in verbose mode; verbose mode progress is
1530 * shown by displaying each file name as it is found.
1532 if (!Verbose
&& clusterCount
% 1000 == 0)
1535 (void) printf(gettext("..done\n"));
1539 scanAndFixMetadata(int fd
)
1542 * First we initialize a few things.
1545 getReadyToSearch(fd
);
1546 createCHKNameList(fd
);
1549 * Make initial scan, taking into account any effect that
1550 * the bad clusters we may have already discovered have
1551 * on meta-data. We may break up some cluster chains
1552 * during this period. The relinkCreatedOrphans() call
1553 * will then give the user the chance to recover stuff
1556 (void) printf(gettext("** Scanning file system meta-data\n"));
1557 summarize(fd
, NO_FAT_IN_SUMMARY
);
1559 printSummary(stderr
);
1560 (void) printf(gettext("** Correcting any meta-data discrepancies\n"));
1561 relinkCreatedOrphans(fd
);
1564 * Clear our usage table and go back over everything, this
1565 * time including looking for clusters floating free in the FAT.
1566 * This may include clusters the user chose to free during the
1570 summarize(fd
, INCLUDE_FAT_IN_SUMMARY
);
1575 printSummary(FILE *outDest
)
1577 (void) fprintf(outDest
,
1578 gettext("%llu bytes.\n"),
1580 TotalClusters
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1581 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1582 (void) fprintf(outDest
,
1583 gettext("%llu bytes in bad sectors.\n"),
1585 BadClusterCount
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1586 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1587 (void) fprintf(outDest
,
1588 gettext("%llu bytes in %d directories.\n"),
1590 DirClusterCount
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1591 TheBIOSParameterBlock
.bpb
.bytes_per_sector
, DirCount
);
1592 if (HiddenClusterCount
) {
1593 (void) fprintf(outDest
,
1594 gettext("%llu bytes in %d hidden files.\n"),
1595 (uint64_t)HiddenClusterCount
*
1596 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1597 TheBIOSParameterBlock
.bpb
.bytes_per_sector
,
1600 (void) fprintf(outDest
,
1601 gettext("%llu bytes in %d files.\n"),
1603 FileClusterCount
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1604 TheBIOSParameterBlock
.bpb
.bytes_per_sector
, FileCount
);
1605 (void) fprintf(outDest
,
1606 gettext("%llu bytes free.\n"), (uint64_t)FreeClusterCount
*
1607 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1608 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1609 (void) fprintf(outDest
,
1610 gettext("%d bytes per allocation unit.\n"),
1611 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1612 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1613 (void) fprintf(outDest
,
1614 gettext("%d total allocation units.\n"), TotalClusters
);
1615 if (ReservedClusterCount
)
1616 (void) fprintf(outDest
, gettext("%d reserved allocation units.\n"),
1617 ReservedClusterCount
);
1618 (void) fprintf(outDest
,
1619 gettext("%d available allocation units.\n"), FreeClusterCount
);