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.
 
 
 
 
 
 

963 lines
34 KiB

/*++
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 <locale.h>
#include <winnlsp.h> // 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<IVssCoordinator> 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<IVssEnumObject> 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<IVssCoordinator> 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<IVssEnumObject> 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"<MISSING>" );
}
// 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);
}