Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1524 lines
39 KiB

//+============================================================================
//
// 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