/*++ Copyright (c) 1999 Microsoft Corporation Abstract: @doc @module vssadmin.cpp | Implementation of the Volume Snapshots demo @end Author: Adi Oltean [aoltean] 09/17/1999 TBD: Add comments. Revision History: Name Date Comments aoltean 09/17/1999 Created --*/ ///////////////////////////////////////////////////////////////////////////// // Includes // The rest of includes are specified here #include "vssadmin.h" #include "versionspecific.h" #include "commandverifier.h" #include #include // in public\internal\base\inc BOOL AssertPrivilege( IN LPCWSTR privName ); //////////////////////////////////////////////////////////////////////// // Standard foo for file name aliasing. This code block must be after // all includes of VSS header files. // #ifdef VSS_FILE_ALIAS #undef VSS_FILE_ALIAS #endif #define VSS_FILE_ALIAS "ADMVADMC" // //////////////////////////////////////////////////////////////////////// // // List of hard coded option names. If you add options, make sure to keep this // list in alphabetical order. // const SVssAdmOption g_asAdmOptions[] = { { VSSADM_O_ALL, L"All", VSSADM_OT_BOOL }, { VSSADM_O_AUTORETRY, L"AutoRetry", VSSADM_OT_NUM }, { VSSADM_O_EXPOSE_USING, L"ExposeUsing", VSSADM_OT_STR }, { VSSADM_O_FOR, L"For", VSSADM_OT_STR }, { VSSADM_O_MAXSIZE, L"MaxSize", VSSADM_OT_NUM }, { VSSADM_O_OLDEST, L"Oldest", VSSADM_OT_BOOL }, { VSSADM_O_ON, L"On", VSSADM_OT_STR }, { VSSADM_O_PROVIDER, L"Provider", VSSADM_OT_STR }, { VSSADM_O_QUIET, L"Quiet", VSSADM_OT_BOOL }, { VSSADM_O_SET, L"Set", VSSADM_OT_STR }, { VSSADM_O_SHAREPATH, L"SharePath", VSSADM_OT_STR }, { VSSADM_O_SNAPSHOT, L"Shadow", VSSADM_OT_STR }, { VSSADM_O_SNAPTYPE, L"Type", VSSADM_OT_STR }, { VSSADM_O_INVALID, NULL, VSSADM_OT_BOOL } }; // // List of vssadmin commands. Keep this in alphabetical order. Also, keep the option flags in the same order as // the EVssAdmOption and g_asAdmOptions. // const SVssAdmCommandsEntry g_asAdmCommands[] = { // Major Minor Option SKUs MsgGen MsgDetail bShowSSTypes All AutoRtry ExpUsing For MaxSize Oldest On Provider Quiet Set ShrePath Snapshot Type { L"Add", L"ShadowStorage", VSSADM_C_ADD_DIFFAREA_INT, SKU_INT, MSG_USAGE_GEN_ADD_DIFFAREA, MSG_USAGE_DTL_ADD_DIFFAREA_INT, FALSE, { V_NO, V_NO, V_NO, V_YES, V_OPT, V_NO, V_YES, V_OPT, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"Add", L"ShadowStorage", VSSADM_C_ADD_DIFFAREA_PUB, SKU_SN, MSG_USAGE_GEN_ADD_DIFFAREA, MSG_USAGE_DTL_ADD_DIFFAREA_PUB, FALSE, { V_NO, V_NO, V_NO, V_YES, V_OPT, V_NO, V_YES, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"Create", L"Shadow", VSSADM_C_CREATE_SNAPSHOT_INT, SKU_INT, MSG_USAGE_GEN_CREATE_SNAPSHOT, MSG_USAGE_DTL_CREATE_SNAPSHOT_INT, TRUE, { V_NO, V_OPT, V_NO, V_YES, V_NO, V_NO, V_NO, V_OPT, V_NO, V_NO, V_NO, V_NO, V_YES } }, { L"Create", L"Shadow", VSSADM_C_CREATE_SNAPSHOT_PUB, SKU_SN, MSG_USAGE_GEN_CREATE_SNAPSHOT, MSG_USAGE_DTL_CREATE_SNAPSHOT_PUB, FALSE, { V_NO, V_OPT, V_NO, V_YES, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"Delete", L"Shadows", VSSADM_C_DELETE_SNAPSHOTS_INT, SKU_INT, MSG_USAGE_GEN_DELETE_SNAPSHOTS, MSG_USAGE_DTL_DELETE_SNAPSHOTS_INT, TRUE, { V_OPT, V_NO, V_NO, V_OPT, V_NO, V_OPT, V_NO, V_NO, V_OPT, V_NO, V_NO, V_OPT, V_OPT } }, { L"Delete", L"Shadows", VSSADM_C_DELETE_SNAPSHOTS_PUB, SKU_SN, MSG_USAGE_GEN_DELETE_SNAPSHOTS, MSG_USAGE_DTL_DELETE_SNAPSHOTS_PUB, FALSE, { V_OPT, V_NO, V_NO, V_OPT, V_NO, V_OPT, V_NO, V_NO, V_OPT, V_NO, V_NO, V_OPT, V_NO } }, { L"Delete", L"ShadowStorage", VSSADM_C_DELETE_DIFFAREAS_INT, SKU_INT, MSG_USAGE_GEN_DELETE_DIFFAREAS, MSG_USAGE_DTL_DELETE_DIFFAREAS_INT, FALSE, { V_NO, V_NO, V_NO, V_YES, V_NO, V_NO, V_OPT, V_OPT, V_OPT, V_NO, V_NO, V_NO, V_NO } }, { L"Delete", L"ShadowStorage", VSSADM_C_DELETE_DIFFAREAS_PUB, SKU_SN, MSG_USAGE_GEN_DELETE_DIFFAREAS, MSG_USAGE_DTL_DELETE_DIFFAREAS_PUB, FALSE, { V_NO, V_NO, V_NO, V_YES, V_NO, V_NO, V_OPT, V_NO, V_OPT, V_NO, V_NO, V_NO, V_NO } }, { L"Expose", L"Shadow", VSSADM_C_EXPOSE_SNAPSHOT, SKU_INT, MSG_USAGE_GEN_EXPOSE_SNAPSHOT, MSG_USAGE_DTL_EXPOSE_SNAPSHOT, FALSE, { V_NO, V_NO, V_OPT, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_OPT, V_YES, V_NO } }, { L"List", L"Providers", VSSADM_C_LIST_PROVIDERS, SKU_A, MSG_USAGE_GEN_LIST_PROVIDERS, MSG_USAGE_DTL_LIST_PROVIDERS, FALSE, { V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"List", L"Shadows", VSSADM_C_LIST_SNAPSHOTS_INT, SKU_INT, MSG_USAGE_GEN_LIST_SNAPSHOTS, MSG_USAGE_DTL_LIST_SNAPSHOTS_INT, TRUE, { V_NO, V_NO, V_NO, V_OPT, V_NO, V_NO, V_NO, V_OPT, V_NO, V_OPT, V_NO, V_OPT, V_OPT } }, { L"List", L"Shadows", VSSADM_C_LIST_SNAPSHOTS_PUB, SKU_A, MSG_USAGE_GEN_LIST_SNAPSHOTS, MSG_USAGE_DTL_LIST_SNAPSHOTS_PUB, FALSE, { V_NO, V_NO, V_NO, V_OPT, V_NO, V_NO, V_NO, V_NO, V_NO, V_OPT, V_NO, V_OPT, V_NO } }, { L"List", L"ShadowStorage", VSSADM_C_LIST_DIFFAREAS_INT, SKU_INT, MSG_USAGE_GEN_LIST_DIFFAREAS, MSG_USAGE_DTL_LIST_DIFFAREAS_INT, FALSE, { V_NO, V_NO, V_NO, V_OPT, V_NO, V_NO, V_OPT, V_OPT, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"List", L"ShadowStorage", VSSADM_C_LIST_DIFFAREAS_PUB, SKU_SN, MSG_USAGE_GEN_LIST_DIFFAREAS, MSG_USAGE_DTL_LIST_DIFFAREAS_PUB, FALSE, { V_NO, V_NO, V_NO, V_OPT, V_NO, V_NO, V_OPT, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"List", L"Volumes", VSSADM_C_LIST_VOLUMES_INT, SKU_INT, MSG_USAGE_GEN_LIST_VOLUMES, MSG_USAGE_DTL_LIST_VOLUMES_INT, TRUE, { V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_OPT, V_NO, V_NO, V_NO, V_NO, V_OPT } }, { L"List", L"Volumes", VSSADM_C_LIST_VOLUMES_PUB, SKU_A, MSG_USAGE_GEN_LIST_VOLUMES, MSG_USAGE_DTL_LIST_VOLUMES_PUB, FALSE, { V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"List", L"Writers", VSSADM_C_LIST_WRITERS, SKU_A, MSG_USAGE_GEN_LIST_WRITERS, MSG_USAGE_DTL_LIST_WRITERS, FALSE, { V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"Resize", L"ShadowStorage", VSSADM_C_RESIZE_DIFFAREA_INT, SKU_INT, MSG_USAGE_GEN_RESIZE_DIFFAREA, MSG_USAGE_DTL_RESIZE_DIFFAREA_INT, FALSE, { V_NO, V_NO, V_NO, V_YES, V_OPT, V_NO, V_YES, V_OPT, V_NO, V_NO, V_NO, V_NO, V_NO } }, { L"Resize", L"ShadowStorage", VSSADM_C_RESIZE_DIFFAREA_PUB, SKU_SN, MSG_USAGE_GEN_RESIZE_DIFFAREA, MSG_USAGE_DTL_RESIZE_DIFFAREA_PUB, FALSE, { V_NO, V_NO, V_NO, V_YES, V_OPT, V_NO, V_YES, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } }, { NULL, NULL, VSSADM_C_NUM_COMMANDS, 0, 0, 0, FALSE, { V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO, V_NO } } }; // // List of snapshot types that are supported by the command line // const SVssAdmSnapshotTypeName g_asAdmTypeNames[]= { { L"ClientAccessible", SKU_SNI, VSS_CTX_CLIENT_ACCESSIBLE, MSG_TYPE_DESCRIPTION_CLIENTACCESSIBLE}, { L"DataVolumeRollback", SKU_SNI, VSS_CTX_NAS_ROLLBACK, MSG_TYPE_DESCRIPTION_DATAVOLUMEROLLBACK}, { L"ApplicationRollback", SKU_I, VSS_CTX_APP_ROLLBACK, MSG_TYPE_DESCRIPTION_APPLICATIONROLLBACK}, { L"FileShareRollback", SKU_I, VSS_CTX_FILE_SHARE_BACKUP, MSG_TYPE_DESCRIPTION_FILESHAREROLLBACK}, { L"Backup", SKU_I, VSS_CTX_BACKUP, MSG_TYPE_DESCRIPTION_BACKUP}, { NULL, 0, 0, 0 } }; ///////////////////////////////////////////////////////////////////////////// // Implementation CVssAdminCLI::CVssAdminCLI( IN INT argc, IN PWSTR argv[] ) /*++ Description: Standard constructor. Initializes internal members --*/ { m_argc = argc; m_argv = argv; m_eFilterObjectType = VSS_OBJECT_UNKNOWN; m_eListedObjectType = VSS_OBJECT_UNKNOWN; m_FilterSnapshotId = GUID_NULL; m_nReturnValue = VSS_CMDRET_ERROR; m_hConsoleOutput = INVALID_HANDLE_VALUE; m_pMapVolumeNames = NULL; m_pVerifier = NULL; } CVssAdminCLI::~CVssAdminCLI() /*++ Description: Standard destructor. Calls Finalize and eventually frees the memory allocated by internal members. --*/ { // Release the cached resource strings for( int nIndex = 0; nIndex < m_mapCachedResourceStrings.GetSize(); nIndex++) { LPCWSTR& pwszResString = m_mapCachedResourceStrings.GetValueAt(nIndex); ::VssFreeString(pwszResString); } // Release the cached provider names for( int nIndex = 0; nIndex < m_mapCachedProviderNames.GetSize(); nIndex++) { LPCWSTR& pwszProvName = m_mapCachedProviderNames.GetValueAt(nIndex); ::VssFreeString(pwszProvName); } // Release the volume name map if necessary if ( m_pMapVolumeNames != NULL) { for( int nIndex = 0; nIndex < m_pMapVolumeNames->GetSize(); nIndex++) { LPCWSTR& pwsz = m_pMapVolumeNames->GetValueAt(nIndex); ::VssFreeString(pwsz); pwsz = m_pMapVolumeNames->GetKeyAt(nIndex); ::VssFreeString(pwsz); } delete m_pMapVolumeNames; } delete m_pVerifier; // Uninitialize the COM library Finalize(); } ///////////////////////////////////////////////////////////////////////////// // Implementation void CVssAdminCLI::GetProviderId( OUT VSS_ID *pProviderId ) { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::GetProviderId" ); // If a provider is not specified, always return babbage if (g_asAdmCommands[ m_sParsedCommand.eAdmCmd].aeOptionFlags[VSSADM_O_PROVIDER] == V_NO) { *pProviderId = VSS_SWPRV_ProviderId; return; } *pProviderId = GUID_NULL; // // If user specified a provider, process option // LPCWSTR pwszProvider = GetOptionValueStr( VSSADM_O_PROVIDER ); if (pwszProvider != NULL ) { // // Determine if this is an ID or a name // if ( !ScanGuid( pwszProvider, *pProviderId ) ) { // Have a provider name, look it up if ( !GetProviderIdByName( pwszProvider, pProviderId ) ) { // Provider name not found, print error OutputErrorMsg( MSG_ERROR_PROVIDER_NAME_NOT_FOUND, pwszProvider ); // Throw S_OK since the error message has already been output ft.Throw( VSSDBG_VSSADMIN, S_OK, L"Already printed error message" ); } } } } LPCWSTR CVssAdminCLI::GetProviderName( IN VSS_ID& ProviderId ) throw(HRESULT) { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::GetProviderName" ); LPCWSTR wszReturnedString = m_mapCachedProviderNames.Lookup(ProviderId); if (wszReturnedString) return wszReturnedString; CComPtr pICoord; ft.CoCreateInstanceWithLog( VSSDBG_VSSADMIN, CLSID_VSSCoordinator, L"Coordinator", CLSCTX_ALL, IID_IVssCoordinator, (IUnknown**)&(pICoord)); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Connection failed with hr = 0x%08lx", ft.hr); CComPtr pIEnumProvider; ft.hr = pICoord->Query( GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_PROVIDER, &pIEnumProvider ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Query failed with hr = 0x%08lx", ft.hr); VSS_OBJECT_PROP Prop; VSS_PROVIDER_PROP& Prov = Prop.Obj.Prov; // Go through the list of providers to find the one we are interested in. ULONG ulFetched; while( 1 ) { ft.hr = pIEnumProvider->Next( 1, &Prop, &ulFetched ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Next failed with hr = 0x%08lx", ft.hr); if (ft.hr == S_FALSE) { // End of enumeration. // Provider not registered? Where did this snapshot come from? // It might be still possible if a snapshot provider was deleted // before querying the snapshot provider but after the snapshot attributes // were queried. BS_ASSERT(ulFetched == 0); return LoadString( IDS_UNKNOWN_PROVIDER ); } ::VssFreeString( Prov.m_pwszProviderVersion ); if (Prov.m_ProviderId == ProviderId) { break; } ::VssFreeString( Prov.m_pwszProviderName ); } // Auto delete the string CVssAutoPWSZ awszProviderName( Prov.m_pwszProviderName ); // Duplicate the new string LPWSTR wszNewString = NULL; BS_ASSERT( (LPCWSTR)awszProviderName != NULL ); ::VssSafeDuplicateStr( ft, wszNewString, awszProviderName ); wszReturnedString = wszNewString; // Save the string in the cache, transfer of pointer ownership. if (!m_mapCachedProviderNames.Add( ProviderId, wszReturnedString )) { ::VssFreeString( wszReturnedString ); ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error"); } return wszReturnedString; } BOOL CVssAdminCLI::GetProviderIdByName( IN LPCWSTR pwszProviderName, OUT VSS_ID *pProviderId ) throw(HRESULT) { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::GetProviderIdByName" ); CComPtr pICoord; ft.CoCreateInstanceWithLog( VSSDBG_VSSADMIN, CLSID_VSSCoordinator, L"Coordinator", CLSCTX_ALL, IID_IVssCoordinator, (IUnknown**)&(pICoord)); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Connection failed with hr = 0x%08lx", ft.hr); CComPtr pIEnumProvider; ft.hr = pICoord->Query( GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_PROVIDER, &pIEnumProvider ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Query failed with hr = 0x%08lx", ft.hr); VSS_OBJECT_PROP Prop; VSS_PROVIDER_PROP& Prov = Prop.Obj.Prov; // Go through the list of providers to find the one we are interested in. ULONG ulFetched; while( 1 ) { ft.hr = pIEnumProvider->Next( 1, &Prop, &ulFetched ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Next failed with hr = 0x%08lx", ft.hr); if (ft.hr == S_FALSE) { // End of enumeration. // Provider not registered? Where did this snapshot come from? // It might be still possible if a snapshot provider was deleted // before querying the snapshot provider but after the snapshot attributes // were queried. BS_ASSERT(ulFetched == 0); *pProviderId = GUID_NULL; return FALSE; } ::VssFreeString( Prov.m_pwszProviderVersion ); if (::_wcsicmp( Prov.m_pwszProviderName, pwszProviderName) == 0) { ::VssFreeString( Prov.m_pwszProviderName ); break; } ::VssFreeString( Prov.m_pwszProviderName ); } *pProviderId = Prov.m_ProviderId; return TRUE; } ///////////////////////////////////////////////////////////////////////////// // Implementation void CVssAdminCLI::Initialize( ) throw(HRESULT) /*++ Description: Initializes the COM library. Called explicitely after instantiating the CVssAdminCLI object. --*/ { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::Initialize" ); // Use the OEM code page ... ::setlocale(LC_ALL, ".OCP"); // Use the console UI language ::SetThreadUILanguage( 0 ); // // Use only the Console routines to output messages. To do so, need to open standard // output. // m_hConsoleOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); if (m_hConsoleOutput == INVALID_HANDLE_VALUE) { ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ), L"Initialize - Error from GetStdHandle(), rc: %d", ::GetLastError() ); } // Initialize COM library ft.hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (ft.HrFailed()) ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"Failure in initializing the COM library 0x%08lx", ft.hr); // Initialize COM security ft.hr = CoInitializeSecurity( NULL, // IN PSECURITY_DESCRIPTOR pSecDesc, -1, // IN LONG cAuthSvc, NULL, // IN SOLE_AUTHENTICATION_SERVICE *asAuthSvc, NULL, // IN void *pReserved1, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // IN DWORD dwAuthnLevel, RPC_C_IMP_LEVEL_IDENTIFY, // IN DWORD dwImpLevel, NULL, // IN void *pAuthList, EOAC_NONE, // IN DWORD dwCapabilities, NULL // IN void *pReserved3 ); if (ft.HrFailed()) { ft.Throw( VSSDBG_VSSADMIN, ft.hr, L" Error: CoInitializeSecurity() returned 0x%08lx", ft.hr ); } // Turns off SEH exception handing for COM servers (BUG# 530092) ft.ComDisableSEH(VSSDBG_VSSADMIN); // // Assert the Backup privilage. Not worried about errors here since VSS will // return access denied return codes if the user doesn't have permission. // (void)::AssertPrivilege (SE_BACKUP_NAME); // Create an instance of the parameter checker m_pVerifier = CCommandVerifier::Instance(); if (m_pVerifier == NULL) ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY, L"Out of memory" ); // Print the header OutputMsg( MSG_UTILITY_HEADER ); } // // Returns true if the command line was parsed fine // BOOL CVssAdminCLI::ParseCmdLine( ) throw(HRESULT) /*++ Description: Parses the command line. --*/ { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::ParseCmdLine" ); // Skip the executable name GetNextCmdlineToken( true ); // Get the first token after the executable name LPCWSTR pwszMajor = GetNextCmdlineToken(); LPCWSTR pwszMinor = NULL; if ( pwszMajor != NULL ) { if ( ::wcscmp( pwszMajor, L"/?" ) == 0 || ::wcscmp( pwszMajor, L"-?" ) == 0 ) return FALSE; pwszMinor = GetNextCmdlineToken(); } if ( pwszMajor == NULL || pwszMinor == NULL ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_COMMAND, L"Incomplete command"); } INT idx; // See if the command is found in list of commands for ( idx = VSSADM_C_FIRST; idx < VSSADM_C_NUM_COMMANDS; ++idx ) { if ( ( dCurrentSKU & g_asAdmCommands[idx].dwSKUs ) && Match( pwszMajor, g_asAdmCommands[idx].pwszMajorOption ) && Match( pwszMinor, g_asAdmCommands[idx].pwszMinorOption ) ) { // // Got a match // break; } } if ( idx == VSSADM_C_NUM_COMMANDS ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_COMMAND, L"Invalid command"); } // // Found the command. // m_eCommandType = ( EVssAdmCommand )idx; m_sParsedCommand.eAdmCmd = ( EVssAdmCommand )idx; // // Now need to process command line options // LPCWSTR pwszOption = GetNextCmdlineToken(); while ( pwszOption != NULL ) { if ( pwszOption[0] == L'/' || pwszOption[0] == L'-' ) { // // Got a named option, now see if it is a valid option // for the command. // // Skip past delimiter ++pwszOption; // // See if they want usage // if ( pwszOption[0] == L'?' ) return FALSE; // Parse out the value part of the named option LPWSTR pwszValue = ::wcschr( pwszOption, L'=' ); if ( pwszValue != NULL ) { // Replace = with NULL char and set value to point to string after the = pwszValue[0] = L'\0'; ++pwszValue; } // At this point, if pwszValue == NULL, it means the option had no = and so no specified value. // If pwszValue[0] == L'\0', then the value is an empty value INT eOpt; // Now figure out which named option this is for ( eOpt = VSSADM_O_FIRST; eOpt < VSSADM_O_NUM_OPTIONS; ++eOpt ) { if ( Match( g_asAdmOptions[eOpt].pwszOptName, pwszOption ) ) break; } // See if this is a bogus option if ( eOpt == VSSADM_O_NUM_OPTIONS ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_OPTION, L"Invalid option: %s", pwszOption); } // See if this option has already been specified if ( m_sParsedCommand.apwszOptionValues[eOpt] != NULL ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_DUPLICATE_OPTION, L"Duplicate option given: %s", pwszOption); } // See if this option is allowed for the command if ( g_asAdmCommands[ m_sParsedCommand.eAdmCmd ].aeOptionFlags[ eOpt ] == V_NO ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_OPTION_NOT_ALLOWED_FOR_COMMAND, L"Option not allowed for this command: %s", pwszOption); } // See if this option is supposed to have a value, BOOL options do not if ( ( g_asAdmOptions[eOpt].eOptType == VSSADM_OT_BOOL && pwszValue != NULL ) || ( g_asAdmOptions[eOpt].eOptType != VSSADM_OT_BOOL && ( pwszValue == NULL || pwszValue[0] == L'\0' ) ) ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_OPTION_VALUE, L"Invalid option value: /%s=%s", pwszOption, pwszValue ? pwszValue : L"" ); } // Finally, we have a valid option, save away the option value. // See if it is a boolean type. In the option array we store the wszVssOptBoolTrue string. // The convention is if the option is NULL, then the boolean option is false, else it // is true. if ( g_asAdmOptions[eOpt].eOptType == VSSADM_OT_BOOL ) ::VssSafeDuplicateStr( ft, m_sParsedCommand.apwszOptionValues[eOpt], x_wszVssOptBoolTrue ); else ::VssSafeDuplicateStr( ft, m_sParsedCommand.apwszOptionValues[eOpt], pwszValue ); } else { // // Got an unnamed option, not valid in any command // ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_INVALID_COMMAND, L"Invalid command"); } pwszOption = GetNextCmdlineToken(); } // We are done parsing the command-line. Now see if any mandatory named options were missing for ( idx = VSSADM_O_FIRST; idx < VSSADM_O_NUM_OPTIONS; ++idx ) { if ( ( m_sParsedCommand.apwszOptionValues[idx] == NULL ) && ( g_asAdmCommands[ m_sParsedCommand.eAdmCmd ].aeOptionFlags[ idx ] == V_YES ) ) { ft.Throw( VSSDBG_VSSADMIN, VSSADM_E_REQUIRED_OPTION_MISSING, L"Required option missing"); } } // // Now fix up certain options if needed // LPWSTR pwszStr; // Need a \ at the end of the FOR option pwszStr = m_sParsedCommand.apwszOptionValues[ VSSADM_O_FOR ]; if ( pwszStr != NULL ) { if ( pwszStr[ ::wcslen( pwszStr ) - 1 ] != L'\\' ) { pwszStr = ::VssReallocString( ft, pwszStr, (LONG)::wcslen( pwszStr ) + 1 ); ::wcscat( pwszStr, L"\\" ); m_sParsedCommand.apwszOptionValues[ VSSADM_O_FOR ] = pwszStr; } } // Need a \ at the end of the ON option pwszStr = m_sParsedCommand.apwszOptionValues[ VSSADM_O_ON ]; if ( pwszStr != NULL ) { if ( pwszStr[ ::wcslen( pwszStr ) - 1 ] != L'\\' ) { pwszStr = ::VssReallocString( ft, pwszStr, (LONG)::wcslen( pwszStr ) + 1 ); ::wcscat( pwszStr, L"\\" ); m_sParsedCommand.apwszOptionValues[ VSSADM_O_ON ] = pwszStr; } } return TRUE; } void CVssAdminCLI::DoProcessing( ) throw(HRESULT) { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::DoProcessing" ); switch( m_sParsedCommand.eAdmCmd ) { case VSSADM_C_CREATE_SNAPSHOT_INT: case VSSADM_C_CREATE_SNAPSHOT_PUB: CreateSnapshot(); break; case VSSADM_C_LIST_PROVIDERS: ListProviders(); break; case VSSADM_C_LIST_SNAPSHOTS_INT: case VSSADM_C_LIST_SNAPSHOTS_PUB: ListSnapshots(); break; case VSSADM_C_LIST_WRITERS: ListWriters(); break; case VSSADM_C_ADD_DIFFAREA_INT: case VSSADM_C_ADD_DIFFAREA_PUB: AddDiffArea(); break; case VSSADM_C_RESIZE_DIFFAREA_INT: case VSSADM_C_RESIZE_DIFFAREA_PUB: ResizeDiffArea(); break; case VSSADM_C_DELETE_DIFFAREAS_INT: case VSSADM_C_DELETE_DIFFAREAS_PUB: DeleteDiffAreas(); break; case VSSADM_C_LIST_DIFFAREAS_INT: case VSSADM_C_LIST_DIFFAREAS_PUB: ListDiffAreas(); break; case VSSADM_C_DELETE_SNAPSHOTS_INT: case VSSADM_C_DELETE_SNAPSHOTS_PUB: DeleteSnapshots(); break; case VSSADM_C_EXPOSE_SNAPSHOT: ExposeSnapshot(); break; case VSSADM_C_LIST_VOLUMES_INT: case VSSADM_C_LIST_VOLUMES_PUB: ListVolumes(); break; default: ft.Throw( VSSDBG_COORD, E_UNEXPECTED, L"Invalid command type: %d", m_eCommandType); } } void CVssAdminCLI::Finalize() /*++ Description: Uninitialize the COM library. Called in CVssAdminCLI destructor. --*/ { // Uninitialize COM library CoUninitialize(); } HRESULT CVssAdminCLI::Main( IN INT argc, IN PWSTR argv[] ) /*++ Function: CVssAdminCLI::Main Description: Static function used as the main entry point in the VSS CLI --*/ { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::Main" ); INT nReturnValue = VSS_CMDRET_ERROR; try { CVssAdminCLI program(argc, argv); try { // Initialize the program. This calls CoInitialize() program.Initialize(); // Parse the command line if ( program.ParseCmdLine() ) { // Do the work... program.DoProcessing(); } else { // Error parsing the command line, print out usage program.PrintUsage(); } ft.hr = S_OK; // Assume that the above methods printed error // messages if there was an error. } VSS_STANDARD_CATCH(ft) nReturnValue = program.GetReturnValue(); // // Log the error if this is create snapshot // if ( ft.hr != S_OK && (program.m_eCommandType == VSSADM_C_CREATE_SNAPSHOT_INT || program.m_eCommandType == VSSADM_C_CREATE_SNAPSHOT_PUB) && !UnloggableError(ft.hr)) { // // Log error message // LPWSTR pwszSnapshotErrMsg; pwszSnapshotErrMsg = program.GetMsg( FALSE, MSG_ERROR_UNABLE_TO_CREATE_SNAPSHOT ); if ( pwszSnapshotErrMsg == NULL ) { ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Error on loading the message string id %d. 0x%08lx", MSG_ERROR_UNABLE_TO_CREATE_SNAPSHOT, ::GetLastError() ); } LONG lMsgNum; LPWSTR pwszMsg = NULL; if ( ::MapVssErrorToMsg( ft.hr, &lMsgNum ) ) { pwszMsg = program.GetMsg( FALSE, lMsgNum ); if ( pwszMsg == NULL ) { ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Error on loading the message string id %d. 0x%08lx", lMsgNum, ::GetLastError() ); } ft.LogError( VSS_ERROR_VSSADMIN_ERROR, VSSDBG_VSSADMIN << pwszSnapshotErrMsg << pwszMsg << ::GetCommandLineW() ); ::VssFreeString( pwszMsg ); } else { // Try to get the system error message pwszMsg = program.GetMsg( FALSE, ft.hr ); if ( pwszMsg != NULL ) { ft.LogError( VSS_ERROR_VSSADMIN_ERROR, VSSDBG_VSSADMIN << pwszSnapshotErrMsg << pwszMsg << ::GetCommandLineW() ); ::VssFreeString( pwszMsg ); } else { WCHAR wszHr[64]; StringCchPrintfW( STRING_CCH_PARAM(wszHr), L"hr = 0x%08x", ft.hr ); ft.LogError( VSS_ERROR_VSSADMIN_ERROR, VSSDBG_VSSADMIN << pwszSnapshotErrMsg << wszHr << ::GetCommandLineW() ); } } ::VssFreeString( pwszSnapshotErrMsg ); } // // Print the error on the display, if any // if ( ft.hr != S_OK ) { LONG lMsgNum; // If the error is empty query, print out a message stating that if ( ft.hr == VSSADM_E_NO_ITEMS_IN_QUERY ) { nReturnValue = VSS_CMDRET_EMPTY_RESULT; program.OutputMsg( MSG_ERROR_NO_ITEMS_FOUND ); } else if ( ::MapVssErrorToMsg(ft.hr, &lMsgNum ) ) { // This is a parsing or VSS error, map it to a msg id program.OutputErrorMsg( lMsgNum ); if ( ft.hr >= VSSADM_E_FIRST_PARSING_ERROR && ft.hr <= VSSADM_E_LAST_PARSING_ERROR ) { program.PrintUsage(); } } else { // Unhandled error, try to get the error string from the system LPWSTR pwszMsg; // Try to get the system error message pwszMsg = program.GetMsg( FALSE, ft.hr ); if ( pwszMsg != NULL ) { program.OutputMsg( MSG_ERROR_UNEXPECTED_WITH_STRING, pwszMsg ); ::VssFreeString( pwszMsg ); } else { program.OutputMsg( MSG_ERROR_UNEXPECTED_WITH_HRESULT, ft.hr ); } } } // The destructor automatically calls CoUninitialize() } VSS_STANDARD_CATCH(ft) return nReturnValue; } BOOL AssertPrivilege( IN LPCWSTR privName ) { HANDLE tokenHandle; BOOL stat = FALSE; if ( ::OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &tokenHandle)) { LUID value; if ( ::LookupPrivilegeValueW( NULL, privName, &value ) ) { TOKEN_PRIVILEGES newState; DWORD error; newState.PrivilegeCount = 1; newState.Privileges[0].Luid = value; newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; /* * We will always call GetLastError below, so clear * any prior error values on this thread. */ ::SetLastError( ERROR_SUCCESS ); stat = ::AdjustTokenPrivileges (tokenHandle, FALSE, &newState, (DWORD)0, NULL, NULL ); /* * Supposedly, AdjustTokenPriveleges always returns TRUE * (even when it fails). So, call GetLastError to be * extra sure everything's cool. */ if ( (error = ::GetLastError()) != ERROR_SUCCESS ) { stat = FALSE; } } DWORD cbTokens; ::GetTokenInformation (tokenHandle, TokenPrivileges, NULL, 0, &cbTokens); TOKEN_PRIVILEGES *pTokens = (TOKEN_PRIVILEGES *) new BYTE[cbTokens]; ::GetTokenInformation (tokenHandle, TokenPrivileges, pTokens, cbTokens, &cbTokens); delete pTokens; ::CloseHandle( tokenHandle ); } return stat; } ///////////////////////////////////////////////////////////////////////////// // WinMain extern "C" INT __cdecl wmain( IN INT argc, IN PWSTR argv[] ) { return CVssAdminCLI::Main(argc, argv); }