1595 lines
43 KiB
C++
1595 lines
43 KiB
C++
// LockBytes.cpp -- Implementation for class CFSLockBytes
|
|
|
|
#include "StdAfx.h"
|
|
|
|
typedef PITransformInstance * PPITI;
|
|
DEBUGDEF(LONG CFSLockBytes::CImpILockBytes::s_cInCriticalSection) // = 0)
|
|
DEBUGDEF(LONG CSegmentLockBytes::CImpILockBytes::s_cInCriticalSection) // = 0)
|
|
DEBUGDEF(LONG CTransformedLockBytes::CImpILockBytes::s_cInCriticalSection) // = 0)
|
|
|
|
HRESULT IITLockBytes::CopyLockBytes
|
|
(ILockBytes *pilbSrc, CULINT ullBaseSrc, CULINT ullLimitSrc,
|
|
ILockBytes *pilbDest, CULINT ullBaseDest
|
|
)
|
|
{
|
|
CULINT ullLimitDest;
|
|
|
|
ullLimitDest = ullBaseDest + (ullLimitSrc - ullBaseSrc);
|
|
|
|
if (ullLimitDest.NonZero() && ullLimitDest < ullBaseDest)
|
|
return STG_E_MEDIUMFULL;
|
|
|
|
PBYTE pbBuffer = PBYTE(_alloca(CB_COPY_BUFFER));
|
|
|
|
if (!pbBuffer)
|
|
return STG_E_INSUFFICIENTMEMORY;
|
|
|
|
for (; (ullLimitSrc.NonZero()? ullBaseSrc < ullLimitSrc : ullBaseSrc.NonZero()); )
|
|
{
|
|
CULINT ullLimit;
|
|
|
|
ullLimit = ullBaseSrc + CB_COPY_BUFFER;
|
|
|
|
UINT cb = (ullLimit <= ullLimitSrc)? CB_COPY_BUFFER
|
|
: (ullLimitSrc - ullBaseSrc).Uli().LowPart;
|
|
|
|
ULONG cbRead;
|
|
|
|
HRESULT hr= pilbSrc->ReadAt(ullBaseSrc.Uli(), pbBuffer, cb, &cbRead);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
if (cb != cbRead)
|
|
return STG_E_READFAULT;
|
|
|
|
ULONG cbWritten;
|
|
|
|
hr= pilbDest->WriteAt(ullBaseDest.Uli(), pbBuffer, cb, &cbWritten);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
if (cb != cbWritten)
|
|
return STG_E_WRITEFAULT;
|
|
|
|
ullBaseSrc += cb;
|
|
ullBaseDest += cb;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
ILockBytes *STDMETHODCALLTYPE FindMatchingLockBytes(const WCHAR *pwcsPath, CImpITUnknown *pLkb)
|
|
{
|
|
for (; pLkb; pLkb = pLkb->NextObject())
|
|
if (((IITLockBytes *)pLkb)->IsNamed(pwcsPath))
|
|
{
|
|
pLkb->AddRef();
|
|
|
|
return (ILockBytes *) pLkb;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT CFSLockBytes::Create(IUnknown *punkOuter, const WCHAR * pwszFileName,
|
|
DWORD grfMode, ILockBytes **pplkb
|
|
)
|
|
{
|
|
CFSLockBytes *pfslkb = New CFSLockBytes(punkOuter);
|
|
|
|
return FinishSetup(pfslkb? pfslkb->m_ImpILockBytes.InitCreateLockBytesOnFS
|
|
(pwszFileName, grfMode)
|
|
: STG_E_INSUFFICIENTMEMORY,
|
|
pfslkb,IID_ILockBytes , (PPVOID) pplkb
|
|
);
|
|
}
|
|
|
|
HRESULT CFSLockBytes::CreateTemp(IUnknown *punkOuter, ILockBytes **pplkb)
|
|
{
|
|
char szTempPath[MAX_PATH];
|
|
|
|
DWORD cbPath= GetTempPath(MAX_PATH, szTempPath);
|
|
|
|
if (!cbPath)
|
|
lstrcpyA(szTempPath, ".\\");
|
|
|
|
char szPrefix[4] = "IMT"; // BugBug! May need to make this a random string.
|
|
|
|
char szFullPath[MAX_PATH];
|
|
|
|
if (!GetTempFileName(szTempPath, szPrefix, 0, szFullPath))
|
|
return CFSLockBytes::CImpILockBytes::STGErrorFromFSError(GetLastError());
|
|
|
|
WCHAR wszFullPath[MAX_PATH];
|
|
|
|
UINT cwc = MultiByteToWideChar(GetACP(), MB_PRECOMPOSED, szFullPath,
|
|
1 + lstrlenA(szFullPath), wszFullPath, MAX_PATH
|
|
);
|
|
|
|
if (!cwc)
|
|
return GetLastError();
|
|
|
|
return Open(punkOuter, wszFullPath, STGM_READWRITE | STGM_SHARE_EXCLUSIVE
|
|
| STGM_DELETEONRELEASE,
|
|
pplkb
|
|
);
|
|
}
|
|
|
|
HRESULT CFSLockBytes::Open(IUnknown *punkOuter, const WCHAR * pwszFileName,
|
|
DWORD grfMode, ILockBytes **pplkb
|
|
)
|
|
{
|
|
ILockBytes *pLockBytes = NULL;
|
|
|
|
if (!punkOuter)
|
|
{
|
|
pLockBytes = CFSLockBytes::CImpILockBytes::FindFSLockBytes(pwszFileName);
|
|
|
|
if (pLockBytes)
|
|
{
|
|
*pplkb = pLockBytes;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
CFSLockBytes *pfslkb = New CFSLockBytes(punkOuter);
|
|
|
|
return FinishSetup(pfslkb? pfslkb->m_ImpILockBytes.InitOpenLockBytesOnFS
|
|
(pwszFileName, grfMode)
|
|
: STG_E_INSUFFICIENTMEMORY,
|
|
pfslkb, IID_ILockBytes, (PPVOID) pplkb
|
|
);
|
|
}
|
|
|
|
CFSLockBytes::CImpILockBytes::CImpILockBytes
|
|
(CFSLockBytes *pBackObj, IUnknown *punkOuter)
|
|
: IITLockBytes(pBackObj, punkOuter, this->m_awszFileName)
|
|
{
|
|
m_hFile = NULL;
|
|
m_fFlushed = TRUE;
|
|
m_grfMode = 0;
|
|
m_cwcFileName = 0;
|
|
m_awszFileName[0] = 0;
|
|
}
|
|
|
|
CFSLockBytes::CImpILockBytes::~CImpILockBytes(void)
|
|
{
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
if (m_hFile)
|
|
{
|
|
// RonM_ASSERT(m_fFlushed);
|
|
|
|
// The above assert is here because we want to avoid
|
|
// relying on the release operation to flush out pending
|
|
// disk I/O. The reason is that neither the destructor
|
|
// nor the Release function can return an error code.
|
|
// Thus you'd never know whether the flush succeeded.
|
|
|
|
if (!m_fFlushed)
|
|
Flush();
|
|
|
|
if (ActiveMark())
|
|
MarkInactive();
|
|
|
|
// if (m_grfMode & STGM_DELETE_ON_RELEASE)
|
|
// ...
|
|
|
|
CloseHandle(m_hFile);
|
|
}
|
|
}
|
|
|
|
// Initialing routines:
|
|
|
|
#define INVALID_MODE 0xFFFFFFFF
|
|
|
|
DWORD adwAccessModes[4] = { GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE, INVALID_MODE };
|
|
|
|
DWORD adwShareModes[8] = { INVALID_MODE, 0, FILE_SHARE_READ, FILE_SHARE_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
INVALID_MODE, INVALID_MODE, INVALID_MODE
|
|
};
|
|
|
|
// The tables adwFileCreateErrors and adwSTGMappedErrors define a mapping from the
|
|
// Win32 file system errors to the STG_E_xxx errors.
|
|
|
|
// BugBug: Can we do this by simple bit fiddling?
|
|
|
|
static LONG adwFileCreateErrors[] =
|
|
{
|
|
ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_TOO_MANY_OPEN_FILES,
|
|
ERROR_ACCESS_DENIED, ERROR_INVALID_HANDLE, ERROR_NOT_ENOUGH_MEMORY,
|
|
ERROR_OUTOFMEMORY, ERROR_INVALID_DRIVE, ERROR_NO_MORE_FILES,
|
|
ERROR_WRITE_PROTECT, ERROR_BAD_UNIT, ERROR_CRC,
|
|
ERROR_SEEK, ERROR_WRITE_FAULT, ERROR_READ_FAULT,
|
|
ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION, ERROR_FILE_EXISTS,
|
|
ERROR_INVALID_PARAMETER, ERROR_DISK_FULL, ERROR_NOACCESS,
|
|
ERROR_INVALID_USER_BUFFER, ERROR_ALREADY_EXISTS, ERROR_INVALID_NAME
|
|
|
|
};
|
|
|
|
static LONG adwSTGMappedErrors[] =
|
|
{
|
|
STG_E_FILENOTFOUND, STG_E_PATHNOTFOUND, STG_E_TOOMANYOPENFILES,
|
|
STG_E_ACCESSDENIED, STG_E_INVALIDHANDLE, STG_E_INSUFFICIENTMEMORY,
|
|
STG_E_INSUFFICIENTMEMORY, STG_E_PATHNOTFOUND, STG_E_NOMOREFILES,
|
|
STG_E_DISKISWRITEPROTECTED, STG_E_PATHNOTFOUND, STG_E_READFAULT,
|
|
STG_E_SEEKERROR, STG_E_WRITEFAULT, STG_E_READFAULT,
|
|
STG_E_SHAREVIOLATION, STG_E_LOCKVIOLATION, STG_E_FILEALREADYEXISTS,
|
|
STG_E_INVALIDPARAMETER, STG_E_MEDIUMFULL, STG_E_INVALIDPOINTER,
|
|
STG_E_INVALIDPOINTER, STG_E_FILEALREADYEXISTS, STG_E_PATHNOTFOUND
|
|
};
|
|
|
|
DWORD CFSLockBytes::CImpILockBytes::STGErrorFromFSError(DWORD fsError)
|
|
{
|
|
// This routine maps Win32 file system errors into STG_E_xxxx errors.
|
|
|
|
UINT cErrs = sizeof(adwFileCreateErrors) / sizeof(DWORD);
|
|
|
|
RonM_ASSERT(cErrs == sizeof(adwSTGMappedErrors)/sizeof(DWORD));
|
|
|
|
DWORD *pdw = (DWORD *) adwFileCreateErrors;
|
|
|
|
for (; cErrs--; pdw++)
|
|
{
|
|
DWORD dw = *pdw;
|
|
|
|
if (dw == fsError)
|
|
return (DWORD) adwSTGMappedErrors[pdw - (DWORD *) adwFileCreateErrors];
|
|
}
|
|
|
|
RonM_ASSERT(FALSE); // We're supposed to map all errors!
|
|
|
|
return STG_E_UNKNOWN; // For when we don't find a match.
|
|
}
|
|
|
|
|
|
HRESULT CFSLockBytes::CImpILockBytes::InitCreateLockBytesOnFS
|
|
(const WCHAR * pwszFileName,
|
|
DWORD grfMode
|
|
)
|
|
{
|
|
return OpenOrCreateLockBytesOnFS(pwszFileName, grfMode, TRUE);
|
|
}
|
|
|
|
HRESULT CFSLockBytes::CImpILockBytes::OpenOrCreateLockBytesOnFS
|
|
(const WCHAR * pwszFileName,
|
|
DWORD grfMode,
|
|
BOOL fCreate
|
|
)
|
|
{
|
|
RonM_ASSERT(!m_hFile);
|
|
|
|
if (grfMode & STGM_TRANSACTED)
|
|
return STG_E_UNIMPLEMENTEDFUNCTION;
|
|
|
|
// The following assert verifies that RW_ACCESS_MASK is correct.
|
|
|
|
RonM_ASSERT(STGM_READ == 0 && STGM_WRITE == 1 && STGM_READWRITE == 2);
|
|
|
|
DWORD dwAccessMode = adwAccessModes[grfMode & RW_ACCESS_MASK];
|
|
|
|
if (dwAccessMode == INVALID_MODE)
|
|
return STG_E_INVALIDFLAG;
|
|
|
|
// The following ASSERT verifies that SHARE_MASK and SHARE_BIT_SHIFT are correct.
|
|
|
|
RonM_ASSERT( STGM_SHARE_DENY_NONE == 0x40
|
|
&& STGM_SHARE_DENY_READ == 0x30
|
|
&& STGM_SHARE_DENY_WRITE == 0x20
|
|
&& STGM_SHARE_EXCLUSIVE == 0x10
|
|
);
|
|
|
|
DWORD dwShareMode = adwShareModes[(grfMode & SHARE_MASK) >> SHARE_BIT_SHIFT];
|
|
|
|
if (dwShareMode == INVALID_MODE)
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; // default is STGM_SHARE_DENY_NONE
|
|
// return STG_E_INVALIDFLAG;
|
|
|
|
m_grfMode = grfMode;
|
|
|
|
char aszFileName[MAX_PATH+1];
|
|
|
|
INT cwc= wcsLen(pwszFileName);
|
|
|
|
if (cwc >= MAX_PATH)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
INT cb = WideCharToMultiByte(CP_USER_DEFAULT(), WC_COMPOSITECHECK | WC_SEPCHARS,
|
|
pwszFileName, cwc+1,aszFileName, MAX_PATH, NULL, NULL);
|
|
|
|
if (!cb)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
DWORD dwCreationMode = fCreate? (grfMode & STGM_CREATE)? CREATE_ALWAYS : CREATE_NEW
|
|
: OPEN_EXISTING;
|
|
|
|
m_hFile = CreateFile(aszFileName, dwAccessMode, dwShareMode, NULL, dwCreationMode,
|
|
(grfMode & STGM_DELETEONRELEASE)? FILE_FLAG_DELETE_ON_CLOSE
|
|
: FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (m_hFile == INVALID_HANDLE_VALUE)
|
|
return STGErrorFromFSError(GetLastError());
|
|
|
|
CopyMemory(m_awszFileName, pwszFileName, sizeof(WCHAR) * (cwc + 1));
|
|
m_cwcFileName = cwc;
|
|
|
|
MarkActive(g_pFSLockBytesFirstActive);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
HRESULT CFSLockBytes::CImpILockBytes::InitOpenLockBytesOnFS
|
|
(const WCHAR * pwszFileName,
|
|
DWORD grfMode
|
|
)
|
|
{
|
|
return OpenOrCreateLockBytesOnFS(pwszFileName, grfMode, FALSE);
|
|
}
|
|
|
|
// ILockBytes methods:
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::ReadAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [length_is][size_is][out] */ void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbRead)
|
|
{
|
|
/*
|
|
This routine reads data synchronously. This means that multiple accesses
|
|
to the file from different threads are forced into a strict sequence with no
|
|
overlap. This could be a serious bottleneck in a multi-threaded environment.
|
|
|
|
We can relax this constraint by using multiple read threads in Win95 or
|
|
overlapped I/O in WinNT.
|
|
|
|
*/
|
|
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
UINT ulResult = SetFilePointer(m_hFile, ulOffset.LowPart, (LONG *) &(ulOffset.HighPart), FILE_BEGIN);
|
|
|
|
if (ulResult == UINT(~0))
|
|
{
|
|
DWORD dwErr= GetLastError();
|
|
|
|
if (dwErr != NO_ERROR)
|
|
return STGErrorFromFSError(dwErr);
|
|
}
|
|
|
|
ULONG cbRead = 0;
|
|
|
|
BOOL fSucceeded = ReadFile(m_hFile, pv, cb, &cbRead, NULL);
|
|
|
|
if (pcbRead)
|
|
*pcbRead = cbRead;
|
|
|
|
if (fSucceeded)
|
|
return NO_ERROR;
|
|
|
|
DWORD dwErr= GetLastError();
|
|
|
|
RonM_ASSERT(cb);
|
|
|
|
if (dwErr == ERROR_HANDLE_EOF)
|
|
return (!cbRead)? S_FALSE : S_OK;
|
|
|
|
return STGErrorFromFSError(dwErr);
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::WriteAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [size_is][in] */ const void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbWritten)
|
|
{
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
m_fFlushed = FALSE;
|
|
|
|
/*
|
|
This routine writes data synchronously. This means that multiple accesses
|
|
to the file from different threads are forced into a strict sequence with no
|
|
overlap. This could be a serious bottleneck in a multi-threaded environment.
|
|
|
|
We can relax this constraint by using multiple read threads in Win95 or
|
|
overlapped I/O in WinNT.
|
|
|
|
*/
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
DWORD ulResult = SetFilePointer(m_hFile, ulOffset.LowPart, (LONG *) &(ulOffset.HighPart), FILE_BEGIN);
|
|
|
|
if (ulResult == UINT(~0))
|
|
{
|
|
DWORD dwErr= GetLastError();
|
|
|
|
if (dwErr != NO_ERROR)
|
|
return STGErrorFromFSError(dwErr);
|
|
}
|
|
|
|
ULONG cbWritten = 0;
|
|
|
|
BOOL fSucceeded = WriteFile(m_hFile, pv, cb, &cbWritten, NULL);
|
|
|
|
if (pcbWritten)
|
|
*pcbWritten = cbWritten;
|
|
|
|
if (fSucceeded)
|
|
return NO_ERROR;
|
|
|
|
return STGErrorFromFSError(GetLastError());
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::Flush( void)
|
|
{
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
BOOL fSucceeded = FlushFileBuffers(m_hFile);
|
|
|
|
HRESULT hr;
|
|
|
|
if (fSucceeded)
|
|
{
|
|
m_fFlushed = TRUE;
|
|
|
|
hr = NO_ERROR;
|
|
}
|
|
else hr = STGErrorFromFSError(GetLastError());
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::SetSize(
|
|
/* [in] */ ULARGE_INTEGER cb)
|
|
{
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
m_fFlushed = FALSE; // Is this necessary?
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
DWORD dwDistLow= SetFilePointer(m_hFile, cb.LowPart, (LONG *) &(cb.HighPart),
|
|
FILE_BEGIN
|
|
);
|
|
|
|
if (!~dwDistLow) // Seek operation failed
|
|
return STGErrorFromFSError(GetLastError());
|
|
|
|
BOOL fSucceeded= SetEndOfFile(m_hFile);
|
|
|
|
if (fSucceeded)
|
|
return NO_ERROR;
|
|
|
|
return STGErrorFromFSError(GetLastError());
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::LockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
if (dwLockType != LOCK_EXCLUSIVE)
|
|
return STG_E_UNIMPLEMENTEDFUNCTION;
|
|
|
|
if (LockFile(m_hFile, libOffset.LowPart, libOffset.HighPart,
|
|
cb.LowPart, cb.HighPart
|
|
)
|
|
)
|
|
return NO_ERROR;
|
|
|
|
return STGErrorFromFSError(GetLastError());
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::UnlockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
if (dwLockType != LOCK_EXCLUSIVE)
|
|
return STG_E_UNIMPLEMENTEDFUNCTION;
|
|
|
|
if (UnlockFile(m_hFile, libOffset.LowPart, libOffset.HighPart,
|
|
cb.LowPart, cb.HighPart
|
|
)
|
|
)
|
|
return NO_ERROR;
|
|
|
|
return STGErrorFromFSError(GetLastError());
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CFSLockBytes::CImpILockBytes::Stat(
|
|
/* [out] */ STATSTG __RPC_FAR *pstatstg,
|
|
/* [in] */ DWORD grfStatFlag)
|
|
{
|
|
RonM_ASSERT(m_hFile);
|
|
|
|
pstatstg->cbSize.LowPart = GetFileSize(m_hFile, &(pstatstg->cbSize.HighPart));
|
|
|
|
DWORD dwErr= GetLastError();
|
|
|
|
if (pstatstg->cbSize.LowPart == 0xFFFFFFFF && dwErr != NO_ERROR)
|
|
return STGErrorFromFSError(dwErr);
|
|
|
|
if (!GetFileTime(m_hFile, &(pstatstg->ctime), &(pstatstg->atime), &(pstatstg->mtime)))
|
|
return STGErrorFromFSError(GetLastError());
|
|
|
|
pstatstg->type = STGTY_LOCKBYTES;
|
|
pstatstg->grfMode = m_grfMode;
|
|
pstatstg->grfLocksSupported = LOCK_EXCLUSIVE;
|
|
pstatstg->clsid = CLSID_NULL;
|
|
pstatstg->grfStateBits = 0;
|
|
pstatstg->reserved = 0;
|
|
|
|
// The code for setting pstatstg->pwcsName must go last because we don't want
|
|
// to allocate memory for the name and then report an error. If we did, our
|
|
// caller would not realize that they needed to deallocate that name memory.
|
|
|
|
if (grfStatFlag == STATFLAG_DEFAULT)
|
|
{
|
|
PWCHAR pwcName= PWCHAR(OLEHeap()->Alloc(sizeof(WCHAR) * (m_cwcFileName+1)));
|
|
|
|
if (!pwcName)
|
|
return STG_E_INSUFFICIENTMEMORY;
|
|
|
|
CopyMemory(pwcName, m_awszFileName, sizeof(WCHAR) * (m_cwcFileName+1));
|
|
|
|
pstatstg->pwcsName= pwcName;
|
|
}
|
|
else pstatstg->pwcsName = NULL;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
ILockBytes *CFSLockBytes::CImpILockBytes::FindFSLockBytes(const WCHAR * pwszFileName)
|
|
{
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
return FindMatchingLockBytes(pwszFileName, (CImpITUnknown *) g_pFSLockBytesFirstActive);
|
|
}
|
|
|
|
HRESULT __stdcall CFSLockBytes::CImpILockBytes::SetTimes
|
|
(FILETIME const * pctime,
|
|
FILETIME const * patime,
|
|
FILETIME const * pmtime
|
|
)
|
|
{
|
|
BOOL fSuccess = SetFileTime(m_hFile, pctime, patime, pmtime);
|
|
|
|
if (fSuccess)
|
|
return NO_ERROR;
|
|
|
|
return STGErrorFromFSError(GetLastError());
|
|
}
|
|
|
|
|
|
CSegmentLockBytes::CImpILockBytes::CImpILockBytes
|
|
(CSegmentLockBytes *pBackObj, IUnknown *punkOuter)
|
|
: IITLockBytes(pBackObj, punkOuter, m_PathInfo.awszStreamPath)
|
|
{
|
|
m_fFlushed = TRUE;
|
|
m_pITFS = NULL;
|
|
m_plbMedium = NULL;
|
|
m_plbTemp = NULL;
|
|
m_plbLockMgr = NULL;
|
|
|
|
ZeroMemory(&m_PathInfo, sizeof(m_PathInfo));
|
|
}
|
|
|
|
CSegmentLockBytes::CImpILockBytes::~CImpILockBytes(void)
|
|
{
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
if (m_plbMedium)
|
|
{
|
|
// RonM_ASSERT(m_fFlushed);
|
|
|
|
// The above assert is here because we want to avoid
|
|
// relying on the release operation to flush out pending
|
|
// disk I/O. The reason is that neither the destructor
|
|
// nor the Release function can return an error code.
|
|
// Thus you'd never know whether the flush succeeded.
|
|
|
|
if (!m_fFlushed)
|
|
Flush();
|
|
|
|
if (m_PathInfo.cUnrecordedChanges)
|
|
m_pITFS->UpdatePathInfo(&m_PathInfo);
|
|
|
|
if (m_plbTemp) // Should have been discarded by Flush.
|
|
m_plbTemp->Release(); // However this can happen if we're low on disk space.
|
|
|
|
if (m_plbLockMgr) // Will exit if we've had a LockRegion call.
|
|
m_plbLockMgr->Release();
|
|
|
|
MarkInactive(); // Take this LockBytes out of the active chain.
|
|
|
|
RonM_ASSERT(m_pITFS); // Because we've got m_plbMedium.
|
|
|
|
if (m_PathInfo.awszStreamPath[0] != L'/')
|
|
m_pITFS->FSObjectReleased();
|
|
|
|
m_plbMedium->Release();
|
|
m_pITFS ->Release();
|
|
}
|
|
else
|
|
{
|
|
RonM_ASSERT(!m_pITFS);
|
|
RonM_ASSERT(!m_plbTemp); // Won't exist if we don't have a lockbyte medium.
|
|
RonM_ASSERT(!m_plbLockMgr); // Won't exist if we don't have a lockbyte medium.
|
|
}
|
|
}
|
|
|
|
HRESULT CSegmentLockBytes::OpenSegment
|
|
(IUnknown *punkOuter, IITFileSystem *pITFS, ILockBytes *pLKBMedium,
|
|
PathInfo *pPI, ILockBytes **pplkb
|
|
)
|
|
{
|
|
CSegmentLockBytes *pSegLKB = New CSegmentLockBytes(punkOuter);
|
|
|
|
return FinishSetup(pSegLKB? pSegLKB->m_ImpILockBytes.InitOpenSegment
|
|
(pITFS, pLKBMedium, pPI)
|
|
: STG_E_INSUFFICIENTMEMORY,
|
|
pSegLKB, IID_ILockBytes, (PPVOID) pplkb
|
|
);
|
|
}
|
|
|
|
HRESULT CSegmentLockBytes::CImpILockBytes::InitOpenSegment
|
|
(IITFileSystem *pITFS, ILockBytes *pLKBMedium, PathInfo *pPI)
|
|
{
|
|
m_pITFS = pITFS;
|
|
m_plbMedium = pLKBMedium;
|
|
m_PathInfo = *pPI;
|
|
#if 0
|
|
m_grfMode = (m_pITFS->IsWriteable())? STGM_READWRITE | STGM_SHARE_DENY_NONE
|
|
: STGM_READ | STGM_SHARE_DENY_NONE;
|
|
#endif // 0
|
|
m_pITFS ->AddRef();
|
|
m_plbMedium->AddRef();
|
|
|
|
m_pITFS->ConnectLockBytes(this);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// ILockBytes methods:
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::ReadAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [length_is][size_is][out] */ void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbRead)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
// Since a particular segmented lockbyte may be shared among
|
|
// several different stream objects with varying grfMode settings,
|
|
// we don't validate read access permission at runtime. Instead
|
|
// lockbyte segments are always opened with the maximum permissions
|
|
// available for the medium, and we rely on the client stream object
|
|
// not to ask us to violate the available medium permissions.
|
|
|
|
#if 0
|
|
RonM_ASSERT( (m_grfMode & RW_ACCESS_MASK) == STGM_READ
|
|
|| (m_grfMode & RW_ACCESS_MASK) == STGM_READWRITE
|
|
);
|
|
// #else
|
|
if ( (m_grfMode & RW_ACCESS_MASK) != STGM_READ
|
|
&& (m_grfMode & RW_ACCESS_MASK) != STGM_READWRITE
|
|
)
|
|
return STG_E_INVALID_FLAG;
|
|
#endif
|
|
|
|
if (!cb)
|
|
{
|
|
if (pcbRead)
|
|
*pcbRead = 0;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
|
|
The rest of the code runs in a critical section because a concurrent
|
|
write operation could move the data into a temporary lockbytes object,
|
|
or it could change the segment boundaries.
|
|
|
|
*/
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (m_plbTemp)
|
|
return m_plbTemp->ReadAt(ulOffset, pv, cb, pcbRead);
|
|
|
|
CULINT ullBase, ullLimit;
|
|
|
|
ullBase = m_PathInfo.ullcbOffset + ulOffset;
|
|
ullLimit = ullBase + cb;
|
|
|
|
CULINT ullLimitSegment = m_PathInfo.ullcbOffset + m_PathInfo.ullcbData;
|
|
|
|
if (ullBase > ullLimitSegment) // Beyond the end of the segment?
|
|
{
|
|
if (pcbRead)
|
|
*pcbRead = 0;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
BOOL fEOS = FALSE;
|
|
|
|
if ( ullLimit < ullBase // Wrapped at 2**64 bytes?
|
|
|| ullLimit > ullLimitSegment // Trying to read past end of segment?
|
|
)
|
|
{
|
|
fEOS = TRUE;
|
|
ullLimit = ullLimitSegment;
|
|
}
|
|
|
|
ULONG cbRead = 0;
|
|
|
|
hr= m_plbMedium->ReadAt(ullBase.Uli(), pv, (ullLimit - ullBase).Uli().LowPart,
|
|
&cbRead
|
|
);
|
|
|
|
if (pcbRead)
|
|
*pcbRead = cbRead;
|
|
|
|
RonM_ASSERT(cb);
|
|
|
|
if (fEOS && hr == NO_ERROR && !cbRead)
|
|
hr = S_FALSE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::WriteAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [size_is][in] */ const void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbWritten)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
// Since a particular segmented lockbyte may be shared among
|
|
// several different stream objects with varying grfMode settings,
|
|
// we don't validate write access permission at runtime. Instead
|
|
// lockbyte segments are always opened with the maximum permissions
|
|
// available for the medium, and we rely on the client stream object
|
|
// not to ask us to violate the available medium permissions.
|
|
|
|
#if 0
|
|
RonM_ASSERT( (m_grfMode & RW_ACCESS_MASK) == STGM_WRITE
|
|
|| (m_grfMode & RW_ACCESS_MASK) == STGM_READWRITE
|
|
);
|
|
// #else
|
|
if ( (m_grfMode & RW_ACCESS_MASK) != STGM_WRITE
|
|
&& (m_grfMode & RW_ACCESS_MASK) != STGM_READWRITE
|
|
)
|
|
return STG_E_INVALID_FLAG;
|
|
#endif
|
|
|
|
if (!cb)
|
|
{
|
|
if (pcbWritten)
|
|
*pcbWritten = 0;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
|
|
The rest of the code runs in a critical section. That's necessary because a
|
|
particular write operation may change the underlying medium from a lockbyte
|
|
segment to a temporary lockbyte object or it may do a realloc which changes
|
|
the segment boundaries.
|
|
|
|
*/
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
hr = m_pITFS->CountWrites();
|
|
|
|
if (m_plbTemp)
|
|
return m_plbTemp->WriteAt(ulOffset, pv, cb, pcbWritten);
|
|
|
|
CULINT ullBase, ullLimit;
|
|
|
|
ullBase = m_PathInfo.ullcbOffset + ulOffset;
|
|
ullLimit = ullBase + cb;
|
|
|
|
CULINT ullLimitSegment = m_PathInfo.ullcbOffset + m_PathInfo.ullcbData;
|
|
|
|
// The assert below verifies that the segment doesn't wrap around
|
|
// through the beginning of the 64-bit address space.
|
|
|
|
RonM_ASSERT( m_PathInfo.ullcbOffset <= ullLimitSegment
|
|
|| !(ullLimitSegment.NonZero())
|
|
);
|
|
|
|
if ( ullBase < m_PathInfo.ullcbOffset
|
|
|| (ullBase > ullLimit && ullLimit.NonZero())
|
|
)
|
|
{
|
|
// The write would wrap around.
|
|
// This is very unlikely -- at least for the next few years.
|
|
|
|
if (pcbWritten)
|
|
*pcbWritten = 0;
|
|
|
|
return STG_E_WRITEFAULT;
|
|
}
|
|
|
|
m_fFlushed = FALSE; // Because we know we're going to write something.
|
|
|
|
// Here we check to see if the write is completely contained within
|
|
// the segment.
|
|
|
|
if ( m_PathInfo.ullcbData.NonZero()
|
|
&& ullBase >= m_PathInfo.ullcbOffset
|
|
&& (ullLimitSegment.NonZero()? ullLimit <= ullLimitSegment
|
|
: ( ullLimit > m_PathInfo.ullcbOffset
|
|
|| !ullLimit.NonZero()
|
|
)
|
|
)
|
|
)
|
|
return m_plbMedium->WriteAt(ullBase.Uli(), pv, cb, pcbWritten);
|
|
|
|
// The write doesn't fit in the segment.
|
|
// Let's see if we can reallocate the segment without moving it.
|
|
//
|
|
// Note that we pass in pointers to both the segment base and the segment limit.
|
|
// You might think that we don't need to pass in the base since the storage
|
|
// manager already knows where the segment is located. However when the segment's
|
|
// size is zero, it really doesn't have a location. So we let the storage manager
|
|
// put it at the end of the medium. This takes care of the case where several
|
|
// lockbyte segments are created and then they are written in some random order.
|
|
|
|
CULINT ullcbNew;
|
|
|
|
ullcbNew = ullLimit - m_PathInfo.ullcbOffset;
|
|
|
|
hr = m_pITFS->ReallocInPlace(&m_PathInfo, ullcbNew);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
ullBase = m_PathInfo.ullcbOffset + ulOffset;
|
|
|
|
return m_plbMedium->WriteAt(ullBase.Uli(), pv, cb, pcbWritten);
|
|
}
|
|
|
|
// We couldn't do an in-place reallocation.
|
|
//
|
|
// So we move the data into a temporary ILockbytes object
|
|
// and then do the write operation there.
|
|
|
|
hr= CFSLockBytes::CreateTemp(NULL, &m_plbTemp);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
((IITLockBytes *) m_plbTemp)->Container()->MarkSecondary();
|
|
|
|
hr = IITLockBytes::CopyLockBytes(m_plbMedium, m_PathInfo.ullcbOffset, ullLimitSegment,
|
|
m_plbTemp, 0
|
|
);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
m_plbTemp->Release();
|
|
m_plbTemp = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pITFS->ReallocInPlace(&m_PathInfo, 0);
|
|
|
|
RonM_ASSERT(SUCCEEDED(hr)); // In place shrinking should always work!
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
return m_plbTemp->WriteAt(ulOffset, pv, cb, pcbWritten);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::Flush( void)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (m_fFlushed)
|
|
return hr;
|
|
|
|
if (m_plbTemp)
|
|
{
|
|
// At some point we moved the data into a temporary file. That's
|
|
// usually because we needed to write beyond the segment boundaries.
|
|
// Now we must move the data back into the lockbyte medium.
|
|
|
|
// First we must reallocate the segment to the current data size
|
|
|
|
STATSTG statstg;
|
|
|
|
hr = m_plbTemp->Stat(&statstg, STATFLAG_NONAME);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
hr = m_pITFS->ReallocEntry(&m_PathInfo, CULINT(statstg.cbSize), FALSE);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
// Then we must copy the data back into the lockbyte medium.
|
|
|
|
hr = IITLockBytes::CopyLockBytes(m_plbTemp, CULINT(0), CULINT(statstg.cbSize),
|
|
m_plbMedium, m_PathInfo.ullcbOffset
|
|
);
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
|
|
// At this point we don't need the temporary lockbyte any more.
|
|
// At least not until the next append operation...
|
|
|
|
m_plbTemp->Release();
|
|
|
|
m_plbTemp = NULL;
|
|
}
|
|
|
|
hr = m_plbMedium->Flush(); // Flush in-memory data to disk.
|
|
|
|
m_fFlushed = TRUE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::SetSize(
|
|
/* [in] */ ULARGE_INTEGER cb)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (m_PathInfo.ullcbData == CULINT(cb))
|
|
return hr;
|
|
|
|
m_fFlushed = FALSE;
|
|
|
|
if (m_plbTemp)
|
|
hr = m_plbTemp->SetSize(cb);
|
|
else
|
|
{
|
|
hr = m_pITFS->ReallocInPlace(&m_PathInfo, CULINT(cb));
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
// Couldn't grow the segment in place. So now we must move the data
|
|
// into a temp lockbyte object. We know it's a grow operation and not
|
|
// a shrinkage because the storage manager can always do an in-place
|
|
// shrink operation.
|
|
|
|
hr = CFSLockBytes::CreateTemp(NULL, &m_plbTemp);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CULINT ullLimitSegment = m_PathInfo.ullcbOffset + m_PathInfo.ullcbData;
|
|
|
|
hr = m_plbTemp->SetSize(cb);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = IITLockBytes::CopyLockBytes
|
|
(m_plbMedium, m_PathInfo.ullcbOffset,
|
|
ullLimitSegment, m_plbTemp, 0
|
|
);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
m_plbTemp->Release();
|
|
|
|
m_plbTemp = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::LockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
/*
|
|
|
|
LockRegion operations on lockbyte segments are little tricky because
|
|
the data may exist in two places -- *m_plbMedium and *m_plbTemp.
|
|
|
|
You might think we could just keep compare the lock span against the
|
|
boundaries of the segment to determine which underlying lockbyte object
|
|
should do the lock operation. In many situations that would work correctly.
|
|
However if a Flush operation occurs when we have a temporary lockbytes object
|
|
active, the segment boundaries will change. In addition the temporary
|
|
object will be discarded.
|
|
|
|
The solution then is to always use a third temporary lockbytes object
|
|
to handle lock/unlock operations. Hence the need for m_plbLockMgr.
|
|
|
|
*/
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (!m_plbLockMgr)
|
|
{
|
|
hr = CFSLockBytes::CreateTemp(NULL, &m_plbLockMgr);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
}
|
|
|
|
hr = m_plbLockMgr->LockRegion(libOffset, cb, dwLockType);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::UnlockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (!m_plbLockMgr)
|
|
hr = STG_E_LOCKVIOLATION;
|
|
else hr = m_plbLockMgr->UnlockRegion(libOffset, cb, dwLockType);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CSegmentLockBytes::CImpILockBytes::Stat(
|
|
/* [out] */ STATSTG __RPC_FAR *pstatstg,
|
|
/* [in] */ DWORD grfStatFlag)
|
|
{
|
|
RonM_ASSERT(m_plbMedium);
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
hr= m_plbMedium->Stat(pstatstg, STATFLAG_NONAME);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_plbTemp)
|
|
{
|
|
STATSTG statstgTmp;
|
|
|
|
hr= m_plbTemp->Stat(&statstgTmp, STATFLAG_NONAME);
|
|
|
|
if (SUCCEEDED(hr))
|
|
pstatstg->cbSize = statstgTmp.cbSize;
|
|
}
|
|
else pstatstg->cbSize = m_PathInfo.ullcbData.Uli();
|
|
|
|
if (grfStatFlag != STATFLAG_NONAME)
|
|
{
|
|
UINT cb = sizeof(WCHAR) * (m_PathInfo.cwcStreamPath + 1);
|
|
|
|
pstatstg->pwcsName = PWCHAR(OLEHeap()->Alloc(cb));
|
|
|
|
if (pstatstg->pwcsName)
|
|
CopyMemory(pstatstg->pwcsName, m_PathInfo.awszStreamPath, cb);
|
|
else hr = STG_E_INSUFFICIENTMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
TransformDescriptor::TransformDescriptor()
|
|
{
|
|
iSpace = ~0;
|
|
pLockBytesChain = NULL;
|
|
cTransformLayers = 0;
|
|
apTransformInstance = NULL;
|
|
}
|
|
|
|
TransformDescriptor::~TransformDescriptor()
|
|
{
|
|
RonM_ASSERT(cs.LockCount() == 0);
|
|
|
|
if (apTransformInstance)
|
|
delete [] (PPITI)apTransformInstance;
|
|
}
|
|
|
|
TransformDescriptor *TransformDescriptor::Create(UINT iDataSpace, UINT cLayers)
|
|
{
|
|
TransformDescriptor *pTD = New TransformDescriptor();
|
|
if (pTD)
|
|
{
|
|
pTD->cTransformLayers = cLayers;
|
|
|
|
pTD->apTransformInstance = (PITransformInstance *)(New PPITI[cLayers]);
|
|
|
|
if (pTD->apTransformInstance)
|
|
ZeroMemory(pTD->apTransformInstance, cLayers * sizeof(PPITI));
|
|
else
|
|
{
|
|
delete pTD; pTD = NULL;
|
|
}
|
|
}
|
|
|
|
return pTD;
|
|
}
|
|
|
|
HRESULT CTransformedLockBytes::Open(IUnknown *punkOuter, PathInfo *pPathInfo,
|
|
TransformDescriptor *pTransformDescriptor,
|
|
IITFileSystem *pITFS,
|
|
ILockBytes **ppLockBytes
|
|
)
|
|
{
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
CTransformedLockBytes *pTLKB = New CTransformedLockBytes(punkOuter);
|
|
|
|
return FinishSetup(pTLKB? pTLKB->m_ImpILockBytes.InitOpen
|
|
(pPathInfo, pTransformDescriptor, pITFS)
|
|
: STG_E_INSUFFICIENTMEMORY,
|
|
pTLKB, IID_ILockBytes, (PPVOID) ppLockBytes
|
|
);
|
|
}
|
|
|
|
|
|
// Constructor and Destructor:
|
|
|
|
CTransformedLockBytes::CImpILockBytes::CImpILockBytes
|
|
(CTransformedLockBytes *pBackObj, IUnknown *punkOuter)
|
|
: IITLockBytes(pBackObj, punkOuter, m_PathInfo.awszStreamPath)
|
|
{
|
|
m_pTransformDescriptor = NULL;
|
|
m_pTransformInstance = NULL;
|
|
m_pITFS = NULL;
|
|
m_plbLockMgr = NULL;
|
|
m_fFlushed = TRUE;
|
|
m_grfMode = 0;
|
|
|
|
ZeroMemory(&m_PathInfo, sizeof(m_PathInfo));
|
|
}
|
|
|
|
CTransformedLockBytes::CImpILockBytes::~CImpILockBytes(void)
|
|
{
|
|
if (m_pTransformInstance)
|
|
{
|
|
if (!m_fFlushed)
|
|
m_pTransformInstance->Flush();
|
|
|
|
m_pTransformInstance->Release();
|
|
|
|
if (m_plbLockMgr)
|
|
m_plbLockMgr->Release();
|
|
|
|
if (ActiveMark())
|
|
MarkInactive();
|
|
}
|
|
|
|
if (m_pITFS)
|
|
m_pITFS->Release();
|
|
}
|
|
|
|
// Initialing routines:
|
|
|
|
HRESULT CTransformedLockBytes::CImpILockBytes::InitOpen(PathInfo *pPathInfo,
|
|
TransformDescriptor *pTransformDescriptor,
|
|
IITFileSystem *pITFS
|
|
)
|
|
{
|
|
m_PathInfo = *pPathInfo;
|
|
|
|
m_pTransformDescriptor = pTransformDescriptor;
|
|
m_pTransformInstance = pTransformDescriptor->apTransformInstance[0];
|
|
m_pITFS = pITFS;
|
|
|
|
m_pTransformInstance->AddRef();
|
|
m_pITFS ->AddRef();
|
|
|
|
MarkActive(pTransformDescriptor->pLockBytesChain);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
ILockBytes *CTransformedLockBytes::CImpILockBytes::FindTransformedLockBytes
|
|
(const WCHAR * pwszFileName,
|
|
TransformDescriptor *pTransformDescriptor
|
|
)
|
|
{
|
|
return FindMatchingLockBytes(pwszFileName, (CImpITUnknown *) pTransformDescriptor->pLockBytesChain);
|
|
}
|
|
|
|
|
|
// ILockBytes methods:
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::ReadAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [length_is][size_is][out] */ void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbRead)
|
|
{
|
|
ImageSpan SpanInfo;
|
|
|
|
SpanInfo.uliHandle = m_PathInfo.ullcbOffset.Uli();
|
|
SpanInfo.uliSize = m_PathInfo.ullcbData .Uli();
|
|
|
|
CSyncWith sw(m_pTransformDescriptor->cs);
|
|
|
|
ULONG cbRead = 0;
|
|
|
|
HRESULT hr = m_pTransformInstance->ReadAt(ulOffset, pv, cb, &cbRead, &SpanInfo);
|
|
|
|
if (pcbRead)
|
|
*pcbRead = cbRead;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::WriteAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [size_is][in] */ const void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbWritten)
|
|
{
|
|
ImageSpan SpanInfo;
|
|
|
|
SpanInfo.uliHandle = m_PathInfo.ullcbOffset.Uli();
|
|
SpanInfo.uliSize = m_PathInfo.ullcbData .Uli();
|
|
|
|
CSyncWith(m_pTransformDescriptor->cs);
|
|
|
|
ULONG cbWritten = 0;
|
|
|
|
HRESULT hr = m_pTransformInstance->WriteAt(ulOffset, pv, cb, &cbWritten, &SpanInfo);
|
|
|
|
if (pcbWritten)
|
|
*pcbWritten = cbWritten;
|
|
|
|
if ( m_PathInfo.ullcbOffset != SpanInfo.uliHandle
|
|
|| m_PathInfo.ullcbData != SpanInfo.uliSize
|
|
)
|
|
{
|
|
m_PathInfo.ullcbOffset = SpanInfo.uliHandle;
|
|
m_PathInfo.ullcbData = SpanInfo.uliSize;
|
|
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr2 = m_pITFS->UpdatePathInfo(&m_PathInfo);
|
|
|
|
if (!SUCCEEDED(hr2))
|
|
return hr2;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::Flush( void)
|
|
{
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
HRESULT hr = m_pTransformInstance->Flush();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::SetSize(
|
|
/* [in] */ ULARGE_INTEGER cb)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::LockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
CSyncWith sw(m_pTransformDescriptor->cs);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (!m_plbLockMgr)
|
|
{
|
|
hr = CFSLockBytes::CreateTemp(NULL, &m_plbLockMgr);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return hr;
|
|
}
|
|
|
|
return m_plbLockMgr->LockRegion(libOffset, cb, dwLockType);
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::UnlockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
CSyncWith(m_pTransformDescriptor->cs);
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (!m_plbLockMgr)
|
|
hr = STG_E_LOCKVIOLATION;
|
|
else hr = m_plbLockMgr->UnlockRegion(libOffset, cb, dwLockType);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CTransformedLockBytes::CImpILockBytes::Stat(
|
|
/* [out] */ STATSTG __RPC_FAR *pstatstg,
|
|
/* [in] */ DWORD grfStatFlag)
|
|
{
|
|
CSyncWith(m_pTransformDescriptor->cs);
|
|
|
|
pstatstg->type = STGTY_LOCKBYTES;
|
|
pstatstg->cbSize = m_PathInfo.ullcbData.Uli();
|
|
pstatstg->grfMode = m_grfMode;
|
|
pstatstg->grfLocksSupported = LOCK_EXCLUSIVE;
|
|
pstatstg->clsid = CLSID_NULL;
|
|
pstatstg->grfStateBits = 0;
|
|
pstatstg->reserved = 0;
|
|
pstatstg->mtime.dwLowDateTime = 0;
|
|
pstatstg->mtime.dwHighDateTime = 0;
|
|
pstatstg->ctime.dwLowDateTime = 0;
|
|
pstatstg->ctime.dwHighDateTime = 0;
|
|
pstatstg->atime.dwLowDateTime = 0;
|
|
pstatstg->atime.dwHighDateTime = 0;
|
|
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (grfStatFlag != STATFLAG_NONAME)
|
|
{
|
|
UINT cb = sizeof(WCHAR) * (m_PathInfo.cwcStreamPath + 1);
|
|
|
|
pstatstg->pwcsName = PWCHAR(OLEHeap()->Alloc(cb));
|
|
|
|
if (pstatstg->pwcsName)
|
|
CopyMemory(pstatstg->pwcsName, m_PathInfo.awszStreamPath, cb);
|
|
else hr = STG_E_INSUFFICIENTMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
CStrmLockBytes::CImpILockBytes::CImpILockBytes
|
|
(CStrmLockBytes *pBackObj, IUnknown *punkOuter)
|
|
: IITLockBytes(pBackObj, punkOuter, this->m_awszLkBName)
|
|
{
|
|
m_pStream = NULL;
|
|
}
|
|
|
|
CStrmLockBytes::CImpILockBytes::~CImpILockBytes(void)
|
|
{
|
|
if (m_pStream)
|
|
m_pStream->Release();
|
|
}
|
|
|
|
HRESULT CStrmLockBytes::OpenUrlStream
|
|
(const WCHAR *pwszURL, ILockBytes **pplkb)
|
|
{
|
|
CSyncWith sw(g_csITFS);
|
|
|
|
ILockBytes *pLkb = CStrmLockBytes::CImpILockBytes::FindStrmLockBytes(pwszURL);
|
|
|
|
if (pLkb)
|
|
{
|
|
*pplkb = pLkb;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
CStrmLockBytes *pLkbStream = New CStrmLockBytes(NULL);
|
|
|
|
return FinishSetup(pLkbStream? pLkbStream->m_ImpILockBytes.InitUrlStream(pwszURL)
|
|
: STG_E_INSUFFICIENTMEMORY,
|
|
pLkbStream, IID_ILockBytes, (PPVOID) pplkb
|
|
);
|
|
}
|
|
|
|
|
|
HRESULT CStrmLockBytes::Create(IUnknown *punkOuter, IStream *pStrm, ILockBytes **pplkb)
|
|
{
|
|
CStrmLockBytes *pLkbStream = New CStrmLockBytes(punkOuter);
|
|
|
|
return FinishSetup(pLkbStream? pLkbStream->m_ImpILockBytes.Init(pStrm)
|
|
: STG_E_INSUFFICIENTMEMORY,
|
|
pLkbStream, IID_ILockBytes, (PPVOID) pplkb
|
|
);
|
|
}
|
|
|
|
// Initialing routines:
|
|
|
|
HRESULT CStrmLockBytes::CImpILockBytes::InitUrlStream(const WCHAR *pwszURL)
|
|
{
|
|
UINT cwc = wcsLen(pwszURL);
|
|
|
|
if (cwc >= MAX_PATH)
|
|
return STG_E_INVALIDNAME;
|
|
|
|
CopyMemory(m_awszLkBName, pwszURL, sizeof(WCHAR) * (cwc + 1));
|
|
|
|
IStream *pstrmRoot = NULL;
|
|
|
|
HRESULT hr = URLOpenBlockingStreamW(NULL, pwszURL, &pstrmRoot, 0, NULL);
|
|
|
|
if (!SUCCEEDED(hr)) return hr;
|
|
|
|
m_pStream = pstrmRoot; pstrmRoot = NULL;
|
|
|
|
MarkActive(g_pStrmLockBytesFirstActive);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT CStrmLockBytes::CImpILockBytes::Init(IStream *pStrm)
|
|
{
|
|
STATSTG statstg;
|
|
|
|
HRESULT hr = pStrm->Stat(&statstg, STATFLAG_DEFAULT);
|
|
|
|
if (!SUCCEEDED(hr)) return hr;
|
|
|
|
UINT cwc = wcsLen(statstg.pwcsName);
|
|
|
|
if (cwc >= MAX_PATH)
|
|
{
|
|
OLEHeap()->Free(statstg.pwcsName);
|
|
|
|
return STG_E_INVALIDNAME;
|
|
}
|
|
|
|
CopyMemory(m_awszLkBName, statstg.pwcsName, sizeof(WCHAR) * (cwc + 1));
|
|
|
|
OLEHeap()->Free(statstg.pwcsName);
|
|
|
|
m_pStream = pStrm;
|
|
|
|
m_pStream->AddRef();
|
|
|
|
MarkActive(g_pStrmLockBytesFirstActive);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
// Search routine
|
|
|
|
ILockBytes *CStrmLockBytes::CImpILockBytes::FindStrmLockBytes(const WCHAR * pwszFileName)
|
|
{
|
|
return FindMatchingLockBytes(pwszFileName, (CImpITUnknown *) g_pStrmLockBytesFirstActive);
|
|
}
|
|
|
|
|
|
// ILockBytes methods:
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::ReadAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [length_is][size_is][out] */ void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbRead)
|
|
{
|
|
CSyncWith sw(m_cs);
|
|
|
|
HRESULT hr = m_pStream->Seek(*(LARGE_INTEGER *)&ulOffset, STREAM_SEEK_SET, NULL);
|
|
|
|
if (!SUCCEEDED(hr)) return hr;
|
|
|
|
hr = m_pStream->Read(pv, cb, pcbRead);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::WriteAt(
|
|
/* [in] */ ULARGE_INTEGER ulOffset,
|
|
/* [size_is][in] */ const void __RPC_FAR *pv,
|
|
/* [in] */ ULONG cb,
|
|
/* [out] */ ULONG __RPC_FAR *pcbWritten)
|
|
{
|
|
CSyncWith sw(m_cs);
|
|
|
|
HRESULT hr = m_pStream->Seek(*(LARGE_INTEGER *)&ulOffset, STREAM_SEEK_SET, NULL);
|
|
|
|
if (!SUCCEEDED(hr)) return hr;
|
|
|
|
hr = m_pStream->Write(pv, cb, pcbWritten);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::Flush( void)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::SetSize(
|
|
/* [in] */ ULARGE_INTEGER cb)
|
|
{
|
|
return m_pStream->SetSize(cb);
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::LockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
return m_pStream->LockRegion(libOffset, cb, dwLockType);
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::UnlockRegion(
|
|
/* [in] */ ULARGE_INTEGER libOffset,
|
|
/* [in] */ ULARGE_INTEGER cb,
|
|
/* [in] */ DWORD dwLockType)
|
|
{
|
|
return m_pStream->UnlockRegion(libOffset, cb, dwLockType);
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CStrmLockBytes::CImpILockBytes::Stat(
|
|
/* [out] */ STATSTG __RPC_FAR *pstatstg,
|
|
/* [in] */ DWORD grfStatFlag)
|
|
{
|
|
HRESULT hr = m_pStream->Stat(pstatstg, grfStatFlag);
|
|
|
|
if (SUCCEEDED(hr))
|
|
pstatstg->type = STGTY_LOCKBYTES;
|
|
|
|
return hr;
|
|
}
|