1244 lines
44 KiB
C++
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");
|
|
}
|