Leaked source code of windows server 2003
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.

942 lines
26 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. ichannel.hxx
  5. Abstract:
  6. Declaration of CVssIOCTLChannel
  7. Adi Oltean [aoltean] 10/18/1999
  8. Revision History:
  9. Name Date Comments
  10. aoltean 10/18/1999 Created
  11. --*/
  12. #ifndef __VSS_ICHANNEL_HXX__
  13. #define __VSS_ICHANNEL_HXX__
  14. #if _MSC_VER > 1000
  15. #pragma once
  16. #endif
  17. ////////////////////////////////////////////////////////////////////////
  18. // Standard foo for file name aliasing. This code block must be after
  19. // all includes of VSS header files.
  20. //
  21. #ifdef VSS_FILE_ALIAS
  22. #undef VSS_FILE_ALIAS
  23. #endif
  24. #define VSS_FILE_ALIAS "INCICHLH"
  25. //
  26. ////////////////////////////////////////////////////////////////////////
  27. /////////////////////////////////////////////////////////////////////////////
  28. // Constants
  29. const x_nMemInitialSize = 4096; // Buffers allocated with 4096 bytes initially.
  30. const x_nMemGrowthFactor = 2; // If allocation fault occurs, the memory is shrinked twice
  31. /////////////////////////////////////////////////////////////////////////////
  32. // CVssIOCTLChannel declarations
  33. /*
  34. This class is used to send IOCTLs and correctly deal with its parameters.
  35. */
  36. typedef enum _VSS_ICHANNEL_LOGGING {
  37. VSS_ICHANNEL_LOG_NONE=0,
  38. VSS_ICHANNEL_LOG_COORD=1,
  39. VSS_ICHANNEL_LOG_PROV=2,
  40. VSS_ICHANNEL_LOG_PROV_TIMEOUT=3,
  41. VSS_ICHANNEL_LOG_PROV_NOTFOUND=4,
  42. VSS_ICHANNEL_LOG_LOVELACE_HOLD=5,
  43. VSS_ICHANNEL_LOG_LOVELACE_RELEASE=6
  44. } VSS_ICHANNEL_LOGGING;
  45. class CVssIOCTLChannel
  46. {
  47. // Constructors& destructors
  48. private:
  49. CVssIOCTLChannel(const CVssIOCTLChannel&); // disable copy constructor
  50. public:
  51. CVssIOCTLChannel(
  52. IN WORD wAlignmentBytes = 0 // Bytes alignment for pushed parameters.
  53. ):
  54. m_hDevice(NULL),
  55. m_dwInputCurrentOffset(0),
  56. m_dwInputAllocatedSize(0),
  57. m_pbInputBuffer(NULL),
  58. m_dwOutputCurrentOffset(0),
  59. m_dwOutputAllocatedSize(0),
  60. m_dwOutputCurrentSize(0),
  61. m_pbOutputBuffer(NULL),
  62. m_wAlignmentBytes(wAlignmentBytes)
  63. {};
  64. ~CVssIOCTLChannel()
  65. {
  66. // Close and reset some offsets
  67. Close();
  68. // Delete the allocated buffers
  69. if (m_pbInputBuffer)
  70. ::free(m_pbInputBuffer);
  71. m_pbInputBuffer = NULL;
  72. m_dwInputAllocatedSize = 0;
  73. if (m_pbOutputBuffer)
  74. ::free(m_pbOutputBuffer);
  75. m_pbOutputBuffer = NULL;
  76. m_dwOutputAllocatedSize = 0;
  77. }
  78. // Main operations
  79. public:
  80. bool IsOpen() const { return (m_hDevice != NULL); };
  81. HRESULT Open(
  82. IN CVssFunctionTracer& ft,
  83. IN const WCHAR* pwszObjectName,
  84. IN bool bEliminateLastBackslash,
  85. IN bool bThrowIfErrorOnOpen,
  86. IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE,
  87. IN DWORD dwAccessType = GENERIC_READ | GENERIC_WRITE,
  88. IN DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
  89. ) throw(HRESULT)
  90. /*
  91. Connect to that device in order to send the IOCTL.
  92. if (bThrowIfErrorOnOpen) throw on E_UNEXPECTED on error.
  93. else return VSS_E_OBJECT_NOT_FOUND if device name not found
  94. otherwise E_UNEXPECTED.
  95. */
  96. {
  97. // The length of the interanl buffer. Includes the trailing zero character.
  98. const nInternalBufferLen = MAX_PATH;
  99. BS_ASSERT(pwszObjectName);
  100. BS_ASSERT(bThrowIfErrorOnOpen || (eLogging == VSS_ICHANNEL_LOG_NONE));
  101. // Reset the error code
  102. ft.hr = S_OK;
  103. // Close
  104. Close();
  105. // Eliminate the internal backslash, if needed
  106. LPCWSTR pwszObjectNameInternal = pwszObjectName;
  107. WCHAR wszBuffer[nInternalBufferLen];
  108. if (bEliminateLastBackslash) {
  109. // Check to see if the buffer is large enough
  110. size_t nObjectNameLen = ::wcslen(pwszObjectName);
  111. if ((nObjectNameLen == 0) || (nInternalBufferLen < nObjectNameLen)) {
  112. BS_ASSERT(false);
  113. ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
  114. L"Error: the buffer is too small to fit %s (%d < %d)",
  115. pwszObjectName, nInternalBufferLen, nObjectNameLen);
  116. }
  117. // Check if the string is terminated with a backslash
  118. if (pwszObjectName[nObjectNameLen - 1] != L'\\') {
  119. BS_ASSERT(false);
  120. ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
  121. L"Error: the object name %s does terminate with a backslash",
  122. pwszObjectName);
  123. }
  124. // Copy the string and append the trailing zero.
  125. ::wcsncpy(wszBuffer, pwszObjectName, nObjectNameLen - 1);
  126. wszBuffer[nObjectNameLen - 1] = L'\0';
  127. BS_ASSERT(::wcslen(wszBuffer) == nObjectNameLen - 1);
  128. pwszObjectNameInternal = wszBuffer;
  129. }
  130. // Open the handle to the new object.
  131. m_hDevice = ::CreateFile(pwszObjectNameInternal,
  132. dwAccessType,
  133. dwShareMode,
  134. NULL,
  135. OPEN_EXISTING,
  136. FILE_ATTRIBUTE_NORMAL,
  137. INVALID_HANDLE_VALUE);
  138. if (m_hDevice == INVALID_HANDLE_VALUE)
  139. {
  140. m_hDevice = NULL;
  141. DWORD dwLastErr = GetLastError();
  142. if (bThrowIfErrorOnOpen) {
  143. switch(eLogging) {
  144. default:
  145. BS_ASSERT(false);
  146. case VSS_ICHANNEL_LOG_NONE:
  147. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  148. L"Could not open the object \'%s\'. [0x%08lx]\n",
  149. pwszObjectNameInternal, dwLastErr);
  150. break;
  151. case VSS_ICHANNEL_LOG_COORD:
  152. ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
  153. L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
  154. pwszObjectNameInternal, dwAccessType, dwShareMode);
  155. break;
  156. case VSS_ICHANNEL_LOG_PROV:
  157. ft.TranslateInternalProviderError(VSSDBG_SWPRV,
  158. HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
  159. L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
  160. pwszObjectNameInternal, dwAccessType, dwShareMode);
  161. break;
  162. }
  163. }
  164. else
  165. {
  166. ft.Trace( VSSDBG_IOCTL,
  167. L"Could not open the object \'%s\'. [0x%08lx]\n",
  168. pwszObjectNameInternal, dwLastErr);
  169. ft.hr = ((dwLastErr == ERROR_FILE_NOT_FOUND) ||
  170. (dwLastErr == ERROR_NOT_READY) ||
  171. (dwLastErr == ERROR_DEVICE_NOT_CONNECTED))?
  172. (VSS_E_OBJECT_NOT_FOUND): E_UNEXPECTED;
  173. }
  174. }
  175. else
  176. {
  177. ft.Trace( VSSDBG_IOCTL,
  178. L"The object \'%s\'. was opened under the device handle 0x%08lx",
  179. pwszObjectNameInternal, m_hDevice);
  180. m_pwszDevice.CopyFrom(pwszObjectNameInternal);
  181. }
  182. return ft.hr;
  183. }
  184. void Close()
  185. {
  186. // Close previous handle, if needed.
  187. if (m_hDevice)
  188. ::CloseHandle(m_hDevice);
  189. m_hDevice= NULL;
  190. // Reset internal offsets
  191. ResetOffsets();
  192. m_dwOutputCurrentSize = 0;
  193. }
  194. HRESULT Call(
  195. IN CVssFunctionTracer& ft,
  196. IN DWORD dwIoControlCode,
  197. IN bool bThrowIfErrorOnCall = true,
  198. IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE
  199. ) throw(HRESULT)
  200. /*
  201. Send the IOCTL to that device.
  202. Before that reserve dwProposedOutputBufferSize bytes for the output buffer.
  203. */
  204. {
  205. DWORD dwProposedOutputBufferSize = 0;
  206. // Reset the error code
  207. ft.hr = S_OK;
  208. BS_ASSERT(bThrowIfErrorOnCall || (eLogging == VSS_ICHANNEL_LOG_NONE));
  209. // Check if connected
  210. if (m_hDevice == NULL)
  211. {
  212. BS_ASSERT(false);
  213. // Reset internal offsets
  214. ResetOffsets();
  215. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  216. L"Channel not open");
  217. }
  218. // Reset output buffer
  219. m_dwOutputCurrentOffset = 0;
  220. m_dwOutputCurrentSize = 0;
  221. // Repeat while the output buffer can hold the data
  222. BOOL bResult = false;
  223. while(true)
  224. {
  225. // If no buffer and no sizes, try with the default size
  226. if ((dwProposedOutputBufferSize == 0) && (m_dwOutputAllocatedSize == 0))
  227. {
  228. BS_ASSERT(m_pbOutputBuffer == NULL);
  229. dwProposedOutputBufferSize = x_nMemInitialSize;
  230. }
  231. // Realloc the existing buffer if needed
  232. if (dwProposedOutputBufferSize > m_dwOutputAllocatedSize)
  233. {
  234. PBYTE pbOutputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbOutputBuffer, dwProposedOutputBufferSize));
  235. if (pbOutputBuffer == NULL)
  236. {
  237. // Reset internal offsets
  238. ResetOffsets();
  239. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  240. L"Could not extend the output buffer");
  241. }
  242. // Change the buffer
  243. m_pbOutputBuffer = pbOutputBuffer;
  244. m_dwOutputAllocatedSize = dwProposedOutputBufferSize;
  245. }
  246. ft.Trace( VSSDBG_IOCTL, L"IOCTL sent: %lx on device %s\n Input buffer size: %ld, Output buffer size: %ld",
  247. dwIoControlCode, m_pwszDevice.GetRef(), m_dwInputCurrentOffset, m_dwOutputAllocatedSize );
  248. ft.TraceBuffer( VSSDBG_IOCTL, m_dwInputCurrentOffset, m_pbInputBuffer );
  249. // Send the IOCTL.
  250. bResult = ::DeviceIoControl(m_hDevice,
  251. dwIoControlCode,
  252. m_pbInputBuffer,
  253. m_dwInputCurrentOffset,
  254. m_pbOutputBuffer,
  255. m_dwOutputAllocatedSize,
  256. &m_dwOutputCurrentSize,
  257. NULL
  258. );
  259. // If the error was "insufficient buffer" then try to reallocate a bigger one.
  260. // Otherwise end the iteration
  261. if (!bResult && ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) || (GetLastError() == ERROR_MORE_DATA)))
  262. dwProposedOutputBufferSize = x_nMemGrowthFactor*m_dwOutputAllocatedSize;
  263. else
  264. break;
  265. }
  266. DWORD dwLastErr = GetLastError();
  267. // Reset internal offsets
  268. ResetOffsets();
  269. // Treat the error case
  270. if (!bResult) {
  271. if (bThrowIfErrorOnCall) {
  272. switch(eLogging) {
  273. default:
  274. BS_ASSERT(false);
  275. case VSS_ICHANNEL_LOG_NONE:
  276. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  277. L"Could not send the IOCTL 0x%08lx on device %s - 0x%08lx. [0x%08lx]\n",
  278. dwIoControlCode, m_pwszDevice.GetRef(), m_hDevice, dwLastErr);
  279. break;
  280. case VSS_ICHANNEL_LOG_COORD:
  281. ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
  282. L"DeviceIoControl(%s - %p,0x%08lx,%p,%d,%p,%d,[%d])",
  283. m_pwszDevice.GetRef(), m_hDevice, dwIoControlCode,
  284. m_pbInputBuffer, m_dwInputCurrentOffset,
  285. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  286. break;
  287. case VSS_ICHANNEL_LOG_LOVELACE_HOLD:
  288. case VSS_ICHANNEL_LOG_LOVELACE_RELEASE:
  289. ft.TranslateInternalLovelaceError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
  290. ((eLogging == VSS_ICHANNEL_LOG_LOVELACE_HOLD) ? TRUE : FALSE),
  291. L"DeviceIoControl(%s - %p,0x%08lx,%p,%d,%p,%d,[%d])",
  292. m_pwszDevice.GetRef(), m_hDevice, dwIoControlCode,
  293. m_pbInputBuffer, m_dwInputCurrentOffset,
  294. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  295. break;
  296. case VSS_ICHANNEL_LOG_PROV_TIMEOUT:
  297. if (dwLastErr == ERROR_INVALID_PARAMETER)
  298. // This logging flag indicates that this actually means timeout
  299. dwLastErr = ERROR_TIMEOUT;
  300. // fall through...
  301. case VSS_ICHANNEL_LOG_PROV:
  302. ft.TranslateInternalProviderError(VSSDBG_SWPRV,
  303. HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
  304. L"DeviceIoControl(%s - %p,0x%08lx,%p,%d,%p,%d,[%d])",
  305. m_pwszDevice.GetRef(), m_hDevice, dwIoControlCode,
  306. m_pbInputBuffer, m_dwInputCurrentOffset,
  307. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  308. break;
  309. case VSS_ICHANNEL_LOG_PROV_NOTFOUND:
  310. {
  311. HRESULT hrThrow = VSS_E_PROVIDER_VETO;
  312. if (dwLastErr == ERROR_INVALID_PARAMETER)
  313. hrThrow = VSS_E_OBJECT_NOT_FOUND;
  314. ft.TranslateInternalProviderError(VSSDBG_SWPRV,
  315. HRESULT_FROM_WIN32(dwLastErr), hrThrow,
  316. L"DeviceIoControl(%s - %p,0x%08lx,%p,%d,%p,%d,[%d])",
  317. m_pwszDevice.GetRef(), m_hDevice, dwIoControlCode,
  318. m_pbInputBuffer, m_dwInputCurrentOffset,
  319. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  320. break;
  321. }
  322. }
  323. } else {
  324. ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx failed on device %s - 0x%08lx. Error code = 0x%08lx",
  325. dwIoControlCode, m_pwszDevice.GetRef(), m_hDevice, dwLastErr);
  326. ft.hr = HRESULT_FROM_WIN32(dwLastErr);
  327. }
  328. } else {
  329. ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx succeeded on device %s - 0x%08lx. \n Output buffer: size received = %ld",
  330. dwIoControlCode, m_pwszDevice.GetRef(), m_hDevice, m_dwOutputCurrentSize );
  331. ft.TraceBuffer( VSSDBG_IOCTL, m_dwOutputCurrentSize, m_pbOutputBuffer );
  332. ft.hr = S_OK; // Clear the previous HRESULT value
  333. }
  334. return ft.hr;
  335. }
  336. template <class T>
  337. PVOID PackArray(
  338. IN CVssFunctionTracer& ft,
  339. IN T* pSourceObject,
  340. IN DWORD dwNumObjects
  341. ) throw(HRESULT)
  342. /*
  343. Copy the contents of the given object(s) into the input buffer...
  344. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  345. */
  346. {
  347. BS_ASSERT(pSourceObject);
  348. BS_ASSERT(dwNumObjects);
  349. // Reset the error code
  350. ft.hr = S_OK;
  351. DWORD dwSize = sizeof(T) * dwNumObjects;
  352. if ( dwSize / dwNumObjects != sizeof(T) )
  353. {
  354. // Reset internal offsets
  355. ResetOffsets();
  356. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  357. L"Arithmetic overflow. dwNumObjects = %lu too large",
  358. dwNumObjects);
  359. }
  360. // Make room for the GUID. Here an E_OUTOFMEMORY exception can occur.
  361. ExpandInputBuffer( ft, dwSize );
  362. BS_ASSERT(m_pbInputBuffer);
  363. BS_ASSERT(m_dwInputCurrentOffset + dwSize <= m_dwInputAllocatedSize);
  364. // Copy the object contents... Check again for arithmetic overflow
  365. if (m_pbInputBuffer + m_dwInputCurrentOffset + dwSize < m_pbInputBuffer)
  366. {
  367. // Reset internal offsets
  368. ResetOffsets();
  369. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  370. L"Arithmetic overflow. m_dwInputCurrentOffset = %lu or dwSize = %lu too large.",
  371. m_dwInputCurrentOffset, dwSize);
  372. }
  373. BS_ASSERT(reinterpret_cast<T*>(reinterpret_cast<PBYTE>(pSourceObject)) == pSourceObject);
  374. ::CopyMemory( m_pbInputBuffer + m_dwInputCurrentOffset,
  375. reinterpret_cast<PBYTE>(pSourceObject),
  376. dwSize
  377. );
  378. // Remember the copied object
  379. T* pCopiedObject = reinterpret_cast<T*>(m_pbInputBuffer + m_dwInputCurrentOffset);
  380. // Increase the current offset
  381. m_dwInputCurrentOffset += dwSize;
  382. if (m_wAlignmentBytes)
  383. {
  384. // Round up the value in relation with the existing alignment
  385. WORD wAlignmentOffset = (WORD)( m_dwInputCurrentOffset % m_wAlignmentBytes );
  386. BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
  387. if (wAlignmentOffset != 0)
  388. m_dwInputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
  389. BS_ASSERT( m_dwInputCurrentOffset % m_wAlignmentBytes == 0 );
  390. }
  391. // Return the copied object
  392. return pCopiedObject;
  393. }
  394. template <class T>
  395. PVOID Pack(
  396. IN CVssFunctionTracer& ft,
  397. IN const T& SourceObject
  398. ) throw(HRESULT)
  399. /*
  400. Copy the contents of the given object(s) into the input buffer...
  401. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  402. */
  403. {
  404. return PackArray( ft, const_cast<T*>(&SourceObject), 1 );
  405. };
  406. PVOID PackSmallString(
  407. IN CVssFunctionTracer& ft,
  408. IN LPCWSTR wszText
  409. ) throw(HRESULT)
  410. /*
  411. Copy the contents of the given string into the input buffer.
  412. The storing format is
  413. +--------+
  414. | Len | USHORT Length, in bytes
  415. +--------+
  416. | String | WCHAR[] String characters, without terminating zero
  417. | ... |
  418. +--------+
  419. If the string pointer is NULL or the string is empty then a NULL string will be put.
  420. +--------+
  421. | 0 | USHORT
  422. +--------+
  423. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  424. The pointer to the string is returned.
  425. */
  426. {
  427. PVOID pwszReturned = NULL;
  428. // Reset the error code
  429. ft.hr = S_OK;
  430. if ( wszText != NULL)
  431. {
  432. size_t nLen = ::wcslen(wszText);
  433. USHORT usLen = (USHORT)(nLen);
  434. // Check buffer overflow
  435. if ((size_t)usLen != nLen)
  436. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY, L"Arithmetic overflow. %ld", nLen);
  437. // Pack the length and the string
  438. Pack( ft, (USHORT)(usLen * sizeof(WCHAR)) );
  439. if ( usLen != 0)
  440. pwszReturned = PackArray( ft, const_cast<LPWSTR>(wszText), usLen );
  441. }
  442. else
  443. Pack( ft, (USHORT)0 );
  444. return pwszReturned;
  445. };
  446. template <class T>
  447. void Unpack(
  448. IN CVssFunctionTracer& ft,
  449. IN T* pDestinationObject,
  450. IN DWORD dwNumObjects = 1,
  451. IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
  452. ) throw(HRESULT)
  453. /*
  454. Copy the contents of the given object(s) from the output buffer...
  455. */
  456. {
  457. BS_ASSERT((pDestinationObject != NULL) || !bTrueUnpack);
  458. BS_ASSERT(dwNumObjects);
  459. // Compute size, in bytes for the allocated objects. Check for Arithmetic overflow.
  460. DWORD dwSize = sizeof(T) * dwNumObjects;
  461. if ( dwSize / dwNumObjects != sizeof(T) )
  462. {
  463. // Reset internal offsets
  464. ResetOffsets();
  465. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  466. L"Arithmetic overflow. dwNumObjects = %lu too large",
  467. dwNumObjects);
  468. }
  469. // Check for buffer overflow.
  470. if ((m_pbOutputBuffer == NULL) ||
  471. (m_dwOutputCurrentOffset + dwSize > m_dwOutputCurrentSize))
  472. {
  473. // Reset internal offsets
  474. ResetOffsets();
  475. BS_ASSERT(false);
  476. // If we are inside of the software provider then we will always throw an VSS_E_PROVIDER_VETO exception
  477. // and we will log the proper entry.
  478. if (ft.IsInSoftwareProvider())
  479. ft.TranslateInternalProviderError(VSSDBG_IOCTL, E_UNEXPECTED, VSS_E_PROVIDER_VETO, L"IOCTL Unpack overflow");
  480. else
  481. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  482. L"Output buffer overflow. Reading bad arguments. dwSize = %lu",
  483. dwSize);
  484. }
  485. // Check again for arithmetic overflow and Copy the object contents...
  486. if (m_pbOutputBuffer + m_dwOutputCurrentOffset + dwSize < m_pbOutputBuffer)
  487. {
  488. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  489. L"Arithmetic overflow. m_dwOutputCurrentOffset = %lu or dwSize = %lu too large.",
  490. m_dwOutputCurrentOffset, dwSize);
  491. // Reset internal offsets
  492. ResetOffsets();
  493. }
  494. PBYTE pbBuffer = reinterpret_cast<PBYTE>((PVOID)(pDestinationObject));
  495. BS_ASSERT(reinterpret_cast<T*>(pbBuffer) == pDestinationObject);
  496. if (bTrueUnpack)
  497. ::CopyMemory( pbBuffer, m_pbOutputBuffer + m_dwOutputCurrentOffset, dwSize );
  498. // Increase the current offset
  499. m_dwOutputCurrentOffset += dwSize;
  500. if (m_wAlignmentBytes)
  501. {
  502. // Round up the value in relation with the existing alignment
  503. WORD wAlignmentOffset = (WORD)( m_dwOutputCurrentOffset % m_wAlignmentBytes );
  504. BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
  505. if (wAlignmentOffset != 0)
  506. m_dwOutputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
  507. BS_ASSERT( m_dwOutputCurrentOffset % m_wAlignmentBytes == 0 );
  508. }
  509. }
  510. LPCWSTR UnpackSmallString(
  511. IN CVssFunctionTracer& ft,
  512. IN OUT LPCWSTR& pwszText,
  513. IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
  514. ) throw(HRESULT)
  515. /*
  516. Allocate a string and copy the contents of the
  517. given string from the output buffer.
  518. Deallocate the previous string, if any.
  519. The storing format is
  520. +--------+
  521. | Len | USHORT Length, in bytes
  522. +--------+
  523. | String | WCHAR[] String characters, without terminating zero
  524. | ... |
  525. +--------+
  526. If the string is empty then a NULL string will be put.
  527. +--------+
  528. | 0 | USHORT
  529. +--------+
  530. If out of memory, the old output buffer is freed and an exception is thrown.
  531. The pointer to the allocated string is returned.
  532. */
  533. {
  534. // Reset the error code
  535. ft.hr = S_OK;
  536. // Free the previous string, if any
  537. if (bTrueUnpack)
  538. ::VssFreeString(pwszText);
  539. // Catch Unpack failures
  540. ft.hr = S_OK;
  541. try
  542. {
  543. // Get the string length
  544. USHORT usSize = 0;
  545. Unpack( ft, & usSize );
  546. // Convert from number of bytes into number of WCHARs
  547. USHORT usLen = (USHORT)(usSize/2);
  548. BS_ASSERT( usLen*2 == usSize );
  549. // Get the string
  550. if (usLen > 0)
  551. {
  552. // Get the string (length = usLen+1, including the zero)
  553. if (bTrueUnpack)
  554. pwszText = ::VssAllocString(ft, usLen);
  555. // Unpack ulLen characters.
  556. Unpack( ft, pwszText, usLen, bTrueUnpack );
  557. // Setup the zero character
  558. if (bTrueUnpack)
  559. const_cast<LPWSTR>(pwszText)[usLen] = L'\0';
  560. }
  561. }
  562. VSS_STANDARD_CATCH(ft)
  563. if (ft.HrFailed()) {
  564. if (bTrueUnpack)
  565. ::VssFreeString(pwszText);
  566. ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
  567. }
  568. return bTrueUnpack? pwszText: NULL;
  569. };
  570. LPCWSTR UnpackZeroString(
  571. IN CVssFunctionTracer& ft,
  572. IN OUT LPCWSTR& pwszText
  573. ) throw(HRESULT)
  574. /*
  575. Allocate a string and copy the contents of the
  576. given string from the output buffer.
  577. Deallocate the previous string, if any.
  578. The storing format is
  579. +--------+
  580. | String | WCHAR[] String characters, with terminating zero
  581. | ... |
  582. +--------+
  583. | 0 |
  584. +--------+
  585. If the string is empty then a NULL string will be put.
  586. +--------+
  587. | 0 | WCHAR
  588. +--------+
  589. If out of memory, the old output buffer is freed anyway and an exception is thrown.
  590. The pointer to the allocated string is returned.
  591. */
  592. {
  593. // Free the previous string, if any
  594. ::VssFreeString(pwszText);
  595. // Reset the error code
  596. ft.hr = S_OK;
  597. // Catch Unpack failures
  598. try
  599. {
  600. // Check for buffer validity.
  601. if (m_pbOutputBuffer == NULL)
  602. {
  603. // Reset internal offsets
  604. ResetOffsets();
  605. BS_ASSERT(false);
  606. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED, L"Reading from NULL buffer");
  607. }
  608. // Get the string in the buffer
  609. LPWSTR pwszTextInBuffer = reinterpret_cast<LPWSTR>(m_pbOutputBuffer + m_dwOutputCurrentOffset);
  610. BS_ASSERT( reinterpret_cast<PBYTE>(pwszTextInBuffer) == m_pbOutputBuffer + m_dwOutputCurrentOffset);
  611. DWORD dwStrLen = (DWORD)::wcslen(pwszTextInBuffer);
  612. if (dwStrLen > 0)
  613. {
  614. // Allocate the new string
  615. pwszText = ::VssAllocString(ft, dwStrLen);
  616. BS_ASSERT(pwszText);
  617. // Unpack ulLen+1 characters (including the zero).
  618. Unpack( ft, pwszText, dwStrLen+1 );
  619. BS_ASSERT(pwszText[dwStrLen] == L'\0');
  620. }
  621. else
  622. {
  623. // Unpack the zero character
  624. WCHAR wchTest;
  625. Unpack( ft, &wchTest);
  626. BS_ASSERT(wchTest == L'\0');
  627. }
  628. }
  629. VSS_STANDARD_CATCH(ft)
  630. if (ft.HrFailed()) {
  631. ::VssFreeString(pwszText);
  632. ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
  633. }
  634. return pwszText;
  635. };
  636. void PadWithZero(
  637. IN CVssFunctionTracer& ft,
  638. IN DWORD dwAlignToBytes = sizeof(INT64)
  639. )
  640. {
  641. INT nBytesRemaining = m_dwInputCurrentOffset % dwAlignToBytes;
  642. // Reset the error code
  643. ft.hr = S_OK;
  644. if (nBytesRemaining != 0)
  645. {
  646. INT nBytesToBePadded = dwAlignToBytes - nBytesRemaining;
  647. for (int i = 0; i < nBytesToBePadded; i++)
  648. Pack( ft, (BYTE)0 );
  649. }
  650. }
  651. void ResetOffsets()
  652. {
  653. m_dwInputCurrentOffset = 0;
  654. m_dwOutputCurrentOffset = 0;
  655. }
  656. DWORD GetCurrentInputOffset() { return m_dwInputCurrentOffset; };
  657. DWORD GetCurrentOutputOffset() { return m_dwOutputCurrentOffset; };
  658. LPCWSTR GetDeviceName() { return m_pwszDevice; };
  659. // Internal operations
  660. private:
  661. void ExpandInputBuffer(
  662. IN CVssFunctionTracer& ft,
  663. IN DWORD dwBytes
  664. ) throw(HRESULT)
  665. /*
  666. Expand the input buffer to accomodate adding another dwBytes.
  667. The m_dwInputCurrentOffset remains unchanged.
  668. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  669. */
  670. {
  671. // Reset the error code
  672. ft.hr = S_OK;
  673. // Check for arithmetic overflow
  674. if (dwBytes + m_dwInputCurrentOffset < m_dwInputCurrentOffset)
  675. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  676. L"Arithmetic overflow. dwBytes = %lu too large", dwBytes);
  677. // Check if the buffer needs to be extended
  678. if (dwBytes + m_dwInputCurrentOffset > m_dwInputAllocatedSize)
  679. {
  680. // Compute the new size of the buffer...
  681. DWORD dwNewSize = m_dwInputAllocatedSize;
  682. while(dwBytes + m_dwInputCurrentOffset > dwNewSize)
  683. {
  684. if (dwNewSize != 0)
  685. {
  686. // Check again for arithmetic overflow
  687. if (dwNewSize * x_nMemGrowthFactor <= dwNewSize)
  688. {
  689. // Reset internal offsets
  690. ResetOffsets();
  691. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  692. L"Arithmetic overflow. dwNewSize = %lu too large", dwNewSize);
  693. }
  694. // Increase the allocated size.
  695. dwNewSize = dwNewSize * x_nMemGrowthFactor;
  696. }
  697. else
  698. dwNewSize = x_nMemInitialSize;
  699. }
  700. // Reallocate the buffer. If realloc fails, the old buffer is still kept.
  701. PBYTE pbNewInputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbInputBuffer, dwNewSize));
  702. if (pbNewInputBuffer == NULL)
  703. {
  704. // Reset internal offsets
  705. ResetOffsets();
  706. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  707. L"Could not extend the input buffer");
  708. }
  709. // Change the buffer
  710. m_pbInputBuffer = pbNewInputBuffer;
  711. m_dwInputAllocatedSize = dwNewSize;
  712. }
  713. }
  714. // Internal data members.
  715. private:
  716. // I/O Device-related members
  717. HANDLE m_hDevice;
  718. // Input buffer
  719. DWORD m_dwInputCurrentOffset;
  720. DWORD m_dwInputAllocatedSize;
  721. PBYTE m_pbInputBuffer;
  722. // Output buffer
  723. DWORD m_dwOutputCurrentOffset;
  724. DWORD m_dwOutputAllocatedSize;
  725. DWORD m_dwOutputCurrentSize;
  726. PBYTE m_pbOutputBuffer;
  727. WORD m_wAlignmentBytes;
  728. CVssAutoPWSZ m_pwszDevice;
  729. };
  730. #endif // __VSS_ICHANNEL_HXX__