You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2397 lines
60 KiB
2397 lines
60 KiB
/*++
|
|
|
|
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;
|
|
}
|