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