1469 lines
38 KiB
C++
1469 lines
38 KiB
C++
|
|
//+============================================================================
|
|
//
|
|
// File: nffmstm.cxx
|
|
//
|
|
// This file provides the NFF (NTFS Flat File) IMappedStream implementation.
|
|
//
|
|
// History:
|
|
// 5/6/98 MikeHill
|
|
// - Misc dbg cleanup.
|
|
//
|
|
//+============================================================================
|
|
|
|
#include <pch.cxx>
|
|
|
|
|
|
|
|
CNFFMappedStream::~CNFFMappedStream()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// If the update stream has the latest data, rename it over the original
|
|
// stream. Ordinarily this replace call will create a new update stream.
|
|
// But since we're going away, tell it not to bother.
|
|
// Errors are ignored here because there's no way to return them.
|
|
// If the caller wishes to avoid this, they should call Flush first.
|
|
|
|
if( NULL != _pstmUpdate )
|
|
{
|
|
ReplaceOriginalWithUpdate( DONT_CREATE_NEW_UPDATE_STREAM );
|
|
DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
|
|
}
|
|
|
|
|
|
// Just to be safe, free the mapping buffer (it should have
|
|
// already been freed).
|
|
|
|
DfpAssert( NULL == _pbMappedStream );
|
|
CoTaskMemFree( _pbMappedStream );
|
|
|
|
// If we've got the global reserved buffer locked,
|
|
// free it now.
|
|
|
|
if (_fLowMem)
|
|
g_ReservedMemory.UnlockMemory();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
CNFFMappedStream::QueryInterface( REFIID riid, void**ppvObject )
|
|
{
|
|
return( _pnffstm->QueryInterface( riid, ppvObject ));
|
|
}
|
|
|
|
ULONG
|
|
CNFFMappedStream::AddRef()
|
|
{
|
|
return( _pnffstm->AddRef() );
|
|
}
|
|
|
|
ULONG
|
|
CNFFMappedStream::Release()
|
|
{
|
|
return( _pnffstm->Release() );
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNFFMappedStream::Open (IMappedStream)
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
CNFFMappedStream::Open( IN VOID *powner, OUT LONG *phr )
|
|
{
|
|
nffITrace( "CNFFMappedStream::Open" );
|
|
VOID *pv = NULL;
|
|
HRESULT sc=S_OK;
|
|
|
|
BOOL fUsingLatestStream = FALSE;
|
|
|
|
DfpAssert(!_fLowMem);
|
|
|
|
_pnffstm->Lock( INFINITE );
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
// If the previous open crashed during a flush, roll forward to the
|
|
// updated copy. If we're only open for read access, then this will
|
|
// just set _fUpdateStreamHasLatest so that we'll know to process
|
|
// reads from that stream.
|
|
|
|
nffChk( RollForwardIfNecessary() );
|
|
|
|
BeginUsingLatestStream();
|
|
fUsingLatestStream = TRUE;
|
|
|
|
// If given a pointer to the owner of this mapped stream,
|
|
// save it. This could be NULL (i.e., when called from
|
|
// ReOpen).
|
|
|
|
if( NULL != powner )
|
|
_pMappedStreamOwner = powner;
|
|
|
|
// If we haven't already read the stream, read it now.
|
|
|
|
if( NULL == _pbMappedStream )
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION fileinfo;
|
|
|
|
DfpAssert( INVALID_HANDLE_VALUE != _pnffstm->GetFileHandle() );
|
|
DfpAssert( 0 == _cbMappedStream );
|
|
DfpAssert( 0 == _cbMappedStreamActual);
|
|
|
|
// Get and validate the size of the file
|
|
|
|
if( !GetFileInformationByHandle( _pnffstm->GetFileHandle(), &fileinfo ))
|
|
{
|
|
nffErr( EH_Err, LAST_SCODE );
|
|
}
|
|
else if( 0 != fileinfo.nFileSizeHigh
|
|
|| CBMAXPROPSETSTREAM < fileinfo.nFileSizeLow )
|
|
{
|
|
nffErr( EH_Err, STG_E_INVALIDHEADER );
|
|
}
|
|
|
|
_cbMappedStream = _cbMappedStreamActual = fileinfo.nFileSizeLow;
|
|
|
|
// Allocate a buffer to hold the Stream. If there isn't sufficient
|
|
// memory in the system, lock and get the reserved buffer. In the
|
|
// end, 'pv' points to the appropriate buffer.
|
|
|
|
#if DBG
|
|
pv = _fSimulateLowMem ? NULL : CoTaskMemAlloc( _cbMappedStreamActual );
|
|
#else
|
|
pv = CoTaskMemAlloc( _cbMappedStreamActual );
|
|
#endif
|
|
|
|
if( NULL == pv )
|
|
{
|
|
// could block until previous property call completes
|
|
pv = g_ReservedMemory.LockMemory();
|
|
|
|
if( NULL == pv )
|
|
nffErr( EH_Err, E_OUTOFMEMORY );
|
|
|
|
_fLowMem = TRUE;
|
|
}
|
|
_pbMappedStream = (BYTE*) pv;
|
|
|
|
// Read in the file.
|
|
|
|
if( 0 != _cbMappedStreamActual )
|
|
{
|
|
ULARGE_INTEGER ulOffset;
|
|
ulOffset.QuadPart = 0;
|
|
|
|
if( FAILED(_pnffstm->SyncReadAtFile( ulOffset, _pbMappedStream,
|
|
_cbMappedStreamActual, &_cbMappedStream)))
|
|
{
|
|
nffErr( EH_Err, LAST_SCODE );
|
|
}
|
|
|
|
|
|
// Ensure that we got all the bytes we requested.
|
|
|
|
if( _cbMappedStream != _cbMappedStreamActual )
|
|
{
|
|
propDbg((DEBTRACE_ERROR,
|
|
"CMappedStreamOnHFile(%08X)::Open bytes-read (%lu) doesn't match bytes-requested (%lu)\n",
|
|
this, _cbMappedStream, _cbMappedStreamActual ));
|
|
nffErr( EH_Err, STG_E_INVALIDHEADER );
|
|
}
|
|
}
|
|
|
|
|
|
#if BIGENDIAN==1
|
|
// Notify our owner that we've read in new data.
|
|
|
|
if( _pMappedStreamOwner != NULL && 0 != _cbMappedStream )
|
|
{
|
|
nffChk( PrOnMappedStreamEvent( _pMappedStreamOwner, _pbMappedStream, _cbMappedStream ) );
|
|
}
|
|
#endif
|
|
|
|
} // if( NULL == _pbMappedStream )
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
EH_Err:
|
|
|
|
if( fUsingLatestStream )
|
|
EndUsingLatestStream();
|
|
|
|
// If there was an error, free any memory we have.
|
|
|
|
if( FAILED(sc) )
|
|
{
|
|
propDbg((DEB_ERROR, "IMappedStream::CNtfsStream(%08X)::Open exception returns %08X\n", this, *phr));
|
|
|
|
if (_fLowMem)
|
|
g_ReservedMemory.UnlockMemory();
|
|
else
|
|
CoTaskMemFree(pv);
|
|
|
|
_pbMappedStream = NULL;
|
|
_cbMappedStream = _cbMappedStreamActual = 0;
|
|
_fLowMem = FALSE;
|
|
}
|
|
|
|
_pnffstm->Unlock();
|
|
*phr = sc;
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::Flush (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID CNFFMappedStream::Flush(OUT LONG *phr)
|
|
{
|
|
nffITrace( "CNFFMappedStream::Flush" );
|
|
HRESULT sc=S_OK;
|
|
BOOL fUsingLatestStream = FALSE;
|
|
|
|
_pnffstm->Lock( INFINITE );;
|
|
|
|
BeginUsingLatestStream();
|
|
fUsingLatestStream = TRUE;
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
if( !IsWriteable() )
|
|
nffErr( EH_Err, STG_E_ACCESSDENIED );
|
|
|
|
|
|
// If the IMappedStream is being used, write it out to the
|
|
// underlying file.
|
|
|
|
if( NULL != _pbMappedStream )
|
|
nffChk( WriteMappedStream() );
|
|
|
|
// Commit the Stream.
|
|
if( !FlushFileBuffers( _pnffstm->GetFileHandle() ))
|
|
nffErr( EH_Err, LAST_SCODE );
|
|
|
|
EndUsingLatestStream();
|
|
fUsingLatestStream = FALSE;
|
|
|
|
nffChk( ReplaceOriginalWithUpdate( CREATE_NEW_UPDATE_STREAM ));
|
|
|
|
sc = S_OK;
|
|
|
|
EH_Err:
|
|
|
|
if( fUsingLatestStream )
|
|
EndUsingLatestStream();
|
|
|
|
_pnffstm->Unlock();
|
|
*phr = sc;
|
|
return;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: IMappedStream::Close
|
|
//
|
|
// Synopsis: Close the mapped stream by writing out
|
|
// the mapping buffer and then freeing it.
|
|
// Errors are ignored, so if the caller wants an
|
|
// opportunity to recover from an error, they should
|
|
// call Flush before calling Close.
|
|
//
|
|
// Arguments: [LONG*] phr
|
|
// An HRESULT error code.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID CNFFMappedStream::Close(OUT LONG *phr)
|
|
{
|
|
nffITrace( "CNFFMappedStream::Close" );
|
|
HRESULT sc=S_OK;
|
|
|
|
_pnffstm->Lock( INFINITE );
|
|
|
|
// So watch out for multiple closes.
|
|
sc = _pnffstm->CheckReverted();
|
|
|
|
// If we are already closed then return immediatly (but don't error)
|
|
if( STG_E_REVERTED == sc )
|
|
{
|
|
sc = S_OK;
|
|
goto EH_Err;
|
|
}
|
|
|
|
// Report any real errors.
|
|
if( FAILED( sc ) )
|
|
nffErr( EH_Err, sc );
|
|
|
|
// Write the changes. We don't need to Commit them,
|
|
// they will be implicitely committed when the
|
|
// Stream is Released.
|
|
|
|
sc = WriteMappedStream();
|
|
|
|
// Even if we fail the write, we must free the memory.
|
|
// (PrClosePropertySet deletes everything whether or not
|
|
// there was an error here, so we must free the memory.
|
|
// There's no danger of this happenning due to out-of-
|
|
// disk-space conditions, because the propset code
|
|
// pre-allocates).
|
|
|
|
CoTaskMemFree( _pbMappedStream );
|
|
_pbMappedStream = NULL;
|
|
|
|
// Re-zero the member data.
|
|
InitMappedStreamMembers();
|
|
|
|
sc = S_OK;
|
|
|
|
EH_Err:
|
|
|
|
_pnffstm->Unlock();
|
|
*phr = sc;
|
|
return;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::ReOpen (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CNFFMappedStream::ReOpen(IN OUT VOID **ppv, OUT LONG *phr)
|
|
{
|
|
nffITrace( "CNFFMappedStream::ReOpen" );
|
|
HRESULT sc=S_OK;
|
|
|
|
*ppv = NULL;
|
|
|
|
_pnffstm->Lock( INFINITE );;
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
this->Open(NULL, &sc);
|
|
nffChk(sc);
|
|
|
|
*ppv = _pbMappedStream;
|
|
|
|
EH_Err:
|
|
_pnffstm->Unlock();
|
|
*phr = sc;
|
|
return;
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::Quiesce (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID CNFFMappedStream::Quiesce(VOID)
|
|
{
|
|
nffITrace( "CNFFMappedStream::Quiesce" );
|
|
// Not necessary for this implemented
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::Map (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CNFFMappedStream::Map(IN BOOLEAN fCreate, OUT VOID **ppv)
|
|
{
|
|
nffITrace( "CNFFMappedStream::Map" );
|
|
HRESULT sc;
|
|
|
|
_pnffstm->Lock( INFINITE );;
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
DfpAssert(_pbMappedStream != NULL);
|
|
*ppv = _pbMappedStream;
|
|
|
|
EH_Err:
|
|
_pnffstm->Unlock();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::Unmap (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CNFFMappedStream::Unmap(BOOLEAN fFlush, VOID **ppv)
|
|
{
|
|
nffITrace( "CNFFMappedStream::Unmap" );
|
|
|
|
*ppv = NULL;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::WriteMappedStream (internal support for IMappedStream)
|
|
//
|
|
// Returns: S_OK if successful, S_FALSE if there was nothing to write.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
#define STACK_BYTES 16
|
|
|
|
HRESULT
|
|
CNFFMappedStream::WriteMappedStream()
|
|
{
|
|
nffITrace( "CNFFMappedStream::WriteMappedStream" );
|
|
HRESULT sc = S_OK;
|
|
ULONG cbWritten;
|
|
BOOL fOwnerSignaled = FALSE;
|
|
BOOL fUsingUpdateStream = FALSE;
|
|
|
|
// We can return right away if there's nothing to write.
|
|
// (_pbMappedStream may be NULL in the error path of our
|
|
// caller).
|
|
|
|
if (!IsModified() || NULL == _pbMappedStream )
|
|
{
|
|
propDbg((DEB_TRACE, "IMappedStream::CNtfsStream(%08X)::Flush returns with not-dirty\n", this));
|
|
return S_FALSE;
|
|
}
|
|
|
|
// Put the update stream's handle into _pnffstm, so that we write out to it.
|
|
|
|
BeginUsingUpdateStream();
|
|
fUsingUpdateStream = TRUE;
|
|
|
|
DfpAssert( INVALID_HANDLE_VALUE != _pnffstm->GetFileHandle() );
|
|
|
|
#if BIGENDIAN==1
|
|
// Notify our owner that we're about to perform a Write.
|
|
nffChk( PrOnMappedStreamEvent( _powner, _pbMappedStream, _cbMappedStream ) );
|
|
fOwnerSignaled = TRUE;
|
|
#endif
|
|
|
|
// Write out the mapping buffer (to the update stream).
|
|
|
|
ULARGE_INTEGER ulOffset;
|
|
ulOffset.QuadPart = 0;
|
|
|
|
nffChk( _pnffstm->SyncWriteAtFile( ulOffset, _pbMappedStream,
|
|
_cbMappedStream, &cbWritten ));
|
|
|
|
if( cbWritten != _cbMappedStream )
|
|
{
|
|
propDbg((DEB_ERROR,
|
|
"CMappedStreamOnHFile(%08X)::Write bytes-written (%lu) doesn't match bytes-requested (%lu)\n",
|
|
this, cbWritten, _cbMappedStream ));
|
|
sc = STG_E_INVALIDHEADER;
|
|
goto EH_Err;
|
|
}
|
|
|
|
// If the buffer is shrinking, this is a good time to shrink the file.
|
|
if (_cbMappedStream < _cbMappedStreamActual)
|
|
{
|
|
nffChk( _pnffstm->SetSize( static_cast<CULargeInteger>(_cbMappedStream) ) );
|
|
_cbMappedStreamActual = _cbMappedStream;
|
|
}
|
|
|
|
if( _fStreamRenameSupported )
|
|
{
|
|
// We wrote the data to the update stream. So flag that it now
|
|
// has the latest data.
|
|
|
|
_fUpdateStreamHasLatest = TRUE;
|
|
DfpAssert( NULL != _pstmUpdate && INVALID_HANDLE_VALUE != _pstmUpdate->GetFileHandle() );
|
|
}
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
EH_Err:
|
|
|
|
#if BIGENDIAN==1
|
|
// Notify our owner that we're done with the Write. We do this
|
|
// whether or not there was an error, because _pbMappedStream is
|
|
// not modified, and therefore intact even in the error path.
|
|
|
|
if( fOwnerSignaled )
|
|
{
|
|
DfpVerify( PrOnMappedStreamEvent( _powner,
|
|
_pbMappedStream, _cbMappedStream ) );
|
|
}
|
|
#endif
|
|
|
|
if( fUsingUpdateStream )
|
|
EndUsingUpdateStream();
|
|
|
|
if (sc == S_OK || sc == STG_E_REVERTED)
|
|
{
|
|
_fMappedStreamDirty = FALSE;
|
|
}
|
|
|
|
propDbg(( DbgFlag(sc,DEB_ITRACE), "CNtfsStream(%08X)::Write %s returns hr=%08X\n",
|
|
this, sc != S_OK ? "exception" : "", sc));
|
|
|
|
return sc;
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::GetSize (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
ULONG CNFFMappedStream::GetSize(OUT LONG *phr)
|
|
{
|
|
nffITrace( "CNFFMappedStream::GetSize" );
|
|
HRESULT sc=S_OK;
|
|
|
|
_pnffstm->Lock( INFINITE );;
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
// If necessary, open the Stream.
|
|
|
|
if( NULL == _pbMappedStream )
|
|
{
|
|
this->Open(NULL, &sc);
|
|
}
|
|
|
|
if( SUCCEEDED(sc) )
|
|
{
|
|
DfpAssert( NULL != _pbMappedStream );
|
|
}
|
|
|
|
// Return the size of the mapped stream. If there was an
|
|
// Open error, it will be zero, and *phr will be set.
|
|
|
|
EH_Err:
|
|
_pnffstm->Unlock();
|
|
*phr = sc;
|
|
|
|
return _cbMappedStream;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::InitMappedStreamMembers
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
void
|
|
CNFFMappedStream::InitMappedStreamMembers()
|
|
{
|
|
nffITrace( "CNFFMappedStream::InitMappedStreamMembers" );
|
|
|
|
_pbMappedStream = NULL;
|
|
_cbMappedStream = 0;
|
|
_cbMappedStreamActual = 0;
|
|
_pMappedStreamOwner = NULL;
|
|
_fLowMem = FALSE;
|
|
_fMappedStreamDirty = FALSE;
|
|
|
|
_fCheckedForRollForward = FALSE;
|
|
_fStreamRenameSupported = FALSE;
|
|
|
|
_cUpdateStreamInUse = _cLatestStreamInUse = 0;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::SetSize (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CNFFMappedStream::SetSize(IN ULONG cb,
|
|
IN BOOLEAN fPersistent,
|
|
IN OUT VOID **ppv, OUT LONG *phr)
|
|
{
|
|
nffITrace( "CNFFMappedStream::SetSize" );
|
|
BYTE *pv;
|
|
|
|
HRESULT &sc = *phr;
|
|
BOOL fUsingUpdateStream = FALSE, fUsingLatestStream = FALSE;
|
|
|
|
DfpAssert(cb != 0);
|
|
|
|
sc = S_OK;
|
|
|
|
_pnffstm->Lock( INFINITE );;
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
if( CBMAXPROPSETSTREAM < cb )
|
|
nffErr( EH_Err, STG_E_MEDIUMFULL );
|
|
|
|
if( fPersistent )
|
|
{
|
|
nffChk( CreateUpdateStreamIfNecessary() );
|
|
BeginUsingUpdateStream();
|
|
fUsingUpdateStream = TRUE;
|
|
}
|
|
else
|
|
{
|
|
BeginUsingLatestStream();
|
|
fUsingLatestStream = TRUE;
|
|
}
|
|
|
|
// if we are growing the data, we should grow the file
|
|
|
|
if( fPersistent && cb > _cbMappedStreamActual )
|
|
{
|
|
nffChk( _pnffstm->SetFileSize( CULargeInteger(cb) ) );
|
|
_cbMappedStreamActual = cb;
|
|
}
|
|
|
|
// We only get here if we either (1) didn't want to grow the
|
|
// underlying stream, or (2) we successfully grew the underlying stream.
|
|
|
|
// Re-size the buffer to the size specified in cb.
|
|
|
|
if( _fLowMem )
|
|
{
|
|
// If we want to grow the buffer In low-memory conditions,
|
|
// no realloc is necessary, because
|
|
// _pbMappedStream is already large enough for the largest
|
|
// property set.
|
|
|
|
if( NULL != ppv )
|
|
*ppv = _pbMappedStream;
|
|
}
|
|
else if ( cb != _cbMappedStream )
|
|
{
|
|
|
|
// We must re-alloc the buffer.
|
|
|
|
#if DBG
|
|
pv = _fSimulateLowMem ? NULL : (PBYTE) CoTaskMemRealloc( _pbMappedStream, cb );
|
|
#else
|
|
pv = (PBYTE)CoTaskMemRealloc( _pbMappedStream, cb );
|
|
#endif
|
|
|
|
if ((pv == NULL) )
|
|
{
|
|
// allocation failed: we need to try using a backup mechanism for
|
|
// more memory.
|
|
// copy the data to the global reserved chunk... we will wait until
|
|
// someone else has released it. it will be released on the way out
|
|
// of the property code.
|
|
|
|
pv = g_ReservedMemory.LockMemory();
|
|
|
|
if( NULL == pv )
|
|
nffErr( EH_Err, E_OUTOFMEMORY );
|
|
|
|
_fLowMem = TRUE;
|
|
|
|
if( NULL != _pbMappedStream )
|
|
{
|
|
memcpy( pv, _pbMappedStream, _cbMappedStream );
|
|
}
|
|
CoTaskMemFree( _pbMappedStream );
|
|
}
|
|
|
|
_pbMappedStream = pv;
|
|
|
|
if( NULL != ppv )
|
|
*ppv = pv;
|
|
}
|
|
_cbMappedStream = cb;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
EH_Err:
|
|
|
|
if( fUsingUpdateStream )
|
|
{
|
|
DfpAssert( !fUsingLatestStream );
|
|
EndUsingUpdateStream();
|
|
}
|
|
else if( fUsingLatestStream )
|
|
{
|
|
EndUsingLatestStream();
|
|
}
|
|
|
|
_pnffstm->Unlock();
|
|
|
|
if( FAILED(*phr) )
|
|
{
|
|
propDbg((DbgFlag(*phr,DEB_ITRACE), "IMappedStream::CNtfsStream(%08X)::SetSize %s returns hr=%08X\n",
|
|
this, *phr != S_OK ? "exception" : "", *phr));
|
|
}
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::Lock (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
CNFFMappedStream::Lock(IN BOOLEAN fExclusive)
|
|
{
|
|
// Don't trace at this level. The noice is too great!
|
|
//nffXTrace( "CNFFMappedStream::Lock");
|
|
|
|
UNREFERENCED_PARM(fExclusive);
|
|
_pnffstm->Lock( INFINITE );
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::Unlock (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
// Should we unlock even if there's an error?
|
|
NTSTATUS
|
|
CNFFMappedStream::Unlock(VOID)
|
|
{
|
|
// Don't trace at this level. The noice is too great!
|
|
//nffXTrace( "CNFFMappedStream::Unlock");
|
|
// if at the end of the properties set/get call we have the low
|
|
// memory region locked, we flush to disk.
|
|
HRESULT sc = S_OK;
|
|
|
|
if (_fLowMem)
|
|
{
|
|
Flush(&sc);
|
|
|
|
g_ReservedMemory.UnlockMemory();
|
|
_pbMappedStream = NULL;
|
|
_cbMappedStream = _cbMappedStreamActual = 0;
|
|
_fLowMem = FALSE;
|
|
propDbg((DEB_PROP_INFO, "CMappedStreamOnHFile(%08X):Unlock low-mem returns NTSTATUS=%08X\n",
|
|
this, sc));
|
|
}
|
|
|
|
_pnffstm->Unlock();
|
|
|
|
return(sc);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::QueryTimeStamps (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CNFFMappedStream::QueryTimeStamps(OUT STATPROPSETSTG *pspss, BOOLEAN fNonSimple) const
|
|
{
|
|
nffITrace( "CNFFMappedStream::QueryTimeStamps" );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::QueryModifyTime (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
CNFFMappedStream::QueryModifyTime(OUT LONGLONG *pll) const
|
|
{
|
|
nffITrace( "CNFFMappedStream::QueryModifyTime" );
|
|
return(FALSE);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: Unused methods by this IMappedStream implementation:
|
|
// QuerySecurity, IsWritable, GetHandle
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
CNFFMappedStream::QuerySecurity(OUT ULONG *pul) const
|
|
{
|
|
nffITrace( "CNFFMappedStream::QuerySecurity" );
|
|
return(FALSE);
|
|
}
|
|
|
|
BOOLEAN
|
|
CNFFMappedStream::IsWriteable() const
|
|
{
|
|
nffITrace( "CNFFMappedStream::IsWriteable" );
|
|
return( (BOOLEAN) _pnffstm->IsWriteable() );
|
|
}
|
|
|
|
HANDLE
|
|
CNFFMappedStream::GetHandle(VOID) const
|
|
{
|
|
nffITrace( "CNFFMappedStream::GetHandle" );
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CNFFMappedStream::SetModified/IsModified (IMappedStream)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
VOID
|
|
CNFFMappedStream::SetModified(OUT LONG *phr)
|
|
{
|
|
nffITrace( "CNFFMappedStream::SetModified" );
|
|
HRESULT &sc = *phr;
|
|
|
|
_pnffstm->Lock( INFINITE );;
|
|
|
|
nffChk( _pnffstm->CheckReverted() );
|
|
|
|
nffChk( CreateUpdateStreamIfNecessary() );
|
|
|
|
_fMappedStreamDirty = TRUE;
|
|
sc = S_OK;
|
|
|
|
EH_Err:
|
|
|
|
_pnffstm->Unlock();
|
|
}
|
|
|
|
BOOLEAN
|
|
CNFFMappedStream::IsModified(VOID) const
|
|
{
|
|
nffITrace( "CNFFMappedStream::IsModified" );
|
|
return _fMappedStreamDirty;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: ImappedStream::IsNtMappedStream/SetChangePending
|
|
//
|
|
// Synopsis: Debug routines.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
#if DBGPROP
|
|
BOOLEAN
|
|
CNFFMappedStream::IsNtMappedStream(VOID) const
|
|
{
|
|
nffITrace( "CNFFMappedStream::IsNtMappedStream" );
|
|
return(TRUE);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if DBGPROP
|
|
BOOLEAN
|
|
CNFFMappedStream::SetChangePending(BOOLEAN f)
|
|
{
|
|
nffITrace( "CNFFMappedStream::SetChangePending" );
|
|
return(f);
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// CNFFMappedStream::BeginUsingLatestStream/EndUsingLatestStream
|
|
//
|
|
// These routines are similar to Begin/EndUsing*Update*Stream,
|
|
// except that they honor the _fUpdateStreamHasLatest flag.
|
|
// Thus, if the original stream has the latest data, then this
|
|
// routine will do nothing.
|
|
//
|
|
|
|
void
|
|
CNFFMappedStream::BeginUsingLatestStream()
|
|
{
|
|
if( _fUpdateStreamHasLatest )
|
|
{
|
|
if( 0 == _cLatestStreamInUse++ )
|
|
BeginUsingUpdateStream();
|
|
}
|
|
}
|
|
|
|
void
|
|
CNFFMappedStream::EndUsingLatestStream()
|
|
{
|
|
if( 0 != _cLatestStreamInUse )
|
|
{
|
|
EndUsingUpdateStream();
|
|
_cLatestStreamInUse--;
|
|
}
|
|
|
|
DfpAssert( static_cast<USHORT>(-1) != _cLatestStreamInUse );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// CNFFMappedStream::BeginUsingUpdateStream
|
|
//
|
|
// This is called when the update stream is to be used. It
|
|
// does nothing, though, if we don't have an update stream
|
|
// (e.g. if the file system doesn't support stream renames).
|
|
// We increment the _cUpdateStreamInUse count, so that we can determine in
|
|
// EndUsingUpdateStream when to swap the handles back.
|
|
|
|
void
|
|
CNFFMappedStream::BeginUsingUpdateStream()
|
|
{
|
|
if( NULL != _pstmUpdate
|
|
&&
|
|
INVALID_HANDLE_VALUE != _pstmUpdate->GetFileHandle()
|
|
&&
|
|
0 == _cUpdateStreamInUse++ )
|
|
{
|
|
HANDLE hTemp = _pnffstm->_hFile;
|
|
_pnffstm->_hFile = _pstmUpdate->_hFile;
|
|
_pstmUpdate->_hFile = hTemp;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// CNFFMappedStream::EndUsingUpdateStream
|
|
//
|
|
// Decrement the _cUpdateStreamInUse count. And, if that puts
|
|
// the count down to zero, swap the handles back.
|
|
//
|
|
|
|
void
|
|
CNFFMappedStream::EndUsingUpdateStream()
|
|
{
|
|
if( 0 != _cUpdateStreamInUse
|
|
&&
|
|
0 == --_cUpdateStreamInUse )
|
|
{
|
|
DfpAssert( NULL != _pstmUpdate && INVALID_HANDLE_VALUE != _pstmUpdate->GetFileHandle() );
|
|
|
|
HANDLE hTemp = _pnffstm->_hFile;
|
|
_pnffstm->_hFile = _pstmUpdate->_hFile;
|
|
_pstmUpdate->_hFile = hTemp;
|
|
}
|
|
DfpAssert( static_cast<USHORT>(-1) != _cUpdateStreamInUse );
|
|
}
|
|
|
|
|
|
inline HRESULT
|
|
CNFFMappedStream::CreateUpdateStreamIfNecessary()
|
|
{
|
|
if( _fStreamRenameSupported
|
|
&&
|
|
( NULL == _pstmUpdate
|
|
||
|
|
INVALID_HANDLE_VALUE == _pstmUpdate->GetFileHandle()
|
|
)
|
|
)
|
|
{
|
|
return( OpenUpdateStream( TRUE ));
|
|
}
|
|
else
|
|
return( S_OK );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNFFMAppedStream::RollForwardIfNecessary (non-interface method)
|
|
//
|
|
// In the open path, we look to see if there's a leftover update stream for
|
|
// a previous open of the stream, which must have crashed during a write.
|
|
// If we're opening for write, we fix the problem. Otherwise, we
|
|
// just remember that we'll have to read out of the update stream.
|
|
//
|
|
// See the CNtfsStreamForPropStg class declaration for a description of
|
|
// this transactioning.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CNFFMappedStream::RollForwardIfNecessary()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BY_HANDLE_FILE_INFORMATION ByHandleFileInformation;
|
|
|
|
// If we've already checked for this, then we needn't check again.
|
|
if( _fCheckedForRollForward )
|
|
goto Exit;
|
|
|
|
// We also needn't do anything if we're creating, since that overwrites
|
|
// any existing data anyway.
|
|
|
|
if( !(STGM_CREATE & _pnffstm->_grfMode) )
|
|
{
|
|
// Get the size of the current stream.
|
|
if( !GetFileInformationByHandle( _pnffstm->_hFile, &ByHandleFileInformation ))
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Exit;
|
|
}
|
|
|
|
// If the size is zero, then there might be an update
|
|
// stream with the real data.
|
|
|
|
if( 0 == ByHandleFileInformation.nFileSizeLow
|
|
&&
|
|
0 == ByHandleFileInformation.nFileSizeHigh )
|
|
{
|
|
// See if there's an update stream
|
|
hr = OpenUpdateStream( FALSE );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// We have a zero-length main stream and an update stream,
|
|
// so there must have been a crash in ReplaceOriginalWithUpdate,
|
|
// after the truncation but before the NtSetInformationFile
|
|
// (FileRenameInformation).
|
|
|
|
// If this is a writable stream, rename the update stream
|
|
// over the zero-length one. Otherwise, we'll just read from
|
|
// the update stream.
|
|
|
|
_fUpdateStreamHasLatest = TRUE;
|
|
|
|
if( IsWriteable() )
|
|
{
|
|
hr = ReplaceOriginalWithUpdate( DONT_CREATE_NEW_UPDATE_STREAM );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
}
|
|
else if( STG_E_FILENOTFOUND == hr )
|
|
// Ignore the case where there's no update stream. This happens
|
|
// when the stream is created without STGM_CREATE set.
|
|
hr = S_OK;
|
|
else
|
|
goto Exit;
|
|
}
|
|
} // if( !(STGM_CREATE & _grfMode) )
|
|
|
|
// We don't need to check for this again.
|
|
_fCheckedForRollForward = TRUE;
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
} // CNtfsStreamForPropStg::RollForwardIfNecessary
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNtfsStreamForPropStg::ReplaceOriginalWithUpdate (internal method)
|
|
//
|
|
// This method renames the update stream over the original stream, then
|
|
// creates a new update stream (with no data but properly sized). If, however,
|
|
// the update stream doesn't have the latest data anyway, then this routine
|
|
// noops.
|
|
//
|
|
// See the CNtfsStreamForPropStg class declaration for a description of
|
|
// this transactioning.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CNFFMappedStream::ReplaceOriginalWithUpdate( enumCREATE_NEW_UPDATE_STREAM CreateNewUpdateStream )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
NTSTATUS status;
|
|
|
|
FILE_END_OF_FILE_INFORMATION file_end_of_file_information;
|
|
IO_STATUS_BLOCK io_status_block;
|
|
|
|
// If the original stream already has the latest data, then
|
|
// there's nothing to do.
|
|
|
|
if( !_fUpdateStreamHasLatest )
|
|
goto Exit;
|
|
|
|
DfpAssert( NULL != _pstmUpdate );
|
|
DfpAssert( 0 == _cUpdateStreamInUse );
|
|
|
|
// We must write the update data all the way to disk.
|
|
hr = _pstmUpdate->Flush();
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// Truncate the original stream so that it can be overwritten.
|
|
// After this atomic operation, the update stream is considered
|
|
// *the* stream (which is why we had to flush it above).
|
|
|
|
file_end_of_file_information.EndOfFile = CLargeInteger(0);
|
|
|
|
status = NtSetInformationFile( _pnffstm->_hFile, &io_status_block,
|
|
&file_end_of_file_information,
|
|
sizeof(file_end_of_file_information),
|
|
FileEndOfFileInformation );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
hr = NtStatusToScode(status);
|
|
goto Exit;
|
|
}
|
|
|
|
NtClose( _pnffstm->_hFile );
|
|
_pnffstm->_hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// Rename the updated stream over the original (now empty) stream.
|
|
// This is atomic.
|
|
|
|
hr = _pstmUpdate->Rename( _pnffstm->_pwcsName, TRUE );
|
|
if( FAILED(hr) )
|
|
{
|
|
// Go into the reverted state
|
|
NtClose( _pstmUpdate->_hFile );
|
|
_pstmUpdate->_hFile = INVALID_HANDLE_VALUE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Make the updated stream the master
|
|
|
|
_pnffstm->_hFile = _pstmUpdate->_hFile;
|
|
_pstmUpdate->_hFile = INVALID_HANDLE_VALUE;
|
|
_fUpdateStreamHasLatest = FALSE;
|
|
|
|
// Optionally create a new update stream.
|
|
|
|
if( CREATE_NEW_UPDATE_STREAM == CreateNewUpdateStream )
|
|
{
|
|
// return an error if we cannot create the update stream
|
|
hr = OpenUpdateStream( TRUE );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
else
|
|
DfpAssert( DONT_CREATE_NEW_UPDATE_STREAM == CreateNewUpdateStream );
|
|
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
} // CNFFMappedStream::ReplaceOriginalWithUpdate()
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNFFMappedStream::OpenUpdateStream
|
|
//
|
|
// This method opens the update stream, to which stream updates are written.
|
|
// This is necessary to provide a minimal level of transactioning.
|
|
//
|
|
// See the CNtfsStreamForPropStg class declaration for a description of
|
|
// this transactioning.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CNFFMappedStream::OpenUpdateStream( BOOL fCreate )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HANDLE hStream = INVALID_HANDLE_VALUE;
|
|
CNtfsUpdateStreamName UpdateStreamName = _pnffstm->_pwcsName;
|
|
|
|
// Open the NTFS stream
|
|
|
|
hr = _pnffstm->_pnffstg->GetStreamHandle( &hStream,
|
|
UpdateStreamName,
|
|
_pnffstm->_grfMode | (fCreate ? STGM_CREATE : 0),
|
|
fCreate );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// If necessary, instantiate a CNtfsUpdateStreamForPropStg
|
|
|
|
if( NULL == _pstmUpdate )
|
|
{
|
|
_pstmUpdate = new CNtfsUpdateStreamForPropStg( _pnffstm->_pnffstg, _pnffstm->_pBlockingLock );
|
|
if( NULL == _pstmUpdate )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Put the NTFS stream handle into the CNtfsUpdateStreamForPropStg
|
|
|
|
hr = _pnffstm->_pnffstg->InitCNtfsStream( _pstmUpdate, hStream,
|
|
_pnffstm->_grfMode | (fCreate ? STGM_CREATE : 0),
|
|
UpdateStreamName );
|
|
|
|
hStream = INVALID_HANDLE_VALUE; // ownership of the handle has changed
|
|
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
// If we're creating the update stream, size it to match the size
|
|
// of the original stream.
|
|
|
|
if( fCreate )
|
|
{
|
|
ULONG ulSize = GetSize(&hr);
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
hr = _pstmUpdate->SetSize( CULargeInteger(ulSize) );
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
if( INVALID_HANDLE_VALUE != hStream )
|
|
NtClose( hStream );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// If we were attempting a create but failed, then ensure the
|
|
// update stream is gone.
|
|
|
|
if( NULL != _pstmUpdate && fCreate )
|
|
_pstmUpdate->Delete();
|
|
|
|
DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
|
|
}
|
|
|
|
return( hr );
|
|
|
|
} // CNFFMappedStream::OpenUpdateStream()
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNFFMappedStream::Init (override from CNtfsStream)
|
|
//
|
|
// This method initializes the CNtfsStream, and checks the file system to
|
|
// determine if we can support the update stream (for robustness). The
|
|
// necessary file system support is stream renaming, which we use to provide
|
|
// a minimal level of transactioning.
|
|
//
|
|
// See the CNtfsStreamForPropStg class declaration for a description of
|
|
// this transactioning.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CNFFMappedStream::Init( HANDLE hFile )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
FILE_FS_ATTRIBUTE_INFORMATION file_fs_attribute_information;
|
|
IO_STATUS_BLOCK io_status_block;
|
|
|
|
// Check to see if we'll be able to support stream renaming.
|
|
|
|
if( NULL != _pnffstm->_pnffstg )
|
|
{
|
|
// We can at least see an IStorage for the file, so stream renaming
|
|
// could potentially work, but we also need to query the file system
|
|
// attributes to see if it actually supports it.
|
|
|
|
status = NtQueryVolumeInformationFile( hFile, &io_status_block,
|
|
&file_fs_attribute_information,
|
|
sizeof(file_fs_attribute_information),
|
|
FileFsAttributeInformation );
|
|
|
|
// We should always get a buffer-overflow error here, because we don't
|
|
// provide enough buffer for the file system name, but that's OK because
|
|
// we don't need it (status_buffer_overflow is just a warning, so the rest
|
|
// of the data is good).
|
|
|
|
if( !NT_SUCCESS(status) && STATUS_BUFFER_OVERFLOW != status)
|
|
{
|
|
hr = NtStatusToScode(status);
|
|
goto Exit;
|
|
}
|
|
|
|
// There's no attribute bit which says "supports stream rename". The best
|
|
// we can do is look for another NTFS5 feature and make an inferrence.
|
|
|
|
if( FILE_SUPPORTS_OBJECT_IDS & file_fs_attribute_information.FileSystemAttributes )
|
|
_fStreamRenameSupported = TRUE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
} // CNFFMappedStream::Init()
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
CNFFMappedStream::ShutDown()
|
|
{ // mikehill step
|
|
HRESULT hr = S_OK;
|
|
|
|
_pnffstm->Lock( INFINITE );
|
|
|
|
// Close the mapped stream
|
|
Close( &hr );
|
|
if( FAILED(hr) && STG_E_REVERTED != hr )
|
|
propDbg(( DEB_ERROR, "CNFFMappedStream(0x%x)::ShutDown failed call to CNtfsStream::Close (%08x)\n",
|
|
this, hr ));
|
|
|
|
// Overwrite the original stream with the update (if necessary),
|
|
// but don't bother to create a new update stream afterwards.
|
|
|
|
if( NULL != _pstmUpdate )
|
|
{
|
|
hr = ReplaceOriginalWithUpdate( DONT_CREATE_NEW_UPDATE_STREAM );
|
|
if( FAILED(hr) )
|
|
propDbg(( DEB_ERROR, "CNFFMappedStream(0x%x)::ShutDown failed call to ReplaceOriginalWithUpdate (%08x)\n",
|
|
this, hr ));
|
|
}
|
|
|
|
// Release the update stream.
|
|
if( NULL != _pstmUpdate )
|
|
DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
|
|
|
|
propDbg(( DbgFlag(hr,DEB_ITRACE), "CNFFMappedStream(0x%x)::ShutDown() returns %08x\n", this, hr ));
|
|
|
|
_pnffstm->Unlock();
|
|
return( hr );
|
|
|
|
} // CNFFMappedStream::ShutDown
|
|
|
|
|
|
void
|
|
CNFFMappedStream::Read( void *pv, ULONG ulOffset, ULONG *pcbCopy )
|
|
{
|
|
if( *pcbCopy > _cbMappedStream )
|
|
*pcbCopy = 0;
|
|
else if( *pcbCopy > _cbMappedStream - ulOffset )
|
|
*pcbCopy = _cbMappedStream - ulOffset;
|
|
|
|
memcpy( pv, &_pbMappedStream[ ulOffset ], *pcbCopy );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
CNFFMappedStream::Write( const void *pv, ULONG ulOffset, ULONG *pcbCopy )
|
|
{
|
|
if( *pcbCopy > _cbMappedStream )
|
|
*pcbCopy = 0;
|
|
else if( *pcbCopy + ulOffset > _cbMappedStream )
|
|
*pcbCopy = _cbMappedStream - ulOffset;
|
|
|
|
memcpy( &_pbMappedStream[ulOffset], pv, *pcbCopy );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: IStorageTest::UseNTFS4Streams (DBG only)
|
|
//
|
|
// This method can be used to disable the stream-renaming necessary for
|
|
// robust property sets. This emulates an NTFS4 volume.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#if DBG
|
|
HRESULT STDMETHODCALLTYPE
|
|
CNFFMappedStream::UseNTFS4Streams( BOOL fUseNTFS4Streams )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if( _fUpdateStreamHasLatest )
|
|
{
|
|
hr = STG_E_INVALIDPARAMETER;
|
|
propDbg(( DEB_ERROR, "CNtfsStreamForPropStg(0x%x)::UseNTFS4Streams(%s)"
|
|
"was called while an update stream was already in use\n",
|
|
this, fUseNTFS4Streams ? "True" : "False" ));
|
|
}
|
|
else if( fUseNTFS4Streams )
|
|
{
|
|
DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
|
|
_fStreamRenameSupported = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Shutting NTFS4streams off isn't implemented
|
|
hr = E_NOTIMPL;
|
|
}
|
|
|
|
return( hr );
|
|
|
|
} // CNFFMAppedStream::UseNTFS4Streams
|
|
#endif // #if DBG
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CNFFMappedStream::GetFormatVersion( WORD *pw )
|
|
{
|
|
return( E_NOTIMPL );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CNFFMappedStream::SimulateLowMemory( BOOL fSimulate )
|
|
{
|
|
_fSimulateLowMem = fSimulate;
|
|
return( S_OK );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
#if DBG
|
|
LONG
|
|
CNFFMappedStream::GetLockCount()
|
|
{
|
|
return( _pnffstm->GetLockCount() );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
#if DBG
|
|
HRESULT
|
|
CNFFMappedStream::IsDirty()
|
|
{
|
|
return( _fMappedStreamDirty ? S_OK : S_FALSE );
|
|
}
|
|
#endif // #if DBG
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNtfsUpdateStreamForPropStg::ShutDown (overrides CNtfsStream)
|
|
//
|
|
// Override so that we can remove this stream from the linked-list, but
|
|
// not do a flush. See the CNtfsStreamForPropStg class declaration for
|
|
// more information on this class.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CNtfsUpdateStreamForPropStg::ShutDown()
|
|
{
|
|
RemoveSelfFromList();
|
|
return( S_OK );
|
|
} // CNtfsUpdateStreamForPropStg::ShutDown
|
|
|
|
|
|
|
|
|
|
|