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.

773 lines
19 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Abstract:
  4. @doc
  5. @module vssadmin.cpp | Implementation of the Volume Snapshots demo
  6. @end
  7. Author:
  8. Adi Oltean [aoltean] 09/17/1999
  9. TBD:
  10. Add comments.
  11. Revision History:
  12. Name Date Comments
  13. aoltean 09/17/1999 Created
  14. --*/
  15. /////////////////////////////////////////////////////////////////////////////
  16. // Includes
  17. // The rest of includes are specified here
  18. #include "vssadmin.h"
  19. #include <locale.h>
  20. #include <winnlsp.h> // in public\internal\base\inc
  21. ////////////////////////////////////////////////////////////////////////
  22. // Standard foo for file name aliasing. This code block must be after
  23. // all includes of VSS header files.
  24. //
  25. #ifdef VSS_FILE_ALIAS
  26. #undef VSS_FILE_ALIAS
  27. #endif
  28. #define VSS_FILE_ALIAS "ADMVADMC"
  29. //
  30. ////////////////////////////////////////////////////////////////////////
  31. /////////////////////////////////////////////////////////////////////////////
  32. // Implementation
  33. CVssAdminCLI::CVssAdminCLI(
  34. IN HINSTANCE hInstance
  35. )
  36. /*++
  37. Description:
  38. Standard constructor. Initializes internal members
  39. --*/
  40. {
  41. BS_ASSERT(hInstance != NULL);
  42. m_hInstance = hInstance;
  43. m_eCommandType = VSS_CMD_UNKNOWN;
  44. m_eListType = VSS_LIST_UNKNOWN;
  45. m_eFilterObjectType = VSS_OBJECT_UNKNOWN;
  46. m_eListedObjectType = VSS_OBJECT_UNKNOWN;
  47. m_FilterSnapshotSetId = GUID_NULL;
  48. m_FilterSnapshotId = GUID_NULL;
  49. m_pwszCmdLine = NULL;
  50. m_nReturnValue = VSS_CMDRET_ERROR;
  51. m_hConsoleOutput = INVALID_HANDLE_VALUE;
  52. }
  53. CVssAdminCLI::~CVssAdminCLI()
  54. /*++
  55. Description:
  56. Standard destructor. Calls Finalize and eventually frees the
  57. memory allocated by internal members.
  58. --*/
  59. {
  60. // Release the cached resource strings
  61. for( int nIndex = 0; nIndex < m_mapCachedResourceStrings.GetSize(); nIndex++) {
  62. LPCWSTR& pwszResString = m_mapCachedResourceStrings.GetValueAt(nIndex);
  63. ::VssFreeString(pwszResString);
  64. }
  65. // Release the cached provider names
  66. for( int nIndex = 0; nIndex < m_mapCachedProviderNames.GetSize(); nIndex++) {
  67. LPCWSTR& pwszProvName = m_mapCachedProviderNames.GetValueAt(nIndex);
  68. ::VssFreeString(pwszProvName);
  69. }
  70. // Release the cached command line
  71. ::VssFreeString(m_pwszCmdLine);
  72. // Uninitialize the COM library
  73. Finalize();
  74. }
  75. /////////////////////////////////////////////////////////////////////////////
  76. // Implementation
  77. LPCWSTR CVssAdminCLI::LoadString(
  78. IN CVssFunctionTracer& ft,
  79. IN UINT uStringId
  80. )
  81. {
  82. LPCWSTR wszReturnedString = m_mapCachedResourceStrings.Lookup(uStringId);
  83. if (wszReturnedString)
  84. return wszReturnedString;
  85. // Load the string from resources.
  86. WCHAR wszBuffer[nStringBufferSize];
  87. INT nReturnedCharacters = ::LoadStringW(
  88. GetInstance(),
  89. uStringId,
  90. wszBuffer,
  91. nStringBufferSize - 1
  92. );
  93. if (nReturnedCharacters == 0)
  94. ft.Throw( VSSDBG_COORD, E_UNEXPECTED,
  95. L"Error on loading the string %u. 0x%08lx",
  96. uStringId, ::GetLastError() );
  97. // Duplicate the new string
  98. LPWSTR wszNewString = NULL;
  99. ::VssSafeDuplicateStr( ft, wszNewString, wszBuffer );
  100. wszReturnedString = wszNewString;
  101. // Save the string in the cache
  102. if ( !m_mapCachedResourceStrings.Add( uStringId, wszReturnedString ) ) {
  103. ::VssFreeString( wszReturnedString );
  104. ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error");
  105. }
  106. return wszReturnedString;
  107. }
  108. LPCWSTR CVssAdminCLI::GetNextCmdlineToken(
  109. IN CVssFunctionTracer& ft,
  110. IN bool bFirstToken /* = false */
  111. ) throw(HRESULT)
  112. /*++
  113. Description:
  114. This function returns the tokens in the command line.
  115. The function will skip any separators (space and tab).
  116. If bFirstCall == true then it will return the first token.
  117. Otherwise subsequent calls will return subsequent tokens.
  118. If the last token is NULL then there are no more tokens in the command line.
  119. --*/
  120. {
  121. return ::wcstok( bFirstToken? m_pwszCmdLine: NULL, wszVssFmtSpaces );
  122. UNREFERENCED_PARAMETER(ft);
  123. }
  124. bool CVssAdminCLI::Match(
  125. IN CVssFunctionTracer& ft,
  126. IN LPCWSTR wszString,
  127. IN LPCWSTR wszPatternString
  128. ) throw(HRESULT)
  129. /*++
  130. Description:
  131. This function returns true iif the given string matches the
  132. pattern string. The comparison is case insensitive.
  133. --*/
  134. {
  135. // If the string is NULL then the Match failed.
  136. if (wszString == NULL) return false;
  137. // Check for string equality (case insensitive)
  138. return (::_wcsicmp( wszString, wszPatternString ) == 0);
  139. UNREFERENCED_PARAMETER(ft);
  140. }
  141. bool CVssAdminCLI::ScanGuid(
  142. IN CVssFunctionTracer& /* ft */,
  143. IN LPCWSTR wszString,
  144. IN VSS_ID& Guid
  145. ) throw(HRESULT)
  146. /*++
  147. Description:
  148. This function returns true iif the given string matches a guid.
  149. The guid is returned in the proper variable.
  150. The formatting is case insensitive.
  151. --*/
  152. {
  153. return SUCCEEDED(::CLSIDFromString(W2OLE(const_cast<WCHAR*>(wszString)), &Guid));
  154. }
  155. void CVssAdminCLI::Output(
  156. IN CVssFunctionTracer& ft,
  157. IN UINT uFormatStringId,
  158. ...
  159. ) throw(HRESULT)
  160. /*++
  161. Description:
  162. This function returns true iif the given string matches the
  163. pattern strig from resources. The comparison is case insensitive.
  164. --*/
  165. {
  166. WCHAR wszOutputBuffer[nStringBufferSize];
  167. // Load the format string
  168. LPCWSTR wszFormat = LoadString( ft, uFormatStringId );
  169. // Format the final string
  170. va_list marker;
  171. va_start( marker, uFormatStringId );
  172. _vsnwprintf( wszOutputBuffer, nStringBufferSize - 1, wszFormat, marker );
  173. va_end( marker );
  174. // Print the final string to the output
  175. OutputOnConsole( wszOutputBuffer );
  176. }
  177. void CVssAdminCLI::Output(
  178. IN CVssFunctionTracer& ft,
  179. IN LPCWSTR wszFormat,
  180. ...
  181. ) throw(HRESULT)
  182. /*++
  183. Description:
  184. This function returns true iif the given string matches the
  185. pattern strig from resources. The comparison is case insensitive.
  186. --*/
  187. {
  188. WCHAR wszOutputBuffer[nStringBufferSize];
  189. // Format the final string
  190. va_list marker;
  191. va_start( marker, wszFormat );
  192. _vsnwprintf( wszOutputBuffer, nStringBufferSize - 1, wszFormat, marker );
  193. va_end( marker );
  194. // Print the final string to the output
  195. OutputOnConsole( wszOutputBuffer );
  196. UNREFERENCED_PARAMETER(ft);
  197. }
  198. void CVssAdminCLI::OutputOnConsole(
  199. IN LPCWSTR wszStr
  200. )
  201. {
  202. DWORD dwCharsOutput;
  203. DWORD fdwMode;
  204. static BOOL bFirstTime = TRUE;
  205. static BOOL bIsTrueConsoleOutput;
  206. if ( m_hConsoleOutput == INVALID_HANDLE_VALUE )
  207. {
  208. throw E_UNEXPECTED;
  209. }
  210. if ( bFirstTime )
  211. {
  212. //
  213. // Stash away the results in static vars. bIsTrueConsoleOutput is TRUE when the
  214. // standard output handle is pointing to a console character device.
  215. //
  216. bIsTrueConsoleOutput = ( ::GetFileType( m_hConsoleOutput ) & FILE_TYPE_CHAR ) &&
  217. ::GetConsoleMode( m_hConsoleOutput, &fdwMode );
  218. bFirstTime = FALSE;
  219. }
  220. if ( bIsTrueConsoleOutput )
  221. {
  222. //
  223. // Output to the console
  224. //
  225. if ( !::WriteConsoleW( m_hConsoleOutput,
  226. ( PVOID )wszStr,
  227. ( DWORD )::wcslen( wszStr ),
  228. &dwCharsOutput,
  229. NULL ) )
  230. {
  231. throw HRESULT_FROM_WIN32( ::GetLastError() );
  232. }
  233. }
  234. else
  235. {
  236. //
  237. // Output being redirected. WriteConsoleW doesn't work for redirected output. Convert
  238. // UNICODE to the current output CP multibyte charset.
  239. //
  240. // ---------------- To be removed later - Start -----------------
  241. //
  242. // Translate \n to \r\n since the string might be directed to a disk file.
  243. // Remove this code if the .rc file can be updated to include
  244. // \r\n. This was needed since we are in UI lockdown.
  245. //
  246. LPWSTR pwszConversion;
  247. // Allocate a string twice the size of the original
  248. pwszConversion = ( LPWSTR )::malloc( ( ( ::wcslen( wszStr ) * 2 ) + 1 ) * sizeof( WCHAR ) );
  249. if ( pwszConversion == NULL )
  250. {
  251. throw E_OUTOFMEMORY;
  252. }
  253. // Copy the string to the new string and place a \r in front of any \n. Also
  254. // handle the case if \r\n is already in the string.
  255. DWORD dwIdx = 0;
  256. while ( wszStr[0] != L'\0' )
  257. {
  258. if ( wszStr[0] == L'\r' && wszStr[1] == L'\n' )
  259. {
  260. pwszConversion[dwIdx++] = L'\r';
  261. pwszConversion[dwIdx++] = L'\n';
  262. wszStr += 2;
  263. }
  264. else if ( wszStr[0] == L'\n' )
  265. {
  266. pwszConversion[dwIdx++] = L'\r';
  267. pwszConversion[dwIdx++] = L'\n';
  268. wszStr += 1;
  269. }
  270. else
  271. {
  272. pwszConversion[dwIdx++] = wszStr[0];
  273. wszStr += 1;
  274. }
  275. }
  276. pwszConversion[dwIdx] = L'\0';
  277. // ---------------- To be removed later - End -----------------
  278. LPSTR pszMultibyteBuffer;
  279. DWORD dwByteCount;
  280. //
  281. // Get size of temp buffer needed for the conversion.
  282. //
  283. dwByteCount = ::WideCharToMultiByte(
  284. ::GetConsoleOutputCP(),
  285. 0,
  286. pwszConversion,
  287. -1,
  288. NULL,
  289. 0,
  290. NULL,
  291. NULL
  292. );
  293. if ( dwByteCount == 0 )
  294. {
  295. ::free( pwszConversion );
  296. throw HRESULT_FROM_WIN32( ::GetLastError() );
  297. }
  298. pszMultibyteBuffer = ( LPSTR )::malloc( dwByteCount );
  299. if ( pszMultibyteBuffer == NULL )
  300. {
  301. ::free( pwszConversion );
  302. throw E_OUTOFMEMORY;
  303. }
  304. //
  305. // Now convert it.
  306. //
  307. dwByteCount = ::WideCharToMultiByte(
  308. ::GetConsoleOutputCP(),
  309. 0,
  310. pwszConversion,
  311. -1,
  312. pszMultibyteBuffer,
  313. dwByteCount,
  314. NULL,
  315. NULL
  316. );
  317. ::free( pwszConversion );
  318. if ( dwByteCount == 0 )
  319. {
  320. ::free( pszMultibyteBuffer );
  321. throw HRESULT_FROM_WIN32( ::GetLastError() );
  322. }
  323. // Finally output it.
  324. if ( !::WriteFile(
  325. m_hConsoleOutput,
  326. pszMultibyteBuffer,
  327. dwByteCount - 1, // Get rid of the trailing NULL char
  328. &dwCharsOutput,
  329. NULL ) )
  330. {
  331. throw HRESULT_FROM_WIN32( ::GetLastError() );
  332. }
  333. ::free( pszMultibyteBuffer );
  334. }
  335. }
  336. LPCWSTR CVssAdminCLI::GetProviderName(
  337. IN CVssFunctionTracer& ft,
  338. IN VSS_ID& ProviderId
  339. ) throw(HRESULT)
  340. {
  341. LPCWSTR wszReturnedString = m_mapCachedProviderNames.Lookup(ProviderId);
  342. if (wszReturnedString)
  343. return wszReturnedString;
  344. CComPtr<IVssCoordinator> pICoord;
  345. ft.LogVssStartupAttempt();
  346. ft.hr = pICoord.CoCreateInstance( CLSID_VSSCoordinator );
  347. if ( ft.HrFailed() )
  348. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Connection failed with hr = 0x%08lx", ft.hr);
  349. CComPtr<IVssEnumObject> pIEnumProvider;
  350. ft.hr = pICoord->Query( GUID_NULL,
  351. VSS_OBJECT_NONE,
  352. VSS_OBJECT_PROVIDER,
  353. &pIEnumProvider );
  354. if ( ft.HrFailed() )
  355. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Query failed with hr = 0x%08lx", ft.hr);
  356. VSS_OBJECT_PROP Prop;
  357. VSS_PROVIDER_PROP& Prov = Prop.Obj.Prov;
  358. // Go through the list of providers to find the one we are interested in.
  359. ULONG ulFetched;
  360. while( 1 )
  361. {
  362. ft.hr = pIEnumProvider->Next( 1, &Prop, &ulFetched );
  363. if ( ft.HrFailed() )
  364. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Next failed with hr = 0x%08lx", ft.hr);
  365. if (ft.hr == S_FALSE) {
  366. // End of enumeration.
  367. // Provider not registered? Where did this snapshot come from?
  368. // It might be still possible if a snapshot provider was deleted
  369. // before querying the snapshot provider but after the snapshot attributes
  370. // were queried.
  371. BS_ASSERT(ulFetched == 0);
  372. return LoadString( ft, IDS_UNKNOWN_PROVIDER );
  373. }
  374. if (Prov.m_ProviderId == ProviderId)
  375. break;
  376. }
  377. // Duplicate the new string
  378. LPWSTR wszNewString = NULL;
  379. BS_ASSERT(Prov.m_pwszProviderName);
  380. ::VssSafeDuplicateStr( ft, wszNewString, Prov.m_pwszProviderName );
  381. wszReturnedString = wszNewString;
  382. // Save the string in the cache
  383. if (!m_mapCachedProviderNames.Add( ProviderId, wszReturnedString )) {
  384. ::VssFreeString( wszReturnedString );
  385. ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error");
  386. }
  387. return wszReturnedString;
  388. }
  389. /////////////////////////////////////////////////////////////////////////////
  390. // Implementation
  391. void CVssAdminCLI::Initialize(
  392. IN CVssFunctionTracer& ft
  393. ) throw(HRESULT)
  394. /*++
  395. Description:
  396. Initializes the COM library. Called explicitely after instantiating the CVssAdminCLI object.
  397. --*/
  398. {
  399. // Use the OEM code page ...
  400. ::setlocale(LC_ALL, ".OCP");
  401. // Use the console UI language
  402. ::SetThreadUILanguage( 0 );
  403. //
  404. // Use only the Console routines to output messages. To do so, need to open standard
  405. // output.
  406. //
  407. m_hConsoleOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
  408. if (m_hConsoleOutput == INVALID_HANDLE_VALUE)
  409. {
  410. ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ),
  411. L"Initialize - Error from GetStdHandle(), rc: %d",
  412. ::GetLastError() );
  413. }
  414. // Initialize COM library
  415. ft.hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  416. if (ft.HrFailed())
  417. ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Failure in initializing the COM library 0x%08lx", ft.hr);
  418. // Initialize COM security
  419. ft.hr = CoInitializeSecurity(
  420. NULL, // IN PSECURITY_DESCRIPTOR pSecDesc,
  421. -1, // IN LONG cAuthSvc,
  422. NULL, // IN SOLE_AUTHENTICATION_SERVICE *asAuthSvc,
  423. NULL, // IN void *pReserved1,
  424. RPC_C_AUTHN_LEVEL_CONNECT, // IN DWORD dwAuthnLevel,
  425. RPC_C_IMP_LEVEL_IMPERSONATE, // IN DWORD dwImpLevel,
  426. NULL, // IN void *pAuthList,
  427. EOAC_NONE, // IN DWORD dwCapabilities,
  428. NULL // IN void *pReserved3
  429. );
  430. if (ft.HrFailed()) {
  431. ft.Throw( VSSDBG_VSSADMIN, ft.hr,
  432. L" Error: CoInitializeSecurity() returned 0x%08lx", ft.hr );
  433. }
  434. // Print the header
  435. Output( ft, IDS_HEADER );
  436. // Initialize the command line
  437. ::VssSafeDuplicateStr( ft, m_pwszCmdLine, ::GetCommandLineW() );
  438. }
  439. void CVssAdminCLI::ParseCmdLine(
  440. IN CVssFunctionTracer& ft
  441. ) throw(HRESULT)
  442. /*++
  443. Description:
  444. Parses the command line.
  445. --*/
  446. {
  447. // Skip the executable name
  448. GetNextCmdlineToken( ft, true );
  449. // Get the first token after the executable name
  450. LPCWSTR pwszArg1 = GetNextCmdlineToken( ft );
  451. // Check if this is a list process
  452. if ( Match( ft, pwszArg1, wszVssOptList)) {
  453. m_eCommandType = VSS_CMD_LIST;
  454. // Get the next token after "list"
  455. LPCWSTR pwszArg2 = GetNextCmdlineToken( ft );
  456. // Check if this is a list snapshots process
  457. if ( Match( ft, pwszArg2, wszVssOptSnapshots )) {
  458. m_eListType = VSS_LIST_SNAPSHOTS;
  459. // Get the next token after "snapshots"
  460. LPCWSTR pwszArg3 = GetNextCmdlineToken( ft );
  461. if ( pwszArg3 == NULL ) {
  462. m_FilterSnapshotSetId = GUID_NULL;
  463. m_eFilterObjectType = VSS_OBJECT_NONE;
  464. return;
  465. }
  466. // Check if this is a snapshot set filter
  467. if ( ::_wcsnicmp( pwszArg3, wszVssOptSet, ::wcslen( wszVssOptSet ) ) == 0 ) {
  468. // Get the next token after "snapshots"
  469. LPCWSTR pwszArg4 = pwszArg3 + ::wcslen( wszVssOptSet );
  470. // Get the snapshot set Id
  471. if ( (pwszArg4[0] != '\0' ) && ScanGuid( ft, pwszArg4, m_FilterSnapshotSetId ) && (GetNextCmdlineToken(ft) == NULL) ) {
  472. m_eFilterObjectType = VSS_OBJECT_SNAPSHOT_SET;
  473. return;
  474. }
  475. }
  476. }
  477. // Check if this is a list writers process
  478. if ( Match( ft, pwszArg2, wszVssOptWriters) && (GetNextCmdlineToken(ft) == NULL)) {
  479. m_eListType = VSS_LIST_WRITERS;
  480. m_FilterSnapshotSetId = GUID_NULL;
  481. m_eFilterObjectType = VSS_OBJECT_NONE;
  482. return;
  483. }
  484. // Check if this is a list providers process
  485. if ( Match( ft, pwszArg2, wszVssOptProviders)&& (GetNextCmdlineToken(ft) == NULL)) {
  486. m_eListType = VSS_LIST_PROVIDERS;
  487. m_FilterSnapshotSetId = GUID_NULL;
  488. m_eFilterObjectType = VSS_OBJECT_NONE;
  489. return;
  490. }
  491. }
  492. else if (pwszArg1 == NULL)
  493. m_nReturnValue = VSS_CMDRET_SUCCESS;
  494. m_eCommandType = VSS_CMD_USAGE;
  495. }
  496. void CVssAdminCLI::DoProcessing(
  497. IN CVssFunctionTracer& ft
  498. ) throw(HRESULT)
  499. {
  500. switch( m_eCommandType)
  501. {
  502. case VSS_CMD_USAGE:
  503. PrintUsage(ft);
  504. break;
  505. case VSS_CMD_LIST:
  506. switch (m_eListType)
  507. {
  508. case VSS_LIST_SNAPSHOTS:
  509. ListSnapshots(ft);
  510. break;
  511. case VSS_LIST_WRITERS:
  512. ListWriters(ft);
  513. break;
  514. case VSS_LIST_PROVIDERS:
  515. ListProviders(ft);
  516. break;
  517. default:
  518. ft.Throw( VSSDBG_COORD, E_UNEXPECTED,
  519. L"Invalid list type: %d %d",
  520. m_eListType, m_eCommandType);
  521. }
  522. break;
  523. default:
  524. ft.Throw( VSSDBG_COORD, E_UNEXPECTED,
  525. L"Invalid command type: %d", m_eCommandType);
  526. }
  527. }
  528. void CVssAdminCLI::Finalize()
  529. /*++
  530. Description:
  531. Uninitialize the COM library. Called in CVssAdminCLI destructor.
  532. --*/
  533. {
  534. // Uninitialize COM library
  535. CoUninitialize();
  536. }
  537. HRESULT CVssAdminCLI::Main(
  538. IN HINSTANCE hInstance
  539. )
  540. /*++
  541. Function:
  542. CVssAdminCLI::Main
  543. Description:
  544. Static function used as the main entry point in the VSS CLI
  545. --*/
  546. {
  547. CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::Main" );
  548. INT nReturnValue = VSS_CMDRET_ERROR;
  549. try
  550. {
  551. CVssAdminCLI program(hInstance);
  552. try
  553. {
  554. // Initialize the program. This calls CoInitialize()
  555. program.Initialize(ft);
  556. // Parse the command line
  557. program.ParseCmdLine(ft);
  558. // Do the work...
  559. program.DoProcessing(ft);
  560. }
  561. VSS_STANDARD_CATCH(ft)
  562. // Prints the error, if any
  563. if (ft.HrFailed())
  564. program.Output( ft, IDS_ERROR, ft.hr );
  565. nReturnValue = program.GetReturnValue();
  566. // The destructor automatically calls CoUninitialize()
  567. }
  568. VSS_STANDARD_CATCH(ft)
  569. return nReturnValue;
  570. }
  571. /////////////////////////////////////////////////////////////////////////////
  572. // WinMain
  573. extern "C" int WINAPI _tWinMain(
  574. IN HINSTANCE hInstance,
  575. IN HINSTANCE /*hPrevInstance*/,
  576. IN LPTSTR /*lpCmdLine*/,
  577. IN int /*nShowCmd*/)
  578. {
  579. return CVssAdminCLI::Main(hInstance);
  580. }