/*++ Copyright (c) 2000 Microsoft Corporation Module Name: filestm.c Abstract: This modules implements IStream over a file. Author: Jay Krell (a-JayK) June 2000 Revision History: --*/ #define RTL_DECLARE_STREAMS 1 #define RTL_DECLARE_FILE_STREAM 1 #include "ntos.h" #include "nt.h" #include "ntrtl.h" #include "nturtl.h" #include "objidl.h" #define RTLP_FILE_STREAM_NOT_IMPL(x) \ KdPrintEx((DPFLTR_SXS_ID, DPFLTR_ERROR_LEVEL, "RTLSXS: %s() E_NOTIMPL", __FUNCTION__)); \ return E_NOTIMPL; #if !defined(RTLP_FILE_STREAM_HRESULT_FROM_STATUS) #if defined(RTLP_HRESULT_FROM_STATUS) #define RTLP_FILE_STREAM_HRESULT_FROM_STATUS(x) RTLP_HRESULT_FROM_STATUS(x) #else #define RTLP_FILE_STREAM_HRESULT_FROM_STATUS(x) HRESULT_FROM_WIN32(RtlNtStatusToDosErrorNoTeb(x)) //#define RTLP_FILE_STREAM_HRESULT_FROM_STATUS(x) HRESULT_FROM_WIN32(RtlNtStatusToDosError(x)) //#define RTLP_FILE_STREAM_HRESULT_FROM_STATUS(x) HRESULT_FROM_NT(x) #endif #endif HRESULT STDMETHODCALLTYPE RtlInitFileStream( PRTL_FILE_STREAM FileStream ) { RtlZeroMemory(FileStream, sizeof(*FileStream)); return NOERROR; } HRESULT STDMETHODCALLTYPE RtlCloseFileStream( PRTL_FILE_STREAM FileStream ) { const HANDLE FileHandle = FileStream->FileHandle; NTSTATUS Status = STATUS_SUCCESS; HRESULT Hr = NOERROR; if (FileHandle != NULL) { FileStream->FileHandle = NULL; Status = NtClose(FileHandle); if (!NT_SUCCESS(Status)) { Hr = RTLP_FILE_STREAM_HRESULT_FROM_STATUS(Status); } } return Hr; } ULONG STDMETHODCALLTYPE RtlAddRefFileStream( PRTL_FILE_STREAM FileStream ) { LONG ReferenceCount = InterlockedIncrement(&FileStream->ReferenceCount); return ReferenceCount; } ULONG STDMETHODCALLTYPE RtlReleaseFileStream( PRTL_FILE_STREAM FileStream ) { LONG ReferenceCount = InterlockedDecrement(&FileStream->ReferenceCount); if (ReferenceCount == 0 && FileStream->FinalRelease != NULL) { FileStream->FinalRelease(FileStream); } return ReferenceCount; } HRESULT STDMETHODCALLTYPE RtlQueryInterfaceFileStream( IStream* Functions, PRTL_FILE_STREAM Data, const IID* Interface, PVOID* Object ) { if (IsEqualGUID(Interface, &IID_IUnknown) || IsEqualGUID(Interface, &IID_IStream) || IsEqualGUID(Interface, &IID_ISequentialStream) ) { InterlockedIncrement(&Data->ReferenceCount); *Object = Functions; return NOERROR; } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE RtlReadFileStream( PRTL_FILE_STREAM FileStream, PVOID Buffer, ULONG BytesToRead, ULONG* BytesRead ) { // // based on Win32 ReadFile // we should allow asynchronous i/o here.. put the IO_STATUS_BLOCK // in the RTL_FILE_STREAM.. // IO_STATUS_BLOCK IoStatusBlock; const HANDLE FileHandle = FileStream->FileHandle; NTSTATUS Status = STATUS_SUCCESS; HRESULT Hr = NOERROR; Status = NtReadFile( FileHandle, NULL, // optional event NULL, // optional apc routine NULL, // optional apc context &IoStatusBlock, Buffer, BytesToRead, NULL, // optional offset NULL // optional "key" ); if ( Status == STATUS_PENDING) { // Operation must complete before return & IoStatusBlock destroyed Status = NtWaitForSingleObject(FileHandle, FALSE, NULL); if (NT_SUCCESS(Status)) { Status = IoStatusBlock.Status; } } if (NT_SUCCESS(Status)) { *BytesRead = (ULONG)IoStatusBlock.Information; // cast from ULONG_PTR Hr = NOERROR; } else if (Status == STATUS_END_OF_FILE) { *BytesRead = 0; Hr = NOERROR; } else { if (NT_WARNING(Status)) { *BytesRead = (ULONG)IoStatusBlock.Information; // cast from ULONG_PTR } Hr = RTLP_FILE_STREAM_HRESULT_FROM_STATUS(Status); } return Hr; } HRESULT STDMETHODCALLTYPE RtlWriteFileStream( PRTL_FILE_STREAM FileStream, const VOID* Buffer, ULONG BytesToWrite, ULONG* BytesWritten ) { RTLP_FILE_STREAM_NOT_IMPL(Write); } HRESULT STDMETHODCALLTYPE RtlSeekFileStream( PRTL_FILE_STREAM FileStream, LARGE_INTEGER Distance, DWORD Origin, ULARGE_INTEGER* NewPosition ) { // // based closely on Win32 SetFilePointer // HRESULT Hr = NOERROR; NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK IoStatusBlock; FILE_POSITION_INFORMATION CurrentPosition; const HANDLE FileHandle = FileStream->FileHandle; switch (Origin) { case STREAM_SEEK_SET: CurrentPosition.CurrentByteOffset = Distance; break; case STREAM_SEEK_CUR: Status = NtQueryInformationFile( FileHandle, &IoStatusBlock, &CurrentPosition, sizeof(CurrentPosition), FilePositionInformation ); if (!NT_SUCCESS(Status)) { Hr = RTLP_FILE_STREAM_HRESULT_FROM_STATUS(Status); goto Exit; } CurrentPosition.CurrentByteOffset.QuadPart += Distance.QuadPart; break; case STREAM_SEEK_END: { FILE_STANDARD_INFORMATION StandardInfo; Status = NtQueryInformationFile( FileHandle, &IoStatusBlock, &StandardInfo, sizeof(StandardInfo), FileStandardInformation ); if (!NT_SUCCESS(Status)) { Hr = RTLP_FILE_STREAM_HRESULT_FROM_STATUS(Status); goto Exit; } // SetFilePointer uses + here. Which is correct? // Descriptions of how Seek work are always unclear on this point.. CurrentPosition.CurrentByteOffset.QuadPart = StandardInfo.EndOfFile.QuadPart - Distance.QuadPart; } break; default: // You would expect this to be E_INVALIDARG, but since // the IStream // but IStream docs weakly suggest STG_E_INVALIDPOINTER. Hr = STG_E_INVALIDFUNCTION; // E_INVALIDARG? goto Exit; } if (CurrentPosition.CurrentByteOffset.QuadPart < 0) { // You would expect this to be E_INVALIDARG, // but IStream docs say to return STG_E_INVALIDPOINTER. Hr = STG_E_INVALIDPOINTER; goto Exit; } // // Set the current file position // Status = NtSetInformationFile( FileHandle, &IoStatusBlock, &CurrentPosition, sizeof(CurrentPosition), FilePositionInformation ); NewPosition->QuadPart = CurrentPosition.CurrentByteOffset.QuadPart; Hr = NOERROR; Exit: return Hr; } HRESULT STDMETHODCALLTYPE RtlSetFileStreamSize( PRTL_FILE_STREAM FileStream, ULARGE_INTEGER NewSize ) { // // based on Win32 SetEndOfFile, but is independent of current seek pointer // NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK IoStatusBlock; FILE_END_OF_FILE_INFORMATION EndOfFile; FILE_ALLOCATION_INFORMATION Allocation; const HANDLE FileHandle = FileStream->FileHandle; EndOfFile.EndOfFile.QuadPart = NewSize.QuadPart; Allocation.AllocationSize.QuadPart = NewSize.QuadPart; Status = NtSetInformationFile( FileHandle, &IoStatusBlock, &EndOfFile, sizeof(EndOfFile), FileEndOfFileInformation ); if (!NT_SUCCESS(Status)) { goto Exit; } Status = NtSetInformationFile( FileHandle, &IoStatusBlock, &Allocation, sizeof(Allocation), FileAllocationInformation ); if (!NT_SUCCESS(Status)) { goto Exit; } Status = STATUS_SUCCESS; Exit: if (NT_SUCCESS(Status)) { return NOERROR; } else { return RTLP_FILE_STREAM_HRESULT_FROM_STATUS(Status); } } HRESULT STDMETHODCALLTYPE RtlCopyFileStreamTo( PRTL_FILE_STREAM FileStream, IStream* AnotherStream, ULARGE_INTEGER NumberOfBytesToCopyLargeInteger, ULARGE_INTEGER* NumberOfBytesRead, ULARGE_INTEGER* NumberOfBytesWrittenLargeInteger ) { // // Memory mapping where possible would be nice (but beware sockets and consoles). // see \vsee\lib\CWin32Stream. // RTLP_FILE_STREAM_NOT_IMPL(CopyTo); } HRESULT STDMETHODCALLTYPE RtlCommitFileStream( PRTL_FILE_STREAM FileStream, ULONG Flags ) { RTLP_FILE_STREAM_NOT_IMPL(Commit); } HRESULT STDMETHODCALLTYPE RtlRevertFileStream( PRTL_FILE_STREAM FileStream ) { RTLP_FILE_STREAM_NOT_IMPL(Revert); } HRESULT STDMETHODCALLTYPE RtlLockFileStreamRegion( PRTL_FILE_STREAM FileStream, ULARGE_INTEGER Offset, ULARGE_INTEGER NumberOfBytes, ULONG LockType ) { RTLP_FILE_STREAM_NOT_IMPL(LockRegion); } HRESULT STDMETHODCALLTYPE RtlUnlockFileStreamRegion( PRTL_FILE_STREAM FileStream, ULARGE_INTEGER Offset, ULARGE_INTEGER NumberOfBytes, ULONG LockType ) { RTLP_FILE_STREAM_NOT_IMPL(UnlockRegion); } HRESULT STDMETHODCALLTYPE RtlStatFileStream( PRTL_FILE_STREAM FileStream, STATSTG* StatusInformation, ULONG Flags ) { RTLP_FILE_STREAM_NOT_IMPL(Stat); } HRESULT STDMETHODCALLTYPE RtlCloneFileStream( PRTL_FILE_STREAM FileStream, IStream** NewStream ) { RTLP_FILE_STREAM_NOT_IMPL(Clone); }