WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/ntfsstream.cpp
2020-09-30 16:53:49 +02:00

925 lines
23 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1999 Microsoft Corporation
*
* Module Name:
*
* NtfsStream.cpp
*
* Abstract:
*
* This file provides the Flat File IStream definition.
*
* Created:
*
* 4/26/1999 Mike Hillberg
*
\**************************************************************************/
#include "precomp.hpp"
#include "LargeInt.hpp"
#include "time.h"
#include "FileTime.hpp"
#include "NtfsStream.hpp"
IStream *
CreateStreamOnFile(
const OLECHAR * pwcsName,
UINT access // GENERIC_READ and/or GENERIC_WRITE
)
{
HANDLE hFile;
FileStream * stream;
UINT disposition;
DWORD grfMode = STGM_SHARE_EXCLUSIVE;
DWORD shareMode = 0;
switch (access)
{
case GENERIC_READ:
disposition = OPEN_EXISTING;
shareMode = FILE_SHARE_READ;
grfMode |= STGM_READ;
break;
case GENERIC_WRITE:
disposition = CREATE_ALWAYS;
grfMode |= STGM_WRITE;
break;
// Note that OPEN_ALWAYS does NOT clear existing file attributes (like size)
case GENERIC_READ|GENERIC_WRITE:
disposition = OPEN_ALWAYS;
grfMode |= STGM_READWRITE;
break;
default:
return NULL;
}
if (Globals::IsNt)
{
hFile = CreateFileW(pwcsName, access, shareMode, NULL,
disposition, FILE_ATTRIBUTE_NORMAL, NULL);
}
else // Windows 9x - non-Unicode
{
AnsiStrFromUnicode nameStr(pwcsName);
if (nameStr.IsValid())
{
hFile = CreateFileA(nameStr, access, shareMode, NULL,
disposition, FILE_ATTRIBUTE_NORMAL, NULL);
}
else
{
hFile = INVALID_HANDLE_VALUE;
}
}
if ((hFile != INVALID_HANDLE_VALUE) && (hFile != NULL))
{
stream = new FileStream();
if (stream != NULL)
{
HRESULT hResult;
hResult = stream->Init(hFile, grfMode, pwcsName);
if (!FAILED(hResult))
{
return stream;
}
delete stream;
}
CloseHandle(hFile);
}
return NULL;
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::AddRef (IUnknown)
//
//+----------------------------------------------------------------------------
ULONG
FileStream::AddRef()
{
LONG cRefs;
cRefs = InterlockedIncrement( &_cRefs );
return cRefs;
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Release (IUnknown)
//
//+----------------------------------------------------------------------------
ULONG
FileStream::Release()
{
ULONG ulRet = InterlockedDecrement( &_cRefs );
if( 0 == ulRet )
delete this;
return( ulRet );
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::AddRef (IUnknown)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::QueryInterface(
REFIID riid,
void** ppv )
{
HRESULT sc=S_OK;
if( IsEqualIID( riid, IID_IUnknown )
||
IsEqualIID( riid, IID_IStream )
||
IsEqualIID( riid, IID_ISequentialStream ) )
{
*ppv = static_cast<IStream*>(this);
AddRef();
return( S_OK );
}
else
{
return( E_NOINTERFACE );
}
return sc;
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Seek (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER *puliNewPos)
{
HRESULT sc = S_OK;
LARGE_INTEGER liFileSize;
LARGE_INTEGER liNewPos;
Lock( INFINITE );
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:
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: FileStream::SetSize (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::SetSize(
ULARGE_INTEGER uliNewSize)
{
HRESULT sc = S_OK;
CLargeInteger liEOF;
if ( uliNewSize.HighPart != 0 )
nffErr(EH_Err, STG_E_INVALIDFUNCTION);
Lock( INFINITE );
nffChk( CheckReverted() );
// If this stream is mapped, set the size accordingly
sc = SetFileSize( CULargeInteger(uliNewSize) );
if( !FAILED(sc) )
sc = S_OK;
EH_Err:
Unlock();
return( sc);
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::CopyTo (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::CopyTo(
IStream *pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER *pcbRead,
ULARGE_INTEGER *pcbWritten)
{
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;
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 && (cbRequested > 0))
{
nffChk( this->Seek( -static_cast<CLargeInteger>(cbPerCopy+STREAMBUFFERSIZE),
STREAM_SEEK_CUR, NULL ) );
nffChk( pstm->Seek( -static_cast<CLargeInteger>(cbPerCopy+STREAMBUFFERSIZE),
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: FileStream::Commit (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Commit( DWORD grfCommitFlags )
{
HRESULT sc = S_OK;
Lock( INFINITE );
nffChk( CheckReverted() );
// NTRAID#NTBUG9-368729-2001-04-13-gilmanw "ISSUE: FileStream object - handle other stream commit flags"
// Are there other commit flags that need to be handled?
if( !(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE & grfCommitFlags) )
{
if( !FlushFileBuffers( _hFile ))
sc = HRESULT_FROM_WIN32( GetLastError() );
}
EH_Err:
Unlock();
return sc;
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Revert (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Revert(void)
{
// We only support direct-mode.
return CheckReverted();
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::LockRegion (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
HRESULT sc = S_OK;
Lock( INFINITE );
nffChk( CheckReverted() );
// NTRAID#NTBUG9-368745-2001-04-13-gilmanw "ISSUE: FileStream::LockRegion - handle other lock flags"
// Are all the lock types supported here?
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, HRESULT_FROM_WIN32( GetLastError() ));
}
EH_Err:
Unlock();
return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Stat (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Stat(
STATSTG *pstatstg,
DWORD grfStatFlag)
{
STATSTG statstg;
HRESULT sc = S_OK;
BY_HANDLE_FILE_INFORMATION ByHandleFileInformation;
statstg.pwcsName = NULL;
Lock( INFINITE );
nffChk( CheckReverted() );
ZeroMemory((void*)&statstg, sizeof(STATSTG));
// Get the name, if desired
if( (STATFLAG_NONAME & grfStatFlag) )
statstg.pwcsName = NULL;
else
{
nffMem( statstg.pwcsName = reinterpret_cast<WCHAR*>
( CoTaskMemAlloc( sizeof(WCHAR)*(UnicodeStringLength(_pwcsName) + 1) )));
UnicodeStringCopy( statstg.pwcsName, _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, HRESULT_FROM_WIN32( GetLastError() ));
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 );
Unlock();
return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Clone (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Clone(
IStream** ppstm)
{
// NTRAID#NTBUG9-368747-2001-04-13-gilmanw "ISSUE: FileStream::Clone returns E_NOTIMPL"
return( E_NOTIMPL );
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Read (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Read(
void* pv,
ULONG cb,
ULONG* pcbRead)
{
LARGE_INTEGER lOffset;
HRESULT sc = S_OK;
ULONG cbRead = 0;
lOffset.LowPart = _liCurrentSeekPosition.LowPart;
lOffset.HighPart = _liCurrentSeekPosition.HighPart;
if (lOffset.HighPart < 0)
{
return( TYPE_E_SIZETOOBIG );
}
Lock( INFINITE );
nffChk( CheckReverted() );
if(SetFilePointer(_hFile, lOffset.LowPart, &lOffset.HighPart,
FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
nffChk( HRESULT_FROM_WIN32(GetLastError()));
}
if(!ReadFile(_hFile, pv, cb, &cbRead, NULL))
{
nffChk( HRESULT_FROM_WIN32(GetLastError()));
}
_liCurrentSeekPosition += cbRead;
if( NULL != pcbRead )
*pcbRead = cbRead;
EH_Err:
Unlock();
return( sc );
}
//+----------------------------------------------------------------------------
//
// Method: FileStream::Write (IStream)
//
//+----------------------------------------------------------------------------
HRESULT
FileStream::Write(
const void* pv,
ULONG cb,
ULONG* pcbWritten)
{
LARGE_INTEGER lOffset;
HRESULT sc = S_OK;
ULONG cbWritten = 0;
lOffset.LowPart = _liCurrentSeekPosition.LowPart;
lOffset.HighPart = _liCurrentSeekPosition.HighPart;
if (lOffset.HighPart < 0)
{
return( TYPE_E_SIZETOOBIG );
}
Lock( INFINITE );
nffChk( CheckReverted() );
if(SetFilePointer(_hFile, lOffset.LowPart, &lOffset.HighPart,
FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
nffChk( HRESULT_FROM_WIN32(GetLastError()));
}
if(!WriteFile(_hFile, pv, cb, &cbWritten, NULL))
{
nffChk(HRESULT_FROM_WIN32(GetLastError()));
}
_liCurrentSeekPosition += cbWritten;
if( NULL != pcbWritten )
*pcbWritten = cbWritten;
EH_Err:
Unlock();
return( sc );
}
//+-------------------------------------------------------------------
//
// Member: FileStream Constructor
//
//--------------------------------------------------------------------
FileStream::FileStream( )
{
_cRefs = 1;
_grfMode = 0;
_hFile = INVALID_HANDLE_VALUE;
_liCurrentSeekPosition = 0;
_pwcsName = NULL;
_bCritSecInitialized = FALSE;
__try
{
InitializeCriticalSection( &_critsec );
_bCritSecInitialized = TRUE;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// If we've thrown then _bCritSecInitialized will be FALSE and
// Init() will automatically fail.
}
}
//+-------------------------------------------------------------------
//
// Member: FileStream Destructor
//
//--------------------------------------------------------------------
FileStream::~FileStream()
{
// Close the file
if( INVALID_HANDLE_VALUE != _hFile )
CloseHandle( _hFile );
if( NULL != _pwcsName )
CoTaskMemFree( _pwcsName );
if (_bCritSecInitialized)
{
// We don't need to reset _bCrisSecInitialized to FALSE since the
// object has been destroyed
DeleteCriticalSection( &_critsec );
}
}
//+-------------------------------------------------------------------
//
// Member: FileStream::Init
//
//--------------------------------------------------------------------
HRESULT
FileStream::Init(
HANDLE hFile, // File handle of this Stream.
DWORD grfMode, // Open Modes
const OLECHAR * pwcsName) // Name of the Stream
{
// If we couldn't allocate the critical section then return an Error
if (!_bCritSecInitialized)
{
return E_FAIL;
}
HRESULT sc=S_OK;
HANDLE ev;
// 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 )
{
nffMem( _pwcsName = reinterpret_cast<WCHAR*>
( CoTaskMemAlloc( sizeof(WCHAR)*(UnicodeStringLength(pwcsName) + 1) )));
UnicodeStringCopy( _pwcsName, pwcsName );
}
EH_Err:
return sc;
}
//+----------------------------------------------------------------------------
//
// FileStream 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
FileStream::ShutDown()
{
HRESULT sc=S_OK;
if( INVALID_HANDLE_VALUE == _hFile )
return S_OK;
//
// 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;
return S_OK;
}
//+----------------------------------------------------------------------------
//
// FileStream::SetFileSize (private, non-interface method)
//
// Set the size of the _hFile. This is used by the IStream & IMappedStream
// SetSize methods
//
//+----------------------------------------------------------------------------
HRESULT // private
FileStream::SetFileSize( const CULargeInteger &uliNewSize )
{
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, HRESULT_FROM_WIN32( GetLastError() ));
// Set this as the new eof
if( !SetEndOfFile( _hFile ))
nffErr( EH_Err, HRESULT_FROM_WIN32( GetLastError() ));
EH_Err:
return( sc );
}
HRESULT
FileStream::UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType)
{
HRESULT sc = S_OK;
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, HRESULT_FROM_WIN32(GetLastError()) );
}
EH_Err:
Unlock();
return( sc );
}