375 lines
9.2 KiB
C++
375 lines
9.2 KiB
C++
|
//-----------------------------------------------------------------------
|
||
|
//
|
||
|
// File: chkdsk.cxx
|
||
|
//
|
||
|
// Contents: Sanity checking and recovery mechanism for multistream files
|
||
|
//
|
||
|
// Argument:
|
||
|
//
|
||
|
// History: 9-July-92 t-chrisy Created.
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#include "chkdsk.hxx"
|
||
|
|
||
|
// Global variables, declared as so for convenience.
|
||
|
CMSFHeader *pheader;
|
||
|
CFat *pFat;
|
||
|
CFat *pMiniFat;
|
||
|
CDirectory *pDir;
|
||
|
CDIFat *pDIFat;
|
||
|
BOOL fixdf;
|
||
|
CFatVector *pfvFat, *pfvMiniFat;
|
||
|
wchar_t pwcsDocfile[_MAX_PATH];
|
||
|
DFLAGS df = DF_READWRITE | DF_DENYWRITE;
|
||
|
|
||
|
extern SCODE DllMultiStreamFromCorruptedStream(CMStream MSTREAM_NEAR **ppms,
|
||
|
ILockBytes **pplstStream,
|
||
|
DWORD dwFlags);
|
||
|
|
||
|
// Function Prototypes
|
||
|
void BuildFatTables();
|
||
|
void MarkFatTables();
|
||
|
void TreeWalk(CDirEntry *pde, SID sid);
|
||
|
BOOL GetOption(int argc, char *argv[]);
|
||
|
void Usage(char *pszProgName);
|
||
|
void DIFTable();
|
||
|
|
||
|
void main(int argc, char *argv[])
|
||
|
{
|
||
|
CFileStream *pfilestr;
|
||
|
CMStream MSTREAM_NEAR *pms;
|
||
|
SCODE scRc;
|
||
|
ILockBytes *pilb;
|
||
|
|
||
|
// if fixdf returns yes, open docfile without a copy;
|
||
|
// otherwise, open docfile with a copy and operate on the copy.
|
||
|
fixdf = GetOption(argc,argv);
|
||
|
pfilestr = new CFileStream;
|
||
|
|
||
|
// creating ILockBytes implementation for the given file
|
||
|
// Note: When a docfile is corrupted, the chkdsk utility
|
||
|
// calls the original CFileStream::Init. If any objects
|
||
|
// fail to instantiate, the approach is to call an
|
||
|
// alternative Init routine, which can force the instantiation
|
||
|
// of Directory and MiniFat objects.
|
||
|
|
||
|
if (fixdf==TRUE) // -f specified, write allowed.
|
||
|
{
|
||
|
df &= ~0x03;
|
||
|
df |= DF_WRITE;
|
||
|
printf("Trying to open file...\n");
|
||
|
scRc = pfilestr->Init(pwcsDocfile,RSF_OPEN,df);
|
||
|
if (FAILED(scRc))
|
||
|
{
|
||
|
printf("Error creating ILockBytes.\n");
|
||
|
exit(FAIL_CREATE_ILB);
|
||
|
}
|
||
|
}
|
||
|
else // open a read-only copy of filestream
|
||
|
{
|
||
|
df &= ~0x300; // clear access bits
|
||
|
df |= DF_DENYWRITE;
|
||
|
printf("Trying to open file...\n");
|
||
|
scRc = pfilestr->Init(pwcsDocfile,RSF_OPEN,df);
|
||
|
if (FAILED(scRc))
|
||
|
{
|
||
|
printf("Error creating ILockBytes.\n");
|
||
|
exit(FAIL_CREATE_ILB);
|
||
|
}
|
||
|
else printf("Successfully created ILockBytes.\n");
|
||
|
}
|
||
|
|
||
|
scRc = pfilestr->Validate();
|
||
|
if (scRc == STG_E_INVALIDHANDLE)
|
||
|
{
|
||
|
printf("Filestream signature is not valid.\n");
|
||
|
exit(INVALID_DOCFILE);
|
||
|
}
|
||
|
|
||
|
// CFileStream is essentially equivalent to ILockBytes.
|
||
|
pilb = (ILockBytes *) pfilestr;
|
||
|
scRc = DllMultiStreamFromStream(&pms,&pilb,0);
|
||
|
|
||
|
if (FAILED(scRc))
|
||
|
if (FAILED(scRc = DllMultiStreamFromCorruptedStream
|
||
|
(&pms,&pilb,0)))
|
||
|
{
|
||
|
exit(FAIL_CREATE_MULTISTREAM);
|
||
|
printf("Error creating a multistream.\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
// When an multi-stream is instantiated, the following control structures
|
||
|
// are automatically instantiated.
|
||
|
pheader = pms->GetHeader();
|
||
|
pDir = pms->GetDir();
|
||
|
pFat = pms->GetFat();
|
||
|
pMiniFat = pms->GetMiniFat();
|
||
|
pDIFat = pms->GetDIFat();
|
||
|
|
||
|
printf("\tBuilding fat tables...\n");
|
||
|
BuildFatTables();
|
||
|
printf("\tExamining the DIFat...\n");
|
||
|
DIFTable();
|
||
|
printf("\tExamining Fat and MiniFat chains...\n");
|
||
|
MarkFatTables();
|
||
|
printf("\tChecking completed.\n");
|
||
|
delete(pfvFat);
|
||
|
delete(pfvMiniFat);
|
||
|
pfilestr->Release();
|
||
|
printf("Memory blocks freed.\n");
|
||
|
}
|
||
|
|
||
|
void BuildFatTables()
|
||
|
{
|
||
|
// Build two tables: one for Fat sectors, the other for Minifat sectors.
|
||
|
|
||
|
FSINDEX FatLen,MiniFatLen;
|
||
|
FatLen = pheader->GetFatLength();
|
||
|
MiniFatLen = pheader->GetMiniFatLength();
|
||
|
pfvFat = new CFatVector(TABLE_SIZE);
|
||
|
pfvFat->Init(FatLen);
|
||
|
if (MiniFatLen == 0)
|
||
|
printf("No MiniFat to be checked.\n");
|
||
|
else
|
||
|
{
|
||
|
pfvMiniFat = new CFatVector(TABLE_SIZE);
|
||
|
pfvMiniFat->Init(MiniFatLen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MarkFatTables()
|
||
|
{
|
||
|
CDirEntry *pde;
|
||
|
|
||
|
// Walk through all the fat chains and mark the new table with the
|
||
|
// first SID number encountered.
|
||
|
|
||
|
pDir->SidToEntry(SIDROOT,&pde);
|
||
|
TreeWalk(pde,SIDROOT); // pde points to the root entry now
|
||
|
}
|
||
|
|
||
|
void TreeWalk(CDirEntry *pde, SID sid)
|
||
|
{
|
||
|
CDirEntry *pchild, *pnext;
|
||
|
SID childsid, nextsid;
|
||
|
SCODE scRc,scRcM;
|
||
|
FSINDEX fitable,fioffset;
|
||
|
SECT sectentry, sect;
|
||
|
CFatSect *pfsec;
|
||
|
CFatVector *pfv;
|
||
|
CFat *pf;
|
||
|
ULONG uldesize;
|
||
|
|
||
|
pDir->GetStart(sid,§);
|
||
|
uldesize = pde->GetSize();
|
||
|
|
||
|
if (uldesize >= MINISTREAMSIZE) // storage is in FAT
|
||
|
{
|
||
|
pfv = pfvFat;
|
||
|
pf = pFat;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pfv = pfvMiniFat;
|
||
|
pf = pMiniFat;
|
||
|
}
|
||
|
|
||
|
// Check if LUID exceeds MaxLUID. If so, report the error.
|
||
|
if (pde->GetLuid() > pheader->GetLuid())
|
||
|
printf("LUID for dir entry #%lu exceeds MAXLuid.\n",sid);
|
||
|
|
||
|
while (sect < MAXREGSECT)
|
||
|
{
|
||
|
if (sid == SIDROOT)
|
||
|
break; // nothing should be in root stream
|
||
|
// Use fitable and fioffset to index into the fat (or minifat)
|
||
|
// table and mark the field with visited.
|
||
|
// at the same time, check for loops or crosslinks.
|
||
|
|
||
|
//Note: 3 cases
|
||
|
fitable = sect / (TABLE_SIZE);
|
||
|
fioffset = sect % (TABLE_SIZE);
|
||
|
pfv->GetTable(fitable,&pfsec); // pfsec = ptr to CFatSect
|
||
|
sectentry = pfsec->GetSect(fioffset);
|
||
|
|
||
|
// printf("\tsect = %lu \t \t sectentry = %lu \t stream_size = %lu\n",
|
||
|
// sect,sectentry, uldesize);
|
||
|
// Mark the FatTables as well as fixing the multistream.
|
||
|
// Right now, the routine only marks the FatTables.
|
||
|
//Note: 3 cases...but the last two cases may not
|
||
|
// be handled the same.
|
||
|
if (sectentry > MAXREGSECT)
|
||
|
pfsec->SetSect(fioffset,sid);
|
||
|
else if (sectentry == sid)
|
||
|
{
|
||
|
// discontinue the current stream chain by marking
|
||
|
// current SECT as ENDOFCHAIN.
|
||
|
pf->SetNext(sect,ENDOFCHAIN);
|
||
|
pfsec->SetSect(fioffset,ENDOFCHAIN);
|
||
|
printf("Loop detected at fat SECT %ul\n",sectentry);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pf->SetNext(sect,ENDOFCHAIN);
|
||
|
pfsec->SetSect(fioffset,ENDOFCHAIN);
|
||
|
printf("Crosslink detected at Fat SECT %lu with stream #%lu\n",
|
||
|
sect,sid);
|
||
|
}
|
||
|
// get the next sector to be examined
|
||
|
// !!!!! Need to use the Fat object to track down next sector
|
||
|
pf->GetNext(sect,§);
|
||
|
}
|
||
|
|
||
|
// Recursively go down the tree
|
||
|
// pchild and pnext must point to the original tree for
|
||
|
// efficiency purposes.
|
||
|
|
||
|
childsid = pde->GetChild();
|
||
|
if (childsid != NOSTREAM)
|
||
|
{
|
||
|
pDir->SidToEntry(childsid,&pchild);
|
||
|
TreeWalk(pchild,childsid);
|
||
|
}
|
||
|
nextsid = pde->GetNext();
|
||
|
if (nextsid != NOSTREAM)
|
||
|
{
|
||
|
pDir->SidToEntry(nextsid,&pnext);
|
||
|
TreeWalk(pnext,nextsid);
|
||
|
}
|
||
|
|
||
|
if (fixdf==TRUE)
|
||
|
{
|
||
|
scRc = pFat->Flush();
|
||
|
scRcM = pMiniFat->Flush();
|
||
|
if (FAILED(scRc) || FAILED(scRcM))
|
||
|
printf("Failed to write all modified FatSects out to stream.\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL GetOption(int argc, char *argv[])
|
||
|
{
|
||
|
char *pszArg, *pszProgName;
|
||
|
BOOL ArgsOK = FALSE, Fix = FALSE;
|
||
|
pszProgName = *argv++;
|
||
|
|
||
|
while ((pszArg = *argv++) != NULL)
|
||
|
{
|
||
|
if (*pszArg == '-' || *pszArg == '/')
|
||
|
{
|
||
|
switch (tolower(*(++pszArg)))
|
||
|
{
|
||
|
case 'f': // fix the errors.
|
||
|
Fix = TRUE; // open file with read-only without a copy.
|
||
|
break;
|
||
|
case 'n': // name of the docfile to be opened.
|
||
|
// path of the filename.
|
||
|
mbstowcs(pwcsDocfile,++pszArg,_MAX_PATH);
|
||
|
Fix = FALSE;
|
||
|
ArgsOK = TRUE;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else ArgsOK = FALSE;
|
||
|
}
|
||
|
if (ArgsOK == FALSE)
|
||
|
{
|
||
|
printf("0 argument or invalid command line argument.\n");
|
||
|
Usage(pszProgName);
|
||
|
exit(INVALID_ARG);
|
||
|
}
|
||
|
return Fix;
|
||
|
}
|
||
|
|
||
|
void Usage(char *pszProgName)
|
||
|
{
|
||
|
printf("Usage: %s\n", pszProgName);
|
||
|
printf(" -f fix requested by user.\n");
|
||
|
printf(" -n <name of docfile>\n");
|
||
|
printf("The -n option must be specified.\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
void DIFTable() // August 11, 1992
|
||
|
{
|
||
|
// Walk through each DIF sector array to detect loops and
|
||
|
// crosslinks.
|
||
|
SCODE scRc;
|
||
|
BOOL FatOK = TRUE;
|
||
|
SECT sect, sectentry;
|
||
|
FSINDEX diflen, fatlen, fitable, fioffset, index, minifatlen,uldif,ulr;
|
||
|
CFatSect *pfsec;
|
||
|
|
||
|
diflen = pheader->GetDifLength();
|
||
|
fatlen = pheader->GetFatLength();
|
||
|
minifatlen = pheader->GetMiniFatLength();
|
||
|
|
||
|
// testing the validity of pheader->GetDifLength
|
||
|
if (fatlen > CSECTFAT) // diflen > 0
|
||
|
{
|
||
|
ulr = ( ((fatlen - CSECTFAT)%TABLE_SIZE) > 0 )? 1: 0;
|
||
|
uldif = CSECTFAT + (fatlen-CSECTFAT)/TABLE_SIZE + ulr;
|
||
|
}
|
||
|
else uldif = 0;
|
||
|
if (diflen!=uldif)
|
||
|
printf("DIFLEN in header is inconsistent with FatLEN.\n");
|
||
|
|
||
|
for (index=0; index<fatlen; index++)
|
||
|
{
|
||
|
pDIFat->GetFatSect(index,§);
|
||
|
if (sect < MAXREGSECT)
|
||
|
{
|
||
|
fitable = sect / TABLE_SIZE;
|
||
|
fioffset = sect % TABLE_SIZE;
|
||
|
pfvFat->GetTable(fitable,&pfsec); // pfsec = ptr to CFatSect
|
||
|
sectentry = pfsec->GetSect(fioffset);
|
||
|
|
||
|
if (sectentry > MAXREGSECT)
|
||
|
pfsec->SetSect(fioffset,SIDFAT);
|
||
|
else
|
||
|
{
|
||
|
printf("Crosslink! DIF index #%u points\n",index);
|
||
|
printf(" to the same location %u.\n", sect);
|
||
|
FatOK = FALSE;
|
||
|
}
|
||
|
}
|
||
|
pDIFat->GetFatSect(index+1,§);
|
||
|
}
|
||
|
|
||
|
if (FatOK == TRUE)
|
||
|
printf("No errors found in DIFat.\n");
|
||
|
|
||
|
// Walk through the terminating cells in each sector array to check
|
||
|
// the correctness of chaining.
|
||
|
printf("\tWalking through DIFTable chain.\n");
|
||
|
for (index = 0; index<diflen; index++)
|
||
|
{
|
||
|
pDIFat->GetSect(index,§);
|
||
|
fitable = sect/TABLE_SIZE;
|
||
|
fioffset = sect%TABLE_SIZE;
|
||
|
pfvFat->GetTable(fitable,&pfsec); // pfsec = ptr to CFatSect
|
||
|
sectentry = pfsec->GetSect(fioffset);
|
||
|
if ((sectentry!=ENDOFCHAIN) && (index == diflen-1))
|
||
|
printf("ERROR! ENDOFCHAIN expected at the end of DIFat.\n.");
|
||
|
pDIFat->SetFatSect(fioffset,SIDDIF);
|
||
|
pfsec->SetSect(fioffset,SIDDIF);
|
||
|
}
|
||
|
if (fixdf==TRUE)
|
||
|
{
|
||
|
scRc = pDIFat->FlushAll();
|
||
|
if (FAILED(scRc))
|
||
|
printf("Failed to write all modified FatSects out to stream.\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|