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
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);
|
|
}
|
|
|
|
|