//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2000. // // File: main.cxx // // Contents: Fixes a catalog after propagating from one machine to another. // // History: 12 Jan 2000 dlee Created from propdump // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include "mmfile.hxx" DECLARE_INFOLEVEL(ci) unsigned const SixtyFourK = 1024 * 64; //#define LOG CCoTaskAllocator CoTaskAllocator; // exported data definition void * CCoTaskAllocator::Allocate(ULONG cbSize) { return CoTaskMemAlloc( cbSize ); } void CCoTaskAllocator::Free(void *pv) { CoTaskMemFree(pv); } void Usage() { printf( "Usage: PropCi directory\n"); printf( " directory specifies the directory where the catalog exists.\n"); printf( "\n" ); printf( "This application converts file indexes in the catalog specified.\n" ); exit( 1 ); } WCHAR const * wcsistr( WCHAR const * wcsString, WCHAR const * wcsPattern ) { if ( (wcsPattern == 0) || (*wcsPattern == 0) ) return wcsString; ULONG cwcPattern = wcslen(wcsPattern); while ( *wcsString != 0 ) { while ( (*wcsString != 0) && (towupper(*wcsString) != towupper(*wcsPattern)) ) wcsString++; if ( 0 == *wcsString ) return 0; if ( _wcsnicmp( wcsString, wcsPattern, cwcPattern) == 0 ) return wcsString; wcsString++; } return 0; } //wcsistr void AppendBackslash( WCHAR *p ) { int x = wcslen( p ); if ( 0 != x && L'\\' != p[x-1] ) { p[x] = L'\\'; p[x+1] = 0; } } //AppendBackslash void FindFieldRec( WCHAR const * pwcFile, PROPID pid, CPropDesc & prop, ULONG & cTotal, ULONG & cFixed, ULONG & culFixed ) { cTotal = 0; cFixed = 0; culFixed = 0; HANDLE h = CreateFile( pwcFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0 ); if ( INVALID_HANDLE_VALUE == h ) { printf( "Can't open file %ws. Error %u\n", pwcFile, GetLastError() ); exit( 1 ); } ULONG cbRead; static BYTE abTemp[SixtyFourK]; if ( ReadFile( h, abTemp, sizeof abTemp , &cbRead, 0 ) ) { // Loop through records BOOL fFound = FALSE; CPropDesc * pPropDesc = (CPropDesc *)abTemp; for ( unsigned i = 0; i < cbRead/(sizeof(CPropDesc) + sizeof(ULONG)); i++, pPropDesc = (CPropDesc *)(((BYTE *)pPropDesc) + sizeof(CPropDesc) + sizeof(ULONG)) ) { if ( 0 != pPropDesc->Pid() ) { if ( pPropDesc->Pid() == pid ) { memcpy( &prop, pPropDesc, sizeof prop ); fFound = TRUE; } cTotal++; if ( pPropDesc->Offset() != -1 ) { cFixed++; culFixed += (pPropDesc->Size() / sizeof(DWORD)); } } } if ( !fFound ) { printf( "can't find pid %#x\n", pid ); exit( 1 ); } } else { printf( "Can't read file %ws. Error %u\n", pwcFile, GetLastError() ); exit( 1 ); } #ifdef LOG printf( "pid %d, total %d, fixed %d\n", pid, cTotal, cFixed ); printf( " pid %d, vt %d, ostart %d, cbmax %d, ordinal %d, mask %d, rec %d, fmodify %d\n", prop.Pid(), prop.Type(), prop.Offset(), prop.Size(), prop.Ordinal(), prop.Mask(), prop.Record(), prop.Modifiable() ); #endif CloseHandle( h ); } //FindFieldRec NTSTATUS CiNtOpenNoThrow( HANDLE & h, WCHAR const * pwcsPath, ACCESS_MASK DesiredAccess, ULONG ShareAccess, ULONG OpenOptions ) { h = INVALID_HANDLE_VALUE; UNICODE_STRING uScope; if ( !RtlDosPathNameToNtPathName_U( pwcsPath, &uScope, 0, 0 ) ) return STATUS_INSUFFICIENT_RESOURCES; // // Open the file // OBJECT_ATTRIBUTES ObjectAttr; InitializeObjectAttributes( &ObjectAttr, // Structure &uScope, // Name OBJ_CASE_INSENSITIVE, // Attributes 0, // Root 0 ); // Security IO_STATUS_BLOCK IoStatus; NTSTATUS Status = NtOpenFile( &h, // Handle DesiredAccess, // Access &ObjectAttr, // Object Attributes &IoStatus, // I/O Status block ShareAccess, // Shared access OpenOptions ); // Flags RtlFreeHeap( RtlProcessHeap(), 0, uScope.Buffer ); return Status; } //CiNtOpenNoThrow FILEID GetFileId( WCHAR * pwcPath ) { if ( 2 == wcslen( pwcPath ) ) wcscat( pwcPath, L"\\" ); HANDLE h; NTSTATUS status = CiNtOpenNoThrow( h, pwcPath, FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0 ); if ( FAILED( status ) ) { printf( "Can't open file %ws to get fileid. Error %#x\n", pwcPath, status ); return 0; } FILE_INTERNAL_INFORMATION fii; IO_STATUS_BLOCK IoStatus; status = NtQueryInformationFile( h, &IoStatus, &fii, sizeof fii, FileInternalInformation ); NtClose( h ); if ( NT_SUCCESS( status ) ) status = IoStatus.Status; if ( NT_SUCCESS( status ) ) return fii.IndexNumber.QuadPart; return 0; } //GetFileId void GetRecordInformation( WCHAR const * pwcFile, ULONG & cRecPerBuf, ULONG & cbRec ) { cRecPerBuf = 0; cbRec = 0; HANDLE h = CreateFile( pwcFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0 ); if ( INVALID_HANDLE_VALUE == h ) { printf( "Can't open file %ws. Error %u\n", pwcFile, GetLastError() ); exit( 1 ); } ULONG cbRead; static BYTE abTemp[SixtyFourK]; if ( ReadFile( h, abTemp, sizeof(abTemp), &cbRead, 0 ) ) { // // Determine record size // if ( abTemp[0] != 0 || abTemp[1] != 0 ) { printf( "Record 0 not blank. File corrupt!\n" ); exit( 1 ); } // 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 ); exit( 1 ); } 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 ); exit( 1 ); } cRecPerBuf = sizeof(abTemp) / cbRec; break; default: printf( "First non-zero byte is not a proper signature (%u)!\n", *(SHORT *)&abTemp[i] ); exit( 1 ); } } else { printf( "can't read from file %ws, error %d\n", pwcFile, GetLastError() ); exit( 1 ); } #ifdef LOG printf( "cRecPerBuf %d, cbRec %d\n", cRecPerBuf, cbRec ); #endif CloseHandle( h ); } //GetRecordInformation void PatchFileIDs( WCHAR const * pwcDir, WCHAR const * pwcPri, WCHAR const * pwcSec ) { // // First, read the property specifications for secondary wid, fileindex, // lastseen, and path. The first 3 are in the primary and the last in // the secondary. // WCHAR awcPriProp[ MAX_PATH ]; wcscpy( awcPriProp, pwcDir ); wcscat( awcPriProp, L"CIP10000.001" ); ULONG cPriTotal, cPriFixed, culPriFixed; CPropDesc SecWidPtrFieldRec; FindFieldRec( awcPriProp, pidSecondaryStorage, SecWidPtrFieldRec, cPriTotal, cPriFixed, culPriFixed ); CPropDesc FileIdFieldRec; FindFieldRec( awcPriProp, pidFileIndex, FileIdFieldRec, cPriTotal, cPriFixed, culPriFixed ); CPropDesc LastSeenFieldRec; FindFieldRec( awcPriProp, pidLastSeenTime, LastSeenFieldRec, cPriTotal, cPriFixed, culPriFixed ); WCHAR awcSecProp[ MAX_PATH ]; wcscpy( awcSecProp, pwcDir ); wcscat( awcSecProp, L"CIP20000.001" ); ULONG cSecTotal, cSecFixed, culSecFixed; CPropDesc PathFieldRec; FindFieldRec( awcSecProp, pidPath, PathFieldRec, cSecTotal, cSecFixed, culSecFixed ); // // Get information about the number and size of records. // ULONG cPriRecPerBuf, cbPriRec; GetRecordInformation( pwcPri, cPriRecPerBuf, cbPriRec ); ULONG cSecRecPerBuf, cbSecRec; GetRecordInformation( pwcSec, cSecRecPerBuf, cbSecRec ); // // Walk the property store, get the secondary wid, read the path from // the secondary store, lookup the fileid, and write the fileid back // info the primary store. // CMMFile pri( pwcPri, TRUE ); if ( !pri.Ok() ) { printf( "can't map primary\n" ); exit( 1 ); } BYTE * pb = (BYTE *) pri.GetMapping(); BYTE * pbBase = pb; CMMFile sec( pwcSec, TRUE ); if ( !sec.Ok() ) { printf( "can't map secondary\n" ); exit( 1 ); } BYTE * pbSecBase = (BYTE *) sec.GetMapping(); FILETIME ftNow; GetSystemTimeAsFileTime( &ftNow ); #ifdef LOG printf( "pb %#x, size %d\n", pb, pri.FileSize() ); #endif ULONG iRec = 0, iRecTotal = 0; do { #ifdef LOG printf( "record %d\n", iRecTotal ); #endif // If we're at the end of a 64k page, go on to the next one. if ( iRec == cPriRecPerBuf ) { iRec = 0; pb += 65536; } COnDiskPropertyRecord * pRec = new( iRec, pb, cbPriRec/4 ) COnDiskPropertyRecord; if ( (BYTE *) pRec >= ( pbBase + pri.FileSize() ) ) break; if ( pRec->IsTopLevel() ) { PROPVARIANT var; static BYTE abExtra[MAX_PATH * 5]; unsigned cbExtra = sizeof(abExtra); pRec->ReadFixed( SecWidPtrFieldRec.Ordinal(), SecWidPtrFieldRec.Mask(), SecWidPtrFieldRec.Offset(), cPriTotal, SecWidPtrFieldRec.Type(), var, abExtra, &cbExtra, *((PStorage *)0) ); if ( VT_UI4 != var.vt ) { printf( "failure: secondary wid wasn't a UI4\n" ); exit( 1 ); } #ifdef LOG printf( "secondary wid %d\n", var.ulVal ); #endif ULONG iTargetSection = var.ulVal/cSecRecPerBuf; BYTE * pbSec = pbSecBase + ( iTargetSection * SixtyFourK ); // Get the secondary store record COnDiskPropertyRecord * pRec2 = new( var.ulVal % cSecRecPerBuf, pbSec, cbSecRec/4 ) COnDiskPropertyRecord; // Read the path cbExtra = sizeof abExtra; #ifdef LOG printf( "pRec2: %#x\n", pRec2 ); #endif var.vt = VT_EMPTY; if ( !pRec2->ReadVariable( PathFieldRec.Ordinal(), PathFieldRec.Mask(), culSecFixed, cSecTotal, cSecFixed, var, abExtra, &cbExtra ) ) { // It's in an overflow record BOOL fOk; do { // // Check for existing overflow block. // WORKID widOverflow = pRec2->OverflowBlock(); if ( 0 == widOverflow ) break; iTargetSection = widOverflow / cSecRecPerBuf; pbSec = pbSecBase + ( iTargetSection * SixtyFourK ); pRec2 = new( widOverflow % cSecRecPerBuf, pbSec, cbSecRec/4 ) COnDiskPropertyRecord; #ifdef LOG printf( "overflow pRec2: %#x\n", pRec2 ); #endif ULONG Ordinal = PathFieldRec.Ordinal() - cSecFixed; DWORD Mask = (1 << ((Ordinal % 16) * 2) ); fOk = pRec2->ReadVariable( Ordinal, // Ordinal (assuming 0 fixed) Mask, // Mask (assuming 0 fixed) 0, // Fixed properties cSecTotal - cSecFixed, 0, // Count of fixed properties var, abExtra, &cbExtra ); } while ( !fOk ); } if ( VT_LPWSTR == var.vt ) { // Get and set the fileindex for this volume FILEID fid = GetFileId( var.pwszVal ); PROPVARIANT varId; varId.vt = VT_UI8; varId.hVal.QuadPart = fid; pRec->WriteFixed( FileIdFieldRec.Ordinal(), FileIdFieldRec.Mask(), FileIdFieldRec.Offset(), FileIdFieldRec.Type(), cPriTotal, varId ); // Set the last seen time so we don't reindex the file PROPVARIANT varLS; varLS.vt = VT_FILETIME; varLS.filetime = ftNow; pRec->WriteFixed( LastSeenFieldRec.Ordinal(), LastSeenFieldRec.Mask(), LastSeenFieldRec.Offset(), LastSeenFieldRec.Type(), cPriTotal, varLS ); #ifdef LOG printf( "fileid %#I64x, %ws\n", fid, var.pwszVal ); #endif } } if ( pRec->IsValidType() ) { iRec += pRec->CountRecords(); iRecTotal += pRec->CountRecords(); } else { iRec++; iRecTotal++; } } while ( TRUE ); pri.Flush(); } //PatchFileIDs void GetNtVolumeInformation( ULONG ulVolumeId, ULONGLONG & ullVolumeCreationTime, ULONG & ulVolumeSerialNumber, ULONGLONG & ullJournalId ) { ullVolumeCreationTime = 0; ulVolumeSerialNumber = 0; ullJournalId = 0; WCHAR wszVolumePath[] = L"\\\\.\\a:"; wszVolumePath[4] = (WCHAR) ulVolumeId; FILE_FS_VOLUME_INFORMATION VolumeInfo; VolumeInfo.VolumeCreationTime.QuadPart = 0; VolumeInfo.VolumeSerialNumber = 0; HANDLE hVolume = CreateFile( wszVolumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0 ); if ( INVALID_HANDLE_VALUE == hVolume ) { printf( "failure: can't open volume %ws\n", wszVolumePath ); exit( 1 ); } IO_STATUS_BLOCK iosb; RtlZeroMemory( &VolumeInfo, sizeof VolumeInfo ); NtQueryVolumeInformationFile( hVolume, &iosb, &VolumeInfo, sizeof(VolumeInfo), FileFsVolumeInformation ); // // This call will only succeed on NTFS NT5 w/ USN Journal enabled. // USN_JOURNAL_DATA UsnJournalInfo; RtlZeroMemory( &UsnJournalInfo, sizeof UsnJournalInfo ); NTSTATUS Status = NtFsControlFile( hVolume, 0, 0, 0, &iosb, FSCTL_QUERY_USN_JOURNAL, 0, 0, &UsnJournalInfo, sizeof UsnJournalInfo ); if ( NT_SUCCESS(Status) && NT_SUCCESS( iosb.Status ) ) { // cool, it's a usn volume } else if ( ( STATUS_JOURNAL_NOT_ACTIVE == Status || STATUS_INVALID_DEVICE_STATE == Status ) ) { // // Usn journal not created, create it // CREATE_USN_JOURNAL_DATA usnCreateData; usnCreateData.MaximumSize = 0x800000; // 8 meg usnCreateData.AllocationDelta = 0x100000; // 1 meg Status = NtFsControlFile( hVolume, 0, 0, 0, &iosb, FSCTL_CREATE_USN_JOURNAL, &usnCreateData, sizeof usnCreateData, 0, 0 ); if ( NT_SUCCESS( Status ) && NT_SUCCESS( iosb.Status ) ) { Status = NtFsControlFile( hVolume, 0, 0, 0, &iosb, FSCTL_QUERY_USN_JOURNAL, 0, 0, &UsnJournalInfo, sizeof UsnJournalInfo ); if (! ( NT_SUCCESS(Status) && NT_SUCCESS( iosb.Status ) ) ) { printf( "can't query usn vol info after creating it %#x\n", Status ); exit( 1 ); } } else { printf( "can't create usn journal %#x\n", Status ); exit( 1 ); } } else { printf( "can't get USN information, probably FAT: %#x\n", Status ); } ullVolumeCreationTime = VolumeInfo.VolumeCreationTime.QuadPart; ulVolumeSerialNumber = VolumeInfo.VolumeSerialNumber; ullJournalId = UsnJournalInfo.UsnJournalID; printf( " new volumecreationtime: %#I64x\n", ullVolumeCreationTime ); printf( " new volumeserialnumber: %#x\n", ulVolumeSerialNumber ); printf( " new journalid: %#I64x\n", ullJournalId ); CloseHandle( hVolume ); } //GetNtVolumeInformation void PatchScopeTable( WCHAR const * pwcDir ) { // Find out how many scopes are in the scope table ULONG cScopes = 0; { WCHAR awcControl[ MAX_PATH ]; wcscpy( awcControl, pwcDir ); wcscat( awcControl, L"cisp0000.000" ); CMMFile Control( awcControl, FALSE ); if ( 0 == Control.GetMapping() ) { printf( "can't open file %ws\n", awcControl ); exit( 1 ); } cScopes = ((ULONG *) Control.GetMapping())[4]; } WCHAR awcOne[ MAX_PATH ]; wcscpy( awcOne, pwcDir ); wcscat( awcOne, L"cisp0000.001" ); // Loop through the scopes and patch { printf( " scope table: %ws has %d scopes\n", awcOne, cScopes ); CMMFile One( awcOne, TRUE ); if ( !One.Ok() ) { printf( "can't map scope table\n" ); exit( 1 ); } BYTE * pb = (BYTE *) One.GetMapping(); for ( ULONG i = 0; i < cScopes; i++ ) { const LONGLONG eSigCiScopeTable = 0x5158515851585158i64; LONGLONG signature; memcpy( &signature, pb, sizeof signature ); pb += sizeof signature; if ( 0 == signature ) break; printf( " scope record: \n" ); if ( eSigCiScopeTable != signature ) { printf( "invalid scope signature: %#I64x\n", signature ); exit( 1 ); } VOLUMEID volumeId; memcpy( &volumeId, pb, sizeof volumeId ); printf( " volumeId: %x\n", volumeId ); pb += sizeof volumeId; ULONGLONG ullNewVolumeCreationTime; ULONG ulNewVolumeSerialNumber; ULONGLONG ullNewJournalId; GetNtVolumeInformation( volumeId, ullNewVolumeCreationTime, ulNewVolumeSerialNumber, ullNewJournalId ); ULONGLONG ullVolumeCreationTime; memcpy( &ullVolumeCreationTime, pb, sizeof ullVolumeCreationTime ); printf( " creation time: %#I64x\n", ullVolumeCreationTime ); memcpy( pb, &ullNewVolumeCreationTime, sizeof ullNewVolumeCreationTime ); pb += sizeof ullVolumeCreationTime; ULONG ulVolumeSerialNumber; memcpy( &ulVolumeSerialNumber, pb, sizeof ulVolumeSerialNumber ); printf( " serial number: %x\n", ulVolumeSerialNumber ); memcpy( pb, &ulNewVolumeSerialNumber, sizeof ulNewVolumeSerialNumber ); pb += sizeof ulVolumeSerialNumber; if ( CI_VOLID_USN_NOT_ENABLED == volumeId ) { FILETIME ft; memcpy( &ft, pb, sizeof ft ); printf( " filetime: %#I64x\n", ft ); pb += sizeof ft; } else { USN usn; memcpy( &usn, pb, sizeof usn ); printf( " usn: %#I64x\n", usn ); USN usnNewMax = 0; memcpy( pb, &usnNewMax, sizeof usnNewMax ); pb += sizeof usn; ULONGLONG JournalId; memcpy( &JournalId, pb, sizeof JournalId ); printf( " JournalId: %#I64x\n", JournalId ); memcpy( pb, &ullNewJournalId, sizeof ullNewJournalId ); pb += sizeof JournalId; } ULONG cwcPath; memcpy( &cwcPath, pb, sizeof cwcPath ); pb += sizeof cwcPath; WCHAR awcPath[ MAX_PATH ]; memcpy( awcPath, pb, cwcPath * sizeof WCHAR ); printf( " path: %ws\n", awcPath ); pb += ( cwcPath * sizeof WCHAR ); } One.Flush(); } WCHAR awcTwo[ MAX_PATH ]; wcscpy( awcTwo, pwcDir ); wcscat( awcTwo, L"cisp0000.002" ); BOOL f = CopyFile( awcOne, awcTwo, FALSE ); if ( !f ) { printf( "can't copy scope list, error %d\n", GetLastError() ); exit( 1 ); } } //PatchScopeDir extern "C" int __cdecl wmain( int argc, WCHAR * argv[] ) { if ( 2 != argc ) Usage(); // // The lone argument is the catalog directory, with or without catalog.wci // if ( wcslen( argv[1] ) > ( MAX_PATH-20 ) ) Usage(); WCHAR awcDir[ MAX_PATH ]; wcscpy( awcDir, argv[1] ); AppendBackslash( awcDir ); if ( 0 == wcsistr( awcDir, L"catalog.wci" ) ) wcscat( awcDir, L"catalog.wci\\" ); // Find and validate the primary and secondary stores exist WCHAR awcPri[MAX_PATH]; wcscpy( awcPri, awcDir ); wcscat( awcPri, L"*.ps1" ); WIN32_FIND_DATA findData; HANDLE h = FindFirstFile( awcPri, &findData ); if ( INVALID_HANDLE_VALUE == h ) { printf( "can't find primary *.ps1 store\n" ); exit( 1 ); } FindClose( h ); wcscpy( awcPri, awcDir ); wcscat( awcPri, findData.cFileName ); WCHAR awcSec[MAX_PATH]; wcscpy( awcSec, awcDir ); wcscat( awcSec, L"*.ps2" ); h = FindFirstFile( awcSec, &findData ); if ( INVALID_HANDLE_VALUE == h ) { printf( "can't find secondary *.ps2 store\n" ); exit( 1 ); } FindClose( h ); wcscpy( awcSec, awcDir ); wcscat( awcSec, findData.cFileName ); // // Do the core work here -- patch the file IDs in the primary store. // Also whack the last seen times so the files aren't refiltered in // case they were copied to the target machine after the catalog was // snapped. // printf( "patching file IDs\n" ); PatchFileIDs( awcDir, awcPri, awcSec ); // // Patch the scope table so cisvc doesn't think it's a different volume. // printf( "patching the scope table\n" ); PatchScopeTable( awcDir ); // // Delete the old fileid hash table since the fileids are wrong. // It'll get recreated automatically when the catalog is opened. // printf( "deleting the fileid hash map\n" ); WCHAR awcHashMap[ MAX_PATH ]; wcscpy( awcHashMap, awcDir ); wcscat( awcHashMap, L"cicat.fid" ); DeleteFile( awcHashMap ); printf( "catalog successfully converted\n" ); return 0; } //wmain