1179 lines
40 KiB
C++
1179 lines
40 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// Copyright © Microsoft Corporation. All rights reserved.
|
|
//
|
|
// This file may contain preliminary information or inaccuracies,
|
|
// and may not correctly represent any associated Microsoft
|
|
// Product as commercially released. All Materials are provided entirely
|
|
// “AS IS.” To the extent permitted by law, MICROSOFT MAKES NO
|
|
// WARRANTY OF ANY KIND, DISCLAIMS ALL EXPRESS, IMPLIED AND STATUTORY
|
|
// WARRANTIES, AND ASSUMES NO LIABILITY TO YOU FOR ANY DAMAGES OF
|
|
// ANY TYPE IN CONNECTION WITH THESE MATERIALS OR ANY INTELLECTUAL PROPERTY IN THEM.
|
|
//
|
|
|
|
|
|
// Main header
|
|
#include "stdafx.h"
|
|
#include <VersionHelpers.h>
|
|
|
|
bool OSVersionCheck()
|
|
{
|
|
bool bIsWin8 = FALSE;
|
|
bIsWin8 = IsWindows8OrGreater();
|
|
return !bIsWin8;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// Main routine
|
|
//
|
|
// Return values:
|
|
// 0 - Success
|
|
// 1 - Object not found
|
|
// 2 - Runtime Error
|
|
// 3 - Memory allocation error
|
|
//
|
|
extern "C" int __cdecl wmain(__in int argc, __in_ecount(argc) WCHAR ** argv)
|
|
{
|
|
(void)HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
|
|
|
FunctionTracer ft(DBG_INFO);
|
|
CommandLineParser obj;
|
|
|
|
try
|
|
{
|
|
if (OSVersionCheck())
|
|
{
|
|
ft.WriteLine(L"This version of vshadow is not supported on this version of Windows." );
|
|
return 2;
|
|
}
|
|
|
|
ft.WriteLine(
|
|
L"\n"
|
|
L"VSHADOW.EXE 3.0 - Volume Shadow Copy sample client.\n"
|
|
L"Copyright (C) 2005 Microsoft Corporation. All rights reserved.\n"
|
|
L"\n"
|
|
);
|
|
|
|
// Build the argument vector
|
|
vector<wstring> arguments;
|
|
for(int i = 1; i < argc; i++)
|
|
arguments.push_back(argv[i]);
|
|
|
|
// Process the arguments and execute the main options
|
|
return obj.MainRoutine(arguments);
|
|
}
|
|
catch(bad_alloc ex)
|
|
{
|
|
// Generic STL allocation error
|
|
ft.WriteLine(L"ERROR: Memory allocation error");
|
|
return 3;
|
|
}
|
|
catch(exception ex)
|
|
{
|
|
// We should never get here (unless we have a bug)
|
|
_ASSERTE(false);
|
|
ft.WriteLine(L"ERROR: STL Exception caught: %S", ex.what());
|
|
return 2;
|
|
}
|
|
catch(HRESULT hr)
|
|
{
|
|
ft.Trace(DBG_INFO, L"HRESULT Error caught: 0x%08lx", hr);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Main routine
|
|
// - First, parse the command-line options
|
|
// - Then execute the appropriate client calls
|
|
//
|
|
// WARNING:
|
|
// - The routine does not check for mutually exclusive flags/options
|
|
//
|
|
// On error, this function throws
|
|
//
|
|
int CommandLineParser::MainRoutine(vector<wstring> arguments)
|
|
{
|
|
FunctionTracer ft(DBG_INFO);
|
|
|
|
// Context for the VSS operation
|
|
DWORD dwContext = VSS_CTX_BACKUP;
|
|
|
|
// List of selected writers during the shadow copy creation process
|
|
vector<wstring> excludedWriterList;
|
|
|
|
// List of selected writers during the shadow copy creation process
|
|
vector<wstring> includedWriterList;
|
|
|
|
// the volume for which to delete the oldest shadow copy
|
|
wstring stringVolume;
|
|
|
|
// The script file prefix
|
|
// Non-empty if scripts have to be generated
|
|
wstring stringFileName;
|
|
|
|
// Script to execute between snapshot creation and backup complete
|
|
wstring execCommand;
|
|
|
|
// The backup components document
|
|
wstring xmlBackupComponentsDoc;
|
|
|
|
// Resync destination
|
|
wstring wsSnapIdDest;
|
|
|
|
// Break flags for BreakSnapshotSet, used by -bex
|
|
DWORD dwBreakExFlags = 0;
|
|
|
|
DWORD dwResyncFlags = 0;
|
|
|
|
// Enumerate each argument
|
|
for(unsigned argIndex = 0; argIndex < arguments.size(); argIndex++)
|
|
{
|
|
//
|
|
// Flags
|
|
//
|
|
|
|
#ifdef VSS_SERVER
|
|
|
|
// Check for the persistent flag
|
|
if (MatchArgument(arguments[argIndex], L"p"))
|
|
{
|
|
ft.WriteLine(L"(Option: Persistent shadow copy)");
|
|
m_bPersistent = true;
|
|
|
|
// The final dwContext needs to be updated based on m_bPersistent and m_bWithWriters
|
|
continue;
|
|
}
|
|
|
|
// Check for the no-writers flag
|
|
if (MatchArgument(arguments[argIndex], L"nw"))
|
|
{
|
|
ft.WriteLine(L"(Option: No-writers option detected)");
|
|
m_bWithWriters = false;
|
|
|
|
// The final dwContext needs to be updated based on m_bPersistent and m_bWithWriters
|
|
continue;
|
|
}
|
|
|
|
// Check for the no-writers flag
|
|
if (MatchArgument(arguments[argIndex], L"nar"))
|
|
{
|
|
ft.WriteLine(L"(Option: No-auto-recovery option detected)");
|
|
dwContext |= VSS_VOLSNAP_ATTR_NO_AUTORECOVERY;
|
|
continue;
|
|
}
|
|
|
|
// Check for the TxF recovery flag
|
|
if (MatchArgument(arguments[argIndex], L"tr"))
|
|
{
|
|
ft.WriteLine(L"(Option: TxF-recover option detected)");
|
|
dwContext |= VSS_VOLSNAP_ATTR_TXF_RECOVERY;
|
|
continue;
|
|
}
|
|
|
|
// Check for the transportability option
|
|
if (MatchArgument(arguments[argIndex], L"t", xmlBackupComponentsDoc))
|
|
{
|
|
ft.WriteLine(L"(Option: Transportable shadow set. Saving xml to file '%s')", xmlBackupComponentsDoc.c_str());
|
|
dwContext |= VSS_VOLSNAP_ATTR_TRANSPORTABLE;
|
|
continue;
|
|
}
|
|
|
|
// Check for the backup components document option
|
|
if (MatchArgument(arguments[argIndex], L"bc", xmlBackupComponentsDoc))
|
|
{
|
|
ft.WriteLine(L"(Option: Saving xml to file '%s')", xmlBackupComponentsDoc.c_str());
|
|
continue;
|
|
}
|
|
|
|
// Check for the differential flag option
|
|
if (MatchArgument(arguments[argIndex], L"ad"))
|
|
{
|
|
ft.WriteLine(L"(Option: Creating differential HW shadow copies)");
|
|
dwContext |= VSS_VOLSNAP_ATTR_DIFFERENTIAL;
|
|
continue;
|
|
}
|
|
|
|
// Check for the plex flag option
|
|
if (MatchArgument(arguments[argIndex], L"ap"))
|
|
{
|
|
ft.WriteLine(L"(Option: Creating plex HW shadow copies)");
|
|
dwContext |= VSS_VOLSNAP_ATTR_PLEX;
|
|
continue;
|
|
}
|
|
|
|
// Check for the Shadow Copy for Shared Folders (i.e. Client Accessible) flag option
|
|
if (MatchArgument(arguments[argIndex], L"scsf"))
|
|
{
|
|
ft.WriteLine(L"(Option: Creating Shadow Copies for Shared Folders - Client Accessible)");
|
|
dwContext = VSS_CTX_CLIENT_ACCESSIBLE;
|
|
continue;
|
|
}
|
|
|
|
#ifdef TEST_FEATURES
|
|
// Check for the delayed post snapshot flag
|
|
if (MatchArgument(arguments[argIndex], L"dps"))
|
|
{
|
|
ft.WriteLine(L"(Option: Delayed post-snapshot option detected)");
|
|
dwContext |= VSS_VOLSNAP_ATTR_DELAYED_POSTSNAPSHOT;
|
|
|
|
// The final dwContext needs to be updated based on m_bPersistent and m_bWithWriters
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
|
|
#endif
|
|
|
|
// Check for the writer exclusion flag
|
|
wstring excludedWriter;
|
|
if (MatchArgument(arguments[argIndex], L"wx", excludedWriter))
|
|
{
|
|
ft.WriteLine(L"(Option: Excluding writer/component '%s')", excludedWriter.c_str());
|
|
excludedWriterList.push_back(excludedWriter);
|
|
continue;
|
|
}
|
|
|
|
// Check for the writer inclusion flag
|
|
wstring includedWriter;
|
|
if (MatchArgument(arguments[argIndex], L"wi", includedWriter))
|
|
{
|
|
ft.WriteLine(L"(Option: Verifying inclusion of writer/component '%s')", includedWriter.c_str());
|
|
includedWriterList.push_back(includedWriter);
|
|
continue;
|
|
}
|
|
|
|
// Check for the "wait" flag
|
|
if (MatchArgument(arguments[argIndex], L"wait"))
|
|
{
|
|
ft.WriteLine(L"(Option: Wait on finish)");
|
|
m_bWaitForFinish = true;
|
|
continue;
|
|
}
|
|
|
|
// Check for the script generation option
|
|
if (MatchArgument(arguments[argIndex], L"script", stringFileName))
|
|
{
|
|
ft.WriteLine(L"(Option: Generate SETVAR script '%s')", stringFileName.c_str());
|
|
continue;
|
|
}
|
|
|
|
// Check for the command execution option
|
|
if (MatchArgument(arguments[argIndex], L"exec", execCommand))
|
|
{
|
|
ft.WriteLine(L"(Option: Execute binary/script after shadow creation '%s')", execCommand.c_str());
|
|
|
|
// Check if the command is a valid CMD/EXE file
|
|
DWORD dwAttributes = GetFileAttributes((LPWSTR)execCommand.c_str());
|
|
if ((dwAttributes == INVALID_FILE_ATTRIBUTES) || ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
|
|
{
|
|
ft.WriteLine(L"ERROR: the parameter '%s' must be an existing file!", execCommand.c_str());
|
|
ft.WriteLine(L"- Note: the -exec command cannot have parameters!");
|
|
throw(E_INVALIDARG);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Check for the tracing option
|
|
if (MatchArgument(arguments[argIndex], L"tracing"))
|
|
{
|
|
ft.WriteLine(L"(Option: Enable tracing)");
|
|
FunctionTracer::EnableTracingMode();
|
|
continue;
|
|
}
|
|
|
|
|
|
// Checks for BreakEx flags
|
|
|
|
if (MatchArgument(arguments[argIndex], L"mask"))
|
|
{
|
|
ft.WriteLine(L"(Option: Mask shadow copy luns)");
|
|
dwBreakExFlags |= VSS_BREAKEX_FLAG_MASK_LUNS;
|
|
continue;
|
|
}
|
|
|
|
if (MatchArgument(arguments[argIndex], L"rw"))
|
|
{
|
|
ft.WriteLine(L"(Option: make readwrite)");
|
|
dwBreakExFlags |= VSS_BREAKEX_FLAG_MAKE_READ_WRITE;
|
|
continue;
|
|
}
|
|
|
|
if (MatchArgument(arguments[argIndex], L"forcerevert"))
|
|
{
|
|
ft.WriteLine(L"(Option: force disk identity revert)");
|
|
dwBreakExFlags |= VSS_BREAKEX_FLAG_REVERT_IDENTITY_ALL;
|
|
continue;
|
|
}
|
|
|
|
if (MatchArgument(arguments[argIndex], L"norevert"))
|
|
{
|
|
ft.WriteLine(L"(Option: no disk identity revert)");
|
|
dwBreakExFlags |= VSS_BREAKEX_FLAG_REVERT_IDENTITY_NONE;
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Operations
|
|
//
|
|
|
|
// Check for /? or -?
|
|
if (MatchArgument(arguments[argIndex], L"?"))
|
|
break;
|
|
|
|
// Query all shadow copies in the set
|
|
if (MatchArgument(arguments[argIndex], L"q"))
|
|
{
|
|
ft.WriteLine(L"(Option: Query all shadow copies)");
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// List all shadow copies in the system
|
|
m_vssClient.QuerySnapshotSet(GUID_NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Query all shadow copies in the set
|
|
wstring id;
|
|
if (MatchArgument(arguments[argIndex], L"qx", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Query shadow copy set)");
|
|
|
|
GUID queryingSnapshotSetID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// List all shadow copies in the set
|
|
m_vssClient.QuerySnapshotSet(queryingSnapshotSetID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Query the specified shadow copy
|
|
if (MatchArgument(arguments[argIndex], L"s", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Query shadow copy)");
|
|
|
|
VSS_ID queryingSnapshotID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// List all shadow copies in the set
|
|
m_vssClient.GetSnapshotProperties(queryingSnapshotID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Delete all shadow copies in the system
|
|
if (MatchArgument(arguments[argIndex], L"da"))
|
|
{
|
|
ft.WriteLine(L"(Option: Delete all shadow copies)");
|
|
|
|
// Test if the user agrees
|
|
string response;
|
|
cout << "This will delete all shadow copies in the system. Are you sure? [Y/N] ";
|
|
cin >> response;
|
|
if ((response.length() != 1) || ((response[0] != 'Y') && (response[0] != 'y')))
|
|
return 0;
|
|
cout << "\n";
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.DeleteAllSnapshots();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Delete a certain shadow copy set
|
|
if (MatchArgument(arguments[argIndex], L"dx", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Delete a shadow copy set)");
|
|
|
|
VSS_ID deletingSnapshotSetID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.DeleteSnapshotSet(deletingSnapshotSetID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Delete a certain shadow copy
|
|
if (MatchArgument(arguments[argIndex], L"ds", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Delete a shadow copy)");
|
|
|
|
VSS_ID deletingSnapshotID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.DeleteSnapshot(deletingSnapshotID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// List the summary writer metadata
|
|
if (MatchArgument(arguments[argIndex], L"wm"))
|
|
{
|
|
ft.WriteLine(L"(Option: List writer metadata)");
|
|
|
|
// Initialize the VSS client
|
|
dwContext = UpdateFinalContext(dwContext);
|
|
m_vssClient.Initialize(dwContext);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.GatherWriterMetadata();
|
|
|
|
// List summary writer metadata
|
|
m_vssClient.ListWriterMetadata(false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// List the extended writer metadata
|
|
if (MatchArgument(arguments[argIndex], L"wm2"))
|
|
{
|
|
ft.WriteLine(L"(Option: List extended writer metadata)");
|
|
|
|
// Initialize the VSS client
|
|
dwContext = UpdateFinalContext(dwContext);
|
|
m_vssClient.Initialize(dwContext);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.GatherWriterMetadata();
|
|
|
|
// List writer metadata
|
|
m_vssClient.ListWriterMetadata(true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (MatchArgument(arguments[argIndex], L"wm3"))
|
|
{
|
|
ft.WriteLine(L"(Option: List writer metadata in XML)");
|
|
|
|
// Initialize the VSS client
|
|
dwContext = UpdateFinalContext(dwContext);
|
|
m_vssClient.Initialize(dwContext);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.GatherWriterMetadataToScreen();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// List the writer state
|
|
if (MatchArgument(arguments[argIndex], L"ws"))
|
|
{
|
|
ft.WriteLine(L"(Option: List writer status)");
|
|
|
|
// Initialize the VSS client
|
|
dwContext = UpdateFinalContext(dwContext);
|
|
m_vssClient.Initialize(dwContext);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.GatherWriterMetadata();
|
|
|
|
// Gather writer status
|
|
m_vssClient.GatherWriterStatus();
|
|
|
|
// List writer status
|
|
m_vssClient.ListWriterStatus();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef VSS_SERVER
|
|
// Delete the oldest shadow copy for a volume
|
|
if (MatchArgument(arguments[argIndex], L"do", stringVolume))
|
|
{
|
|
ft.WriteLine(L"(Option: Delete the oldest shadow copy for %s)", stringVolume.c_str());
|
|
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
m_vssClient.DeleteOldestSnapshot(stringVolume);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Revert a volume to the specified shadow copy
|
|
if (MatchArgument(arguments[argIndex], L"revert", id))
|
|
{
|
|
ft.WriteLine(L"(revert a shadow copy)");
|
|
|
|
VSS_ID revertingShadowId = WString2Guid(id);
|
|
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
m_vssClient.RevertToSnapshot(revertingShadowId);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Break this shadow copy set to readonly
|
|
if (MatchArgument(arguments[argIndex], L"b", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Break shadow copy set)");
|
|
|
|
VSS_ID breakingSnapshotSetID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// List all shadow copies in the set
|
|
m_vssClient.BreakSnapshotSet(breakingSnapshotSetID, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Break this shadow copy set to writable volumes
|
|
if (MatchArgument(arguments[argIndex], L"bw", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Break shadow copy set as writable)");
|
|
|
|
VSS_ID breakingSnapshotSetID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// Break all shadow copies in the set
|
|
if (m_bWaitForFinish || execCommand.length()>0)
|
|
{
|
|
vector<wstring> volumeList;
|
|
m_vssClient.BreakSnapshotSet(breakingSnapshotSetID, true, &volumeList);
|
|
|
|
// Executing the custom command if needed
|
|
if (execCommand.length()>0)
|
|
ExecCommand(execCommand);
|
|
|
|
// Wait if needed
|
|
if (m_bWaitForFinish)
|
|
{
|
|
ft.WriteLine(L"\nPress <ENTER> to make the volumes writable...");
|
|
#pragma warning(suppress: 6031) //Intentionally ignore the return value of getchar()
|
|
getchar();
|
|
m_bWaitForFinish = false;
|
|
}
|
|
|
|
// Make the volumes read-write
|
|
ft.WriteLine(L"- Making shadow copy devices from " WSTR_GUID_FMT L" read-write...",
|
|
GUID_PRINTF_ARG(breakingSnapshotSetID));
|
|
|
|
m_vssClient.MakeVolumesReadWrite(volumeList);
|
|
}
|
|
else
|
|
{
|
|
// Break and make them read-write
|
|
m_vssClient.BreakSnapshotSet(breakingSnapshotSetID, true);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Break this shadow copy set using BreakSnapshotSetEx
|
|
if (MatchArgument(arguments[argIndex], L"bex", id))
|
|
{
|
|
ft.WriteLine(L"(Option: Break shadow copy set using BreakSnapshotSetEx)");
|
|
|
|
VSS_ID breakingSnapshotSetID = WString2Guid(id);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// List all shadow copies in the set
|
|
m_vssClient.BreakSnapshotSetEx(breakingSnapshotSetID, dwBreakExFlags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Expose the shadow copy locally
|
|
wstring exposeArgs;
|
|
if (MatchArgument(arguments[argIndex], L"el", exposeArgs))
|
|
{
|
|
ft.WriteLine(L"(Option: Expose a shadow copy)");
|
|
|
|
vector<wstring> exposeArgsArray = SplitWString(exposeArgs, L',');
|
|
if (exposeArgsArray.size() != 2)
|
|
{
|
|
ft.WriteLine(L"ERROR: the -el arguments must contain a GUID and a local path separated by a comma.");
|
|
throw(E_INVALIDARG);
|
|
}
|
|
|
|
VSS_ID snapshotID = WString2Guid(exposeArgsArray[0]);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// Expose locally this shadow copy
|
|
m_vssClient.ExposeSnapshotLocally(snapshotID, exposeArgsArray[1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Expose the shadow copy remotely
|
|
if (MatchArgument(arguments[argIndex], L"er", exposeArgs))
|
|
{
|
|
ft.WriteLine(L"(Option: Expose a shadow copy)");
|
|
|
|
vector<wstring> exposeArgsArray = SplitWString(exposeArgs, L',');
|
|
if ((exposeArgsArray.size() != 2) && (exposeArgsArray.size() != 3))
|
|
{
|
|
ft.WriteLine(L"ERROR: the -er arguments must contain a GUID, a share name and an optional local path separated by a comma.");
|
|
throw(E_INVALIDARG);
|
|
}
|
|
|
|
VSS_ID snapshotID = WString2Guid(exposeArgsArray[0]);
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL);
|
|
|
|
// Expose this shadow copy as a share
|
|
m_vssClient.ExposeSnapshotRemotely(snapshotID,
|
|
exposeArgsArray[1],
|
|
exposeArgsArray.size() == 3? exposeArgsArray[2]: L"");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Import with the given XML file
|
|
if (MatchArgument(arguments[argIndex], L"i", xmlBackupComponentsDoc))
|
|
{
|
|
ft.WriteLine(L"(Option: Import shadow copy set from file '%s')", xmlBackupComponentsDoc.c_str());
|
|
|
|
// Reading the backup components document
|
|
wstring xmlDoc = ReadFileContents(xmlBackupComponentsDoc);
|
|
ft.Trace(DBG_INFO, L"XML document: '%s'", xmlDoc.c_str());
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL, xmlDoc);
|
|
|
|
// List all shadow copies in the system
|
|
m_vssClient.ImportSnapshotSet();
|
|
|
|
// Executing the custom command if needed
|
|
if (execCommand.length() > 0)
|
|
ExecCommand(execCommand);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Perform a restore
|
|
if (MatchArgument(arguments[argIndex], L"r", xmlBackupComponentsDoc))
|
|
{
|
|
ft.WriteLine(L"(Option: Perform a restore)");
|
|
|
|
// Reading the backup components document
|
|
wstring xmlDoc = ReadFileContents(xmlBackupComponentsDoc);
|
|
ft.Trace(DBG_INFO, L"XML document: '%s'", xmlDoc.c_str());
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL, xmlDoc, true);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.GatherWriterMetadata();
|
|
|
|
// Gather writer status
|
|
m_vssClient.GatherWriterStatus();
|
|
|
|
// List writer status
|
|
m_vssClient.ListWriterStatus();
|
|
|
|
// Initialize the list of writers and components for restore
|
|
m_vssClient.InitializeWriterComponentsForRestore();
|
|
|
|
// Select required components for restore
|
|
m_vssClient.SelectComponentsForRestore(excludedWriterList, includedWriterList);
|
|
|
|
// Issue a PreRestore event to the writers
|
|
m_vssClient.PreRestore();
|
|
|
|
// Execute the optional custom command between PreRestore and PostRestore
|
|
try
|
|
{
|
|
// Check selected writer status
|
|
m_vssClient.CheckSelectedWriterStatus();
|
|
|
|
// Executing the custom command if needed
|
|
if (execCommand.length() > 0)
|
|
ExecCommand(execCommand);
|
|
|
|
}
|
|
catch(HRESULT)
|
|
{
|
|
// Notify writers about a failed restore
|
|
m_vssClient.SetFileRestoreStatus(false);
|
|
|
|
// Issue a PostRestore event to the writers
|
|
m_vssClient.PostRestore();
|
|
|
|
throw;
|
|
}
|
|
|
|
// Notify writers about a succesful restore
|
|
m_vssClient.SetFileRestoreStatus(true);
|
|
|
|
// Issue a PostRestore event to the writers
|
|
m_vssClient.PostRestore();
|
|
|
|
// Check selected writer status
|
|
m_vssClient.CheckSelectedWriterStatus();
|
|
|
|
ft.WriteLine(L"\nRestore done.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Perform a simulated restore
|
|
if (MatchArgument(arguments[argIndex], L"rs", xmlBackupComponentsDoc))
|
|
{
|
|
ft.WriteLine(L"(Option: Perform a Simulated restore)");
|
|
|
|
// Reading the backup components document
|
|
wstring xmlDoc = ReadFileContents(xmlBackupComponentsDoc);
|
|
ft.Trace(DBG_INFO, L"XML document: '%s'", xmlDoc.c_str());
|
|
|
|
// Initialize the VSS client
|
|
m_vssClient.Initialize(VSS_CTX_ALL, xmlDoc, true);
|
|
|
|
// Gather writer metadata
|
|
m_vssClient.GatherWriterMetadata();
|
|
|
|
// Gather writer status
|
|
m_vssClient.GatherWriterStatus();
|
|
|
|
// List writer status
|
|
m_vssClient.ListWriterStatus();
|
|
|
|
// Initialize the list of writers and components for restore
|
|
m_vssClient.InitializeWriterComponentsForRestore();
|
|
|
|
// Select required components for restore
|
|
m_vssClient.SelectComponentsForRestore(excludedWriterList, includedWriterList);
|
|
|
|
ft.WriteLine(L"\nRestore simulation done.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Add a resync pair
|
|
if (MatchArgument(arguments[argIndex], L"addresync", wsSnapIdDest))
|
|
{
|
|
|
|
vector<wstring> resyncArgsArray = SplitWString(wsSnapIdDest, L',');
|
|
|
|
VSS_ID snapshotID = WString2Guid(resyncArgsArray[0]);
|
|
|
|
wstring wsDest;
|
|
|
|
if (resyncArgsArray.size() == 1)
|
|
ft.WriteLine(L"(Option: Add snapshot Id for resync to original volume location specified in BCD)");
|
|
else if (resyncArgsArray.size() == 2)
|
|
{
|
|
ft.WriteLine(L"(Option: Add snapshot Id for resync to location: %s)", resyncArgsArray[1].c_str());
|
|
wsDest = resyncArgsArray[1];
|
|
}
|
|
else
|
|
throw(E_INVALIDARG);
|
|
|
|
m_vssClient.AddResyncSet(snapshotID, wsDest);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Set disk signature flag
|
|
if (MatchArgument(arguments[argIndex], L"revertsig"))
|
|
{
|
|
ft.WriteLine(L"(Option: Resync will revert destination to the original disk's signature)");
|
|
dwResyncFlags |= VSS_RECOVERY_REVERT_IDENTITY_ALL;
|
|
continue;
|
|
}
|
|
|
|
// Set NoVolCheck flag
|
|
if (MatchArgument(arguments[argIndex], L"novolcheck"))
|
|
{
|
|
ft.WriteLine(L"(Option: novolcheck flag used. Resync will not check for unselected volumes)");
|
|
dwResyncFlags |= VSS_RECOVERY_NO_VOLUME_CHECK;
|
|
continue;
|
|
}
|
|
|
|
// Perform hardware resync
|
|
if (MatchArgument(arguments[argIndex], L"resync", xmlBackupComponentsDoc))
|
|
{
|
|
wstring xmlDoc;
|
|
|
|
if (xmlBackupComponentsDoc.size())
|
|
{
|
|
if (dwResyncFlags & VSS_RECOVERY_REVERT_IDENTITY_ALL)
|
|
ft.WriteLine(L"Resyncing using source signature as the final disk signature");
|
|
else
|
|
ft.WriteLine(L"Resyncing using target signature as the final disk signature");
|
|
}
|
|
else
|
|
throw(E_INVALIDARG);
|
|
|
|
xmlDoc = ReadFileContents(xmlBackupComponentsDoc);
|
|
|
|
m_vssClient.Initialize(VSS_CTX_ALL, xmlDoc, true);
|
|
m_vssClient.DoResync(dwResyncFlags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Check if the arguments are volumes or file share paths. If yes, try to create the shadow copy set
|
|
if (IsVolume(arguments[argIndex]) || IsUNCPath((VSS_PWSZ)arguments[argIndex].c_str()))
|
|
{
|
|
ft.WriteLine(L"(Option: Create shadow copy set)");
|
|
|
|
ft.Trace(DBG_INFO, L"\nAttempting to create a shadow copy set... (volume %s was added as parameter)", arguments[argIndex].c_str());
|
|
|
|
// Make sure that all the arguments are volumes
|
|
vector<wstring> volumeList;
|
|
volumeList.push_back(GetUniqueVolumeNameForPath(arguments[argIndex], true));
|
|
|
|
// Process the rest of the arguments
|
|
for(unsigned i = argIndex + 1; i < arguments.size(); i++)
|
|
{
|
|
if (!(IsVolume(arguments[i]) || IsUNCPath((VSS_PWSZ)arguments[i].c_str())))
|
|
{
|
|
// No match. Print an error and the usage
|
|
ft.WriteLine(L"\nERROR: invalid parameters %s", GetCommandLine());
|
|
ft.WriteLine(L"- Parameter %s is expected to be a volume or a file share path! (shadow copy creation is assumed)", arguments[i].c_str());
|
|
ft.WriteLine(L"- Example: VSHADOW C:");
|
|
PrintUsage();
|
|
return 1;
|
|
}
|
|
|
|
// Add the volume to the list
|
|
volumeList.push_back(GetUniqueVolumeNameForPath(arguments[i], true));
|
|
}
|
|
|
|
// Initialize the VSS client
|
|
dwContext = UpdateFinalContext(dwContext);
|
|
m_vssClient.Initialize(dwContext);
|
|
|
|
// Create the shadow copy set
|
|
m_vssClient.CreateSnapshotSet(
|
|
volumeList,
|
|
xmlBackupComponentsDoc,
|
|
excludedWriterList,
|
|
includedWriterList
|
|
);
|
|
|
|
// Execute BackupComplete, except in fast snapshot creation
|
|
if ((dwContext & VSS_VOLSNAP_ATTR_DELAYED_POSTSNAPSHOT) == 0)
|
|
{
|
|
try
|
|
{
|
|
// Generate management scripts if needed
|
|
if (stringFileName.length() > 0)
|
|
m_vssClient.GenerateSetvarScript(stringFileName);
|
|
|
|
// Executing the custom command if needed
|
|
if (execCommand.length() > 0)
|
|
ExecCommand(execCommand);
|
|
|
|
}
|
|
catch(HRESULT)
|
|
{
|
|
// Mark backup failure and exit
|
|
if ((dwContext & VSS_VOLSNAP_ATTR_NO_WRITERS) == 0)
|
|
m_vssClient.BackupComplete(false);
|
|
|
|
throw;
|
|
}
|
|
|
|
// Complete the backup
|
|
// Note that this will notify writers that the backup is succesful!
|
|
// (which means eventually log truncation)
|
|
if ((dwContext & VSS_VOLSNAP_ATTR_NO_WRITERS) == 0)
|
|
m_vssClient.BackupComplete(true);
|
|
}
|
|
|
|
ft.WriteLine(L"\nSnapshot creation done.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
// No match. Print an error and the usage
|
|
ft.WriteLine(L"\nERROR: invalid parameter '%s'\n", arguments[argIndex].c_str());
|
|
PrintUsage();
|
|
return 1;
|
|
}
|
|
|
|
PrintUsage();
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// Returns TRUE if the argument is in the following formats
|
|
// -xxxx
|
|
// /xxxx
|
|
// where xxxx is the option pattern
|
|
bool CommandLineParser::MatchArgument(wstring argument, wstring optionPattern)
|
|
{
|
|
FunctionTracer ft(DBG_INFO);
|
|
|
|
ft.Trace(DBG_INFO, L"Matching Arg: '%s' with '%s'\n", argument.c_str(), optionPattern.c_str());
|
|
|
|
bool retVal = (IsEqual(argument, wstring(L"/") + optionPattern) || IsEqual(argument, wstring(L"-") + optionPattern) );
|
|
|
|
ft.Trace(DBG_INFO, L"Return: %s\n", BOOL2TXT(retVal));
|
|
return retVal;
|
|
}
|
|
|
|
|
|
// Returns TRUE if the argument is in the following formats
|
|
// -xxxx=yyyy
|
|
// /xxxx=yyyy
|
|
// where xxxx is the option pattern and yyyy the additional parameter (eventually enclosed in ' or ")
|
|
bool CommandLineParser::MatchArgument(wstring argument, wstring optionPattern, wstring & additionalParameter)
|
|
{
|
|
FunctionTracer ft(DBG_INFO);
|
|
|
|
ft.Trace(DBG_INFO, L"Matching Arg: '%s' with '%s'", argument.c_str(), optionPattern.c_str());
|
|
|
|
_ASSERTE(argument.length() > 0);
|
|
|
|
if ((argument[0] != L'/') && (argument[0] != L'-'))
|
|
return false;
|
|
|
|
// Find the '=' separator between the option and the additional parameter
|
|
size_t equalSignPos = argument.find(L'=');
|
|
if ((equalSignPos == wstring::npos) || (equalSignPos == 0))
|
|
return false;
|
|
|
|
ft.Trace(DBG_INFO, L"%s %d", argument.substr(1, equalSignPos - 1).c_str(), equalSignPos);
|
|
|
|
// Check to see if this is our option
|
|
if (!IsEqual(argument.substr(1, equalSignPos - 1), optionPattern))
|
|
return false;
|
|
|
|
// Isolate the additional parameter
|
|
additionalParameter = argument.substr(equalSignPos + 1);
|
|
|
|
ft.Trace(DBG_INFO, L"- Additional Param: [%s]", additionalParameter.c_str());
|
|
|
|
// We cannot have an empty additional parameter
|
|
if (additionalParameter.length() == 0)
|
|
return false;
|
|
|
|
// Eliminate the enclosing quotes, if any
|
|
size_t lastPos = additionalParameter.length() - 1;
|
|
if ((additionalParameter[0] == L'"') && (additionalParameter[lastPos] == L'"'))
|
|
additionalParameter = additionalParameter.substr(1, additionalParameter.length() - 2);
|
|
|
|
ft.Trace(DBG_INFO, L"Return true; (additional param = %s)", additionalParameter.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//
|
|
// Prints the command line options
|
|
//
|
|
void CommandLineParser::PrintUsage()
|
|
{
|
|
FunctionTracer ft(DBG_INFO);
|
|
|
|
#ifdef VSS_SERVER
|
|
ft.WriteLine(
|
|
L"Usage:\n"
|
|
L" VSHADOW [optional flags] [commands]\n"
|
|
L"\n"
|
|
L"List of optional flags:\n"
|
|
L" -? - Displays the usage screen\n"
|
|
L" -p - Manages persistent shadow copies\n"
|
|
L" -nw - Manages no-writer shadow copies\n"
|
|
L" -nar - Creates shadow copies with no auto-recovery\n"
|
|
L" -tr - Creates TxF-recovered shadow copies\n"
|
|
L" -ad - Creates differential HW shadow copies\n"
|
|
L" -ap - Creates plex HW shadow copies\n"
|
|
L" -scsf - Creates Shadow Copies for Shared Folders (Client Accessible)\n"
|
|
L" -t={file.xml} - Transportable shadow set. Generates also the backup components doc.\n"
|
|
L" -bc={file.xml} - Generates the backup components doc for non-transportable shadow set.\n"
|
|
L" -wi={Writer Name} - Verify that a writer/component is included\n"
|
|
L" -wx={Writer Name} - Exclude a writer/component from set creation or restore\n"
|
|
L" -mask - BreakSnapshotSetEx flag: Mask shadow copy luns from system on break.\n"
|
|
L" -rw - BreakSnapshotSetEx flag: Make shadow copy luns read-write on break.\n"
|
|
L" -forcerevert - BreakSnapshotSetEx flag: Complete operation only if all disk signatures revertable.\n"
|
|
L" -norevert - BreakSnapshotSetEx flag: Do not revert disk signatures.\n"
|
|
L" -revertsig - Revert to the original disk's signature during resync.\n"
|
|
L" -novolcheck - Ignore volume check during resync. Unselected volumes will be overwritten.\n"
|
|
L" -script={file.cmd} - SETVAR script creation\n"
|
|
L" -exec={command} - Custom command executed after shadow creation, import or between break and make-it-write\n"
|
|
L" -wait - Wait before program termination or between shadow set break and make-it-write\n"
|
|
L" -tracing - Runs VSHADOW.EXE with enhanced diagnostics\n"
|
|
L"\n" );
|
|
ft.WriteLine(
|
|
L"List of commands:\n"
|
|
L" {volume list} - Creates a shadow set on these volumes\n"
|
|
L" -ws - List writer status\n"
|
|
L" -wm - List writer summary metadata\n"
|
|
L" -wm2 - List writer detailed metadata\n"
|
|
L" -wm3 - List writer detailed metadata in raw XML format\n"
|
|
L" -q - List all shadow copies in the system\n"
|
|
L" -qx={SnapSetID} - List all shadow copies in this set\n"
|
|
L" -s={SnapID} - List the shadow copy with the given ID\n"
|
|
L" -da - Deletes all shadow copies in the system\n"
|
|
L" -do={volume} - Deletes the oldest shadow of the specified volume\n"
|
|
L" -dx={SnapSetID} - Deletes all shadow copies in this set\n"
|
|
L" -ds={SnapID} - Deletes this shadow copy\n"
|
|
L" -i={file.xml} - Transportable shadow copy import\n"
|
|
L" -b={SnapSetID} - Break the given shadow set into read-only volumes\n"
|
|
L" -bw={SnapSetID} - Break the shadow set into writable volumes\n"
|
|
L" -bex={SnapSetID} - Break using BreakSnapshotSetEx and flags, see options for available flags\n"
|
|
L" -el={SnapID},dir - Expose the shadow copy as a mount point\n"
|
|
L" -el={SnapID},drive - Expose the shadow copy as a drive letter\n"
|
|
L" -er={SnapID},share - Expose the shadow copy as a network share\n"
|
|
L" -er={SnapID},share,path - Expose a child directory from the shadow copy as a share\n"
|
|
L" -r={file.xml} - Restore based on a previously-generated Backup Components document\n"
|
|
L" -rs={file.xml} - Simulated restore based on a previously-generated Backup Components doc\n"\
|
|
L" -revert={SnapID} - Revert a volume to the specified shadow copy\n"
|
|
L" -addresync={SnapID},drive - Resync the given shadow copy to the specified volume\n"
|
|
L" -addresync={SnapID} - Resync the given shadow copy to it's original volume\n"
|
|
L" -resync=bcd.xml - Perform Resync using the specified BCD\n"
|
|
L"\n" );
|
|
ft.WriteLine(
|
|
L"Examples:\n"
|
|
L"\n"
|
|
L" - Non-persistent shadow copy creation on C: and E:\n"
|
|
L" VSHADOW C: E:\n"
|
|
L"\n"
|
|
L" - Non-persistent shadow copy creation on a CSV named Volume1\n"
|
|
L" VSHADOW C:\\ClusterStorage\\Volume1\n"
|
|
L"\n"
|
|
L" - Persistent shadow copy creation on C: (with no writers)\n"
|
|
L" VSHADOW -p -nw C:\n"
|
|
L"\n"
|
|
L" - Transportable shadow copy creation on X:\n"
|
|
L" VSHADOW -t=file1.xml X:\n"
|
|
L"\n"
|
|
L" - Transportable shadow copy import\n"
|
|
L" VSHADOW -i=file1.xml\n"
|
|
L"\n"
|
|
L" - List all shadow copies in the system:\n"
|
|
L" VSHADOW -q\n"
|
|
L"\n"
|
|
L"Please see the README.DOC file for more details.\n"
|
|
L"\n"
|
|
L"\n"
|
|
);
|
|
#else
|
|
ft.WriteLine(
|
|
L"Usage:\n"
|
|
L" VSHADOW [optional flags] [commands]\n"
|
|
L"\n"
|
|
L"List of optional flags:\n"
|
|
L" -? - Displays the usage screen\n"
|
|
L" -wi={Writer Name} - Verify that a writer/component is included\n"
|
|
L" -wx={Writer Name} - Exclude a writer/component from set creation or restore\n"
|
|
L" -bc={file.xml} - Generates the backup components document during shadow creation.\n"
|
|
L" -script={file.cmd} - SETVAR script creation\n"
|
|
L" -exec={command} - Custom command executed after shadow creation\n"
|
|
L" -wait - Wait before program termination \n"
|
|
L" -tracing - Runs VSHADOW.EXE with enhanced diagnostics\n"
|
|
L"\n"
|
|
L"List of commands:\n"
|
|
L" {volume list} - Creates a shadow set on these volumes\n"
|
|
L" -ws - List writer status\n"
|
|
L" -wm - List writer summary metadata\n"
|
|
L" -wm2 - List writer detailed metadata\n"
|
|
L" -wm3 - List writer detailed metadata in raw XML format\n"
|
|
L" -q - List all shadow copies in the system\n"
|
|
L" -qx={SnapSetID} - List all shadow copies in this set\n"
|
|
L" -s={SnapID} - List the shadow copy with the given ID\n"
|
|
L" -da - Deletes all shadow copies in the system\n"
|
|
L" -dx={SnapSetID} - Deletes all shadow copies in this set\n"
|
|
L" -ds={SnapID} - Deletes this shadow copy\n"
|
|
L" -r={file.xml} - Restore based on a previously-generated Backup Components doc\n"
|
|
L" -rs={file.xml} - Simulated restore based on a previously-generated Backup Components doc\n"
|
|
L"\n");
|
|
ft.WriteLine(
|
|
L"Examples:\n"
|
|
L"\n"
|
|
L" - Non-persistent shadow copy creation on C: and D:\n"
|
|
L" VSHADOW C: E:\n"
|
|
L"\n"
|
|
L" - List all shadow copies in the system:\n"
|
|
L" VSHADOW -q\n"
|
|
L"\n"
|
|
L"Please see the README.DOC file for more details.\n"
|
|
L"\n"
|
|
L"\n"
|
|
);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
DWORD CommandLineParser::UpdateFinalContext(DWORD dwContext)
|
|
{
|
|
#ifdef VSS_SERVER
|
|
if (m_bPersistent)
|
|
{
|
|
if (m_bWithWriters)
|
|
dwContext |= VSS_CTX_APP_ROLLBACK;
|
|
else
|
|
dwContext |= VSS_CTX_NAS_ROLLBACK;
|
|
}
|
|
else
|
|
{
|
|
if (m_bWithWriters)
|
|
dwContext |= VSS_CTX_BACKUP;
|
|
else
|
|
dwContext |= VSS_CTX_FILE_SHARE_BACKUP;
|
|
}
|
|
#endif
|
|
|
|
return dwContext;
|
|
}
|
|
|
|
|
|
|
|
CommandLineParser::CommandLineParser()
|
|
{
|
|
// true if it is a is persistent snapshot
|
|
m_bPersistent = false;
|
|
|
|
// false if the snapshot creation is without writers
|
|
m_bWithWriters = true;
|
|
|
|
// true if the user wants to wait for termination
|
|
m_bWaitForFinish = false;
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
CommandLineParser::~CommandLineParser()
|
|
{
|
|
FunctionTracer ft(DBG_INFO);
|
|
|
|
if (m_bWaitForFinish)
|
|
{
|
|
ft.WriteLine(L"\nPress <ENTER> to continue...");
|
|
|
|
#pragma warning(suppress: 6031) //Intentionally ignore the return value of getchar()
|
|
getchar();
|
|
}
|
|
}
|