17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate * with the License.
87c478bd9Sstevel@tonic-gate *
97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate * and limitations under the License.
137c478bd9Sstevel@tonic-gate *
147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate *
207c478bd9Sstevel@tonic-gate * CDDL HEADER END
217c478bd9Sstevel@tonic-gate */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
247c478bd9Sstevel@tonic-gate * All rights reserved.
2548bbca81SDaniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate * fsck_pcfs -- routines for manipulating clusters.
307c478bd9Sstevel@tonic-gate */
317c478bd9Sstevel@tonic-gate #include <stdio.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <unistd.h>
347c478bd9Sstevel@tonic-gate #include <stdlib.h>
357c478bd9Sstevel@tonic-gate #include <libintl.h>
367c478bd9Sstevel@tonic-gate #include <errno.h>
377c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/pc_fs.h>
397c478bd9Sstevel@tonic-gate #include <sys/fs/pc_dir.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/pc_label.h>
417c478bd9Sstevel@tonic-gate #include "pcfs_common.h"
427c478bd9Sstevel@tonic-gate #include "fsck_pcfs.h"
437c478bd9Sstevel@tonic-gate
447c478bd9Sstevel@tonic-gate extern ClusterContents TheRootDir;
457c478bd9Sstevel@tonic-gate extern off64_t FirstClusterOffset;
467c478bd9Sstevel@tonic-gate extern off64_t PartitionOffset;
477c478bd9Sstevel@tonic-gate extern int32_t BytesPerCluster;
487c478bd9Sstevel@tonic-gate extern int32_t TotalClusters;
497c478bd9Sstevel@tonic-gate extern int32_t LastCluster;
507c478bd9Sstevel@tonic-gate extern int32_t RootDirSize;
517c478bd9Sstevel@tonic-gate extern int32_t FATSize;
527c478bd9Sstevel@tonic-gate extern bpb_t TheBIOSParameterBlock;
537c478bd9Sstevel@tonic-gate extern short FATEntrySize;
547c478bd9Sstevel@tonic-gate extern int RootDirModified;
557c478bd9Sstevel@tonic-gate extern int OkayToRelink;
567c478bd9Sstevel@tonic-gate extern int ReadOnly;
577c478bd9Sstevel@tonic-gate extern int IsFAT32;
587c478bd9Sstevel@tonic-gate extern int Verbose;
597c478bd9Sstevel@tonic-gate
607c478bd9Sstevel@tonic-gate static struct pcdir BlankPCDIR;
617c478bd9Sstevel@tonic-gate static CachedCluster *ClusterCache;
627c478bd9Sstevel@tonic-gate static ClusterInfo **InUse;
637c478bd9Sstevel@tonic-gate static int32_t ReservedClusterCount;
647c478bd9Sstevel@tonic-gate static int32_t AllocedClusterCount;
657c478bd9Sstevel@tonic-gate static int32_t FreeClusterCount;
667c478bd9Sstevel@tonic-gate static int32_t BadClusterCount;
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate /*
697c478bd9Sstevel@tonic-gate * Internal statistics
707c478bd9Sstevel@tonic-gate */
717c478bd9Sstevel@tonic-gate static int32_t CachedClusterCount;
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate int32_t HiddenClusterCount;
747c478bd9Sstevel@tonic-gate int32_t FileClusterCount;
757c478bd9Sstevel@tonic-gate int32_t DirClusterCount;
767c478bd9Sstevel@tonic-gate int32_t HiddenFileCount;
777c478bd9Sstevel@tonic-gate int32_t FileCount;
787c478bd9Sstevel@tonic-gate int32_t DirCount;
797c478bd9Sstevel@tonic-gate
807c478bd9Sstevel@tonic-gate static int32_t orphanSizeLookup(int32_t clusterNum);
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate static void
freeNameInfo(int32_t clusterNum)837c478bd9Sstevel@tonic-gate freeNameInfo(int32_t clusterNum)
847c478bd9Sstevel@tonic-gate {
857c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
867c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
877c478bd9Sstevel@tonic-gate return;
887c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
897c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER]->path->references > 1) {
907c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->path->references--;
917c478bd9Sstevel@tonic-gate } else {
927c478bd9Sstevel@tonic-gate free(InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
937c478bd9Sstevel@tonic-gate free(InUse[clusterNum - FIRST_CLUSTER]->path);
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->path = NULL;
967c478bd9Sstevel@tonic-gate }
977c478bd9Sstevel@tonic-gate }
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate static void
printOrphanPath(int32_t clusterNum)1007c478bd9Sstevel@tonic-gate printOrphanPath(int32_t clusterNum)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
1037c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1047c478bd9Sstevel@tonic-gate return;
1057c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER]->path != NULL) {
1067c478bd9Sstevel@tonic-gate (void) printf(gettext("\nOrphaned allocation units originally "
1077c478bd9Sstevel@tonic-gate "allocated to:\n"));
1087c478bd9Sstevel@tonic-gate (void) printf("%s\n",
1097c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->path->fullName);
1107c478bd9Sstevel@tonic-gate freeNameInfo(clusterNum);
1117c478bd9Sstevel@tonic-gate } else {
1127c478bd9Sstevel@tonic-gate (void) printf(gettext("\nOrphaned allocation units originally "
1137c478bd9Sstevel@tonic-gate "allocated to an unknown file or directory:\n"));
1147c478bd9Sstevel@tonic-gate (void) printf(gettext("Orphaned chain begins with allocation "
1157c478bd9Sstevel@tonic-gate "unit %d.\n"), clusterNum);
1167c478bd9Sstevel@tonic-gate }
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate
1197c478bd9Sstevel@tonic-gate static void
printOrphanSize(int32_t clusterNum)1207c478bd9Sstevel@tonic-gate printOrphanSize(int32_t clusterNum)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate int32_t size = orphanSizeLookup(clusterNum);
1237c478bd9Sstevel@tonic-gate
1247c478bd9Sstevel@tonic-gate if (size > 0) {
1257c478bd9Sstevel@tonic-gate (void) printf(gettext("%d bytes in the orphaned chain of "
1267c478bd9Sstevel@tonic-gate "allocation units.\n"), size);
1277c478bd9Sstevel@tonic-gate if (Verbose) {
1287c478bd9Sstevel@tonic-gate (void) printf(gettext("[Starting at allocation "
1297c478bd9Sstevel@tonic-gate "unit %d]\n"), clusterNum);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate }
1337c478bd9Sstevel@tonic-gate
1347c478bd9Sstevel@tonic-gate static void
printOrphanInfo(int32_t clusterNum)1357c478bd9Sstevel@tonic-gate printOrphanInfo(int32_t clusterNum)
1367c478bd9Sstevel@tonic-gate {
1377c478bd9Sstevel@tonic-gate printOrphanPath(clusterNum);
1387c478bd9Sstevel@tonic-gate printOrphanSize(clusterNum);
1397c478bd9Sstevel@tonic-gate }
1407c478bd9Sstevel@tonic-gate
1417c478bd9Sstevel@tonic-gate static int
askAboutFreeing(int32_t clusterNum)1427c478bd9Sstevel@tonic-gate askAboutFreeing(int32_t clusterNum)
1437c478bd9Sstevel@tonic-gate {
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate * If it is not OkayToRelink, we haven't already printed the size
1467c478bd9Sstevel@tonic-gate * of the orphaned chain.
1477c478bd9Sstevel@tonic-gate */
1487c478bd9Sstevel@tonic-gate if (!OkayToRelink)
1497c478bd9Sstevel@tonic-gate printOrphanInfo(clusterNum);
1507c478bd9Sstevel@tonic-gate /*
1517c478bd9Sstevel@tonic-gate * If we are in preen mode, preenBail won't return.
1527c478bd9Sstevel@tonic-gate */
1537c478bd9Sstevel@tonic-gate preenBail("Need user confirmation to free orphaned chain.\n");
1547c478bd9Sstevel@tonic-gate
1557c478bd9Sstevel@tonic-gate (void) printf(
1567c478bd9Sstevel@tonic-gate gettext("Free the allocation units in the orphaned chain ? "
1577c478bd9Sstevel@tonic-gate "(y/n) "));
1587c478bd9Sstevel@tonic-gate return (yes());
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate
1617c478bd9Sstevel@tonic-gate static int
askAboutRelink(int32_t clusterNum)1627c478bd9Sstevel@tonic-gate askAboutRelink(int32_t clusterNum)
1637c478bd9Sstevel@tonic-gate {
1647c478bd9Sstevel@tonic-gate /*
1657c478bd9Sstevel@tonic-gate * Display the size of the chain for the user to consider.
1667c478bd9Sstevel@tonic-gate */
1677c478bd9Sstevel@tonic-gate printOrphanInfo(clusterNum);
1687c478bd9Sstevel@tonic-gate /*
1697c478bd9Sstevel@tonic-gate * If we are in preen mode, preenBail won't return.
1707c478bd9Sstevel@tonic-gate */
1717c478bd9Sstevel@tonic-gate preenBail("Need user confirmation to re-link orphaned chain.\n");
1727c478bd9Sstevel@tonic-gate
1737c478bd9Sstevel@tonic-gate (void) printf(gettext("Re-link orphaned chain into file system ? "
1747c478bd9Sstevel@tonic-gate "(y/n) "));
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate return (yes());
1777c478bd9Sstevel@tonic-gate }
1787c478bd9Sstevel@tonic-gate
1797c478bd9Sstevel@tonic-gate static int
isHidden(int32_t clusterNum)1807c478bd9Sstevel@tonic-gate isHidden(int32_t clusterNum)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
1837c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1847c478bd9Sstevel@tonic-gate return (0);
1857c478bd9Sstevel@tonic-gate
1867c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
1877c478bd9Sstevel@tonic-gate return (0);
1887c478bd9Sstevel@tonic-gate
1897c478bd9Sstevel@tonic-gate return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_HIDDEN);
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate
1927c478bd9Sstevel@tonic-gate static int
isInUse(int32_t clusterNum)1937c478bd9Sstevel@tonic-gate isInUse(int32_t clusterNum)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
1967c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
1977c478bd9Sstevel@tonic-gate return (0);
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate return ((InUse[clusterNum - FIRST_CLUSTER] != NULL) &&
2007c478bd9Sstevel@tonic-gate (InUse[clusterNum - FIRST_CLUSTER]->dirent != NULL));
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate
2037c478bd9Sstevel@tonic-gate /*
2047c478bd9Sstevel@tonic-gate * Caller's may request that we cache the data from a readCluster.
2057c478bd9Sstevel@tonic-gate * The xxxClusterxxxCachexxx routines handle looking for cached data
2067c478bd9Sstevel@tonic-gate * or initially caching the data.
2077c478bd9Sstevel@tonic-gate *
2087c478bd9Sstevel@tonic-gate * XXX - facilitate releasing cached data for low memory situations.
2097c478bd9Sstevel@tonic-gate */
2107c478bd9Sstevel@tonic-gate static CachedCluster *
findClusterCacheEntry(int32_t clusterNum)2117c478bd9Sstevel@tonic-gate findClusterCacheEntry(int32_t clusterNum)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate CachedCluster *loop = ClusterCache;
2147c478bd9Sstevel@tonic-gate
2157c478bd9Sstevel@tonic-gate while (loop != NULL) {
2167c478bd9Sstevel@tonic-gate if (loop->clusterNum == clusterNum)
2177c478bd9Sstevel@tonic-gate return (loop);
2187c478bd9Sstevel@tonic-gate loop = loop->next;
2197c478bd9Sstevel@tonic-gate }
2207c478bd9Sstevel@tonic-gate return (NULL);
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate
2237c478bd9Sstevel@tonic-gate static uchar_t *
findClusterDataInTheCache(int32_t clusterNum)2247c478bd9Sstevel@tonic-gate findClusterDataInTheCache(int32_t clusterNum)
2257c478bd9Sstevel@tonic-gate {
2267c478bd9Sstevel@tonic-gate CachedCluster *loop = ClusterCache;
2277c478bd9Sstevel@tonic-gate
2287c478bd9Sstevel@tonic-gate while (loop) {
2297c478bd9Sstevel@tonic-gate if (loop->clusterNum == clusterNum)
2307c478bd9Sstevel@tonic-gate return (loop->clusterData.bytes);
2317c478bd9Sstevel@tonic-gate loop = loop->next;
2327c478bd9Sstevel@tonic-gate }
2337c478bd9Sstevel@tonic-gate return (NULL);
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate
2367c478bd9Sstevel@tonic-gate static uchar_t *
addToCache(int32_t clusterNum,uchar_t * buf,int32_t * datasize)2377c478bd9Sstevel@tonic-gate addToCache(int32_t clusterNum, uchar_t *buf, int32_t *datasize)
2387c478bd9Sstevel@tonic-gate {
2397c478bd9Sstevel@tonic-gate CachedCluster *new;
2407c478bd9Sstevel@tonic-gate uchar_t *cp;
2417c478bd9Sstevel@tonic-gate
2427c478bd9Sstevel@tonic-gate if ((new = (CachedCluster *)malloc(sizeof (CachedCluster))) == NULL) {
2437c478bd9Sstevel@tonic-gate perror(gettext("No memory for cached cluster info"));
2447c478bd9Sstevel@tonic-gate return (buf);
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate new->clusterNum = clusterNum;
2477c478bd9Sstevel@tonic-gate new->modified = 0;
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gate if ((cp = (uchar_t *)calloc(1, BytesPerCluster)) == NULL) {
2507c478bd9Sstevel@tonic-gate perror(gettext("No memory for cached copy of cluster"));
2517c478bd9Sstevel@tonic-gate free(new);
2527c478bd9Sstevel@tonic-gate return (buf);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate (void) memcpy(cp, buf, *datasize);
2557c478bd9Sstevel@tonic-gate new->clusterData.bytes = cp;
2567c478bd9Sstevel@tonic-gate
2577c478bd9Sstevel@tonic-gate if (Verbose) {
2587c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
2597c478bd9Sstevel@tonic-gate gettext("Allocation unit %d cached.\n"), clusterNum);
2607c478bd9Sstevel@tonic-gate }
2617c478bd9Sstevel@tonic-gate if (ClusterCache == NULL) {
2627c478bd9Sstevel@tonic-gate ClusterCache = new;
2637c478bd9Sstevel@tonic-gate new->next = NULL;
2647c478bd9Sstevel@tonic-gate } else if (new->clusterNum < ClusterCache->clusterNum) {
2657c478bd9Sstevel@tonic-gate new->next = ClusterCache;
2667c478bd9Sstevel@tonic-gate ClusterCache = new;
2677c478bd9Sstevel@tonic-gate } else {
2687c478bd9Sstevel@tonic-gate CachedCluster *loop = ClusterCache;
2697c478bd9Sstevel@tonic-gate CachedCluster *trailer = NULL;
2707c478bd9Sstevel@tonic-gate
2717c478bd9Sstevel@tonic-gate while (loop && new->clusterNum > loop->clusterNum) {
2727c478bd9Sstevel@tonic-gate trailer = loop;
2737c478bd9Sstevel@tonic-gate loop = loop->next;
2747c478bd9Sstevel@tonic-gate }
2757c478bd9Sstevel@tonic-gate trailer->next = new;
2767c478bd9Sstevel@tonic-gate if (loop) {
2777c478bd9Sstevel@tonic-gate new->next = loop;
2787c478bd9Sstevel@tonic-gate } else {
2797c478bd9Sstevel@tonic-gate new->next = NULL;
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate }
2827c478bd9Sstevel@tonic-gate CachedClusterCount++;
2837c478bd9Sstevel@tonic-gate return (new->clusterData.bytes);
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate
2867c478bd9Sstevel@tonic-gate static int
seekCluster(int fd,int32_t clusterNum)2877c478bd9Sstevel@tonic-gate seekCluster(int fd, int32_t clusterNum)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate off64_t seekto;
2907c478bd9Sstevel@tonic-gate int saveError;
2917c478bd9Sstevel@tonic-gate
2927c478bd9Sstevel@tonic-gate seekto = FirstClusterOffset +
2937c478bd9Sstevel@tonic-gate ((off64_t)clusterNum - FIRST_CLUSTER) * BytesPerCluster;
2947c478bd9Sstevel@tonic-gate if (lseek64(fd, seekto, SEEK_SET) != seekto) {
2957c478bd9Sstevel@tonic-gate saveError = errno;
2967c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
2977c478bd9Sstevel@tonic-gate gettext("Seek to Allocation unit #%d failed: "),
2987c478bd9Sstevel@tonic-gate clusterNum);
2997c478bd9Sstevel@tonic-gate (void) fprintf(stderr, strerror(saveError));
3007c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
3017c478bd9Sstevel@tonic-gate return (0);
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate return (1);
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate
3067c478bd9Sstevel@tonic-gate /*
3077c478bd9Sstevel@tonic-gate * getcluster
3087c478bd9Sstevel@tonic-gate * Get cluster bytes off the disk. We always read those bytes into
30948bbca81SDaniel Hoffman * the same static buffer. If the caller wants its own copy of the
31048bbca81SDaniel Hoffman * data it'll have to make its own copy. We'll return all the data
3117c478bd9Sstevel@tonic-gate * read, even if it's short of a full cluster. This is for future use
3127c478bd9Sstevel@tonic-gate * when we might want to relocate any salvagable data from bad clusters.
3137c478bd9Sstevel@tonic-gate */
3147c478bd9Sstevel@tonic-gate static int
getCluster(int fd,int32_t clusterNum,uchar_t ** data,int32_t * datasize)3157c478bd9Sstevel@tonic-gate getCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize)
3167c478bd9Sstevel@tonic-gate {
3177c478bd9Sstevel@tonic-gate static uchar_t *clusterBuffer = NULL;
3187c478bd9Sstevel@tonic-gate int saveError;
3197c478bd9Sstevel@tonic-gate int try;
3207c478bd9Sstevel@tonic-gate
3217c478bd9Sstevel@tonic-gate *datasize = 0;
3227c478bd9Sstevel@tonic-gate *data = NULL;
3237c478bd9Sstevel@tonic-gate
3247c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
3257c478bd9Sstevel@tonic-gate return (RDCLUST_BADINPUT);
3267c478bd9Sstevel@tonic-gate
3277c478bd9Sstevel@tonic-gate if (clusterBuffer == NULL &&
3287c478bd9Sstevel@tonic-gate (clusterBuffer = (uchar_t *)malloc(BytesPerCluster)) == NULL) {
3297c478bd9Sstevel@tonic-gate perror(gettext("No memory for a cluster data buffer"));
3307c478bd9Sstevel@tonic-gate return (RDCLUST_MEMERR);
3317c478bd9Sstevel@tonic-gate }
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate for (try = 0; try < RDCLUST_MAX_RETRY; try++) {
3347c478bd9Sstevel@tonic-gate if (!seekCluster(fd, clusterNum))
3357c478bd9Sstevel@tonic-gate return (RDCLUST_FAIL);
3367c478bd9Sstevel@tonic-gate if ((*datasize = read(fd, clusterBuffer, BytesPerCluster)) ==
3377c478bd9Sstevel@tonic-gate BytesPerCluster) {
3387c478bd9Sstevel@tonic-gate *data = clusterBuffer;
3397c478bd9Sstevel@tonic-gate return (RDCLUST_GOOD);
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate if (*datasize >= 0) {
3437c478bd9Sstevel@tonic-gate *data = clusterBuffer;
3447c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3457c478bd9Sstevel@tonic-gate gettext("Short read of allocation unit #%d\n"), clusterNum);
3467c478bd9Sstevel@tonic-gate } else {
3477c478bd9Sstevel@tonic-gate saveError = errno;
3487c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "Allocation unit %d:", clusterNum);
3497c478bd9Sstevel@tonic-gate (void) fprintf(stderr, strerror(saveError));
3507c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
3517c478bd9Sstevel@tonic-gate }
3527c478bd9Sstevel@tonic-gate return (RDCLUST_FAIL);
3537c478bd9Sstevel@tonic-gate }
3547c478bd9Sstevel@tonic-gate
3557c478bd9Sstevel@tonic-gate static void
writeCachedCluster(int fd,CachedCluster * clustInfo)3567c478bd9Sstevel@tonic-gate writeCachedCluster(int fd, CachedCluster *clustInfo)
3577c478bd9Sstevel@tonic-gate {
3587c478bd9Sstevel@tonic-gate ssize_t bytesWritten;
3597c478bd9Sstevel@tonic-gate
3607c478bd9Sstevel@tonic-gate if (ReadOnly)
3617c478bd9Sstevel@tonic-gate return;
3627c478bd9Sstevel@tonic-gate
3637c478bd9Sstevel@tonic-gate if (Verbose)
3647c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3657c478bd9Sstevel@tonic-gate gettext("Allocation unit %d modified.\n"),
3667c478bd9Sstevel@tonic-gate clustInfo->clusterNum);
3677c478bd9Sstevel@tonic-gate
368*8509e9caSToomas Soome if (seekCluster(fd, clustInfo->clusterNum) == 0)
3697c478bd9Sstevel@tonic-gate return;
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate if ((bytesWritten = write(fd, clustInfo->clusterData.bytes,
3727c478bd9Sstevel@tonic-gate BytesPerCluster)) != BytesPerCluster) {
3737c478bd9Sstevel@tonic-gate if (bytesWritten < 0) {
3747c478bd9Sstevel@tonic-gate perror(gettext("Failed to write modified "
3757c478bd9Sstevel@tonic-gate "allocation unit"));
3767c478bd9Sstevel@tonic-gate } else {
3777c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3787c478bd9Sstevel@tonic-gate gettext("Short write of allocation unit %d\n"),
3797c478bd9Sstevel@tonic-gate clustInfo->clusterNum);
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate (void) close(fd);
3827c478bd9Sstevel@tonic-gate exit(13);
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate }
3857c478bd9Sstevel@tonic-gate
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate * It's cheaper to allocate a lot at a time; malloc overhead pushes
3887c478bd9Sstevel@tonic-gate * you over the brink much more quickly if you don't.
3897c478bd9Sstevel@tonic-gate * This numbers seems to be a fair trade-off between reduced malloc overhead
3907c478bd9Sstevel@tonic-gate * and additional overhead by over-allocating.
3917c478bd9Sstevel@tonic-gate */
3927c478bd9Sstevel@tonic-gate
3937c478bd9Sstevel@tonic-gate #define CHUNKSIZE 1024
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate static ClusterInfo *pool;
3967c478bd9Sstevel@tonic-gate
3977c478bd9Sstevel@tonic-gate static ClusterInfo *
newClusterInfo(void)3987c478bd9Sstevel@tonic-gate newClusterInfo(void)
3997c478bd9Sstevel@tonic-gate {
4007c478bd9Sstevel@tonic-gate
4017c478bd9Sstevel@tonic-gate ClusterInfo *ret;
4027c478bd9Sstevel@tonic-gate
4037c478bd9Sstevel@tonic-gate if (pool == NULL) {
4047c478bd9Sstevel@tonic-gate int i;
4057c478bd9Sstevel@tonic-gate
4067c478bd9Sstevel@tonic-gate pool = (ClusterInfo *)malloc(sizeof (ClusterInfo) * CHUNKSIZE);
4077c478bd9Sstevel@tonic-gate
4087c478bd9Sstevel@tonic-gate if (pool == NULL) {
4097c478bd9Sstevel@tonic-gate perror(
4107c478bd9Sstevel@tonic-gate gettext("Out of memory for cluster information"));
4117c478bd9Sstevel@tonic-gate exit(9);
4127c478bd9Sstevel@tonic-gate }
4137c478bd9Sstevel@tonic-gate
4147c478bd9Sstevel@tonic-gate for (i = 0; i < CHUNKSIZE - 1; i++)
4157c478bd9Sstevel@tonic-gate pool[i].nextfree = &pool[i+1];
4167c478bd9Sstevel@tonic-gate
4177c478bd9Sstevel@tonic-gate pool[CHUNKSIZE-1].nextfree = NULL;
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate ret = pool;
4207c478bd9Sstevel@tonic-gate pool = pool->nextfree;
4217c478bd9Sstevel@tonic-gate
4227c478bd9Sstevel@tonic-gate memset(ret, 0, sizeof (*ret));
4237c478bd9Sstevel@tonic-gate
4247c478bd9Sstevel@tonic-gate return (ret);
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate
4277c478bd9Sstevel@tonic-gate /* Should be called with verified arguments */
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gate static ClusterInfo *
cloneClusterInfo(int32_t clusterNum)4307c478bd9Sstevel@tonic-gate cloneClusterInfo(int32_t clusterNum)
4317c478bd9Sstevel@tonic-gate {
4327c478bd9Sstevel@tonic-gate ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
4337c478bd9Sstevel@tonic-gate
4347c478bd9Sstevel@tonic-gate if (cl->refcnt > 1) {
4357c478bd9Sstevel@tonic-gate ClusterInfo *newCl = newClusterInfo();
4367c478bd9Sstevel@tonic-gate cl->refcnt--;
4377c478bd9Sstevel@tonic-gate *newCl = *cl;
4387c478bd9Sstevel@tonic-gate newCl->refcnt = 1;
4397c478bd9Sstevel@tonic-gate if (newCl->path)
4407c478bd9Sstevel@tonic-gate newCl->path->references++;
4417c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER] = newCl;
4427c478bd9Sstevel@tonic-gate }
4437c478bd9Sstevel@tonic-gate return (InUse[clusterNum - FIRST_CLUSTER]);
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate
4467c478bd9Sstevel@tonic-gate static void
updateFlags(int32_t clusterNum,int newflags)4477c478bd9Sstevel@tonic-gate updateFlags(int32_t clusterNum, int newflags)
4487c478bd9Sstevel@tonic-gate {
4497c478bd9Sstevel@tonic-gate ClusterInfo *cl = InUse[clusterNum - FIRST_CLUSTER];
4507c478bd9Sstevel@tonic-gate
4517c478bd9Sstevel@tonic-gate if (cl->flags != newflags && cl->refcnt > 1)
4527c478bd9Sstevel@tonic-gate cl = cloneClusterInfo(clusterNum);
4537c478bd9Sstevel@tonic-gate
4547c478bd9Sstevel@tonic-gate cl->flags = newflags;
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate
4577c478bd9Sstevel@tonic-gate static void
freeClusterInfo(ClusterInfo * old)4587c478bd9Sstevel@tonic-gate freeClusterInfo(ClusterInfo *old)
4597c478bd9Sstevel@tonic-gate {
4607c478bd9Sstevel@tonic-gate if (--old->refcnt <= 0) {
4617c478bd9Sstevel@tonic-gate if (old->path && --old->path->references <= 0) {
4627c478bd9Sstevel@tonic-gate free(old->path->fullName);
4637c478bd9Sstevel@tonic-gate free(old->path);
4647c478bd9Sstevel@tonic-gate }
4657c478bd9Sstevel@tonic-gate old->nextfree = pool;
4667c478bd9Sstevel@tonic-gate pool = old;
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate
4707c478bd9Sstevel@tonic-gate /*
4717c478bd9Sstevel@tonic-gate * Allocate entries in our sparse array of cluster information.
4727c478bd9Sstevel@tonic-gate * Returns non-zero if the structure already has been allocated
4737c478bd9Sstevel@tonic-gate * (for those keeping score at home).
4747c478bd9Sstevel@tonic-gate *
4757c478bd9Sstevel@tonic-gate * The template parameter, if non-NULL, is used to facilitate sharing
4767c478bd9Sstevel@tonic-gate * the ClusterInfo nodes for the clusters belonging to the same file.
4777c478bd9Sstevel@tonic-gate * The first call to allocInUse for a new file should have *template
4787c478bd9Sstevel@tonic-gate * set to 0; on return, *template then points to the newly allocated
4797c478bd9Sstevel@tonic-gate * ClusterInfo. Second and further calls keep the same value
4807c478bd9Sstevel@tonic-gate * in *template and that ClusterInfo ndoe is then used for all
4817c478bd9Sstevel@tonic-gate * entries in the file. Code that modifies the ClusterInfo nodes
4827c478bd9Sstevel@tonic-gate * should take care proper sharing semantics are maintained (i.e.,
4837c478bd9Sstevel@tonic-gate * copy-on-write using cloneClusterInfo())
4847c478bd9Sstevel@tonic-gate *
4857c478bd9Sstevel@tonic-gate * The ClusterInfo used in the template is guaranted to be in use in
4867c478bd9Sstevel@tonic-gate * at least one other cluster as we never return a value if we didn't
4877c478bd9Sstevel@tonic-gate * set it first. So we can overwrite it without the possibility of a leak.
4887c478bd9Sstevel@tonic-gate */
4897c478bd9Sstevel@tonic-gate static int
allocInUse(int32_t clusterNum,ClusterInfo ** template)4907c478bd9Sstevel@tonic-gate allocInUse(int32_t clusterNum, ClusterInfo **template)
4917c478bd9Sstevel@tonic-gate {
4927c478bd9Sstevel@tonic-gate ClusterInfo *newCl;
4937c478bd9Sstevel@tonic-gate
4947c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
4957c478bd9Sstevel@tonic-gate return (CLINFO_PREVIOUSLY_ALLOCED);
4967c478bd9Sstevel@tonic-gate
4977c478bd9Sstevel@tonic-gate if (template != NULL && *template != NULL)
4987c478bd9Sstevel@tonic-gate newCl = *template;
4997c478bd9Sstevel@tonic-gate else {
5007c478bd9Sstevel@tonic-gate newCl = newClusterInfo();
5017c478bd9Sstevel@tonic-gate if (template)
5027c478bd9Sstevel@tonic-gate *template = newCl;
5037c478bd9Sstevel@tonic-gate }
5047c478bd9Sstevel@tonic-gate
5057c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER] = newCl;
5067c478bd9Sstevel@tonic-gate newCl->refcnt++;
5077c478bd9Sstevel@tonic-gate
5087c478bd9Sstevel@tonic-gate return (CLINFO_NEWLY_ALLOCED);
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate
5117c478bd9Sstevel@tonic-gate static void
markFree(int32_t clusterNum)5127c478bd9Sstevel@tonic-gate markFree(int32_t clusterNum)
5137c478bd9Sstevel@tonic-gate {
5147c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
5157c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5167c478bd9Sstevel@tonic-gate return;
5177c478bd9Sstevel@tonic-gate
5187c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER]) {
5197c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER]->saved)
5207c478bd9Sstevel@tonic-gate free(InUse[clusterNum - FIRST_CLUSTER]->saved);
5217c478bd9Sstevel@tonic-gate freeClusterInfo(InUse[clusterNum - FIRST_CLUSTER]);
5227c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER] = NULL;
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate }
5257c478bd9Sstevel@tonic-gate
5267c478bd9Sstevel@tonic-gate static void
markOrphan(int fd,int32_t clusterNum,struct pcdir * dp)5277c478bd9Sstevel@tonic-gate markOrphan(int fd, int32_t clusterNum, struct pcdir *dp)
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
5307c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5317c478bd9Sstevel@tonic-gate return;
5327c478bd9Sstevel@tonic-gate
5337c478bd9Sstevel@tonic-gate (void) markInUse(fd, clusterNum, dp, NULL, 0, VISIBLE, NULL);
5347c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER] != NULL)
5357c478bd9Sstevel@tonic-gate updateFlags(clusterNum,
5367c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_ORPHAN);
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate static void
markBad(int32_t clusterNum,uchar_t * recovered,int32_t recoveredLen)5407c478bd9Sstevel@tonic-gate markBad(int32_t clusterNum, uchar_t *recovered, int32_t recoveredLen)
5417c478bd9Sstevel@tonic-gate {
5427c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
5437c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
5447c478bd9Sstevel@tonic-gate return;
5457c478bd9Sstevel@tonic-gate
5467c478bd9Sstevel@tonic-gate (void) allocInUse(clusterNum, NULL);
5477c478bd9Sstevel@tonic-gate
5487c478bd9Sstevel@tonic-gate if (recoveredLen) {
5497c478bd9Sstevel@tonic-gate (void) cloneClusterInfo(clusterNum);
5507c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->saved = recovered;
5517c478bd9Sstevel@tonic-gate }
5527c478bd9Sstevel@tonic-gate updateFlags(clusterNum,
5537c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->flags | CLINFO_BAD);
5547c478bd9Sstevel@tonic-gate
5557c478bd9Sstevel@tonic-gate BadClusterCount++;
5567c478bd9Sstevel@tonic-gate if (Verbose)
5577c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
5587c478bd9Sstevel@tonic-gate gettext("Allocation unit %d marked bad.\n"), clusterNum);
5597c478bd9Sstevel@tonic-gate }
5607c478bd9Sstevel@tonic-gate
5617c478bd9Sstevel@tonic-gate static void
clearOrphan(int32_t c)5627c478bd9Sstevel@tonic-gate clearOrphan(int32_t c)
5637c478bd9Sstevel@tonic-gate {
5647c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
5657c478bd9Sstevel@tonic-gate if (c < FIRST_CLUSTER || c > LastCluster)
5667c478bd9Sstevel@tonic-gate return;
5677c478bd9Sstevel@tonic-gate if (InUse[c - FIRST_CLUSTER] != NULL)
5687c478bd9Sstevel@tonic-gate updateFlags(c,
5697c478bd9Sstevel@tonic-gate InUse[c - FIRST_CLUSTER]->flags & ~CLINFO_ORPHAN);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate
5727c478bd9Sstevel@tonic-gate static void
clearInUse(int32_t c)5737c478bd9Sstevel@tonic-gate clearInUse(int32_t c)
5747c478bd9Sstevel@tonic-gate {
5757c478bd9Sstevel@tonic-gate ClusterInfo **clp;
5767c478bd9Sstevel@tonic-gate
5777c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
5787c478bd9Sstevel@tonic-gate if (c < FIRST_CLUSTER || c > LastCluster)
5797c478bd9Sstevel@tonic-gate return;
5807c478bd9Sstevel@tonic-gate
5817c478bd9Sstevel@tonic-gate clp = &InUse[c - FIRST_CLUSTER];
5827c478bd9Sstevel@tonic-gate if (*clp != NULL) {
5837c478bd9Sstevel@tonic-gate freeClusterInfo(*clp);
5847c478bd9Sstevel@tonic-gate *clp = NULL;
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate }
5877c478bd9Sstevel@tonic-gate
5887c478bd9Sstevel@tonic-gate static void
clearAllClusters_InUse()5897c478bd9Sstevel@tonic-gate clearAllClusters_InUse()
5907c478bd9Sstevel@tonic-gate {
5917c478bd9Sstevel@tonic-gate int32_t cc;
5927c478bd9Sstevel@tonic-gate for (cc = FIRST_CLUSTER; cc < LastCluster; cc++) {
5937c478bd9Sstevel@tonic-gate clearInUse(cc);
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate }
5967c478bd9Sstevel@tonic-gate
5977c478bd9Sstevel@tonic-gate static void
makeUseTable(void)5987c478bd9Sstevel@tonic-gate makeUseTable(void)
5997c478bd9Sstevel@tonic-gate {
6007c478bd9Sstevel@tonic-gate if (InUse != NULL) {
6017c478bd9Sstevel@tonic-gate clearAllClusters_InUse();
6027c478bd9Sstevel@tonic-gate return;
6037c478bd9Sstevel@tonic-gate }
6047c478bd9Sstevel@tonic-gate if ((InUse = (ClusterInfo **)
6057c478bd9Sstevel@tonic-gate calloc(TotalClusters, sizeof (ClusterInfo *))) == NULL) {
6067c478bd9Sstevel@tonic-gate perror(gettext("No memory for internal table"));
6077c478bd9Sstevel@tonic-gate exit(9);
6087c478bd9Sstevel@tonic-gate }
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate static void
countClusters(void)6127c478bd9Sstevel@tonic-gate countClusters(void)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate int32_t c;
6157c478bd9Sstevel@tonic-gate
6167c478bd9Sstevel@tonic-gate BadClusterCount = HiddenClusterCount =
6177c478bd9Sstevel@tonic-gate AllocedClusterCount = FreeClusterCount = 0;
6187c478bd9Sstevel@tonic-gate
6197c478bd9Sstevel@tonic-gate for (c = FIRST_CLUSTER; c < LastCluster; c++) {
6207c478bd9Sstevel@tonic-gate if (badInFAT(c)) {
6217c478bd9Sstevel@tonic-gate BadClusterCount++;
6227c478bd9Sstevel@tonic-gate } else if (isMarkedBad(c)) {
6237c478bd9Sstevel@tonic-gate /*
6247c478bd9Sstevel@tonic-gate * This catches the bad sectors found
6257c478bd9Sstevel@tonic-gate * during thorough verify that have never been
6267c478bd9Sstevel@tonic-gate * allocated to a file. Without this check, we
6277c478bd9Sstevel@tonic-gate * count these guys as free.
6287c478bd9Sstevel@tonic-gate */
6297c478bd9Sstevel@tonic-gate BadClusterCount++;
6307c478bd9Sstevel@tonic-gate markBadInFAT(c);
6317c478bd9Sstevel@tonic-gate } else if (isHidden(c)) {
6327c478bd9Sstevel@tonic-gate HiddenClusterCount++;
6337c478bd9Sstevel@tonic-gate } else if (isInUse(c)) {
6347c478bd9Sstevel@tonic-gate AllocedClusterCount++;
6357c478bd9Sstevel@tonic-gate } else {
6367c478bd9Sstevel@tonic-gate FreeClusterCount++;
6377c478bd9Sstevel@tonic-gate }
6387c478bd9Sstevel@tonic-gate }
6397c478bd9Sstevel@tonic-gate }
6407c478bd9Sstevel@tonic-gate
6417c478bd9Sstevel@tonic-gate /*
6427c478bd9Sstevel@tonic-gate * summarizeFAT
6437c478bd9Sstevel@tonic-gate * Mark orphans without directory entries as allocated.
6447c478bd9Sstevel@tonic-gate * XXX - these chains should be reclaimed!
6457c478bd9Sstevel@tonic-gate * XXX - merge this routine with countClusters (same loop, duh.)
6467c478bd9Sstevel@tonic-gate */
6477c478bd9Sstevel@tonic-gate static void
summarizeFAT(int fd)6487c478bd9Sstevel@tonic-gate summarizeFAT(int fd)
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate int32_t c;
6517c478bd9Sstevel@tonic-gate ClusterInfo *tmpl = NULL;
6527c478bd9Sstevel@tonic-gate
6537c478bd9Sstevel@tonic-gate for (c = FIRST_CLUSTER; c < LastCluster; c++) {
6547c478bd9Sstevel@tonic-gate if (!freeInFAT(c) && !badInFAT(c) && !reservedInFAT(c) &&
6557c478bd9Sstevel@tonic-gate !isInUse(c)) {
6567c478bd9Sstevel@tonic-gate (void) markInUse(fd, c, &BlankPCDIR, NULL, 0, VISIBLE,
6577c478bd9Sstevel@tonic-gate &tmpl);
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate }
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate
6627c478bd9Sstevel@tonic-gate static void
getReadyToSearch(int fd)6637c478bd9Sstevel@tonic-gate getReadyToSearch(int fd)
6647c478bd9Sstevel@tonic-gate {
6657c478bd9Sstevel@tonic-gate getFAT(fd);
6667c478bd9Sstevel@tonic-gate if (!IsFAT32)
6677c478bd9Sstevel@tonic-gate getRootDirectory(fd);
6687c478bd9Sstevel@tonic-gate }
6697c478bd9Sstevel@tonic-gate
6707c478bd9Sstevel@tonic-gate
6717c478bd9Sstevel@tonic-gate static char PathName[MAXPATHLEN];
6727c478bd9Sstevel@tonic-gate
6737c478bd9Sstevel@tonic-gate static void
summarize(int fd,int includeFAT)6747c478bd9Sstevel@tonic-gate summarize(int fd, int includeFAT)
6757c478bd9Sstevel@tonic-gate {
6767c478bd9Sstevel@tonic-gate struct pcdir *ignorep1, *ignorep2 = NULL;
6777c478bd9Sstevel@tonic-gate int32_t ignore32;
6787c478bd9Sstevel@tonic-gate char ignore;
6797c478bd9Sstevel@tonic-gate int pathlen;
6807c478bd9Sstevel@tonic-gate
6817c478bd9Sstevel@tonic-gate ReservedClusterCount = 0;
6827c478bd9Sstevel@tonic-gate AllocedClusterCount = 0;
6837c478bd9Sstevel@tonic-gate HiddenClusterCount = 0;
6847c478bd9Sstevel@tonic-gate FileClusterCount = 0;
6857c478bd9Sstevel@tonic-gate FreeClusterCount = 0;
6867c478bd9Sstevel@tonic-gate DirClusterCount = 0;
6877c478bd9Sstevel@tonic-gate BadClusterCount = 0;
6887c478bd9Sstevel@tonic-gate HiddenFileCount = 0;
6897c478bd9Sstevel@tonic-gate FileCount = 0;
6907c478bd9Sstevel@tonic-gate DirCount = 0;
6917c478bd9Sstevel@tonic-gate ignorep1 = ignorep2 = NULL;
6927c478bd9Sstevel@tonic-gate ignore = '\0';
6937c478bd9Sstevel@tonic-gate
6947c478bd9Sstevel@tonic-gate PathName[0] = '\0';
6957c478bd9Sstevel@tonic-gate pathlen = 0;
6967c478bd9Sstevel@tonic-gate
6977c478bd9Sstevel@tonic-gate getReadyToSearch(fd);
6987c478bd9Sstevel@tonic-gate /*
6997c478bd9Sstevel@tonic-gate * Traverse the full meta-data tree to talley what clusters
7007c478bd9Sstevel@tonic-gate * are in use. The root directory is an area outside of the
7017c478bd9Sstevel@tonic-gate * file space on FAT12 and FAT16 file systems. On FAT32 file
7027c478bd9Sstevel@tonic-gate * systems, the root directory is in a file area cluster just
7037c478bd9Sstevel@tonic-gate * like any other directory.
7047c478bd9Sstevel@tonic-gate */
7057c478bd9Sstevel@tonic-gate if (!IsFAT32) {
7067c478bd9Sstevel@tonic-gate traverseFromRoot(fd, 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL,
7077c478bd9Sstevel@tonic-gate ignore, &ignorep1, &ignore32, &ignorep2, PathName,
7087c478bd9Sstevel@tonic-gate &pathlen);
7097c478bd9Sstevel@tonic-gate } else {
7107c478bd9Sstevel@tonic-gate DirCount++;
7117c478bd9Sstevel@tonic-gate traverseDir(fd, TheBIOSParameterBlock.bpb32.root_dir_clust,
7127c478bd9Sstevel@tonic-gate 0, PCFS_VISIT_SUBDIRS, PCFS_TRAVERSE_ALL, ignore,
7137c478bd9Sstevel@tonic-gate &ignorep1, &ignore32, &ignorep2, PathName, &pathlen);
7147c478bd9Sstevel@tonic-gate }
7157c478bd9Sstevel@tonic-gate
7167c478bd9Sstevel@tonic-gate if (includeFAT)
7177c478bd9Sstevel@tonic-gate summarizeFAT(fd);
7187c478bd9Sstevel@tonic-gate countClusters();
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate
7217c478bd9Sstevel@tonic-gate int
isMarkedBad(int32_t clusterNum)7227c478bd9Sstevel@tonic-gate isMarkedBad(int32_t clusterNum)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
7257c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
7267c478bd9Sstevel@tonic-gate return (0);
7277c478bd9Sstevel@tonic-gate
7287c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
7297c478bd9Sstevel@tonic-gate return (0);
7307c478bd9Sstevel@tonic-gate
7317c478bd9Sstevel@tonic-gate return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_BAD);
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate
7347c478bd9Sstevel@tonic-gate static int
isMarkedOrphan(int32_t clusterNum)7357c478bd9Sstevel@tonic-gate isMarkedOrphan(int32_t clusterNum)
7367c478bd9Sstevel@tonic-gate {
7377c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
7387c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
7397c478bd9Sstevel@tonic-gate return (0);
7407c478bd9Sstevel@tonic-gate
7417c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
7427c478bd9Sstevel@tonic-gate return (0);
7437c478bd9Sstevel@tonic-gate
7447c478bd9Sstevel@tonic-gate return (InUse[clusterNum - FIRST_CLUSTER]->flags & CLINFO_ORPHAN);
7457c478bd9Sstevel@tonic-gate }
7467c478bd9Sstevel@tonic-gate
7477c478bd9Sstevel@tonic-gate static void
orphanChain(int fd,int32_t c,struct pcdir * ndp)7487c478bd9Sstevel@tonic-gate orphanChain(int fd, int32_t c, struct pcdir *ndp)
7497c478bd9Sstevel@tonic-gate {
7507c478bd9Sstevel@tonic-gate ClusterInfo *tmpl = NULL;
7517c478bd9Sstevel@tonic-gate
7527c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
7537c478bd9Sstevel@tonic-gate if (c < FIRST_CLUSTER || c > LastCluster)
7547c478bd9Sstevel@tonic-gate return;
7557c478bd9Sstevel@tonic-gate clearInUse(c);
7567c478bd9Sstevel@tonic-gate markOrphan(fd, c, ndp);
7577c478bd9Sstevel@tonic-gate c = nextInChain(c);
7587c478bd9Sstevel@tonic-gate while (c != 0) {
7597c478bd9Sstevel@tonic-gate clearInUse(c);
7607c478bd9Sstevel@tonic-gate clearOrphan(c);
7617c478bd9Sstevel@tonic-gate (void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, &tmpl);
7627c478bd9Sstevel@tonic-gate c = nextInChain(c);
7637c478bd9Sstevel@tonic-gate }
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate
7667c478bd9Sstevel@tonic-gate static int32_t
findAFreeCluster(int32_t startAt)7677c478bd9Sstevel@tonic-gate findAFreeCluster(int32_t startAt)
7687c478bd9Sstevel@tonic-gate {
7697c478bd9Sstevel@tonic-gate int32_t look = startAt;
7707c478bd9Sstevel@tonic-gate
7717c478bd9Sstevel@tonic-gate for (;;) {
7727c478bd9Sstevel@tonic-gate if (freeInFAT(look)) {
7737c478bd9Sstevel@tonic-gate break;
7747c478bd9Sstevel@tonic-gate }
7757c478bd9Sstevel@tonic-gate if (look == LastCluster)
7767c478bd9Sstevel@tonic-gate look = FIRST_CLUSTER;
7777c478bd9Sstevel@tonic-gate else
7787c478bd9Sstevel@tonic-gate look++;
7797c478bd9Sstevel@tonic-gate if (look == startAt)
7807c478bd9Sstevel@tonic-gate break;
7817c478bd9Sstevel@tonic-gate }
7827c478bd9Sstevel@tonic-gate if (look != startAt)
7837c478bd9Sstevel@tonic-gate return (look);
7847c478bd9Sstevel@tonic-gate else
7857c478bd9Sstevel@tonic-gate return (0);
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate
7887c478bd9Sstevel@tonic-gate static void
setEndOfDirectory(struct pcdir * dp)7897c478bd9Sstevel@tonic-gate setEndOfDirectory(struct pcdir *dp)
7907c478bd9Sstevel@tonic-gate {
7917c478bd9Sstevel@tonic-gate dp->pcd_filename[0] = PCD_UNUSED;
7927c478bd9Sstevel@tonic-gate }
7937c478bd9Sstevel@tonic-gate
7947c478bd9Sstevel@tonic-gate static void
emergencyEndOfDirectory(int fd,int32_t secondToLast)7957c478bd9Sstevel@tonic-gate emergencyEndOfDirectory(int fd, int32_t secondToLast)
7967c478bd9Sstevel@tonic-gate {
7977c478bd9Sstevel@tonic-gate ClusterContents dirdata;
7987c478bd9Sstevel@tonic-gate int32_t dirdatasize = 0;
7997c478bd9Sstevel@tonic-gate
8007c478bd9Sstevel@tonic-gate if (readCluster(fd, secondToLast, &(dirdata.bytes), &dirdatasize,
8017c478bd9Sstevel@tonic-gate RDCLUST_DO_CACHE) != RDCLUST_GOOD) {
8027c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8037c478bd9Sstevel@tonic-gate gettext("Unable to read allocation unit %d.\n"),
8047c478bd9Sstevel@tonic-gate secondToLast);
8057c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8067c478bd9Sstevel@tonic-gate gettext("Cannot allocate a new allocation unit to hold an"
8077c478bd9Sstevel@tonic-gate " end-of-directory marker.\nCannot access allocation unit"
8087c478bd9Sstevel@tonic-gate " to overwrite existing directory entry with\nthe marker."
8097c478bd9Sstevel@tonic-gate " Needed directory truncation has failed. Giving up.\n"));
8107c478bd9Sstevel@tonic-gate (void) close(fd);
8117c478bd9Sstevel@tonic-gate exit(11);
8127c478bd9Sstevel@tonic-gate }
8137c478bd9Sstevel@tonic-gate setEndOfDirectory(dirdata.dirp);
8147c478bd9Sstevel@tonic-gate markClusterModified(secondToLast);
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate
8177c478bd9Sstevel@tonic-gate static void
makeNewEndOfDirectory(struct pcdir * entry,int32_t secondToLast,int32_t newCluster,ClusterContents * newData)8187c478bd9Sstevel@tonic-gate makeNewEndOfDirectory(struct pcdir *entry, int32_t secondToLast,
8197c478bd9Sstevel@tonic-gate int32_t newCluster, ClusterContents *newData)
8207c478bd9Sstevel@tonic-gate {
8217c478bd9Sstevel@tonic-gate setEndOfDirectory(newData->dirp);
8227c478bd9Sstevel@tonic-gate markClusterModified(newCluster);
8237c478bd9Sstevel@tonic-gate /*
8247c478bd9Sstevel@tonic-gate * There are two scenarios. One is that we truncated the
8257c478bd9Sstevel@tonic-gate * directory in the very beginning. The other is that we
8267c478bd9Sstevel@tonic-gate * truncated it in the middle or at the end. In the first
8277c478bd9Sstevel@tonic-gate * scenario, the secondToLast argument is not a valid cluster
8287c478bd9Sstevel@tonic-gate * (it's zero), and so we actually need to change the start
8297c478bd9Sstevel@tonic-gate * cluster for the directory to this new start cluster. In
8307c478bd9Sstevel@tonic-gate * the second scenario, the secondToLast cluster we received
8317c478bd9Sstevel@tonic-gate * as an argument needs to be pointed at the new end of
8327c478bd9Sstevel@tonic-gate * directory.
8337c478bd9Sstevel@tonic-gate */
8347c478bd9Sstevel@tonic-gate if (secondToLast == 0) {
8357c478bd9Sstevel@tonic-gate updateDirEnt_Start(entry, newCluster);
8367c478bd9Sstevel@tonic-gate } else {
8377c478bd9Sstevel@tonic-gate writeFATEntry(secondToLast, newCluster);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate markLastInFAT(newCluster);
8407c478bd9Sstevel@tonic-gate }
8417c478bd9Sstevel@tonic-gate
8427c478bd9Sstevel@tonic-gate static void
createNewEndOfDirectory(int fd,struct pcdir * entry,int32_t secondToLast)8437c478bd9Sstevel@tonic-gate createNewEndOfDirectory(int fd, struct pcdir *entry, int32_t secondToLast)
8447c478bd9Sstevel@tonic-gate {
8457c478bd9Sstevel@tonic-gate ClusterContents dirdata;
8467c478bd9Sstevel@tonic-gate int32_t dirdatasize = 0;
8477c478bd9Sstevel@tonic-gate int32_t freeCluster;
8487c478bd9Sstevel@tonic-gate
8497c478bd9Sstevel@tonic-gate if (((freeCluster = findAFreeCluster(secondToLast)) != 0)) {
8507c478bd9Sstevel@tonic-gate if (readCluster(fd, freeCluster, &(dirdata.bytes),
8517c478bd9Sstevel@tonic-gate &dirdatasize, RDCLUST_DO_CACHE) == RDCLUST_GOOD) {
8527c478bd9Sstevel@tonic-gate if (Verbose) {
8537c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8547c478bd9Sstevel@tonic-gate gettext("Grabbed allocation unit #%d "
8557c478bd9Sstevel@tonic-gate "for truncated\ndirectory's new end "
8567c478bd9Sstevel@tonic-gate "of directory.\n"), freeCluster);
8577c478bd9Sstevel@tonic-gate }
8587c478bd9Sstevel@tonic-gate makeNewEndOfDirectory(entry, secondToLast,
8597c478bd9Sstevel@tonic-gate freeCluster, &dirdata);
8607c478bd9Sstevel@tonic-gate return;
8617c478bd9Sstevel@tonic-gate }
8627c478bd9Sstevel@tonic-gate }
8637c478bd9Sstevel@tonic-gate if (secondToLast == 0) {
8647c478bd9Sstevel@tonic-gate if (freeCluster == 0) {
8657c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("File system full.\n"));
8667c478bd9Sstevel@tonic-gate } else {
8677c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8687c478bd9Sstevel@tonic-gate gettext("Unable to read allocation unit %d.\n"),
8697c478bd9Sstevel@tonic-gate freeCluster);
8707c478bd9Sstevel@tonic-gate }
8717c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8727c478bd9Sstevel@tonic-gate gettext("Cannot allocate a new allocation unit to hold "
8737c478bd9Sstevel@tonic-gate "an end-of-directory marker.\nNo existing directory "
8747c478bd9Sstevel@tonic-gate "entries can be overwritten with the marker,\n"
8757c478bd9Sstevel@tonic-gate "the only unit allocated to the directory is "
8767c478bd9Sstevel@tonic-gate "inaccessible.\nNeeded directory truncation has failed. "
8777c478bd9Sstevel@tonic-gate "Giving up.\n"));
8787c478bd9Sstevel@tonic-gate (void) close(fd);
8797c478bd9Sstevel@tonic-gate exit(11);
8807c478bd9Sstevel@tonic-gate }
8817c478bd9Sstevel@tonic-gate emergencyEndOfDirectory(fd, secondToLast);
8827c478bd9Sstevel@tonic-gate }
8837c478bd9Sstevel@tonic-gate
8847c478bd9Sstevel@tonic-gate /*
8857c478bd9Sstevel@tonic-gate * truncAtCluster
8867c478bd9Sstevel@tonic-gate * Given a directory entry and a cluster number, search through
8877c478bd9Sstevel@tonic-gate * the cluster chain for the entry and make the cluster previous
8887c478bd9Sstevel@tonic-gate * to the given cluster in the chain the last cluster in the file.
8897c478bd9Sstevel@tonic-gate * The number of orphaned bytes is returned. For a chain that's
8907c478bd9Sstevel@tonic-gate * a directory we need to do some special handling, since we'll be
8917c478bd9Sstevel@tonic-gate * getting rid of the end of directory notice by truncating.
8927c478bd9Sstevel@tonic-gate */
8937c478bd9Sstevel@tonic-gate static int64_t
truncAtCluster(int fd,struct pcdir * entry,int32_t cluster)8947c478bd9Sstevel@tonic-gate truncAtCluster(int fd, struct pcdir *entry, int32_t cluster)
8957c478bd9Sstevel@tonic-gate {
8967c478bd9Sstevel@tonic-gate uint32_t oldSize, newSize;
8977c478bd9Sstevel@tonic-gate int32_t prev, count, follow;
8987c478bd9Sstevel@tonic-gate int dir = (entry->pcd_attr & PCA_DIR);
8997c478bd9Sstevel@tonic-gate
9007c478bd9Sstevel@tonic-gate prev = 0; count = 0;
9017c478bd9Sstevel@tonic-gate follow = extractStartCluster(entry);
9027c478bd9Sstevel@tonic-gate while (follow != cluster && follow >= FIRST_CLUSTER &&
9037c478bd9Sstevel@tonic-gate follow <= LastCluster) {
9047c478bd9Sstevel@tonic-gate prev = follow;
9057c478bd9Sstevel@tonic-gate count++;
9067c478bd9Sstevel@tonic-gate follow = nextInChain(follow);
9077c478bd9Sstevel@tonic-gate }
9087c478bd9Sstevel@tonic-gate if (follow != cluster) {
9097c478bd9Sstevel@tonic-gate /*
9107c478bd9Sstevel@tonic-gate * We didn't find the cluster they wanted to trunc at
9117c478bd9Sstevel@tonic-gate * anywhere in the entry's chain. So we'll leave the
9127c478bd9Sstevel@tonic-gate * entry alone, and return a negative value so they
9137c478bd9Sstevel@tonic-gate * can know something is wrong.
9147c478bd9Sstevel@tonic-gate */
9157c478bd9Sstevel@tonic-gate return (-1);
9167c478bd9Sstevel@tonic-gate }
9177c478bd9Sstevel@tonic-gate if (Verbose) {
9187c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
9197c478bd9Sstevel@tonic-gate gettext("Chain truncation at unit #%d\n"), cluster);
9207c478bd9Sstevel@tonic-gate }
9217c478bd9Sstevel@tonic-gate if (!dir) {
9227c478bd9Sstevel@tonic-gate oldSize = extractSize(entry);
9237c478bd9Sstevel@tonic-gate newSize = count *
9247c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster *
9257c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector;
9267c478bd9Sstevel@tonic-gate if (newSize == 0)
9277c478bd9Sstevel@tonic-gate updateDirEnt_Start(entry, 0);
9287c478bd9Sstevel@tonic-gate } else {
9297c478bd9Sstevel@tonic-gate newSize = 0;
9307c478bd9Sstevel@tonic-gate }
9317c478bd9Sstevel@tonic-gate updateDirEnt_Size(entry, newSize);
9327c478bd9Sstevel@tonic-gate if (dir) {
9337c478bd9Sstevel@tonic-gate createNewEndOfDirectory(fd, entry, prev);
9347c478bd9Sstevel@tonic-gate } else if (prev != 0) {
9357c478bd9Sstevel@tonic-gate markLastInFAT(prev);
9367c478bd9Sstevel@tonic-gate }
9377c478bd9Sstevel@tonic-gate if (dir) {
9387c478bd9Sstevel@tonic-gate /*
9397c478bd9Sstevel@tonic-gate * We don't really know what the size of a directory is
9407c478bd9Sstevel@tonic-gate * but it is important for us to know if this truncation
9417c478bd9Sstevel@tonic-gate * results in an orphan with any size. The value we
9427c478bd9Sstevel@tonic-gate * return from this routine for a normal file is the
9437c478bd9Sstevel@tonic-gate * number of bytes left in the chain. For a directory
9447c478bd9Sstevel@tonic-gate * we can't be exact, and the caller doesn't really
9457c478bd9Sstevel@tonic-gate * expect us to be. For a directory the caller only
9467c478bd9Sstevel@tonic-gate * cares if there are zero bytes left or more than
9477c478bd9Sstevel@tonic-gate * zero bytes left. We'll return 1 to indicate
9487c478bd9Sstevel@tonic-gate * more than zero.
9497c478bd9Sstevel@tonic-gate */
9507c478bd9Sstevel@tonic-gate if ((follow = nextInChain(follow)) != 0)
9517c478bd9Sstevel@tonic-gate return (1);
9527c478bd9Sstevel@tonic-gate else
9537c478bd9Sstevel@tonic-gate return (0);
9547c478bd9Sstevel@tonic-gate }
9557c478bd9Sstevel@tonic-gate /*
9567c478bd9Sstevel@tonic-gate * newSize should always be smaller than the old one, since
9577c478bd9Sstevel@tonic-gate * we are decreasing the number of clusters allocated to the file.
9587c478bd9Sstevel@tonic-gate */
9597c478bd9Sstevel@tonic-gate return ((int64_t)oldSize - (int64_t)newSize);
9607c478bd9Sstevel@tonic-gate }
9617c478bd9Sstevel@tonic-gate
9627c478bd9Sstevel@tonic-gate static struct pcdir *
updateOrphanedChainMetadata(int fd,struct pcdir * dp,int32_t endCluster,int isBad)9637c478bd9Sstevel@tonic-gate updateOrphanedChainMetadata(int fd, struct pcdir *dp, int32_t endCluster,
9647c478bd9Sstevel@tonic-gate int isBad)
9657c478bd9Sstevel@tonic-gate {
9667c478bd9Sstevel@tonic-gate struct pcdir *ndp = NULL;
9677c478bd9Sstevel@tonic-gate int64_t remainder;
9687c478bd9Sstevel@tonic-gate char *newName = NULL;
9697c478bd9Sstevel@tonic-gate int chosenName;
9707c478bd9Sstevel@tonic-gate int dir = (dp->pcd_attr & PCA_DIR);
9717c478bd9Sstevel@tonic-gate
9727c478bd9Sstevel@tonic-gate /*
9737c478bd9Sstevel@tonic-gate * If the truncation fails, (which ought not to happen),
9747c478bd9Sstevel@tonic-gate * there's no need to go any further, we just return
9757c478bd9Sstevel@tonic-gate * a null value for the new directory entry pointer.
9767c478bd9Sstevel@tonic-gate */
9777c478bd9Sstevel@tonic-gate remainder = truncAtCluster(fd, dp, endCluster);
9787c478bd9Sstevel@tonic-gate if (remainder < 0)
9797c478bd9Sstevel@tonic-gate return (ndp);
9807c478bd9Sstevel@tonic-gate if (!dir && isBad) {
9817c478bd9Sstevel@tonic-gate /*
9827c478bd9Sstevel@tonic-gate * Subtract out the bad cluster from the remaining size
9837c478bd9Sstevel@tonic-gate * We always assume the cluster being deleted from the
9847c478bd9Sstevel@tonic-gate * file is full size, but that might not be the case
9857c478bd9Sstevel@tonic-gate * for the last cluster of the file, so that is why
9867c478bd9Sstevel@tonic-gate * we check for negative remainder value.
9877c478bd9Sstevel@tonic-gate */
9887c478bd9Sstevel@tonic-gate remainder -= TheBIOSParameterBlock.bpb.sectors_per_cluster *
9897c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector;
9907c478bd9Sstevel@tonic-gate if (remainder < 0)
9917c478bd9Sstevel@tonic-gate remainder = 0;
9927c478bd9Sstevel@tonic-gate }
9937c478bd9Sstevel@tonic-gate /*
9947c478bd9Sstevel@tonic-gate * Build a new directory entry for the rest of the chain.
9957c478bd9Sstevel@tonic-gate * Later, if the user okays it, we'll link this entry into the
9967c478bd9Sstevel@tonic-gate * root directory. The new entry will start out as a
9977c478bd9Sstevel@tonic-gate * copy of the truncated entry.
9987c478bd9Sstevel@tonic-gate */
9997c478bd9Sstevel@tonic-gate if ((remainder != 0) &&
10007c478bd9Sstevel@tonic-gate ((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
10017c478bd9Sstevel@tonic-gate ((ndp = newDirEnt(dp)) != NULL)) {
10027c478bd9Sstevel@tonic-gate if (Verbose) {
10037c478bd9Sstevel@tonic-gate if (dir)
10047c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
10057c478bd9Sstevel@tonic-gate gettext("Orphaned directory chain.\n"));
10067c478bd9Sstevel@tonic-gate else
10077c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
10087c478bd9Sstevel@tonic-gate gettext("Orphaned chain, %u bytes.\n"),
10097c478bd9Sstevel@tonic-gate (uint32_t)remainder);
10107c478bd9Sstevel@tonic-gate }
10117c478bd9Sstevel@tonic-gate if (!dir)
10127c478bd9Sstevel@tonic-gate updateDirEnt_Size(ndp, (uint32_t)remainder);
10137c478bd9Sstevel@tonic-gate if (isBad)
10147c478bd9Sstevel@tonic-gate updateDirEnt_Start(ndp, nextInChain(endCluster));
10157c478bd9Sstevel@tonic-gate else
10167c478bd9Sstevel@tonic-gate updateDirEnt_Start(ndp, endCluster);
10177c478bd9Sstevel@tonic-gate updateDirEnt_Name(ndp, newName);
10187c478bd9Sstevel@tonic-gate addEntryToCHKList(chosenName);
10197c478bd9Sstevel@tonic-gate }
10207c478bd9Sstevel@tonic-gate return (ndp);
10217c478bd9Sstevel@tonic-gate }
10227c478bd9Sstevel@tonic-gate
10237c478bd9Sstevel@tonic-gate /*
10247c478bd9Sstevel@tonic-gate * splitChain()
10257c478bd9Sstevel@tonic-gate *
10267c478bd9Sstevel@tonic-gate * split a cluster allocation chain into two cluster chains
10277c478bd9Sstevel@tonic-gate * around a given cluster (problemCluster). This results in two
10287c478bd9Sstevel@tonic-gate * separate directory entries; the original (dp), and one we hope
10297c478bd9Sstevel@tonic-gate * to create and return a pointer to to the caller (*newdp).
10307c478bd9Sstevel@tonic-gate * This second entry is the orphan chain, and it may end up in
10317c478bd9Sstevel@tonic-gate * the root directory as a FILEnnnn.CHK file. We also return the
10327c478bd9Sstevel@tonic-gate * starting cluster of the orphan chain to the caller (*orphanStart).
10337c478bd9Sstevel@tonic-gate */
10347c478bd9Sstevel@tonic-gate void
splitChain(int fd,struct pcdir * dp,int32_t problemCluster,struct pcdir ** newdp,int32_t * orphanStart)10357c478bd9Sstevel@tonic-gate splitChain(int fd, struct pcdir *dp, int32_t problemCluster,
10367c478bd9Sstevel@tonic-gate struct pcdir **newdp, int32_t *orphanStart)
10377c478bd9Sstevel@tonic-gate {
10387c478bd9Sstevel@tonic-gate struct pcdir *ndp = NULL;
10397c478bd9Sstevel@tonic-gate int isBad = isMarkedBad(problemCluster);
10407c478bd9Sstevel@tonic-gate
10417c478bd9Sstevel@tonic-gate ndp = updateOrphanedChainMetadata(fd, dp, problemCluster, isBad);
10427c478bd9Sstevel@tonic-gate *newdp = ndp;
10437c478bd9Sstevel@tonic-gate clearInUse(problemCluster);
10447c478bd9Sstevel@tonic-gate if (isBad) {
10457c478bd9Sstevel@tonic-gate clearOrphan(problemCluster);
10467c478bd9Sstevel@tonic-gate *orphanStart = nextInChain(problemCluster);
10477c478bd9Sstevel@tonic-gate orphanChain(fd, *orphanStart, ndp);
10487c478bd9Sstevel@tonic-gate markBadInFAT(problemCluster);
10497c478bd9Sstevel@tonic-gate } else {
10507c478bd9Sstevel@tonic-gate *orphanStart = problemCluster;
10517c478bd9Sstevel@tonic-gate orphanChain(fd, problemCluster, ndp);
10527c478bd9Sstevel@tonic-gate }
10537c478bd9Sstevel@tonic-gate }
10547c478bd9Sstevel@tonic-gate
10557c478bd9Sstevel@tonic-gate /*
10567c478bd9Sstevel@tonic-gate * freeOrphan
10577c478bd9Sstevel@tonic-gate *
10587c478bd9Sstevel@tonic-gate * User has requested that an orphaned cluster chain be freed back
10597c478bd9Sstevel@tonic-gate * into the file area.
10607c478bd9Sstevel@tonic-gate */
10617c478bd9Sstevel@tonic-gate static void
freeOrphan(int32_t c)10627c478bd9Sstevel@tonic-gate freeOrphan(int32_t c)
10637c478bd9Sstevel@tonic-gate {
10647c478bd9Sstevel@tonic-gate int32_t n;
10657c478bd9Sstevel@tonic-gate
10667c478bd9Sstevel@tonic-gate /*
10677c478bd9Sstevel@tonic-gate * Free the directory entry we explicitly created for
10687c478bd9Sstevel@tonic-gate * the orphaned clusters.
10697c478bd9Sstevel@tonic-gate */
10707c478bd9Sstevel@tonic-gate if (InUse[c - FIRST_CLUSTER]->dirent != NULL)
10717c478bd9Sstevel@tonic-gate free(InUse[c - FIRST_CLUSTER]->dirent);
10727c478bd9Sstevel@tonic-gate /*
10737c478bd9Sstevel@tonic-gate * Then mark the clusters themselves as available.
10747c478bd9Sstevel@tonic-gate */
10757c478bd9Sstevel@tonic-gate do {
10767c478bd9Sstevel@tonic-gate n = nextInChain(c);
10777c478bd9Sstevel@tonic-gate markFreeInFAT(c);
10787c478bd9Sstevel@tonic-gate markFree(c);
10797c478bd9Sstevel@tonic-gate c = n;
10807c478bd9Sstevel@tonic-gate } while (c != 0);
10817c478bd9Sstevel@tonic-gate }
10827c478bd9Sstevel@tonic-gate
10837c478bd9Sstevel@tonic-gate /*
10847c478bd9Sstevel@tonic-gate * Rewrite the InUse field for a cluster chain. Can be used on a partial
10857c478bd9Sstevel@tonic-gate * chain if provided with a stopAtCluster.
10867c478bd9Sstevel@tonic-gate */
10877c478bd9Sstevel@tonic-gate static void
redoInUse(int fd,int32_t c,struct pcdir * ndp,int32_t stopAtCluster)10887c478bd9Sstevel@tonic-gate redoInUse(int fd, int32_t c, struct pcdir *ndp, int32_t stopAtCluster)
10897c478bd9Sstevel@tonic-gate {
10907c478bd9Sstevel@tonic-gate while (c && c != stopAtCluster) {
10917c478bd9Sstevel@tonic-gate clearInUse(c);
10927c478bd9Sstevel@tonic-gate (void) markInUse(fd, c, ndp, NULL, 0, VISIBLE, NULL);
10937c478bd9Sstevel@tonic-gate c = nextInChain(c);
10947c478bd9Sstevel@tonic-gate }
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate
10977c478bd9Sstevel@tonic-gate static struct pcdir *
orphanDirEntLookup(int32_t clusterNum)10987c478bd9Sstevel@tonic-gate orphanDirEntLookup(int32_t clusterNum)
10997c478bd9Sstevel@tonic-gate {
11007c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
11017c478bd9Sstevel@tonic-gate return (NULL);
11027c478bd9Sstevel@tonic-gate
11037c478bd9Sstevel@tonic-gate if (isInUse(clusterNum)) {
11047c478bd9Sstevel@tonic-gate return (InUse[clusterNum - FIRST_CLUSTER]->dirent);
11057c478bd9Sstevel@tonic-gate } else {
11067c478bd9Sstevel@tonic-gate return (NULL);
11077c478bd9Sstevel@tonic-gate }
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate
11107c478bd9Sstevel@tonic-gate static int32_t
orphanSizeLookup(int32_t clusterNum)11117c478bd9Sstevel@tonic-gate orphanSizeLookup(int32_t clusterNum)
11127c478bd9Sstevel@tonic-gate {
11137c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
11147c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
11157c478bd9Sstevel@tonic-gate return (-1);
11167c478bd9Sstevel@tonic-gate
11177c478bd9Sstevel@tonic-gate if (isInUse(clusterNum)) {
11187c478bd9Sstevel@tonic-gate return (extractSize(InUse[clusterNum - FIRST_CLUSTER]->dirent));
11197c478bd9Sstevel@tonic-gate } else {
11207c478bd9Sstevel@tonic-gate return (-1);
11217c478bd9Sstevel@tonic-gate }
11227c478bd9Sstevel@tonic-gate }
11237c478bd9Sstevel@tonic-gate
11247c478bd9Sstevel@tonic-gate /*
11257c478bd9Sstevel@tonic-gate * linkOrphan
11267c478bd9Sstevel@tonic-gate *
11277c478bd9Sstevel@tonic-gate * User has requested that an orphaned cluster chain be brought back
11287c478bd9Sstevel@tonic-gate * into the file system. So we have to make a new directory entry
11297c478bd9Sstevel@tonic-gate * in the root directory and point it at the cluster chain.
11307c478bd9Sstevel@tonic-gate */
11317c478bd9Sstevel@tonic-gate static void
linkOrphan(int fd,int32_t start)11327c478bd9Sstevel@tonic-gate linkOrphan(int fd, int32_t start)
11337c478bd9Sstevel@tonic-gate {
11347c478bd9Sstevel@tonic-gate struct pcdir *newEnt = NULL;
11357c478bd9Sstevel@tonic-gate struct pcdir *dp;
11367c478bd9Sstevel@tonic-gate
11377c478bd9Sstevel@tonic-gate if ((dp = orphanDirEntLookup(start)) != NULL) {
11387c478bd9Sstevel@tonic-gate newEnt = addRootDirEnt(fd, dp);
11397c478bd9Sstevel@tonic-gate } else {
11407c478bd9Sstevel@tonic-gate (void) printf(gettext("Re-link of orphaned chain failed."
11417c478bd9Sstevel@tonic-gate " Allocation units will remain orphaned.\n"));
11427c478bd9Sstevel@tonic-gate }
11437c478bd9Sstevel@tonic-gate /*
11447c478bd9Sstevel@tonic-gate * A cluster isn't really InUse() unless it is referenced,
11457c478bd9Sstevel@tonic-gate * so if newEnt is NULL here, we are in effect using markInUse()
11467c478bd9Sstevel@tonic-gate * to note that the cluster is NOT in use.
11477c478bd9Sstevel@tonic-gate */
11487c478bd9Sstevel@tonic-gate redoInUse(fd, start, newEnt, 0);
11497c478bd9Sstevel@tonic-gate }
11507c478bd9Sstevel@tonic-gate
11517c478bd9Sstevel@tonic-gate /*
11527c478bd9Sstevel@tonic-gate * relinkCreatedOrphans
11537c478bd9Sstevel@tonic-gate *
11547c478bd9Sstevel@tonic-gate * While marking clusters as bad, we can create orphan cluster
11557c478bd9Sstevel@tonic-gate * chains. Since we were the ones doing the marking, we were able to
11567c478bd9Sstevel@tonic-gate * keep track of the orphans we created. Now we want to go through
11577c478bd9Sstevel@tonic-gate * all those chains and either get them back into the file system or
11587c478bd9Sstevel@tonic-gate * free them depending on the user's input.
11597c478bd9Sstevel@tonic-gate */
11607c478bd9Sstevel@tonic-gate static void
relinkCreatedOrphans(int fd)11617c478bd9Sstevel@tonic-gate relinkCreatedOrphans(int fd)
11627c478bd9Sstevel@tonic-gate {
11637c478bd9Sstevel@tonic-gate int32_t c;
11647c478bd9Sstevel@tonic-gate
11657c478bd9Sstevel@tonic-gate for (c = FIRST_CLUSTER; c < LastCluster; c++) {
11667c478bd9Sstevel@tonic-gate if (isMarkedOrphan(c)) {
11677c478bd9Sstevel@tonic-gate if (OkayToRelink && askAboutRelink(c)) {
11687c478bd9Sstevel@tonic-gate linkOrphan(fd, c);
11697c478bd9Sstevel@tonic-gate } else if (askAboutFreeing(c)) {
11707c478bd9Sstevel@tonic-gate freeOrphan(c);
11717c478bd9Sstevel@tonic-gate }
11727c478bd9Sstevel@tonic-gate clearOrphan(c);
11737c478bd9Sstevel@tonic-gate }
11747c478bd9Sstevel@tonic-gate }
11757c478bd9Sstevel@tonic-gate }
11767c478bd9Sstevel@tonic-gate
11777c478bd9Sstevel@tonic-gate /*
11787c478bd9Sstevel@tonic-gate * relinkFATOrphans
11797c478bd9Sstevel@tonic-gate *
11807c478bd9Sstevel@tonic-gate * We want to find orphans not represented in the meta-data.
11817c478bd9Sstevel@tonic-gate * These are chains marked in the FAT as being in use but
11827c478bd9Sstevel@tonic-gate * not referenced anywhere by any directory entries.
11837c478bd9Sstevel@tonic-gate * We'll go through the whole FAT and mark the first cluster
11847c478bd9Sstevel@tonic-gate * in any such chain as an orphan. Then we can just use
11857c478bd9Sstevel@tonic-gate * the relinkCreatedOrphans routine to get them back into the
11867c478bd9Sstevel@tonic-gate * file system or free'ed depending on the user's input.
11877c478bd9Sstevel@tonic-gate */
11887c478bd9Sstevel@tonic-gate static void
relinkFATOrphans(int fd)11897c478bd9Sstevel@tonic-gate relinkFATOrphans(int fd)
11907c478bd9Sstevel@tonic-gate {
11917c478bd9Sstevel@tonic-gate struct pcdir *ndp = NULL;
11927c478bd9Sstevel@tonic-gate int32_t cc, c, n;
11937c478bd9Sstevel@tonic-gate int32_t bpc, newSize;
11947c478bd9Sstevel@tonic-gate char *newName;
11957c478bd9Sstevel@tonic-gate int chosenName;
11967c478bd9Sstevel@tonic-gate
11977c478bd9Sstevel@tonic-gate for (c = FIRST_CLUSTER; c < LastCluster; c++) {
11987c478bd9Sstevel@tonic-gate if (freeInFAT(c) || badInFAT(c) ||
11997c478bd9Sstevel@tonic-gate reservedInFAT(c) || isInUse(c))
12007c478bd9Sstevel@tonic-gate continue;
12017c478bd9Sstevel@tonic-gate cc = 1;
12027c478bd9Sstevel@tonic-gate n = c;
12037c478bd9Sstevel@tonic-gate while (n = nextInChain(n))
12047c478bd9Sstevel@tonic-gate cc++;
12057c478bd9Sstevel@tonic-gate bpc = TheBIOSParameterBlock.bpb.sectors_per_cluster *
12067c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector;
12077c478bd9Sstevel@tonic-gate newSize = cc * bpc;
12087c478bd9Sstevel@tonic-gate if (((newName = nextAvailableCHKName(&chosenName)) != NULL) &&
12097c478bd9Sstevel@tonic-gate ((ndp = newDirEnt(NULL)) != NULL)) {
12107c478bd9Sstevel@tonic-gate updateDirEnt_Size(ndp, newSize);
12117c478bd9Sstevel@tonic-gate updateDirEnt_Start(ndp, c);
12127c478bd9Sstevel@tonic-gate updateDirEnt_Name(ndp, newName);
12137c478bd9Sstevel@tonic-gate addEntryToCHKList(chosenName);
12147c478bd9Sstevel@tonic-gate }
12157c478bd9Sstevel@tonic-gate orphanChain(fd, c, ndp);
12167c478bd9Sstevel@tonic-gate }
12177c478bd9Sstevel@tonic-gate relinkCreatedOrphans(fd);
12187c478bd9Sstevel@tonic-gate }
12197c478bd9Sstevel@tonic-gate
12207c478bd9Sstevel@tonic-gate static void
relinkOrphans(int fd)12217c478bd9Sstevel@tonic-gate relinkOrphans(int fd)
12227c478bd9Sstevel@tonic-gate {
12237c478bd9Sstevel@tonic-gate relinkCreatedOrphans(fd);
12247c478bd9Sstevel@tonic-gate relinkFATOrphans(fd);
12257c478bd9Sstevel@tonic-gate }
12267c478bd9Sstevel@tonic-gate
12277c478bd9Sstevel@tonic-gate static void
checkForFATLoop(int32_t clusterNum)12287c478bd9Sstevel@tonic-gate checkForFATLoop(int32_t clusterNum)
12297c478bd9Sstevel@tonic-gate {
12307c478bd9Sstevel@tonic-gate int32_t prev = clusterNum;
12317c478bd9Sstevel@tonic-gate int32_t follow;
12327c478bd9Sstevel@tonic-gate
12337c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
12347c478bd9Sstevel@tonic-gate return;
12357c478bd9Sstevel@tonic-gate
12367c478bd9Sstevel@tonic-gate follow = nextInChain(clusterNum);
12377c478bd9Sstevel@tonic-gate while (follow != clusterNum && follow >= FIRST_CLUSTER &&
12387c478bd9Sstevel@tonic-gate follow <= LastCluster) {
12397c478bd9Sstevel@tonic-gate prev = follow;
12407c478bd9Sstevel@tonic-gate follow = nextInChain(follow);
12417c478bd9Sstevel@tonic-gate }
12427c478bd9Sstevel@tonic-gate if (follow == clusterNum) {
12437c478bd9Sstevel@tonic-gate /*
12447c478bd9Sstevel@tonic-gate * We found a loop. Eradicate it by changing
12457c478bd9Sstevel@tonic-gate * the last cluster in the loop to be last
12467c478bd9Sstevel@tonic-gate * in the chain instead instead of pointing
12477c478bd9Sstevel@tonic-gate * back to the first cluster.
12487c478bd9Sstevel@tonic-gate */
12497c478bd9Sstevel@tonic-gate markLastInFAT(prev);
12507c478bd9Sstevel@tonic-gate }
12517c478bd9Sstevel@tonic-gate }
12527c478bd9Sstevel@tonic-gate
12537c478bd9Sstevel@tonic-gate static void
sharedChainError(int fd,int32_t clusterNum,struct pcdir * badEntry)12547c478bd9Sstevel@tonic-gate sharedChainError(int fd, int32_t clusterNum, struct pcdir *badEntry)
12557c478bd9Sstevel@tonic-gate {
12567c478bd9Sstevel@tonic-gate /*
12577c478bd9Sstevel@tonic-gate * If we have shared clusters, it is either because the
12587c478bd9Sstevel@tonic-gate * cluster somehow got assigned to multiple files and/or
12597c478bd9Sstevel@tonic-gate * because of a loop in the cluster chain. In either
12607c478bd9Sstevel@tonic-gate * case we want to truncate the offending file at the
12617c478bd9Sstevel@tonic-gate * cluster of contention. Then, we will want to run
12627c478bd9Sstevel@tonic-gate * through the remainder of the chain. If we find ourselves
12637c478bd9Sstevel@tonic-gate * back at the top, we will know there is a loop in the
12647c478bd9Sstevel@tonic-gate * FAT we need to remove.
12657c478bd9Sstevel@tonic-gate */
12667c478bd9Sstevel@tonic-gate if (Verbose)
12677c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12687c478bd9Sstevel@tonic-gate gettext("Truncating chain due to duplicate allocation of "
12697c478bd9Sstevel@tonic-gate "unit %d.\n"), clusterNum);
12707c478bd9Sstevel@tonic-gate /*
12717c478bd9Sstevel@tonic-gate * Note that we don't orphan anything here, because the duplicate
12727c478bd9Sstevel@tonic-gate * part of the chain may be part of another valid chain.
12737c478bd9Sstevel@tonic-gate */
12747c478bd9Sstevel@tonic-gate (void) truncAtCluster(fd, badEntry, clusterNum);
12757c478bd9Sstevel@tonic-gate checkForFATLoop(clusterNum);
12767c478bd9Sstevel@tonic-gate }
12777c478bd9Sstevel@tonic-gate
12787c478bd9Sstevel@tonic-gate void
truncChainWithBadCluster(int fd,struct pcdir * dp,int32_t startCluster)12797c478bd9Sstevel@tonic-gate truncChainWithBadCluster(int fd, struct pcdir *dp, int32_t startCluster)
12807c478bd9Sstevel@tonic-gate {
12817c478bd9Sstevel@tonic-gate struct pcdir *orphanEntry;
12827c478bd9Sstevel@tonic-gate int32_t orphanStartCluster;
12837c478bd9Sstevel@tonic-gate int32_t c = startCluster;
12847c478bd9Sstevel@tonic-gate
12857c478bd9Sstevel@tonic-gate while (c != 0) {
12867c478bd9Sstevel@tonic-gate if (isMarkedBad(c)) {
12877c478bd9Sstevel@tonic-gate /*
12887c478bd9Sstevel@tonic-gate * splitChain() truncates the current guy and
12897c478bd9Sstevel@tonic-gate * then makes an orphan chain out of the remaining
12907c478bd9Sstevel@tonic-gate * clusters. When we come back from the split
12917c478bd9Sstevel@tonic-gate * we'll want to continue looking for bad clusters
12927c478bd9Sstevel@tonic-gate * in the orphan chain.
12937c478bd9Sstevel@tonic-gate */
12947c478bd9Sstevel@tonic-gate splitChain(fd, dp, c,
12957c478bd9Sstevel@tonic-gate &orphanEntry, &orphanStartCluster);
12967c478bd9Sstevel@tonic-gate /*
12977c478bd9Sstevel@tonic-gate * There is a chance that we weren't able or weren't
12987c478bd9Sstevel@tonic-gate * required to make a directory entry for the
12997c478bd9Sstevel@tonic-gate * remaining clusters. In that case we won't go
13007c478bd9Sstevel@tonic-gate * on, because we couldn't make any more splits
13017c478bd9Sstevel@tonic-gate * anyway.
13027c478bd9Sstevel@tonic-gate */
13037c478bd9Sstevel@tonic-gate if (orphanEntry == NULL)
13047c478bd9Sstevel@tonic-gate break;
13057c478bd9Sstevel@tonic-gate c = orphanStartCluster;
13067c478bd9Sstevel@tonic-gate dp = orphanEntry;
13077c478bd9Sstevel@tonic-gate continue;
13087c478bd9Sstevel@tonic-gate }
13097c478bd9Sstevel@tonic-gate c = nextInChain(c);
13107c478bd9Sstevel@tonic-gate }
13117c478bd9Sstevel@tonic-gate }
13127c478bd9Sstevel@tonic-gate
13137c478bd9Sstevel@tonic-gate int32_t
nextInChain(int32_t currentCluster)13147c478bd9Sstevel@tonic-gate nextInChain(int32_t currentCluster)
13157c478bd9Sstevel@tonic-gate {
13167c478bd9Sstevel@tonic-gate int32_t nextCluster;
13177c478bd9Sstevel@tonic-gate
13187c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
13197c478bd9Sstevel@tonic-gate if (currentCluster < FIRST_CLUSTER || currentCluster > LastCluster)
13207c478bd9Sstevel@tonic-gate return (0);
13217c478bd9Sstevel@tonic-gate
13227c478bd9Sstevel@tonic-gate /*
13237c478bd9Sstevel@tonic-gate * Look up FAT entry of next link in cluster chain,
13247c478bd9Sstevel@tonic-gate * if this one is the last one return 0 as the next link.
13257c478bd9Sstevel@tonic-gate */
13267c478bd9Sstevel@tonic-gate nextCluster = readFATEntry(currentCluster);
13277c478bd9Sstevel@tonic-gate if (nextCluster < FIRST_CLUSTER || nextCluster > LastCluster)
13287c478bd9Sstevel@tonic-gate return (0);
13297c478bd9Sstevel@tonic-gate
13307c478bd9Sstevel@tonic-gate return (nextCluster);
13317c478bd9Sstevel@tonic-gate }
13327c478bd9Sstevel@tonic-gate
13337c478bd9Sstevel@tonic-gate /*
13347c478bd9Sstevel@tonic-gate * findImpactedCluster
13357c478bd9Sstevel@tonic-gate *
13367c478bd9Sstevel@tonic-gate * Called when someone modifies what they believe might be a cached
13377c478bd9Sstevel@tonic-gate * cluster entry, but when they only have a directory entry pointer
13387c478bd9Sstevel@tonic-gate * and not the cluster number. We have to go dig up what cluster
13397c478bd9Sstevel@tonic-gate * they are modifying.
13407c478bd9Sstevel@tonic-gate */
13417c478bd9Sstevel@tonic-gate int32_t
findImpactedCluster(struct pcdir * modified)13427c478bd9Sstevel@tonic-gate findImpactedCluster(struct pcdir *modified)
13437c478bd9Sstevel@tonic-gate {
13447c478bd9Sstevel@tonic-gate CachedCluster *loop;
13457c478bd9Sstevel@tonic-gate /*
13467c478bd9Sstevel@tonic-gate * Check to see if it's in the root directory first
13477c478bd9Sstevel@tonic-gate */
13487c478bd9Sstevel@tonic-gate if (!IsFAT32 && ((uchar_t *)modified >= TheRootDir.bytes) &&
13497c478bd9Sstevel@tonic-gate ((uchar_t *)modified < TheRootDir.bytes + RootDirSize))
13507c478bd9Sstevel@tonic-gate return (FAKE_ROOTDIR_CLUST);
13517c478bd9Sstevel@tonic-gate
13527c478bd9Sstevel@tonic-gate loop = ClusterCache;
13537c478bd9Sstevel@tonic-gate while (loop) {
13547c478bd9Sstevel@tonic-gate if (((uchar_t *)modified >= loop->clusterData.bytes) &&
13557c478bd9Sstevel@tonic-gate ((uchar_t *)modified <
13567c478bd9Sstevel@tonic-gate (loop->clusterData.bytes + BytesPerCluster))) {
13577c478bd9Sstevel@tonic-gate return (loop->clusterNum);
13587c478bd9Sstevel@tonic-gate }
13597c478bd9Sstevel@tonic-gate loop = loop->next;
13607c478bd9Sstevel@tonic-gate }
13617c478bd9Sstevel@tonic-gate /*
13627c478bd9Sstevel@tonic-gate * Guess it wasn't cached after all...
13637c478bd9Sstevel@tonic-gate */
13647c478bd9Sstevel@tonic-gate return (0);
13657c478bd9Sstevel@tonic-gate }
13667c478bd9Sstevel@tonic-gate
13677c478bd9Sstevel@tonic-gate void
writeClusterMods(int fd)13687c478bd9Sstevel@tonic-gate writeClusterMods(int fd)
13697c478bd9Sstevel@tonic-gate {
13707c478bd9Sstevel@tonic-gate CachedCluster *loop = ClusterCache;
13717c478bd9Sstevel@tonic-gate
13727c478bd9Sstevel@tonic-gate while (loop) {
13737c478bd9Sstevel@tonic-gate if (loop->modified)
13747c478bd9Sstevel@tonic-gate writeCachedCluster(fd, loop);
13757c478bd9Sstevel@tonic-gate loop = loop->next;
13767c478bd9Sstevel@tonic-gate }
13777c478bd9Sstevel@tonic-gate }
13787c478bd9Sstevel@tonic-gate
13797c478bd9Sstevel@tonic-gate void
squirrelPath(struct nameinfo * pathInfo,int32_t clusterNum)13807c478bd9Sstevel@tonic-gate squirrelPath(struct nameinfo *pathInfo, int32_t clusterNum)
13817c478bd9Sstevel@tonic-gate {
13827c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
13837c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
13847c478bd9Sstevel@tonic-gate return;
13857c478bd9Sstevel@tonic-gate if (InUse[clusterNum - FIRST_CLUSTER] == NULL)
13867c478bd9Sstevel@tonic-gate return;
13877c478bd9Sstevel@tonic-gate InUse[clusterNum - FIRST_CLUSTER]->path = pathInfo;
13887c478bd9Sstevel@tonic-gate }
13897c478bd9Sstevel@tonic-gate
13907c478bd9Sstevel@tonic-gate int
markInUse(int fd,int32_t clusterNum,struct pcdir * referencer,struct pcdir * longRef,int32_t longStartCluster,int isHiddenFile,ClusterInfo ** template)13917c478bd9Sstevel@tonic-gate markInUse(int fd, int32_t clusterNum, struct pcdir *referencer, struct
13927c478bd9Sstevel@tonic-gate pcdir *longRef, int32_t longStartCluster, int isHiddenFile,
13937c478bd9Sstevel@tonic-gate ClusterInfo **template)
13947c478bd9Sstevel@tonic-gate {
13957c478bd9Sstevel@tonic-gate int alreadyMarked;
13967c478bd9Sstevel@tonic-gate ClusterInfo *cl;
13977c478bd9Sstevel@tonic-gate
13987c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
13997c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
14007c478bd9Sstevel@tonic-gate return (CLINFO_NEWLY_ALLOCED);
14017c478bd9Sstevel@tonic-gate
14027c478bd9Sstevel@tonic-gate alreadyMarked = allocInUse(clusterNum, template);
14037c478bd9Sstevel@tonic-gate if ((alreadyMarked == CLINFO_PREVIOUSLY_ALLOCED) &&
14047c478bd9Sstevel@tonic-gate (isInUse(clusterNum))) {
14057c478bd9Sstevel@tonic-gate sharedChainError(fd, clusterNum, referencer);
14067c478bd9Sstevel@tonic-gate return (CLINFO_PREVIOUSLY_ALLOCED);
14077c478bd9Sstevel@tonic-gate }
14087c478bd9Sstevel@tonic-gate cl = InUse[clusterNum - FIRST_CLUSTER];
14097c478bd9Sstevel@tonic-gate /*
14107c478bd9Sstevel@tonic-gate * If Cl is newly allocated (refcnt <= 1) we must fill in the fields.
14117c478bd9Sstevel@tonic-gate * If Cl has different fields, we must clone it.
14127c478bd9Sstevel@tonic-gate */
14137c478bd9Sstevel@tonic-gate
14147c478bd9Sstevel@tonic-gate if (cl->refcnt <= 1 || cl->dirent != referencer ||
14157c478bd9Sstevel@tonic-gate cl->longent != longRef ||
14167c478bd9Sstevel@tonic-gate cl->longEntStartClust != longStartCluster) {
14177c478bd9Sstevel@tonic-gate if (cl->refcnt > 1)
14187c478bd9Sstevel@tonic-gate cl = cloneClusterInfo(clusterNum);
14197c478bd9Sstevel@tonic-gate cl->dirent = referencer;
14207c478bd9Sstevel@tonic-gate cl->longent = longRef;
14217c478bd9Sstevel@tonic-gate cl->longEntStartClust = longStartCluster;
14227c478bd9Sstevel@tonic-gate if (isHiddenFile)
14237c478bd9Sstevel@tonic-gate cl->flags |= CLINFO_HIDDEN;
14247c478bd9Sstevel@tonic-gate
14257c478bd9Sstevel@tonic-gate /*
14267c478bd9Sstevel@tonic-gate * Return cl as the template to use for other clusters in
14277c478bd9Sstevel@tonic-gate * this file
14287c478bd9Sstevel@tonic-gate */
14297c478bd9Sstevel@tonic-gate if (template)
14307c478bd9Sstevel@tonic-gate *template = cl;
14317c478bd9Sstevel@tonic-gate }
14327c478bd9Sstevel@tonic-gate return (CLINFO_NEWLY_ALLOCED);
14337c478bd9Sstevel@tonic-gate }
14347c478bd9Sstevel@tonic-gate
14357c478bd9Sstevel@tonic-gate void
markClusterModified(int32_t clusterNum)14367c478bd9Sstevel@tonic-gate markClusterModified(int32_t clusterNum)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate CachedCluster *c;
14397c478bd9Sstevel@tonic-gate
14407c478bd9Sstevel@tonic-gate if (clusterNum == FAKE_ROOTDIR_CLUST) {
14417c478bd9Sstevel@tonic-gate RootDirModified = 1;
14427c478bd9Sstevel@tonic-gate return;
14437c478bd9Sstevel@tonic-gate }
14447c478bd9Sstevel@tonic-gate
14457c478bd9Sstevel@tonic-gate /* silent failure for bogus clusters */
14467c478bd9Sstevel@tonic-gate if (clusterNum < FIRST_CLUSTER || clusterNum > LastCluster)
14477c478bd9Sstevel@tonic-gate return;
14487c478bd9Sstevel@tonic-gate
14497c478bd9Sstevel@tonic-gate if (c = findClusterCacheEntry(clusterNum)) {
14507c478bd9Sstevel@tonic-gate c->modified = 1;
14517c478bd9Sstevel@tonic-gate } else {
14527c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
14537c478bd9Sstevel@tonic-gate gettext("Unexpected internal error: "
14547c478bd9Sstevel@tonic-gate "Missing cache entry [%d]\n"), clusterNum);
14557c478bd9Sstevel@tonic-gate exit(10);
14567c478bd9Sstevel@tonic-gate }
14577c478bd9Sstevel@tonic-gate }
14587c478bd9Sstevel@tonic-gate
14597c478bd9Sstevel@tonic-gate /*
14607c478bd9Sstevel@tonic-gate * readCluster
14617c478bd9Sstevel@tonic-gate * caller wants to read cluster clusterNum. We should return
14627c478bd9Sstevel@tonic-gate * a pointer to the read data in "data", and fill in the number
14637c478bd9Sstevel@tonic-gate * of bytes read in "datasize". If shouldCache is non-zero
14647c478bd9Sstevel@tonic-gate * we should allocate cache space to the cluster, otherwise we
14657c478bd9Sstevel@tonic-gate * just return a pointer to a buffer we re-use whenever cacheing
14667c478bd9Sstevel@tonic-gate * is not requested.
14677c478bd9Sstevel@tonic-gate */
14687c478bd9Sstevel@tonic-gate int
readCluster(int fd,int32_t clusterNum,uchar_t ** data,int32_t * datasize,int shouldCache)14697c478bd9Sstevel@tonic-gate readCluster(int fd, int32_t clusterNum, uchar_t **data, int32_t *datasize,
14707c478bd9Sstevel@tonic-gate int shouldCache)
14717c478bd9Sstevel@tonic-gate {
14727c478bd9Sstevel@tonic-gate uchar_t *newBuf;
14737c478bd9Sstevel@tonic-gate int rv;
14747c478bd9Sstevel@tonic-gate
14757c478bd9Sstevel@tonic-gate *data = NULL;
14767c478bd9Sstevel@tonic-gate if ((*data = findClusterDataInTheCache(clusterNum)) != NULL) {
14777c478bd9Sstevel@tonic-gate *datasize = BytesPerCluster;
14787c478bd9Sstevel@tonic-gate return (RDCLUST_GOOD);
14797c478bd9Sstevel@tonic-gate }
14807c478bd9Sstevel@tonic-gate
14817c478bd9Sstevel@tonic-gate rv = getCluster(fd, clusterNum, &newBuf, datasize);
14827c478bd9Sstevel@tonic-gate if (rv != RDCLUST_GOOD)
14837c478bd9Sstevel@tonic-gate return (rv);
14847c478bd9Sstevel@tonic-gate
14857c478bd9Sstevel@tonic-gate /*
14867c478bd9Sstevel@tonic-gate * Caller requested we NOT cache the data from this read.
14877c478bd9Sstevel@tonic-gate * So, we just return a pointer to the common data buffer.
14887c478bd9Sstevel@tonic-gate */
14897c478bd9Sstevel@tonic-gate if (shouldCache == 0) {
14907c478bd9Sstevel@tonic-gate *data = newBuf;
14917c478bd9Sstevel@tonic-gate return (rv);
14927c478bd9Sstevel@tonic-gate }
14937c478bd9Sstevel@tonic-gate
14947c478bd9Sstevel@tonic-gate /*
14957c478bd9Sstevel@tonic-gate * Caller requested we cache the data from this read.
14967c478bd9Sstevel@tonic-gate * So, if we have some data, add it to the cache by
14977c478bd9Sstevel@tonic-gate * copying it out of the common buffer into new storage.
14987c478bd9Sstevel@tonic-gate */
14997c478bd9Sstevel@tonic-gate if (*datasize > 0)
15007c478bd9Sstevel@tonic-gate *data = addToCache(clusterNum, newBuf, datasize);
15017c478bd9Sstevel@tonic-gate return (rv);
15027c478bd9Sstevel@tonic-gate }
15037c478bd9Sstevel@tonic-gate
15047c478bd9Sstevel@tonic-gate void
findBadClusters(int fd)15057c478bd9Sstevel@tonic-gate findBadClusters(int fd)
15067c478bd9Sstevel@tonic-gate {
15077c478bd9Sstevel@tonic-gate int32_t clusterCount;
15087c478bd9Sstevel@tonic-gate int32_t datasize;
15097c478bd9Sstevel@tonic-gate uchar_t *data;
15107c478bd9Sstevel@tonic-gate
15117c478bd9Sstevel@tonic-gate BadClusterCount = 0;
15127c478bd9Sstevel@tonic-gate makeUseTable();
15137c478bd9Sstevel@tonic-gate (void) printf(gettext("** Scanning allocation units\n"));
15147c478bd9Sstevel@tonic-gate for (clusterCount = FIRST_CLUSTER;
15157c478bd9Sstevel@tonic-gate clusterCount < LastCluster; clusterCount++) {
15167c478bd9Sstevel@tonic-gate if (readCluster(fd, clusterCount,
15177c478bd9Sstevel@tonic-gate &data, &datasize, RDCLUST_DONT_CACHE) < 0) {
15187c478bd9Sstevel@tonic-gate if (Verbose)
15197c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
15207c478bd9Sstevel@tonic-gate gettext("\nUnreadable allocation unit %d.\n"),
15217c478bd9Sstevel@tonic-gate clusterCount);
15227c478bd9Sstevel@tonic-gate markBad(clusterCount, data, datasize);
15237c478bd9Sstevel@tonic-gate }
15247c478bd9Sstevel@tonic-gate /*
15257c478bd9Sstevel@tonic-gate * Progress meter, display a '.' for every 1000 clusters
15267c478bd9Sstevel@tonic-gate * processed. We don't want to display this when
15277c478bd9Sstevel@tonic-gate * we are in verbose mode; verbose mode progress is
15287c478bd9Sstevel@tonic-gate * shown by displaying each file name as it is found.
15297c478bd9Sstevel@tonic-gate */
15307c478bd9Sstevel@tonic-gate if (!Verbose && clusterCount % 1000 == 0)
15317c478bd9Sstevel@tonic-gate (void) printf(".");
15327c478bd9Sstevel@tonic-gate }
15337c478bd9Sstevel@tonic-gate (void) printf(gettext("..done\n"));
15347c478bd9Sstevel@tonic-gate }
15357c478bd9Sstevel@tonic-gate
15367c478bd9Sstevel@tonic-gate void
scanAndFixMetadata(int fd)15377c478bd9Sstevel@tonic-gate scanAndFixMetadata(int fd)
15387c478bd9Sstevel@tonic-gate {
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate * First we initialize a few things.
15417c478bd9Sstevel@tonic-gate */
15427c478bd9Sstevel@tonic-gate makeUseTable();
15437c478bd9Sstevel@tonic-gate getReadyToSearch(fd);
15447c478bd9Sstevel@tonic-gate createCHKNameList(fd);
15457c478bd9Sstevel@tonic-gate
15467c478bd9Sstevel@tonic-gate /*
15477c478bd9Sstevel@tonic-gate * Make initial scan, taking into account any effect that
15487c478bd9Sstevel@tonic-gate * the bad clusters we may have already discovered have
15497c478bd9Sstevel@tonic-gate * on meta-data. We may break up some cluster chains
15507c478bd9Sstevel@tonic-gate * during this period. The relinkCreatedOrphans() call
15517c478bd9Sstevel@tonic-gate * will then give the user the chance to recover stuff
15527c478bd9Sstevel@tonic-gate * we've created.
15537c478bd9Sstevel@tonic-gate */
15547c478bd9Sstevel@tonic-gate (void) printf(gettext("** Scanning file system meta-data\n"));
15557c478bd9Sstevel@tonic-gate summarize(fd, NO_FAT_IN_SUMMARY);
15567c478bd9Sstevel@tonic-gate if (Verbose)
15577c478bd9Sstevel@tonic-gate printSummary(stderr);
15587c478bd9Sstevel@tonic-gate (void) printf(gettext("** Correcting any meta-data discrepancies\n"));
15597c478bd9Sstevel@tonic-gate relinkCreatedOrphans(fd);
15607c478bd9Sstevel@tonic-gate
15617c478bd9Sstevel@tonic-gate /*
15627c478bd9Sstevel@tonic-gate * Clear our usage table and go back over everything, this
15637c478bd9Sstevel@tonic-gate * time including looking for clusters floating free in the FAT.
15647c478bd9Sstevel@tonic-gate * This may include clusters the user chose to free during the
15657c478bd9Sstevel@tonic-gate * relink phase.
15667c478bd9Sstevel@tonic-gate */
15677c478bd9Sstevel@tonic-gate makeUseTable();
15687c478bd9Sstevel@tonic-gate summarize(fd, INCLUDE_FAT_IN_SUMMARY);
15697c478bd9Sstevel@tonic-gate relinkOrphans(fd);
15707c478bd9Sstevel@tonic-gate }
15717c478bd9Sstevel@tonic-gate
15727c478bd9Sstevel@tonic-gate void
printSummary(FILE * outDest)15737c478bd9Sstevel@tonic-gate printSummary(FILE *outDest)
15747c478bd9Sstevel@tonic-gate {
15757c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
15767c478bd9Sstevel@tonic-gate gettext("%llu bytes.\n"),
15777c478bd9Sstevel@tonic-gate (uint64_t)
15787c478bd9Sstevel@tonic-gate TotalClusters * TheBIOSParameterBlock.bpb.sectors_per_cluster *
15797c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector);
15807c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
15817c478bd9Sstevel@tonic-gate gettext("%llu bytes in bad sectors.\n"),
15827c478bd9Sstevel@tonic-gate (uint64_t)
15837c478bd9Sstevel@tonic-gate BadClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
15847c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector);
15857c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
15867c478bd9Sstevel@tonic-gate gettext("%llu bytes in %d directories.\n"),
15877c478bd9Sstevel@tonic-gate (uint64_t)
15887c478bd9Sstevel@tonic-gate DirClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
15897c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector, DirCount);
15907c478bd9Sstevel@tonic-gate if (HiddenClusterCount) {
15917c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
15927c478bd9Sstevel@tonic-gate gettext("%llu bytes in %d hidden files.\n"),
15937c478bd9Sstevel@tonic-gate (uint64_t)HiddenClusterCount *
15947c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster *
15957c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector,
15967c478bd9Sstevel@tonic-gate HiddenFileCount);
15977c478bd9Sstevel@tonic-gate }
15987c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
15997c478bd9Sstevel@tonic-gate gettext("%llu bytes in %d files.\n"),
16007c478bd9Sstevel@tonic-gate (uint64_t)
16017c478bd9Sstevel@tonic-gate FileClusterCount * TheBIOSParameterBlock.bpb.sectors_per_cluster *
16027c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector, FileCount);
16037c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
16047c478bd9Sstevel@tonic-gate gettext("%llu bytes free.\n"), (uint64_t)FreeClusterCount *
16057c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster *
16067c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector);
16077c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
16087c478bd9Sstevel@tonic-gate gettext("%d bytes per allocation unit.\n"),
16097c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.sectors_per_cluster *
16107c478bd9Sstevel@tonic-gate TheBIOSParameterBlock.bpb.bytes_per_sector);
16117c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
16127c478bd9Sstevel@tonic-gate gettext("%d total allocation units.\n"), TotalClusters);
16137c478bd9Sstevel@tonic-gate if (ReservedClusterCount)
16147c478bd9Sstevel@tonic-gate (void) fprintf(outDest, gettext("%d reserved allocation units.\n"),
16157c478bd9Sstevel@tonic-gate ReservedClusterCount);
16167c478bd9Sstevel@tonic-gate (void) fprintf(outDest,
16177c478bd9Sstevel@tonic-gate gettext("%d available allocation units.\n"), FreeClusterCount);
16187c478bd9Sstevel@tonic-gate }
1619