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.

1224 lines
35 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Abstract:
  4. @doc
  5. @module utility.cpp | Implementation of the Volume Snapshots admin utility
  6. @end
  7. Author:
  8. Adi Oltean [aoltean] 09/17/1999
  9. Stefan Steiner [ssteiner] 03/27/2001
  10. TBD:
  11. Add comments.
  12. Revision History:
  13. Name Date Comments
  14. ssteiner 03/27/2001 Created
  15. --*/
  16. /////////////////////////////////////////////////////////////////////////////
  17. // Includes
  18. // The rest of includes are specified here
  19. #include "vssadmin.h"
  20. #include <float.h>
  21. #define VSS_LINE_BREAK_COLUMN (79)
  22. ////////////////////////////////////////////////////////////////////////
  23. // Standard foo for file name aliasing. This code block must be after
  24. // all includes of VSS header files.
  25. //
  26. #ifdef VSS_FILE_ALIAS
  27. #undef VSS_FILE_ALIAS
  28. #endif
  29. #define VSS_FILE_ALIAS "ADMUTILC"
  30. LPCWSTR CVssAdminCLI::GetVolumeDisplayName(
  31. IN LPCWSTR pwszVolumeName
  32. )
  33. {
  34. //
  35. // Note that if something fails in this function, the program can continue
  36. // to run. It's just that certain output will not show the volume display
  37. // name along with the volume name
  38. //
  39. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::GetVolumeDisplayName" );
  40. if ( m_pMapVolumeNames == NULL )
  41. {
  42. // Create a Coordinator interface
  43. CComPtr<IVssSnapshotMgmt> pIMgmt;
  44. ft.CoCreateInstanceWithLog(
  45. VSSDBG_VSSADMIN,
  46. CLSID_VssSnapshotMgmt,
  47. L"VssSnapshotMgmt",
  48. CLSCTX_ALL,
  49. IID_IVssSnapshotMgmt,
  50. (IUnknown**)&(pIMgmt));
  51. if ( ft.HrFailed() )
  52. ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Connection failed with hr = 0x%08lx", ft.hr);
  53. //
  54. // Get the list of all volumes
  55. //
  56. CComPtr<IVssEnumMgmtObject> pIEnumMgmt;
  57. ft.hr = pIMgmt->QueryVolumesSupportedForSnapshots(
  58. VSS_SWPRV_ProviderId,
  59. VSS_CTX_ALL,
  60. &pIEnumMgmt );
  61. if ( ft.HrFailed() )
  62. ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"QueryVolumesSupportedForSnapshots failed, hr = 0x%08lx", ft.hr);
  63. if ( ft.hr == S_FALSE )
  64. // empty query
  65. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_NO_ITEMS_IN_QUERY,
  66. L"CVssAdminCLI::ListVolumes: No volumes found that satisfy the query" );
  67. //
  68. // Query each volume to see if diff areas exist.
  69. //
  70. VSS_MGMT_OBJECT_PROP Prop;
  71. VSS_VOLUME_PROP& VolProp = Prop.Obj.Vol;
  72. m_pMapVolumeNames = new CVssSimpleMap<LPCWSTR, LPCWSTR>;
  73. if ( m_pMapVolumeNames == NULL )
  74. ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error");
  75. for(;;)
  76. {
  77. // Get next element
  78. ULONG ulFetched;
  79. ft.hr = pIEnumMgmt->Next( 1, &Prop, &ulFetched );
  80. if ( ft.HrFailed() )
  81. ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Next failed with hr = 0x%08lx", ft.hr);
  82. // Test if the cycle is finished
  83. if (ft.hr == S_FALSE) {
  84. BS_ASSERT( ulFetched == 0);
  85. break;
  86. }
  87. // If it is a simple drive letter it comes back like V:\, change to V:
  88. if ( ::wcslen( VolProp.m_pwszVolumeDisplayName ) == 3 && VolProp.m_pwszVolumeDisplayName[2] == L'\\' )
  89. VolProp.m_pwszVolumeDisplayName[2] = L'\0';
  90. // Add it to the map. Note that the strings in the volume property struct are transferred to the
  91. // map.
  92. if ( !m_pMapVolumeNames->Add( VolProp.m_pwszVolumeName, VolProp.m_pwszVolumeDisplayName ) )
  93. {
  94. ::VssFreeString( VolProp.m_pwszVolumeName );
  95. ::VssFreeString( VolProp.m_pwszVolumeDisplayName );
  96. ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error");
  97. }
  98. }
  99. }
  100. // Returns NULL if the volume name is not found
  101. return m_pMapVolumeNames->Lookup(pwszVolumeName);
  102. }
  103. LPCWSTR CVssAdminCLI::LoadString(
  104. IN UINT uStringId
  105. )
  106. {
  107. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::LoadString" );
  108. LPCWSTR wszReturnedString = m_mapCachedResourceStrings.Lookup(uStringId);
  109. if (wszReturnedString)
  110. return wszReturnedString;
  111. // Load the string from resources.
  112. WCHAR wszBuffer[x_nStringBufferSize];
  113. INT nReturnedCharacters = ::LoadStringW(
  114. GetModuleHandle(NULL),
  115. uStringId,
  116. wszBuffer,
  117. x_nStringBufferSize - 1
  118. );
  119. if (nReturnedCharacters == 0)
  120. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
  121. L"Error on loading the string %u. 0x%08lx",
  122. uStringId, ::GetLastError() );
  123. // Duplicate the new string
  124. LPWSTR wszNewString = NULL;
  125. ::VssSafeDuplicateStr( ft, wszNewString, wszBuffer );
  126. wszReturnedString = wszNewString;
  127. // Save the string in the cache
  128. if ( !m_mapCachedResourceStrings.Add( uStringId, wszReturnedString ) ) {
  129. ::VssFreeString( wszReturnedString );
  130. ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error");
  131. }
  132. return wszReturnedString;
  133. }
  134. LPCWSTR CVssAdminCLI::GetNextCmdlineToken(
  135. IN bool bFirstToken /* = false */
  136. ) throw(HRESULT)
  137. /*++
  138. Description:
  139. This function returns the tokens in the command line.
  140. The function will skip any separators (space and tab).
  141. If bFirstCall == true then it will return the first token.
  142. Otherwise subsequent calls will return subsequent tokens.
  143. If the last token is NULL then there are no more tokens in the command line.
  144. --*/
  145. {
  146. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::GetNextCmdlineToken" );
  147. static INT iCurrArgc;
  148. WCHAR *pwsz;
  149. if ( bFirstToken )
  150. iCurrArgc = 0;
  151. if ( iCurrArgc >= m_argc )
  152. return NULL;
  153. pwsz = m_argv[iCurrArgc++];
  154. return pwsz;
  155. }
  156. bool CVssAdminCLI::Match(
  157. IN LPCWSTR wszString,
  158. IN LPCWSTR wszPatternString
  159. ) throw(HRESULT)
  160. /*++
  161. Description:
  162. This function returns true iif the given string matches the
  163. pattern string. The comparison is case insensitive.
  164. --*/
  165. {
  166. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::Match" );
  167. // If the string is NULL then the Match failed.
  168. if (wszString == NULL) return false;
  169. // Check for string equality (case insensitive)
  170. return (::_wcsicmp( wszString, wszPatternString ) == 0);
  171. }
  172. bool CVssAdminCLI::ScanGuid(
  173. IN LPCWSTR wszString,
  174. OUT VSS_ID& Guid
  175. ) throw(HRESULT)
  176. /*++
  177. Description:
  178. This function returns true iif the given string matches a guid.
  179. The guid is returned in the proper variable.
  180. The formatting is case insensitive.
  181. --*/
  182. {
  183. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::ScanGuid" );
  184. return SUCCEEDED(::CLSIDFromString(W2OLE(const_cast<WCHAR*>(wszString)), &Guid));
  185. }
  186. void CVssAdminCLI::Output(
  187. IN LPCWSTR wszFormat,
  188. ...
  189. ) throw(HRESULT)
  190. /*++
  191. Description:
  192. This function returns true if the given string matches the
  193. pattern strig from resources. The comparison is case insensitive.
  194. --*/
  195. {
  196. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::Output" );
  197. WCHAR wszOutputBuffer[x_nStringBufferSize];
  198. // Format the final string
  199. va_list marker;
  200. va_start( marker, wszFormat );
  201. StringCchVPrintfW( STRING_CCH_PARAM(wszOutputBuffer), wszFormat, marker );
  202. va_end( marker );
  203. // Print the final string to the output
  204. OutputOnConsole( wszOutputBuffer );
  205. }
  206. void CVssAdminCLI::OutputMsg(
  207. IN LONG msgId,
  208. ...
  209. )
  210. /*++
  211. Description:
  212. This function outputs a msg.mc message.
  213. --*/
  214. {
  215. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::OutputMsg" );
  216. va_list args;
  217. LPWSTR lpMsgBuf;
  218. va_start( args, msgId );
  219. if (::FormatMessage(
  220. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
  221. NULL,
  222. msgId,
  223. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  224. (LPWSTR) &lpMsgBuf,
  225. 0,
  226. &args
  227. ))
  228. {
  229. OutputOnConsole( lpMsgBuf );
  230. ::LocalFree( lpMsgBuf );
  231. }
  232. else
  233. {
  234. if (::FormatMessage(
  235. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  236. NULL,
  237. msgId,
  238. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  239. (LPWSTR) &lpMsgBuf,
  240. 0,
  241. &args))
  242. {
  243. OutputOnConsole( lpMsgBuf );
  244. ::LocalFree( lpMsgBuf );
  245. }
  246. else
  247. {
  248. ::wprintf( L"Unable to format message for id %x - %d\n", msgId, ::GetLastError( ));
  249. }
  250. }
  251. va_end( args );
  252. }
  253. LPWSTR CVssAdminCLI::GetMsg(
  254. IN BOOL bLineBreaks,
  255. IN LONG msgId,
  256. ...
  257. )
  258. {
  259. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::GetMsg" );
  260. va_list args;
  261. LPWSTR lpMsgBuf;
  262. LPWSTR lpReturnStr = NULL;
  263. va_start( args, msgId );
  264. if (::FormatMessageW(
  265. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
  266. ( bLineBreaks ? VSS_LINE_BREAK_COLUMN : FORMAT_MESSAGE_MAX_WIDTH_MASK ),
  267. NULL,
  268. msgId,
  269. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  270. (LPWSTR) &lpMsgBuf,
  271. 0,
  272. &args
  273. ))
  274. {
  275. ::VssSafeDuplicateStr( ft, lpReturnStr, lpMsgBuf );
  276. ::LocalFree( lpMsgBuf );
  277. }
  278. else if (::FormatMessageW(
  279. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
  280. ( bLineBreaks ? VSS_LINE_BREAK_COLUMN : FORMAT_MESSAGE_MAX_WIDTH_MASK ),
  281. NULL,
  282. msgId,
  283. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  284. (LPWSTR) &lpMsgBuf,
  285. 0,
  286. &args ) )
  287. {
  288. ::VssSafeDuplicateStr( ft, lpReturnStr, lpMsgBuf );
  289. ::LocalFree( lpMsgBuf );
  290. }
  291. va_end( args );
  292. // Returns NULL if message was not found
  293. return lpReturnStr;
  294. }
  295. void CVssAdminCLI::AppendMessageToStr(
  296. IN LPWSTR pwszString,
  297. IN SIZE_T cMaxStrLen,
  298. IN LONG lMsgId,
  299. IN DWORD AttrBit,
  300. IN LPCWSTR pwszDelimitStr
  301. ) throw( HRESULT )
  302. {
  303. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::AppendMessageToStr" );
  304. size_t cMaxAppendChars = cMaxStrLen - wcslen(pwszString);
  305. if ( pwszString[0] != L'\0' )
  306. {
  307. // First append the delimiter
  308. ::wcsncat( pwszString, pwszDelimitStr, cMaxAppendChars );
  309. cMaxAppendChars = cMaxStrLen - wcslen(pwszString);
  310. }
  311. // If this is a known message, lMsgId != 0
  312. if ( lMsgId != 0 )
  313. {
  314. LPWSTR pwszMsg;
  315. pwszMsg = GetMsg( FALSE, lMsgId );
  316. if ( pwszMsg == NULL )
  317. {
  318. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
  319. L"Error on loading the message string id %d. 0x%08lx",
  320. lMsgId, ::GetLastError() );
  321. }
  322. ::wcsncat( pwszString, pwszMsg, cMaxAppendChars );
  323. cMaxAppendChars = cMaxStrLen - wcslen(pwszString);
  324. ::VssFreeString( pwszMsg );
  325. }
  326. else
  327. {
  328. // No message for this one, just append the attribute in hex
  329. WCHAR pwszBitStr[64];
  330. StringCchPrintfW( STRING_CCH_PARAM(pwszBitStr),
  331. L"0x%x", AttrBit
  332. );
  333. ::wcsncat( pwszString, pwszBitStr, cMaxAppendChars);
  334. cMaxAppendChars = cMaxStrLen - wcslen(pwszString);
  335. }
  336. }
  337. //
  338. // Scans a number input by the user and converts it to a LONGLONG. Accepts the following
  339. // unit suffixes: B, K, KB, M, MB, G, GB, T, TB, P, PB, E, EB and floating point.
  340. //
  341. LONGLONG CVssAdminCLI::ScanNumber(
  342. IN LPCWSTR pwszNumToConvert,
  343. IN BOOL bSuffixAllowed
  344. ) throw( HRESULT )
  345. {
  346. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::ScanNumber" );
  347. WCHAR wUnit = L'B';
  348. SIZE_T NumStrLen;
  349. // If the string is NULL, assume infinite size
  350. if ( pwszNumToConvert == NULL )
  351. return -1;
  352. //
  353. // Set up an automatically released temporary string
  354. //
  355. CVssAutoPWSZ autoStrNum;
  356. autoStrNum.CopyFrom( pwszNumToConvert );
  357. LPWSTR pwszNum = autoStrNum.GetRef();
  358. NumStrLen = ::wcslen( pwszNum );
  359. // Remove trailing spaces
  360. while ( NumStrLen > 0 && pwszNum[ NumStrLen - 1 ] == L' ' )
  361. {
  362. NumStrLen -= 1;
  363. pwszNum[ NumStrLen ] = L'\0';
  364. }
  365. // If the string is empty, assume infinite size
  366. if ( NumStrLen == 0 )
  367. return -1;
  368. if ( bSuffixAllowed )
  369. {
  370. // See if there is a suffix with three or more alpha chars, if so, error out.
  371. if ( NumStrLen > 3 && iswalpha( pwszNum[ NumStrLen - 3 ] ) && iswalpha( pwszNum[ NumStrLen - 2 ] ) &&
  372. iswalpha( pwszNum[ NumStrLen - 1 ] ) )
  373. {
  374. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_NUMBER,
  375. L"Invalid input number: '%s', too many alpha chars in suffix.", pwszNumToConvert );
  376. }
  377. // Now see if there is a single or double byte alpha suffix. If so, put the suffix in wUnit.
  378. else if ( NumStrLen > 2 && iswalpha( pwszNum[ NumStrLen - 2 ] ) && ( towupper( pwszNum[ NumStrLen - 1 ] ) == L'B' ) )
  379. {
  380. if ( towupper( pwszNum[ NumStrLen - 2 ] ) == L'B' )
  381. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_NUMBER,
  382. L"Invalid input number: '%s', BB is not a valid suffix", pwszNumToConvert );
  383. wUnit = pwszNum[ NumStrLen - 2 ];
  384. pwszNum[ NumStrLen - 2 ] = L'\0';
  385. NumStrLen -= 2;
  386. }
  387. else if ( NumStrLen > 1 && iswalpha( pwszNum[ NumStrLen - 1 ] ) )
  388. {
  389. wUnit = pwszNum[ NumStrLen - 1 ];
  390. pwszNum[ NumStrLen - 1 ] = L'\0';
  391. NumStrLen -= 1;
  392. }
  393. }
  394. else
  395. {
  396. // Let's make sure the string is only filled with digits...
  397. SIZE_T cStr = ::wcslen( pwszNum );
  398. for ( SIZE_T i = 0; i < cStr; ++i )
  399. {
  400. if ( ! iswdigit( pwszNum[ i ] ) )
  401. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_NUMBER,
  402. L"Invalid input number: '%s', number must be all digits", pwszNumToConvert );
  403. }
  404. }
  405. // At this point, the rest of the string should be a valid floating point number
  406. double dSize;
  407. if ( swscanf( pwszNum, L"%lf", &dSize ) != 1 )
  408. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_NUMBER,
  409. L"Invalid input number: %s", pwszNumToConvert );
  410. // Now bump up the size based on suffix
  411. switch( towupper( wUnit ) )
  412. {
  413. case L'B':
  414. break;
  415. case L'E':
  416. dSize *= 1024.;
  417. case L'P':
  418. dSize *= 1024.;
  419. case L'T':
  420. dSize *= 1024.;
  421. case L'G':
  422. dSize *= 1024.;
  423. case L'M':
  424. dSize *= 1024.;
  425. case L'K':
  426. dSize *= 1024.;
  427. break;
  428. default:
  429. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_NUMBER,
  430. L"Invalid input number: %s", pwszNumToConvert );
  431. break;
  432. }
  433. LONGLONG llRetVal;
  434. llRetVal = (LONGLONG)dSize;
  435. if ( llRetVal <= -1 )
  436. ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_NUMBER,
  437. L"Invalid input number: %s", pwszNumToConvert );
  438. return llRetVal;
  439. }
  440. //
  441. // Format a LONGLONG into the appropriate string using xB (KB, MB, GB, TB, PB, EB) suffixes.
  442. // Must use ::VssFreeString() to free returned string.
  443. //
  444. LPWSTR CVssAdminCLI::FormatNumber(
  445. IN LONGLONG llNum
  446. ) throw( HRESULT )
  447. {
  448. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::FormatNumber" );
  449. // If the string is -1 or less, assume this is infinite.
  450. if ( llNum < 0 )
  451. {
  452. LPWSTR pwszMsg = GetMsg( FALSE, MSG_INFINITE );
  453. if ( pwszMsg == NULL )
  454. {
  455. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
  456. L"Error on loading the message string id %d. 0x%08lx",
  457. MSG_INFINITE, ::GetLastError() );
  458. }
  459. return pwszMsg;
  460. }
  461. // Now convert the size into string
  462. UINT nExaBytes = (UINT)((llNum >> 60));
  463. UINT nPetaBytes = (UINT)((llNum >> 50) & 0x3ff);
  464. UINT nTerraBytes = (UINT)((llNum >> 40) & 0x3ff);
  465. UINT nGigaBytes = (UINT)((llNum >> 30) & 0x3ff);
  466. UINT nMegaBytes = (UINT)((llNum >> 20) & 0x3ff);
  467. UINT nKiloBytes = (UINT)((llNum >> 10) & 0x3ff);
  468. UINT nBytes = (UINT)( llNum & 0x3ff);
  469. LPCWSTR pwszUnit;
  470. double dSize = 0.0;
  471. // Display only biggest units, and never more than 999
  472. // instead of "1001 KB" we display "0.98 MB"
  473. if ( (nExaBytes) > 0 || (nPetaBytes > 999) )
  474. {
  475. pwszUnit = L"EB";
  476. dSize = ((double)llNum / (double)( 1 << 30 )) / (double)( 1 << 30 ) ;
  477. }
  478. else if ( (nPetaBytes) > 0 || (nTerraBytes > 999) )
  479. {
  480. pwszUnit = L"PB";
  481. dSize = ((double)llNum / (double)( 1 << 30 )) / (double)( 1 << 20 ) ;
  482. }
  483. else if ( (nTerraBytes) > 0 || (nGigaBytes > 999) )
  484. {
  485. pwszUnit = L"TB";
  486. dSize = ((double)llNum / (double)( 1 << 30 )) / (double)( 1 << 10 ) ;
  487. }
  488. else if ( (nGigaBytes) > 0 || (nMegaBytes > 999) )
  489. {
  490. pwszUnit = L"GB";
  491. dSize = (double)llNum / (double)( 1 << 30 ) ;
  492. }
  493. else if ( (nMegaBytes) > 0 || (nKiloBytes > 999) )
  494. {
  495. pwszUnit = L"MB";
  496. dSize = (double)llNum / (double)( 1 << 20 ) ;
  497. }
  498. else if ( (nKiloBytes) > 0 || (nBytes > 999) )
  499. {
  500. pwszUnit = L"KB";
  501. dSize = (double)llNum / (double)( 1 << 10 ) ;
  502. }
  503. else
  504. {
  505. pwszUnit = L"B";
  506. dSize = (double)nBytes;
  507. }
  508. // BUG 453314: Trunc used instead of Round
  509. // For explanation of the workaround look for the following KB Article:
  510. // Q184234: PRB: printf() and _fcvt() Might Produce Incorrect Rounding
  511. double dRoundedSize = dSize + 1e-10;
  512. // Format with op to three decimal points
  513. WCHAR pwszSize[64];
  514. StringCchPrintfW( STRING_CCH_PARAM(pwszSize),
  515. L"%.3f", dRoundedSize
  516. );
  517. SIZE_T len = ::wcslen( pwszSize );
  518. // Truncate trailing zeros
  519. while ( len > 0 && pwszSize[ len - 1 ] == L'0' )
  520. {
  521. len -= 1;
  522. pwszSize[ len ] = L'\0';
  523. }
  524. // Truncate trailing decimal point
  525. if ( len > 0 && pwszSize[ len - 1 ] == L'.' )
  526. pwszSize[ len - 1 ] = L'\0';
  527. // Now attach the unit suffix
  528. ::wcscat( pwszSize, L" " );
  529. ::wcscat( pwszSize, pwszUnit );
  530. // Allocate a string buffer and return it.
  531. LPWSTR pwszRetStr = NULL;
  532. ::VssSafeDuplicateStr( ft, pwszRetStr, pwszSize );
  533. return pwszRetStr;
  534. }
  535. /*++
  536. Description:
  537. This function outputs a msg.mc message.
  538. --*/
  539. void CVssAdminCLI::OutputErrorMsg(
  540. IN LONG msgId,
  541. ...
  542. )
  543. {
  544. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::OutputErrorMsg" );
  545. va_list args;
  546. LPWSTR lpMsgBuf;
  547. va_start( args, msgId );
  548. if (::FormatMessage(
  549. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
  550. NULL,
  551. MSG_ERROR,
  552. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  553. (LPWSTR) &lpMsgBuf,
  554. 0,
  555. NULL
  556. ))
  557. {
  558. OutputOnConsole( lpMsgBuf );
  559. ::LocalFree( lpMsgBuf );
  560. }
  561. if (::FormatMessage(
  562. (msgId >= MSG_FIRST_MESSAGE_ID ? FORMAT_MESSAGE_FROM_HMODULE :
  563. FORMAT_MESSAGE_FROM_SYSTEM)
  564. | FORMAT_MESSAGE_ALLOCATE_BUFFER,
  565. NULL,
  566. msgId,
  567. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  568. (LPWSTR) &lpMsgBuf,
  569. 0,
  570. &args
  571. ))
  572. {
  573. OutputOnConsole( L" " );
  574. OutputOnConsole( lpMsgBuf );
  575. OutputOnConsole( L" \r\n" );
  576. ::LocalFree( lpMsgBuf );
  577. }
  578. else
  579. {
  580. if (::FormatMessage(
  581. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  582. NULL,
  583. msgId,
  584. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  585. (LPWSTR) &lpMsgBuf,
  586. 0,
  587. &args))
  588. {
  589. OutputOnConsole( L" " );
  590. OutputOnConsole( lpMsgBuf );
  591. OutputOnConsole( L" \r\n" );
  592. ::LocalFree( lpMsgBuf );
  593. }
  594. else
  595. {
  596. ::wprintf( L"Unable to format message for id %x - %d\n", msgId, ::GetLastError( ));
  597. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
  598. L"Error on loading the message string id %d. 0x%08lx",
  599. msgId, ::GetLastError() );
  600. }
  601. }
  602. va_end( args );
  603. }
  604. BOOL CVssAdminCLI::PromptUserForConfirmation(
  605. IN LONG lPromptMsgId,
  606. IN ULONG ulNum
  607. )
  608. {
  609. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::PromptUserForConfirmation" );
  610. BOOL bRetVal = FALSE;
  611. //
  612. // First check to see if in quiet mode. If so, simply return
  613. // true
  614. //
  615. if ( GetOptionValueBool( VSSADM_O_QUIET ) )
  616. return TRUE;
  617. //
  618. // Load the response message string, in English it is "YN"
  619. //
  620. LPWSTR pwszResponse;
  621. pwszResponse = GetMsg( FALSE, MSG_YESNO_RESPONSE_DATA );
  622. if ( pwszResponse == NULL )
  623. {
  624. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
  625. L"Error on loading the message string id %d. 0x%08lx",
  626. MSG_YESNO_RESPONSE_DATA, ::GetLastError() );
  627. }
  628. //
  629. // Now prompt the user, note, parameter 4 is the affirmative response (Y) and
  630. // parameter 5 is the negative reponse (N)
  631. //
  632. OutputMsg( lPromptMsgId, ulNum, pwszResponse[0], pwszResponse[1] );
  633. WCHAR wcIn;
  634. DWORD fdwMode;
  635. // Make sure we are outputting to a real console.
  636. if ( ( ::GetFileType( m_hConsoleOutput ) & FILE_TYPE_CHAR ) &&
  637. ::GetConsoleMode( m_hConsoleOutput, &fdwMode ) )
  638. {
  639. // Going to the console, ask the user.
  640. wcIn = ::MyGetChar();
  641. }
  642. else
  643. {
  644. // Output redirected, assume NO
  645. wcIn = pwszResponse[1]; // N
  646. }
  647. WCHAR wcsOutput[16];
  648. StringCchPrintfW( STRING_CCH_PARAM(wcsOutput),
  649. L"%c\n\n", wcIn
  650. );
  651. OutputOnConsole( wcsOutput );
  652. //
  653. // Compare the character using the proper W32 function and
  654. // not towupper().
  655. //
  656. if ( ::CompareStringW( LOCALE_INVARIANT,
  657. NORM_IGNORECASE | NORM_IGNOREKANATYPE,
  658. &wcIn,
  659. 1,
  660. pwszResponse + 0, // Y
  661. 1 ) == CSTR_EQUAL )
  662. {
  663. bRetVal = TRUE;
  664. }
  665. else
  666. {
  667. bRetVal = FALSE;
  668. }
  669. ::CoTaskMemFree( pwszResponse );
  670. return bRetVal;
  671. }
  672. void CVssAdminCLI::OutputOnConsole(
  673. IN LPCWSTR wszStr
  674. )
  675. {
  676. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::OutputOnConsole" );
  677. DWORD dwCharsOutput;
  678. DWORD fdwMode;
  679. static BOOL bFirstTime = TRUE;
  680. static BOOL bIsTrueConsoleOutput;
  681. if ( m_hConsoleOutput == INVALID_HANDLE_VALUE )
  682. {
  683. throw E_UNEXPECTED;
  684. }
  685. if ( bFirstTime )
  686. {
  687. //
  688. // Stash away the results in static vars. bIsTrueConsoleOutput is TRUE when the
  689. // standard output handle is pointing to a console character device.
  690. //
  691. bIsTrueConsoleOutput = ( ::GetFileType( m_hConsoleOutput ) & FILE_TYPE_CHAR ) &&
  692. ::GetConsoleMode( m_hConsoleOutput, &fdwMode );
  693. bFirstTime = FALSE;
  694. }
  695. if ( bIsTrueConsoleOutput )
  696. {
  697. //
  698. // Output to the console
  699. //
  700. if ( !::WriteConsoleW( m_hConsoleOutput,
  701. ( PVOID )wszStr,
  702. ( DWORD )::wcslen( wszStr ),
  703. &dwCharsOutput,
  704. NULL ) )
  705. {
  706. throw HRESULT_FROM_WIN32( ::GetLastError() );
  707. }
  708. }
  709. else
  710. {
  711. //
  712. // Output being redirected. WriteConsoleW doesn't work for redirected output. Convert
  713. // UNICODE to the current output CP multibyte charset.
  714. //
  715. LPSTR lpszTmpBuffer;
  716. DWORD dwByteCount;
  717. //
  718. // Get size of temp buffer needed for the conversion.
  719. //
  720. dwByteCount = ::WideCharToMultiByte(
  721. ::GetConsoleOutputCP(),
  722. 0,
  723. wszStr,
  724. -1,
  725. NULL,
  726. 0,
  727. NULL,
  728. NULL
  729. );
  730. if ( dwByteCount == 0 )
  731. {
  732. throw HRESULT_FROM_WIN32( ::GetLastError() );
  733. }
  734. lpszTmpBuffer = ( LPSTR )::malloc( dwByteCount );
  735. if ( lpszTmpBuffer == NULL )
  736. {
  737. throw E_OUTOFMEMORY;
  738. }
  739. //
  740. // Now convert it.
  741. //
  742. dwByteCount = ::WideCharToMultiByte(
  743. ::GetConsoleOutputCP(),
  744. 0,
  745. wszStr,
  746. -1,
  747. lpszTmpBuffer,
  748. dwByteCount,
  749. NULL,
  750. NULL
  751. );
  752. if ( dwByteCount == 0 )
  753. {
  754. ::free( lpszTmpBuffer );
  755. throw HRESULT_FROM_WIN32( ::GetLastError() );
  756. }
  757. // Finally output it.
  758. if ( !::WriteFile(
  759. m_hConsoleOutput,
  760. lpszTmpBuffer,
  761. dwByteCount - 1, // Get rid of the trailing NULL char
  762. &dwCharsOutput,
  763. NULL ) )
  764. {
  765. throw HRESULT_FROM_WIN32( ::GetLastError() );
  766. }
  767. ::free( lpszTmpBuffer );
  768. }
  769. }
  770. BOOL CVssAdminCLI::UnloggableError(IN HRESULT hError)
  771. {
  772. switch (hError)
  773. {
  774. case VSSADM_E_INVALID_NUMBER:
  775. case VSSADM_E_INVALID_COMMAND:
  776. case VSSADM_E_INVALID_OPTION:
  777. case VSSADM_E_INVALID_OPTION_VALUE:
  778. case VSSADM_E_DUPLICATE_OPTION:
  779. case VSSADM_E_OPTION_NOT_ALLOWED_FOR_COMMAND:
  780. case VSSADM_E_REQUIRED_OPTION_MISSING:
  781. case VSSADM_E_INVALID_SET_OF_OPTIONS:
  782. return TRUE;
  783. default:
  784. return FALSE;
  785. }
  786. }
  787. //
  788. // Returns TRUE if error message was mapped
  789. //
  790. BOOL MapVssErrorToMsg(
  791. IN HRESULT hr,
  792. OUT LONG *plMsgNum
  793. ) throw( HRESULT )
  794. {
  795. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"MapVssErrorToMsg" );
  796. ft.Trace( VSSDBG_VSSADMIN, L"Input HR: 0x%08x", hr );
  797. LONG msg = 0;
  798. *plMsgNum = 0;
  799. switch ( hr )
  800. {
  801. case VSSADM_E_NO_ITEMS_IN_QUERY:
  802. msg = MSG_ERROR_NO_ITEMS_FOUND;
  803. break;
  804. // Parsing errors:
  805. case VSSADM_E_INVALID_NUMBER:
  806. msg = MSG_ERROR_INVALID_INPUT_NUMBER;
  807. break;
  808. case VSSADM_E_INVALID_COMMAND:
  809. msg = MSG_ERROR_INVALID_COMMAND;
  810. break;
  811. case VSSADM_E_INVALID_OPTION:
  812. msg = MSG_ERROR_INVALID_OPTION;
  813. break;
  814. case E_INVALIDARG:
  815. case VSSADM_E_INVALID_OPTION_VALUE:
  816. msg = MSG_ERROR_INVALID_OPTION_VALUE;
  817. break;
  818. case VSSADM_E_DUPLICATE_OPTION:
  819. msg = MSG_ERROR_DUPLICATE_OPTION;
  820. break;
  821. case VSSADM_E_OPTION_NOT_ALLOWED_FOR_COMMAND:
  822. msg = MSG_ERROR_OPTION_NOT_ALLOWED_FOR_COMMAND;
  823. break;
  824. case VSSADM_E_REQUIRED_OPTION_MISSING:
  825. msg = MSG_ERROR_REQUIRED_OPTION_MISSING;
  826. break;
  827. case VSSADM_E_INVALID_SET_OF_OPTIONS:
  828. msg = MSG_ERROR_INVALID_SET_OF_OPTIONS;
  829. break;
  830. case VSSADM_E_SNAPSHOT_NOT_FOUND:
  831. msg = MSG_ERROR_SNAPSHOT_NOT_FOUND2;
  832. break;
  833. case VSSADM_E_DELETION_DENIED:
  834. msg = MSG_ERROR_DELETION_DENIED;
  835. break;
  836. // VSS errors
  837. case VSS_E_PROVIDER_NOT_REGISTERED:
  838. msg = MSG_ERROR_VSS_PROVIDER_NOT_REGISTERED;
  839. break;
  840. case VSS_E_OBJECT_NOT_FOUND:
  841. msg = MSG_ERROR_VSS_VOLUME_NOT_FOUND;
  842. break;
  843. case VSS_E_PROVIDER_VETO:
  844. msg = MSG_ERROR_VSS_PROVIDER_VETO;
  845. break;
  846. case VSS_E_VOLUME_NOT_SUPPORTED:
  847. msg = MSG_ERROR_VSS_VOLUME_NOT_SUPPORTED;
  848. break;
  849. case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER:
  850. msg = MSG_ERROR_VSS_VOLUME_NOT_SUPPORTED_BY_PROVIDER;
  851. break;
  852. case VSS_E_UNEXPECTED_PROVIDER_ERROR:
  853. msg = MSG_ERROR_VSS_UNEXPECTED_PROVIDER_ERROR;
  854. break;
  855. case VSS_E_FLUSH_WRITES_TIMEOUT:
  856. msg = MSG_ERROR_VSS_FLUSH_WRITES_TIMEOUT;
  857. break;
  858. case VSS_E_HOLD_WRITES_TIMEOUT:
  859. msg = MSG_ERROR_VSS_HOLD_WRITES_TIMEOUT;
  860. break;
  861. case VSS_E_UNEXPECTED_WRITER_ERROR:
  862. msg = MSG_ERROR_VSS_UNEXPECTED_WRITER_ERROR;
  863. break;
  864. case VSS_E_SNAPSHOT_SET_IN_PROGRESS:
  865. msg = MSG_ERROR_VSS_SNAPSHOT_SET_IN_PROGRESS;
  866. break;
  867. case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED:
  868. msg = MSG_ERROR_VSS_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED;
  869. break;
  870. case VSS_E_UNSUPPORTED_CONTEXT:
  871. msg = MSG_ERROR_VSS_UNSUPPORTED_CONTEXT;
  872. break;
  873. case VSS_E_MAXIMUM_DIFFAREA_ASSOCIATIONS_REACHED:
  874. msg = MSG_ERROR_VSS_MAXIMUM_DIFFAREA_ASSOCIATIONS_REACHED;
  875. break;
  876. case VSS_E_INSUFFICIENT_STORAGE:
  877. msg = MSG_ERROR_VSS_INSUFFICIENT_STORAGE;
  878. break;
  879. case E_OUTOFMEMORY:
  880. msg = MSG_ERROR_OUT_OF_MEMORY;
  881. break;
  882. case E_ACCESSDENIED:
  883. msg = MSG_ERROR_ACCESS_DENIED;
  884. break;
  885. case VSS_E_BAD_STATE:
  886. case VSS_E_CORRUPT_XML_DOCUMENT:
  887. case VSS_E_INVALID_XML_DOCUMENT:
  888. case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED:
  889. msg = MSG_ERROR_INTERNAL_VSSADMIN_ERROR;
  890. break;
  891. }
  892. if ( msg == 0 )
  893. return FALSE;
  894. *plMsgNum = msg;
  895. ft.Trace( VSSDBG_VSSADMIN, L"Output Msg#: 0x%08x", msg );
  896. return TRUE;
  897. }
  898. LPWSTR GuidToString(
  899. IN GUID guid
  900. )
  901. {
  902. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"GuidToString" );
  903. LPWSTR pwszGuid;
  904. // {36e4be76-035d-11d5-9ef2-806d6172696f}
  905. const x_MaxGuidSize = 40;
  906. pwszGuid = (LPWSTR)::CoTaskMemAlloc( x_MaxGuidSize * sizeof( WCHAR ) );
  907. if ( pwszGuid == NULL )
  908. ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY,
  909. L"Error from CoTaskMemAlloc: 0x%08lx",
  910. ::GetLastError() );
  911. StringCchPrintfW( pwszGuid, x_MaxGuidSize,
  912. WSTR_GUID_FMT, GUID_PRINTF_ARG( guid )
  913. );
  914. return pwszGuid;
  915. }
  916. LPWSTR DateTimeToString(
  917. IN VSS_TIMESTAMP *pTimeStamp
  918. )
  919. {
  920. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"DateTimeToString" );
  921. LPWSTR pwszDateTime;
  922. SYSTEMTIME stLocal;
  923. FILETIME ftLocal;
  924. WCHAR pwszDate[ 64 ];
  925. WCHAR pwszTime[ 64 ];
  926. if ( pTimeStamp == NULL || *pTimeStamp == 0 )
  927. {
  928. SYSTEMTIME sysTime;
  929. FILETIME fileTime;
  930. // Get current time
  931. ::GetSystemTime( &sysTime );
  932. // Convert system time to file time
  933. ::SystemTimeToFileTime( &sysTime, &fileTime );
  934. // Compensate for local TZ
  935. ::FileTimeToLocalFileTime( &fileTime, &ftLocal );
  936. }
  937. else
  938. {
  939. // Compensate for local TZ
  940. ::FileTimeToLocalFileTime( (FILETIME *)pTimeStamp, &ftLocal );
  941. }
  942. // Finally convert it to system time
  943. ::FileTimeToSystemTime( &ftLocal, &stLocal );
  944. // Convert timestamp to a date string
  945. ::GetDateFormatW( GetThreadLocale( ),
  946. DATE_SHORTDATE,
  947. &stLocal,
  948. NULL,
  949. pwszDate,
  950. sizeof( pwszDate ) / sizeof( pwszDate[0] ));
  951. // Convert timestamp to a time string
  952. ::GetTimeFormatW( GetThreadLocale( ),
  953. 0,
  954. &stLocal,
  955. NULL,
  956. pwszTime,
  957. sizeof( pwszTime ) / sizeof( pwszTime[0] ));
  958. // Now combine the strings and return it
  959. pwszDateTime = (LPWSTR)::CoTaskMemAlloc( ( ::wcslen( pwszDate ) + ::wcslen( pwszTime ) + 2 ) * sizeof( pwszDate[0] ) );
  960. if ( pwszDateTime == NULL )
  961. ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY,
  962. L"Error from CoTaskMemAlloc, rc: %d",
  963. ::GetLastError() );
  964. ::wcscpy( pwszDateTime, pwszDate );
  965. ::wcscat( pwszDateTime, L" " );
  966. ::wcscat( pwszDateTime, pwszTime );
  967. return pwszDateTime;
  968. }
  969. LPWSTR LonglongToString(
  970. IN LONGLONG llValue
  971. )
  972. {
  973. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"LonglongToString" );
  974. WCHAR wszLL[64];
  975. LPWSTR pwszRetVal = NULL;
  976. ::_i64tow( llValue, wszLL, 10 );
  977. ::VssSafeDuplicateStr( ft, pwszRetVal, wszLL );
  978. return pwszRetVal;
  979. }
  980. /*++
  981. Description:
  982. Uses the win32 console functions to get one character of input.
  983. --*/
  984. WCHAR MyGetChar(
  985. )
  986. {
  987. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"MyGetChar" );
  988. DWORD fdwOldMode, fdwMode;
  989. HANDLE hStdin;
  990. WCHAR chBuffer[2];
  991. hStdin = ::GetStdHandle(STD_INPUT_HANDLE);
  992. if (hStdin == INVALID_HANDLE_VALUE)
  993. {
  994. ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ),
  995. L"MyGetChar - Error from GetStdHandle(), rc: %d",
  996. ::GetLastError() );
  997. }
  998. if (!::GetConsoleMode(hStdin, &fdwOldMode))
  999. {
  1000. ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ),
  1001. L"MyGetChar - Error from GetConsoleMode(), rc: %d",
  1002. ::GetLastError() );
  1003. }
  1004. fdwMode = fdwOldMode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT );
  1005. if (!::SetConsoleMode(hStdin, fdwMode))
  1006. {
  1007. ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ),
  1008. L"MyGetChar - Error from SetConsoleMode(), rc: %d",
  1009. ::GetLastError() );
  1010. }
  1011. // Flush the console input buffer to make sure there is no queued input
  1012. ::FlushConsoleInputBuffer( hStdin );
  1013. // Without line and echo input modes, ReadFile returns
  1014. // when any input is available.
  1015. DWORD dwBytesRead;
  1016. if (!::ReadConsoleW(hStdin, chBuffer, 1, &dwBytesRead, NULL))
  1017. {
  1018. ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ),
  1019. L"MyGetChar - Error from ReadConsoleW(), rc: %d",
  1020. ::GetLastError() );
  1021. }
  1022. // Restore the original console mode.
  1023. ::SetConsoleMode(hStdin, fdwOldMode);
  1024. return chBuffer[0];
  1025. }