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.
29 * fsck_pcfs -- routines for manipulating clusters.
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"
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
;
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
;
71 static int32_t CachedClusterCount
;
73 int32_t HiddenClusterCount
;
74 int32_t FileClusterCount
;
75 int32_t DirClusterCount
;
76 int32_t HiddenFileCount
;
80 static int32_t orphanSizeLookup(int32_t clusterNum
);
83 freeNameInfo(int32_t clusterNum
)
85 /* silent failure for bogus clusters */
86 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
88 if (InUse
[clusterNum
- FIRST_CLUSTER
]->path
!= NULL
) {
89 if (InUse
[clusterNum
- FIRST_CLUSTER
]->path
->references
> 1) {
90 InUse
[clusterNum
- FIRST_CLUSTER
]->path
->references
--;
92 free(InUse
[clusterNum
- FIRST_CLUSTER
]->path
->fullName
);
93 free(InUse
[clusterNum
- FIRST_CLUSTER
]->path
);
95 InUse
[clusterNum
- FIRST_CLUSTER
]->path
= NULL
;
100 printOrphanPath(int32_t clusterNum
)
102 /* silent failure for bogus clusters */
103 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
105 if (InUse
[clusterNum
- FIRST_CLUSTER
]->path
!= NULL
) {
106 (void) printf(gettext("\nOrphaned allocation units originally "
108 (void) printf("%s\n",
109 InUse
[clusterNum
- FIRST_CLUSTER
]->path
->fullName
);
110 freeNameInfo(clusterNum
);
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
);
120 printOrphanSize(int32_t clusterNum
)
122 int32_t size
= orphanSizeLookup(clusterNum
);
125 (void) printf(gettext("%d bytes in the orphaned chain of "
126 "allocation units.\n"), size
);
128 (void) printf(gettext("[Starting at allocation "
129 "unit %d]\n"), clusterNum
);
135 printOrphanInfo(int32_t clusterNum
)
137 printOrphanPath(clusterNum
);
138 printOrphanSize(clusterNum
);
142 askAboutFreeing(int32_t clusterNum
)
145 * If it is not OkayToRelink, we haven't already printed the size
146 * of the orphaned chain.
149 printOrphanInfo(clusterNum
);
151 * If we are in preen mode, preenBail won't return.
153 preenBail("Need user confirmation to free orphaned chain.\n");
156 gettext("Free the allocation units in the orphaned chain ? "
162 askAboutRelink(int32_t clusterNum
)
165 * Display the size of the chain for the user to consider.
167 printOrphanInfo(clusterNum
);
169 * If we are in preen mode, preenBail won't return.
171 preenBail("Need user confirmation to re-link orphaned chain.\n");
173 (void) printf(gettext("Re-link orphaned chain into file system ? "
180 isHidden(int32_t clusterNum
)
182 /* silent failure for bogus clusters */
183 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
186 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
189 return (InUse
[clusterNum
- FIRST_CLUSTER
]->flags
& CLINFO_HIDDEN
);
193 isInUse(int32_t clusterNum
)
195 /* silent failure for bogus clusters */
196 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
199 return ((InUse
[clusterNum
- FIRST_CLUSTER
] != NULL
) &&
200 (InUse
[clusterNum
- FIRST_CLUSTER
]->dirent
!= NULL
));
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.
208 * XXX - facilitate releasing cached data for low memory situations.
210 static CachedCluster
*
211 findClusterCacheEntry(int32_t clusterNum
)
213 CachedCluster
*loop
= ClusterCache
;
215 while (loop
!= NULL
) {
216 if (loop
->clusterNum
== clusterNum
)
224 findClusterDataInTheCache(int32_t clusterNum
)
226 CachedCluster
*loop
= ClusterCache
;
229 if (loop
->clusterNum
== clusterNum
)
230 return (loop
->clusterData
.bytes
);
237 addToCache(int32_t clusterNum
, uchar_t
*buf
, int32_t *datasize
)
242 if ((new = (CachedCluster
*)malloc(sizeof (CachedCluster
))) == NULL
) {
243 perror(gettext("No memory for cached cluster info"));
246 new->clusterNum
= clusterNum
;
249 if ((cp
= (uchar_t
*)calloc(1, BytesPerCluster
)) == NULL
) {
250 perror(gettext("No memory for cached copy of cluster"));
254 (void) memcpy(cp
, buf
, *datasize
);
255 new->clusterData
.bytes
= cp
;
258 (void) fprintf(stderr
,
259 gettext("Allocation unit %d cached.\n"), clusterNum
);
261 if (ClusterCache
== NULL
) {
264 } else if (new->clusterNum
< ClusterCache
->clusterNum
) {
265 new->next
= ClusterCache
;
268 CachedCluster
*loop
= ClusterCache
;
269 CachedCluster
*trailer
= NULL
;
271 while (loop
&& new->clusterNum
> loop
->clusterNum
) {
282 CachedClusterCount
++;
283 return (new->clusterData
.bytes
);
287 seekCluster(int fd
, int32_t clusterNum
)
292 seekto
= FirstClusterOffset
+
293 ((off64_t
)clusterNum
- FIRST_CLUSTER
) * BytesPerCluster
;
294 if (lseek64(fd
, seekto
, SEEK_SET
) != seekto
) {
296 (void) fprintf(stderr
,
297 gettext("Seek to Allocation unit #%d failed: "),
299 (void) fprintf(stderr
, strerror(saveError
));
300 (void) fprintf(stderr
, "\n");
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.
315 getCluster(int fd
, int32_t clusterNum
, uchar_t
**data
, int32_t *datasize
)
317 static uchar_t
*clusterBuffer
= NULL
;
324 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
325 return (RDCLUST_BADINPUT
);
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
);
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
)) ==
338 *data
= clusterBuffer
;
339 return (RDCLUST_GOOD
);
342 if (*datasize
>= 0) {
343 *data
= clusterBuffer
;
344 (void) fprintf(stderr
,
345 gettext("Short read of allocation unit #%d\n"), clusterNum
);
348 (void) fprintf(stderr
, "Allocation unit %d:", clusterNum
);
349 (void) fprintf(stderr
, strerror(saveError
));
350 (void) fprintf(stderr
, "\n");
352 return (RDCLUST_FAIL
);
356 writeCachedCluster(int fd
, CachedCluster
*clustInfo
)
358 ssize_t bytesWritten
;
364 (void) fprintf(stderr
,
365 gettext("Allocation unit %d modified.\n"),
366 clustInfo
->clusterNum
);
368 if (seekCluster(fd
, clustInfo
->clusterNum
) == 0)
371 if ((bytesWritten
= write(fd
, clustInfo
->clusterData
.bytes
,
372 BytesPerCluster
)) != BytesPerCluster
) {
373 if (bytesWritten
< 0) {
374 perror(gettext("Failed to write modified "
377 (void) fprintf(stderr
,
378 gettext("Short write of allocation unit %d\n"),
379 clustInfo
->clusterNum
);
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.
393 #define CHUNKSIZE 1024
395 static ClusterInfo
*pool
;
406 pool
= (ClusterInfo
*)malloc(sizeof (ClusterInfo
) * CHUNKSIZE
);
410 gettext("Out of memory for cluster information"));
414 for (i
= 0; i
< CHUNKSIZE
- 1; i
++)
415 pool
[i
].nextfree
= &pool
[i
+1];
417 pool
[CHUNKSIZE
-1].nextfree
= NULL
;
420 pool
= pool
->nextfree
;
422 memset(ret
, 0, sizeof (*ret
));
427 /* Should be called with verified arguments */
430 cloneClusterInfo(int32_t clusterNum
)
432 ClusterInfo
*cl
= InUse
[clusterNum
- FIRST_CLUSTER
];
434 if (cl
->refcnt
> 1) {
435 ClusterInfo
*newCl
= newClusterInfo();
440 newCl
->path
->references
++;
441 InUse
[clusterNum
- FIRST_CLUSTER
] = newCl
;
443 return (InUse
[clusterNum
- FIRST_CLUSTER
]);
447 updateFlags(int32_t clusterNum
, int newflags
)
449 ClusterInfo
*cl
= InUse
[clusterNum
- FIRST_CLUSTER
];
451 if (cl
->flags
!= newflags
&& cl
->refcnt
> 1)
452 cl
= cloneClusterInfo(clusterNum
);
454 cl
->flags
= newflags
;
458 freeClusterInfo(ClusterInfo
*old
)
460 if (--old
->refcnt
<= 0) {
461 if (old
->path
&& --old
->path
->references
<= 0) {
462 free(old
->path
->fullName
);
465 old
->nextfree
= pool
;
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).
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())
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.
490 allocInUse(int32_t clusterNum
, ClusterInfo
**template)
494 if (InUse
[clusterNum
- FIRST_CLUSTER
] != NULL
)
495 return (CLINFO_PREVIOUSLY_ALLOCED
);
497 if (template != NULL
&& *template != NULL
)
500 newCl
= newClusterInfo();
505 InUse
[clusterNum
- FIRST_CLUSTER
] = newCl
;
508 return (CLINFO_NEWLY_ALLOCED
);
512 markFree(int32_t clusterNum
)
514 /* silent failure for bogus clusters */
515 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
518 if (InUse
[clusterNum
- FIRST_CLUSTER
]) {
519 free(InUse
[clusterNum
- FIRST_CLUSTER
]->saved
);
520 freeClusterInfo(InUse
[clusterNum
- FIRST_CLUSTER
]);
521 InUse
[clusterNum
- FIRST_CLUSTER
] = NULL
;
526 markOrphan(int fd
, int32_t clusterNum
, struct pcdir
*dp
)
528 /* silent failure for bogus clusters */
529 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
532 (void) markInUse(fd
, clusterNum
, dp
, NULL
, 0, VISIBLE
, NULL
);
533 if (InUse
[clusterNum
- FIRST_CLUSTER
] != NULL
)
534 updateFlags(clusterNum
,
535 InUse
[clusterNum
- FIRST_CLUSTER
]->flags
| CLINFO_ORPHAN
);
539 markBad(int32_t clusterNum
, uchar_t
*recovered
, int32_t recoveredLen
)
541 /* silent failure for bogus clusters */
542 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
545 (void) allocInUse(clusterNum
, NULL
);
548 (void) cloneClusterInfo(clusterNum
);
549 InUse
[clusterNum
- FIRST_CLUSTER
]->saved
= recovered
;
551 updateFlags(clusterNum
,
552 InUse
[clusterNum
- FIRST_CLUSTER
]->flags
| CLINFO_BAD
);
556 (void) fprintf(stderr
,
557 gettext("Allocation unit %d marked bad.\n"), clusterNum
);
561 clearOrphan(int32_t c
)
563 /* silent failure for bogus clusters */
564 if (c
< FIRST_CLUSTER
|| c
> LastCluster
)
566 if (InUse
[c
- FIRST_CLUSTER
] != NULL
)
568 InUse
[c
- FIRST_CLUSTER
]->flags
& ~CLINFO_ORPHAN
);
572 clearInUse(int32_t c
)
576 /* silent failure for bogus clusters */
577 if (c
< FIRST_CLUSTER
|| c
> LastCluster
)
580 clp
= &InUse
[c
- FIRST_CLUSTER
];
582 freeClusterInfo(*clp
);
588 clearAllClusters_InUse()
591 for (cc
= FIRST_CLUSTER
; cc
< LastCluster
; cc
++) {
600 clearAllClusters_InUse();
603 if ((InUse
= (ClusterInfo
**)
604 calloc(TotalClusters
, sizeof (ClusterInfo
*))) == NULL
) {
605 perror(gettext("No memory for internal table"));
615 BadClusterCount
= HiddenClusterCount
=
616 AllocedClusterCount
= FreeClusterCount
= 0;
618 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
621 } else if (isMarkedBad(c
)) {
623 * This catches the bad sectors found
624 * during thorough verify that have never been
625 * allocated to a file. Without this check, we
626 * count these guys as free.
630 } else if (isHidden(c
)) {
631 HiddenClusterCount
++;
632 } else if (isInUse(c
)) {
633 AllocedClusterCount
++;
642 * Mark orphans without directory entries as allocated.
643 * XXX - these chains should be reclaimed!
644 * XXX - merge this routine with countClusters (same loop, duh.)
650 ClusterInfo
*tmpl
= NULL
;
652 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
653 if (!freeInFAT(c
) && !badInFAT(c
) && !reservedInFAT(c
) &&
655 (void) markInUse(fd
, c
, &BlankPCDIR
, NULL
, 0, VISIBLE
,
662 getReadyToSearch(int fd
)
666 getRootDirectory(fd
);
670 static char PathName
[MAXPATHLEN
];
673 summarize(int fd
, int includeFAT
)
675 struct pcdir
*ignorep1
, *ignorep2
= NULL
;
680 ReservedClusterCount
= 0;
681 AllocedClusterCount
= 0;
682 HiddenClusterCount
= 0;
683 FileClusterCount
= 0;
684 FreeClusterCount
= 0;
690 ignorep1
= ignorep2
= NULL
;
696 getReadyToSearch(fd
);
698 * Traverse the full meta-data tree to talley what clusters
699 * are in use. The root directory is an area outside of the
700 * file space on FAT12 and FAT16 file systems. On FAT32 file
701 * systems, the root directory is in a file area cluster just
702 * like any other directory.
705 traverseFromRoot(fd
, 0, PCFS_VISIT_SUBDIRS
, PCFS_TRAVERSE_ALL
,
706 ignore
, &ignorep1
, &ignore32
, &ignorep2
, PathName
,
710 traverseDir(fd
, TheBIOSParameterBlock
.bpb32
.root_dir_clust
,
711 0, PCFS_VISIT_SUBDIRS
, PCFS_TRAVERSE_ALL
, ignore
,
712 &ignorep1
, &ignore32
, &ignorep2
, PathName
, &pathlen
);
721 isMarkedBad(int32_t clusterNum
)
723 /* silent failure for bogus clusters */
724 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
727 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
730 return (InUse
[clusterNum
- FIRST_CLUSTER
]->flags
& CLINFO_BAD
);
734 isMarkedOrphan(int32_t clusterNum
)
736 /* silent failure for bogus clusters */
737 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
740 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
743 return (InUse
[clusterNum
- FIRST_CLUSTER
]->flags
& CLINFO_ORPHAN
);
747 orphanChain(int fd
, int32_t c
, struct pcdir
*ndp
)
749 ClusterInfo
*tmpl
= NULL
;
751 /* silent failure for bogus clusters */
752 if (c
< FIRST_CLUSTER
|| c
> LastCluster
)
755 markOrphan(fd
, c
, ndp
);
760 (void) markInUse(fd
, c
, ndp
, NULL
, 0, VISIBLE
, &tmpl
);
766 findAFreeCluster(int32_t startAt
)
768 int32_t look
= startAt
;
771 if (freeInFAT(look
)) {
774 if (look
== LastCluster
)
775 look
= FIRST_CLUSTER
;
788 setEndOfDirectory(struct pcdir
*dp
)
790 dp
->pcd_filename
[0] = PCD_UNUSED
;
794 emergencyEndOfDirectory(int fd
, int32_t secondToLast
)
796 ClusterContents dirdata
;
797 int32_t dirdatasize
= 0;
799 if (readCluster(fd
, secondToLast
, &(dirdata
.bytes
), &dirdatasize
,
800 RDCLUST_DO_CACHE
) != RDCLUST_GOOD
) {
801 (void) fprintf(stderr
,
802 gettext("Unable to read allocation unit %d.\n"),
804 (void) fprintf(stderr
,
805 gettext("Cannot allocate a new allocation unit to hold an"
806 " end-of-directory marker.\nCannot access allocation unit"
807 " to overwrite existing directory entry with\nthe marker."
808 " Needed directory truncation has failed. Giving up.\n"));
812 setEndOfDirectory(dirdata
.dirp
);
813 markClusterModified(secondToLast
);
817 makeNewEndOfDirectory(struct pcdir
*entry
, int32_t secondToLast
,
818 int32_t newCluster
, ClusterContents
*newData
)
820 setEndOfDirectory(newData
->dirp
);
821 markClusterModified(newCluster
);
823 * There are two scenarios. One is that we truncated the
824 * directory in the very beginning. The other is that we
825 * truncated it in the middle or at the end. In the first
826 * scenario, the secondToLast argument is not a valid cluster
827 * (it's zero), and so we actually need to change the start
828 * cluster for the directory to this new start cluster. In
829 * the second scenario, the secondToLast cluster we received
830 * as an argument needs to be pointed at the new end of
833 if (secondToLast
== 0) {
834 updateDirEnt_Start(entry
, newCluster
);
836 writeFATEntry(secondToLast
, newCluster
);
838 markLastInFAT(newCluster
);
842 createNewEndOfDirectory(int fd
, struct pcdir
*entry
, int32_t secondToLast
)
844 ClusterContents dirdata
;
845 int32_t dirdatasize
= 0;
848 if (((freeCluster
= findAFreeCluster(secondToLast
)) != 0)) {
849 if (readCluster(fd
, freeCluster
, &(dirdata
.bytes
),
850 &dirdatasize
, RDCLUST_DO_CACHE
) == RDCLUST_GOOD
) {
852 (void) fprintf(stderr
,
853 gettext("Grabbed allocation unit #%d "
854 "for truncated\ndirectory's new end "
855 "of directory.\n"), freeCluster
);
857 makeNewEndOfDirectory(entry
, secondToLast
,
858 freeCluster
, &dirdata
);
862 if (secondToLast
== 0) {
863 if (freeCluster
== 0) {
864 (void) fprintf(stderr
, gettext("File system full.\n"));
866 (void) fprintf(stderr
,
867 gettext("Unable to read allocation unit %d.\n"),
870 (void) fprintf(stderr
,
871 gettext("Cannot allocate a new allocation unit to hold "
872 "an end-of-directory marker.\nNo existing directory "
873 "entries can be overwritten with the marker,\n"
874 "the only unit allocated to the directory is "
875 "inaccessible.\nNeeded directory truncation has failed. "
880 emergencyEndOfDirectory(fd
, secondToLast
);
885 * Given a directory entry and a cluster number, search through
886 * the cluster chain for the entry and make the cluster previous
887 * to the given cluster in the chain the last cluster in the file.
888 * The number of orphaned bytes is returned. For a chain that's
889 * a directory we need to do some special handling, since we'll be
890 * getting rid of the end of directory notice by truncating.
893 truncAtCluster(int fd
, struct pcdir
*entry
, int32_t cluster
)
895 uint32_t oldSize
, newSize
;
896 int32_t prev
, count
, follow
;
897 int dir
= (entry
->pcd_attr
& PCA_DIR
);
900 follow
= extractStartCluster(entry
);
901 while (follow
!= cluster
&& follow
>= FIRST_CLUSTER
&&
902 follow
<= LastCluster
) {
905 follow
= nextInChain(follow
);
907 if (follow
!= cluster
) {
909 * We didn't find the cluster they wanted to trunc at
910 * anywhere in the entry's chain. So we'll leave the
911 * entry alone, and return a negative value so they
912 * can know something is wrong.
917 (void) fprintf(stderr
,
918 gettext("Chain truncation at unit #%d\n"), cluster
);
921 oldSize
= extractSize(entry
);
923 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
924 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
926 updateDirEnt_Start(entry
, 0);
930 updateDirEnt_Size(entry
, newSize
);
932 createNewEndOfDirectory(fd
, entry
, prev
);
933 } else if (prev
!= 0) {
938 * We don't really know what the size of a directory is
939 * but it is important for us to know if this truncation
940 * results in an orphan with any size. The value we
941 * return from this routine for a normal file is the
942 * number of bytes left in the chain. For a directory
943 * we can't be exact, and the caller doesn't really
944 * expect us to be. For a directory the caller only
945 * cares if there are zero bytes left or more than
946 * zero bytes left. We'll return 1 to indicate
949 if ((follow
= nextInChain(follow
)) != 0)
955 * newSize should always be smaller than the old one, since
956 * we are decreasing the number of clusters allocated to the file.
958 return ((int64_t)oldSize
- (int64_t)newSize
);
961 static struct pcdir
*
962 updateOrphanedChainMetadata(int fd
, struct pcdir
*dp
, int32_t endCluster
,
965 struct pcdir
*ndp
= NULL
;
967 char *newName
= NULL
;
969 int dir
= (dp
->pcd_attr
& PCA_DIR
);
972 * If the truncation fails, (which ought not to happen),
973 * there's no need to go any further, we just return
974 * a null value for the new directory entry pointer.
976 remainder
= truncAtCluster(fd
, dp
, endCluster
);
981 * Subtract out the bad cluster from the remaining size
982 * We always assume the cluster being deleted from the
983 * file is full size, but that might not be the case
984 * for the last cluster of the file, so that is why
985 * we check for negative remainder value.
987 remainder
-= TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
988 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
993 * Build a new directory entry for the rest of the chain.
994 * Later, if the user okays it, we'll link this entry into the
995 * root directory. The new entry will start out as a
996 * copy of the truncated entry.
998 if ((remainder
!= 0) &&
999 ((newName
= nextAvailableCHKName(&chosenName
)) != NULL
) &&
1000 ((ndp
= newDirEnt(dp
)) != NULL
)) {
1003 (void) fprintf(stderr
,
1004 gettext("Orphaned directory chain.\n"));
1006 (void) fprintf(stderr
,
1007 gettext("Orphaned chain, %u bytes.\n"),
1008 (uint32_t)remainder
);
1011 updateDirEnt_Size(ndp
, (uint32_t)remainder
);
1013 updateDirEnt_Start(ndp
, nextInChain(endCluster
));
1015 updateDirEnt_Start(ndp
, endCluster
);
1016 updateDirEnt_Name(ndp
, newName
);
1017 addEntryToCHKList(chosenName
);
1025 * split a cluster allocation chain into two cluster chains
1026 * around a given cluster (problemCluster). This results in two
1027 * separate directory entries; the original (dp), and one we hope
1028 * to create and return a pointer to to the caller (*newdp).
1029 * This second entry is the orphan chain, and it may end up in
1030 * the root directory as a FILEnnnn.CHK file. We also return the
1031 * starting cluster of the orphan chain to the caller (*orphanStart).
1034 splitChain(int fd
, struct pcdir
*dp
, int32_t problemCluster
,
1035 struct pcdir
**newdp
, int32_t *orphanStart
)
1037 struct pcdir
*ndp
= NULL
;
1038 int isBad
= isMarkedBad(problemCluster
);
1040 ndp
= updateOrphanedChainMetadata(fd
, dp
, problemCluster
, isBad
);
1042 clearInUse(problemCluster
);
1044 clearOrphan(problemCluster
);
1045 *orphanStart
= nextInChain(problemCluster
);
1046 orphanChain(fd
, *orphanStart
, ndp
);
1047 markBadInFAT(problemCluster
);
1049 *orphanStart
= problemCluster
;
1050 orphanChain(fd
, problemCluster
, ndp
);
1057 * User has requested that an orphaned cluster chain be freed back
1058 * into the file area.
1061 freeOrphan(int32_t c
)
1066 * Free the directory entry we explicitly created for
1067 * the orphaned clusters.
1069 free(InUse
[c
- FIRST_CLUSTER
]->dirent
);
1071 * Then mark the clusters themselves as available.
1082 * Rewrite the InUse field for a cluster chain. Can be used on a partial
1083 * chain if provided with a stopAtCluster.
1086 redoInUse(int fd
, int32_t c
, struct pcdir
*ndp
, int32_t stopAtCluster
)
1088 while (c
&& c
!= stopAtCluster
) {
1090 (void) markInUse(fd
, c
, ndp
, NULL
, 0, VISIBLE
, NULL
);
1095 static struct pcdir
*
1096 orphanDirEntLookup(int32_t clusterNum
)
1098 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1101 if (isInUse(clusterNum
)) {
1102 return (InUse
[clusterNum
- FIRST_CLUSTER
]->dirent
);
1109 orphanSizeLookup(int32_t clusterNum
)
1111 /* silent failure for bogus clusters */
1112 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1115 if (isInUse(clusterNum
)) {
1116 return (extractSize(InUse
[clusterNum
- FIRST_CLUSTER
]->dirent
));
1125 * User has requested that an orphaned cluster chain be brought back
1126 * into the file system. So we have to make a new directory entry
1127 * in the root directory and point it at the cluster chain.
1130 linkOrphan(int fd
, int32_t start
)
1132 struct pcdir
*newEnt
= NULL
;
1135 if ((dp
= orphanDirEntLookup(start
)) != NULL
) {
1136 newEnt
= addRootDirEnt(fd
, dp
);
1138 (void) printf(gettext("Re-link of orphaned chain failed."
1139 " Allocation units will remain orphaned.\n"));
1142 * A cluster isn't really InUse() unless it is referenced,
1143 * so if newEnt is NULL here, we are in effect using markInUse()
1144 * to note that the cluster is NOT in use.
1146 redoInUse(fd
, start
, newEnt
, 0);
1150 * relinkCreatedOrphans
1152 * While marking clusters as bad, we can create orphan cluster
1153 * chains. Since we were the ones doing the marking, we were able to
1154 * keep track of the orphans we created. Now we want to go through
1155 * all those chains and either get them back into the file system or
1156 * free them depending on the user's input.
1159 relinkCreatedOrphans(int fd
)
1163 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
1164 if (isMarkedOrphan(c
)) {
1165 if (OkayToRelink
&& askAboutRelink(c
)) {
1167 } else if (askAboutFreeing(c
)) {
1178 * We want to find orphans not represented in the meta-data.
1179 * These are chains marked in the FAT as being in use but
1180 * not referenced anywhere by any directory entries.
1181 * We'll go through the whole FAT and mark the first cluster
1182 * in any such chain as an orphan. Then we can just use
1183 * the relinkCreatedOrphans routine to get them back into the
1184 * file system or free'ed depending on the user's input.
1187 relinkFATOrphans(int fd
)
1189 struct pcdir
*ndp
= NULL
;
1191 int32_t bpc
, newSize
;
1195 for (c
= FIRST_CLUSTER
; c
< LastCluster
; c
++) {
1196 if (freeInFAT(c
) || badInFAT(c
) ||
1197 reservedInFAT(c
) || isInUse(c
))
1201 while (n
= nextInChain(n
))
1203 bpc
= TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1204 TheBIOSParameterBlock
.bpb
.bytes_per_sector
;
1206 if (((newName
= nextAvailableCHKName(&chosenName
)) != NULL
) &&
1207 ((ndp
= newDirEnt(NULL
)) != NULL
)) {
1208 updateDirEnt_Size(ndp
, newSize
);
1209 updateDirEnt_Start(ndp
, c
);
1210 updateDirEnt_Name(ndp
, newName
);
1211 addEntryToCHKList(chosenName
);
1213 orphanChain(fd
, c
, ndp
);
1215 relinkCreatedOrphans(fd
);
1219 relinkOrphans(int fd
)
1221 relinkCreatedOrphans(fd
);
1222 relinkFATOrphans(fd
);
1226 checkForFATLoop(int32_t clusterNum
)
1228 int32_t prev
= clusterNum
;
1231 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1234 follow
= nextInChain(clusterNum
);
1235 while (follow
!= clusterNum
&& follow
>= FIRST_CLUSTER
&&
1236 follow
<= LastCluster
) {
1238 follow
= nextInChain(follow
);
1240 if (follow
== clusterNum
) {
1242 * We found a loop. Eradicate it by changing
1243 * the last cluster in the loop to be last
1244 * in the chain instead instead of pointing
1245 * back to the first cluster.
1247 markLastInFAT(prev
);
1252 sharedChainError(int fd
, int32_t clusterNum
, struct pcdir
*badEntry
)
1255 * If we have shared clusters, it is either because the
1256 * cluster somehow got assigned to multiple files and/or
1257 * because of a loop in the cluster chain. In either
1258 * case we want to truncate the offending file at the
1259 * cluster of contention. Then, we will want to run
1260 * through the remainder of the chain. If we find ourselves
1261 * back at the top, we will know there is a loop in the
1262 * FAT we need to remove.
1265 (void) fprintf(stderr
,
1266 gettext("Truncating chain due to duplicate allocation of "
1267 "unit %d.\n"), clusterNum
);
1269 * Note that we don't orphan anything here, because the duplicate
1270 * part of the chain may be part of another valid chain.
1272 (void) truncAtCluster(fd
, badEntry
, clusterNum
);
1273 checkForFATLoop(clusterNum
);
1277 truncChainWithBadCluster(int fd
, struct pcdir
*dp
, int32_t startCluster
)
1279 struct pcdir
*orphanEntry
;
1280 int32_t orphanStartCluster
;
1281 int32_t c
= startCluster
;
1284 if (isMarkedBad(c
)) {
1286 * splitChain() truncates the current guy and
1287 * then makes an orphan chain out of the remaining
1288 * clusters. When we come back from the split
1289 * we'll want to continue looking for bad clusters
1290 * in the orphan chain.
1292 splitChain(fd
, dp
, c
,
1293 &orphanEntry
, &orphanStartCluster
);
1295 * There is a chance that we weren't able or weren't
1296 * required to make a directory entry for the
1297 * remaining clusters. In that case we won't go
1298 * on, because we couldn't make any more splits
1301 if (orphanEntry
== NULL
)
1303 c
= orphanStartCluster
;
1312 nextInChain(int32_t currentCluster
)
1314 int32_t nextCluster
;
1316 /* silent failure for bogus clusters */
1317 if (currentCluster
< FIRST_CLUSTER
|| currentCluster
> LastCluster
)
1321 * Look up FAT entry of next link in cluster chain,
1322 * if this one is the last one return 0 as the next link.
1324 nextCluster
= readFATEntry(currentCluster
);
1325 if (nextCluster
< FIRST_CLUSTER
|| nextCluster
> LastCluster
)
1328 return (nextCluster
);
1332 * findImpactedCluster
1334 * Called when someone modifies what they believe might be a cached
1335 * cluster entry, but when they only have a directory entry pointer
1336 * and not the cluster number. We have to go dig up what cluster
1337 * they are modifying.
1340 findImpactedCluster(struct pcdir
*modified
)
1342 CachedCluster
*loop
;
1344 * Check to see if it's in the root directory first
1346 if (!IsFAT32
&& ((uchar_t
*)modified
>= TheRootDir
.bytes
) &&
1347 ((uchar_t
*)modified
< TheRootDir
.bytes
+ RootDirSize
))
1348 return (FAKE_ROOTDIR_CLUST
);
1350 loop
= ClusterCache
;
1352 if (((uchar_t
*)modified
>= loop
->clusterData
.bytes
) &&
1353 ((uchar_t
*)modified
<
1354 (loop
->clusterData
.bytes
+ BytesPerCluster
))) {
1355 return (loop
->clusterNum
);
1360 * Guess it wasn't cached after all...
1366 writeClusterMods(int fd
)
1368 CachedCluster
*loop
= ClusterCache
;
1372 writeCachedCluster(fd
, loop
);
1378 squirrelPath(struct nameinfo
*pathInfo
, int32_t clusterNum
)
1380 /* silent failure for bogus clusters */
1381 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1383 if (InUse
[clusterNum
- FIRST_CLUSTER
] == NULL
)
1385 InUse
[clusterNum
- FIRST_CLUSTER
]->path
= pathInfo
;
1389 markInUse(int fd
, int32_t clusterNum
, struct pcdir
*referencer
, struct
1390 pcdir
*longRef
, int32_t longStartCluster
, int isHiddenFile
,
1391 ClusterInfo
**template)
1396 /* silent failure for bogus clusters */
1397 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1398 return (CLINFO_NEWLY_ALLOCED
);
1400 alreadyMarked
= allocInUse(clusterNum
, template);
1401 if ((alreadyMarked
== CLINFO_PREVIOUSLY_ALLOCED
) &&
1402 (isInUse(clusterNum
))) {
1403 sharedChainError(fd
, clusterNum
, referencer
);
1404 return (CLINFO_PREVIOUSLY_ALLOCED
);
1406 cl
= InUse
[clusterNum
- FIRST_CLUSTER
];
1408 * If Cl is newly allocated (refcnt <= 1) we must fill in the fields.
1409 * If Cl has different fields, we must clone it.
1412 if (cl
->refcnt
<= 1 || cl
->dirent
!= referencer
||
1413 cl
->longent
!= longRef
||
1414 cl
->longEntStartClust
!= longStartCluster
) {
1416 cl
= cloneClusterInfo(clusterNum
);
1417 cl
->dirent
= referencer
;
1418 cl
->longent
= longRef
;
1419 cl
->longEntStartClust
= longStartCluster
;
1421 cl
->flags
|= CLINFO_HIDDEN
;
1424 * Return cl as the template to use for other clusters in
1430 return (CLINFO_NEWLY_ALLOCED
);
1434 markClusterModified(int32_t clusterNum
)
1438 if (clusterNum
== FAKE_ROOTDIR_CLUST
) {
1439 RootDirModified
= 1;
1443 /* silent failure for bogus clusters */
1444 if (clusterNum
< FIRST_CLUSTER
|| clusterNum
> LastCluster
)
1447 if (c
= findClusterCacheEntry(clusterNum
)) {
1450 (void) fprintf(stderr
,
1451 gettext("Unexpected internal error: "
1452 "Missing cache entry [%d]\n"), clusterNum
);
1459 * caller wants to read cluster clusterNum. We should return
1460 * a pointer to the read data in "data", and fill in the number
1461 * of bytes read in "datasize". If shouldCache is non-zero
1462 * we should allocate cache space to the cluster, otherwise we
1463 * just return a pointer to a buffer we re-use whenever cacheing
1467 readCluster(int fd
, int32_t clusterNum
, uchar_t
**data
, int32_t *datasize
,
1474 if ((*data
= findClusterDataInTheCache(clusterNum
)) != NULL
) {
1475 *datasize
= BytesPerCluster
;
1476 return (RDCLUST_GOOD
);
1479 rv
= getCluster(fd
, clusterNum
, &newBuf
, datasize
);
1480 if (rv
!= RDCLUST_GOOD
)
1484 * Caller requested we NOT cache the data from this read.
1485 * So, we just return a pointer to the common data buffer.
1487 if (shouldCache
== 0) {
1493 * Caller requested we cache the data from this read.
1494 * So, if we have some data, add it to the cache by
1495 * copying it out of the common buffer into new storage.
1498 *data
= addToCache(clusterNum
, newBuf
, datasize
);
1503 findBadClusters(int fd
)
1505 int32_t clusterCount
;
1509 BadClusterCount
= 0;
1511 (void) printf(gettext("** Scanning allocation units\n"));
1512 for (clusterCount
= FIRST_CLUSTER
;
1513 clusterCount
< LastCluster
; clusterCount
++) {
1514 if (readCluster(fd
, clusterCount
,
1515 &data
, &datasize
, RDCLUST_DONT_CACHE
) < 0) {
1517 (void) fprintf(stderr
,
1518 gettext("\nUnreadable allocation unit %d.\n"),
1520 markBad(clusterCount
, data
, datasize
);
1523 * Progress meter, display a '.' for every 1000 clusters
1524 * processed. We don't want to display this when
1525 * we are in verbose mode; verbose mode progress is
1526 * shown by displaying each file name as it is found.
1528 if (!Verbose
&& clusterCount
% 1000 == 0)
1531 (void) printf(gettext("..done\n"));
1535 scanAndFixMetadata(int fd
)
1538 * First we initialize a few things.
1541 getReadyToSearch(fd
);
1542 createCHKNameList(fd
);
1545 * Make initial scan, taking into account any effect that
1546 * the bad clusters we may have already discovered have
1547 * on meta-data. We may break up some cluster chains
1548 * during this period. The relinkCreatedOrphans() call
1549 * will then give the user the chance to recover stuff
1552 (void) printf(gettext("** Scanning file system meta-data\n"));
1553 summarize(fd
, NO_FAT_IN_SUMMARY
);
1555 printSummary(stderr
);
1556 (void) printf(gettext("** Correcting any meta-data discrepancies\n"));
1557 relinkCreatedOrphans(fd
);
1560 * Clear our usage table and go back over everything, this
1561 * time including looking for clusters floating free in the FAT.
1562 * This may include clusters the user chose to free during the
1566 summarize(fd
, INCLUDE_FAT_IN_SUMMARY
);
1571 printSummary(FILE *outDest
)
1573 (void) fprintf(outDest
,
1574 gettext("%llu bytes.\n"),
1576 TotalClusters
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1577 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1578 (void) fprintf(outDest
,
1579 gettext("%llu bytes in bad sectors.\n"),
1581 BadClusterCount
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1582 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1583 (void) fprintf(outDest
,
1584 gettext("%llu bytes in %d directories.\n"),
1586 DirClusterCount
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1587 TheBIOSParameterBlock
.bpb
.bytes_per_sector
, DirCount
);
1588 if (HiddenClusterCount
) {
1589 (void) fprintf(outDest
,
1590 gettext("%llu bytes in %d hidden files.\n"),
1591 (uint64_t)HiddenClusterCount
*
1592 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1593 TheBIOSParameterBlock
.bpb
.bytes_per_sector
,
1596 (void) fprintf(outDest
,
1597 gettext("%llu bytes in %d files.\n"),
1599 FileClusterCount
* TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1600 TheBIOSParameterBlock
.bpb
.bytes_per_sector
, FileCount
);
1601 (void) fprintf(outDest
,
1602 gettext("%llu bytes free.\n"), (uint64_t)FreeClusterCount
*
1603 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1604 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1605 (void) fprintf(outDest
,
1606 gettext("%d bytes per allocation unit.\n"),
1607 TheBIOSParameterBlock
.bpb
.sectors_per_cluster
*
1608 TheBIOSParameterBlock
.bpb
.bytes_per_sector
);
1609 (void) fprintf(outDest
,
1610 gettext("%d total allocation units.\n"), TotalClusters
);
1611 if (ReservedClusterCount
)
1612 (void) fprintf(outDest
, gettext("%d reserved allocation units.\n"),
1613 ReservedClusterCount
);
1614 (void) fprintf(outDest
,
1615 gettext("%d available allocation units.\n"), FreeClusterCount
);