mirror of https://github.com/lianthony/NT4.0
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.
585 lines
13 KiB
585 lines
13 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1991-1993
|
|
//
|
|
// File: fstream.cpp
|
|
//
|
|
// implements a IStream on a DOS file.
|
|
//
|
|
// History:
|
|
// 02-20-94 ToddLa created
|
|
//
|
|
//!!!BUGBUG: put VTable in .text
|
|
//---------------------------------------------------------------------------
|
|
|
|
#define NO_INCLUDE_UNION
|
|
|
|
#include "shellprv.h"
|
|
|
|
|
|
//
|
|
// Class definition
|
|
//
|
|
class FileStream {
|
|
|
|
public:
|
|
// *** IUnknown methods ***
|
|
STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj);
|
|
STDMETHOD_(ULONG,AddRef) (THIS);
|
|
STDMETHOD_(ULONG,Release) (THIS);
|
|
|
|
// *** IStream methods ***
|
|
STDMETHOD(Read) (THIS_ VOID *pv, ULONG cb, ULONG *pcbRead);
|
|
STDMETHOD(Write) (THIS_ VOID const *pv, ULONG cb, ULONG *pcbWritten);
|
|
STDMETHOD(Seek) (THIS_ LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
|
|
STDMETHOD(SetSize) (THIS_ ULARGE_INTEGER libNewSize);
|
|
STDMETHOD(CopyTo) (THIS_ IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
|
|
STDMETHOD(Commit) (THIS_ DWORD grfCommitFlags);
|
|
STDMETHOD(Revert) (THIS);
|
|
STDMETHOD(LockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
|
|
STDMETHOD(UnlockRegion) (THIS_ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
|
|
STDMETHOD(Stat) (THIS_ STATSTG *pstatstg, DWORD grfStatFlag);
|
|
STDMETHOD(Clone)(THIS_ IStream * *ppstm);
|
|
|
|
public:
|
|
FileStream(HFILE hf, UINT mode);
|
|
~FileStream();
|
|
|
|
private:
|
|
int cRef; // Reference count
|
|
HFILE hFile; // the file.
|
|
BOOL fWrite; // TRUE if writing.
|
|
|
|
|
|
int ib;
|
|
int cbBufLen; // length of buffer if reading
|
|
#define CBSTREAMBUF 4096
|
|
BYTE ab[CBSTREAMBUF];
|
|
};
|
|
|
|
//
|
|
// contructor
|
|
//
|
|
FileStream::FileStream(HFILE hf, UINT mode)
|
|
{
|
|
this->cRef = 1;
|
|
this->hFile = hf;
|
|
this->fWrite = (mode & OF_WRITE);
|
|
|
|
if (this->fWrite)
|
|
{
|
|
this->ib = 0;
|
|
}
|
|
else
|
|
{
|
|
this->cbBufLen = 0;
|
|
this->ib = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Destructor
|
|
//
|
|
FileStream::~FileStream()
|
|
{
|
|
if (this->fWrite)
|
|
Commit(0);
|
|
|
|
|
|
#ifdef UNICODE
|
|
Assert((HANDLE) this->hFile != INVALID_HANDLE_VALUE);
|
|
CloseHandle((HANDLE)hFile);
|
|
#else
|
|
Assert(this->hFile != HFILE_ERROR);
|
|
_lclose(hFile);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::QueryInterface
|
|
//
|
|
HRESULT STDMETHODCALLTYPE FileStream::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IStream) || IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
*ppvObj=this;
|
|
this->cRef++;
|
|
return NOERROR;
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::AddRef
|
|
//
|
|
ULONG STDMETHODCALLTYPE FileStream::AddRef()
|
|
{
|
|
// DebugMsg(DM_TRACE, "FileStream::AddRef(%d)", this->cRef);
|
|
|
|
this->cRef++;
|
|
return this->cRef;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Release
|
|
//
|
|
ULONG STDMETHODCALLTYPE FileStream::Release()
|
|
{
|
|
// DebugMsg(DM_TRACE, "FileStream::Release(%d)", this->cRef);
|
|
|
|
this->cRef--;
|
|
|
|
if (this->cRef>0)
|
|
{
|
|
return this->cRef;
|
|
}
|
|
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Read
|
|
//
|
|
STDMETHODIMP FileStream::Read(VOID *pv, ULONG cb, ULONG *pcbRead)
|
|
{
|
|
ULONG cbReadRequestSize = cb;
|
|
UINT cbT, cbRead;
|
|
HRESULT hres = NOERROR;
|
|
|
|
while (cb > 0)
|
|
{
|
|
// Assert if we are beyond the bufferlen and Not CBSTREAMBUF which
|
|
// would imply a seek happened...
|
|
Assert((this->ib <= this->cbBufLen) || (this->ib == CBSTREAMBUF));
|
|
|
|
if (this->ib < this->cbBufLen)
|
|
{
|
|
cbT = this->cbBufLen - this->ib;
|
|
|
|
if (cbT > cb)
|
|
cbT = cb;
|
|
|
|
hmemcpy(pv, &this->ab[this->ib], cbT);
|
|
this->ib += cbT;
|
|
cb -= cbT;
|
|
|
|
if (cb == 0)
|
|
break;
|
|
|
|
(BYTE *&)pv += cbT;
|
|
}
|
|
|
|
// Buffer's empty. Handle rest of large reads directly...
|
|
//
|
|
if (cb > CBSTREAMBUF)
|
|
{
|
|
cbT = cb - cb % CBSTREAMBUF;
|
|
cbRead = _hread(this->hFile, pv, cbT);
|
|
|
|
if (cbRead == (UINT)-1)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("Stream read IO error %d"), GetLastError());
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
cb -= cbRead;
|
|
(BYTE *&)pv += cbRead;
|
|
|
|
if (cbT != cbRead)
|
|
break; // end of file
|
|
}
|
|
|
|
if (cb == 0)
|
|
break;
|
|
|
|
// was the last read a partial read? if so we are done
|
|
//
|
|
if (this->cbBufLen > 0 && this->cbBufLen < CBSTREAMBUF)
|
|
{
|
|
// DebugMsg(DM_TRACE, "Stream is empty");
|
|
break;
|
|
}
|
|
|
|
// Read an entire buffer's worth. We may try to read past EOF,
|
|
// so we must only check for != 0...
|
|
//
|
|
cbRead = _hread(this->hFile, this->ab, CBSTREAMBUF);
|
|
if (cbRead == (UINT)-1)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("Stream read IO error 2 %d"), GetLastError());
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
if (cbRead == 0)
|
|
break;
|
|
|
|
this->ib = 0;
|
|
this->cbBufLen = cbRead;
|
|
}
|
|
|
|
if (pcbRead)
|
|
*pcbRead = cbReadRequestSize - cb;
|
|
|
|
if (cb != 0)
|
|
{
|
|
// DebugMsg(DM_TRACE, "FileStream::Read() incomplete read");
|
|
hres = S_FALSE; // still success! but not completely
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Write
|
|
//
|
|
STDMETHODIMP FileStream::Write(VOID const *pv, ULONG cb, ULONG *pcbWritten)
|
|
{
|
|
ULONG cbRequestedWrite = cb;
|
|
UINT cbT;
|
|
HRESULT hres = NOERROR;
|
|
|
|
// DebugMsg(DM_TRACE, "FileStream::Write(%d bytes)", cb);
|
|
|
|
while (cb > 0)
|
|
{
|
|
if (this->ib < CBSTREAMBUF)
|
|
{
|
|
cbT = min((ULONG)(CBSTREAMBUF - this->ib), cb);
|
|
|
|
hmemcpy(&this->ab[this->ib], pv, cbT);
|
|
this->ib += cbT;
|
|
cb -= cbT;
|
|
|
|
if (cb == 0)
|
|
break;
|
|
|
|
(BYTE *&)pv += cbT;
|
|
}
|
|
|
|
hres = Commit(0);
|
|
if (FAILED(hres))
|
|
break;
|
|
|
|
if (cb > CBSTREAMBUF)
|
|
{
|
|
UINT cbWrite;
|
|
|
|
cbT = cb - cb % CBSTREAMBUF;
|
|
|
|
cbWrite = _hwrite(this->hFile, (LPCSTR)pv, cbT);
|
|
|
|
if (cbWrite == (UINT)-1)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("Stream write IO error 2, %d"), GetLastError());
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
cb -= cbWrite;
|
|
(BYTE *&)pv += cbWrite;
|
|
|
|
if (cbWrite != cbT)
|
|
break; // media full, we are done
|
|
}
|
|
}
|
|
|
|
if (pcbWritten)
|
|
*pcbWritten = cbRequestedWrite - cb;
|
|
|
|
if ((cb != 0) && (hres == NOERROR))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("FileStream::Write() incomplete"));
|
|
hres = S_FALSE; // still success! but not completely
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Seek
|
|
//
|
|
STDMETHODIMP FileStream::Seek(LARGE_INTEGER dlibMove,
|
|
DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
|
|
{
|
|
LONG l;
|
|
|
|
// DebugMsg(DM_TRACE, "FileStream::Seek(%d)", dlibMove.LowPart);
|
|
|
|
if (fWrite)
|
|
Commit(0);
|
|
else
|
|
{
|
|
this->ib = CBSTREAMBUF;
|
|
this->cbBufLen = 0; // Say we have not read it yet.
|
|
}
|
|
|
|
// HighPart should be 0, unless its been set to 0xFFFFFFFF to
|
|
// indicate "read everything"
|
|
|
|
Assert(dlibMove.HighPart == 0 || dlibMove.HighPart == (ULONG) -1);
|
|
|
|
l = _llseek(hFile, dlibMove.LowPart, (int)dwOrigin);
|
|
|
|
if (plibNewPosition)
|
|
{
|
|
plibNewPosition->LowPart = (DWORD)l;
|
|
plibNewPosition->HighPart= 0;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::SetSize
|
|
//
|
|
STDMETHODIMP FileStream::SetSize(ULARGE_INTEGER libNewSize)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// REVIEW: this could use the internal buffer in the stream to avoid
|
|
// extra buffer copies.
|
|
//
|
|
STDMETHODIMP FileStream::CopyTo(IStream *pstmTo, ULARGE_INTEGER cb,
|
|
ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
|
|
{
|
|
BYTE buf[512];
|
|
ULONG cbRead;
|
|
HRESULT hres = NOERROR;
|
|
|
|
if (pcbRead)
|
|
{
|
|
pcbRead->LowPart = 0;
|
|
pcbRead->HighPart = 0;
|
|
}
|
|
if (pcbWritten)
|
|
{
|
|
pcbWritten->LowPart = 0;
|
|
pcbWritten->HighPart = 0;
|
|
}
|
|
|
|
Assert(cb.HighPart == 0);
|
|
|
|
while (cb.LowPart)
|
|
{
|
|
hres = this->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
|
|
|
|
if (pcbRead)
|
|
pcbRead->LowPart += cbRead;
|
|
|
|
if (FAILED(hres) || (cbRead == 0))
|
|
break;
|
|
|
|
cb.LowPart -= cbRead;
|
|
|
|
hres = pstmTo->Write(buf, cbRead, &cbRead);
|
|
|
|
if (pcbWritten)
|
|
pcbWritten->LowPart += cbRead;
|
|
|
|
if (FAILED(hres) || (cbRead == 0))
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Commit
|
|
//
|
|
STDMETHODIMP FileStream::Commit(DWORD grfCommitFlags)
|
|
{
|
|
if (this->fWrite)
|
|
{
|
|
if (this->ib > 0)
|
|
{
|
|
if (_hwrite(hFile, (LPCSTR)this->ab, this->ib) != this->ib)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("FileStream::Commit() incompleate write %d"), GetLastError());
|
|
return STG_E_MEDIUMFULL;
|
|
}
|
|
this->ib = 0;
|
|
}
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Revert
|
|
//
|
|
STDMETHODIMP FileStream::Revert()
|
|
{
|
|
//
|
|
// Currently not supported
|
|
//
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::LockRegion
|
|
//
|
|
STDMETHODIMP FileStream::LockRegion(ULARGE_INTEGER libOffset,
|
|
ULARGE_INTEGER cb,
|
|
DWORD dwLockType)
|
|
|
|
{
|
|
//
|
|
// Currently not supported
|
|
//
|
|
return E_NOTIMPL;
|
|
}
|
|
//
|
|
// Member: FileStream::UnlockRegion
|
|
//
|
|
STDMETHODIMP FileStream::UnlockRegion(ULARGE_INTEGER libOffset,
|
|
ULARGE_INTEGER cb,
|
|
DWORD dwLockType)
|
|
{
|
|
//
|
|
// Currently not supported
|
|
//
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Stat
|
|
//
|
|
STDMETHODIMP FileStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
|
|
{
|
|
//
|
|
// Currently not supported
|
|
//
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// Member: FileStream::Clone
|
|
//
|
|
STDMETHODIMP FileStream::Clone(IStream * *ppstm)
|
|
{
|
|
//
|
|
// Currently not supported
|
|
//
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// create a IStream from a DOS file name.
|
|
//
|
|
extern "C" LPSTREAM WINAPI OpenFileStream(LPCTSTR szFile, DWORD grfMode)
|
|
{
|
|
HFILE hFile;
|
|
|
|
// DebugMsg(DM_TRACE, "OpenFileStream(%s, %08lX)", szFile, grfMode);
|
|
|
|
//
|
|
// we dont handle all modes, all the other's just map to OF_* flags
|
|
//
|
|
if (grfMode &
|
|
(STGM_TRANSACTED |
|
|
STGM_PRIORITY |
|
|
STGM_DELETEONRELEASE |
|
|
STGM_CONVERT))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("OpenFileStream: Invalid STGM_ mode"));
|
|
return NULL;
|
|
}
|
|
|
|
if (grfMode & OF_READWRITE)
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("OpenFileStream: READWRITE not allowed"));
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if ( grfMode & OF_CREATE)
|
|
{
|
|
// Need to get the file attributes of the file first, so
|
|
// that CREATE_ALWAYS will succeed for HIDDEN and SYSTEM
|
|
// attributes.
|
|
DWORD dwAttrib = GetFileAttributes( szFile );
|
|
if (0xFFFFFFFF == dwAttrib )
|
|
{
|
|
// something went wrong, so set attributes to something
|
|
// normal before we try to create the file...
|
|
dwAttrib = 0;
|
|
}
|
|
|
|
// OF_CREATE
|
|
hFile = (HFILE)CreateFile( szFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
dwAttrib,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwDesiredAccess;
|
|
DWORD dwShareMode;
|
|
DWORD dwShareBits;
|
|
|
|
// not OF_CREATE
|
|
if ( grfMode & OF_WRITE )
|
|
{
|
|
dwDesiredAccess = GENERIC_WRITE;
|
|
}
|
|
else
|
|
{
|
|
dwDesiredAccess = GENERIC_READ;
|
|
}
|
|
if ( grfMode & OF_READWRITE )
|
|
{
|
|
dwDesiredAccess |= (GENERIC_READ | GENERIC_WRITE);
|
|
}
|
|
dwShareBits = grfMode & (OF_SHARE_EXCLUSIVE
|
|
| OF_SHARE_DENY_WRITE
|
|
| OF_SHARE_DENY_READ
|
|
| OF_SHARE_DENY_NONE);
|
|
switch( dwShareBits ) {
|
|
case OF_SHARE_DENY_WRITE:
|
|
dwShareMode = FILE_SHARE_READ;
|
|
break;
|
|
case OF_SHARE_DENY_READ:
|
|
dwShareMode = FILE_SHARE_WRITE;
|
|
break;
|
|
case OF_SHARE_EXCLUSIVE:
|
|
dwShareMode = 0;
|
|
default:
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
break;
|
|
}
|
|
|
|
|
|
hFile = (HFILE)CreateFile( szFile,
|
|
dwDesiredAccess,
|
|
dwShareMode,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
}
|
|
#else
|
|
if (grfMode & OF_CREATE)
|
|
hFile = _lcreat(szFile, 0);
|
|
else
|
|
hFile = _lopen(szFile, (UINT)grfMode);
|
|
#endif
|
|
|
|
if (hFile == HFILE_ERROR)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("OpenFileStream: _lopen() failed %s"), szFile);
|
|
return NULL;
|
|
}
|
|
|
|
return (IStream *)new FileStream(hFile, (UINT)grfMode);
|
|
}
|