//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2000. // // File: propdump.cxx // // Contents: Property file dump utility // // History: 29 Oct 1996 KyleP Created // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include 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] []\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] dump only in primary/secondary store\n"); printf(" is the name of the primary file.\n"); printf(" 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"); }