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.

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