|
|
//+============================================================================
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1998.
//
// File: hntfsstg.cxx
//
// This file provides the NFF (NTFS Flat File) IStream implementation.
//
// History:
// 5/6/98 MikeHill
// - Use CoTaskMem rather than new/delete.
//
//+============================================================================
#include <pch.cxx>
#include <tstr.h>
#include "cli.hxx"
#include "expparam.hxx"
#ifdef _MAC_NODOC
ASSERTDATA // File-specific data for FnAssert
#endif
DECLARE_INFOLEVEL(nff)
#ifndef DEB_INFO
#define DEB_INFO DEB_USER1
#endif
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::AddRef (IUnknown)
//
//+----------------------------------------------------------------------------
ULONG CNtfsStream::AddRef() { LONG cRefs;
cRefs = InterlockedIncrement( &_cRefs );
nffDebug((DEB_REFCOUNT, "CNtfsStream::AddRef(this=%x) == %d\n", this, cRefs)); return cRefs; }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Release (IUnknown)
//
//+----------------------------------------------------------------------------
ULONG CNtfsStream::Release() { ULONG ulRet = InterlockedDecrement( &_cRefs );
if( 0 == ulRet ) { RemoveSelfFromList(); delete this; } nffDebug((DEB_REFCOUNT, "CNtfsStream::Release(this=%x) == %d\n", this, ulRet));
return( ulRet ); }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::AddRef (IUnknown)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::QueryInterface( REFIID riid, void** ppv ) { HRESULT sc=S_OK;
#if DBG == 1
WCHAR strIID[64]; StringFromGUID2(riid, strIID, 64); nffDebug(( DEB_TRACE | DEB_REFCOUNT, "CNtfsStream::QueryInterface( %ws )\n", strIID )); #endif
NFF_VALIDATE( QueryInterface( riid, ppv ) );
nffChk( CheckReverted() );
if( IsEqualIID( riid, IID_IUnknown ) || IsEqualIID( riid, IID_IStream ) || IsEqualIID( riid, IID_ISequentialStream ) ) { *ppv = static_cast<IStream*>(this); AddRef(); return( S_OK ); } else if( IsEqualIID( riid, IID_IMappedStream )) { *ppv = static_cast<IMappedStream*>(&_nffMappedStream); AddRef(); return( S_OK ); } else if( IsEqualIID( riid, IID_ILockBytes )) { *ppv = static_cast<ILockBytes*>(this); AddRef(); return( S_OK ); } #if DBG == 1
else if( IsEqualIID( riid, IID_IStorageTest )) { *ppv = static_cast<IStorageTest*>(this); AddRef(); return( S_OK ); } #endif // #if DBG
else { nffDebug(( DEB_TRACE | DEB_REFCOUNT, "CNtfsStream::QueryInterface() Failed E_NOINTERFACE\n" )); return( E_NOINTERFACE ); }
EH_Err: return sc; }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Seek (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *puliNewPos) { HRESULT sc = S_OK; LARGE_INTEGER liFileSize; LARGE_INTEGER liNewPos = { 0, 0 };
nffDebug(( DEB_TRACE, "CNtfsStream::Seek( %x:%08x, %d, %p );\n", dlibMove.HighPart, dlibMove.LowPart, dwOrigin, puliNewPos ));
NFF_VALIDATE( Seek( dlibMove, dwOrigin, puliNewPos ) );
Lock( INFINITE );
nffChk( CheckReverted() );
switch( dwOrigin ) { case STREAM_SEEK_SET: liNewPos.QuadPart = dlibMove.QuadPart; break;
case STREAM_SEEK_CUR: liNewPos.QuadPart = _liCurrentSeekPosition.QuadPart + dlibMove.QuadPart; break;
case STREAM_SEEK_END: liFileSize.LowPart = GetFileSize( _hFile, (ULONG*)(&liFileSize.HighPart) );
if( 0xFFFFFFFF == liFileSize.LowPart && NO_ERROR != GetLastError() ) { nffChk( HRESULT_FROM_WIN32( GetLastError() ) ); }
liNewPos.QuadPart = liFileSize.QuadPart + dlibMove.QuadPart; break;
default: nffChk(STG_E_INVALIDPARAMETER); break; }
// Compatibility with Docfile. Seeking < 0 fails.
if( liNewPos.QuadPart < 0 ) nffErr( EH_Err, STG_E_INVALIDFUNCTION );
_liCurrentSeekPosition = liNewPos;
// If desired, give the caller the now-current seek position.
if( NULL != puliNewPos ) *puliNewPos = _liCurrentSeekPosition;
EH_Err: Unlock(); return( sc ); }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::SetSize (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::SetSize( ULARGE_INTEGER uliNewSize) { HRESULT sc = S_OK; CLargeInteger liEOF;
if ( uliNewSize.HighPart != 0 ) nffErr(EH_Err, STG_E_INVALIDFUNCTION);
nffDebug(( DEB_ITRACE | DEB_INFO | DEB_WRITE, "CNtfsStream::SetSize(%x:%x) hdl=%x, stream='%ws'\n", uliNewSize.QuadPart, _hFile, _pwcsName ));
NFF_VALIDATE( SetSize( uliNewSize ) );
Lock( INFINITE );
nffChk( CheckReverted() );
// If this stream is mapped, set the size accordingly
if( _nffMappedStream.IsMapped() ) { _nffMappedStream.SetSize( uliNewSize.LowPart, TRUE, NULL, &sc ); } else { sc = SetFileSize( CULargeInteger(uliNewSize) ); }
if( !FAILED(sc) ) sc = S_OK;
EH_Err:
Unlock(); return( sc);
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::CopyTo (IStream)
//
// There's no way of knowing what the IStream is to which we're copying, so
// we have to assume that we might be copying to ourself. And given that
// assumption, we have to deal with the case that this is an overlapping
// copy (e.g., "copy 10 bytes from offset 0 to offset 5").
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::CopyTo( IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { return E_NOTIMPL; // Commenting out this code, because it isn't currently used and is
// not fully tested.
/*
nffXTrace( "CNtfsStream::CopyTo" );
HRESULT sc = S_OK; PVOID pv = NULL; ULONG cbRead = 0, cbWritten = 0; CULargeInteger cbReadTotal = 0, cbWrittenTotal = 0; CLargeInteger liZero = 0; CULargeInteger uliOriginalSourcePosition, uliOriginalDestPosition; CULargeInteger cbSourceSize, cbDestSize; ULONG cbPerCopy = 0; STATSTG statstg; CULargeInteger cbRequested = cb; BOOL fCopyForward;
NFF_VALIDATE( CopyTo( pstm, cb, pcbRead, pcbWritten ) );
Lock( INFINITE );
nffChk( CheckReverted() );
if( NULL == pstm) nffErr( EH_Err, STG_E_INVALIDPARAMETER );
// Determine how much we'll copy at a time.
// As of this writing, STREAMBUFFERSIZE is 8192 bytes
if( cbRequested > STREAMBUFFERSIZE ) cbPerCopy = STREAMBUFFERSIZE; else cbPerCopy = cbRequested.LowPart;
// ------------------------------------------------------------------
// Get the current stream sizes/positions, and adjust the destination
// size if necessary
// ------------------------------------------------------------------
nffChk( this->Seek( liZero, STREAM_SEEK_CUR, &uliOriginalSourcePosition ) );
nffChk( pstm->Seek( liZero, STREAM_SEEK_CUR, &uliOriginalDestPosition ) );
nffChk( this->Stat( &statstg, STATFLAG_NONAME ) );
cbSourceSize = statstg.cbSize;
nffChk( pstm->Stat( &statstg, STATFLAG_NONAME ) );
cbDestSize = statstg.cbSize;
// Ensure the sizes are valid (we can't handle anything with the high bit
// set, because Seek takes a signed offset).
if( static_cast<CLargeInteger>(cbSourceSize) < 0 || static_cast<CLargeInteger>(cbDestSize) < 0 ) { nffErr( EH_Err, STG_E_INVALIDHEADER ); }
// Don't copy more than the source stream has available
if( cbRequested > cbSourceSize - uliOriginalSourcePosition ) cbRequested = cbSourceSize - uliOriginalSourcePosition;
// If necessary, grow the destination stream.
if( cbSourceSize - uliOriginalSourcePosition > cbDestSize - uliOriginalDestPosition ) { cbDestSize = cbSourceSize - uliOriginalSourcePosition + uliOriginalDestPosition; nffChk( pstm->SetSize( cbDestSize ) ); }
// ----------------------
// Allocate a copy buffer
// ----------------------
nffMem( pv = CoTaskMemAlloc( cbPerCopy ) );
// -----------------------------------------------------------------------------
// Determine if we're copying forwards (high seek position to low) or backwards.
// -----------------------------------------------------------------------------
fCopyForward = TRUE; if( uliOriginalSourcePosition < uliOriginalDestPosition ) { // E.g., say we're copying 15 bytes from offset 0 to offset 5,
// and we're only able to copy 10 bytes at a time.
// If we copy bytes 0-9 to offset 5, we'll end up overwriting
// bytes 10-14, and be unable to complete the copy.
// So instead, we'll copy bytes 5-14 to offset 10, and finish
// up by copying bytes 0-4 to offset 5.
fCopyForward = FALSE;
// To do this kind of backwards copy, we need to start by seeking
// towards the end of the stream.
CULargeInteger uliNewSourcePosition, uliNewDestPosition;
uliNewSourcePosition = cbSourceSize - cbPerCopy; nffChk( this->Seek( uliNewSourcePosition, STREAM_SEEK_SET, NULL ) );
uliNewDestPosition = cbDestSize - cbPerCopy; nffChk( pstm->Seek( uliNewDestPosition, STREAM_SEEK_SET, NULL ) );
}
// --------------
// Copy in chunks
// --------------
cbPerCopy = cbRequested > cbPerCopy ? cbPerCopy : cbRequested.LowPart; while( cbRequested > 0 ) { // Read from the source
nffChk( this->Read( pv, cbPerCopy, &cbRead ) );
if( cbRead != cbPerCopy ) nffErr(EH_Err, STG_E_READFAULT);
cbReadTotal += cbRead;
// Write to the dest
nffChk( pstm->Write( pv, cbPerCopy, &cbWritten ) );
if( cbWritten != cbPerCopy ) nffErr( EH_Err, STG_E_WRITEFAULT );
cbWrittenTotal += cbWritten;
// Adjust the amount remaining to be copied
cbRequested -= cbPerCopy;
// Determine how much to copy in the next iteration (this will
// always be cbPerCopy until the last iteration). If copying
// backwards, we need to manually adjust the seek pointer.
cbPerCopy = (cbRequested > cbPerCopy) ? cbPerCopy : cbRequested.LowPart; if( !fCopyForward ) { nffChk( this->Seek( -static_cast<CLargeInteger>(cbPerCopy), STREAM_SEEK_CUR, NULL ) );
nffChk( pstm->Seek( -static_cast<CLargeInteger>(cbPerCopy), STREAM_SEEK_CUR, NULL ) ); }
}
// If we were backward-copying, adjust the seek pointers
// as if we had forward-copied
if( !fCopyForward ) { uliOriginalSourcePosition += cbReadTotal; nffChk( this->Seek( uliOriginalSourcePosition, STREAM_SEEK_SET, NULL ) );
uliOriginalDestPosition += cbWrittenTotal; nffChk( pstm->Seek( uliOriginalDestPosition, STREAM_SEEK_SET, NULL ) ); }
// ----
// Exit
// ----
if( NULL != pcbRead ) *pcbRead = cbReadTotal; if( NULL != pcbWritten ) *pcbWritten = cbWrittenTotal;
EH_Err:
if( NULL != pv ) CoTaskMemFree(pv);
Unlock(); return(sc); */
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Commit (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Commit( DWORD grfCommitFlags ) { nffXTrace( "CNtfsStream::Commit" ); HRESULT sc = S_OK;
NFF_VALIDATE( Commit( grfCommitFlags ) ); if(~(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE|STGC_DEFAULT) & grfCommitFlags) nffErr( EH_Err, STG_E_INVALIDFLAG );
Lock( INFINITE );
nffChkTo ( EH_Unlock, CheckReverted() );
if( !(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE & grfCommitFlags) ) { if( !FlushFileBuffers( _hFile )) sc = LAST_SCODE; }
EH_Unlock: Unlock(); EH_Err: return sc;
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Revert (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Revert(void) { nffXTrace( "CNtfsStream::Revert" ); // We only support direct-mode.
return CheckReverted(); }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::LockRegion (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::LockRegion( ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { nffXTrace( "CNtfsStream::LockRegion" ); HRESULT sc = S_OK;
NFF_VALIDATE( LockRegion( libOffset, cb, dwLockType ) );
Lock( INFINITE );
nffChk( CheckReverted() );
if (dwLockType != LOCK_EXCLUSIVE && dwLockType != LOCK_ONLYONCE) nffErr( EH_Err, STG_E_INVALIDFUNCTION );
if( !LockFile( _hFile, libOffset.LowPart, libOffset.HighPart, cb.LowPart, cb.HighPart)) { nffErr( EH_Err, LAST_SCODE ); }
EH_Err:
Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::UnlockRegion (ILockBytes)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::UnlockRegion( ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { nffXTrace( "CNtfsStream::UnlockRegion" ); HRESULT sc = S_OK;
NFF_VALIDATE( UnlockRegion( libOffset, cb, dwLockType ) );
Lock( INFINITE );
nffChk( CheckReverted() );
if (dwLockType != LOCK_EXCLUSIVE && dwLockType != LOCK_ONLYONCE) { nffErr( EH_Err, STG_E_INVALIDFUNCTION ); }
if( !UnlockFile(_hFile, libOffset.LowPart, libOffset.HighPart, cb.LowPart, cb.HighPart)) { nffErr( EH_Err, LAST_SCODE ); }
EH_Err:
Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Stat (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Stat( STATSTG *pstatstg, DWORD grfStatFlag) { nffXTrace( "CNtfsStream::Stat" ); STATSTG statstg; HRESULT sc = S_OK; NTSTATUS status = STATUS_SUCCESS; FILE_ACCESS_INFORMATION file_access_information; IO_STATUS_BLOCK IoStatusBlock;
BY_HANDLE_FILE_INFORMATION ByHandleFileInformation;
NFF_VALIDATE( Stat( pstatstg, grfStatFlag ) );
Lock( INFINITE );
nffChkTo ( EH_Lock, CheckReverted() );
ZeroMemory((void*)&statstg, sizeof(STATSTG));
// Get the name, if desired
if( (STATFLAG_NONAME & grfStatFlag) ) statstg.pwcsName = NULL; else { nffAssert( NULL != _pwcsName ); ULONG cbName = sizeof(WCHAR)*((ULONG)wcslen(_pwcsName) + 1); nffMem( statstg.pwcsName = reinterpret_cast<WCHAR*> ( CoTaskMemAlloc( cbName ))); StringCbCopy( statstg.pwcsName, cbName, _pwcsName ); }
// Get the type
statstg.type = STGTY_STREAM;
statstg.grfLocksSupported = LOCK_EXCLUSIVE | LOCK_ONLYONCE;
// Get the size & times.
if( !GetFileInformationByHandle( _hFile, &ByHandleFileInformation )) nffErr( EH_Err, LAST_SCODE );
statstg.cbSize.LowPart = ByHandleFileInformation.nFileSizeLow; statstg.cbSize.HighPart = ByHandleFileInformation.nFileSizeHigh;
// We get a time back in ByHandleFileInformation, but it's the file's times,
// not the streams times. So really the stream times are not supported, and
// we'll just set them to zero.
statstg.mtime = statstg.atime = statstg.ctime = CFILETIME(0);
// Get the STGM modes
statstg.grfMode = _grfMode & ~STGM_CREATE;
*pstatstg = statstg;
EH_Err: if( FAILED(sc) && NULL != statstg.pwcsName ) CoTaskMemFree( statstg.pwcsName );
EH_Lock: Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Clone (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Clone( IStream** ppstm) { nffXTrace( "CNtfsStream::Clone" ); return( E_NOTIMPL ); }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Read (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Read( void* pv, ULONG cb, ULONG* pcbRead) { nffXTrace( "CNtfsStream::Read" ); HRESULT sc = S_OK; ULONG cbRead = 0;
nffDebug(( DEB_ITRACE, "Read( pv=0x%x, cb=0x%x );\n", pv, cb ));
NFF_VALIDATE( Read( pv, cb, pcbRead ) );
Lock( INFINITE );
nffChk( CheckReverted() );
nffChk( ReadAt( _liCurrentSeekPosition, pv, cb, &cbRead ) );
_liCurrentSeekPosition += cbRead; nffDebug(( DEB_ITRACE, "Read() read %x bytes.\n", cbRead )); if( NULL != pcbRead ) *pcbRead = cbRead;
EH_Err:
Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::ReadAt (ILockBytes)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::ReadAt( ULARGE_INTEGER ulOffset, void* pv, ULONG cb, ULONG* pcbRead) { nffXTrace( "CNtfsStream::ReadAt" ); HRESULT sc = S_OK;
nffDebug(( DEB_ITRACE, "ReadAt( off=%x:%x, pv=0x%x, cb=0x%x );\n", ulOffset, pv, cb ));
NFF_VALIDATE( ReadAt( ulOffset, pv, cb, pcbRead ) );
if( static_cast<LONG>(ulOffset.HighPart) < 0 ) return TYPE_E_SIZETOOBIG;
Lock( INFINITE );
nffChk( CheckReverted() );
// Is this stream mapped?
if( _nffMappedStream.IsMapped() ) { // This stream is mapped. We'll read directly from the mapping buffer.
_nffMappedStream.Read( pv, ulOffset.LowPart, &cb );
if( NULL != pcbRead ) *pcbRead = cb; } else { // No, just read from the file.
nffChk( SyncReadAtFile( ulOffset, pv, cb, pcbRead ) ); }
nffDebug(( DEB_ITRACE, "ReadAt() read %x bytes.\n", NULL == pcbRead ? -1 : *pcbRead ));
EH_Err: Unlock(); return( sc ); }
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Write (IStream)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Write( const void* pv, ULONG cb, ULONG* pcbWritten) { nffXTrace( "CNtfsStream::Write" ); HRESULT sc = S_OK; ULONG cbWritten = 0;
nffDebug(( DEB_ITRACE, "Write( pv=0x%x, cb=0x%x );\n", pv, cb ));
NFF_VALIDATE( Write( pv, cb, pcbWritten ) );
Lock( INFINITE );
nffChk( CheckReverted() );
nffChk(WriteAt( _liCurrentSeekPosition, pv, cb, &cbWritten ));
_liCurrentSeekPosition += cbWritten;
nffDebug(( DEB_ITRACE, "Write() wrote %x bytes.\n", cbWritten ));
if( NULL != pcbWritten ) *pcbWritten = cbWritten;
EH_Err:
Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::WriteAt (ILockBytes)
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::WriteAt( ULARGE_INTEGER ulOffset, const void* pv, ULONG cb, ULONG* pcbWritten) { nffXTrace( "CNtfsStream::WriteAt" ); HRESULT sc = S_OK;
nffDebug(( DEB_ITRACE, "WriteAt( off=%x:%x, pv=0x%x, cb=0x%x );\n", ulOffset, pv, cb ));
NFF_VALIDATE( WriteAt( ulOffset, pv, cb, pcbWritten ) );
if( ((LONG)(ulOffset.HighPart)) < 0 ) return( TYPE_E_SIZETOOBIG );
Lock( INFINITE );
nffChk( CheckReverted() );
// Is this stream mapped?
if( _nffMappedStream.IsMapped() ) { // This stream is mapped, we'll take the Write to the mapping.
ULONG iPosition = _nffMappedStream.SizeOfMapping() - _liCurrentSeekPosition.LowPart;
if( cb > iPosition ) { _nffMappedStream.SetSize( iPosition + cb, TRUE, NULL, &sc ); nffChk(sc); }
_nffMappedStream.Write( pv, ulOffset.LowPart, &cb );
if( NULL != pcbWritten ) *pcbWritten = cb; } else { // No, just write to the file.
nffChk( SyncWriteAtFile( ulOffset, pv, cb, pcbWritten ) ); }
nffDebug(( DEB_ITRACE, "WriteAt() wrote %x bytes.\n", NULL == pcbWritten ? -1 : *pcbWritten ));
EH_Err:
Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: CNtfsStream::Flush
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Flush() { HRESULT sc = S_OK;
Lock( INFINITE ); nffChk( CheckReverted() );
_nffMappedStream.Flush(&sc);
EH_Err: Unlock(); return(sc);
}
//+-------------------------------------------------------------------
//
// Member: CNtfsStream Constructor
//
//--------------------------------------------------------------------
CNtfsStream::CNtfsStream( CNtfsStorage *pnffstg, IBlockingLock *pBlockingLock ) #pragma warning(disable: 4355)
: _nffMappedStream( this ), #pragma warning(default: 4355)
_pnffstg( pnffstg ) { nffXTrace( "CNtfsStream::CNtfsStream" ); nffDebug(( DEB_REFCOUNT, "new CNtfsStream Constructed with cRefs=1\n" ));
_sig = NTFSSTREAM_SIG; _cRefs = 1; _grfMode = 0; _hFile = INVALID_HANDLE_VALUE; _liCurrentSeekPosition = 0; _pnffstmNext = NULL; _pnffstmPrev = NULL; _pwcsName = NULL;
nffAssert( NULL != pBlockingLock ); _pBlockingLock = pBlockingLock; _pBlockingLock->AddRef();
_ovlp.Internal = _ovlp.InternalHigh = 0; _ovlp.Offset = _ovlp.OffsetHigh = 0; _ovlp.hEvent = NULL;
}
//+-------------------------------------------------------------------
//
// Member: CNtfsStream Destructor
//
//--------------------------------------------------------------------
CNtfsStream::~CNtfsStream() {
nffXTrace( "CNtfsStream::~CNtfsStream" ); nffDebug(( DEB_INFO, "CNtfsStream: Close 0x%x.\n", _hFile ));
// Shut down the mapped stream
_nffMappedStream.ShutDown();
// Close the file
if( INVALID_HANDLE_VALUE != _hFile ) NtClose( _hFile );
if( NULL != _ovlp.hEvent ) CloseHandle( _ovlp.hEvent );
if( NULL != _pwcsName ) CoTaskMemFree( _pwcsName );
// Release the object that provides access to the tree lock.
nffAssert( NULL != _pBlockingLock ); _pBlockingLock->Release();
_sig = NTFSSTREAM_SIGDEL; }
//+-------------------------------------------------------------------
//
// Member: CNtfsStream::Init
//
//--------------------------------------------------------------------
HRESULT CNtfsStream::Init( HANDLE hFile, // File handle of this NTFS Stream.
DWORD grfMode, // Open Modes
const OLECHAR * pwcsName, // Name of the Stream
CNtfsStream *pnffstm) // Previously Open NTFS Stream (list)
{ HRESULT sc=S_OK; HANDLE ev;
nffITrace( "CNtfsStream::Init" );
// We now own this file handle, and are responsible for closing it.
_hFile = hFile;
// Save the STGM_ flags so we can return them in a Stat call.
_grfMode = grfMode;
// Save the stream name
if( NULL != _pwcsName ) { CoTaskMemFree( _pwcsName ); _pwcsName = NULL; }
if( NULL != pwcsName ) { ULONG cbName = sizeof(WCHAR)*((ULONG)wcslen(pwcsName) + 1); nffMem( _pwcsName = reinterpret_cast<WCHAR*> ( CoTaskMemAlloc( cbName ))); StringCbCopy( _pwcsName, cbName, pwcsName ); }
// All the streams live on a list held by the root Storage.
if(NULL != pnffstm) InsertSelfIntoList(pnffstm);
nffChk( _nffMappedStream.Init( _hFile ));
EH_Err: return sc; }
//+----------------------------------------------------------------------------
//
// CNtfsStream Non-Interface::ShutDown
//
// Flush data, Close File handle and mark the object as reverted.
// This is called when the Storage is released and when the Oplock Breaks.
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::ShutDown() { nffITrace( "CNtfsStream::ShutDown" ); HRESULT sc=S_OK;
if( INVALID_HANDLE_VALUE == _hFile ) return S_OK;
nffDebug(( DEB_INFO, "CNtfsStream::ShutDown(%x)\n", _hFile ));
//
// Shut down the mapped stream
//
_nffMappedStream.ShutDown();
//
// Close the file/stream handle and mark the IStream object as
// Reverted by giving the file handle an invalid value.
//
CloseHandle(_hFile); _hFile = INVALID_HANDLE_VALUE;
// We don't need the parent CNtfsStorage any longer, and more importantly it could
// be going away.
_pnffstg = NULL;
//
// Remove IStream object from the Storage's list
//
RemoveSelfFromList();
return S_OK; }
//+----------------------------------------------------------------------------
//
// CNtfsStream Non-Interface::Rename
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Rename( const WCHAR *pwcsName, BOOL fOverWrite ) { IO_STATUS_BLOCK io_status_block; PFILE_RENAME_INFORMATION pFileRenInfo=NULL; // _alloca()'ed
NTSTATUS status;
CNtfsStreamName nsnName = pwcsName; // Convert to ":name:$DATA"
LONG cbBufSize=0; HRESULT sc=S_OK;
nffDebug(( DEB_INFO | DEB_WRITE, "CNtfsStream::Rename(%ws -> %ws)\n", _pwcsName, pwcsName ));
Lock( INFINITE ); nffChk( CheckReverted() );
// Size and allocate a FILE_RENAME_INFOMATION buffer. The size argument
// to NtSetInformationFile must be correct, so subtract 1 WCHAR for the
// FileName[1] in the struct we are not using.
//
cbBufSize = sizeof(FILE_RENAME_INFORMATION) - sizeof(WCHAR); cbBufSize += (ULONG)nsnName.Count() * sizeof(WCHAR); __try { pFileRenInfo = (PFILE_RENAME_INFORMATION) _alloca( cbBufSize ); } __except(EXCEPTION_EXECUTE_HANDLER) { nffErr(EH_Err, STG_E_INSUFFICIENTMEMORY); }
// Load the FILE_RENAME_INFORMATION structure
pFileRenInfo->ReplaceIfExists = (BOOLEAN) fOverWrite; pFileRenInfo->RootDirectory = NULL; pFileRenInfo->FileNameLength = (ULONG)nsnName.Count() * sizeof(WCHAR); memcpy( pFileRenInfo->FileName, (const WCHAR*)nsnName, pFileRenInfo->FileNameLength );
// Rename the stream
status = NtSetInformationFile( _hFile, &io_status_block, pFileRenInfo, cbBufSize, FileRenameInformation ); if( !NT_SUCCESS(status) ) { nffChk( sc = NtStatusToScode(status) ); }
// Discard the old name and Save the new name
CoTaskMemFree( _pwcsName );
// reuse cbBufSize
cbBufSize = sizeof(WCHAR) * ((ULONG)wcslen(pwcsName)+1);
nffMem( _pwcsName = (WCHAR*) CoTaskMemAlloc( cbBufSize )); StringCbCopy( _pwcsName, cbBufSize, pwcsName );
sc = S_OK;
EH_Err:
Unlock(); return( sc );
}
//+----------------------------------------------------------------------------
//
// CNtfsStream Non-Interface::Delete
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::Delete() { HRESULT sc=S_OK;
nffDebug(( DEB_INFO | DEB_WRITE, "Delete(stm=%ws)\n", _pwcsName ));
nffChk( CheckReverted() );
if( IsWriteable() ) nffChk( DeleteStream( &_hFile ));
EH_Err:
return( sc );
}
//+----------------------------------------------------------------------------
//
// CNtfsStream Non-Interface::DeleteStream
//
// This method is static so that it can be called globally.
//
//+----------------------------------------------------------------------------
HRESULT // static
CNtfsStream::DeleteStream( HANDLE *phStream ) { HRESULT sc=S_OK; NTSTATUS status = STATUS_SUCCESS; FILE_DISPOSITION_INFORMATION Disposition; IO_STATUS_BLOCK IoStatusBlock;
// Execute the following statement:
// Disposition.DeleteFile = TRUE;
// We can't actually write the code that way, because "DeleteFile" is #defined to
// "DeleteFileW".
nffDebug(( DEB_INFO | DEB_WRITE, "DeleteStream(hdl=%x)\n", *phStream ));
*reinterpret_cast<BOOLEAN*>(&Disposition) = TRUE; DfpAssert( sizeof(Disposition) == sizeof(BOOLEAN) );
// Mark the file for delete on close
// Note that if this is the Contents stream we can delete it successfully,
// but NTFS actually just truncates it to zero length.
status = NtSetInformationFile( *phStream, &IoStatusBlock, &Disposition, sizeof(Disposition), FileDispositionInformation ); if( !NT_SUCCESS(status) ) { nffErr( EH_Err, NtStatusToScode(status) ); }
NtClose( *phStream ); *phStream = INVALID_HANDLE_VALUE;
EH_Err:
return( sc );
}
//+----------------------------------------------------------------------------
//
// CNtfsStream::SetFileSize (private, non-interface method)
//
// Set the size of the _hFile. This is used by the IStream & IMappedStream
// SetSize methods
//
//+----------------------------------------------------------------------------
HRESULT // private
CNtfsStream::SetFileSize( const CULargeInteger &uliNewSize ) { nffITrace( "CNtfsStream::SetFileSize" ); HRESULT sc = S_OK; CLargeInteger liEOF;
// We have to convert uliNewSize into a LARGE_INTEGER, so ensure that it can
// be cast without loss of data.
liEOF = static_cast<CLargeInteger>(uliNewSize); if( liEOF < 0 ) nffErr( EH_Err, STG_E_INVALIDPARAMETER );
// Move to what will be the new end-of-file position.
liEOF.LowPart = SetFilePointer( _hFile, liEOF.LowPart, &liEOF.HighPart, FILE_BEGIN ); if( 0xFFFFFFFF == liEOF.LowPart && NO_ERROR != GetLastError() ) nffErr( EH_Err, LAST_SCODE );
// Set this as the new eof
if( !SetEndOfFile( _hFile )) nffErr( EH_Err, LAST_SCODE );
EH_Err:
return( sc );
}
//+-------------------------------------------------------------------
//
// Member: CNtfsStream::InsertSelfIntoList/RemoveSelfFromList
//
//--------------------------------------------------------------------
// We are passed a "current" element of a doubly linked list.
// Insert "this" right after the given List element.
//
VOID CNtfsStream::InsertSelfIntoList(CNtfsStream *pnffstmCurrent) { nffDebug(( DEB_ITRACE, "CNtfsStream::InsertSelfIntoList this=%x\n", this ));
// If we're already in the list, or there is no list, then we're done.
if( NULL != _pnffstmNext || NULL == pnffstmCurrent ) return;
// Note that this lock takes the tree mutex - it locks the whole
// storage
Lock( INFINITE );
// "this" point back to the current element
// and points forward to current's next element
_pnffstmPrev = pnffstmCurrent; _pnffstmNext = pnffstmCurrent->_pnffstmNext;
// the current element now points forward to "this"
// and if the next is not NULL it points back at this.
pnffstmCurrent->_pnffstmNext = this; if(NULL != _pnffstmNext) _pnffstmNext->_pnffstmPrev = this;
Unlock(); }
VOID CNtfsStream::RemoveSelfFromList() { nffDebug(( DEB_ITRACE, "CNtfsStream::RemoveSelfFromList this=%x\n", this ));
// Note that this lock takes the tree mutex - it locks the whole
// storage
Lock( INFINITE );
// My next element's previous pointer is given my previous pointer.
if(NULL != _pnffstmNext) _pnffstmNext->_pnffstmPrev = _pnffstmPrev;
// My previous element's next pointer is given my next pointer.
if(NULL != _pnffstmPrev) _pnffstmPrev->_pnffstmNext = _pnffstmNext;
_pnffstmNext = NULL; _pnffstmPrev = NULL;
Unlock(); }
//+-------------------------------------------------------------------
//
// CNtfsStream Non-Interface::SyncReadAtFile
//
// Synopsis: Provide synchronous IO for a file handle open in
// asynchronous mode.
//
//--------------------------------------------------------------------
HRESULT CNtfsStream::SyncReadAtFile( ULARGE_INTEGER ulOffset, PVOID pv, ULONG cb, PULONG pcbRead) { HRESULT sc=S_OK; LONG err=ERROR_SUCCESS; //
// We use the single OVERLAPPED structure in the object.
// This saves us from creating an Event everytime. We
// require the TreeMutex will keep us single threaded.
//
_ovlp.Offset = ulOffset.LowPart; _ovlp.OffsetHigh = ulOffset.HighPart;
nffDebug(( DEB_ITRACE | DEB_INFO | DEB_READ, "SyncReadAtFile(_hFile=0x%x off=%x:%x, pv=0x%x, cb=0x%x )" " stream='%ws'\n", _hFile, ulOffset.HighPart, ulOffset.LowPart, pv, cb, _pwcsName ));
if( !ReadFile( _hFile, pv, cb, pcbRead, &_ovlp ) ) { err = GetLastError();
if(ERROR_SUCCESS != err && ERROR_HANDLE_EOF != err) nffChk( HRESULT_FROM_WIN32( err ) ); }
nffDebug(( DEB_INFO, "SyncReadAtFile() read 0x%x bytes.\n", *pcbRead ));
EH_Err: return sc; }
//+-------------------------------------------------------------------
//
// CNtfsStream Non-Interface::SyncWriteAtFile
//
// Synopsis: Provide synchronous IO for a file handle open in
// asynchronous mode.
//
//--------------------------------------------------------------------
HRESULT CNtfsStream::SyncWriteAtFile( ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, PULONG pcbWritten) { HRESULT sc=S_OK; //
// We use the single OVERLAPPED structure in the object.
// We require the TreeMutex will keep us single threaded.
//
_ovlp.Offset = ulOffset.LowPart; _ovlp.OffsetHigh = ulOffset.HighPart;
nffDebug(( DEB_ITRACE | DEB_INFO | DEB_WRITE, "SyncWriteAtFile(_hFile=0x%x, off=%x:%x, pv=0x%x, cb=0x%x );" " stream='%ws'\n", _hFile, ulOffset.HighPart, ulOffset.LowPart, pv, cb, _pwcsName ));
//
// We expect either OK or FALSE/ERROR_IO_PENDING.
//
if( !WriteFile( _hFile, pv, cb, pcbWritten, &_ovlp ) ) { nffChk( HRESULT_FROM_WIN32( GetLastError() ) ); }
nffDebug(( DEB_ITRACE | DEB_INFO, "SyncWriteAtFile() wrote 0x%x bytes.\n", *pcbWritten ));
EH_Err: return sc; }
//+----------------------------------------------------------------------------
//
// CNtfsStream Non-Interface::SetStreamTime
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::SetStreamTime( const FILETIME* pctime, const FILETIME* patime, const FILETIME* pmtime) { nffDebug((DEB_INFO | DEB_STATCTRL | DEB_ITRACE, "SetStreamTime() hdl=%x, stream='%ws'\n", _hFile, _pwcsName ));
return SetFileHandleTime( _hFile, pctime, patime, pmtime ); }
//+----------------------------------------------------------------------------
//
// CNtfsStream Static Non-Interface::SetFileHandleTime
//
//+----------------------------------------------------------------------------
HRESULT CNtfsStream::SetFileHandleTime( HANDLE hFile, const FILETIME* pctime, const FILETIME* patime, const FILETIME* pmtime) { HRESULT sc=S_OK;
if( INVALID_HANDLE_VALUE == hFile ) { nffDebug(( DEB_IWARN, "CNtfsStream::SetFileHandleTime on hFile == -1\n" )); return S_OK; }
nffDebug(( DEB_STATCTRL | DEB_ITRACE, "SetFileHandleTime(%p %p %p) hdl=%x\n", pctime, patime, pmtime, hFile ));
nffBool( ::SetFileTime( hFile, pctime, patime, pmtime ) ); EH_Err: return sc; }
#if DBG
HRESULT STDMETHODCALLTYPE CNtfsStream::UseNTFS4Streams( BOOL fUseNTFS4Streams ) { return( _nffMappedStream.UseNTFS4Streams( fUseNTFS4Streams )); } #endif // #if DBG
#if DBG
HRESULT STDMETHODCALLTYPE CNtfsStream::GetFormatVersion(WORD *pw) { return( E_NOTIMPL ); } #endif // #if DBG
#if DBG
HRESULT STDMETHODCALLTYPE CNtfsStream::SimulateLowMemory( BOOL fSimulate ) { return( _nffMappedStream.SimulateLowMemory( fSimulate )); } #endif // #if DBG
#if DBG
HRESULT STDMETHODCALLTYPE CNtfsStream::GetLockCount() { return( NULL == _pnffstg ? 0 : _pnffstg->GetLockCount() ); } #endif // #if DBG
#if DBG
HRESULT STDMETHODCALLTYPE CNtfsStream::IsDirty() { return( E_NOTIMPL ); } #endif // #if DBG
|