//+--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation, 1991 - 1998 // // File: dellog.cxx // // Contents: Deletion log for usns // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- #include <pch.cxx> #pragma hdrstop #include <mmstrm.hxx> #include "cicat.hxx" #include "dellog.hxx" const LONGLONG eSigDelLog = 0x64656c746e6c6f67i64; // Signature //+--------------------------------------------------------------------------- // // Member: CFakeVolIdMap::CFakeVolIdMap // // Synopsis: Constructor // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- CFakeVolIdMap::CFakeVolIdMap() { for ( ULONG i=0; i<COUNT_SPECIAL_CHARS; i++) _aVolIdSpecial[i] = 0; } //+--------------------------------------------------------------------------- // // Member: CFakeVolIdMap::VolIdToFakeVolId // // Synopsis: Converts a volume id to a fake volume id in the range // 0..RTL_MAX_DRIVE_LETTERS-1 // // History: 28-Jul-97 SitaramR Created // // Notes: Drive letters in the range 'a' to 'z' are mapped by subtracting // 'a', which is the base volume id, i.e. they are in the range // 0..25 The volume id's for the remaining drive letters are // maintained in _aVolIdSpecial, and the fake volume ids for these // special drives are in the range 26..RTL_MAX_DRIVE_LETTERS-1. // //---------------------------------------------------------------------------- ULONG CFakeVolIdMap::VolIdToFakeVolId( VOLUMEID volumeId ) { // // Volume ids are obtained by the ascii value of the drive letter // Win4Assert( volumeId < 0xff ); if ( volumeId >= VolumeIdBase && volumeId-VolumeIdBase < COUNT_ALPHABETS ) { return volumeId-VolumeIdBase; } // // Lookup in _aVolIdSpecial // for ( ULONG i=0; i<COUNT_SPECIAL_CHARS; i++ ) { if ( _aVolIdSpecial[i] == volumeId ) return COUNT_ALPHABETS + i; } for ( i=0; i<COUNT_SPECIAL_CHARS; i++ ) { if ( _aVolIdSpecial[i] == 0 ) { _aVolIdSpecial[i] = volumeId; return COUNT_ALPHABETS + i; } } Win4Assert( !"Volume id map overflow" ); return 0; } //+--------------------------------------------------------------------------- // // Member: CFakeVolIdMap::FakeVolIdToVolId // // Synopsis: Converts a fake volume id in the range 0..RTL_MAX_DRIVE_LETTERS-1 // to a real volume id // // History: 28-Jul-97 SitaramR Created // // Notes: See VolIdToFakeVolId for mapping info. // //---------------------------------------------------------------------------- VOLUMEID CFakeVolIdMap::FakeVolIdToVolId( ULONG fakeVolId ) { Win4Assert( fakeVolId < RTL_MAX_DRIVE_LETTERS ); if ( fakeVolId < COUNT_ALPHABETS ) return VolumeIdBase + fakeVolId; else return _aVolIdSpecial[fakeVolId-COUNT_ALPHABETS]; } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::CDeletionLog // // Synopsis: Constructor // // Arguments: [fileIdMap] -- File id map // [cicat] -- Ci catalog // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- CDeletionLog::CDeletionLog( CFileIdMap & fileIdMap, CiCat& cicat ) : _fileIdMap(fileIdMap), _cicat(cicat) { } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::FastInit // // Synopsis: Initialization // // Arguments: [pStorage] -- Ci storage // [pwcsCatDir] -- Catalog dir // [version] -- Version # // // History: 28-Jul-97 SitaramR Created // 02-Mar-98 KitmanH Used QueryDeletionlog to obtain a new // CMmStream // // Notes: The persistent format is <vol id><cEntries><entry 1>..<entry cEntries> // for each volume id, and each entry has <fileid><wid><usn>. // //---------------------------------------------------------------------------- void CDeletionLog::FastInit( CiStorage * pStorage, ULONG version ) { XPtr<PMmStream> sStrm( pStorage->QueryDeletionLog() ); _xPersStream.Set( new CDynStream( sStrm.GetPointer() ) ); sStrm.Acquire(); _xPersStream->CheckVersion( *pStorage, version ); ULONG cVolumes = _xPersStream->Count(); _xPersStream->InitializeForRead(); for ( ULONG i=0; i<cVolumes; i++ ) { ULONG cbRead; LONGLONG llSig; cbRead = _xPersStream->Read( &llSig, sizeof(llSig) ); if ( cbRead != sizeof(llSig) ) FatalCorruption( cbRead, sizeof(llSig) ); if ( eSigDelLog != llSig ) { ciDebugOut(( DEB_ERROR, "CDeletionLog: Signature mismatch 0x%x:0x%x\n", lltoHighPart(llSig), lltoLowPart(llSig) )); FatalCorruption( 0, 0 ); } VOLUMEID volumeId; cbRead = _xPersStream->Read( &volumeId, sizeof(VOLUMEID) ); if ( cbRead != sizeof(VOLUMEID) ) FatalCorruption( cbRead, sizeof(VOLUMEID) ); ULONG cEntries; cbRead = _xPersStream->Read( &cEntries, sizeof(ULONG) ); if ( cbRead != sizeof(ULONG) ) FatalCorruption( cbRead, sizeof(ULONG) ); for ( ULONG j=0; j<cEntries;j++ ) { FILEID fileId; cbRead = _xPersStream->Read( &fileId, sizeof(FILEID) ); if ( cbRead != sizeof(FILEID) ) FatalCorruption( cbRead, sizeof(FILEID) ); WORKID wid; cbRead = _xPersStream->Read( &wid, sizeof(WORKID) ); if ( cbRead != sizeof(WORKID) ) FatalCorruption( cbRead, sizeof(WORKID) ); USN usn; cbRead = _xPersStream->Read( &usn, sizeof(USN) ); if ( cbRead != sizeof(USN) ) FatalCorruption( cbRead, sizeof(USN) ); MarkForDeletion( volumeId, fileId, wid, usn ); } } } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::ReInit // // Synopsis: Empties the deletion log // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CDeletionLog::ReInit( ULONG version ) { CLock lock(_mutex); _xPersStream->SetVersion( version ); for ( ULONG i=0; i<RTL_MAX_DRIVE_LETTERS; i++ ) _aDelLogEntryList[i].Clear(); Flush(); } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::Flush // // Synopsis: Serializes the deletion log to disk // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CDeletionLog::Flush() { Win4Assert( !_xPersStream.IsNull() ); CLock lock(_mutex); _xPersStream->InitializeForWrite( GetSize() ); LONGLONG llSig = eSigDelLog; ULONG cVolumes = 0; for ( ULONG i=0; i<RTL_MAX_DRIVE_LETTERS; i++ ) { if ( _aDelLogEntryList[i].Count() > 0 ) { cVolumes++; _xPersStream->Write( &llSig, sizeof(llSig) ); VOLUMEID volumeId = _fakeVolIdMap.FakeVolIdToVolId( i ); _xPersStream->Write( &volumeId, sizeof(VOLUMEID) ); ULONG cEntries = _aDelLogEntryList[i].Count(); _xPersStream->Write( &cEntries, sizeof(ULONG) ); #if CIDBG==1 ULONG cEntriesInList = 0; USN usnPrev = 0; #endif for ( CDelLogEntryListIter entryListIter( _aDelLogEntryList[i] ); !_aDelLogEntryList[i].AtEnd(entryListIter); _aDelLogEntryList[i].Advance( entryListIter) ) { FILEID fileId = entryListIter->FileId(); _xPersStream->Write( &fileId, sizeof(FILEID) ); WORKID wid = entryListIter->WorkId(); _xPersStream->Write( &wid, sizeof(WORKID) ); USN usn = entryListIter->Usn(); _xPersStream->Write( &usn, sizeof(USN) ); #if CIDBG==1 // // Check usn's are monotonically increasing // cEntriesInList++; Win4Assert( entryListIter->Usn() >= usnPrev ); usnPrev = entryListIter->Usn(); #endif } #if CIDBG==1 Win4Assert( cEntriesInList == _aDelLogEntryList[i].Count() ); #endif } } _xPersStream->SetCount( cVolumes ); _xPersStream->Flush(); } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::MarkForDeletion // // Synopsis: Adds a deletion entry // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CDeletionLog::MarkForDeletion( VOLUMEID volumeId, FILEID fileId, WORKID wid, USN usn ) { CLock lock(_mutex); XPtr<CDelLogEntry> xEntry( new CDelLogEntry( fileId, wid, usn ) ); ULONG fakeVolId = _fakeVolIdMap.VolIdToFakeVolId( volumeId ); Win4Assert( fakeVolId < RTL_MAX_DRIVE_LETTERS ); if ( _aDelLogEntryList[fakeVolId].Count() == 0 ) { // // Empty list case // _aDelLogEntryList[fakeVolId].Queue( xEntry.Acquire() ); } else { CDelLogEntry *pEntryLast = _aDelLogEntryList[fakeVolId].GetLast(); if ( xEntry->Usn() > pEntryLast->Usn() ) { // // If the usn is less than the last entry's usn, then it means that it // is a usn that is being replayed, and there is no need to add it // to the deletion log again. // _aDelLogEntryList[fakeVolId].Queue( xEntry.Acquire() ); } } } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::ProcessChangesFlush // // Synopsis: Process the list of changes that has been flushed by // framework/changelog and do the actual deletes from // the file id map. // // Arguments: [usnFlushInfoList] -- List of changes flushed // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CDeletionLog::ProcessChangesFlush( CUsnFlushInfoList & usnFlushInfoList ) { CLock lock( _mutex ); for ( ULONG i=0; i<usnFlushInfoList.Count(); i++ ) { CUsnFlushInfo *pFlushInfo = usnFlushInfoList.Get(i); ULONG fakeVolId = _fakeVolIdMap.VolIdToFakeVolId( pFlushInfo->VolumeId() ); Win4Assert( fakeVolId < RTL_MAX_DRIVE_LETTERS ); CDelLogEntryList& entryList = _aDelLogEntryList[fakeVolId]; #if CIDBG==1 USN usnPrev = 0; #endif CDelLogEntryListIter entryListIter( entryList ); while ( !entryList.AtEnd( entryListIter ) ) { CDelLogEntry *pEntry = entryListIter.GetEntry(); entryList.Advance( entryListIter ); #if CIDBG==1 // // Check that usn's are monotonically increasing // Win4Assert( pEntry->Usn() >= usnPrev ); usnPrev = pEntry->Usn(); #endif if ( pEntry->Usn() <= pFlushInfo->UsnHighest() ) { entryList.RemoveFromList( pEntry ); _fileIdMap.Delete( pEntry->FileId(), pEntry->WorkId() ); delete pEntry; } else { // // Since the list is in increasing usn order, we are done // with this volume id. // break; } } } } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::GetSize // // Synopsis: Returns the size of the serialized stream in bytes // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- ULONG CDeletionLog::GetSize() { // // Start with a slop factor // ULONG ulSize = 1024; for ( ULONG i=0; i<RTL_MAX_DRIVE_LETTERS; i++ ) { ulSize += sizeof(VOLUMEID) // volume id field + sizeof(ULONG) // cEntries field + _aDelLogEntryList[i].Count() * sizeof(CDelLogEntry); } return ulSize; } //+--------------------------------------------------------------------------- // // Member: CDeletionLog::FatalCorruption // // Synopsis: Handles deletion log corruption // // Arguments: [cbRead] -- Count of bytes read // [cbToRead] -- Count of bytes to read // // History: 28-Jul-97 SitaramR Created // //---------------------------------------------------------------------------- void CDeletionLog::FatalCorruption( ULONG cbRead, ULONG cbToRead ) { Win4Assert( !"Corrupt deletion log" ); ciDebugOut(( DEB_ERROR, "CDeletionLog: read %d bytes instead of %d\n", cbRead, cbToRead )); PStorage & storage = _cicat.GetStorage(); storage.ReportCorruptComponent( L"Deletion log" ); THROW( CException( CI_CORRUPT_CATALOG ) ); }