//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1991 - 2000. // // File: RCSTXACT.CXX // // Contents: RecoverableStream Transactions // // Classes: CRcovStrmTrans, // CRcovStrmReadTrans, // CRcovStrmWriteTrans, // CRcovStrmAppendTrans, // CRcovStrmMDTrans // // // History: 28-Jan-1994 SrikantS Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #ifndef FACB_MAPPING_GRANULARITY #define VACB_MAPPING_GRANULARITY 262144 #endif #define CI_PAGES_IN_CACHE_PAGE (VACB_MAPPING_GRANULARITY/CI_PAGE_SIZE) #define CACHE_PAGE_TO_CI_PAGE_SHIFT 6 // 64 //+--------------------------------------------------------------------------- // // Function: CiPagesFromCachePages // // Synopsis: Given a number in "cache" page units, it converts into // "ci" pages. A cache page is 256K in size and a ci page is // 4K in size. // // Arguments: [cachePage] - A number in cache page units // // History: 10-17-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- inline LONGLONG CiPagesFromCachePages( const LONGLONG & cachePage ) { Win4Assert( (1 << CACHE_PAGE_TO_CI_PAGE_SHIFT) == CI_PAGES_IN_CACHE_PAGE ); return cachePage << CACHE_PAGE_TO_CI_PAGE_SHIFT; } inline ULONG PgCachePgTrunc(ULONG x) { return x & ~(VACB_MAPPING_GRANULARITY - 1); } inline BOOL IsHighBitSet( ULONG ul ) { return ul & 0x80000000; } //+--------------------------------------------------------------------------- // // Member: CRcovStrmTrans::CRcovStrmTrans // // Synopsis: // // Arguments: [obj] - // [op] - // // Returns: // // Modifies: // // History: 1-28-94 srikants Created // 3-13-98 kitmanh Don't Open backup mmstreams if catalog // is read-only // // Notes: // //---------------------------------------------------------------------------- CRcovStrmTrans::CRcovStrmTrans( PRcovStorageObj &obj, RcovOpType op ) : _obj(obj), _hdr(obj.GetHeader()), _sObj(_obj) { Win4Assert( opNone != op ); // // Read the header from the disk. // _obj.ReadHeader(); _iPrim = _hdr.GetPrimary(); _iBack = _hdr.GetBackup(); _iCurr = _iBack; // // Check if the backup is clean or not. // if ( !_hdr.IsBackupClean() ) { ciDebugOut(( DEB_WARN, "CRcovStrmTrans::Cleaning up a Failed Previous Trans\n" )); // // Open the streams and create memory mapped streams for // accessing the backup in write mode and the primary in // read mode. // _obj.Open( _hdr.GetPrimary(), FALSE ); // We can't cleanup if the catalog is read only, so just fail if ( obj.IsReadOnly() ) { PStorage & storage = _obj.GetStorage(); storage.ReportCorruptComponent( L"RcovStorageTrans2" ); THROW (CException( CI_CORRUPT_DATABASE ) ); } _obj.Open( _hdr.GetBackup(), TRUE ); // // There is an assumption here that the primary stream // is as big as the header says but it was not true in a corrupt // shutdown. // PMmStream & primStrm = _obj.GetMmStream( _iPrim ); LONGLONG llcbMinimum = _hdr.GetCbToSkip(_iPrim) + _hdr.GetUserDataSize(_iPrim); if ( llcbMinimum > primStrm.Size() ) { ciDebugOut((DEB_ERROR, "**** CI MAY HAVE LOST IMPORTANT INFO ***\n" )); ciDebugOut((DEB_ERROR, "**** EMPTYING CI RCOV OBJECT ***** \n" )); PStorage & storage = _obj.GetStorage(); Win4Assert( !"Corrupt catalog" ); storage.ReportCorruptComponent( L"RcovStorageTrans1" ); Win4Assert( !"Recoverable object corrupt" ); THROW (CException( CI_CORRUPT_DATABASE ) ); } CleanupAndSynchronize(); } Win4Assert( _hdr.IsBackupClean() ); Win4Assert( _hdr.GetCbToSkip(_iPrim) == _hdr.GetCbToSkip(_iBack) ); Win4Assert( _hdr.IsBackupClean() ); _hdr.SetRcovOp( op ); // // Open the stream(s) for read/write access as needed. // if ( opRead == op ) { _obj.Open( _hdr.GetPrimary(), FALSE ); _iCurr = _iPrim; } else { // Win4Assert( !obj.IsReadOnly() ); // We can get here if adding a property to the property map when // the catalog is read-only by issuing a query on a property we // haven't seen before. if ( obj.IsReadOnly() ) THROW( CException( E_ACCESSDENIED ) ); _obj.Open( _hdr.GetBackup(), TRUE ); _obj.WriteHeader(); } Win4Assert( _obj.IsOpen(_iCurr) ); Seek( 0 ); END_CONSTRUCTION( CRcovStrmTrans ); } //+--------------------------------------------------------------------------- // // Member: CRcovStrmTrans::CleanupAndSynchronize // // Synopsis: // // Returns: // // Modifies: // // History: 9-12-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::CleanupAndSynchronize() { Win4Assert( !_hdr.IsBackupClean() ); if ( 0 != _hdr.GetCbToSkip(_iBack) ) { // // Get rid of the bytes to be skipped in the front. // EmptyBackupStream(); } Win4Assert( 0 == _hdr.GetCbToSkip(_iBack) ); SetStrmSize( _iBack, _hdr.GetUserDataSize(_iPrim) ); _hdr.SetUserDataSize( _iBack, _hdr.GetUserDataSize(_iPrim) ); _hdr.SetCount( _iBack, _hdr.GetCount(_iPrim) ); CopyToBack( 0, 0, _hdr.GetUserDataSize(_iPrim) ); if ( _hdr.GetCbToSkip(_iPrim) != _hdr.GetCbToSkip(_iBack) ) { Unmap( _iPrim ); _obj.Close( _iPrim ); CommitPh1(); Win4Assert( 0 != _hdr.GetCbToSkip(_iBack) ); EmptyBackupStream(); SetStrmSize( _iBack, _hdr.GetUserDataSize(_iPrim) ); _hdr.SetUserDataSize( _iBack, _hdr.GetUserDataSize(_iPrim) ); CopyToBack( 0, 0, _hdr.GetUserDataSize(_iPrim) ); } Win4Assert( _hdr.GetCbToSkip(_iBack) == _hdr.GetCbToSkip(_iBack) ); Win4Assert( 0 == _hdr.GetCbToSkip(_iBack) ); CommitPh2(); } inline LONGLONG llCommonPageTrunc ( LONGLONG cb ) { return (cb & ~(COMMON_PAGE_SIZE-1)); } inline LONGLONG llCommonPageRound ( LONGLONG cb ) { return ( cb + (COMMON_PAGE_SIZE-1) ) & ~(COMMON_PAGE_SIZE-1); } //+--------------------------------------------------------------------------- // // Member: CRcovStrmTrans::Seek // // Synopsis: Seeks to the specified offset. This is an exported function // the offset is the offset visible to the user, ie, offset // after the "bytes to skip" in the front of the stream. // // Arguments: [offset] - offset into the stream. If offset is ENDOFSTRM, // it will be positioned after the last "user" byte. // // Returns: BOOL - FALSE if seek is to beyond end of data, TRUE otherwise // // History: 9-19-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- BOOL CRcovStrmTrans::Seek( ULONG offset ) { PMmStream & mmStrm = _obj.GetMmStream( _iCurr ); // // Compute the length of the committed part of the stream. // (Excluding the hole in the front). // Win4Assert( mmStrm.Size() >= _hdr.GetCbToSkip(_iCurr) ); ULONG cbStrmMax = _GetCommittedStrmSize(_iCurr); if ( ENDOFSTRM == offset ) { offset = _hdr.GetUserDataSize(_iCurr); } else if ( offset >= _hdr.GetUserDataSize(_iCurr) ) { Win4Assert( 0 == offset || _hdr.GetUserDataSize(_iCurr) == offset ); return FALSE; } // // Add the offset of the starting of user data from the beginning // of the "present but invisible to user" range of bytes. // offset += _hdr.GetUserDataStart(_iCurr); _Seek( offset ); return TRUE; } //+--------------------------------------------------------------------------- // // Function: _Seek // // Synopsis: Seeks the current stream to the specified offset. // If the offset specified is ENDOFSTRM, it will be // positioned after the last valid byte. // // Effects: As long as the current offset is < the full size // of the stream ( not the valid size in the header but // the actual size of the strm ), it will be mapped. // // Arguments: [offset] -- byte offset from the beginning of the // "hole". // // History: 2-02-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::_Seek( ULONG offset ) { Win4Assert( ENDOFSTRM != offset ); Win4Assert( offset >= _hdr.GetUserDataStart(_iCurr) ); Win4Assert( offset <= _hdr.GetFullSize(_iCurr) ); CStrmInfo & si = _aStrmInfo[_iCurr]; PMmStream & mmStrm = _obj.GetMmStream( _iCurr ); // // Compute the length of the committed part of the stream. // (Excluding the hole in the front). // Win4Assert( mmStrm.Size() >= _hdr.GetCbToSkip(_iCurr) ); si._oCurrent = offset; if ( IsMapped(si._oCurrent) || _GetCommittedStrmSize( mmStrm, _iCurr) <= si._oCurrent ) { return; } Win4Assert( _GetCommittedStrmSize( mmStrm, _iCurr) > si._oCurrent ); // // Since the current offset is within the valid range of bytes // and it is not mapped, we have to unmap the currently mapped // range (if any) and map the new range. // CMmStreamBuf & sbuf = _obj.GetMmStreamBuf( _iCurr ); if ( sbuf.Get() ) { mmStrm.Flush( sbuf, sbuf.Size() ); mmStrm.Unmap( sbuf ); } // // We always map on page boundaries. // si._oMapLow = CommonPageTrunc( si._oCurrent ); si._oMapHigh = si._oMapLow + (COMMON_PAGE_SIZE-1); LONGLONG llOffset = _hdr.GetHoleLength(_iCurr) + si._oMapLow; mmStrm.Map( sbuf, COMMON_PAGE_SIZE, lltoLowPart(llOffset), (ULONG) lltoHighPart(llOffset), mmStrm.isWritable() // Map for writing only if the stream // is writable. ); #if CIDBG==1 LONGLONG llHighMap = llOffset+COMMON_PAGE_SIZE-1; Win4Assert( llHighMap < mmStrm.Size() ); #endif // CIDBG==1 Win4Assert( IsMapped( si._oCurrent ) ); } //+--------------------------------------------------------------------------- // // Function: Advance // // Synopsis: Advances the current offset by the specified number of // bytes. // // Arguments: [cb] -- Number of bytes by which to increment the // current position. // // History: 2-02-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::Advance( ULONG cb ) { CStrmInfo & si = _aStrmInfo[_iCurr]; Win4Assert( !IsHighBitSet(si._oCurrent) ); ULONG offset = si._oCurrent + cb; _Seek( offset ); } //+--------------------------------------------------------------------------- // // Function: Read // // Synopsis: Reads the specified number of bytes from the current // stream into the given buffer. // // Effects: The current pointer will be advanced by the number of // bytes read. // // Arguments: [pvBuf] -- Buffer to read the data into. // [cbToRead] -- Number of bytes to read. // // Returns: The number of bytes read. It will not read beyond the // end of valid bytes (end of stream). // // History: 2-02-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- ULONG CRcovStrmTrans::Read( void *pvBuf, ULONG cbToRead ) { CStrmInfo & si = _aStrmInfo[_iCurr]; ULONG cbRead = 0; BYTE * pbDst = (BYTE *) pvBuf; while ( (cbToRead > 0) && (si._oCurrent < _hdr.GetFullSize(_iCurr)) ) { _Seek( si._oCurrent ); ULONG cbMapped = si._oMapHigh - si._oCurrent + 1; ULONG cbCopy = min ( cbToRead, cbMapped ); Win4Assert( cbMapped && cbCopy ); ULONG oStart = si._oCurrent - si._oMapLow; CMmStreamBuf & sbuf = _obj.GetMmStreamBuf( _iCurr ); BYTE * pbSrc = (BYTE *)sbuf.Get() + oStart; memcpy( pbDst, pbSrc, cbCopy ); pbDst += cbCopy; cbToRead -= cbCopy; cbRead += cbCopy; si._oCurrent += cbCopy; } return cbRead; } //+--------------------------------------------------------------------------- // // Function: Write // // Synopsis: Writes the given bytes to the current backup stream // // Effects: The current pointer will be advanced by the number // of bytes written. If necessary, the stream will be // grown to accommodate the given bytes. // // Arguments: [pvBuf] -- Buffer containing the data. // [cbToWrite] -- Number of bytes to write. // // History: 2-02-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::Write( const void *pvBuf, ULONG cbToWrite ) { Win4Assert( cbToWrite && pvBuf || !cbToWrite ); Win4Assert( _iCurr == _iBack ); CStrmInfo & si = _aStrmInfo[_iCurr]; if ( si._oCurrent+ cbToWrite > _hdr.GetFullSize(_iCurr) ) { // // Grow the stream for writing the given bytes. // ULONG cbDelta = (si._oCurrent + cbToWrite) - _hdr.GetFullSize(_iCurr); Grow( _iCurr, cbDelta ); } BYTE * pbSrc = (BYTE *) pvBuf; while (cbToWrite > 0) { Win4Assert( si._oCurrent < _hdr.GetFullSize( _iCurr ) ); _Seek( si._oCurrent ); ULONG cbMapped = si._oMapHigh - si._oCurrent + 1; ULONG cbCopy = min ( cbToWrite, cbMapped ); Win4Assert( cbCopy && cbMapped ); ULONG oStart = si._oCurrent - si._oMapLow; CMmStreamBuf & sbuf = _obj.GetMmStreamBuf( _iCurr ); BYTE * pbDst = (BYTE *)sbuf.Get() + oStart; memcpy( pbDst, pbSrc, cbCopy ); pbSrc += cbCopy; si._oCurrent += cbCopy; cbToWrite -= cbCopy; } } //+--------------------------------------------------------------------------- // // Function: Unmap // // Synopsis: Unmaps the currently mapped region of the specified // stream. // // Effects: _aStrmInfo[nCopy] will be updated to indicate that // nothing is mapped. // // Arguments: [nCopy] -- Stream to unmap. // // History: 2-02-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::Unmap( CRcovStorageHdr::DataCopyNum nCopy ) { CStrmInfo & si = _aStrmInfo[nCopy]; if ( ENDOFSTRM == si._oMapLow ) { Win4Assert( ENDOFSTRM == si._oMapHigh ); return; } PMmStream & mmStrm = _obj.GetMmStream( nCopy ); CMmStreamBuf & sbuf = _obj.GetMmStreamBuf( nCopy ); if ( sbuf.Get() ) { mmStrm.Flush( sbuf, sbuf.Size() ); mmStrm.Unmap( sbuf ); } // // Reset the high and low end points of the mapping info. // si.Reset(); return; } //+--------------------------------------------------------------------------- // // Function: Grow // // Synopsis: Grows the current stream to accommodate "cbDelta" more // bytes in the stream. // // Effects: The valid size entry in the header for the current // stream will be updated to indicate the new size. // // Arguments: [nCopy] -- The stream which needs to be grown. // [cbDelta] -- Number of bytes to grow by. // // History: 2-02-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- inline void CRcovStrmTrans::Grow( CRcovStorageHdr::DataCopyNum nCopy, ULONG cbDelta ) { // // We don't want to do overflow checking or use 64bit arithmetic. // 2GB for a changelog is huge enough! // Win4Assert( !IsHighBitSet( _hdr.GetUserDataSize(nCopy) ) ); ULONG cbNew = _hdr.GetUserDataSize(nCopy) + cbDelta; PMmStream & mmStrm = _obj.GetMmStream(nCopy); Win4Assert( mmStrm.Size() >= _hdr.GetCbToSkip(nCopy) ); ULONG ulCommittedSize = lltoul( mmStrm.Size() - _hdr.GetCbToSkip(nCopy) ); // // Check if the stream is big enough to accommodate the additional // bytes. // if ( ulCommittedSize >= cbNew ) { _hdr.SetUserDataSize(nCopy, cbNew); return; } // // We have to grow the stream also. // SetStrmSize( nCopy, cbNew ); _hdr.SetUserDataSize(nCopy, cbNew ); } //+--------------------------------------------------------------------------- // // Function: SetStrmSize // // Synopsis: Sets the size of the given stream to be the specified // number of bytes. The size will be rounded up to the // nearest COMMON_PAGE_SIZE. However, the stream will // never be reduced below COMMON_PAGE_SIZE, ie, the minimum // size of the stream will be adjusted to be COMMON_PAGE_SIZE. // // Effects: The stream will be unmapped after this operation. // // Arguments: [nCopy] -- The stream whose size must be set. // [cbNew] -- New size of the stream. // // History: 2-03-94 srikants Created // // Notes: This is an internal function and it is assumed that the // necessary transaction protocol is being followed. Also // note that the size in the header is NOT set by this // method. // //---------------------------------------------------------------------------- void CRcovStrmTrans::SetStrmSize( CRcovStorageHdr::DataCopyNum nCopy, ULONG cbNew ) { PMmStream & mmStrm = _obj.GetMmStream( nCopy ); // // Size must be atleast one OFS page in size. // if ( 0 == cbNew ) cbNew = COMMON_PAGE_SIZE; Unmap( nCopy ); LONGLONG llTotalLen = cbNew + _hdr.GetCbToSkip(nCopy); llTotalLen = llCommonPageRound( llTotalLen ); if ( mmStrm.Size() != llTotalLen ) // optimization. { mmStrm.SetSize( _obj.GetStorage(), lltoLowPart(llTotalLen), (ULONG) lltoHighPart(llTotalLen) ); } } //+--------------------------------------------------------------------------- // // Function: EmptyBackupStream // // Synopsis: Empties the contents of the backup stream by setting its // size to "0" (in user space to COMMON_PAGE_SIZE). // // History: 10-18-95 srikants Created // // Notes: When there is a hole in a sparse stream, we have to first // set its size to 0 to remove the sparseness completely. // Otherwise, we will try writing/reading into decommitted // space. // // For example, if there is a 256K hole in a 1M stream, setting // the size of the stream to 64K will leave a 64K hole in the // front. To avoid this, we first set the size to 0 and then // set size to 64K to get committed 64K stream. // //---------------------------------------------------------------------------- void CRcovStrmTrans::EmptyBackupStream() { ciDebugOut(( DEB_ITRACE, "Emptying contents of backup stream\n" )); PMmStream & mmStrm = _obj.GetMmStream( _iBack ); Unmap( _iBack ); // // In use space we cannot have a 0 length stream because mapping // of a zero length stream fails. Also, there are no "holes" in // user space. // ULONG cbLow = COMMON_PAGE_SIZE; mmStrm.SetSize( _obj.GetStorage(), cbLow, 0 ); _hdr.SetUserDataSize(_iBack,0); _hdr.SetCbToSkip(_iBack,0); _obj.WriteHeader(); } //+--------------------------------------------------------------------------- // // Function: CopyToBack // // Synopsis: Copies the specified number of bytes from the current // primary stream to the backup stream. // // Effects: // // Arguments: [oSrc] -- Starting byte offset in the primary where // to start the copying from (Offset from the beginning of the // user data). // [oDst] -- Starting byte offset in the backup where // to copy the data to. (Offset from the beginning of the user // data.) // [cbToCopy] -- Number of bytes to copy. // // History: 2-03-94 srikants Created // // Notes: This does not modify the "valid size" of the backup // stream - it is upto the caller to change it. Also, it // is assumed that the backup stream is big enough to // hold all the data and the primary data is big enough // to give the data. // //---------------------------------------------------------------------------- void CRcovStrmTrans::CopyToBack( ULONG oSrc, ULONG oDst, ULONG cbToCopy ) { // // Transform the offsets to the offset from the beginning of the // hole. // Win4Assert( oSrc != ENDOFSTRM ); Win4Assert( oDst != ENDOFSTRM ); oSrc += _hdr.GetUserDataStart(_iPrim); oDst += _hdr.GetUserDataStart(_iBack); ULONG cbLeft = cbToCopy; const CRcovStorageHdr::DataCopyNum iStartCurr = _iCurr; CStrmInfo & siPrim = _aStrmInfo[_iPrim]; CStrmInfo & siBack = _aStrmInfo[_iBack]; while ( cbLeft > 0 ) { SetCurrentStrm( _iPrim ); _Seek( oSrc ); Win4Assert( IsMapped( oSrc ) ); SetCurrentStrm( _iBack ); _Seek( oDst ); Win4Assert( IsMapped( oDst ) ); PMmStream & mmPrimStrm = _obj.GetMmStream( _iPrim ); PMmStream & mmBackStrm = _obj.GetMmStream( _iBack ); CMmStreamBuf & sPrimBuf = _obj.GetMmStreamBuf( _iPrim ); CMmStreamBuf & sBackBuf = _obj.GetMmStreamBuf( _iBack ); const BYTE *pbSrc = (const BYTE *) sPrimBuf.Get(); pbSrc += ( oSrc - siPrim._oMapLow); BYTE * pbDst = (BYTE *) sBackBuf.Get(); pbDst += ( oDst - siBack._oMapLow); ULONG cbMapRead = siPrim._oMapHigh - oSrc + 1; ULONG cbMapWrite = siBack._oMapHigh - oDst + 1; Win4Assert( cbMapRead && cbMapWrite ); ULONG cbCopy = min( cbLeft, min(cbMapRead, cbMapWrite) ); Win4Assert( cbCopy ); memcpy( pbDst, pbSrc, cbCopy ); oSrc += cbCopy; oDst += cbCopy; cbLeft -= cbCopy; } // // Restore the current index stream to be the same as the // original. // _iCurr = iStartCurr; } //+--------------------------------------------------------------------------- // // Function: CommitPh1 // // Synopsis: Commits the changes to the current backup stream and // makes it the primary. It also opens the new backup // stream in a write mode for making changes to the new backup // stream in the phase 2 of the commit process. // // Effects: The backup stream gets flushed to the disk. // // Arguments: (none) // // History: 2-05-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::CommitPh1() { // // Flush the backup stream. // PMmStream & mmStrm = _obj.GetMmStream( _iBack ); CMmStreamBuf & sbuf = _obj.GetMmStreamBuf( _iBack ); Unmap( _iBack ); // Should unmap before flushing. // // Swap the primary and backup in the header and write // out the header. // _hdr.SwitchPrimary(); _obj.WriteHeader(); Win4Assert( _iPrim == _hdr.GetBackup() ); // // Swap the primary and backup streams and make // _iCurr the new backup. This is to prepare for // the phase 2 of the commit process. // _iCurr = _iPrim; _iPrim = _iBack; _iBack = _iCurr; // // Now open the current backup stream for writing. // _obj.Open( _iBack, TRUE ); } //+--------------------------------------------------------------------------- // // Function: CommitPh2 // // Synopsis: Commits the phase 2 of the transaction. It writes out // the changes to the backup stream. Upon successful // completion, it marks that both streams are clean in // the header and closes the streams. // // Effects: After this returns, the streams are no longer accessible // as they are closed. They should be re-opened for another // transaction. // // Arguments: (none) // // History: 2-10-94 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmTrans::CommitPh2() { // // Close the primary stream and mark that it has been unmapped. // _obj.Close( _iPrim ); // close automatically unmaps if it is // mapped. _aStrmInfo[_iPrim].Reset(); // // Now Flush the backup and close it. // PMmStream & mmStrm = _obj.GetMmStream( _iBack ); CMmStreamBuf & sbuf = _obj.GetMmStreamBuf( _iBack ); Unmap( _iBack ); // Must unmap before flushing. _obj.Close( _iBack ); // // Update the header that the object is now clean. // _hdr.SyncBackup(); _obj.WriteHeader(); } #if 0 void CRcovStrmTrans::Compare() { #if CIDBG == 1 Win4Assert( !_obj.IsOpen( _hdr.GetPrimary() ) ); Win4Assert( !_obj.IsOpen( _hdr.GetBackup() ) ); if( _hdr.GetUserDataSize( _hdr.GetPrimary() ) != _hdr.GetUserDataSize( _hdr.GetBackup() ) ) { ciDebugOut(( DEB_ERROR, "Primary (%u) = 0x%x, Secondary (%u) = 0x%x\n", _hdr.GetPrimary(), _hdr.GetUserDataSize( _hdr.GetPrimary() ), _hdr.GetBackup(), _hdr.GetUserDataSize( _hdr.GetBackup() ) )); Win4Assert( _hdr.GetUserDataSize( _hdr.GetPrimary() ) == _hdr.GetUserDataSize( _hdr.GetBackup() ) ); } _obj.Open( _hdr.GetPrimary(), FALSE ); _obj.Open( _hdr.GetBackup(), FALSE ); ULONG oSrc = _hdr.GetUserDataStart( _hdr.GetPrimary() ); ULONG oDst = _hdr.GetUserDataStart( _hdr.GetBackup() ); if ( oSrc != oDst ) { ciDebugOut(( DEB_ERROR, "Primary (%u) = 0x%x, Secondary (%u) = 0x%x\n", _hdr.GetPrimary(), oSrc, _hdr.GetBackup(), oDst )); Win4Assert( oSrc == oDst ); } if ( _hdr.GetUserDataSize( _hdr.GetPrimary() ) != _hdr.GetUserDataSize( _hdr.GetBackup() ) ) { ciDebugOut(( DEB_ERROR, "Primary (%u) = 0x%x, Secondary (%u) = 0x%x\n", _hdr.GetPrimary(), _hdr.GetUserDataSize( _hdr.GetPrimary() ), _hdr.GetBackup(), _hdr.GetUserDataSize( _hdr.GetBackup() ) )); Win4Assert( _hdr.GetUserDataSize( _hdr.GetPrimary() ) == _hdr.GetUserDataSize( _hdr.GetBackup() ) ); } ULONG cbLeft = _hdr.GetUserDataSize( _hdr.GetPrimary() ); CStrmInfo & siPrim = _aStrmInfo[_hdr.GetPrimary()]; CStrmInfo & siBack = _aStrmInfo[_hdr.GetBackup()]; while ( cbLeft > 0 ) { SetCurrentStrm( _hdr.GetPrimary() ); _Seek( oSrc ); Win4Assert( IsMapped( oSrc ) ); SetCurrentStrm( _hdr.GetBackup() ); _Seek( oDst ); Win4Assert( IsMapped( oDst ) ); PMmStream & mmPrimStrm = _obj.GetMmStream( _hdr.GetPrimary() ); PMmStream & mmBackStrm = _obj.GetMmStream( _hdr.GetBackup() ); CMmStreamBuf & sPrimBuf = _obj.GetMmStreamBuf( _hdr.GetPrimary() ); CMmStreamBuf & sBackBuf = _obj.GetMmStreamBuf( _hdr.GetBackup() ); const BYTE *pbSrc = (const BYTE *) sPrimBuf.Get(); pbSrc += ( oSrc - siPrim._oMapLow); BYTE * pbDst = (BYTE *) sBackBuf.Get(); pbDst += ( oDst - siBack._oMapLow); ULONG cbMapRead = siPrim._oMapHigh - oSrc + 1; ULONG cbMapWrite = siBack._oMapHigh - oDst + 1; Win4Assert( cbMapRead && cbMapWrite ); ULONG cbCopy = min( cbLeft, min(cbMapRead, cbMapWrite) ); Win4Assert( cbCopy ); if ( 0 != memcmp( pbDst, pbSrc, cbCopy ) ) { ciDebugOut(( DEB_ERROR, "CRcovStrmTrans::Compare -- mismatch\n" )); ciDebugOut(( DEB_ERROR | DEB_NOCOMPNAME, " Primary (%u) offset 0x%x, pb = 0x%x\n", _hdr.GetPrimary(), oSrc, pbSrc )); ciDebugOut(( DEB_ERROR | DEB_NOCOMPNAME, " Backup (%u) offset 0x%x, pb = 0x%x\n", _hdr.GetBackup(), oDst, pbDst )); Win4Assert( !"CRcovStrmTrans::Compare -- mismatch" ); } oSrc += cbCopy; oDst += cbCopy; cbLeft -= cbCopy; } _obj.Close( _hdr.GetPrimary() ); _obj.Close( _hdr.GetBackup() ); #endif // CIDBG } #endif void CRcovStrmWriteTrans::Commit() { Win4Assert( XActAbort == CTransaction::GetStatus() ); CommitPh1(); // // After phase1 is successfully committed, the transaction will be // completed by making "forward progress". So, we should not throw // now even if the phase2 fails. Even if phase2 fails now, it will // be eventually completed later when a new transaction is created // for this object. // TRY { // // Copy the entire contents from the new primary to the // new backup. // SetStrmSize( _iBack, GetStorageHdr().GetUserDataSize(_iPrim) ); GetStorageHdr().SetUserDataSize( _iBack, GetStorageHdr().GetUserDataSize(_iPrim) ); CopyToBack( 0, 0, GetStorageHdr().GetUserDataSize(_iPrim)); CommitPh2(); Win4Assert( GetStorageHdr().IsBackupClean() ); } CATCH( CException, e ) { GetRcovObj().Close( _iPrim ); GetRcovObj().Close( _iBack ); ciDebugOut(( DEB_ERROR, "Phase2 of CRcomStrmWriteTrans failed 0x%X\n", e.GetErrorCode() )); } END_CATCH CRcovStrmTrans::Commit(); } void CRcovStrmWriteTrans::Empty() { Win4Assert( _iCurr == _iBack ); SetStrmSize( _iBack, 0 ); GetStorageHdr().SetUserDataSize( _iBack, 0 ); Seek(0); } CRcovStrmAppendTrans::CRcovStrmAppendTrans( PRcovStorageObj & obj ) : CRcovStrmTrans( obj, opAppend ) { // // Seek to end of file. // Seek( ENDOFSTRM ); END_CONSTRUCTION( CRcovStrmAppendTrans ); } void CRcovStrmAppendTrans::Commit() { Win4Assert( XActAbort == CTransaction::GetStatus() ); Win4Assert( GetStorageHdr().GetUserDataSize(_iPrim) <= GetStorageHdr().GetUserDataSize(_iBack) ); CTransaction::_status = XActAbort; CommitPh1(); // // After phase1 is successfully committed, the transaction will be // completed by making "forward progress". So, we should not throw // now even if the phase2 fails. Even if phase2 fails now, it will // be eventually completed later when a new transaction is created // for this object. // TRY { // // Now copy the contents of the primary to the backup // from the point where append started. // ULONG cbToCopy = GetStorageHdr().GetUserDataSize(_iPrim) - GetStorageHdr().GetUserDataSize(_iBack); ULONG offset = GetStorageHdr().GetUserDataSize(_iBack); ciFAILTEST( STATUS_DISK_FULL ); Grow(_iBack, cbToCopy); ciFAILTEST( STATUS_DISK_FULL ); CopyToBack( offset, offset, cbToCopy ); ciFAILTEST( STATUS_DISK_FULL ); CommitPh2(); ciFAILTEST( STATUS_DISK_FULL ); } CATCH ( CException, e ) { GetRcovObj().Close( _iPrim ); GetRcovObj().Close( _iBack ); ciDebugOut(( DEB_ERROR, "Phase2 of CRcomStrmAppendTrans failed 0x%X\n", e.GetErrorCode() )); } END_CATCH CRcovStrmTrans::Commit(); } CRcovStrmMDTrans::CRcovStrmMDTrans( PRcovStorageObj & obj, MDOp op, ULONG cb ) : CRcovStrmTrans( obj, opMetaData ), _op(op), _cbOp(cb) { END_CONSTRUCTION( CRcovStrmMDTrans ); } void CRcovStrmMDTrans::Commit() { Win4Assert( XActAbort == CTransaction::GetStatus() ); switch ( _op ) { case mdopSetSize: SetSize( _cbOp ); break; case mdopGrow: Grow( _cbOp ); break; case mdopFrontShrink: ShrinkFromFront( _cbOp ); break; case mdopBackCompact: CompactFromEnd( _cbOp ); break; default: Win4Assert( ! "Control Should Not Come Here" ); break; } CRcovStrmTrans::Commit(); } void CRcovStrmMDTrans::SetSize( ULONG cbNew ) { // // set the size on the backup first. // Win4Assert( _iCurr == _iBack ); CRcovStrmTrans::SetStrmSize( _iBack, cbNew ); GetStorageHdr().SetUserDataSize( _iBack, cbNew ); // // Do the phase1 commit now. // CommitPh1(); // // After phase1 is successfully committed, the transaction will be // completed by making "forward progress". So, we should not throw // now even if the phase2 fails. Even if phase2 fails now, it will // be eventually completed later when a new transaction is created // for this object. // TRY { // // Apply the same operation on the new backup. // Win4Assert( _iCurr == _iBack ); CRcovStrmTrans::SetStrmSize( _iBack, cbNew ); CommitPh2(); Win4Assert( GetStorageHdr().IsBackupClean() ); } CATCH( CException,e ) { GetRcovObj().Close( _iPrim ); GetRcovObj().Close( _iBack ); ciDebugOut(( DEB_ERROR, "Phase2 of CRcovStrmMDTrans failed 0x%X\n", e.GetErrorCode() )); } END_CATCH } void CRcovStrmMDTrans::Grow( ULONG cbDelta ) { const ULONG cbNew = GetStorageHdr().GetUserDataSize(_iCurr)+cbDelta; CRcovStrmMDTrans::SetSize( cbNew ); } void CRcovStrmMDTrans::CompactFromEnd( ULONG cbDelta ) { Win4Assert( cbDelta <= GetStorageHdr().GetUserDataSize( _iCurr ) ); Win4Assert( _iCurr == _iBack ); cbDelta = min (cbDelta, GetStorageHdr().GetUserDataSize( _iCurr )); const ULONG cbNew = GetStorageHdr().GetUserDataSize(_iCurr) - cbDelta; CRcovStrmMDTrans::SetSize( cbNew ); } //+--------------------------------------------------------------------------- // // Member: CRcovStrmMDTrans::IncreaseBytesToSkip // // Synopsis: Increases the number of bytes to be "skipped" in the front // by the specified amount. // // Arguments: [cbDelta] - Number of additional bytes to skip. // // History: 9-19-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmMDTrans::IncreaseBytesToSkip( ULONG cbDelta ) { CRcovStorageHdr & hdr = GetStorageHdr(); // // Update the hole length and the valid data length. // Win4Assert( hdr.GetUserDataSize(_iBack) >= cbDelta ); const ULONG cbNewValid = hdr.GetUserDataSize(_iPrim) - cbDelta; hdr.SetUserDataSize( _iBack, cbNewValid ); Win4Assert( hdr.GetCbToSkip(_iPrim) == hdr.GetCbToSkip(_iBack) ); const LONGLONG llcbNewSkip = hdr.GetCbToSkip(_iBack)+cbDelta; hdr.SetCbToSkip( _iBack, llcbNewSkip ); // Phase1 complete - just set the values for the backup. hdr.SwitchPrimary(); Win4Assert( _iPrim == hdr.GetBackup() ); _iCurr = _iPrim; _iPrim = _iBack; _iBack = _iCurr; // phase 2 complete. Commit the changes hdr.SyncBackup(); GetRcovObj().WriteHeader(); } //+--------------------------------------------------------------------------- // // Member: CRcovStrmMDTrans::CopyShrinkFromFront // // Synopsis: Implements a shrink from front by copying bytes. // // Arguments: [cbNew] - Number of bytes that will be in the stream after // doing a shrink from front. // [cbDelta] - Number of bytes to shrink in the front. // // History: 10-02-95 srikants Created ( Moved from ShrinkFromFront() // methoed ) // // Notes: // //---------------------------------------------------------------------------- void CRcovStrmMDTrans::CopyShrinkFromFront( ULONG cbNew, ULONG cbDelta ) { CRcovStorageHdr & hdr = GetStorageHdr(); ULONG oDst = 0; ULONG oSrc = cbDelta; // this check is an optimization if ( hdr.GetHoleLength(_iBack) > SHRINK_FROM_FRONT_PAGE_SIZE ) { // // If the hole in front is <= SHRINK_FROM_FRONT_PAGE_SIZE, then // this there is no "sparseness" in the front. // EmptyBackupStream(); } // // Open the Primary in a read-only mode and close it before // calling CommitPh1(). // Copy the contents from oSrc in the primary to oDst in the // backup stream. // hdr.SetCbToSkip(_iBack,0); CRcovStrmTrans::SetStrmSize( _iBack, cbNew ); hdr.SetUserDataSize( _iBack, cbNew ); GetRcovObj().Open( _iPrim, FALSE ); CopyToBack( oSrc, oDst, cbNew ); Unmap( _iPrim ); GetRcovObj().Close( _iPrim ); // // Commit the phase1 changes now. // CommitPh1(); // // After phase1 is successfully committed, the transaction will be // completed by making "forward progress". So, we should not throw // now even if the phase2 fails. Even if phase2 fails now, it will // be eventually completed later when a new transaction is created // for this object. // TRY { // // We have to synchronize the new backup with the new primary. // Win4Assert( _iCurr == _iBack ); // this check is an optimization if ( hdr.GetHoleLength(_iBack) > SHRINK_FROM_FRONT_PAGE_SIZE ) { // // If the hole in front is <= SHRINK_FROM_FRONT_PAGE_SIZE, then // this there is no "sparseness" in the front. // EmptyBackupStream(); } hdr.SetCbToSkip(_iBack,0); CRcovStrmTrans::SetStrmSize( _iBack, cbNew ); CopyToBack( 0, 0, cbNew ); hdr.SetUserDataSize( _iBack, cbNew ); CommitPh2(); Win4Assert( hdr.IsBackupClean() ); } CATCH( CException, e ) { GetRcovObj().Close( _iPrim ); GetRcovObj().Close( _iBack ); ciDebugOut(( DEB_ERROR, "Phase2 of ShrinkFromFront() failed 0x%X\n", e.GetErrorCode() )); } END_CATCH } void CRcovStrmMDTrans::ShrinkFromFront( ULONG cbDelta ) { CRcovStorageHdr & hdr = GetStorageHdr(); Win4Assert( cbDelta <= hdr.GetUserDataSize( _iCurr ) ); Win4Assert( _iCurr == _iBack ); cbDelta = min (cbDelta, hdr.GetUserDataSize( _iCurr )); Win4Assert( hdr.GetCbToSkip(_iCurr) >= hdr.GetHoleLength(_iCurr) ); const ULONG cbCommittedSkip = lltoul( hdr.GetCbToSkip(_iCurr) - hdr.GetHoleLength(_iCurr) ); // // If the total number of bytes "present but invisible" to user is // < the threshold, don't do any copying or shrinking. Just increment // the bytest to skip and return. // if ( cbCommittedSkip + cbDelta < SHRINK_FROM_FRONT_PAGE_SIZE ) { IncreaseBytesToSkip( cbDelta ); return; } const ULONG cbNew = hdr.GetUserDataSize(_iCurr) - cbDelta; CopyShrinkFromFront( cbNew, cbDelta ); } //+--------------------------------------------------------------------------- // // Member: CCopyRcovObject::_SetDstSize // // Synopsis: Sets the size of the destination object to be same as // the source object. // // History: 3-17-97 srikants Created // //---------------------------------------------------------------------------- void CCopyRcovObject::_SetDstSize() { CRcovStrmMDTrans trans( _dst, CRcovStrmMDTrans::mdopSetSize, _cbSrc ); trans.Commit(); } //+--------------------------------------------------------------------------- // // Member: CCopyRcovObject::_CopyData // // Synopsis: Copies the data from the source object to the destination // object. // // History: 3-17-97 srikants Created // //---------------------------------------------------------------------------- void CCopyRcovObject::_CopyData() { const cbPageSize = 4096; // Read and write 4k at a time. XArray xByte( cbPageSize ); // // Start a read transaction on the source object // CRcovStrmReadTrans srcTrans( _src ); // // Start a write transaction on the destination. // CRcovStrmWriteTrans dstTrans( _dst ); dstTrans.Empty(); dstTrans.Seek(0); ULONG cbRemaining = _cbSrc; while ( cbRemaining > 0 ) { ULONG cbToCopy = min( cbRemaining, cbPageSize ); srcTrans.Read( xByte.GetPointer(), cbToCopy ); dstTrans.Write( xByte.GetPointer(), cbToCopy ); cbRemaining -= cbToCopy; } // // Set the user header. // CRcovUserHdr usrHdr; _srcHdr.GetUserHdr( _srcHdr.GetPrimary(), usrHdr ); _dstHdr.SetUserHdr( _dstHdr.GetBackup(), usrHdr ); // // Set the number of records. // ULONG nRec = _srcHdr.GetCount( _srcHdr.GetPrimary() ); _dstHdr.SetCount( _dstHdr.GetBackup(), nRec ); // // Commit the transaction. // dstTrans.Commit(); } //+--------------------------------------------------------------------------- // // Member: CCopyRcovObject::DoIt // // Synopsis: Copies the contents of one object to another object. // // Returns: STATUS_SUCCESS if successful; // Other error code if there is an error. // // History: 3-17-97 srikants Created // //---------------------------------------------------------------------------- NTSTATUS CCopyRcovObject::DoIt() { NTSTATUS status = STATUS_SUCCESS; TRY { _SetDstSize(); _CopyData(); } CATCH( CException, e ) { status = e.GetErrorCode(); ciDebugOut(( DEB_ERROR, "CCopyRcovObject::DoIt. Error 0x%X\n", status )); } END_CATCH return status; }