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.

938 lines
30 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 (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. #ifdef IN_VSS
  152. case VSS_ICHANNEL_LOG_COORD:
  153. ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
  154. L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
  155. pwszObjectNameInternal, dwAccessType, dwShareMode);
  156. break;
  157. case VSS_ICHANNEL_LOG_PROV:
  158. ft.TranslateInternalProviderError(VSSDBG_SWPRV,
  159. HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
  160. L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
  161. pwszObjectNameInternal, dwAccessType, dwShareMode);
  162. break;
  163. #endif
  164. }
  165. }
  166. else
  167. {
  168. ft.Trace( VSSDBG_IOCTL,
  169. L"Could not open the object \'%s\'. [0x%08lx]\n",
  170. pwszObjectNameInternal, dwLastErr);
  171. //ft.hr = ((dwLastErr == ERROR_FILE_NOT_FOUND) ||
  172. // (dwLastErr == ERROR_NOT_READY) ||
  173. // (dwLastErr == ERROR_DEVICE_NOT_CONNECTED))?
  174. // (VSS_E_OBJECT_NOT_FOUND): E_UNEXPECTED;
  175. ft.hr = HRESULT_FROM_WIN32(dwLastErr);
  176. }
  177. }
  178. else
  179. {
  180. ft.Trace( VSSDBG_IOCTL,
  181. L"The object \'%s\'. was opened under the device handle 0x%08lx",
  182. pwszObjectNameInternal, m_hDevice);
  183. }
  184. return ft.hr;
  185. }
  186. void Close()
  187. {
  188. // Close previous handle, if needed.
  189. if (m_hDevice)
  190. ::CloseHandle(m_hDevice);
  191. m_hDevice= NULL;
  192. // Reset internal offsets
  193. ResetOffsets();
  194. m_dwOutputCurrentSize = 0;
  195. }
  196. HRESULT Call(
  197. IN CVssFunctionTracer& ft,
  198. IN DWORD dwIoControlCode,
  199. IN bool bThrowIfErrorOnCall = true,
  200. IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE
  201. ) throw(HRESULT)
  202. /*
  203. Send the IOCTL to that device.
  204. Before that reserve dwProposedOutputBufferSize bytes for the output buffer.
  205. */
  206. {
  207. DWORD dwProposedOutputBufferSize = 0;
  208. // Reset the error code
  209. ft.hr = S_OK;
  210. BS_ASSERT(bThrowIfErrorOnCall || (eLogging == VSS_ICHANNEL_LOG_NONE));
  211. // Check if connected
  212. if (m_hDevice == NULL)
  213. {
  214. BS_ASSERT(false);
  215. // Reset internal offsets
  216. ResetOffsets();
  217. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  218. L"Channel not open");
  219. }
  220. // Reset output buffer
  221. m_dwOutputCurrentOffset = 0;
  222. m_dwOutputCurrentSize = 0;
  223. // Repeat while the output buffer can hold the data
  224. BOOL bResult = false;
  225. while(true)
  226. {
  227. // If no buffer and no sizes, try with the default size
  228. if ((dwProposedOutputBufferSize == 0) && (m_dwOutputAllocatedSize == 0))
  229. {
  230. BS_ASSERT(m_pbOutputBuffer == NULL);
  231. dwProposedOutputBufferSize = x_nMemInitialSize;
  232. }
  233. // Realloc the existing buffer if needed
  234. if (dwProposedOutputBufferSize > m_dwOutputAllocatedSize)
  235. {
  236. PBYTE pbOutputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbOutputBuffer, dwProposedOutputBufferSize));
  237. if (pbOutputBuffer == NULL)
  238. {
  239. // Reset internal offsets
  240. ResetOffsets();
  241. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  242. L"Could not extend the output buffer");
  243. }
  244. // Change the buffer
  245. m_pbOutputBuffer = pbOutputBuffer;
  246. m_dwOutputAllocatedSize = dwProposedOutputBufferSize;
  247. }
  248. ft.Trace( VSSDBG_IOCTL, L"IOCTL sent: %lx\n Input buffer size: %ld, Output buffer size: %ld",
  249. dwIoControlCode, m_dwInputCurrentOffset, m_dwOutputAllocatedSize );
  250. ft.TraceBuffer( VSSDBG_IOCTL, m_dwInputCurrentOffset, m_pbInputBuffer );
  251. // Send the IOCTL.
  252. bResult = ::DeviceIoControl(m_hDevice,
  253. dwIoControlCode,
  254. m_pbInputBuffer,
  255. m_dwInputCurrentOffset,
  256. m_pbOutputBuffer,
  257. m_dwOutputAllocatedSize,
  258. &m_dwOutputCurrentSize,
  259. NULL
  260. );
  261. // If the error was "insufficient buffer" then try to reallocate a bigger one.
  262. // Otherwise end the iteration
  263. if (!bResult && ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) || (GetLastError() == ERROR_MORE_DATA)))
  264. dwProposedOutputBufferSize = x_nMemGrowthFactor*m_dwOutputAllocatedSize;
  265. else
  266. break;
  267. }
  268. DWORD dwLastErr = GetLastError();
  269. // Reset internal offsets
  270. ResetOffsets();
  271. // Treat the error case
  272. if (!bResult) {
  273. if (bThrowIfErrorOnCall) {
  274. switch(eLogging) {
  275. default:
  276. BS_ASSERT(false);
  277. case VSS_ICHANNEL_LOG_NONE:
  278. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  279. L"Could not send the IOCTL 0x%08lx on device 0x%08lx. [0x%08lx]\n",
  280. dwIoControlCode, m_hDevice, dwLastErr);
  281. break;
  282. case VSS_ICHANNEL_LOG_COORD:
  283. ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
  284. L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
  285. m_hDevice, dwIoControlCode,
  286. m_pbInputBuffer, m_dwInputCurrentOffset,
  287. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  288. break;
  289. #ifdef IN_VSS
  290. case VSS_ICHANNEL_LOG_LOVELACE_HOLD:
  291. case VSS_ICHANNEL_LOG_LOVELACE_RELEASE:
  292. ft.TranslateInternalLovelaceError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
  293. ((eLogging == VSS_ICHANNEL_LOG_LOVELACE_HOLD) ? TRUE : FALSE),
  294. L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
  295. m_hDevice, dwIoControlCode,
  296. m_pbInputBuffer, m_dwInputCurrentOffset,
  297. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  298. break;
  299. case VSS_ICHANNEL_LOG_PROV_TIMEOUT:
  300. if (dwLastErr == ERROR_INVALID_PARAMETER)
  301. // This logging flag indicates that this actually means timeout
  302. dwLastErr = ERROR_TIMEOUT;
  303. // fall through...
  304. case VSS_ICHANNEL_LOG_PROV:
  305. ft.TranslateInternalProviderError(VSSDBG_SWPRV,
  306. HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
  307. L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
  308. m_hDevice, dwIoControlCode,
  309. m_pbInputBuffer, m_dwInputCurrentOffset,
  310. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  311. break;
  312. case VSS_ICHANNEL_LOG_PROV_NOTFOUND:
  313. {
  314. HRESULT hrThrow = VSS_E_PROVIDER_VETO;
  315. if (dwLastErr == ERROR_INVALID_PARAMETER)
  316. hrThrow = VSS_E_OBJECT_NOT_FOUND;
  317. ft.TranslateInternalProviderError(VSSDBG_SWPRV,
  318. HRESULT_FROM_WIN32(dwLastErr), hrThrow,
  319. L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
  320. m_hDevice, dwIoControlCode,
  321. m_pbInputBuffer, m_dwInputCurrentOffset,
  322. m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
  323. break;
  324. }
  325. #endif
  326. }
  327. } else {
  328. ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx failed on device 0x%08lx. Error code = 0x%08lx",
  329. dwIoControlCode, m_hDevice, dwLastErr);
  330. ft.hr = HRESULT_FROM_WIN32(dwLastErr);
  331. }
  332. } else {
  333. ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx succeeded on device 0x%08lx. \n Output buffer: size received = %ld",
  334. dwIoControlCode, m_hDevice, m_dwOutputCurrentSize );
  335. ft.TraceBuffer( VSSDBG_IOCTL, m_dwOutputCurrentSize, m_pbOutputBuffer );
  336. ft.hr = S_OK; // Clear the previous HRESULT value
  337. }
  338. return ft.hr;
  339. }
  340. template <class T>
  341. PVOID PackArray(
  342. IN CVssFunctionTracer& ft,
  343. IN T* pSourceObject,
  344. IN DWORD dwNumObjects
  345. ) throw(HRESULT)
  346. /*
  347. Copy the contents of the given object(s) into the input buffer...
  348. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  349. */
  350. {
  351. BS_ASSERT(pSourceObject);
  352. BS_ASSERT(dwNumObjects);
  353. // Reset the error code
  354. ft.hr = S_OK;
  355. DWORD dwSize = sizeof(T) * dwNumObjects;
  356. if ( dwSize / dwNumObjects != sizeof(T) )
  357. {
  358. // Reset internal offsets
  359. ResetOffsets();
  360. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  361. L"Arithmetic overflow. dwNumObjects = %lu too large",
  362. dwNumObjects);
  363. }
  364. // Make room for the GUID. Here an E_OUTOFMEMORY exception can occur.
  365. ExpandInputBuffer( ft, dwSize );
  366. BS_ASSERT(m_pbInputBuffer);
  367. BS_ASSERT(m_dwInputCurrentOffset + dwSize <= m_dwInputAllocatedSize);
  368. // Copy the object contents... Check again for arithmetic overflow
  369. if (m_pbInputBuffer + m_dwInputCurrentOffset + dwSize < m_pbInputBuffer)
  370. {
  371. // Reset internal offsets
  372. ResetOffsets();
  373. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  374. L"Arithmetic overflow. m_dwInputCurrentOffset = %lu or dwSize = %lu too large.",
  375. m_dwInputCurrentOffset, dwSize);
  376. }
  377. BS_ASSERT(reinterpret_cast<T*>(reinterpret_cast<PBYTE>(pSourceObject)) == pSourceObject);
  378. ::CopyMemory( m_pbInputBuffer + m_dwInputCurrentOffset,
  379. reinterpret_cast<PBYTE>(pSourceObject),
  380. dwSize
  381. );
  382. // Remember the copied object
  383. T* pCopiedObject = reinterpret_cast<T*>(m_pbInputBuffer + m_dwInputCurrentOffset);
  384. // Increase the current offset
  385. m_dwInputCurrentOffset += dwSize;
  386. if (m_wAlignmentBytes)
  387. {
  388. // Round up the value in relation with the existing alignment
  389. WORD wAlignmentOffset = (WORD)( m_dwInputCurrentOffset % m_wAlignmentBytes );
  390. BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
  391. if (wAlignmentOffset != 0)
  392. m_dwInputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
  393. BS_ASSERT( m_dwInputCurrentOffset % m_wAlignmentBytes == 0 );
  394. }
  395. // Return the copied object
  396. return pCopiedObject;
  397. }
  398. template <class T>
  399. PVOID Pack(
  400. IN CVssFunctionTracer& ft,
  401. IN const T& SourceObject
  402. ) throw(HRESULT)
  403. /*
  404. Copy the contents of the given object(s) into the input buffer...
  405. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  406. */
  407. {
  408. return PackArray( ft, const_cast<T*>(&SourceObject), 1 );
  409. };
  410. PVOID PackSmallString(
  411. IN CVssFunctionTracer& ft,
  412. IN LPCWSTR wszText
  413. ) throw(HRESULT)
  414. /*
  415. Copy the contents of the given string into the input buffer.
  416. The storing format is
  417. +--------+
  418. | Len | USHORT Length, in bytes
  419. +--------+
  420. | String | WCHAR[] String characters, without terminating zero
  421. | ... |
  422. +--------+
  423. If the string pointer is NULL or the string is empty then a NULL string will be put.
  424. +--------+
  425. | 0 | USHORT
  426. +--------+
  427. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  428. The pointer to the string is returned.
  429. */
  430. {
  431. PVOID pwszReturned = NULL;
  432. // Reset the error code
  433. ft.hr = S_OK;
  434. if ( wszText != NULL)
  435. {
  436. size_t nLen = ::wcslen(wszText);
  437. USHORT usLen = (USHORT)(nLen);
  438. // Check buffer overflow
  439. if ((size_t)usLen != nLen)
  440. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY, L"Arithmetic overflow. %ld", nLen);
  441. // Pack the length and the string
  442. Pack( ft, (USHORT)(usLen * sizeof(WCHAR)) );
  443. if ( usLen != 0)
  444. pwszReturned = PackArray( ft, const_cast<LPWSTR>(wszText), usLen );
  445. }
  446. else
  447. Pack( ft, (USHORT)0 );
  448. return pwszReturned;
  449. };
  450. template <class T>
  451. void Unpack(
  452. IN CVssFunctionTracer& ft,
  453. IN T* pDestinationObject,
  454. IN DWORD dwNumObjects = 1,
  455. IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
  456. ) throw(HRESULT)
  457. /*
  458. Copy the contents of the given object(s) from the output buffer...
  459. */
  460. {
  461. BS_ASSERT((pDestinationObject != NULL) || !bTrueUnpack);
  462. BS_ASSERT(dwNumObjects);
  463. // Compute size, in bytes for the allocated objects. Check for Arithmetic overflow.
  464. DWORD dwSize = sizeof(T) * dwNumObjects;
  465. if ( dwSize / dwNumObjects != sizeof(T) )
  466. {
  467. // Reset internal offsets
  468. ResetOffsets();
  469. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  470. L"Arithmetic overflow. dwNumObjects = %lu too large",
  471. dwNumObjects);
  472. }
  473. // Check for buffer overflow.
  474. if ((m_pbOutputBuffer == NULL) ||
  475. (m_dwOutputCurrentOffset + dwSize > m_dwOutputCurrentSize))
  476. {
  477. // Reset internal offsets
  478. ResetOffsets();
  479. BS_ASSERT(false);
  480. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
  481. L"Output buffer overflow. Reading bad arguments. dwSize = %lu",
  482. dwSize);
  483. }
  484. // Check again for arithmetic overflow and Copy the object contents...
  485. if (m_pbOutputBuffer + m_dwOutputCurrentOffset + dwSize < m_pbOutputBuffer)
  486. {
  487. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  488. L"Arithmetic overflow. m_dwOutputCurrentOffset = %lu or dwSize = %lu too large.",
  489. m_dwOutputCurrentOffset, dwSize);
  490. // Reset internal offsets
  491. ResetOffsets();
  492. }
  493. PBYTE pbBuffer = reinterpret_cast<PBYTE>((PVOID)(pDestinationObject));
  494. BS_ASSERT(reinterpret_cast<T*>(pbBuffer) == pDestinationObject);
  495. if (bTrueUnpack)
  496. ::CopyMemory( pbBuffer, m_pbOutputBuffer + m_dwOutputCurrentOffset, dwSize );
  497. // Increase the current offset
  498. m_dwOutputCurrentOffset += dwSize;
  499. if (m_wAlignmentBytes)
  500. {
  501. // Round up the value in relation with the existing alignment
  502. WORD wAlignmentOffset = (WORD)( m_dwOutputCurrentOffset % m_wAlignmentBytes );
  503. BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
  504. if (wAlignmentOffset != 0)
  505. m_dwOutputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
  506. BS_ASSERT( m_dwOutputCurrentOffset % m_wAlignmentBytes == 0 );
  507. }
  508. }
  509. LPCWSTR UnpackSmallString(
  510. IN CVssFunctionTracer& ft,
  511. IN OUT LPCWSTR& pwszText,
  512. IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
  513. ) throw(HRESULT)
  514. /*
  515. Allocate a string and copy the contents of the
  516. given string from the output buffer.
  517. Deallocate the previous string, if any.
  518. The storing format is
  519. +--------+
  520. | Len | USHORT Length, in bytes
  521. +--------+
  522. | String | WCHAR[] String characters, without terminating zero
  523. | ... |
  524. +--------+
  525. If the string is empty then a NULL string will be put.
  526. +--------+
  527. | 0 | USHORT
  528. +--------+
  529. If out of memory, the old output buffer is freed and an exception is thrown.
  530. The pointer to the allocated string is returned.
  531. */
  532. {
  533. // Reset the error code
  534. ft.hr = S_OK;
  535. // Free the previous string, if any
  536. if (bTrueUnpack)
  537. ::VssFreeString(pwszText);
  538. // Catch Unpack failures
  539. ft.hr = S_OK;
  540. try
  541. {
  542. // Get the string length
  543. USHORT usSize = 0;
  544. Unpack( ft, & usSize );
  545. // Convert from number of bytes into number of WCHARs
  546. USHORT usLen = (USHORT)(usSize/2);
  547. BS_ASSERT( usLen*2 == usSize );
  548. // Get the string
  549. if (usLen > 0)
  550. {
  551. // Get the string (length = usLen+1, including the zero)
  552. if (bTrueUnpack)
  553. pwszText = ::VssAllocString(ft, usLen);
  554. // Unpack ulLen characters.
  555. Unpack( ft, pwszText, usLen, bTrueUnpack );
  556. // Setup the zero character
  557. if (bTrueUnpack)
  558. const_cast<LPWSTR>(pwszText)[usLen] = L'\0';
  559. }
  560. }
  561. VSS_STANDARD_CATCH(ft)
  562. if (ft.HrFailed()) {
  563. if (bTrueUnpack)
  564. ::VssFreeString(pwszText);
  565. ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
  566. }
  567. return bTrueUnpack? pwszText: NULL;
  568. };
  569. LPCWSTR UnpackZeroString(
  570. IN CVssFunctionTracer& ft,
  571. IN OUT LPCWSTR& pwszText
  572. ) throw(HRESULT)
  573. /*
  574. Allocate a string and copy the contents of the
  575. given string from the output buffer.
  576. Deallocate the previous string, if any.
  577. The storing format is
  578. +--------+
  579. | String | WCHAR[] String characters, with terminating zero
  580. | ... |
  581. +--------+
  582. | 0 |
  583. +--------+
  584. If the string is empty then a NULL string will be put.
  585. +--------+
  586. | 0 | WCHAR
  587. +--------+
  588. If out of memory, the old output buffer is freed anyway and an exception is thrown.
  589. The pointer to the allocated string is returned.
  590. */
  591. {
  592. // Free the previous string, if any
  593. ::VssFreeString(pwszText);
  594. // Reset the error code
  595. ft.hr = S_OK;
  596. // Catch Unpack failures
  597. try
  598. {
  599. // Check for buffer validity.
  600. if (m_pbOutputBuffer == NULL)
  601. {
  602. // Reset internal offsets
  603. ResetOffsets();
  604. BS_ASSERT(false);
  605. ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED, L"Reading from NULL buffer");
  606. }
  607. // Get the string in the buffer
  608. LPWSTR pwszTextInBuffer = reinterpret_cast<LPWSTR>(m_pbOutputBuffer + m_dwOutputCurrentOffset);
  609. BS_ASSERT( reinterpret_cast<PBYTE>(pwszTextInBuffer) == m_pbOutputBuffer + m_dwOutputCurrentOffset);
  610. DWORD dwStrLen = (DWORD)::wcslen(pwszTextInBuffer);
  611. if (dwStrLen > 0)
  612. {
  613. // Allocate the new string
  614. pwszText = ::VssAllocString(ft, dwStrLen);
  615. BS_ASSERT(pwszText);
  616. // Unpack ulLen+1 characters (including the zero).
  617. Unpack( ft, pwszText, dwStrLen+1 );
  618. BS_ASSERT(pwszText[dwStrLen] == L'\0');
  619. }
  620. else
  621. {
  622. // Unpack the zero character
  623. WCHAR wchTest;
  624. Unpack( ft, &wchTest);
  625. BS_ASSERT(wchTest == L'\0');
  626. }
  627. }
  628. VSS_STANDARD_CATCH(ft)
  629. if (ft.HrFailed()) {
  630. ::VssFreeString(pwszText);
  631. ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
  632. }
  633. return pwszText;
  634. };
  635. void PadWithZero(
  636. IN CVssFunctionTracer& ft,
  637. IN DWORD dwAlignToBytes = sizeof(INT64)
  638. )
  639. {
  640. INT nBytesRemaining = m_dwInputCurrentOffset % dwAlignToBytes;
  641. // Reset the error code
  642. ft.hr = S_OK;
  643. if (nBytesRemaining != 0)
  644. {
  645. INT nBytesToBePadded = dwAlignToBytes - nBytesRemaining;
  646. for (int i = 0; i < nBytesToBePadded; i++)
  647. Pack( ft, (BYTE)0 );
  648. }
  649. }
  650. void ResetOffsets()
  651. {
  652. m_dwInputCurrentOffset = 0;
  653. m_dwOutputCurrentOffset = 0;
  654. }
  655. DWORD GetCurrentInputOffset() { return m_dwInputCurrentOffset; };
  656. DWORD GetCurrentOutputOffset() { return m_dwOutputCurrentOffset; };
  657. // Internal operations
  658. private:
  659. void ExpandInputBuffer(
  660. IN CVssFunctionTracer& ft,
  661. IN DWORD dwBytes
  662. ) throw(HRESULT)
  663. /*
  664. Expand the input buffer to accomodate adding another dwBytes.
  665. The m_dwInputCurrentOffset remains unchanged.
  666. If out of memory, the old input buffer remains unchanged and an exception is thrown.
  667. */
  668. {
  669. // Reset the error code
  670. ft.hr = S_OK;
  671. // Check for arithmetic overflow
  672. if (dwBytes + m_dwInputCurrentOffset < m_dwInputCurrentOffset)
  673. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  674. L"Arithmetic overflow. dwBytes = %lu too large", dwBytes);
  675. // Check if the buffer needs to be extended
  676. if (dwBytes + m_dwInputCurrentOffset > m_dwInputAllocatedSize)
  677. {
  678. // Compute the new size of the buffer...
  679. DWORD dwNewSize = m_dwInputAllocatedSize;
  680. while(dwBytes + m_dwInputCurrentOffset > dwNewSize)
  681. {
  682. if (dwNewSize != 0)
  683. {
  684. // Check again for arithmetic overflow
  685. if (dwNewSize * x_nMemGrowthFactor <= dwNewSize)
  686. {
  687. // Reset internal offsets
  688. ResetOffsets();
  689. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  690. L"Arithmetic overflow. dwNewSize = %lu too large", dwNewSize);
  691. }
  692. // Increase the allocated size.
  693. dwNewSize = dwNewSize * x_nMemGrowthFactor;
  694. }
  695. else
  696. dwNewSize = x_nMemInitialSize;
  697. }
  698. // Reallocate the buffer. If realloc fails, the old buffer is still kept.
  699. PBYTE pbNewInputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbInputBuffer, dwNewSize));
  700. if (pbNewInputBuffer == NULL)
  701. {
  702. // Reset internal offsets
  703. ResetOffsets();
  704. ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
  705. L"Could not extend the input buffer");
  706. }
  707. // Change the buffer
  708. m_pbInputBuffer = pbNewInputBuffer;
  709. m_dwInputAllocatedSize = dwNewSize;
  710. }
  711. }
  712. // Internal data members.
  713. private:
  714. // I/O Device-related members
  715. HANDLE m_hDevice;
  716. // Input buffer
  717. DWORD m_dwInputCurrentOffset;
  718. DWORD m_dwInputAllocatedSize;
  719. PBYTE m_pbInputBuffer;
  720. // Output buffer
  721. DWORD m_dwOutputCurrentOffset;
  722. DWORD m_dwOutputAllocatedSize;
  723. DWORD m_dwOutputCurrentSize;
  724. PBYTE m_pbOutputBuffer;
  725. WORD m_wAlignmentBytes;
  726. };
  727. #endif // __VSS_ICHANNEL_HXX__