windows-nt/Source/XPSP1/NT/inetsrv/query/apps/propdump/main.cxx
2020-09-26 16:20:57 +08:00

1244 lines
44 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2000.
//
// File: propdump.cxx
//
// Contents: Property file dump utility
//
// History: 29 Oct 1996 KyleP Created
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <proprec.hxx>
#include <propdesc.hxx>
DECLARE_INFOLEVEL(ci)
unsigned const SixtyFourK = 1024 * 64;
unsigned const cMaxFields = 10;
unsigned fVerbose = 0;
enum eRecType
{
Virgin,
Top,
Overflow,
Free
};
struct SmallInfo
{
ULONG Type;
ULONG Length;
ULONG ulPrev;
ULONG ulNext;
ULONG ulChainLength;
};
void Usage()
{
printf("Usage: PropDump [-?] [-v] [-m] [-f primary_field] [-s secondary_field] <primary file> [<secondary file>]\n");
printf(" -? to dump this usage information.\n");
printf(" -v to be verbose.\n");
printf(" -m[o] to dump the metadata (o --> only. No integrity check).\n");
printf(" -f field is used to dump a specific field in the primary store records.\n");
printf(" -s field is used to dump a specific field in the secondary store records.\n");
printf(" -c secWidPtrField is used to check for consistency between pri and sec stores. Param is usually 3.\n");
printf(" -w[ps] <workid> dump only <workid> in primary/secondary store\n");
printf(" <primary file> is the name of the primary file.\n");
printf(" <secondary file> is the name of secondary file.\n");
printf(" Specify primary and secondary for the two-level prop store.\n");
printf(" If secondary is not specified, the primary will be construed as the name of the single store.\n");
}
char * pszPrimFile = 0;
char * pszSecFile = 0;
BOOL fReadMetadata = 0;
BOOL fMetadataOnly = 0;
unsigned cField1 = 0;
unsigned cField2 = 0;
int aiField1[cMaxFields];
int aiField2[cMaxFields];
BOOL fTwoLevel = FALSE;
ULONG cPriRecPerBuf, cbPriRec, cPriTotal, cPriFixed;
ULONG cSecRecPerBuf, cbSecRec, cSecTotal, cSecFixed;
ULONG cMaxWidsInSecStore;
int cWidPtrField = 0;
ULONG widPrimary = 0xFFFFFFFF;
ULONG widSecondary = 0xFFFFFFFF;
const DWORD PrimaryStore = 0;
const DWORD SecondaryStore = 1;
void DumpStore(char *pszFile, unsigned cField, int *aiField, DWORD dwLevel, ULONG wid);
void CheckConsistencyBetweenStores(char * pszPrimFile, char *pszSecFile);
CPropDesc aFieldRec[cMaxFields];
CPropDesc SecWidPtrFieldRec;
int __cdecl main( int argc, char * argv[] )
{
if (argc < 2)
{
Usage();
return 0;
}
for ( int i = 1; i < argc; i++ )
{
if ( argv[i][0] == '-' )
{
switch ( argv[i][1] )
{
case '?':
Usage();
return 0;
case 'v':
case 'V':
fVerbose = 1;
break;
case 'm':
case 'M':
fReadMetadata = TRUE;
if ( argv[i][2] == 'o' || argv[i][2] == 'O' )
fMetadataOnly = TRUE;
break;
case 'f':
case 'F':
i++;
if ( cField1 < cMaxFields )
aiField1[cField1++] = strtol( argv[i], 0, 10 );
break;
case 's':
case 'S':
i++;
if ( cField2 < cMaxFields )
aiField2[cField2++] = strtol( argv[i], 0, 10 );
break;
case 'c':
case 'C':
i++;
cWidPtrField = strtol(argv[i], 0, 10);
// this works only if fReadMetadata is turned on
fReadMetadata = TRUE;
break;
case 'w':
case 'W':
switch ( argv[i][2] )
{
case 'p':
case 'P':
case '\0':
i++;
widPrimary = strtol(argv[i], 0, 10);
break;
case 's':
case 'S':
i++;
widSecondary = strtol(argv[i], 0, 10);
break;
}
}
}
else
{
if (!pszPrimFile)
pszPrimFile = argv[i];
else
{
pszSecFile = argv[i];
fTwoLevel = TRUE;
}
}
}
// If we don't have a primary file or a secondary file, look for them in the current directory.
if (!pszPrimFile)
{
fTwoLevel = TRUE;
pszPrimFile = "00000002.ps1";
pszSecFile = "00000002.ps2";
}
// Verify that the propstore files are indeed available!
if (0xFFFFFFFF == GetFileAttributesA(pszPrimFile))
{
printf("Can't find primary file %s for reason %d\n", pszPrimFile, GetLastError());
return 0;
}
if (0xFFFFFFFF == GetFileAttributesA(pszSecFile))
{
printf("Can't find secondary file. Assuming single level property store\n");
fTwoLevel = FALSE;
}
else
fTwoLevel = TRUE;
cPriRecPerBuf = cbPriRec = cSecRecPerBuf = cbSecRec = 0;
//
// CheckConsistencyBetweenStores relies on some of the information gathered
// by calls to DumpStore, so they should be called before consistency check.
//
DumpStore(pszPrimFile, cField1, aiField1, PrimaryStore, widPrimary );
if (fTwoLevel)
{
DumpStore(pszSecFile, cField2, aiField2, SecondaryStore, widSecondary);
if (cWidPtrField > 0)
{
CheckConsistencyBetweenStores(pszPrimFile, pszSecFile);
}
}
return 0;
}
void DumpStore(char *pszFile, unsigned cField, int *aiField, DWORD dwLevel, ULONG wid )
{
BYTE abTemp[SixtyFourK];
//
// First, read out the metadata
//
unsigned cFixed = 0;
unsigned cTotal = 0;
unsigned culFixed = 0;
if ( fReadMetadata || cField != 0 )
{
//
// Build path.
//
char szPath[MAX_PATH];
strcpy( szPath, pszFile );
char * pLastSlash = strrchr( szPath, '\\' );
if (pLastSlash)
pLastSlash++;
else
pLastSlash = szPath;
if (fTwoLevel)
{
if (PrimaryStore == dwLevel)
strcpy( pLastSlash, "CIP10000.001" );
else
strcpy( pLastSlash, "CIP20000.001" );
}
else
strcpy( pLastSlash, "CIPS0000.001" );
HANDLE h = CreateFileA( szPath,
GENERIC_READ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
0,
0 );
if ( INVALID_HANDLE_VALUE == h )
{
printf( "Can't open file %s. Error %u\n", szPath, GetLastError() );
return;
}
ULONG cbRead;
if ( ReadFile( h,
abTemp,
sizeof(abTemp),
&cbRead,
0 ) )
{
//
// Loop through records
//
CPropDesc * pPropDesc = (CPropDesc *)abTemp;
if ( fReadMetadata )
printf( "Record\t%-25s Type\t\tOffset\tSize\tOrdinal\tMask\n",
" Pid" );
for ( unsigned i = 0;
i < cbRead/(sizeof(CPropDesc) + sizeof(ULONG));
i++, pPropDesc = (CPropDesc *)(((BYTE *)pPropDesc) + sizeof(CPropDesc) + sizeof(ULONG)) )
{
if ( pPropDesc->Pid() != 0 )
{
if ( fReadMetadata )
{
char * pszPidName = 0;
char * pszMarker = "";
switch (pPropDesc->Pid())
{
case pidSecurity:
pszPidName = "pidSecurity"; break;
case pidDirectory:
pszPidName = "pidDirectory"; break;
case pidClassId:
pszPidName = "pidClassId"; break;
case pidStorageType:
pszPidName = "pidStorageType"; break;
case pidFileIndex:
pszPidName = "pidFileIndex"; break;
case pidVolumeId:
pszPidName = "pidVolumeId"; break;
case pidParentWorkId:
pszPidName = "pidParentWorkId"; break;
case pidLastChangeUsn:
pszPidName = "pidLastChangeUsn"; break;
case pidName:
pszPidName = "pidName"; break;
case pidSize:
pszPidName = "pidSize"; break;
case pidAttrib:
pszPidName = "pidAttrib"; break;
case pidWriteTime:
pszPidName = "pidWriteTime"; break;
case pidCreateTime:
pszPidName = "pidCreateTime"; break;
case pidAccessTime:
pszPidName = "pidAccessTime"; break;
case pidShortName:
pszPidName = "pidShortName"; break;
case pidWorkId:
pszPidName = "pidWorkId"; break;
case pidUnfiltered:
pszPidName = "pidUnfiltered"; break;
case pidRevName:
pszPidName = "pidRevName"; break;
case pidVirtualPath:
pszPidName = "pidVirtualPath"; break;
case pidLastSeenTime:
pszPidName = "pidLastSeenTime"; break;
case pidPath:
pszPidName = "pidPath";
pszMarker = "**";
break;
case pidSecondaryStorage:
pszPidName = "pidSecondaryStorage";
pszMarker = "*";
break;
}
static char achBuf[20];
if ( 0 == pszPidName )
{
sprintf( achBuf, " %d(0x%x)",
pPropDesc->Pid(), pPropDesc->Pid() );
}
else
{
sprintf( achBuf, "%-3s%d(%s)",
pszMarker, pPropDesc->Pid(), pszPidName );
}
printf( "%u:\t%-25s %u (0x%x)\t%d\t%u\t%u\t0x%x\n",
pPropDesc->Record(),
achBuf,
pPropDesc->Type(), pPropDesc->Type(),
pPropDesc->Offset(),
pPropDesc->Size(),
pPropDesc->Ordinal(),
pPropDesc->Mask() );
}
cTotal++;
if ( pPropDesc->Offset() != -1 )
{
cFixed++;
culFixed += (pPropDesc->Size() / sizeof(DWORD));
}
// copy the description of the sec top-level wid ptr for later use
if ( PrimaryStore == dwLevel && cWidPtrField && cWidPtrField == (int)pPropDesc->Record() )
{
memcpy( &SecWidPtrFieldRec, pPropDesc, sizeof(aFieldRec[0]) );
}
for ( unsigned j = 0; j < cField; j++ )
{
if ( aiField[j] == (int)pPropDesc->Record() )
memcpy( &aFieldRec[j], pPropDesc, sizeof(aFieldRec[0]) );
if (cWidPtrField && cWidPtrField == (int)j )
{
// verify that we are capturing the right property
// description for the sec top-level wid ptr
for (int k = 0; k < sizeof(aFieldRec[0]); k++)
{
Win4Assert(RtlEqualMemory(&aFieldRec[j], &SecWidPtrFieldRec, sizeof(SecWidPtrFieldRec)));
}
}
}
}
}
printf( "\n%u Properties, %u Fixed totaling %u bytes (* = Secondary link, ** = Path)\n\n",
cTotal, cFixed, culFixed * sizeof(DWORD) );
}
else
{
printf( "Can't read file %s. Error %u\n", szPath, GetLastError() );
return;
}
CloseHandle(h);
if ( fMetadataOnly )
return;
}
HANDLE h = CreateFileA( pszFile,
GENERIC_READ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
0,
0 );
if ( INVALID_HANDLE_VALUE == h &&
ERROR_SHARING_VIOLATION == GetLastError() )
{
h = CreateFileA( pszFile,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0 );
}
if ( INVALID_HANDLE_VALUE == h )
{
printf( "Can't open file %s. Error %u\n", pszFile, GetLastError() );
return;
}
BY_HANDLE_FILE_INFORMATION fi;
if ( !GetFileInformationByHandle( h, &fi ) )
{
printf( "Error %u getting size from handle\n", GetLastError() );
return;
}
ULONG oFile = 0;
ULONG cbRec = 0;
ULONG cRecPerBuf;
ULONG cRecTotal;
ULONG cTotalSections = 0;
HANDLE hSmallInfo;
SmallInfo * aSmallInfo = 0;
ULONG iSection = 0;
BOOL fFirst = TRUE;
ULONG iCurrentPct = 0;
ULONG cTopLevel = 0;
ULONG cOverflow = 0;
ULONG cFree = 0;
ULONG cVirgin = 0;
for (;;)
{
ULONG cbRead;
if ( ReadFile( h,
abTemp,
sizeof(abTemp),
&cbRead,
0 ) )
{
if (fVerbose)
printf( "READ: 0x%x bytes, offset 0x%x\n", cbRead, oFile );
if ( 0 == cbRead )
break;
if ( fFirst )
{
//
// Determine record size
//
if ( abTemp[0] != 0 || abTemp[1] != 0 )
{
printf( "Record 0 not blank. File corrupt!\n" );
break;
}
// First record should be all empty and only the first
// record should be so. So counting all leading zeros gives us
// the size of the record.
for ( unsigned i = 0; i < cbRead && abTemp[i] == 0; i++ )
continue;
if ( i == cbRead )
{
printf( "First %uK segment all zero!. File corrupt!\n", sizeof(abTemp)/1024 );
break;
}
switch ( *(USHORT *)&abTemp[i] )
{
case 0x5555:
case 0xAAAA:
case 0xBBBB:
case 0xCCCC:
case 0xDDDD:
cbRec = i;
if ( cbRec % 4 != 0 )
{
printf( "Record size (%u bytes) not DWORD aligned!\n\n", cbRec );
return;
}
cRecPerBuf = sizeof(abTemp) / cbRec;
printf( "Record size: %u bytes (%u / %uK)\n", i, cRecPerBuf, sizeof(abTemp)/1024 );
cTotalSections = (fi.nFileSizeLow + sizeof(abTemp) - 1)/ sizeof abTemp;
hSmallInfo = LocalAlloc( LMEM_MOVEABLE,
((fi.nFileSizeLow / sizeof(abTemp)) + 1) * cRecPerBuf * sizeof(SmallInfo) );
aSmallInfo = (SmallInfo *)LocalLock( hSmallInfo );
break;
default:
printf( "First non-zero byte is not a proper signature (%u)!\n", *(SHORT *)&abTemp[i] );
return;
}
if (PrimaryStore == dwLevel)
{
cPriRecPerBuf = cRecPerBuf;
cbPriRec = cbRec;
cPriTotal = cTotal;
cPriFixed = cFixed;
}
else
{
cSecRecPerBuf = cRecPerBuf;
cbSecRec = cbRec;
cSecTotal = cTotal;
cSecFixed = cFixed;
}
fFirst = FALSE;
}
ULONG iRec = 0;
while ( iRec < cRecPerBuf )
{
COnDiskPropertyRecord * pRec = new( iRec, abTemp, cbRec/4 ) COnDiskPropertyRecord;
if ( !pRec->IsValidType() )
{
printf( "Record 0x%x (%u:%u) (file offset 0x%x) is corrupt!\n",
iSection * cRecPerBuf + iRec,
iSection, iRec,
iSection * sizeof(abTemp) + iRec * cbRec );
}
if (fVerbose )
printf( "%u:%u, %s, length = %u record(s)",
iSection, iRec,
pRec->IsNormalTopLevel() ? "Top Level Normal" :
pRec->IsOverflow() ? "Overflow" :
pRec->IsFreeRecord() ? "Free Normal" :
pRec->IsTopLevel() ? "Top Level Lean":
pRec->IsLeanFreeRecord() ? "Free Lean":
"Virgin",
pRec->CountRecords() );
aSmallInfo[iSection*cRecPerBuf + iRec].ulChainLength = 0;
BOOL fPrint = (0xFFFFFFFF == wid || iSection*cRecPerBuf + iRec == wid);
if ( pRec->IsOverflow() )
{
aSmallInfo[iSection*cRecPerBuf + iRec].Type = Overflow;
cOverflow++;
for ( unsigned j = 0; j < cField; j++ )
{
PROPVARIANT var;
BYTE abExtra[MAX_PATH * 5];
unsigned cbExtra = sizeof(abExtra);
if ( 0 == j && fPrint )
printf( "%6u: ", iSection*cRecPerBuf + iRec );
if ( aFieldRec[j].IsFixedSize() )
continue;
ULONG Ordinal = aFieldRec[j].Ordinal() - cFixed;
DWORD Mask = (1 << ((Ordinal % 16) * 2) );
BOOL fOk = pRec->ReadVariable( Ordinal,
Mask,
0,
cTotal - cFixed,
0,
var,
abExtra,
&cbExtra );
if ( fOk && fPrint )
{
switch ( var.vt )
{
case VT_LPSTR:
printf( "%s ", var.pszVal );
break;
case VT_LPWSTR:
printf( "%ws ", var.pwszVal );
break;
}
}
}
if ( 0 != cField && fPrint )
printf( "\n" );
}
else if ( pRec->IsNormalTopLevel() )
{
aSmallInfo[iSection*cRecPerBuf + iRec].Type = Top;
aSmallInfo[iSection*cRecPerBuf + iRec].ulChainLength = pRec->GetOverflowChainLength();
cTopLevel++;
for ( unsigned j = 0; j < cField; j++ )
{
PROPVARIANT var;
BYTE abExtra[MAX_PATH * 5];
unsigned cbExtra = sizeof(abExtra);
if ( 0 == j && fPrint )
printf( "%6u: ", iSection*cRecPerBuf + iRec );
if ( aFieldRec[j].IsFixedSize() )
{
pRec->ReadFixed( aFieldRec[j].Ordinal(),
aFieldRec[j].Mask(),
aFieldRec[j].Offset(),
cTotal,
aFieldRec[j].Type(),
var,
abExtra,
&cbExtra,
*((PStorage *)0) );
if ( fPrint )
{
switch ( var.vt )
{
case VT_EMPTY:
printf( "n/a " );
break;
case VT_I4:
printf( "%8d ", var.lVal );
break;
case VT_UI4:
printf( "%8u ", var.ulVal );
break;
case VT_FILETIME:
{
SYSTEMTIME stTime;
FileTimeToSystemTime( &var.filetime, &stTime );
WCHAR awcBuffer[100] = L"???";
GetDateFormat( LOCALE_USER_DEFAULT, // locale
0, // flags
&stTime, // time
L"MM/dd/yy ", // format
awcBuffer, // output
sizeof(awcBuffer)/sizeof(awcBuffer[0]) );
GetTimeFormat( LOCALE_USER_DEFAULT, // locale
0, // flags
&stTime, // time
L"hh:mmt", // format
awcBuffer + 10, // output
sizeof(awcBuffer)/sizeof(awcBuffer[0]) - 10 );
awcBuffer[15] += 0x20; // lowercase
printf( "%ws ", awcBuffer );
}
break;
case VT_I8:
printf( "%12hu ", var.hVal );
break;
case VT_UI8:
printf( "0x%08lx%08lx ", (ULONG) (var.uhVal.QuadPart>>32), (ULONG) var.uhVal.QuadPart );
break;
}
}
}
else
{
BOOL fOk = pRec->ReadVariable( aFieldRec[j].Ordinal(),
aFieldRec[j].Mask(),
culFixed,
cTotal,
cFixed,
var,
abExtra,
&cbExtra );
if ( fOk && fPrint )
{
switch ( var.vt )
{
case VT_LPSTR:
printf( "%s ", var.pszVal );
break;
case VT_LPWSTR:
printf( "%ws ", var.pwszVal );
break;
}
}
}
}
if ( 0 != cField && fPrint )
printf( "\n" );
}
else if ( pRec->IsTopLevel() )
{
// This is a lean top level record
aSmallInfo[iSection*cRecPerBuf + iRec].Type = Top;
aSmallInfo[iSection*cRecPerBuf + iRec].ulChainLength = 0;
cTopLevel++;
for ( unsigned j = 0; j < cField; j++ )
{
PROPVARIANT var;
BYTE abExtra[MAX_PATH * 5];
unsigned cbExtra = sizeof(abExtra);
if ( 0 == j && fPrint )
printf( "%6u: ", iSection*cRecPerBuf + iRec );
Win4Assert(aFieldRec[j].IsFixedSize());
{
pRec->ReadFixed( aFieldRec[j].Ordinal(),
aFieldRec[j].Mask(),
aFieldRec[j].Offset(),
cTotal,
aFieldRec[j].Type(),
var,
abExtra,
&cbExtra,
*((PStorage *)0) );
if ( fPrint )
{
switch ( var.vt )
{
case VT_EMPTY:
printf( "n/a " );
break;
case VT_I4:
printf( "%8d ", var.lVal );
break;
case VT_UI4:
printf( "%8u ", var.ulVal );
break;
case VT_FILETIME:
{
SYSTEMTIME stTime;
FileTimeToSystemTime( &var.filetime, &stTime );
WCHAR awcBuffer[100] = L"???";
GetDateFormat( LOCALE_USER_DEFAULT, // locale
0, // flags
&stTime, // time
L"MM/dd/yy ", // format
awcBuffer, // output
sizeof(awcBuffer)/sizeof(awcBuffer[0]) );
GetTimeFormat( LOCALE_USER_DEFAULT, // locale
0, // flags
&stTime, // time
L"hh:mmt", // format
awcBuffer + 10, // output
sizeof(awcBuffer)/sizeof(awcBuffer[0]) - 10 );
awcBuffer[15] += 0x20; // lowercase
printf( "%ws ", awcBuffer );
}
break;
case VT_I8:
printf( "%12hu ", var.hVal );
break;
case VT_UI8:
printf( "0x%08lx%08lx ", (ULONG) (var.uhVal.QuadPart>>32), (ULONG) var.uhVal.QuadPart );
break;
}
}
}
}
if ( 0 != cField && fPrint )
printf( "\n" );
}
else if ( pRec->IsFreeRecord() )
{
aSmallInfo[iSection*cRecPerBuf + iRec].Type = Free;
cFree++;
}
else
{
aSmallInfo[iSection*cRecPerBuf + iRec].Type = Virgin;
cVirgin++;
}
if (pRec->IsNormalTopLevel() || pRec->IsOverflow())
{
aSmallInfo[iSection*cRecPerBuf + iRec].ulPrev = pRec->ToplevelBlock();
aSmallInfo[iSection*cRecPerBuf + iRec].ulNext = pRec->OverflowBlock();
if ( pRec->OverflowBlock() != 0 )
{
/* printf( ", Overflow = %u:%u (file offset 0x%x)",
pRec->OverflowBlock() / cRecPerBuf,
pRec->OverflowBlock() % cRecPerBuf,
(pRec->OverflowBlock() / cRecPerBuf) * sizeof(abTemp) +
(pRec->OverflowBlock() % cRecPerBuf) * cbRec ); */
}
}
else if (pRec->IsTopLevel())
{
aSmallInfo[iSection*cRecPerBuf + iRec].ulPrev = 0;
aSmallInfo[iSection*cRecPerBuf + iRec].ulNext = 0;
}
else if (pRec->IsFreeRecord())
{
aSmallInfo[iSection*cRecPerBuf + iRec].ulPrev = pRec->GetPreviousFreeRecord();
aSmallInfo[iSection*cRecPerBuf + iRec].ulNext = pRec->GetNextFreeRecord();
}
aSmallInfo[iSection*cRecPerBuf + iRec].Length = pRec->CountRecords();
if ( pRec->CountRecords() == 0 )
{
printf( "Record %u (file offset 0x%x) is zero length!\n",
iSection * cRecPerBuf + iRec,
iSection * sizeof(abTemp) + iRec * cbRec );
}
if (fVerbose)
printf( "\n" );
if ( pRec->IsValidType() )
iRec += pRec->CountRecords();
else
iRec++;
ULONG iPct = (iSection * (100/5) / cTotalSections) * 5;
if (iPct != iCurrentPct)
{
iCurrentPct = iPct;
if (0 == wid)
printf( "Read %u%%\n", iCurrentPct );
}
}
iSection++;
oFile += cbRead;
}
else
{
ULONG Status = GetLastError();
if ( Status == ERROR_HANDLE_EOF )
break;
else
{
printf( "Error %u reading file.\n", Status );
}
}
}
if (SecondaryStore == dwLevel)
cMaxWidsInSecStore = iSection * cRecPerBuf;
if (wid == 0 || wid == 0xFFFFFFFF)
{
printf( "Read 100%%\n" );
printf( "%6u Top-Level records\n"
"%6u Overflow records\n"
"%6u Free records\n"
"%6u Virgin records\n", cTopLevel, cOverflow, cFree, cVirgin );
}
CloseHandle( h );
//
// Now check inter-record state
//
unsigned iRec = 0;
unsigned iCurrentSection = 0;
iCurrentPct = 0;
while ( iRec < iSection * cRecPerBuf )
{
if ( aSmallInfo[iRec].Type == Top )
{
unsigned iOverflowRec = aSmallInfo[iRec].ulNext;
unsigned cOverflowRec = 0;
while ( 0 != iOverflowRec )
{
if ( aSmallInfo[iOverflowRec].Type != Overflow )
{
printf( "Record 0x%x (%u:%u) (file offset 0x%x) should be overflow and is not!\n Top level = 0x%x (%u:%u) (file offset 0x%x)\n",
iOverflowRec,
iOverflowRec / cRecPerBuf,
iOverflowRec % cRecPerBuf,
(iOverflowRec / cRecPerBuf) * sizeof(abTemp) +
(iOverflowRec % cRecPerBuf) * cbRec,
iRec,
iRec / cRecPerBuf,
iRec % cRecPerBuf,
(iRec / cRecPerBuf) * sizeof(abTemp) +
(iRec % cRecPerBuf) * cbRec );
break;
}
iOverflowRec = aSmallInfo[iOverflowRec].ulNext;
cOverflowRec++;
if ( cOverflowRec > aSmallInfo[iRec].ulChainLength )
break;
}
if ( aSmallInfo[iRec].ulChainLength != cOverflowRec )
{
printf( "Record 0x%x (%u:%u) (file offset 0x%x) chain length mismatch %d,%d!\n",
iRec,
iRec / cRecPerBuf,
iRec % cRecPerBuf,
(iRec / cRecPerBuf) * sizeof(abTemp) +
(iRec % cRecPerBuf) * cbRec,
aSmallInfo[iRec].ulChainLength, cOverflowRec );
}
}
if (aSmallInfo[iRec].Length == 0)
{
printf( "%u:%u (file offset 0x%x) zero length record!\n",
iRec / cRecPerBuf,
iRec % cRecPerBuf,
(iRec / cRecPerBuf) * sizeof(abTemp) +
(iRec % cRecPerBuf) * cbRec );
iRec++;
} else {
iRec += aSmallInfo[iRec].Length;
}
if ( (iRec / cRecPerBuf) != iCurrentSection )
{
if (fVerbose)
printf( "Checked section %u\n", iCurrentSection );
ULONG iPct = (iCurrentSection * (100/5) / iSection) * 5;
if (iPct != iCurrentPct)
{
iCurrentPct = iPct;
if (wid == 0 || wid == 0xFFFFFFFF)
printf( "Checked %u%%\n", iCurrentPct );
}
iCurrentSection = (iRec / cRecPerBuf);
}
}
if (wid == 0 || wid == 0xFFFFFFFF)
printf( "Checked 100%%\n" );
LocalUnlock( hSmallInfo );
LocalFree( hSmallInfo );
}
void CheckConsistencyBetweenStores(char * pszPriFile, char *pszSecFile)
{
BYTE abTemp[SixtyFourK];
BYTE abTemp2[SixtyFourK];
ULONG iSection2 = 0, iTargetSection = 0, oFile = 0, iSection = 0;
ULONG cInconsistencies = 0;
ULONG cPriTotalSections = 0;
ULONG cSecTotalSections = 0;
ULONG iCurrentPct = 0;
HANDLE hPri = CreateFileA( pszPriFile,
GENERIC_READ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
0,
0 );
if ( INVALID_HANDLE_VALUE == hPri )
{
printf( "Can't open file %s. Error %u\n", pszPriFile, GetLastError() );
return;
}
HANDLE hSec = CreateFileA( pszSecFile,
GENERIC_READ,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
0,
0 );
if ( INVALID_HANDLE_VALUE == hSec )
{
printf( "Can't open file %s. Error %u\n", pszSecFile, GetLastError() );
return;
}
BY_HANDLE_FILE_INFORMATION fi;
if ( GetFileInformationByHandle( hPri, &fi ) )
{
cPriTotalSections = (fi.nFileSizeLow + sizeof(abTemp) - 1)/ sizeof abTemp;
}
else
{
printf( "Error %u getting size from primary's handle\n", GetLastError() );
return;
}
if ( GetFileInformationByHandle( hSec, &fi ) )
{
cSecTotalSections = (fi.nFileSizeLow + sizeof(abTemp) - 1)/ sizeof abTemp;
}
else
{
printf( "Error %u getting size from secondary's handle\n", GetLastError() );
return;
}
ULONG ulSecWidPtr = widInvalid;
// read the first large page of the secondary store into memory.
DWORD cbRead2;
ReadFile( hSec, abTemp2, sizeof(abTemp2), &cbRead2, 0 );
for (;;)
{
ULONG cbRead;
if ( ReadFile( hPri, abTemp, sizeof(abTemp), &cbRead, 0 ) )
{
if ( 0 == cbRead )
break;
ULONG iRec = 0;
while ( iRec < cPriRecPerBuf )
{
COnDiskPropertyRecord * pRec = new( iRec, abTemp, cbPriRec/4 ) COnDiskPropertyRecord;
if ( pRec->IsTopLevel() )
{
Win4Assert(!pRec->IsNormalTopLevel());
PROPVARIANT var;
BYTE abExtra[MAX_PATH * 5];
unsigned cbExtra = sizeof(abExtra);
Win4Assert(SecWidPtrFieldRec.IsFixedSize());
{
pRec->ReadFixed( SecWidPtrFieldRec.Ordinal(),
SecWidPtrFieldRec.Mask(),
SecWidPtrFieldRec.Offset(),
cPriTotal,
SecWidPtrFieldRec.Type(),
var,
abExtra,
&cbExtra,
*((PStorage *)0) );
switch ( var.vt )
{
case VT_EMPTY:
Win4Assert(!"Reading VT_EMPTY. Expect to read VT_UI4");
break;
case VT_I4:
Win4Assert(!"Reading VT_I4. Expect to read VT_UI4");
break;
case VT_UI4:
{
// Do we need to read a new large page?
iTargetSection = var.ulVal/cSecRecPerBuf;
if (var.ulVal > cMaxWidsInSecStore) // ensure that it is a valid wid in secondary store.
{
printf("Wid %u (0x%x) is pointing to a non-existent wid %u (0x%x) in Secondary store.\n",
iSection*cPriRecPerBuf + iRec, iSection*cPriRecPerBuf + iRec,
var.ulVal, var.ulVal);
continue;
}
if (iSection2 != iTargetSection)
{
// seek to the target section and read the large page into buffer.
//SetFilePointer(hSec, (iTargetSection - iSection2)*SixtyFourK, 0, FILE_CURRENT);
SetFilePointer(hSec, iTargetSection*SixtyFourK, 0, FILE_BEGIN);
ReadFile( hSec, abTemp2, sizeof(abTemp2), &cbRead2, 0 );
iSection2 = iTargetSection;
}
// Get the record in question.
COnDiskPropertyRecord * pRec2 = new( var.ulVal % cSecRecPerBuf, abTemp2, cbSecRec/4 ) COnDiskPropertyRecord;
// Now grill it!
if (!pRec2->IsNormalTopLevel())
{
cInconsistencies++;
// Invalid record type
printf("Error: Wid %u (0x%x), (%u:%u) is pointing to a non-toplevel wid %u (0x%x), ",
iSection*cPriRecPerBuf + iRec,
iSection*cPriRecPerBuf + iRec,
iSection, iRec,
var.ulVal, var.ulVal);
printf("(%u:%u), %s, length = %u record(s)\n",
iSection2, var.ulVal % cSecRecPerBuf,
pRec2->IsNormalTopLevel() ? "Top Level Normal" :
pRec2->IsOverflow() ? "Overflow" :
pRec2->IsFreeRecord() ? "Free Normal" :
pRec2->IsTopLevel() ? "Top Level Lean":
pRec2->IsLeanFreeRecord() ? "Free Lean":
"Virgin",
pRec2->CountRecords() );
}
break;
}
case VT_FILETIME:
Win4Assert(!"Reading VT_FILETIME. Expect to read VT_UI4");
break;
case VT_I8:
Win4Assert(!"Reading VT_I8. Expect to read VT_UI4");
break;
case VT_UI8:
Win4Assert(!"Reading VT_UI8. Expect to read VT_UI4");
break;
}
}
}
else if ( pRec->IsFreeRecord() )
{
}
else
{
}
if ( pRec->CountRecords() == 0 )
{
printf( "Record %u (file offset 0x%x) is zero length!\n",
iSection * cPriRecPerBuf + iRec,
iSection * sizeof(abTemp) + iRec * cbPriRec );
}
if ( pRec->IsValidType() )
iRec += pRec->CountRecords();
else
iRec++;
ULONG iPct = (iSection * (100/5) / cPriTotalSections) * 5;
if (iPct != iCurrentPct)
{
iCurrentPct = iPct;
printf( "Read %u%%\n", iCurrentPct );
}
}
iSection++;
oFile += cbRead;
}
else
{
ULONG Status = GetLastError();
if ( Status == ERROR_HANDLE_EOF )
break;
else
{
printf( "Error %u reading file.\n", Status );
}
}
}
CloseHandle( hPri );
CloseHandle( hSec );
if (cInconsistencies)
{
printf("%d inconsistencies were detected between the two stores.\n", cInconsistencies);
}
else
printf("No inconsistencies were detected between the two stores.\n");
}