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.
938 lines
30 KiB
938 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ichannel.hxx
|
|
|
|
Abstract:
|
|
|
|
Declaration of CVssIOCTLChannel
|
|
|
|
|
|
Adi Oltean [aoltean] 10/18/1999
|
|
|
|
Revision History:
|
|
|
|
Name Date Comments
|
|
aoltean 10/18/1999 Created
|
|
|
|
--*/
|
|
|
|
#ifndef __VSS_ICHANNEL_HXX__
|
|
#define __VSS_ICHANNEL_HXX__
|
|
|
|
#if _MSC_VER > 1000
|
|
#pragma once
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Standard foo for file name aliasing. This code block must be after
|
|
// all includes of VSS header files.
|
|
//
|
|
#ifdef VSS_FILE_ALIAS
|
|
#undef VSS_FILE_ALIAS
|
|
#endif
|
|
#define VSS_FILE_ALIAS "INCICHLH"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Constants
|
|
|
|
|
|
const x_nMemInitialSize = 4096; // Buffers allocated with 4096 bytes initially.
|
|
const x_nMemGrowthFactor = 2; // If allocation fault occurs, the memory is shrinked twice
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CVssIOCTLChannel declarations
|
|
|
|
|
|
/*
|
|
|
|
This class is used to send IOCTLs and correctly deal with its parameters.
|
|
|
|
*/
|
|
|
|
typedef enum _VSS_ICHANNEL_LOGGING {
|
|
VSS_ICHANNEL_LOG_NONE=0,
|
|
VSS_ICHANNEL_LOG_COORD=1,
|
|
VSS_ICHANNEL_LOG_PROV=2,
|
|
VSS_ICHANNEL_LOG_PROV_TIMEOUT=3,
|
|
VSS_ICHANNEL_LOG_PROV_NOTFOUND=4,
|
|
VSS_ICHANNEL_LOG_LOVELACE_HOLD=5,
|
|
VSS_ICHANNEL_LOG_LOVELACE_RELEASE=6
|
|
} VSS_ICHANNEL_LOGGING;
|
|
|
|
|
|
class CVssIOCTLChannel
|
|
{
|
|
|
|
// Constructors& destructors
|
|
private:
|
|
CVssIOCTLChannel(const CVssIOCTLChannel&); // disable copy constructor
|
|
|
|
public:
|
|
|
|
CVssIOCTLChannel(
|
|
IN WORD wAlignmentBytes = 0 // Bytes alignment for pushed parameters.
|
|
):
|
|
m_hDevice(NULL),
|
|
m_dwInputCurrentOffset(0),
|
|
m_dwInputAllocatedSize(0),
|
|
m_pbInputBuffer(NULL),
|
|
m_dwOutputCurrentOffset(0),
|
|
m_dwOutputAllocatedSize(0),
|
|
m_dwOutputCurrentSize(0),
|
|
m_pbOutputBuffer(NULL),
|
|
m_wAlignmentBytes(wAlignmentBytes)
|
|
{};
|
|
|
|
~CVssIOCTLChannel()
|
|
{
|
|
// Close and reset some offsets
|
|
Close();
|
|
|
|
// Delete the allocated buffers
|
|
if (m_pbInputBuffer)
|
|
::free(m_pbInputBuffer);
|
|
m_pbInputBuffer = NULL;
|
|
m_dwInputAllocatedSize = 0;
|
|
|
|
if (m_pbOutputBuffer)
|
|
::free(m_pbOutputBuffer);
|
|
m_pbOutputBuffer = NULL;
|
|
m_dwOutputAllocatedSize = 0;
|
|
}
|
|
|
|
// Main operations
|
|
public:
|
|
|
|
bool IsOpen() const { return (m_hDevice != NULL); };
|
|
|
|
|
|
HRESULT Open(
|
|
IN CVssFunctionTracer& ft,
|
|
IN const WCHAR* pwszObjectName,
|
|
IN bool bEliminateLastBackslash,
|
|
IN bool bThrowIfErrorOnOpen,
|
|
IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE,
|
|
IN DWORD dwAccessType = GENERIC_READ | GENERIC_WRITE,
|
|
IN DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Connect to that device in order to send the IOCTL.
|
|
|
|
if (bThrowIfErrorOnOpen) throw on E_UNEXPECTED on error.
|
|
else return VSS_E_OBJECT_NOT_FOUND if device name not found
|
|
otherwise E_UNEXPECTED.
|
|
|
|
*/
|
|
|
|
{
|
|
// The length of the interanl buffer. Includes the trailing zero character.
|
|
const nInternalBufferLen = MAX_PATH;
|
|
|
|
BS_ASSERT(pwszObjectName);
|
|
BS_ASSERT(bThrowIfErrorOnOpen || (eLogging == VSS_ICHANNEL_LOG_NONE));
|
|
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
// Close
|
|
Close();
|
|
|
|
// Eliminate the internal backslash, if needed
|
|
LPCWSTR pwszObjectNameInternal = pwszObjectName;
|
|
|
|
WCHAR wszBuffer[nInternalBufferLen];
|
|
if (bEliminateLastBackslash) {
|
|
|
|
// Check to see if the buffer is large enough
|
|
size_t nObjectNameLen = ::wcslen(pwszObjectName);
|
|
if (nInternalBufferLen < nObjectNameLen) {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Error: the buffer is too small to fit %s (%d < %d)",
|
|
pwszObjectName, nInternalBufferLen, nObjectNameLen);
|
|
}
|
|
|
|
// Check if the string is terminated with a backslash
|
|
if (pwszObjectName[nObjectNameLen - 1] != L'\\') {
|
|
BS_ASSERT(false);
|
|
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
|
|
L"Error: the object name %s does terminate with a backslash",
|
|
pwszObjectName);
|
|
}
|
|
|
|
// Copy the string and append the trailing zero.
|
|
::wcsncpy(wszBuffer, pwszObjectName, nObjectNameLen - 1);
|
|
wszBuffer[nObjectNameLen - 1] = L'\0';
|
|
BS_ASSERT(::wcslen(wszBuffer) == nObjectNameLen - 1);
|
|
|
|
pwszObjectNameInternal = wszBuffer;
|
|
}
|
|
|
|
|
|
// Open the handle to the new object.
|
|
m_hDevice = ::CreateFile(pwszObjectNameInternal,
|
|
dwAccessType,
|
|
dwShareMode,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
INVALID_HANDLE_VALUE);
|
|
if (m_hDevice == INVALID_HANDLE_VALUE)
|
|
{
|
|
m_hDevice = NULL;
|
|
DWORD dwLastErr = GetLastError();
|
|
if (bThrowIfErrorOnOpen) {
|
|
switch(eLogging) {
|
|
default:
|
|
BS_ASSERT(false);
|
|
case VSS_ICHANNEL_LOG_NONE:
|
|
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
|
|
L"Could not open the object \'%s\'. [0x%08lx]\n",
|
|
pwszObjectNameInternal, dwLastErr);
|
|
break;
|
|
|
|
#ifdef IN_VSS
|
|
case VSS_ICHANNEL_LOG_COORD:
|
|
ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
|
|
L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
|
|
pwszObjectNameInternal, dwAccessType, dwShareMode);
|
|
break;
|
|
|
|
case VSS_ICHANNEL_LOG_PROV:
|
|
ft.TranslateInternalProviderError(VSSDBG_SWPRV,
|
|
HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
|
|
L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
|
|
pwszObjectNameInternal, dwAccessType, dwShareMode);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ft.Trace( VSSDBG_IOCTL,
|
|
L"Could not open the object \'%s\'. [0x%08lx]\n",
|
|
pwszObjectNameInternal, dwLastErr);
|
|
|
|
//ft.hr = ((dwLastErr == ERROR_FILE_NOT_FOUND) ||
|
|
// (dwLastErr == ERROR_NOT_READY) ||
|
|
// (dwLastErr == ERROR_DEVICE_NOT_CONNECTED))?
|
|
// (VSS_E_OBJECT_NOT_FOUND): E_UNEXPECTED;
|
|
|
|
ft.hr = HRESULT_FROM_WIN32(dwLastErr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ft.Trace( VSSDBG_IOCTL,
|
|
L"The object \'%s\'. was opened under the device handle 0x%08lx",
|
|
pwszObjectNameInternal, m_hDevice);
|
|
}
|
|
|
|
return ft.hr;
|
|
}
|
|
|
|
|
|
void Close()
|
|
{
|
|
// Close previous handle, if needed.
|
|
if (m_hDevice)
|
|
::CloseHandle(m_hDevice);
|
|
m_hDevice= NULL;
|
|
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
m_dwOutputCurrentSize = 0;
|
|
}
|
|
|
|
|
|
HRESULT Call(
|
|
IN CVssFunctionTracer& ft,
|
|
IN DWORD dwIoControlCode,
|
|
IN bool bThrowIfErrorOnCall = true,
|
|
IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Send the IOCTL to that device.
|
|
Before that reserve dwProposedOutputBufferSize bytes for the output buffer.
|
|
|
|
*/
|
|
|
|
{
|
|
DWORD dwProposedOutputBufferSize = 0;
|
|
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
BS_ASSERT(bThrowIfErrorOnCall || (eLogging == VSS_ICHANNEL_LOG_NONE));
|
|
|
|
// Check if connected
|
|
if (m_hDevice == NULL)
|
|
{
|
|
BS_ASSERT(false);
|
|
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
|
|
L"Channel not open");
|
|
}
|
|
|
|
// Reset output buffer
|
|
m_dwOutputCurrentOffset = 0;
|
|
m_dwOutputCurrentSize = 0;
|
|
|
|
// Repeat while the output buffer can hold the data
|
|
BOOL bResult = false;
|
|
while(true)
|
|
{
|
|
// If no buffer and no sizes, try with the default size
|
|
if ((dwProposedOutputBufferSize == 0) && (m_dwOutputAllocatedSize == 0))
|
|
{
|
|
BS_ASSERT(m_pbOutputBuffer == NULL);
|
|
dwProposedOutputBufferSize = x_nMemInitialSize;
|
|
}
|
|
|
|
// Realloc the existing buffer if needed
|
|
if (dwProposedOutputBufferSize > m_dwOutputAllocatedSize)
|
|
{
|
|
PBYTE pbOutputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbOutputBuffer, dwProposedOutputBufferSize));
|
|
if (pbOutputBuffer == NULL)
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Could not extend the output buffer");
|
|
}
|
|
|
|
// Change the buffer
|
|
m_pbOutputBuffer = pbOutputBuffer;
|
|
m_dwOutputAllocatedSize = dwProposedOutputBufferSize;
|
|
}
|
|
|
|
ft.Trace( VSSDBG_IOCTL, L"IOCTL sent: %lx\n Input buffer size: %ld, Output buffer size: %ld",
|
|
dwIoControlCode, m_dwInputCurrentOffset, m_dwOutputAllocatedSize );
|
|
ft.TraceBuffer( VSSDBG_IOCTL, m_dwInputCurrentOffset, m_pbInputBuffer );
|
|
|
|
// Send the IOCTL.
|
|
bResult = ::DeviceIoControl(m_hDevice,
|
|
dwIoControlCode,
|
|
m_pbInputBuffer,
|
|
m_dwInputCurrentOffset,
|
|
m_pbOutputBuffer,
|
|
m_dwOutputAllocatedSize,
|
|
&m_dwOutputCurrentSize,
|
|
NULL
|
|
);
|
|
|
|
// If the error was "insufficient buffer" then try to reallocate a bigger one.
|
|
// Otherwise end the iteration
|
|
if (!bResult && ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) || (GetLastError() == ERROR_MORE_DATA)))
|
|
dwProposedOutputBufferSize = x_nMemGrowthFactor*m_dwOutputAllocatedSize;
|
|
else
|
|
break;
|
|
}
|
|
|
|
DWORD dwLastErr = GetLastError();
|
|
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
// Treat the error case
|
|
if (!bResult) {
|
|
if (bThrowIfErrorOnCall) {
|
|
switch(eLogging) {
|
|
default:
|
|
BS_ASSERT(false);
|
|
case VSS_ICHANNEL_LOG_NONE:
|
|
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
|
|
L"Could not send the IOCTL 0x%08lx on device 0x%08lx. [0x%08lx]\n",
|
|
dwIoControlCode, m_hDevice, dwLastErr);
|
|
break;
|
|
|
|
case VSS_ICHANNEL_LOG_COORD:
|
|
ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
|
|
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
|
|
m_hDevice, dwIoControlCode,
|
|
m_pbInputBuffer, m_dwInputCurrentOffset,
|
|
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
|
|
break;
|
|
|
|
#ifdef IN_VSS
|
|
case VSS_ICHANNEL_LOG_LOVELACE_HOLD:
|
|
case VSS_ICHANNEL_LOG_LOVELACE_RELEASE:
|
|
ft.TranslateInternalLovelaceError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
|
|
((eLogging == VSS_ICHANNEL_LOG_LOVELACE_HOLD) ? TRUE : FALSE),
|
|
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
|
|
m_hDevice, dwIoControlCode,
|
|
m_pbInputBuffer, m_dwInputCurrentOffset,
|
|
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
|
|
break;
|
|
|
|
case VSS_ICHANNEL_LOG_PROV_TIMEOUT:
|
|
if (dwLastErr == ERROR_INVALID_PARAMETER)
|
|
// This logging flag indicates that this actually means timeout
|
|
dwLastErr = ERROR_TIMEOUT;
|
|
|
|
// fall through...
|
|
|
|
case VSS_ICHANNEL_LOG_PROV:
|
|
ft.TranslateInternalProviderError(VSSDBG_SWPRV,
|
|
HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
|
|
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
|
|
m_hDevice, dwIoControlCode,
|
|
m_pbInputBuffer, m_dwInputCurrentOffset,
|
|
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
|
|
break;
|
|
|
|
case VSS_ICHANNEL_LOG_PROV_NOTFOUND:
|
|
{
|
|
HRESULT hrThrow = VSS_E_PROVIDER_VETO;
|
|
if (dwLastErr == ERROR_INVALID_PARAMETER)
|
|
hrThrow = VSS_E_OBJECT_NOT_FOUND;
|
|
ft.TranslateInternalProviderError(VSSDBG_SWPRV,
|
|
HRESULT_FROM_WIN32(dwLastErr), hrThrow,
|
|
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
|
|
m_hDevice, dwIoControlCode,
|
|
m_pbInputBuffer, m_dwInputCurrentOffset,
|
|
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx failed on device 0x%08lx. Error code = 0x%08lx",
|
|
dwIoControlCode, m_hDevice, dwLastErr);
|
|
ft.hr = HRESULT_FROM_WIN32(dwLastErr);
|
|
}
|
|
} else {
|
|
ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx succeeded on device 0x%08lx. \n Output buffer: size received = %ld",
|
|
dwIoControlCode, m_hDevice, m_dwOutputCurrentSize );
|
|
ft.TraceBuffer( VSSDBG_IOCTL, m_dwOutputCurrentSize, m_pbOutputBuffer );
|
|
ft.hr = S_OK; // Clear the previous HRESULT value
|
|
}
|
|
|
|
return ft.hr;
|
|
}
|
|
|
|
|
|
template <class T>
|
|
PVOID PackArray(
|
|
IN CVssFunctionTracer& ft,
|
|
IN T* pSourceObject,
|
|
IN DWORD dwNumObjects
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Copy the contents of the given object(s) into the input buffer...
|
|
If out of memory, the old input buffer remains unchanged and an exception is thrown.
|
|
|
|
*/
|
|
|
|
{
|
|
BS_ASSERT(pSourceObject);
|
|
BS_ASSERT(dwNumObjects);
|
|
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
DWORD dwSize = sizeof(T) * dwNumObjects;
|
|
if ( dwSize / dwNumObjects != sizeof(T) )
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Arithmetic overflow. dwNumObjects = %lu too large",
|
|
dwNumObjects);
|
|
}
|
|
|
|
// Make room for the GUID. Here an E_OUTOFMEMORY exception can occur.
|
|
ExpandInputBuffer( ft, dwSize );
|
|
BS_ASSERT(m_pbInputBuffer);
|
|
BS_ASSERT(m_dwInputCurrentOffset + dwSize <= m_dwInputAllocatedSize);
|
|
|
|
// Copy the object contents... Check again for arithmetic overflow
|
|
if (m_pbInputBuffer + m_dwInputCurrentOffset + dwSize < m_pbInputBuffer)
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Arithmetic overflow. m_dwInputCurrentOffset = %lu or dwSize = %lu too large.",
|
|
m_dwInputCurrentOffset, dwSize);
|
|
}
|
|
|
|
BS_ASSERT(reinterpret_cast<T*>(reinterpret_cast<PBYTE>(pSourceObject)) == pSourceObject);
|
|
::CopyMemory( m_pbInputBuffer + m_dwInputCurrentOffset,
|
|
reinterpret_cast<PBYTE>(pSourceObject),
|
|
dwSize
|
|
);
|
|
|
|
// Remember the copied object
|
|
T* pCopiedObject = reinterpret_cast<T*>(m_pbInputBuffer + m_dwInputCurrentOffset);
|
|
|
|
// Increase the current offset
|
|
m_dwInputCurrentOffset += dwSize;
|
|
|
|
if (m_wAlignmentBytes)
|
|
{
|
|
// Round up the value in relation with the existing alignment
|
|
WORD wAlignmentOffset = (WORD)( m_dwInputCurrentOffset % m_wAlignmentBytes );
|
|
BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
|
|
if (wAlignmentOffset != 0)
|
|
m_dwInputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
|
|
BS_ASSERT( m_dwInputCurrentOffset % m_wAlignmentBytes == 0 );
|
|
}
|
|
|
|
// Return the copied object
|
|
return pCopiedObject;
|
|
}
|
|
|
|
|
|
template <class T>
|
|
PVOID Pack(
|
|
IN CVssFunctionTracer& ft,
|
|
IN const T& SourceObject
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Copy the contents of the given object(s) into the input buffer...
|
|
If out of memory, the old input buffer remains unchanged and an exception is thrown.
|
|
|
|
*/
|
|
|
|
{
|
|
return PackArray( ft, const_cast<T*>(&SourceObject), 1 );
|
|
};
|
|
|
|
|
|
PVOID PackSmallString(
|
|
IN CVssFunctionTracer& ft,
|
|
IN LPCWSTR wszText
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Copy the contents of the given string into the input buffer.
|
|
The storing format is
|
|
+--------+
|
|
| Len | USHORT Length, in bytes
|
|
+--------+
|
|
| String | WCHAR[] String characters, without terminating zero
|
|
| ... |
|
|
+--------+
|
|
|
|
If the string pointer is NULL or the string is empty then a NULL string will be put.
|
|
|
|
+--------+
|
|
| 0 | USHORT
|
|
+--------+
|
|
|
|
If out of memory, the old input buffer remains unchanged and an exception is thrown.
|
|
|
|
The pointer to the string is returned.
|
|
|
|
*/
|
|
|
|
{
|
|
PVOID pwszReturned = NULL;
|
|
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
if ( wszText != NULL)
|
|
{
|
|
size_t nLen = ::wcslen(wszText);
|
|
USHORT usLen = (USHORT)(nLen);
|
|
|
|
// Check buffer overflow
|
|
if ((size_t)usLen != nLen)
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY, L"Arithmetic overflow. %ld", nLen);
|
|
|
|
// Pack the length and the string
|
|
Pack( ft, (USHORT)(usLen * sizeof(WCHAR)) );
|
|
if ( usLen != 0)
|
|
pwszReturned = PackArray( ft, const_cast<LPWSTR>(wszText), usLen );
|
|
}
|
|
else
|
|
Pack( ft, (USHORT)0 );
|
|
|
|
return pwszReturned;
|
|
};
|
|
|
|
|
|
template <class T>
|
|
void Unpack(
|
|
IN CVssFunctionTracer& ft,
|
|
IN T* pDestinationObject,
|
|
IN DWORD dwNumObjects = 1,
|
|
IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Copy the contents of the given object(s) from the output buffer...
|
|
|
|
*/
|
|
|
|
{
|
|
BS_ASSERT((pDestinationObject != NULL) || !bTrueUnpack);
|
|
BS_ASSERT(dwNumObjects);
|
|
|
|
// Compute size, in bytes for the allocated objects. Check for Arithmetic overflow.
|
|
DWORD dwSize = sizeof(T) * dwNumObjects;
|
|
if ( dwSize / dwNumObjects != sizeof(T) )
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Arithmetic overflow. dwNumObjects = %lu too large",
|
|
dwNumObjects);
|
|
}
|
|
|
|
// Check for buffer overflow.
|
|
if ((m_pbOutputBuffer == NULL) ||
|
|
(m_dwOutputCurrentOffset + dwSize > m_dwOutputCurrentSize))
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
BS_ASSERT(false);
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
|
|
L"Output buffer overflow. Reading bad arguments. dwSize = %lu",
|
|
dwSize);
|
|
}
|
|
|
|
// Check again for arithmetic overflow and Copy the object contents...
|
|
if (m_pbOutputBuffer + m_dwOutputCurrentOffset + dwSize < m_pbOutputBuffer)
|
|
{
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Arithmetic overflow. m_dwOutputCurrentOffset = %lu or dwSize = %lu too large.",
|
|
m_dwOutputCurrentOffset, dwSize);
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
}
|
|
|
|
PBYTE pbBuffer = reinterpret_cast<PBYTE>((PVOID)(pDestinationObject));
|
|
BS_ASSERT(reinterpret_cast<T*>(pbBuffer) == pDestinationObject);
|
|
if (bTrueUnpack)
|
|
::CopyMemory( pbBuffer, m_pbOutputBuffer + m_dwOutputCurrentOffset, dwSize );
|
|
|
|
// Increase the current offset
|
|
m_dwOutputCurrentOffset += dwSize;
|
|
|
|
if (m_wAlignmentBytes)
|
|
{
|
|
// Round up the value in relation with the existing alignment
|
|
WORD wAlignmentOffset = (WORD)( m_dwOutputCurrentOffset % m_wAlignmentBytes );
|
|
BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
|
|
if (wAlignmentOffset != 0)
|
|
m_dwOutputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
|
|
BS_ASSERT( m_dwOutputCurrentOffset % m_wAlignmentBytes == 0 );
|
|
}
|
|
}
|
|
|
|
LPCWSTR UnpackSmallString(
|
|
IN CVssFunctionTracer& ft,
|
|
IN OUT LPCWSTR& pwszText,
|
|
IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Allocate a string and copy the contents of the
|
|
given string from the output buffer.
|
|
|
|
Deallocate the previous string, if any.
|
|
|
|
The storing format is
|
|
+--------+
|
|
| Len | USHORT Length, in bytes
|
|
+--------+
|
|
| String | WCHAR[] String characters, without terminating zero
|
|
| ... |
|
|
+--------+
|
|
|
|
If the string is empty then a NULL string will be put.
|
|
|
|
+--------+
|
|
| 0 | USHORT
|
|
+--------+
|
|
|
|
If out of memory, the old output buffer is freed and an exception is thrown.
|
|
|
|
The pointer to the allocated string is returned.
|
|
|
|
|
|
*/
|
|
|
|
{
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
// Free the previous string, if any
|
|
if (bTrueUnpack)
|
|
::VssFreeString(pwszText);
|
|
|
|
// Catch Unpack failures
|
|
ft.hr = S_OK;
|
|
try
|
|
{
|
|
// Get the string length
|
|
USHORT usSize = 0;
|
|
Unpack( ft, & usSize );
|
|
|
|
// Convert from number of bytes into number of WCHARs
|
|
USHORT usLen = (USHORT)(usSize/2);
|
|
BS_ASSERT( usLen*2 == usSize );
|
|
|
|
// Get the string
|
|
if (usLen > 0)
|
|
{
|
|
// Get the string (length = usLen+1, including the zero)
|
|
if (bTrueUnpack)
|
|
pwszText = ::VssAllocString(ft, usLen);
|
|
|
|
// Unpack ulLen characters.
|
|
Unpack( ft, pwszText, usLen, bTrueUnpack );
|
|
|
|
// Setup the zero character
|
|
if (bTrueUnpack)
|
|
const_cast<LPWSTR>(pwszText)[usLen] = L'\0';
|
|
}
|
|
}
|
|
VSS_STANDARD_CATCH(ft)
|
|
|
|
if (ft.HrFailed()) {
|
|
if (bTrueUnpack)
|
|
::VssFreeString(pwszText);
|
|
ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
|
|
}
|
|
|
|
return bTrueUnpack? pwszText: NULL;
|
|
};
|
|
|
|
|
|
LPCWSTR UnpackZeroString(
|
|
IN CVssFunctionTracer& ft,
|
|
IN OUT LPCWSTR& pwszText
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Allocate a string and copy the contents of the
|
|
given string from the output buffer.
|
|
|
|
Deallocate the previous string, if any.
|
|
|
|
The storing format is
|
|
+--------+
|
|
| String | WCHAR[] String characters, with terminating zero
|
|
| ... |
|
|
+--------+
|
|
| 0 |
|
|
+--------+
|
|
|
|
If the string is empty then a NULL string will be put.
|
|
|
|
+--------+
|
|
| 0 | WCHAR
|
|
+--------+
|
|
|
|
If out of memory, the old output buffer is freed anyway and an exception is thrown.
|
|
|
|
The pointer to the allocated string is returned.
|
|
|
|
|
|
*/
|
|
|
|
{
|
|
// Free the previous string, if any
|
|
::VssFreeString(pwszText);
|
|
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
// Catch Unpack failures
|
|
try
|
|
{
|
|
// Check for buffer validity.
|
|
if (m_pbOutputBuffer == NULL)
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
BS_ASSERT(false);
|
|
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED, L"Reading from NULL buffer");
|
|
}
|
|
|
|
// Get the string in the buffer
|
|
LPWSTR pwszTextInBuffer = reinterpret_cast<LPWSTR>(m_pbOutputBuffer + m_dwOutputCurrentOffset);
|
|
BS_ASSERT( reinterpret_cast<PBYTE>(pwszTextInBuffer) == m_pbOutputBuffer + m_dwOutputCurrentOffset);
|
|
DWORD dwStrLen = (DWORD)::wcslen(pwszTextInBuffer);
|
|
|
|
if (dwStrLen > 0)
|
|
{
|
|
// Allocate the new string
|
|
pwszText = ::VssAllocString(ft, dwStrLen);
|
|
BS_ASSERT(pwszText);
|
|
|
|
// Unpack ulLen+1 characters (including the zero).
|
|
Unpack( ft, pwszText, dwStrLen+1 );
|
|
BS_ASSERT(pwszText[dwStrLen] == L'\0');
|
|
}
|
|
else
|
|
{
|
|
// Unpack the zero character
|
|
WCHAR wchTest;
|
|
Unpack( ft, &wchTest);
|
|
BS_ASSERT(wchTest == L'\0');
|
|
}
|
|
}
|
|
VSS_STANDARD_CATCH(ft)
|
|
|
|
if (ft.HrFailed()) {
|
|
::VssFreeString(pwszText);
|
|
ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
|
|
}
|
|
|
|
|
|
return pwszText;
|
|
};
|
|
|
|
|
|
void PadWithZero(
|
|
IN CVssFunctionTracer& ft,
|
|
IN DWORD dwAlignToBytes = sizeof(INT64)
|
|
)
|
|
{
|
|
INT nBytesRemaining = m_dwInputCurrentOffset % dwAlignToBytes;
|
|
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
if (nBytesRemaining != 0)
|
|
{
|
|
INT nBytesToBePadded = dwAlignToBytes - nBytesRemaining;
|
|
for (int i = 0; i < nBytesToBePadded; i++)
|
|
Pack( ft, (BYTE)0 );
|
|
}
|
|
}
|
|
|
|
void ResetOffsets()
|
|
{
|
|
m_dwInputCurrentOffset = 0;
|
|
m_dwOutputCurrentOffset = 0;
|
|
}
|
|
|
|
DWORD GetCurrentInputOffset() { return m_dwInputCurrentOffset; };
|
|
|
|
DWORD GetCurrentOutputOffset() { return m_dwOutputCurrentOffset; };
|
|
|
|
// Internal operations
|
|
private:
|
|
|
|
void ExpandInputBuffer(
|
|
IN CVssFunctionTracer& ft,
|
|
IN DWORD dwBytes
|
|
) throw(HRESULT)
|
|
|
|
/*
|
|
|
|
Expand the input buffer to accomodate adding another dwBytes.
|
|
The m_dwInputCurrentOffset remains unchanged.
|
|
If out of memory, the old input buffer remains unchanged and an exception is thrown.
|
|
|
|
*/
|
|
|
|
{
|
|
// Reset the error code
|
|
ft.hr = S_OK;
|
|
|
|
// Check for arithmetic overflow
|
|
if (dwBytes + m_dwInputCurrentOffset < m_dwInputCurrentOffset)
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Arithmetic overflow. dwBytes = %lu too large", dwBytes);
|
|
|
|
// Check if the buffer needs to be extended
|
|
if (dwBytes + m_dwInputCurrentOffset > m_dwInputAllocatedSize)
|
|
{
|
|
// Compute the new size of the buffer...
|
|
DWORD dwNewSize = m_dwInputAllocatedSize;
|
|
while(dwBytes + m_dwInputCurrentOffset > dwNewSize)
|
|
{
|
|
if (dwNewSize != 0)
|
|
{
|
|
// Check again for arithmetic overflow
|
|
if (dwNewSize * x_nMemGrowthFactor <= dwNewSize)
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Arithmetic overflow. dwNewSize = %lu too large", dwNewSize);
|
|
}
|
|
|
|
// Increase the allocated size.
|
|
dwNewSize = dwNewSize * x_nMemGrowthFactor;
|
|
}
|
|
else
|
|
dwNewSize = x_nMemInitialSize;
|
|
}
|
|
|
|
// Reallocate the buffer. If realloc fails, the old buffer is still kept.
|
|
PBYTE pbNewInputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbInputBuffer, dwNewSize));
|
|
if (pbNewInputBuffer == NULL)
|
|
{
|
|
// Reset internal offsets
|
|
ResetOffsets();
|
|
|
|
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
|
|
L"Could not extend the input buffer");
|
|
}
|
|
|
|
// Change the buffer
|
|
m_pbInputBuffer = pbNewInputBuffer;
|
|
m_dwInputAllocatedSize = dwNewSize;
|
|
}
|
|
}
|
|
|
|
|
|
// Internal data members.
|
|
private:
|
|
|
|
// I/O Device-related members
|
|
HANDLE m_hDevice;
|
|
|
|
// Input buffer
|
|
DWORD m_dwInputCurrentOffset;
|
|
DWORD m_dwInputAllocatedSize;
|
|
PBYTE m_pbInputBuffer;
|
|
|
|
// Output buffer
|
|
DWORD m_dwOutputCurrentOffset;
|
|
DWORD m_dwOutputAllocatedSize;
|
|
DWORD m_dwOutputCurrentSize;
|
|
PBYTE m_pbOutputBuffer;
|
|
WORD m_wAlignmentBytes;
|
|
|
|
};
|
|
|
|
|
|
#endif // __VSS_ICHANNEL_HXX__
|