Sample C Program to parse out the exFAT Directory Structure


It is going to become very obvious that I am not a good C programmer. But them, I’m not trying to program for optimal performance. This is in the “quick and Dirty” one time shot thing to produce something that I can confirm what I am looking at. What you get is a program that can effective dump the exFAT file system which is handy if you want to look at your DD image with a hex editor (like WinHex) and peek inside. If you can get this program to work for you, what you also get is decimal byte offsets of where the records are, so if you set the hex editor addressing to decimal offsets, you have the exact offset to position to. Then you can go there and “carve” out your pieces.

The open statement is now hard-coded for the path and file name, I know it is simple to make it an argument, for now just call me lazy!

// exFAT.cpp : Defines the entry point for the console application.
//
/*********************************************************************************************************/
/*           This is the Include Section to bring in headers                                                                                                                   */
/*********************************************************************************************************/

#include “stdafx.h”
#include <fcntl.h>
#include <stdio.h>
#include <stddef.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
#include <winnt.h>
#include <math.h>

struct
{
   GUID OemParameterType; //Value is OEM_FLASH_PARAMETER_GUID
   UINT32 EraseBlockSize; //Erase block size in bytes
   UINT32 PageSize;
   UINT32 NumberOfSpareBlocks;
   UINT32 tRandomAccess; //Random Access Time in nanoseconds
   UINT32 tProgram; //Program time in nanoseconds
   UINT32 tReadCycle; // Serial read cycle time in nanoseconds
   UINT32 tWriteCycle; // Write Cycle time in nanoseconds
   UCHAR Reserved[4];
}
FlashParameters;

unsigned char   MainVBR[6144];     /* suck in the whole vbr                                  */
unsigned char   UPCaseTable[6144];    /* suck in the whole Up-Case Table                        */
unsigned char DirEntrySet[608];    /* suck in the whole directory entry set                  */

 /******************************************************************************************************/
 /*      Define DIRTBL                                                                                                                                                                       */
 /*          DIRTBL is a matrix used to save the collected entries defining a directory so that we can                                   */
 /*          List and traverse the subdirectories and determine the paths                                                                                   */
 /******************************************************************************************************/

struct {
   long     DirFirstClusterAddr;
   long     DirParent;
   long     ParentIndex;
   unsigned char   DirEntCode;
   unsigned char   DirEntryName[512];
   unsigned char   ParentDirectory[512];

} DIRTBL[256];

 /******************************************************************************************************/
 /*      Define Global variables used in program                                                                                                                           */
 /******************************************************************************************************/

 int   DIRTBL_Index;                                                /* Keep Track of Directory Table                                                             */
 long  CurrentClus;                                                     /* Cluster Being Processed                                                                        */
 long  NextCluster;                                                     /* for computing the next cluster in a chain                                            */
 long  OffsetInFat;                                                      /* The Location of FAT Chain                                                                   */
 long  NextClusLink;                                                  /* Next Cluster based on FAT Chain                                                        */
 long  HeapOffsetClus;                                               /* Offset of the Heap                                                                                   */
 long  BitMapLoc;                                                       /* Byte Location of the Bit Map                                                                 */
 long  UPCaseTableLoc;                                            /* Byte Location of the UP Case Table                                                      */
 long  ReadCnt;                                                           /* Used for Receiving Byte Count Read from read command               */
 long  BperS;                                                               /* Bytes per Sector                                                                                       */
 long  SperC;                                                               /* Sectors per Cluster                                                                                  */
 long  BperC;                                                               /* Bytes per Cluster                                                                                     */
 long  F6 = 0xFFFFFFF6;                                        /* The largest cluster number allowed                                                      */
 long  F7 = 0xFFFFFFF7;                                         /* This marks a bad cluster                                                                        */
 long  FF = 0xFFFFFFFF;                                       /* End of FAT Chain                                                                                      */
 long  ZE = 0x00000000;                                       /* No FAT CHain Indicator                                                                          */
 unsigned long CRC;

/**********************************************************************************************************/
/* This functions will return a 1 if the cluster is allocated and 0 if the cluster is not allocated                                                     */
/**********************************************************************************************************/

int BitMap(int FD, long cluster)
{
 long    BitMapByteLoc;
 long    BitMapByteOffset;
 long    BitTest;
 long    ClusterBit;
 long    RdCnt;
 long    ClusOffset;
 unsigned char  BitMapByte;

 ClusOffset = cluster – 2;                                                                        /* First Cluster is at index                                                    */
 ClusterBit = ClusOffset % 8;                                                                /* Find Remainder                                                                  */
 BitMapByteOffset = (ClusOffset – ClusterBit) / 8;                           /* Determine which byte in table has ptr                            */
                                                                                                                   /* For Historical Reasons, First Cluster is at Index 2        */
 BitMapByteLoc = BitMapLoc + BitMapByteOffset;                          /* Find Offset to BitMap Byte                                              */
 lseek (FD, BitMapByteLoc, SEEK_SET);
 RdCnt = read(FD, &BitMapByte, 1);
 BitTest = pow(2, ClusterBit);
 return((BitMapByte & BitTest) == BitTest);
}
/**********************************************************************************************************/
/* This functions will Dump the entire allocation map                                                                                                                       */
/**********************************************************************************************************/

void DumpBitMap(int FD, long Blocks)
{
 long    AllocStat;
 long    i;
 long    StartRange;
 long    EndRange;
 int     InAlloc;
 int     InUnAlloc;
 long    CntAlloc;
 long    CntUnAlloc;

 StartRange = -1;
 EndRange = -1;
 InAlloc = -1;
 InUnAlloc = -1;
 CntAlloc = 0;
 CntUnAlloc = 0;

 for (i = 2; i <= Blocks +1; i++)
 {
  AllocStat = BitMap(FD, i);
  if (AllocStat)
  {
   CntAlloc++;                                                                          /* Count Allocated Cluster Units         */

   if (InUnAlloc == 1)                                                              /* we were in an unallocation range      */
   {
    printf(“-%012d unallocated clusters\n”, i-1);                /* Close unallocation range              */
    printf(“%012d”, i);                                                             /* Open new allocation range             */
    InUnAlloc = 0;
    InAlloc = 1;
   }

   if ((InAlloc == -1) | (InUnAlloc == -1))                            /* we are in an allocation range         */
   {
    printf(“%012d”, i);                                                              /* Open new allocation range             */
    InUnAlloc = 0;
    InAlloc = 1;
   }
  }

  if (!AllocStat)
  {
   CntUnAlloc++;                                                                      /* Count Free Blocks                      */

   if (InAlloc == 1)                                                                    /* we were in an allocation range         */
   {
    printf(“-%012d allocated clusters\n”, i-1);                     /* Close allocation range                 */
    printf(“%012d”, i);                                                              /* Open new unallocation range            */
    InUnAlloc = 1;
    InAlloc = 0;
   }
   if ((InAlloc == -1) | (InUnAlloc == -1))                              /* we are in the first pass               */
   {
    printf(“%012d”, i);                                                               /* Open new unallocation range            */
    InUnAlloc = 1;
    InAlloc = 0;
   }
  }
 }
 if (InAlloc == 1)                                                                     /* we were in an unallocation range       */
  {
   printf(“-%012d allocated clusters\n”, i-1);                     /* Close unallocation range               */
  }
 if (InUnAlloc == 1)                                                                /* we were in an unallocation range       */
  {
   printf(“-%012d unallocated clusters\n”, i-1);                 /* Close unallocation range               */
  }
 printf(“\n\nPrinting Simulated chkdsk totals\n”);
 printf(“\n\n%12d bytes in each allocation unit.\n”, BperC);
 printf(“%12d Total allocation units on disk.\n%12d Allocation units available on disk.\n%12d Allocation units in use.\n”, Blocks, CntUnAlloc, CntAlloc);
}
/**********************************************************************************************************/
/* This function will look at all FAT entries and count cell variations                                                                                             */
/**********************************************************************************************************/
void FatWalk(int FD, long Blocks)
{

 long FatCellAddr;                                                                    /* For current Cell Address               */     
 long i;                                                                                        /* index                                  */
 unsigned long FatCellData;                                                    /* To Hold FAT Data                       */
 long CntFF;             /* Count End Of Chains                    */
 long CntZE;             /* Count Zero Records                     */
 long CntF7;             /* Count Bad Blocks                       */
 long NonZero;            /* All Else                               */

 CntFF = 0;
 CntZE = 0;
 CntF7 = 0;
 NonZero = 0;

 for (i = 2; i < Blocks+2; i++)
 {
  FatCellAddr = OffsetInFat + i * 4;                      /* Compute Cell Address                   */
  ReadCnt = lseek(FD, FatCellAddr, SEEK_SET);    /* Point to Cell                          */
  ReadCnt = read(FD, &FatCellData, 4);     /* Read Cell                              */

  switch(FatCellData)

  {

  case (0xFFFFFFFF) : CntFF++;

   break;
  case (0x00000000) : CntZE++;

   break;
  case (0xFFFFFFF7) : CntF7++;

   break;

  default : NonZero++;
   break;
  }
 }
 printf(“FF (End Of Chains): %6d  F7 (Bad Clusters): %6d  Cell Contains Zero: %6d NonZero (Remaining Non-Zero Cells): %6d\n”, CntFF, CntF7, CntZE, NonZero);
}

/**********************************************************************************************************/
/* This function will walk the FAT chain and print the clusters in order                                                                                        */
/**********************************************************************************************************/
long WalkFatChain(int FD, long FirstCluster)
{

 long FatCellAddr;           /* For current Cell Address               */     
 unsigned long FatCellData;         /* To Hold FAT Data                       */
 long ChainSize;            /* To Count number of clusters in chain   */
 FatCellData = 0;           /* Initialize to unused cell flag         */
 ChainSize = 0;            /* Initialize chain cell count            */
 FatCellData = FirstCluster;                                 /* Copy starting Cluster                  */

 /* If there is only one cluster, then the cell contents should be zero, nothing to do                 */
 /* Therefore, the first cell should not be end of chain                                               */

 while(FatCellData != FF)                                    /* stop at end of chain                   */
 {
  FatCellAddr = OffsetInFat + FatCellData * 4;   /* Compute Cell Address                   */
  ReadCnt = lseek(FD, FatCellAddr, SEEK_SET);    /* Point to Cell                          */
  ReadCnt = read(FD, &FatCellData, 4);     /* Read Cell                              */
  if (FatCellData == 0) return(ChainSize);                /* if we hit a zero, nothing to do        */
  printf(”   Address of Cell: %7d Hex Contents of Cell: %08X Dec Contents of Cell: %7d\n”, FatCellAddr, FatCellData, FatCellData);
  ChainSize++;           /* Count The Cell                         */
 }
 return(ChainSize);           /* Tell caller how many clusters in chain */
}
/**********************************************************************************************************/

/**********************************************************************************************************/

UINT16 EntrySetChecksum(const unsigned char octets[], long NumberOfBytes)
{
  UINT16 Checksum = 0;
  long Index;
 
  for (Index = 0; Index < NumberOfBytes; Index++)
 {
  if (Index == 2 || Index == 3)
  {
   continue;
  }
  Checksum = ((Checksum <<15) | (Checksum>> 1)) + (UINT16) octets[Index];
 }
  return Checksum;
}

/**********************************************************************************************************/

/**********************************************************************************************************/

UINT32 UPCaseChecksum(const unsigned char octets[], long NumberOfBytes)
{
  UINT32 Checksum = 0;
  long Index;
  printf(“Enter UP-Case Table CHecksum Calc: Number of Bytes: %d, First Byte %02X\n”, NumberOfBytes, octets[0]);
  for (Index = 0; Index < NumberOfBytes; Index++)
 {
  Checksum = ((Checksum <<31) | (Checksum>> 1)) + (UINT32) octets[Index];
 }
  return Checksum;
}

/**********************************************************************************************************/

/**********************************************************************************************************/
UINT32 VBRChecksum(const unsigned char octets[], long NumberOfBytes)
{
  UINT32 Checksum = 0;
  long Index;
 
  for (Index = 0; Index < NumberOfBytes; Index++)
 {
  if (Index == 106 || Index == 107 || Index == 112)
  {
   continue;
  }
  Checksum = ((Checksum <<31) | (Checksum>> 1)) + (UINT32) octets[Index];
 }
  return Checksum;
}
/**********************************************************************************************************/
/* This routine is called with the location of a Root Directory Record. The values presented are:                                             */
/*      FD – File Descriptor for the DD Image File                                                                                                                                */
/*      DEperC – The number of 32 byte directory enteries within a cluster                                                                                   */
/*      RootLoc – The byte offset from the beginning of the file of the start of cluste record.                                                        */
/*      The RootLoc would be the decimal offset into the file when using WinHex to analyze the file                                           */
/*      By Dumping this value, it can be used to follow a WinHex dump side by side                                                                      */
/**********************************************************************************************************/

void DumpRootDirectory(int FD, long DEperC, long RootLoc, long FirstClus)
{

 /******************************************************************************************************/
 /*      Define the Bit Allocation Map Dirctory Entry                                                                                                                    */
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeX81;                                       /* 000-000 */
    unsigned char BitMapFlags;                                             /* 001-001 */
                                                                                                 /* Bit 0 – 0, which First Allocation Bitmap */
                                                                                                 /* Bit 0 – 1, Which Second Allocation BitMap (When # Fat = 2) */
    unsigned char Reserved[18];                                          /* 002-019 */
    unsigned long FirstCluster;                                              /* 020-023 */
    unsigned long long DataLength;                                      /* 024-031 */

 } AllocationBitMapEntry;

 /******************************************************************************************************/
 /*      Define UP Case Table Directory Entry                                                                                                                               */
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeX82;                                        /* 000-000 */
    unsigned char Reserved1[3];                                            /* 001-003 */
    unsigned long TableChecksum;                                        /* 004-007 */
    unsigned char Reserved2[12];                                          /* 008-019 */
    unsigned long FirstCluster;                                               /* 020-023 */
    unsigned long long DataLength;                                       /* 024-031 */

 } UpCaseTableEntry;

 /******************************************************************************************************/
 /*       Define Volume Label Directory Entry                                                                                                                               */
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeX83;                                        /* 000-000 */
    unsigned char CharacterCount;                                        /* 001-001 */
    unsigned char VolumeLabel[22];                                     /* 002-023 */
    unsigned char Reserved[8];                                              /* 024-031 */

 } VolumeLabelDirectoryEntry;

 /******************************************************************************************************/
 /*       Define a Directory Entry record in the Directory                                                                                                             */
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeX85;                                        /* 000-000 */
    unsigned char SecondaryCount;                                       /* 001-001 */ 
    unsigned char SetChecksum[2];                                       /* 002-003 */
    unsigned char FileAttributes[2];                                      /* 004-005 */
                                                                                                  /* bits:    3210              */
                                                                                                  /* ReadOnly  0  X”01″ */
                                                                                                  /* Hidden  1  X”02″ */
                                                                                                  /* System  2  X”04″ */
                                                                                                  /* Reserved1 3  X”08″ */
                                                                                                  /* bits     7654              */
                                                                                                  /* Directory 4  X”10″ */
                                                                                                  /* Archive  5  X”20″ */
                                                                                                  /* Reserved2 6-15       */
    unsigned char Reserved1[2];                                           /* 006-007 */
    long   CreateTimestamp;                                                   /* 008-011 */
    long   LastModifiedTimestamp;                                        /* 012-015 */
    long   LastAccessedTimestamp;                                       /* 016-019 */
    unsigned char Create10msIncrement;                            /* 020-020 */
    unsigned char LastModified10msIncrement;                 /* 021-021 */
    unsigned char CreateTZ;                                                   /* 022-022 */
    unsigned char LastModifiedTZ;                                        /* 023-023 */
    unsigned char LastAccessedTZ;                                        /* 024-024 */
    unsigned char Reserved2[7];                                             /* 025-031 */

 } DirectoryEntry;

 /******************************************************************************************************/
 /*         Define the Stream Extension Directory Record                                                                                                             */
 /******************************************************************************************************/

  struct {
    unsigned char   EntryTypeXC0;               /* 000-000 */
    unsigned char GeneralSecondaryFlags;  /* 001-001 */
                   /* Bit 0 – Allocation Possible */
                   /* Bit 1 – No FAT Chain        */
    unsigned char Reserved1;     /* 002-002 */
    unsigned char   NameLength;                 /* 003-003 */
    unsigned char   NameHash[2];                /* 004-005 */
    unsigned char Reserved2[2];               /* 006-007 */
    unsigned long long ValidDataLength;  /* 008-015 */
    unsigned long   Reserved3;              /* 016-019 */
    unsigned long FirstCluster;    /* 020-023 */
    unsigned long long DataLength;              /* 024-031 */

 } StreamExtensionDirectoryEntry;

 /******************************************************************************************************/
/*         Define the File Name Directory Entry Record                                                                                                                 */
/*              There are from 1 to 17 of these depening on the filename length, 15 character per ent                                       */
 /******************************************************************************************************/

  struct {
    unsigned char   EntryTypeXC1;               /* 000-000 */
    unsigned char GeneralSecondaryFlags;  /* 001-001 */
                   /* Bit 0 – Allocation Possible */
                   /* Bit 1 – No FAT Chain        */
    unsigned char FileName[30];    /* 002-031 */
    
 } FileNameDirectoryEntry;

 /******************************************************************************************************/
 /*         Define the Volume GUID Directory Entry Record                                                                                                         */                                                                                 
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeXA0;               /* 000-000 */
    unsigned char SecondaryCount;    /* 001-001 */ 
    unsigned char SetChecksum[2];    /* 002-003 */
    unsigned char GeneralPrimaryFlags[2];  /* 004-005 */
                   /* Bit 0 – Allocation Possible */
                   /* Bit 1 – No FAT Chain        */
    unsigned char VolumeGUID[16];    /* 006-021 */
    unsigned char Reserved[10];    /* 022-031 */

 } VolumeGUIDDirectoryEntry;

 /******************************************************************************************************/
/*       This table i not defined in exFAT Version 1.0                                                                                                                   */
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeXA1;               /* 000-000 */
    unsigned char Reserved[31];    /* 001-031 */ 
 } TexFATPaddingEntry;

 /******************************************************************************************************/
 /*       This table i not defined in exFAT Version 1.0                                                                                                                   */
 /******************************************************************************************************/

 struct {
    unsigned char   EntryTypeXA2;               /* 000-000 */
    unsigned char Reserved[31];    /* 001-031 */ 
 } WindowsCEAccessControlTableDirectoryEntry;

 /******************************************************************************************************/
 /*      Define local variables used in program                                                                                                                                */
 /******************************************************************************************************/

   unsigned char RootType;
   int    RootIndex;
   long   TS_Year;
   long   TS_Month;
   long   TS_Day;
   long   TS_Hours;
   long   TS_Minutes;
   long   TS_DoubleSeconds;
   long   Date_Work;
   long   FileLoc;      /* Used to Calculate the byte location of a file          */
   long long       BlkDiff;                        /* slack space in block                                   */
   long long       BlkTemp;                        /* temporary number of cluster bytes holding file         */
   signed int  ReadCnt;      /* used to collect # of bytes read                        */
   signed int  NameOffset;      /* used to track where part of directory name is recorded */
   signed int  DirInProgress;     /* if 0 we are not in a directory, if 1 we are processing */
   signed int  i;
   unsigned int tz;
   unsigned int tzp;
   UINT16   CalcChecksum;                   /* returned checksum                                      */
 RootType = 255;
 RootIndex = 0;
 CurrentClus = FirstClus;
 
 printf(“\nFirst Cluster being processed is cluster #: %5d Located at byte location: %7d Fat Byte Offset is: %5d\n”, FirstClus, RootLoc, OffsetInFat);

 while (RootType != 0){

  RootIndex++;

  if (RootIndex > DEperC) {
   printf(“\nEnd Of Directory Cluster Reached\n”);

   /* We have to look at the FAT Chain to see if there are any more clusters            */

   NextClusLink = OffsetInFat + (CurrentClus * 4);
   printf(“\nNext Cluster Link is located in the FAT table at: %7d\n”,NextClusLink);
   lseek (FD, NextClusLink, SEEK_SET);
   ReadCnt = read(FD, &NextCluster, 4);

   /* we check the chaining of the FAT links, however, we may terminate on a dir entry of zero and not even hit */
   /* the end of chains, but if the last cluster is actually full, we will hit this code                        */

   /* If we are walking the chain, which means that there are at least 2 clusters, then all F’s is end of chain */
   /* We don’t test here, but if we are actually in the first cluster and hit the all F’s, we actually have a   */
   /* Problem because something isn’t correct                                                                   */
   if (NextCluster == FF)
   {
    printf(“End Of Chain Hit\n\n”);
    return;

   }
   /* If we are at the first cluster, and the FAT chain is zero, then there is no chain at all, and the file is */
   /* only one cluster in size. However, if we are walking the chain, and hit zero on anything other than the   */
   /* first cluster, then this isn’t correct either and we have a poblem.                                       */
   if (NextCluster == ZE)

   {
    printf(“No Chain Defined, only One Cluster\n\n”);
    return;

   }

   RootIndex = 1;
   CurrentClus = NextCluster;
   RootLoc = (HeapOffsetClus + CurrentClus -2) * BperC;
   printf(“Next Cluster Address is cluster: %d New Byte Location is: %7d\n”,NextCluster, RootLoc);
   
   if ( BitMap(FD, CurrentClus)) printf(“Cluster %7d is being processed and is Allocated\n”, CurrentClus);
   if (!BitMap(FD, CurrentClus)) printf(“Cluster %7d is being processed and is Not Allocated\n”, CurrentClus);
  }
  printf(“\nSeeking Relative Byte Location: %d, For Directory Index: %d, “,lseek (FD, RootLoc, SEEK_SET), RootIndex);
  printf(“Reading 1 byte, Bytes read: %d”, read(FD, &RootType, 1));
  printf(“\nRoot Entry Type Read is: %01X “, RootType);

  switch (RootType)
  {
   case (131) : printf(“Volume Record\n”);
      ReadCnt = read(FD, &VolumeLabelDirectoryEntry.CharacterCount, 31);
      printf(“Volume Label Size is %d characters, Label:”,VolumeLabelDirectoryEntry.CharacterCount);
      wprintf(L”%s\n”, VolumeLabelDirectoryEntry.VolumeLabel);
      
      RootLoc = RootLoc + 32;

    break;

   case (130) : printf(“Up Case Table Record\n”);
      ReadCnt = read(FD, &UpCaseTableEntry.Reserved1[0], 31);
      printf(“UP Case Table Checksum %04X\n”, UpCaseTableEntry.TableChecksum);
      printf(“Up Case Table First Cluster %d\n”,UpCaseTableEntry.FirstCluster);
      printf(“Up Case Table Data Length   %d\n”,UpCaseTableEntry.DataLength);
      UPCaseTableLoc = (HeapOffsetClus + UpCaseTableEntry.FirstCluster – 2) * BperC;
      printf(“UP Case Table Byte Offset into Image is: %7d\n”, UPCaseTableLoc);
      if ( BitMap(FD, UpCaseTableEntry.FirstCluster)) printf(“Cluster %7d is Allocated\n”, UpCaseTableEntry.FirstCluster);
      if (!BitMap(FD, UpCaseTableEntry.FirstCluster)) printf(“Cluster %7d is Not Allocated\n”, UpCaseTableEntry.FirstCluster);

      lseek(FD, UPCaseTableLoc, SEEK_SET);
      read(FD, &UPCaseTable, 6144);
      printf(“Up-Case Table Checksum Calculation %04X\n”, UPCaseChecksum(&UPCaseTable[0], UpCaseTableEntry.DataLength));

      RootLoc = RootLoc + 32;

    break;

   case (129) : printf(“Bitmap Table Record\n”);
      ReadCnt = read(FD, &AllocationBitMapEntry.BitMapFlags, 31);
      printf(“Bit Map First Cluster %d\n”, AllocationBitMapEntry.FirstCluster);
      printf(“Bit Map Data Length   %d\n”, AllocationBitMapEntry.DataLength);
      printf(“Bit Map Flags         %02X”, AllocationBitMapEntry.BitMapFlags);
      if ((AllocationBitMapEntry.BitMapFlags & 01) == 00) printf(” First Bitmap Allocation\n”);
      if ((AllocationBitMapEntry.BitMapFlags & 01) == 01) printf(” Second Bitmap Allocation\n”);
      BitMapLoc = (HeapOffsetClus + AllocationBitMapEntry.FirstCluster – 2) * BperC;
      printf(“Bit Map Byte Offset into Image is: %7d\n”, BitMapLoc);
      if ( BitMap(FD, AllocationBitMapEntry.FirstCluster)) printf(“Cluster %7d is Allocated\n”, AllocationBitMapEntry.FirstCluster);
      if (!BitMap(FD, AllocationBitMapEntry.FirstCluster)) printf(“Cluster %7d is Not Allocated\n”, AllocationBitMapEntry.FirstCluster);     
      RootLoc = RootLoc + 32;

    break;
   case (005) :
      if (RootType == 005) printf(“Directory Entry Record (Deleted)\n”);  /* X’05’ */
      
   case (133) :
      if (RootType == 133) printf(“Directory Entry Record\n”);     /* X’85’ */

      DirInProgress = 0;       /* Not a Directory vs. a File */
      ReadCnt = read(FD, &DirectoryEntry.SecondaryCount, 31);
      lseek (FD, RootLoc, SEEK_SET);    /* re-seek to start of 85 entry */
      ReadCnt = read(FD, &DirEntrySet, 608);
      printf(“Checksum:                %02X%02X\n”, DirectoryEntry.SetChecksum[1],DirectoryEntry.SetChecksum[0]);
      
      ReadCnt = (DirectoryEntry.SecondaryCount + 1) * 32;
      printf(“Calculated Checksum is:  %04X Size Directory Set (bytes): %4d\n”, CalcChecksum = EntrySetChecksum(&DirEntrySet[0], ReadCnt), ReadCnt);
      printf(“Secondary Count          %03d\n”, DirectoryEntry.SecondaryCount);
      printf(“File Attributes:         %02X%02X”, DirectoryEntry.FileAttributes[1],DirectoryEntry.FileAttributes[0]);

      if (((DirectoryEntry.FileAttributes[0] & 16) == 16) && (RootType == 133))
      {
       printf(” Directory “);
       DirInProgress = 1;       /* Processing a chained dir    */
       DIRTBL_Index++;        /* record a directory entry    */
       NameOffset = 0;        /* indexing for directory name */
       DIRTBL[DIRTBL_Index].DirEntCode = RootType; /* save in diretory table      */
       DIRTBL[DIRTBL_Index].DirParent = FirstClus; /* Save Parent Cluster Value   */
      }

      if ((DirectoryEntry.FileAttributes[0] & 04) == 04) printf(” System “);
      if ((DirectoryEntry.FileAttributes[0] & 02) == 02) printf(” Hidden “);
      if ((DirectoryEntry.FileAttributes[0] & 01) == 01) printf(” Read/Only “);
      if ((DirectoryEntry.FileAttributes[0] & 32) == 32) printf(” Archive “);
      printf(“\n”);

      printf(“Create Timestamp:        %02X “, DirectoryEntry.CreateTimestamp);
        Date_Work = DirectoryEntry.CreateTimestamp;     /* get a timestamp*/
        TS_DoubleSeconds = Date_Work %  32;                         /* Get 5 bits     */
        TS_DoubleSeconds = TS_DoubleSeconds * 2;                    /* Double it      */
        Date_Work = Date_Work / 32;                                 /* Shift 5 bits   */
        TS_Minutes = Date_Work % 64;                                /* Get 6 bits     */
        Date_Work = Date_Work / 64;                                 /* Shift 6 bits   */
        TS_Hours = Date_Work % 32;                                  /* Get 5 bits     */
        Date_Work = Date_Work /32;                                  /* Shift 5 bits   */
        TS_Day = Date_Work % 32;                                    /* get 5 Bits     */
        Date_Work = Date_Work /32;                                  /* Shift 5 Bits   */
        TS_Month = Date_Work % 16;                                  /* Get 4 bits     */
        Date_Work = Date_Work / 16;                                 /* Shift 4 bits   */
        TS_Year = Date_Work + 1980;                                 /* Year is 1980+  */
        printf(” %02d/%02d/%04d %02d:%02d:%02d\n”,TS_Month, TS_Day, TS_Year, TS_Hours, TS_Minutes, TS_DoubleSeconds);

      printf(“Last Modified Timestamp: %02X “, DirectoryEntry.LastModifiedTimestamp);
        Date_Work = DirectoryEntry.LastModifiedTimestamp;   /* get a timestamp*/
        TS_DoubleSeconds = Date_Work %  32;                         /* Get 5 bits     */
        TS_DoubleSeconds = TS_DoubleSeconds * 2;                    /* Double it      */
        Date_Work = Date_Work / 32;                                 /* Shift 5 bits   */
        TS_Minutes = Date_Work % 64;                                /* Get 6 bits     */
        Date_Work = Date_Work / 64;                                 /* Shift 6 bits   */
        TS_Hours = Date_Work % 32;                                  /* Get 5 bits     */
        Date_Work = Date_Work /32;                                  /* Shift 5 bits   */
        TS_Day = Date_Work % 32;                                    /* get 5 Bits     */
        Date_Work = Date_Work /32;                                  /* Shift 5 Bits   */
        TS_Month = Date_Work % 16;                                  /* Get 4 bits     */
        Date_Work = Date_Work / 16;                                 /* Shift 4 bits   */
        TS_Year = Date_Work + 1980;                                 /* Year is 1980+  */
        printf(” %02d/%02d/%04d %02d:%02d:%02d\n”,TS_Month, TS_Day, TS_Year, TS_Hours, TS_Minutes, TS_DoubleSeconds);

      printf(“Last Accessed Timestamp: %02X “, DirectoryEntry.LastAccessedTimestamp);
        Date_Work = DirectoryEntry.LastAccessedTimestamp;   /* get a timestamp*/
        TS_DoubleSeconds = Date_Work %  32;                         /* Get 5 bits     */
        TS_DoubleSeconds = TS_DoubleSeconds * 2;                    /* Double it      */
        Date_Work = Date_Work / 32;                                 /* Shift 5 bits   */
        TS_Minutes = Date_Work % 64;                                /* Get 6 bits     */
        Date_Work = Date_Work / 64;                                 /* Shift 6 bits   */
        TS_Hours = Date_Work % 32;                                  /* Get 5 bits     */
        Date_Work = Date_Work /32;                                  /* Shift 5 bits   */
        TS_Day = Date_Work % 32;                                    /* get 5 Bits     */
        Date_Work = Date_Work /32;                                  /* Shift 5 Bits   */
        TS_Month = Date_Work % 16;                                  /* Get 4 bits     */
        Date_Work = Date_Work / 16;                                 /* Shift 4 bits   */
        TS_Year = Date_Work + 1980;                                 /* Year is 1980+  */
        printf(” %02d/%02d/%04d %02d:%02d:%02d\n”,TS_Month, TS_Day, TS_Year, TS_Hours, TS_Minutes, TS_DoubleSeconds);

        printf(” 10 ms Offset Create           %02X%4d\n”, DirectoryEntry.Create10msIncrement, DirectoryEntry.Create10msIncrement);
        printf(” 10 ms Offset Modified         %02X%4d\n”, DirectoryEntry.LastModified10msIncrement, DirectoryEntry.LastModified10msIncrement);
        printf(” Time Zone Create              %02X%4d”, DirectoryEntry.CreateTZ, DirectoryEntry.CreateTZ);
         tz = DirectoryEntry.CreateTZ, DirectoryEntry.CreateTZ;
         if (tz >= 208){tzp = ((256-tz) % 4) * 15; tz = (256-tz) / 4; printf(” Value of tz is: GMT -%02d:%02d\n”, tz, tzp); }
         else if ((tz >= 128) && (tz <= 192)){tzp = ((tz-128) % 4) * 15; tz = (tz-128) / 4; printf(” Value of tz is: GMT +%02d:%02d\n”, tz, tzp); }
         else printf(” NOT UTC Timestamp\n”);

        printf(” Time Zone Modified            %02X%4d”, DirectoryEntry.LastModifiedTZ, DirectoryEntry.LastModifiedTZ);
         tz = DirectoryEntry.CreateTZ, DirectoryEntry.LastModifiedTZ;
         if (tz >= 208){tzp = ((256-tz) % 4) * 15;  tz = (256-tz) / 4; printf(” Value of tz is: GMT -%02d:%02d\n”, tz, tzp); }
         else if ((tz >= 128) && (tz <= 192)){tzp = ((tz-128) % 4) * 15; tz = (tz-128) / 4; printf(” Value of tz is: GMT +%02d:%02d\n”, tz, tzp); }
         else printf(” NOT UTC Timestamp\n”);

        printf(” Time Zone Last Accessed       %02X%4d”, DirectoryEntry.LastAccessedTZ, DirectoryEntry.LastAccessedTZ);
         tz = DirectoryEntry.CreateTZ, DirectoryEntry.LastAccessedTZ;
         if (tz >= 208){tzp = ((256-tz) % 4) * 15;  tz = (256-tz) / 4; printf(” Value of tz is: GMT -%02d:%02d\n”, tz, tzp); }
         else if ((tz >= 128) && (tz <= 192)){tzp = ((tz-128) % 4) * 15; tz = (tz-128) / 4; printf(” Value of tz is: GMT +%02d:%02d\n”, tz, tzp); }
         else printf(” NOT UTC Timestamp\n”);

        
      
        RootLoc = RootLoc + 32;
    break;

   case (64)  :
      if (RootType == 64)  printf(“Directory Entry Record, Stream Extension (Deleted)\n”);

   case (192) :
      if (RootType == 192) printf(“Directory Entry Record, Stream Extension\n”);

      ReadCnt = read(FD, &StreamExtensionDirectoryEntry.GeneralSecondaryFlags, 31);
      FileLoc = (HeapOffsetClus + StreamExtensionDirectoryEntry.FirstCluster -2) * BperC;
      printf(“Secondary Flags:  %02X\n”, StreamExtensionDirectoryEntry.GeneralSecondaryFlags);
      if ((StreamExtensionDirectoryEntry.GeneralSecondaryFlags & 01) == 01) printf(”     Flag Bit 0: Allocation Possible\n”);
      if ((StreamExtensionDirectoryEntry.GeneralSecondaryFlags & 01) != 01) printf(”     Flag Bit 0: Allocation Not Possible\n”);
      if ((StreamExtensionDirectoryEntry.GeneralSecondaryFlags & 02) != 02) printf(”     Flag Bit 1: FAT Chain Valid\n”);
      if ((StreamExtensionDirectoryEntry.GeneralSecondaryFlags & 02) == 02) printf(”     Flag Bit 1: FAT Chain Invalid\n”);
      printf(“Length of UniCode Filename is: %d\n”, StreamExtensionDirectoryEntry.NameLength);
      printf(“Name Hash Value is:            %02X%02X\n”, StreamExtensionDirectoryEntry.NameHash[0], StreamExtensionDirectoryEntry.NameHash[1]);

      if (StreamExtensionDirectoryEntry.FirstCluster == 0)
       printf(“Stream Extension First Cluster Not Defined\n”);

      if (StreamExtensionDirectoryEntry.FirstCluster != 0)
      {
       printf(“Stream Extension First Cluster %7d Byte Location: %7d\n”, StreamExtensionDirectoryEntry.FirstCluster, FileLoc);
       if ( BitMap(FD, StreamExtensionDirectoryEntry.FirstCluster)) printf(“Cluster %7d is Allocated\n”, StreamExtensionDirectoryEntry.FirstCluster);
       if (!BitMap(FD, StreamExtensionDirectoryEntry.FirstCluster)) printf(“Cluster %7d is Not Allocated\n”, StreamExtensionDirectoryEntry.FirstCluster);     
      }
      
      BlkTemp = StreamExtensionDirectoryEntry.DataLength / BperC;      /* compute number of blocks       */
      BlkDiff = StreamExtensionDirectoryEntry.DataLength – (BlkTemp * BperC);   /* computer slack                 */
      if (BlkDiff > 0) BlkTemp++;              /* computer true number of blocks */
      printf(“Stream Extension Data Length         %7d “, StreamExtensionDirectoryEntry.DataLength);
      printf(“Bytes Slack: %7d “, BlkDiff);
      printf(“Clusters Used: %7d\n”, BlkTemp);

      BlkTemp = StreamExtensionDirectoryEntry.ValidDataLength / BperC;                /* compute number of blocks       */
      BlkDiff = StreamExtensionDirectoryEntry.ValidDataLength – (BlkTemp * BperC);    /* computer slack                 */
      if (BlkDiff > 0) BlkTemp++;              /* computer true number of blocks */
      printf(“Stream Extension Valid Data Length   %7d “, StreamExtensionDirectoryEntry.ValidDataLength);
      printf(“Bytes Slack: %7d “, BlkDiff);
      printf(“Clusters Used: %7d\n”, BlkTemp);
     
      if (DirInProgress)
       DIRTBL[DIRTBL_Index].DirFirstClusterAddr = StreamExtensionDirectoryEntry.FirstCluster;

      if (((StreamExtensionDirectoryEntry.GeneralSecondaryFlags & 02) == 02) & (RootType == 192))
       {
        printf(“\nDumping FAT Chain\n”);          /* Title Line                      */
        ReadCnt = WalkFatChain(FD, StreamExtensionDirectoryEntry.FirstCluster); /* Traverse the Linked List        */
        printf(“Size of Chain is: %5d\n”, ReadCnt);        /* Print Chain Size                */
       }

      RootLoc = RootLoc + 32;

    break;

   case (65) :
      if (RootType == 65) printf(“Directory Entry Record, File Name Extension (Deleted)\n”);

   case (193) :
      if (RootType == 193) printf(“Directory Entry Record, File Name Extension\n”);

      ReadCnt = read(FD, &FileNameDirectoryEntry.GeneralSecondaryFlags, 31);
      printf(“Secondary Flags:  %02X\n”, FileNameDirectoryEntry.GeneralSecondaryFlags);
      if ((FileNameDirectoryEntry.GeneralSecondaryFlags & 01) == 01) printf(”     Flag Bit 0: Allocation Possible\n”);
      if ((FileNameDirectoryEntry.GeneralSecondaryFlags & 01) != 01) printf(”     Flag Bit 0: Allocation Not Possible\n”);
      if ((FileNameDirectoryEntry.GeneralSecondaryFlags & 02) != 02) printf(”     Flag Bit 1: FAT Chain Valid\n”);
      if ((FileNameDirectoryEntry.GeneralSecondaryFlags & 02) == 02) printf(”     Flag Bit 1: FAT Chain Invalid\n”);
      wprintf(L”%s”, FileNameDirectoryEntry.FileName);
      printf(“\n”);
      if (DirInProgress) for (i = 0; i < 30; i++)
      {
       DIRTBL[DIRTBL_Index].DirEntryName[NameOffset] = FileNameDirectoryEntry.FileName[i];
       NameOffset++;
      }

      RootLoc = RootLoc + 32;

    break;

   case (003) : printf(“No Volume Label\n”);
      RootLoc = lseek(FD, 31, SEEK_CUR);
    break;

   case (160) : printf(“Volume GUID Record\n”);
      ReadCnt = read(FD, &VolumeGUIDDirectoryEntry.SecondaryCount, 31);
      printf(“General Primary Flags:  %02X\n”, VolumeGUIDDirectoryEntry.GeneralPrimaryFlags[1]);
      if ((VolumeGUIDDirectoryEntry.GeneralPrimaryFlags[1] & 01) == 01) printf(”     Flag Bit 0: Allocation Possible\n”);
      if ((VolumeGUIDDirectoryEntry.GeneralPrimaryFlags[1] & 01) != 01) printf(”     Flag Bit 0: Allocation Not Possible\n”);
      if ((VolumeGUIDDirectoryEntry.GeneralPrimaryFlags[1] & 02) != 02) printf(”     Flag Bit 1: FAT Chain Valid\n”);
      if ((VolumeGUIDDirectoryEntry.GeneralPrimaryFlags[1] & 02) == 02) printf(”     Flag Bit 1: FAT Chain Invalid\n”);
   
      RootLoc = RootLoc + 32;

    break;

   case (161) : printf(“TexFAT Padding Entry Record\n”);
      ReadCnt = read(FD, &TexFATPaddingEntry.Reserved[0], 31);
    
      RootLoc = RootLoc + 32;

    break;

   case (162) : printf(“Windows CE Access Control Directory Record\n”);
      ReadCnt = read(FD, &WindowsCEAccessControlTableDirectoryEntry.Reserved[0], 31);

      RootLoc = RootLoc + 32;

    break;

   case (002) : printf(“Up Case Table Record (Deleted)\n”);
      RootLoc = lseek(FD, 31, SEEK_CUR);
    break;

   case (001) : printf(“Bitmap Table Record (Deleted)\n”);
      RootLoc = lseek(FD, 31, SEEK_CUR);
    break;

   case (000) : printf(“Unused Entry\n”);
      RootLoc = lseek(FD, 31, SEEK_CUR);
    break;

   default : printf(“Unknown\n”);
      RootLoc = lseek(FD, 31, SEEK_CUR);
    break;

   } /* switch */
  } /* while */
 return;

}
int _tmain(int argc, _TCHAR* argv[])
{

 /******************************************************************************************************/
 /*       This is the layout definition of the Main Boot Sector which is the first sector on disk                                                */
 /******************************************************************************************************/

 /* Main Boot Sector                                                */
 struct {
    char    JumpBoot[3];   /* 000-002 */
    char    FileSystemName[8];  /* 003-010 */
    char    MustBeZero[53];   /* 011-063 */
    long int   PartitionOffset;  /* 064-067 */
    long int   VolumeLength;   /* 067-071 */
    long long int  TotalVolumeSectors;  /* 072-079 */
    long    FatOffset;    /* 080-083 */
    long    FatLength;    /* 084-087 */
    long    ClusterHeapOffset;  /* 088-091 */
    long    ClusterCount;   /* 092-095 */
    long    RootDirFirstClust;  /* 096-099 */
    long    VolumeSerialNumber;  /* 100-103 */
    unsigned char  FileSystemRevision2; /* 104-104 */
    unsigned char  FileSystemRevision1;    /* 105-105 */
    unsigned char  VolumeFlags[2];   /* 106-107 */
                  /* bit 0 ActiveFat       */
                  /* bit 1 VolumeDirty     */
                  /* bit 2 MediaFailure    */
                  /* bit 3 ClearToZero     */
                  /* bit 4-15 Reserved     */
    unsigned char  BytesPerSectorShift; /* 108-108 */
    unsigned char  SectorsPerClusterShift; /* 109-109 */
    unsigned char  NumberOfFats;   /* 110-110 */
    unsigned char  DriveSelect;   /* 111-111 */
    unsigned char  PercentInUse;   /* 112-112 */
    unsigned char  Reserved[7];            /* 113-119 */
    unsigned char  BootCode[390];   /* 120-509 */
    unsigned char  Signature[2];   /* 510-511 */
 } VBR;

 /******************************************************************************************************/
 /*       This is the layout definition of the Main Extended Boot Sector which are the 8 sectors that                                   */
 /*       follow the Master Boot Sector. This sector is all boot code plus a signature                                                                */
 /*       This program “assumes” a 512 byte sector. However, the specification allows for larger or                                    */
 /*       Different size sectors, however it looks like the sector can not be smaller than 512.                                                 */
 /*       This is significant because the Master Boot Sector has the signature at th end of the 512                                       */
 /*       entry regarless of the size of the master boot sector, while the patent says that the                                                */
 /*       signature is the last four bytesof the sector, regardless of sector size. Not consistent.                                              */
 /******************************************************************************************************/

 struct {
    unsigned char  ExtendedBootCode[510]; /* 000-509 */
    unsigned char       Signature[2];   /* 510-511 */

 } MEBS;

 /******************************************************************************************************/
 /*       This is the layout definition of the Main Boot Checksum Sector which is the last sector of                                       */
 /*       the 12 sector Volume Boot Sector Record Set. Each 4 positions contain a 32 bit checksum                                      */
 /******************************************************************************************************/

 struct {
    unsigned long  Checksum[128];   /* 000-511 */

 } MBCS;

 /******************************************************************************************************/
 /*       This is the layout definition of the Main OEM Parameters                                                                                             */
 /*       This will be at sector 10 and is vendor specific. Haven’t seen any values yet in this area                                          */
 /******************************************************************************************************/

 struct {
    unsigned char  ParametersGuid0[16]; /* 000-015 */
    unsigned char  CustomDefined0[32];     /* 016-047 */

    unsigned char  ParametersGuid1[16]; /* 048-063 */
    unsigned char  CustomDefined1[32];     /* 064-095 */

    unsigned char  ParametersGuid2[16]; /* 096-111 */
    unsigned char  CustomDefined2[32];     /* 112-143 */

    unsigned char  ParametersGuid3[16]; /* 144-159 */
    unsigned char  CustomDefined3[32];     /* 160-191 */

    unsigned char  ParametersGuid4[16]; /* 192-207 */
    unsigned char  CustomDefined4[32];     /* 208-239 */

    unsigned char  ParametersGuid5[16]; /* 240-255 */
    unsigned char  CustomDefined5[32];     /* 256-287 */

    unsigned char  ParametersGuid6[16]; /* 288-303 */
    unsigned char  CustomDefined6[32];     /* 304-335 */

    unsigned char  ParametersGuid7[16]; /* 336-351 */
    unsigned char  CustomDefined7[32];     /* 352-383 */

    unsigned char  ParametersGuid8[16]; /* 384-399 */
    unsigned char  CustomDefined8[32];     /* 400-431 */

    unsigned char  ParametersGuid9[16]; /* 432-447 */
    unsigned char  CustomDefined9[32];     /* 448-479 */

    unsigned char  Reserved[32];   /* 480-511 */

 } OEMParms;

 /******************************************************************************************************/
 /*       This is a one 512 byte sector of the FAT. The First 32 bit entry is reserved and is the                                            */
 /*       Media Type, which should be x’FFFFFFF8′ Which i the media ype for Hard Disk                                                    */
 /*       The other Media Types, as used in FAT and FAT32 are Floppy Disk Media Types, but Format does                  */
 /*       Not allow you to format floppy disks, so the media Type should probably always be this value.                            */
 /*       The 2nd 32 bit entry is reserved and should always be x’FFFFFFFF’.                                                                       */
 /*       The 3rd to end of FAT is the FAT Links, and each value should never exceed x’FFFFFFF6′                                 */
 /*       x’FFFFFFF7′ marks bad clusters                                                                                                                                       */
 /*       x’FFFFFFFF’ marks the end of a cluster chain                                                                                                                */
 /*       x’00000000′ marks that there is no chain allocated                                                                                                      */
 /******************************************************************************************************/

 struct {

    unsigned long int LINK [128];  /* this assumed sector size 512 */

 } FAT;

 /******************************************************************************************************/
 /*      Define local variables used in program                                                                                                                               */
 /******************************************************************************************************/

 long   DEperC;
 long   FatLocClus;
 long   FatSizClus;
 long   FatLoc;
 long   RootLoc;
 int    i;
 int    j;
 int    FD;
 signed int  ReadCode;
 long   MBS_Loc;
 long   BMBS_Loc;
 long   MBCS_Loc;
 long   BMBCS_Loc;

 DIRTBL_Index = 0;

 
 /* Print Out Constants used for FAT Link Traversal                          */
 printf(“F6 = %08X, F7 = %08X, FF = %08X, ZE = %08X\n”, F6, F7, FF, ZE);
 /* Although this is variable, and can change, it looks like the following   */
 /* Cluster Ordering might be consistent, and begins in the Cluster Heap     */
 /*     Bit Map                                                              */
 /*     UP Case Table                                                        */
 /*     Root Directory                                                       */

 printf(“Opening File\n”);
 FD = open(“t:/exFAT Extraction/rename short file.dd”,O_RDONLY | O_BINARY);
 printf(“File Openned, FD=%d\n”,FD);
 if (FD < 0)
 {
  printf(“Open File Failed – FD = %d\n”, FD);
  exit(-1);
 }
 ReadCode = read(FD, &VBR, 512);

 BperS = pow(2,VBR.BytesPerSectorShift);
 SperC = pow(2,VBR.SectorsPerClusterShift);
 BperC = BperS * SperC;
 FatLocClus = VBR.FatOffset / SperC;
 FatSizClus = VBR.FatLength / SperC;
 OffsetInFat = VBR.FatOffset * BperS;
 MBS_Loc = 0;
 BMBS_Loc = 12;
 MBCS_Loc = 11 * BperS;
 BMBCS_Loc = 23 * BperS;
 
 printf(“Main Boot Sector located at Sector                  %4d Byte Location %6d\n”, 0, MBS_Loc);
 printf(“Main Boot Checksum Sector located at Sector         %4d Byte Location %6d\n”, 11, MBCS_Loc);
 printf(“Backup Main Boot Sector located at Sector           %4d Byte Location %6d\n”, 12, BMBS_Loc);
 printf(“Backup Main Boot Checksum Sector located at Sector  %4d Byte Location %6d\n”, 23, BMBCS_Loc);

 printf(“\n Dumping the Main Boot Sector – First Sector on Device\n\n”);

 printf(“OEM Label is: %s\n”, VBR.FileSystemName);
 printf(“Partition Offset is: %d\n”, VBR.PartitionOffset);
 printf(“Total Volume sectors is: %d\n”, VBR.TotalVolumeSectors);
 printf(“Fat Location (in sectors) is: %d (in clusters) is: %d Location is: %7d\n”, VBR.FatOffset, FatLocClus, OffsetInFat);
 printf(“Fat Physical Size (in sectors) is: %d (in clusters) is: %d\n”, VBR.FatLength, FatSizClus);
 printf(“Cluster Heap Offset is: %d\n”, VBR.ClusterHeapOffset);
 printf(“Volume Allocation Units (bit count)is: %d\n”, VBR.ClusterCount);
 printf(“Root Directory First Cluster is: %d\n”, VBR.RootDirFirstClust);
 printf(“Volume Serial Number: %08X\n”, VBR.VolumeSerialNumber);
 printf(“File System Revision: %01X.%01X\n”, VBR.FileSystemRevision1, VBR.FileSystemRevision2);

 printf(“Volume Flags: %01X%01X\n”, VBR.VolumeFlags[0], VBR.VolumeFlags[1]);
   if ((VBR.VolumeFlags[1] & 1) == 00) printf(”     First FAT Active\n”);
   if ((VBR.VolumeFlags[1] & 1) == 01) printf(”     Second FAT Active\n”);
   if ((VBR.VolumeFlags[1] & 2) == 00) printf(”     Volume Clean\n”);
   if ((VBR.VolumeFlags[1] & 2) == 01) printf(”     Volume Dirty\n”);
   if ((VBR.VolumeFlags[1] & 4) == 00) printf(”     No Media Failures Reported\n”);
   if ((VBR.VolumeFlags[1] & 4) == 01) printf(”     Media Failures Reported\n”);
   if ((VBR.VolumeFlags[1] & 8) == 00) printf(”     Clear to Zero not set\n”);
   if ((VBR.VolumeFlags[1] & 8) == 01) printf(”     Clear to Zero is set\n”);

 printf(“Bytes Per Sector: 2 to the %02X power is:  %d\n”, VBR.BytesPerSectorShift, BperS);
 printf(“Sectors Per Cluster: 2 to the %02X power is: %d\n”, VBR.SectorsPerClusterShift, SperC);
 printf(“Bytes per Cluster: %d\n”,BperC);
 printf(“Number of FATS on this Volume: %d\n”, VBR.NumberOfFats);
 printf(“Drive Select is: %02X\n”, VBR.DriveSelect);
 printf(“Percent in Use: %d\n”, VBR.PercentInUse);
 printf(“Sector Signature: %02X%02X\n”, VBR.Signature[0], VBR.Signature[1]);

 FatLoc = VBR.FatOffset * BperS;
 printf (“Cursor Set To %d\n”,lseek(FD, FatLoc, SEEK_SET));
 ReadCode = read(FD, &FAT, 512);
 printf (“Media Descriptor Field %04X\n\n”, FAT.LINK[0]);

 /**********************************************************************************************/
 /* After the Main Boot Sector there are 8 more Main Extended Boot Sectors, verify signature                        */
 /* The patent says these signature should be x’50AA’ but I see that these are also x’55AA’                              */
 /**********************************************************************************************/
 for (i = 1; i < 9; i++)
 {
  j = i * BperS;                          /* get sector byte offset                         */
  lseek(FD, j, SEEK_SET);                 /* Position the file pointer                      */
  read(FD, &MEBS, 512);     /* Read the next boot Sector Record               */
  printf(“The signature of Main Extended Boot Sector %d = %02X%02X at Sector Byte Location %7d\n”, i, MEBS.Signature[0], MEBS.Signature[1], j);
 }
 /**********************************************************************************************/
 /* After the Main Boot Sector there are 8 more Main Extended Boot Sectors, verify signature                        */
 /* The patent says these signature should be x’50AA’ but I see that these are also x’55AA’                              */
 /**********************************************************************************************/
 for (i = 1; i < 9; i++)
 {
  j = (i + 12)  * BperS;                  /* get sector byte offset                         */
  lseek(FD, j, SEEK_SET);                 /* Position the file pointer                      */
  read(FD, &MEBS, 512);     /* Read the next boot Sector Record               */
  printf(“The signature of Main Extended Boot Sector %d = %02X%02X at Sector Byte Location %7d\n”, i, MEBS.Signature[0], MEBS.Signature[1], j);
 }

 /**********************************************************************************************/
 /* Process the Checksum record of the Main Boot Sector Volume Record                                                             */
 /**********************************************************************************************/
 lseek(FD, MBCS_Loc, SEEK_SET);              /* Position the file pointer                      */
 read(FD, &MBCS, 512);      /* Read the next boot Sector Record               */
 printf(“Main Boot Checksum First Value is:   %04X\n”,MBCS.Checksum[0]);
 printf(“Main Boot Checksum Last  Value is:   %04X\n”,MBCS.Checksum[127]);

 /**********************************************************************************************/
 /* Process the Checksum record of the Main Boot Sector Volume Record                                                             */
 /**********************************************************************************************/
 lseek(FD, BMBCS_Loc, SEEK_SET);    /* Position the file pointer                      */
 read(FD, &MBCS, 512);      /* Read the next boot Sector Record               */ 
 printf(“Backup Boot Checksum First Value is: %04X\n”,MBCS.Checksum[0]);
 printf(“Backup Boot Checksum Last  Value is: %04X\n”,MBCS.Checksum[127]);

 /**********************************************************************************************/
 /* We need to compute the location of the First Directory Cluster                                                                          */
 /* The ClusterHeapOffset is at Index 2 but is in tracks (as per patent)                                                                 */
 /* So we compoute the value of HeapOffsetClus as ClusterHeapffset / SperC                                                      */
 /* and then we need to multiply the cluster number (after subtracting 2) by Bytes Per Cluster                       */
 /**********************************************************************************************/

 HeapOffsetClus = VBR.ClusterHeapOffset / SperC;
 RootLoc = (HeapOffsetClus + VBR.RootDirFirstClust -2) * BperC;

 printf(“\nThe location of the root directory is: %d\n”, RootLoc);
 printf(“Cursor Set To %d\n”,lseek(FD, RootLoc, SEEK_SET));

 /*Each Cluster can have DEperC Directory Entries, with each entry as 32 bytes long*/
 DEperC = BperC / 32;
 printf(“\nEach Directory Cluster will have %d Enteries\n”, DEperC);

 printf(“\n\n\n>>>>>>>>>>>>>>>>>>>> Traverse the Root Directory <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n\n”);

 DumpRootDirectory(FD, DEperC, RootLoc, VBR.RootDirFirstClust);

 printf(“\n\n\n>>>>>>>>>>>>>>>>>>>> Traverse Sub Directories Directory <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n\n”);

 for (i = 1; i <= DIRTBL_Index; i++)
 {
  RootLoc = ((HeapOffsetClus + DIRTBL[i].DirFirstClusterAddr – 2) * BperC);
  printf(“\n\nIndex: %3d Entry Code: %0X First Cluster Location: %5d “, i, DIRTBL[i].DirEntCode, DIRTBL[i].DirFirstClusterAddr);
  printf(” Byte Offset: %10d “, RootLoc);
  wprintf(L”Directory: %s \n”,DIRTBL[i].DirEntryName);
  DumpRootDirectory(FD, DEperC, RootLoc, DIRTBL[i].DirFirstClusterAddr);
 }

 /* Initialize Parent Index  */
 for (i=1; i <= DIRTBL_Index; i++)
 {
  DIRTBL[i].ParentIndex = 0;
 }

 for (i = 1; i <= DIRTBL_Index; i++)
 {
  for (j = 1; j <= DIRTBL_Index; j++)
  {
   if (DIRTBL[i].DirFirstClusterAddr == DIRTBL[j].DirParent)
    DIRTBL[j].ParentIndex = i;
  }
 }

 printf(“\n\n\n>>>>>>>>>>>>>>>>>>>> Recap Directory Entries <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n\n”);

 for (i = 1; i <= DIRTBL_Index; i++)
 {
  RootLoc = ((HeapOffsetClus + DIRTBL[i].DirFirstClusterAddr – 2) * BperC);
  printf(“\n\nIndex: %3d Entry Code: %0X First Cluster Location: %4d “, i, DIRTBL[i].DirEntCode, DIRTBL[i].DirFirstClusterAddr);
  printf(” Parent Cluster %5d  Index %5d “, DIRTBL[i].DirParent, DIRTBL[i].ParentIndex);
  printf(” Byte Offset: %10d “, RootLoc);
  wprintf(L”Directory: %s”,DIRTBL[i].DirEntryName);

  j = DIRTBL[i].ParentIndex;
   while (j != 0)
   {
    wprintf(L” in %s”, DIRTBL[j].DirEntryName);
    j = DIRTBL[j].ParentIndex;
   }
  if (j == 0) printf(” in Root “);
  printf(“\n”);
 }

 printf(“\n\nDumping Bit Allocation Map\n”);
 DumpBitMap(FD, VBR.ClusterCount);

 printf(“\n\nAnalyzing 1st FAT\n”);
 FatWalk(FD, VBR.ClusterCount);

 lseek(FD, 0, SEEK_SET);
 read(FD, &MainVBR, 6144);
 printf(“VBR Checksum Calculation %04X\n”, VBRChecksum(&MainVBR[0], 5632));

 close (FD);
 return(0);
}

Advertisements

One thought on “Sample C Program to parse out the exFAT Directory Structure

  1. Pingback: exFAT OEM Parms and the GUID | exFAT Extended FAT File System

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s