windows-nt/Source/XPSP1/NT/base/ntsetup/opktools/cvtarea/cvtarea.c
2020-09-26 16:20:57 +08:00

2398 lines
58 KiB
C

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
cvtarea.c
Abstract:
Creates file on a volume of specified size (bytes, kb, mb, gb, %free, %dis) at
specified/random location contig/noncontig
Author:
Raja Sivagaminathan [sivaraja] 01/12/2000
Revision History:
--*/
#include "CVTAREA.H"
int _cdecl main(int argc, char *argv[])
{
FILE *file;
// Global initializations
gpHeadNode = NULL;
gpFATNodeCount = 0;
if (!ProcessCommandLine(argc, argv))
{
return 1;
}
//
// check how file name is supplied
//
if (gsFileParam[1] != ':')
{
gcDrive = GetCurrentDrive();
if (!gcDrive)
{
Mes("Unable to get current drive\n");
return 1;
}
if (gsFileParam[0] != '\\')
{
strcpy(gsFileName, "\\");
if (!GetCurrentDirectory(gcDrive, gsCurrentDir))
{
Mes("Unable to get current directory.\n");
return 1;
}
if (gsCurrentDir[0] != 0) // may be root directory
{
strcat(gsFileName, gsCurrentDir);
if (gsCurrentDir[strlen(gsCurrentDir) - 1] != '\\')
{
strcat(gsFileName, "\\");
}
}
}
else
{
// so that the next strcat works fine
gsFileName[0] = 0;
}
strcat(gsFileName, gsFileParam);
}
else
{
gcDrive = gsFileParam[0];
gcDrive = (BYTE) toupper((int) gcDrive);
if (gcDrive < 'A' || gcDrive > 'Z')
{
Mes("Invalid drive name.\n");
return 1;
}
if (gsFileParam[2] == '\\')
{
strcpy(gsFileName, gsFileParam+2);
}
else
{
strcpy(gsFileName, "\\");
if (!GetCurrentDirectory(gcDrive, gsCurrentDir))
{
Mes("Unable to get current directory.\n");
return 1;
}
if (gsCurrentDir[0] != 0) // may be root directory
{
strcat(gsFileName, gsCurrentDir);
if (gsCurrentDir[strlen(gsCurrentDir) - 1] != '\\')
{
strcat(gsFileName, "\\");
}
}
strcat(gsFileName, gsFileParam+2);
}
}
if (gnDumpMode)
{
if (!LockVolume(gcDrive, READONLYLOCK))
{
Mes("Unable to lock volume\n");
return 1;
}
if (!BuildDriveInfo(gcDrive, &gsDrvInfo))
{
return 1;
}
GetAllInfoOfFile(&gsDrvInfo, gsFileName, &gsFileLoc, &gsFileInfo);
printf("Cluster allocation for %s\n\n", gsFileName);
printf("Starting at:\tNumber of Clusters:\n\n");
gnClusterFrom = gsFileLoc.StartCluster;
gnClusterProgress = gnClusterFrom;
gnClustersCounted = 0;
while(1)
{
gnClusterProgressPrev = gnClusterProgress;
gnClusterProgress = FindNextCluster(&gsDrvInfo, gnClusterProgress);
gnClustersCounted++;
//
// contigous ?
//
if (gnClusterProgress != gnClusterProgressPrev+1)
{
printf("%lu\t\t%lu\n", gnClusterFrom, gnClustersCounted);
gnClustersCounted = 0;
gnClusterFrom = gnClusterProgress;
}
if (gnClusterProgress >= GetFATEOF(&gsDrvInfo) - 7)
{
printf("EndOfFile\n");
break;
}
if (gnClusterProgress > gsDrvInfo.TotalClusters || gnClusterProgress < 2)
{
Mes("FATAL ERROR : FAT is corrupt.\n");
break;
}
}
//
// All done, unlock volume
//
if (!UnlockVolume(gcDrive))
{
Mes("WARNING : Unable to unlock volume\n");
//return 1;
}
}
else
{
if (gnFreeSpaceDumpMode)
{
if (!LockVolume(gcDrive, READONLYLOCK))
{
Mes("Unable to lock volume\n");
return 1;
}
if (!BuildDriveInfo(gcDrive, &gsDrvInfo))
{
return 1;
}
printf("Free Clusters on Drive %c:\n\n", gcDrive);
gnClustersCounted = 0;
//
// locate first free cluster
//
gnClusterProgress = FindFreeCluster(&gsDrvInfo);
if (!gnClusterProgress)
{
Mes("No free clusters found.\n");
return 0;
}
printf("Starting at:\tNumber of Clusters:\n\n");
for (;gnClusterProgress <= gsDrvInfo.TotalClusters; gnClusterProgress++)
{
if (FindNextCluster(&gsDrvInfo, gnClusterProgress) == 0)
{
// if this is 0 then gnClusterFrom needs to be updated
if (gnClustersCounted == 0)
{
gnClusterFrom = gnClusterProgress;
}
gnClustersCounted++;
}
else
{
if (gnClustersCounted)
{
printf("%lu\t\t%lu\n", gnClusterFrom, gnClustersCounted);
gnClustersCounted = 0;
}
}
}
//
// do we still have something to print?
//
if (gnClustersCounted)
{
printf("%lu\t\t%lu\n", gnClusterFrom, gnClustersCounted);
}
//
// All done, unlock volume
//
if (!UnlockVolume(gcDrive))
{
Mes("WARNING : Unable to unlock volume\n");
//return 1;
}
}
else
{
//
// let DOS create a 0 length file
//
file = fopen(gsFileParam, "w+");
if (!file)
{
Mes("Error creating file\n");
return 1;
}
else
{
fclose(file);
}
if (!LockVolume(gcDrive, READWRITELOCK))
{
Mes("Unable to lock volume\n");
return 1;
}
if (!BuildDriveInfo(gcDrive, &gsDrvInfo))
{
return 1;
}
GetAllInfoOfFile(&gsDrvInfo, gsFileName, &gsFileLoc, &gsFileInfo);
if (gsFileLoc.Found != 1)
{
Mes("FATAL ERROR : Unable to locate created file\n");
return 1;
}
//
// Do all the operations requested in command line parameters
//
if (gbValidateFirstClusterParam)
{
gnFirstCluster = ConvertClusterUnit(&gsDrvInfo);
if (!gnFirstCluster)
{
DisplayUsage();
return 0;
}
}
Mes("Computing cluster requirement...\n");
gnClustersRequired = GetClustersRequired(&gsDrvInfo);
printf("Clusters Required : %lu\n", gnClustersRequired);
//
// Check if the file size will exceed DWORD (4GB)
//
if ((FOURGB / (gsDrvInfo.BytesPerSector * gsDrvInfo.SectorsPerCluster)) < gnClustersRequired)
{
Mes("*** WARNING: Clusters required exceed FAT32 file system limit. ***\n");
gnClustersRequired = FOURGB / (gsDrvInfo.BytesPerSector * gsDrvInfo.SectorsPerCluster);
printf("%lu clusters (= 4GB) will be allocated.\n", gnClustersRequired);
}
//
// if Contigous clusters required make sure they are available
//
if (gnContig)
{
Mes("Finding contigous clusters...\n");
gnClusterStart = GetContigousStart(&gsDrvInfo, gnClustersRequired);
if (gnClusterStart == 0)
{
Mes("Unable to find contigous clusters\n");
return 0;
}
}
else
{
gnClusterStart = gnFirstCluster;
}
if (gnStrictLocation && gnClusterStart != gnFirstCluster)
{
Mes("Unable to allocate clusters at/from the requested location\n");
return 1;
}
//
// Ready to allocate clusters and set file information
//
Mes("Allocating clusters...\n");
gnAllocated = OccupyClusters(&gsDrvInfo, gnClusterStart, gnClustersRequired);
printf("%lu clusters allocated.\n", gnAllocated);
Mes("Committing FAT Sectors...\n");
CcCommitFATSectors(&gsDrvInfo);
//
// Now set file information
//
Mes("Setting file information...\n");
gsFileInfo.StartCluster = gnClusterStart;
if (gnSizeUnit)
{
gsFileInfo.Size = gnAllocated * gsDrvInfo.SectorsPerCluster * gsDrvInfo.BytesPerSector;
}
else
{
if (gnAllocated != gnClustersRequired)
{
gsFileInfo.Size = gnAllocated * gsDrvInfo.SectorsPerCluster * gsDrvInfo.BytesPerSector;
}
else
{
gsFileInfo.Size = gnSize;
}
}
if (!SetFileInfo(&gsDrvInfo, &gsFileLoc, &gsFileInfo))
{
Mes("FATAL ERROR : Error setting file information\n");
return 1;
}
CcCommitFATSectors(&gsDrvInfo);
//
// All done, unlock volume
//
if (!UnlockVolume(gcDrive))
{
Mes("WARNING : Unable to unlock volume\n");
//return 1;
}
// DeallocateLRUMRUList();
// DeallocateFATCacheTree(gpHeadNode);
DeallocateFATCacheList();
Mes("File created successfully.\n");
}
}
return 0;
}
UINT16 ProcessCommandLine(int argc, char *argv[])
{
UINT16 ti, tn;
char sStr[50];
if (argc < 3)
{
DisplayUsage();
return 0;
}
//
// Get FileName
//
strcpy(gsFileParam, argv[1]);
tn = strlen(gsFileParam);
//
// Do simple validation
//
for (ti = 0; ti < tn; ti++)
{
if (gsFileParam[ti] == '*' || gsFileParam[ti] == '?')
{
DisplayUsage();
return 0;
}
}
gnDumpMode = 0;
gnFreeSpaceDumpMode = 0;
//
// Get Size
//
strcpy(sStr, argv[2]);
if (stricmp(sStr, "/info") == 0)
{
gnDumpMode = 1;
//
// dont accept anyother parameter if /info is entered
//
if (argc > 3)
{
DisplayUsage();
return 0;
}
else
{
return 1;
}
}
else
{
if (stricmp(sStr, "/freespace") == 0)
{
gnFreeSpaceDumpMode = 1;
if (argc > 3)
{
DisplayUsage();
return 0;
}
else
{
if (strlen(gsFileParam) > 2 ||
toupper(gsFileParam[0]) < 'A' ||
toupper(gsFileParam[0]) > 'Z' ||
gsFileParam[1] != ':')
{
DisplayUsage();
return 0;
}
return 1;
}
}
}
if (!PureNumber(sStr))
{
DisplayUsage();
return 0;
}
gnSize = (UINT32) atol(sStr);
if (gnSize == 0)
{
DisplayUsage();
return 0;
}
gnContig = 0;
gnStrictLocation = 0;
gnSizeUnit = 0;
gnClusterUnit = 0;
gnFirstCluster = 0;
gnClusterStart = 0;
gbValidateFirstClusterParam = 0;
for (ti = 3; ti < (UINT16) argc; ti++)
{
strcpy(sStr, argv[ti]);
//
// Check each argument if it qualifies and remember them in global variables
//
if (ti == 3)
{
if (stricmp(sStr, "KB") == 0)
{
gnSizeUnit = 1;
continue;
}
else
{
if (stricmp(sStr, "MB") == 0)
{
gnSizeUnit = 2;
continue;
}
else
{
if (stricmp(sStr, "GB") == 0)
{
gnSizeUnit = 3;
continue;
}
else
{
if (stricmp(sStr, "%free") == 0)
{
gnSizeUnit = 4;
continue;
}
else
{
if (stricmp(sStr, "%disk") == 0)
{
gnSizeUnit = 5;
continue;
}
}
}
}
}
}
if (stricmp(sStr, "/contig") == 0)
{
gnContig = 1;
continue;
}
if (stricmp(sStr, "/firstcluster") == 0)
{
if ((UINT16) argc <= ti)
{
DisplayUsage();
return 0;
}
else
{
// this parm must be a number
strcpy(sStr, argv[ti+1]);
if (!PureNumber(sStr))
{
DisplayUsage();
return 0;
}
gnFirstCluster = atol(sStr);
if (gnFirstCluster == 0)
{
DisplayUsage();
return 0;
}
else
{
gbValidateFirstClusterParam = 1;
//
// look for optional unit
//
if ((UINT16) argc > ti + 1)
{
strcpy(sStr, argv[ti+2]);
if (stricmp(sStr, "KB") == 0 ||
stricmp(sStr, "MB") == 0 ||
stricmp(sStr, "GB") == 0 ||
stricmp(sStr, "/strictlocation") == 0)
{
if (stricmp(sStr, "KB") == 0)
{
gnClusterUnit = 1;
}
else
{
if (stricmp(sStr, "MB") == 0)
{
gnClusterUnit = 2;
}
else
{
if (stricmp(sStr, "GB") == 0)
{
Mes("gnClusterUnit is 3\n");
gnClusterUnit = 3;
}
else
{
if (stricmp(sStr, "/strictlocation") == 0)
{
gnStrictLocation = 1;
}
}
}
}
// if cluster unit AND /strictlocation specified
if (!gnStrictLocation && (UINT16) argc > ti + 2)
{
strcpy(sStr, argv[ti+3]);
if (stricmp(sStr, "/strictlocation") == 0)
{
gnStrictLocation = 1;
ti+=3;
continue;
}
}
ti+=2;
continue;
}
}
ti++;
continue;
}
}
}
// if it got here the command line is not recognized (junk parameter)
DisplayUsage();
return 0;
}
return 1;
}
UINT16 PureNumber(char *sNumStr)
{
UINT16 ti, tn;
tn = strlen(sNumStr);
for (ti = 0; ti < tn; ti++)
{
if (sNumStr[ti] < 48 || sNumStr[ti] > 57)
{
return 0;
}
}
return 1;
}
void DisplayUsage(void)
{
Mes("Invalid parameters\n");
Mes("USAGE:\n\n");
Mes("CVTAREA <filename> <size> (<units>) (/contig) \n\t\t(/firstcluster <cluster> (clusunits) (/strictlocation))\n\n");
Mes("\t<filename> - is a filename.\n");
Mes("\t<size> - is a 32 bit integer.\n");
Mes("\t<units> - is a modifier for <size> valid options are \n\t\tKB, MB, GB, ");
putchar(37);
// if we dont seperate this, stupid thing comes up with unrecognized escape sequence
Mes("disk and ");
putchar(37);
// if we dont seperate this, stupid thing comes up with floating point error because of %f
Mes("free\n");
Mes("\t/contig - the file must be created contiguously on disk.\n");
Mes("\t/firstcluster - the first cluster at which the file shall be located.\n\n\n");
Mes("CVTAREA <filename> </info>\n\n");
Mes("\t<filename> - is a filename.\n");
Mes("\t/info - dumps cluster numbers allocated for a given file\n\n\n");
Mes("CVTAREA <drivename> </freespace>\n\n");
Mes("\tdrivename - is a drive letter followed by : example C:\n");
Mes("\t/freespace - dumps free space chunks\n");
}
void Mes(char *pMessage)
{
printf(pMessage);
}
//
// Sector related functions
//
UINT16 LockVolume(BYTE nDrive, BYTE nMode)
{
union _REGS inregs;
union _REGS outregs;
struct _SREGS segregs;
// Lock volume
outregs.x.cflag = 1;
inregs.x.ax = 0x440d;
inregs.h.bh = nMode;
inregs.h.bl = nDrive - 64;
inregs.h.ch = 8;
inregs.h.cl = 0x4a;
inregs.x.dx = 0;
int86x(0x21, &inregs, &outregs, &segregs);
if (outregs.x.cflag & 1)
{
return 0;
}
else
{
return 1;
}
}
UINT16 UnlockVolume(BYTE nDrive)
{
union _REGS inregs;
union _REGS outregs;
struct _SREGS segregs;
// Lock volume
outregs.x.cflag = 1;
inregs.x.ax = 0x440d;
inregs.h.bl = nDrive - 64;
inregs.h.ch = 8;
inregs.h.cl = 0x6a;
int86x(0x21, &inregs, &outregs, &segregs);
if (outregs.x.cflag & 1)
{
return 0;
}
else
{
return 1;
}
}
BYTE GetCurrentDrive(void)
{
union _REGS inregs;
union _REGS outregs;
inregs.h.ah = 0x19;
int86(0x21, &inregs, &outregs);
if (outregs.x.cflag & 1)
{
return 0;
}
return outregs.h.al + 65;
}
BYTE GetCurrentDirectory(BYTE nDrive, BYTE *pBuffer)
{
union _REGS inregs;
union _REGS outregs;
struct _SREGS segregs;
outregs.x.cflag = 1;
inregs.x.ax = 0x7147; // get current directory
inregs.h.dl = nDrive - 64;
segregs.ds = FP_SEG(pBuffer);
inregs.x.si = FP_OFF(pBuffer);
int86x(0x21, &inregs, &outregs, &segregs);
if (outregs.x.cflag & 1)
{
// Try old method
inregs.h.ah = 0x47;
int86x(0x21, &inregs, &outregs, &segregs);
if (outregs.x.cflag & 1)
{
return 0;
}
}
return 1;
}
UINT16 ReadSector(BYTE nDrive, UINT32 nStartSector, UINT16 nCount, BYTE *pBuffer)
{
BYTE DriveNum;
union _REGS inregs;
union _REGS outregs;
struct _SREGS segregs;
ABSPACKET AbsPacket, *pAbs;
// Try int 21h 7305 first
pAbs = &AbsPacket;
// A: = 1, B: = 2, .....
DriveNum = nDrive - 65;
Tx.e.evx = 0;
Tx.e.evx = nStartSector;
AbsPacket.SectorLow = Tx.x.vx;
AbsPacket.SectorHigh = Tx.x.xvx;
AbsPacket.SectorCount = nCount;
AbsPacket.BufferOffset = FP_OFF(pBuffer);
AbsPacket.BufferSegment = FP_SEG(pBuffer);
segregs.ds = FP_SEG(pAbs);
segregs.es = FP_SEG(pAbs);
inregs.x.bx = FP_OFF(pAbs);
inregs.x.cx = 0xffff;
inregs.h.dl = nDrive-64;
inregs.x.ax = 0x7305;
//
// Read mode SI = 0
//
inregs.x.si = 0;
outregs.x.ax = 0;
outregs.x.cflag = 0;
int86x(0x21, &inregs, &outregs, &segregs);
if (outregs.x.cflag & 0x1)
{
goto ErrorRead;
}
return 1;
ErrorRead:
return 0;
}
UINT16 WriteSector(BYTE nDrive, UINT32 nStartSector, UINT16 nCount, BYTE *pBuffer)
{
BYTE DriveNum;
union _REGS inregs;
union _REGS outregs;
struct _SREGS segregs;
ABSPACKET AbsPacket, *pAbs;
// Try int 21h 7305 first
pAbs = &AbsPacket;
// A: = 1, B: = 2, .....
DriveNum = nDrive - 65;
Tx.e.evx = 0;
Tx.e.evx = nStartSector;
AbsPacket.SectorLow = Tx.x.vx;
AbsPacket.SectorHigh = Tx.x.xvx;
AbsPacket.SectorCount = nCount;
AbsPacket.BufferOffset = FP_OFF(pBuffer);
AbsPacket.BufferSegment = FP_SEG(pBuffer);
segregs.ds = FP_SEG(pAbs);
segregs.es = FP_SEG(pAbs);
inregs.x.bx = FP_OFF(pAbs);
inregs.x.cx = 0xffff;
inregs.h.dl = nDrive-64;
inregs.x.ax = 0x7305;
//
// Write mode SI != 0 (bit 1 set)
//
inregs.x.si = 1;
outregs.x.ax = 0;
outregs.x.cflag = 0;
int86x(0x21, &inregs, &outregs, &segregs);
if (outregs.x.cflag & 0x1)
{
goto ErrorWrite;
}
return 1;
ErrorWrite:
return 0;
}
//
// Boot related functions
//
UINT16 BuildDriveInfo(BYTE Drive, BPBINFO *pDrvInfo)
{
BYTE *pSector;
pSector = (BYTE *) malloc(1024);
if (!pSector)
{
Mes("Memory allocation error\n");
return 0;
}
//
// Although FAT32 boot sector spans two sectors BPB is contained in the first sector
// So we only read one sector
//
if (!ReadSector(Drive, 0, 1, pSector))
{
Mes("Unable to read boot sector\n");
return 0;
}
if ((strncmp(pSector+54, "FAT16 ", 8) == 0) || strncmp(pSector+54, "FAT12 ", 8) == 0)
{
GetFATBPBInfo(pSector, pDrvInfo);
}
else
{
if (strncmp(pSector+82, "FAT32 ", 8) == 0)
{
GetFAT32BPBInfo(pSector, pDrvInfo);
}
else
{
Mes("Unsupported file system\n");
return 0;
}
}
free(pSector);
if (!pDrvInfo->ReliableInfo)
{
Mes("Drive is either corrupted or unable to get BPB Info\n");
}
pDrvInfo->Drive = Drive;
return 1;
}
UINT16 GetFATBPBInfo(BYTE *pBootSector, BPBINFO *pDrvInfo)
{
pDrvInfo->ReliableInfo = 0;
if (pBootSector[510] != 0x55 || pBootSector[511] != 0xAA)
{
Mes("Invalid boot sector\n");
return 0;
}
else
{
pDrvInfo->BytesPerSector = (pBootSector[0x0c] << 8) | pBootSector[0x0b];
if (pDrvInfo->BytesPerSector == 0) // Just be safe
{
Mes("Invalid boot sector\n");
return 0;
}
pDrvInfo->SectorsPerCluster = pBootSector[0x0d];
if (pDrvInfo->SectorsPerCluster == 0) // Just be safe
{
Mes("Invalid boot sector\n");
return 0;
}
pDrvInfo->ReservedBeforeFAT = (pBootSector[0x0f] << 8) | pBootSector[0x0e];
pDrvInfo->FATCount = pBootSector[0x10];
if (pDrvInfo->FATCount == 0) // Just be safe
{
Mes("Invalid boot sector\n");
return 0;
}
pDrvInfo->MaxRootDirEntries = (pBootSector[0x12] << 8) | pBootSector[0x11];
pDrvInfo->TotalSectors = (pBootSector[0x14] << 8) | pBootSector[0x13];
pDrvInfo->MediaID = pBootSector[0x15];
pDrvInfo->SectorsPerFAT = (pBootSector[0x17] << 8) | pBootSector[0x16];
if (pDrvInfo->SectorsPerFAT == 0) // Just be safe
{
Mes("Invalid boot sector\n");
return 0;
}
pDrvInfo->SectorsPerTrack = (pBootSector[0x19] << 8) | pBootSector[0x18];
pDrvInfo->Heads = (pBootSector[0x1b] << 8) | pBootSector[0x1a];
pDrvInfo->HiddenSectors = (pBootSector[0x1d] << 8) | pBootSector[0x1c];
Rx.h.vl = pBootSector[0x20]; Rx.h.vh = pBootSector[0x21]; Rx.h.xvl = pBootSector[0x22];Rx.h.xvh = pBootSector[0x23];
pDrvInfo->BigTotalSectors = Rx.e.evx;
pDrvInfo->RootDirCluster = 0;
//
// The following informations are useful and are not directly from Boot sector
//
pDrvInfo->TotalRootDirSectors = ((pDrvInfo->MaxRootDirEntries * 32) / 512) + 1;
if (((pDrvInfo->MaxRootDirEntries * 32) % 512) == 0)
{
pDrvInfo->TotalRootDirSectors--;
}
pDrvInfo->TotalSystemSectors = (UINT32)pDrvInfo->ReservedBeforeFAT + (UINT32)pDrvInfo->FATCount * (UINT32)pDrvInfo->SectorsPerFAT + (UINT32) pDrvInfo->TotalRootDirSectors;
pDrvInfo->FirstRootDirSector = (UINT32)((UINT32)pDrvInfo->ReservedBeforeFAT + (UINT32)pDrvInfo->FATCount * (UINT32)pDrvInfo->SectorsPerFAT);
//
// If TotalSectors is zero then the actual total sectors value is in BigTotalSectors
// and it means the drive is BIGDOS (> 32MB).
//
if (pDrvInfo->TotalSectors == 0)
{
pDrvInfo->TotalClusters = ((pDrvInfo->BigTotalSectors - pDrvInfo->TotalSystemSectors) / pDrvInfo->SectorsPerCluster) + 2 - 1; // +2 Because clusters starts from 2
}
else
{
pDrvInfo->TotalClusters = ((pDrvInfo->TotalSectors - pDrvInfo->TotalSystemSectors) / pDrvInfo->SectorsPerCluster) + 2 - 1; // +2 because clusters starts from 2
}
//
// Determine the fat type
//
if (pDrvInfo->TotalClusters <= 4096)
{
pDrvInfo->FATType = 12;
}
else
{
pDrvInfo->FATType = 16;
}
}
//
// Validate all the info above
//
if (pDrvInfo->FATCount == 2 && pDrvInfo->SectorsPerFAT != 0 &&
pDrvInfo->SectorsPerCluster != 0 && pDrvInfo->BytesPerSector == 512 &&
(pDrvInfo->TotalSectors != 0 || pDrvInfo->BigTotalSectors != 0) &&
pDrvInfo->ReservedBeforeFAT != 0)
{
pDrvInfo->ReliableInfo = 1;
}
return 1;
}
UINT16 GetFAT32BPBInfo(BYTE *pBootSector, BPBINFO *pDrvInfo)
{
pDrvInfo->ReliableInfo = 0;
if (pBootSector[510] != 0x55 || pBootSector[511] != 0xAA)
{
Mes("Invalid boot sector\n");
return 0;
}
else
{
//
// Build the drive information now
//
pDrvInfo->BytesPerSector = (pBootSector[0x0c] << 8) | pBootSector[0x0b];
pDrvInfo->SectorsPerCluster = pBootSector[0x0d];
pDrvInfo->ReservedBeforeFAT = (pBootSector[0x0f] << 8) | pBootSector[0x0e];
pDrvInfo->FATCount = pBootSector[0x10];
pDrvInfo->MaxRootDirEntries = (pBootSector[0x12] << 8) | pBootSector[0x11];
pDrvInfo->TotalSectors = 0;
pDrvInfo->MediaID = pBootSector[0x15];
pDrvInfo->SectorsPerTrack = (pBootSector[0x19] << 8) | pBootSector[0x18];
pDrvInfo->Heads = (pBootSector[0x1b] << 8) | pBootSector[0x1a];
Rx.h.vl = pBootSector[0x1c]; Rx.h.vh = pBootSector[0x1d]; Rx.h.xvl = pBootSector[0x1e];Rx.h.xvh = pBootSector[0x1f];
pDrvInfo->HiddenSectors = Rx.e.evx;
Rx.h.vl = pBootSector[0x20]; Rx.h.vh = pBootSector[0x21]; Rx.h.xvl = pBootSector[0x22];Rx.h.xvh = pBootSector[0x23];
pDrvInfo->BigTotalSectors = Rx.e.evx;
Rx.h.vl = pBootSector[0x24]; Rx.h.vh = pBootSector[0x25]; Rx.h.xvl = pBootSector[0x26];Rx.h.xvh = pBootSector[0x27];
pDrvInfo->SectorsPerFAT = Rx.e.evx;
Rx.h.vl = pBootSector[0x2c]; Rx.h.vh = pBootSector[0x2d]; Rx.h.xvl = pBootSector[0x2e];Rx.h.xvh = pBootSector[0x2f];
pDrvInfo->RootDirCluster = Rx.e.evx;
//
// The following informations are useful and are not directly from the Boot sector
//
pDrvInfo->TotalSystemSectors = (UINT32)((UINT32)pDrvInfo->ReservedBeforeFAT + (UINT32)pDrvInfo->FATCount * (UINT32) pDrvInfo->SectorsPerFAT);
pDrvInfo->FirstRootDirSector = (UINT32)((UINT32)pDrvInfo->TotalSystemSectors + (UINT32)(pDrvInfo->RootDirCluster - 2) * (UINT32)pDrvInfo->SectorsPerCluster + 1);
pDrvInfo->TotalClusters = ((pDrvInfo->BigTotalSectors - pDrvInfo->TotalSystemSectors) / pDrvInfo->SectorsPerCluster) + 2 - 1; // +2 Because clusters starts from 2
pDrvInfo->FATType = 32;
//
// Validate all the info above
//
if (pDrvInfo->FATCount == 2 && pDrvInfo->SectorsPerFAT != 0 &&
pDrvInfo->SectorsPerCluster != 0 && pDrvInfo->BytesPerSector == 512 &&
(pDrvInfo->TotalSectors != 0 || pDrvInfo->BigTotalSectors != 0) &&
pDrvInfo->ReservedBeforeFAT != 0)
{
pDrvInfo->ReliableInfo = 1;
}
}
return 1;
}
UINT16 AddNode(PNODE pNode)
{
if (!pNode)
{
return 1;
}
if (!gpHeadNode)
{
gpHeadNode = pNode;
gpTailNode = pNode;
pNode->Back = NULL;
pNode->Next = NULL;
}
else
{
pNode->Next = gpHeadNode;
pNode->Back = NULL;
gpHeadNode->Back = pNode;
gpHeadNode = pNode;
}
return 1;
}
PNODE FindNode(UINT32 nSector)
{
PNODE pNode;
if (!gpHeadNode)
{
return NULL;
}
pNode = gpHeadNode;
while (pNode)
{
if (pNode->Sector == nSector)
{
break;
}
pNode = pNode->Next;
}
return pNode;
}
PNODE RemoveNode(void)
{
// Removes last node
PNODE pNode;
if (!gpTailNode)
{
return NULL;
}
pNode = gpTailNode;
gpTailNode = pNode->Back;
pNode->Back = NULL;
if (gpTailNode)
{
gpTailNode->Next = NULL;
}
return pNode;
}
void DeallocateFATCacheList(void)
{
PNODE pNode;
while (gpHeadNode)
{
pNode = gpHeadNode;
gpHeadNode = gpHeadNode->Next;
free(pNode->Buffer);
free(pNode);
}
}
//
// Functions related to FAT caching
//
BYTE *CcReadFATSector(BPBINFO *pDrvInfo, UINT32 nFATSector)
{
// Locate nFATSector in the cache
PNODE pNode;
pNode = FindNode(nFATSector);
if (!pNode)
{
//
// If MAXCACHE reached use LRU MRU scheme
//
if (gpFATNodeCount < MAXCACHE)
{
pNode = malloc(sizeof(NODE)+5);
if (!pNode)
{
return NULL;
}
pNode->Buffer = malloc(512);
if (!pNode->Buffer)
{
return NULL;
}
ReadSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + nFATSector, 1, pNode->Buffer);
pNode->Sector = nFATSector;
pNode->Dirty = 0;
AddNode(pNode);
gpFATNodeCount++;
}
else
{
// RemoveLRUMakeMRU(gpLRU->Node);
// pNode = gpMRU->Node;
pNode = RemoveNode();
if (pNode->Dirty)
{
WriteSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + pNode->Sector, 1, pNode->Buffer);
WriteSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + pDrvInfo->SectorsPerFAT + pNode->Sector, 1, pNode->Buffer);
}
pNode->Sector = nFATSector;
ReadSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + nFATSector, 1, pNode->Buffer);
AddNode(pNode);
}
}
return pNode->Buffer;
}
UINT16 CcWriteFATSector(BPBINFO *pDrvInfo, UINT32 nFATSector)
{
PNODE pNode;
pNode = FindNode(nFATSector);
if (!pNode)
{
// must be found, because every FAT node we write must gone thru CcReadFATSector first
return 0;
}
else
{
pNode->Dirty = 1;
}
}
void CcCommitFATSectors(BPBINFO *pDrvInfo)
{
PNODE pNode;
if (!gpHeadNode)
{
return;
}
pNode = gpHeadNode;
while (pNode)
{
if (pNode->Dirty)
{
// 1st FAT copy
WriteSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + pNode->Sector, 1, pNode->Buffer);
// 2nd FAT copy
WriteSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + pDrvInfo->SectorsPerFAT + pNode->Sector, 1, pNode->Buffer);
pNode->Dirty = 0;
}
pNode = pNode->Next;
}
return;
}
//
// FAT related functions
//
UINT32 FindNextCluster(BPBINFO *pDrvInfo,UINT32 CurrentCluster)
{
UINT32 ToRead;
UINT32 SeekOffset;
BYTE JustAByte;
Rx.e.evx = 0;
switch (pDrvInfo->FATType)
{
case 12:
ToRead = (CurrentCluster*3/2)/512;
PettyFATSector = CcReadFATSector(pDrvInfo, ToRead);
//**ReadSector(DrvInfo->Drive, DrvInfo->ReservedBeforeFAT + ToRead, 1, (BYTE *) PettyFATSector);
//
// Go to that cluster location
//
SeekOffset = (CurrentCluster*3/2) % 512;
if ((CurrentCluster % 2) == 0) // if even
{
JustAByte = (BYTE) (PettyFATSector[SeekOffset+1] & 0x0f);
Rx.h.vl = PettyFATSector[SeekOffset]; Rx.h.vh = JustAByte;
}
else // if cluster number is odd the calculation is different
{
JustAByte = (BYTE) (PettyFATSector[SeekOffset] & 0xf0);
Rx.h.vl = JustAByte; Rx.h.vh = PettyFATSector[SeekOffset+1];
Rx.x.vx >>= 4;
}
Rx.h.xvl = 0; Rx.h.xvh = 0;
break;
case 16:
ToRead = CurrentCluster/256; // 256 cells in a 16 bit FAT sector
PettyFATSector = CcReadFATSector(pDrvInfo, ToRead);
//**ReadSector(DrvInfo->Drive, DrvInfo->ReservedBeforeFAT + ToRead, 1, (BYTE *)PettyFATSector);
//
// Go to that Cluster location
//
SeekOffset = (CurrentCluster - (ToRead * 256)) * 2;
Rx.h.vl = PettyFATSector[SeekOffset]; Rx.h.vh = PettyFATSector[SeekOffset+1];
Rx.h.xvl = 0; Rx.h.xvh = 0;
break;
case 32:
ToRead = CurrentCluster/128; // 128 cells in a 32 bit FAT sector
PettyFATSector = CcReadFATSector(pDrvInfo, ToRead);
//**ReadSector(DrvInfo->Drive, DrvInfo->ReservedBeforeFAT + ToRead, 1, (BYTE *)PettyFATSector);
//
// Go to that Cluster location
//
SeekOffset = (CurrentCluster - (ToRead * 128)) * 4;
Rx.h.vl = PettyFATSector[SeekOffset]; Rx.h.vh = PettyFATSector[SeekOffset+1];
Rx.h.xvl = PettyFATSector[SeekOffset+2]; Rx.h.xvh = PettyFATSector[SeekOffset+3];
break;
default:
Rx.e.evx = 0;
break;
}
return Rx.e.evx;
}
UINT16 UpdateFATLocation(BPBINFO *pDrvInfo, UINT32 CurrentCluster,UINT32 PointingValue)
{
UINT32 ToRead;
UINT32 SeekOffset;
if (CurrentCluster == 0 || CurrentCluster == 1 || CurrentCluster >= (GetFATEOF(pDrvInfo)-7))
{
return 0;
}
switch (pDrvInfo->FATType)
{
case 12:
ToRead = (CurrentCluster*3/2)/512;
PettyFATSector = CcReadFATSector(pDrvInfo, ToRead);
//**ReadSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + ToRead, 1, PettyFATSector);
//
// Go to that location
//
SeekOffset = (CurrentCluster*3/2) % 512;
if ((CurrentCluster % 2) == 0) // if even
{
Rx.e.evx = 0;
Rx.x.vx = (UINT16) PointingValue;
PettyFATSector[SeekOffset+1] = (BYTE) (PettyFATSector[SeekOffset+1] & 0xf0);
Rx.h.vh = (BYTE) (Rx.h.vh & 0x0f);
PettyFATSector[SeekOffset+1] = PettyFATSector[SeekOffset+1] | Rx.h.vh;
PettyFATSector[SeekOffset] = Rx.h.vl;
}
else // if cluster number is odd the calculation is different
{
Rx.e.evx = 0;
Rx.x.vx = (UINT16) PointingValue;
PettyFATSector[SeekOffset] = (BYTE)(PettyFATSector[SeekOffset] & 0x0f);
Rx.h.vl = (BYTE) (Rx.h.vl & 0xf0);
PettyFATSector[SeekOffset] = PettyFATSector[SeekOffset] | Rx.h.vl;
PettyFATSector[SeekOffset+1] = Rx.h.vh;
}
break;
case 16:
ToRead = CurrentCluster/256; // 256 cells in a 16 bit FAT sector
PettyFATSector = CcReadFATSector(pDrvInfo, ToRead);
//**ReadSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + ToRead, 1, PettyFATSector);
//
// Go to that location
//
SeekOffset = (CurrentCluster % 256) * 2;
Rx.e.evx = 0;
Rx.x.vx = (UINT16) PointingValue;
PettyFATSector[SeekOffset] = Rx.h.vl;PettyFATSector[SeekOffset+1] = Rx.h.vh;
break;
case 32:
ToRead = CurrentCluster/128; // 128 cells in a 32 bit FAT sector
PettyFATSector = CcReadFATSector(pDrvInfo, ToRead);
//**ReadSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + ToRead, 1, PettyFATSector);
//
// Go to that location
//
SeekOffset = (CurrentCluster % 128) * 4;
Rx.e.evx = 0;
Rx.e.evx = PointingValue;
PettyFATSector[SeekOffset] = Rx.h.vl; PettyFATSector[SeekOffset+1] = Rx.h.vh;
PettyFATSector[SeekOffset+2] = Rx.h.xvl; PettyFATSector[SeekOffset+3] = Rx.h.xvh;
break;
default:
return 0;
}
CcWriteFATSector(pDrvInfo, ToRead);
//**Update FAT 1st Copy
//WriteSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + ToRead, 1, PettyFATSector);
// Update FAT 2nd Copy
//**WriteSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + pDrvInfo->SectorsPerFAT + ToRead, 1, PettyFATSector);
return 1;
}
UINT32 FindFreeCluster(BPBINFO *pDrvInfo)
{
UINT32 ti, tj;
UINT32 FreeCluster;
UINT32 SeekOffset;
if (pDrvInfo->FATType == 12)
{
//
// Instead of messing up with FAT16 and FAT32's smooth operation use the slow method
//
for (ti = 2; ti <= pDrvInfo->TotalClusters; ti++)
{
//
// this just aint gonna slow down things on a huge 2MB FAT12 partition
//
if (FindNextCluster(pDrvInfo, ti) == 0)
{
LastClusterAllocated = ti;
return ti;
}
}
return 0;
}
else
{
// ** this part was developed before FAT caching was implemented... and the purpose was
// for better performance
// But with FAT caching it is even better or no difference at all, so this part is left alone.
//
// Start looking from Cluster 2
//
FreeCluster = 2;
for (ti = 1; ti <= pDrvInfo->SectorsPerFAT; ti++)
{
//**if (!ReadSector(pDrvInfo->Drive, pDrvInfo->ReservedBeforeFAT + ti-1, 1, PettyFATSector))
PettyFATSector = CcReadFATSector(pDrvInfo, ti-1);
if (!PettyFATSector)
{
return 0;
}
tj = 0;
while (tj < 512)
{
switch (pDrvInfo->FATType)
{
case 16:
SeekOffset = (FreeCluster * 2) % (UINT32) 512;
if (PettyFATSector[SeekOffset] == 0 && PettyFATSector[SeekOffset+1] == 0)
{
LastClusterAllocated = FreeCluster;
return FreeCluster;
}
tj += 2;
break;
case 32:
SeekOffset = (FreeCluster * 4) % (UINT32) 512;
if (PettyFATSector[SeekOffset] == 0 && PettyFATSector[SeekOffset+1] == 0
&& PettyFATSector[SeekOffset+2] == 0 && PettyFATSector[SeekOffset+3] == 0)
{
LastClusterAllocated = FreeCluster;
return FreeCluster;
}
tj += 4;
break;
default:
return 0;
}
FreeCluster++;
if (FreeCluster > pDrvInfo->TotalClusters)
{
return 0;
}
}
}
return 0;
}
}
UINT32 QFindFreeCluster(BPBINFO *pDrvInfo)
{
// LastClusterAllocated is critical for this function to work fast.
// It starts from LastClusterAllocated+1 to find a free cluster and calls
// the regular FindFreeCluster if did not find a free cluster between
// LastClusterAllocated+1 and TotalClusters
UINT32 ti;
UINT32 FreeCluster;
FreeCluster = 0;
for (ti = LastClusterAllocated+1; ti <= pDrvInfo->TotalClusters; ti++)
{
if (FindNextCluster(pDrvInfo, ti) == 0)
{
LastClusterAllocated = ti;
return ti;
}
}
if (FreeCluster == 0)
{
FreeCluster = FindFreeCluster(pDrvInfo);
}
return FreeCluster;
}
UINT32 GetFATEOF(BPBINFO *pDrvInfo)
{
UINT32 FATEOF;
switch (pDrvInfo->FATType)
{
case 12:
FATEOF = 0x0fff;
break;
case 16:
FATEOF = 0xffff;
break;
case 32:
FATEOF = 0x0fffffff;
break;
}
return FATEOF;
}
UINT32 GetFreeClusters(BPBINFO *pDrvInfo)
{
UINT32 nCount;
UINT32 ti;
nCount = 0;
for (ti = 2; ti <= pDrvInfo->TotalClusters; ti++)
{
if (FindNextCluster(pDrvInfo, ti) == 0)
{
nCount++;
}
}
return nCount;
}
UINT32 ConvertClusterUnit(BPBINFO *pDrvInfo)
{
//
// one KB = 2 sectors, we use 2 to avoid get thru overflow as much as possible (although we have overflow check)
// when gnClusterUnit is not 0, it means a start location from the beginning of the disk BY SIZE.
// for example, if "/firstcluster 1 GB" is specified, it means start from a cluster after (skipping)
// 1 GB space from the beginning of the disk
//
UINT32 nFirstCluster;
nFirstCluster = gnFirstCluster;
switch (gnClusterUnit)
{
case 0:
// do nothing
break;
case 1: // Start Cluster unit specified in KB
if (nFirstCluster < 0x80000000) // overflow check
{
nFirstCluster = ((nFirstCluster * 2) / pDrvInfo->SectorsPerCluster) + 2;
}
else
{
nFirstCluster = 0;
}
break;
case 2: // Start Cluster unit specified in MB
if (nFirstCluster < 0x200000) // overflow check
{
nFirstCluster = ((nFirstCluster * 2 * 1024) / pDrvInfo->SectorsPerCluster) + 2;
}
else
{
nFirstCluster = 0;
}
break;
case 3: // Start Cluster unit specified in GB
if (nFirstCluster < 0x800) // overflow check
{
nFirstCluster = ((nFirstCluster * 2 * 1024 * 1024) / pDrvInfo->SectorsPerCluster) + 2;
}
else
{
nFirstCluster = 0;
}
break;
default:
// do nothing
break;
}
if ((nFirstCluster > pDrvInfo->TotalClusters) || (nFirstCluster < 2))
{
nFirstCluster = 0;
}
return nFirstCluster;
}
UINT32 GetClustersRequired(BPBINFO *pDrvInfo)
{
UINT32 nClustersRequired;
UINT32 nDriveClusterSize;
UINT32 tnSize;
nDriveClusterSize = (UINT32) pDrvInfo->SectorsPerCluster * (UINT32) pDrvInfo->BytesPerSector;
tnSize = gnSize;
//
// calcs looks vague, but we try to avoid as much overflow as possible
//
switch (gnSizeUnit)
{
case 0: //bytes
nClustersRequired = tnSize / nDriveClusterSize;
if (tnSize % nDriveClusterSize)
{
nClustersRequired++;
}
break;
case 1: //KB
if (nDriveClusterSize >= 1024)
{
nClustersRequired = tnSize / (nDriveClusterSize / 1024);
if (tnSize % (nDriveClusterSize / 1024))
{
nClustersRequired++;
}
}
else
{
// Here we go by sectors, still trying to avoid overflow
tnSize = tnSize * 2; // sectors
nClustersRequired = tnSize / pDrvInfo->SectorsPerCluster;
if (tnSize % pDrvInfo->SectorsPerCluster)
{
nClustersRequired++;
}
}
break;
case 2: // MB
if (nDriveClusterSize >= 1024)
{
tnSize = tnSize * 1024;
nClustersRequired = tnSize / (nDriveClusterSize / 1024);
if (tnSize % (nDriveClusterSize / 1024))
{
nClustersRequired++;
}
}
else
{
tnSize = tnSize * 2 * 1024; //sectors
nClustersRequired = tnSize / pDrvInfo->SectorsPerCluster;
if (tnSize % pDrvInfo->SectorsPerCluster)
{
nClustersRequired++;
}
}
break;
case 3: // GB
if (nDriveClusterSize >= 1024)
{
tnSize = tnSize * 1024 * 1024;
nClustersRequired = tnSize / (nDriveClusterSize / 1024);
if (tnSize % (nDriveClusterSize / 1024))
{
nClustersRequired++;
}
}
else
{
tnSize = tnSize * 2 * 1024 * 1024; //sectors
nClustersRequired = tnSize / pDrvInfo->SectorsPerCluster;
if (tnSize % pDrvInfo->SectorsPerCluster)
{
nClustersRequired++;
}
}
break;
case 4:
// based on percentage free
nClustersRequired = GetFreeClusters(pDrvInfo);
nClustersRequired = nClustersRequired / 100;
nClustersRequired = nClustersRequired * gnSize;
break;
case 5:
// based on percentage disk size
nClustersRequired = pDrvInfo->TotalClusters;
nClustersRequired = nClustersRequired / 100;
nClustersRequired = nClustersRequired * gnSize;
break;
}
return nClustersRequired;
}
UINT32 GetContigousStart(BPBINFO *pDrvInfo, UINT32 nClustersRequired)
{
UINT32 ti;
UINT32 nContigousStart;
if (gnFirstCluster == 0)
{
nContigousStart = 2;
}
else
{
nContigousStart = gnFirstCluster;
}
//
// -2 is adjustment value, because cluster value starts at 2
//
while ((nContigousStart-2+nClustersRequired) < pDrvInfo->TotalClusters)
{
for (ti = 0; ti < nClustersRequired; ti++)
{
if (FindNextCluster(pDrvInfo, nContigousStart+ti) != 0)
{
break;
}
}
if (ti == nClustersRequired)
{
return nContigousStart;
}
else
{
nContigousStart = nContigousStart+ti+1;
}
}
return 0;
}
UINT32 OccupyClusters(BPBINFO *pDrvInfo, UINT32 nStartCluster, UINT32 nTotalClusters)
{
UINT32 ti, nCurrent, nPrevious;
if (!gnContig)
{
if (nStartCluster == 0)
{
nStartCluster = 2;
}
nCurrent = nStartCluster;
nPrevious = nStartCluster;
//
// First locate a free cluster
//
while (nCurrent <= pDrvInfo->TotalClusters)
{
if (FindNextCluster(pDrvInfo, nCurrent) == 0)
{
break;
}
nCurrent++;
}
nPrevious = nCurrent;
gnClusterStart = nCurrent;
nCurrent++;
// one cluster almost allocated, set ti to 2
ti = 2;
while (ti <= nTotalClusters && nCurrent <= pDrvInfo->TotalClusters)
{
if (FindNextCluster(pDrvInfo, nCurrent) == 0)
{
//
// occupy this cluster
//
UpdateFATLocation(pDrvInfo, nPrevious, nCurrent);
nPrevious = nCurrent;
ti++;
}
nCurrent++;
}
UpdateFATLocation(pDrvInfo, nPrevious, GetFATEOF(pDrvInfo));
if (ti < nTotalClusters)
{
Mes("*** WARNING: Disk full, fewer than required clusters allocated.***\n");
}
return ti-1;
}
else
{
//
// This is a dangerous area. It trusts nStartCluster and nTotalClusters and
// allocates a contigous chain.
//
ti = 1;
nCurrent = nStartCluster;
while (ti < nTotalClusters)
{
nPrevious = nCurrent;
nCurrent++;
UpdateFATLocation(pDrvInfo, nPrevious, nCurrent);
ti++;
}
UpdateFATLocation(pDrvInfo, nCurrent, GetFATEOF(pDrvInfo));
return nTotalClusters;
}
}
//
// Directory related functions
//
UINT16 ReadRootDirSector(BPBINFO *DrvInfo, BYTE *pRootDirBuffer, UINT32 NthSector)
{
// !#! ReadRootDirSector is requested to return 1 sector. But two consiquitive
// sectors are returned so that it helps routine which process the file
// info when an LFN crosses sector boundary
UINT32 SeekSector;
UINT16 NthInChain;// Nth cluster 'order' in the root directory FAT chain
UINT16 ti;
UINT32 NextCluster;
BYTE RetVal;
UINT16 NthInCluster;
RetVal = 1;
switch (DrvInfo->FATType)
{
case 12:
case 16:
if (NthSector > DrvInfo->TotalRootDirSectors)
{
RetVal = 2;
break;
}
SeekSector = (UINT32) DrvInfo->FirstRootDirSector + NthSector - 1;
RetVal = (BYTE) ReadSector(DrvInfo->Drive, SeekSector, 2, pRootDirBuffer);
break;
case 32:
//
// Reading a FAT32 root directory sector is handled in a different way.
// Find out where the requested sector should be residing in the chain
//
NthInChain = (UINT16) (NthSector / (UINT32) DrvInfo->SectorsPerCluster);
NthInCluster = (UINT16) (NthSector - ((UINT32)NthInChain * (UINT32)DrvInfo->SectorsPerCluster));
if (!NthInCluster)
{
NthInChain--;
NthInCluster = DrvInfo->SectorsPerCluster;
}
// Find the cluster at this order in the FAT chain
NextCluster = DrvInfo->RootDirCluster;
ti = 0;
while (ti < NthInChain)
{
if (NextCluster >= (0x0fffffff-7))
{
RetVal = 2;
break;
}
NextCluster = FindNextCluster(DrvInfo, NextCluster);
ti++;
}
if (RetVal != 2)
{
SeekSector = (UINT32) DrvInfo->ReservedBeforeFAT +
DrvInfo->SectorsPerFAT *
(UINT32)DrvInfo->FATCount +
(UINT32) (NextCluster - 2) *
(UINT32) DrvInfo->SectorsPerCluster +
NthInCluster-1;
ReadSector(DrvInfo->Drive, SeekSector, 2, pRootDirBuffer);
// if this is the last sector OF the cluster get next cluster and
// get the first sector
if (NthInCluster == DrvInfo->SectorsPerCluster)
{
NthInCluster = 1;
NextCluster = FindNextCluster(DrvInfo, NextCluster);
if (NextCluster < (0x0fffffff-7))
{
SeekSector = (UINT32) DrvInfo->ReservedBeforeFAT +
DrvInfo->SectorsPerFAT *
(UINT32)DrvInfo->FATCount +
(UINT32) (NextCluster - 2) *
(UINT32) DrvInfo->SectorsPerCluster +
NthInCluster-1;
ReadSector(DrvInfo->Drive, SeekSector, 1, pRootDirBuffer+512); // note the 512 here
}
else
{
for (ti = 512; ti < 1024; ti++)
{
pRootDirBuffer[ti] = 0; // undo the second sector read
}
}
}
}
break;
}
return RetVal;
}
UINT16 WriteRootDirSector(BPBINFO *DrvInfo, BYTE *pRootDirBuffer, UINT32 NthSector)
{
UINT32 SeekSector;
UINT16 NthInChain; // Nth cluster 'order' in the root directory FAT chain
UINT16 ti;
UINT32 NextCluster;
BYTE RetVal;
UINT16 NthInCluster;
RetVal = 1;
switch (DrvInfo->FATType)
{
case 12: // FAT12 and FAT32 are handled the same way
case 16:
if (NthSector > DrvInfo->TotalRootDirSectors)
{
RetVal = 2;
break;
}
SeekSector = (UINT32) DrvInfo->FirstRootDirSector + NthSector-1;
RetVal = (BYTE) WriteSector(DrvInfo->Drive, SeekSector, 1, pRootDirBuffer);
break;
case 32:
// Find out where the requested sector should be going in the chain
NthInChain = (UINT16) (NthSector / (UINT32) DrvInfo->SectorsPerCluster);
NthInCluster = (UINT16) (NthSector - ((UINT32)NthInChain * (UINT32)DrvInfo->SectorsPerCluster));
if (!NthInCluster)
{
NthInChain--;
NthInCluster = DrvInfo->SectorsPerCluster;
}
// Find the cluster at this order in the FAT chain
NextCluster = DrvInfo->RootDirCluster;
ti = 0;
while (ti < NthInChain)
{
if (NextCluster == 0x0fffffff)
{
RetVal = 2;
break;
}
NextCluster = FindNextCluster(DrvInfo, NextCluster);
ti++;
}
if (RetVal != 2)
{
SeekSector = (UINT32) DrvInfo->ReservedBeforeFAT +
DrvInfo->SectorsPerFAT * (UINT32)DrvInfo->FATCount +
(UINT32) (NextCluster - 2) *
(UINT32) DrvInfo->SectorsPerCluster + NthInCluster-1;
WriteSector(DrvInfo->Drive, SeekSector, 1, pRootDirBuffer);
}
break;
}
return RetVal;
}
//
// File related functions
//
void FindFileLocation(BPBINFO *DrvInfo, BYTE *TraversePath, FILELOC *FileLocation)
{
// Parameter TraversePath must be a file name(or dir name) with full path
// The first character must be "\". If the function fails 0 is returned
// in FILELOC->Found. eg. You can pass \Windows\System32\Program Files
// to get "Program Files" location
FILEINFO FileInfo;
BYTE Found;
UINT16 RetVal;
BYTE DirInfo[300];
BYTE CheckInfo[300];
UINT16 ti,tj,n,TraverseCount, Offset;
BYTE i;
UINT32 NthRootDirSector, NextCluster, SectorToRead;
BYTE SectorBuffer[1024];
Found = 0;
ti = strlen(TraversePath);
if (ti < 2)
{
FileLocation->Found = 0;
return;
}
TraverseCount = 0;
// start from next character as the first char is "\" and i value should not change inside while loop
ti = 1;
while (TraversePath[ti] != 0)
{
tj = 0;
for (n = 0; n < 300; n++) DirInfo[n] = 0;
if (TraverseCount != 0)
{
ti++; // increment only after traversing root directory.
}
while (TraversePath[ti] != '\\' && TraversePath[ti] != 0)
{
DirInfo[tj] = TraversePath[ti];
tj++; ti++;
}
DirInfo[tj] = 0;
TraverseCount++;
if (TraverseCount == 1)
{
//
// We are in root directory entry if TraverseCount equals 1
//
Found = 0;
NthRootDirSector = 1;
Offset = 0;
while (!Found)
{
RetVal = ReadRootDirSector(DrvInfo, SectorBuffer, NthRootDirSector);
if (RetVal == 0 || RetVal == 2)
{
break;
}
else
{
while (SectorBuffer[Offset] != 0)
{
if (SectorBuffer[Offset] == 0xe5)
{
Offset+=32;
}
else
{
GetFileInfo(DrvInfo, SectorBuffer, Offset, &FileInfo);
strcpy(CheckInfo, (char *)FileInfo.LFName);
if (strcmpi(CheckInfo, DirInfo) == 0)
{
FileLocation->InCluster = 1;
FileLocation->StartCluster = FileInfo.StartCluster;
FileLocation->NthSector = NthRootDirSector;
FileLocation->NthEntry = Offset/32 + 1;
FileLocation->EntriesTakenUp = FileInfo.EntriesTakenUp;
FileLocation->Size = FileInfo.Size;
FileLocation->Attribute = FileInfo.Attribute;
Found = 1;
break;
}
Offset = Offset + FileInfo.EntriesTakenUp * 32;
}
if (Offset > 511)
{
break;
}
}
}
if (SectorBuffer[Offset] == 0 || Found)
{
break;
}
//
// do not set it to 0 directly
//
Offset = Offset - 512;
NthRootDirSector++;
}
if (!Found)
{
FileLocation->Found = 0;
return;
}
}
else
{
NextCluster = FileLocation->StartCluster;
Offset = 0;
Found = 0;
while (NextCluster < (GetFATEOF(DrvInfo) - 7))
{
for (i = 0; i < DrvInfo->SectorsPerCluster; i++)
{
SectorToRead = (UINT32) ((UINT32)DrvInfo->TotalSystemSectors + (NextCluster - 2) * (UINT32)DrvInfo->SectorsPerCluster + i);
ReadSector(DrvInfo->Drive, SectorToRead, 2, SectorBuffer);
if (i == DrvInfo->SectorsPerCluster-1)
{
if (FindNextCluster(DrvInfo, NextCluster) < (GetFATEOF(DrvInfo) - 7))
{ // Note the +512 carefully
SectorToRead = (UINT32) ((UINT32)DrvInfo->TotalSystemSectors +
(FindNextCluster(DrvInfo, NextCluster) - 2) *
(UINT32)DrvInfo->SectorsPerCluster);
ReadSector(DrvInfo->Drive, SectorToRead, 1, SectorBuffer+512);
}
}
while (1)
{
if (Offset > 511 || SectorBuffer[Offset] == 0)
{
break;
}
if (SectorBuffer[Offset] == 0xe5)
{
Offset+=32;
continue;
}
GetFileInfo(DrvInfo, SectorBuffer, Offset, &FileInfo);
//
// Refer to GetFileInfo if confused
//
strcpy(CheckInfo, FileInfo.LFName);
if (strcmpi(CheckInfo, DirInfo) == 0)
{
FileLocation->InCluster = NextCluster;
FileLocation->StartCluster = FileInfo.StartCluster;
FileLocation->NthSector = (UINT32) i+1;
FileLocation->NthEntry = Offset/32 + 1;
FileLocation->EntriesTakenUp = FileInfo.EntriesTakenUp;
FileLocation->Size = FileInfo.Size;
FileLocation->Attribute = FileInfo.Attribute;
Found = 1;
break;
}
Offset = Offset + FileInfo.EntriesTakenUp * 32;
}
if (Found)
{
break;
}
Offset = Offset - 512; // Should not simply set it to 0
}
if (Found)
{
break;
}
NextCluster = FindNextCluster(DrvInfo, NextCluster);
}
}
}
FileLocation->Found = Found;
}
void GetFileInfo(BPBINFO *DrvInfo, BYTE *DirBuffer, UINT16 Offset, FILEINFO *FileInfo)
{
// GetFileInfo gets the file information from DirBuffer at Offset and
// It supports long file names. Also it stores the number of
// entries that are occupied by this file name in FileInfo->EntriesTakenUp
// !#! If the entry is not a long file name a proper file name is stored
// in LFName with a dot in between primary and ext names to help routines
// which compare file names
UINT16 ti,tj;
UINT16 TimeDateWord;
BYTE StrCompare[7];
UINT32 Temp;
FileInfo->LFName[0] = '\0';
FileInfo->LFNOrphaned = 0;
FileInfo->TrashedEntry = 0;
// Get file attribute
FileInfo->Attribute = DirBuffer[Offset+11];
if ((FileInfo->Attribute & 0x0f) == 0x0f)
{
if (DirBuffer[Offset] >= 'A' && DirBuffer[Offset] <= 'T')
{ // Count of minimum and maximum entries to be an LFN
FileInfo->EntriesTakenUp = (DirBuffer[Offset] & 0x3f) + 1;
// Get the real attribute if it is a long file name. EntriesTakenUp > 1 is a long file name
FileInfo->Attribute = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+11];
}
else
{
FileInfo->TrashedEntry = 1;
FileInfo->LFNOrphaned = 1; // Could be
return;
}
}
else
{
FileInfo->EntriesTakenUp = 1;
}
// Get Primary name
for (ti = 0; ti < 8; ti++)
{
FileInfo->DOSName[ti] = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+ti];
}
// Get extension
FileInfo->DOSExt[0] = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+8];
FileInfo->DOSExt[1] = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+9];
FileInfo->DOSExt[2] = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+10];
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+0x1c];
Rx.h.vh = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+0x1d];
Rx.h.xvl = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+0x1e];
Rx.h.xvh = DirBuffer[(FileInfo->EntriesTakenUp-1)*32+Offset+0x1f];
FileInfo->Size = Rx.e.evx;
switch (DrvInfo->FATType)
{
case 12:
case 16:
// Starting Cluster
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x1a]; Rx.h.vh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x1b];
FileInfo->StartCluster = (UINT32) Rx.x.vx;
// Get File time
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x16]; Rx.h.vh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x17];
TimeDateWord = Rx.x.vx;
FileInfo->Second = (BYTE) (TimeDateWord & 0x001f);
FileInfo->Minute = (BYTE) ((TimeDateWord & 0x07e0) >> 5);
FileInfo->Hour = (BYTE) ((TimeDateWord & 0xf800) >> 11);
// Get File date
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x18];
Rx.h.vh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x19];
TimeDateWord = Rx.x.vx;
FileInfo->Day = (BYTE) (TimeDateWord & 0x001f);
FileInfo->Month = (BYTE) ((TimeDateWord & 0x01e0) >> 5);
FileInfo->Year = ((TimeDateWord & 0xfe00) >> 9) + 1980;
break;
case 32:
// Starting Cluster
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x1a];
Rx.h.vh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x1b];
Rx.h.xvl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x14];
Rx.h.xvh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x15];
FileInfo->StartCluster = Rx.e.evx;
// Get File time
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x16];
Rx.h.vh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x17];
TimeDateWord = Rx.x.vx;
FileInfo->Second = (BYTE) (TimeDateWord & 0x001f);
FileInfo->Minute = (BYTE) ((TimeDateWord & 0x07e0) >> 5);
FileInfo->Hour = (BYTE) ((TimeDateWord & 0xf800) >> 11);
// Get File date
Rx.e.evx = 0;
Rx.h.vl = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x18];
Rx.h.vh = DirBuffer[Offset+(FileInfo->EntriesTakenUp-1)*32+0x19];
TimeDateWord = Rx.x.vx;
FileInfo->Day = (BYTE) (TimeDateWord & 0x001f);
FileInfo->Month = (BYTE) ((TimeDateWord & 0x01e0) >> 5);
FileInfo->Year = ((TimeDateWord & 0xfe00) >> 9) + 1980;
break;
default:
break;
}
if (FileInfo->EntriesTakenUp < 2) // copy DOSName and DOSExt as proper file name to LFName
{
ti = 0; tj = 0;
while(1)
{
if (FileInfo->DOSName[ti] == ' ' || ti == 8)
{
break;
}
FileInfo->LFName[tj] = FileInfo->DOSName[ti];
tj++; ti++;
}
if (ti != 0 && FileInfo->DOSExt[0] != ' ') // Avoid empty names and . in case of no extension
{
FileInfo->LFName[tj] = '.';
ti = 0; tj++;
while (1)
{
if (FileInfo->DOSExt[ti] == ' ' || ti == 3)
{
break;
}
FileInfo->LFName[tj] = FileInfo->DOSExt[ti];
tj++; ti++;
}
}
FileInfo->LFName[tj] = 0; // Terminate with NULL Character
}
else
{ // Fetch the Long file name
ti = 0; tj = FileInfo->EntriesTakenUp - 1;
while( tj > 0)
{
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 1]; ti++; FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 3]; ti++;
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 5]; ti++; FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 7]; ti++;
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 9]; ti++; FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 14]; ti++;
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 16]; ti++; FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 18]; ti++;
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 20]; ti++; FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 22]; ti++;
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 24]; ti++; FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 28]; ti++;
FileInfo->LFName[ti] = DirBuffer[Offset + (tj-1)*32 + 30]; ti++;
tj--;
}
FileInfo->LFName[ti] = 0;
}
// Check for orphaned LFN
if (FileInfo->EntriesTakenUp > 1)
{ // it is a long file name
// Get the strings from the LFN without space for comparison purpose
// Example - if LFN is "Very Long name" the corresponding
// DOS file name would be "VERYLO~?". We extract VeryLo and check
// against VERYLO.
ti = 0; tj = 0;
while (ti < 8 && FileInfo->LFName[tj] != 0 && FileInfo->LFName[tj] != '.')
{
if (FileInfo->LFName[tj] != 32 && FileInfo->LFName[tj] != '.')
{
StrCompare[ti] = FileInfo->LFName[tj];
ti++;
}
tj++;
}
StrCompare[tj] = 0;
// But when there are to many file starting with name "Very Long name"
// the dos file name need not be "VERYLO~?" in all the cases. It could
// be "VERYL~??" or "VERY~???" and so on...
tj = 0;
while (FileInfo->DOSName[tj] != '~' && FileInfo->DOSName[tj] != ' ' && tj < 8)
{
tj++;
}
// ******* This if condition is not efficient
// it is only modified to avoid false LFN errors.
// replace the one with tj after getting more info about CRC values
// *******
if (strnicmp(StrCompare, FileInfo->DOSName, 1) != 0)
{
FileInfo->LFNOrphaned = 1;
}
}
// Check if this file is a trashed entry
if (DrvInfo->BigTotalSectors)
{
Temp = DrvInfo->BigTotalSectors;
}
else
{
Temp = DrvInfo->TotalSectors;
}
if (FileInfo->Year < 1981 || FileInfo->Day > 31 || FileInfo->Month < 1 ||
FileInfo->Month > 12 || FileInfo->Second > 30 ||
FileInfo->Minute > 60 || FileInfo->Hour > 23 ||
FileInfo->StartCluster > DrvInfo->TotalClusters ||
FileInfo->Size/512 > Temp)
{
FileInfo->TrashedEntry = 1;
}
}
BYTE GetAllInfoOfFile(BPBINFO *pDrvInfo, BYTE *FileName, FILELOC *pFileLoc, FILEINFO *pFileInfo)
{
UINT16 Offset;
UINT32 SectorToRead;
BYTE Sector[1024];
FindFileLocation(pDrvInfo, FileName, pFileLoc);
if (!pFileLoc->Found)
{ // need not proceed to find FileInfo
return 0;
}
Offset = (pFileLoc->NthEntry-1) * 32;
if (pFileLoc->InCluster == 1)
{ // file in root directory
ReadRootDirSector(pDrvInfo, Sector, pFileLoc->NthSector);
}
else
{
SectorToRead = (UINT32) ((UINT32)pDrvInfo->TotalSystemSectors + (pFileLoc->InCluster - 2) * (UINT32)pDrvInfo->SectorsPerCluster + pFileLoc->NthSector-1);
ReadSector(pDrvInfo->Drive, SectorToRead, 1, Sector);
}
GetFileInfo(pDrvInfo, Sector, Offset, pFileInfo);
return 1;
}
UINT16 SetFileInfo(BPBINFO *pDrvInfo, FILELOC *pFileLoc, FILEINFO *pFileInfo)
{
// This function loads the entry of a file from its directory sector
// specified in FileLocation and updates it with the new File Info in FILEINFO
// At present this function only changes StartCluster, Size
UINT32 SectorToRead, EmergencySectorToRead;
UINT16 Offset, Temp;
BYTE SectorBuffer[1024];
Offset = (pFileLoc->NthEntry-1)*32;
if (pFileLoc->InCluster == 1)
{ // the file is in root directory. Refer to FindFileLocation function for more info
if (ReadRootDirSector(pDrvInfo, SectorBuffer, pFileLoc->NthSector) != 1)
{ // The return value of the above function is either 0 or 1 or 2. We only want return value 1
return 0;
}
}
else
{
SectorToRead = (UINT32) ((UINT32)pDrvInfo->TotalSystemSectors + (pFileLoc->InCluster - 2) * (UINT32)pDrvInfo->SectorsPerCluster + pFileLoc->NthSector-1);
EmergencySectorToRead = SectorToRead + 1;
if (!ReadSector(pDrvInfo->Drive, SectorToRead, 2, SectorBuffer))
{
return 0;
}
if (pFileLoc->NthSector == pDrvInfo->SectorsPerCluster)
{
if (FindNextCluster(pDrvInfo, pFileLoc->InCluster) < (GetFATEOF(pDrvInfo) - 7)) // EOF can be FFF8 to FFFF
{ // Note the +512 carefully
EmergencySectorToRead = (UINT32) ((UINT32)pDrvInfo->TotalSystemSectors + (FindNextCluster(pDrvInfo, pFileLoc->InCluster) - 2) * (UINT32)pDrvInfo->SectorsPerCluster);
if (!ReadSector(pDrvInfo->Drive, EmergencySectorToRead, 1, SectorBuffer+512))
{
return 0;
}
}
}
}
//
// We have the file entry in SectorBuffer
// Now update the file info located in the SHORTNAME area
//
Temp = (pFileInfo->EntriesTakenUp-1)*32;
//
// Change Cluster value
//
Rx.e.evx = 0;
Rx.e.evx = (UINT32) pFileInfo->StartCluster;
if (pDrvInfo->FATType == 32)
{
SectorBuffer[Offset+Temp+0x1a] = Rx.h.vl;
SectorBuffer[Offset+Temp+0x1b] = Rx.h.vh;
SectorBuffer[Offset+Temp+0x14] = Rx.h.xvl;
SectorBuffer[Offset+Temp+0x15] = Rx.h.xvh;
}
else
{
SectorBuffer[Offset+Temp+0x1a] = Rx.h.vl;
SectorBuffer[Offset+Temp+0x1b] = Rx.h.vh;
}
// Change Size
Rx.e.evx = pFileInfo->Size;
SectorBuffer[Offset+Temp+0x1c] = Rx.h.vl; SectorBuffer[Offset+Temp+0x1d] = Rx.h.vh;
SectorBuffer[Offset+Temp+0x1e] = Rx.h.xvl; SectorBuffer[Offset+Temp+0x1f] = Rx.h.xvh;
if (pFileLoc->InCluster == 1)
{
WriteRootDirSector(pDrvInfo, SectorBuffer, pFileLoc->NthSector);
if (Offset + pFileLoc->EntriesTakenUp * 32 > 512) // Did it cross sector boundary
{
WriteRootDirSector(pDrvInfo, SectorBuffer+512, pFileLoc->NthSector+1);
}
}
else
{
WriteSector(pDrvInfo->Drive, SectorToRead, 1, SectorBuffer);
if (Offset + pFileLoc->EntriesTakenUp * 32 > 512) // Did it cross sector boundary
{
WriteSector(pDrvInfo->Drive, EmergencySectorToRead, 1, SectorBuffer+512);
}
}
// File entry updated
return 1;
}