/*++ 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(::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 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(reinterpret_cast(pSourceObject)) == pSourceObject); ::CopyMemory( m_pbInputBuffer + m_dwInputCurrentOffset, reinterpret_cast(pSourceObject), dwSize ); // Remember the copied object T* pCopiedObject = reinterpret_cast(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 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(&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(wszText), usLen ); } else Pack( ft, (USHORT)0 ); return pwszReturned; }; template 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((PVOID)(pDestinationObject)); BS_ASSERT(reinterpret_cast(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(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(m_pbOutputBuffer + m_dwOutputCurrentOffset); BS_ASSERT( reinterpret_cast(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(::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__