/**************************************************************************\ * * 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(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(cbSourceSize) < 0 || static_cast(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(cbPerCopy+STREAMBUFFERSIZE), STREAM_SEEK_CUR, NULL ) ); nffChk( pstm->Seek( -static_cast(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 ( 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 ( 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(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 ); }