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.
451 lines
17 KiB
451 lines
17 KiB
/*
|
|
**++
|
|
**
|
|
** Copyright (c) 2000-2001 Microsoft Corporation
|
|
**
|
|
**
|
|
** Module Name:
|
|
**
|
|
** util.cpp
|
|
**
|
|
**
|
|
** Abstract:
|
|
**
|
|
** Sample program to
|
|
** - obtain and display the Writer metadata.
|
|
** - create a snapshot set
|
|
**
|
|
** Author:
|
|
**
|
|
** Adi Oltean [aoltean] 05-Dec-2000
|
|
**
|
|
** The sample is based on the Metasnap test program written by Michael C. Johnson.
|
|
**
|
|
**
|
|
** Revision History:
|
|
**
|
|
**--
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Includes
|
|
|
|
#include "vsreq.h"
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Print usage
|
|
|
|
|
|
void CVssSampleRequestor::PrintUsage()
|
|
{
|
|
wprintf(
|
|
L"\nUsage:\n"
|
|
L" vsreq [-b] [-s] [-x <file.xml>] [<volumes>]\n"
|
|
L"\nOptions:\n"
|
|
L" -b Backup includes bootable & system state.\n"
|
|
L" -s Enable component selection.\n"
|
|
L" -x <file.xml> Generate an XML file containing the backup metadata\n"
|
|
L" <volumes> Specifies the volumes to be part of the snapshot set\n"
|
|
L" The volumes in the list must be distinct and \n"
|
|
L" must be separated by space. A volume must be \n"
|
|
L" terminated with a trailing backslask (for example C:\\).\n"
|
|
L"\n"
|
|
L"\nExample:\n"
|
|
L" The following command will create a snapshot set\n"
|
|
L" on the volumes mounted under c:\\ and d:\\\n"
|
|
L"\n"
|
|
L" vsreq c:\\ d:\\ \n"
|
|
L"\n"
|
|
L" The following command will create a snapshot set on the volumes \n"
|
|
L" that contain selected components and also the volume c:\\\n"
|
|
L" Also, the backup will contain bootable and system state.\n"
|
|
L" The XML results will be stored in file.xml\n"
|
|
L"\n"
|
|
L" vsreq -b -s -x file.xml c:\\ \n"
|
|
);
|
|
throw(2);
|
|
}
|
|
|
|
|
|
void CVssSampleRequestor::ParseCommandLine(
|
|
IN INT nArgsCount,
|
|
IN WCHAR ** ppwszArgsArray
|
|
)
|
|
{
|
|
if (nArgsCount == 0)
|
|
PrintUsage();
|
|
|
|
// For each argument in the command line
|
|
bool bParsingVolumes = false;
|
|
INT nCurrentArg = nArgsCount;
|
|
WCHAR ** ppwszCurrentArg = ppwszArgsArray;
|
|
for(; nCurrentArg--; ppwszCurrentArg++)
|
|
{
|
|
if (!bParsingVolumes) {
|
|
// Check for Bootable & system state option
|
|
if (!m_bBootableSystemState && !wcscmp(*ppwszCurrentArg, L"-b")) {
|
|
m_bBootableSystemState = true;
|
|
continue;
|
|
}
|
|
|
|
// Check for Selected components option
|
|
if (!m_bComponentSelectionEnabled && !wcscmp(*ppwszCurrentArg, L"-s")) {
|
|
m_bComponentSelectionEnabled = true;
|
|
continue;
|
|
}
|
|
|
|
// Check for Xml file option
|
|
if (!m_pwszXmlFile && !wcscmp(*ppwszCurrentArg, L"-x")) {
|
|
if (nCurrentArg-- == 0)
|
|
return PrintUsage();
|
|
ppwszCurrentArg++;
|
|
m_pwszXmlFile = *ppwszCurrentArg;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We suppose that the next arguments are the volumes
|
|
bParsingVolumes = true;
|
|
|
|
// Add the volume to the list of snapshotting volumes
|
|
// Make sure that the volume name is valid
|
|
bool bAdded = false;
|
|
if (!AddVolume(*ppwszCurrentArg, bAdded)) {
|
|
wprintf(L"\nError while parsing the command line:\n"
|
|
L"\t%s is not a valid option or a mount point [0x%08lx]\n\n",
|
|
*ppwszCurrentArg, GetLastError() );
|
|
PrintUsage();
|
|
}
|
|
|
|
// Check if the same volume is added twice
|
|
if (!bAdded) {
|
|
wprintf(L"\nError while parsing the command line:\n"
|
|
L"\tThe volume %s is specified twice\n\n", *ppwszCurrentArg );
|
|
PrintUsage();
|
|
}
|
|
}
|
|
|
|
// Check if we added at least one volume
|
|
if ((m_nVolumesCount == 0) && !m_bComponentSelectionEnabled) {
|
|
wprintf(L"\nError while parsing the command line:\n"
|
|
L"\t- You should specify at least one volume or enable component selection\n\n");
|
|
PrintUsage();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Add the given volume by the contained path
|
|
void CVssSampleRequestor::AddVolumeForComponent(
|
|
IN IVssWMFiledesc* pFileDesc
|
|
)
|
|
{
|
|
// Get the component path
|
|
CComBSTR bstrPath;
|
|
CHECK_SUCCESS(pFileDesc->GetPath(&bstrPath));
|
|
|
|
// Trying to find the volume that will contain the path.
|
|
WCHAR wszExpandedPath[MAX_TEXT_BUFFER];
|
|
if (!ExpandEnvironmentStringsW(bstrPath, wszExpandedPath, MAX_TEXT_BUFFER))
|
|
Error( 1, L"\nExpandEnvironmentStringsW(%s, wszExpandedPath, MAX_TEXT_BUFFER) failed with [0x%08lx]\n",
|
|
bstrPath, GetLastError());
|
|
|
|
// Eliminate one by one the terminating folder names, until we reach an existing path.
|
|
// Then get the volume name for that path
|
|
WCHAR wszMountPoint[MAX_TEXT_BUFFER];
|
|
while(true) {
|
|
if (GetVolumePathNameW(wszExpandedPath, wszMountPoint, MAX_TEXT_BUFFER))
|
|
break;
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
|
Error( 1, L"\nGetVolumePathNameW(%s, wszMountPoint, MAX_TEXT_BUFFER) failed with [0x%08lx]\n",
|
|
wszExpandedPath, GetLastError());
|
|
WCHAR* pwszLastBackslashIndex = wcsrchr(wszExpandedPath, L'\\');
|
|
if (!pwszLastBackslashIndex)
|
|
Error( 1, L"\nCannot find anymore a backslash in path %s. \n"
|
|
L"The original path %s seems invalid.\n", wszExpandedPath, bstrPath);
|
|
// Eliminate the last folder name
|
|
pwszLastBackslashIndex[0] = L'\0';
|
|
}
|
|
|
|
// Add the volume, if possible
|
|
bool bAdded = false;
|
|
if (!AddVolume( wszMountPoint, bAdded ))
|
|
Error( 1, L"\nUnexpected error: cannot add volume %s to the snapshot set.\n", wszMountPoint);
|
|
wprintf (L" [Volume %s (that contains the file) %s marked as a candidate for snapshot]\n",
|
|
wszMountPoint, bAdded? L"is": L"is already");
|
|
}
|
|
|
|
|
|
// Add the given volume in the list of potential candidates for snapshots
|
|
// - Returns "false" if the volume does not correspond to a real mount point
|
|
// (and GetLastError() will contain the correct Win32 error code)
|
|
// - Sets "true" in the bAdded parameter if the volume is actually added
|
|
bool CVssSampleRequestor::AddVolume(
|
|
IN WCHAR* pwszVolume,
|
|
OUT bool & bAdded
|
|
)
|
|
{
|
|
// Initialize [out] parameters
|
|
bAdded = false;
|
|
|
|
// Check if the volume represents a real mount point
|
|
WCHAR wszVolumeName[MAX_TEXT_BUFFER];
|
|
if (!GetVolumeNameForVolumeMountPoint(pwszVolume, wszVolumeName, MAX_TEXT_BUFFER))
|
|
return false; // Invalid volume
|
|
|
|
// Check if the volume is already added.
|
|
for (INT nIndex = 0; nIndex < m_nVolumesCount; nIndex++)
|
|
if (!wcscmp(wszVolumeName, m_ppwszVolumeNamesList[nIndex]))
|
|
return true; // Volume already added. Stop here.
|
|
|
|
// Check if we exceeded the maximum number of volumes
|
|
if (m_nVolumesCount == MAX_VOLUMES)
|
|
Error( 1, L"Maximum number of volumes exceeded");
|
|
|
|
// Create a copy of the volume
|
|
WCHAR* pwszNewVolume = _wcsdup(pwszVolume);
|
|
if (pwszNewVolume == NULL)
|
|
Error( 1, L"Memory allocation error");
|
|
|
|
// Create a copy of the volume name
|
|
WCHAR* pwszNewVolumeName = _wcsdup(wszVolumeName);
|
|
if (pwszNewVolumeName == NULL) {
|
|
free(pwszNewVolume);
|
|
Error( 1, L"Memory allocation error");
|
|
}
|
|
|
|
// Add the volume in our internal list of snapshotted volumes
|
|
m_ppwszVolumesList[m_nVolumesCount] = pwszNewVolume;
|
|
m_ppwszVolumeNamesList[m_nVolumesCount] = pwszNewVolumeName;
|
|
m_nVolumesCount++;
|
|
bAdded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// This function displays the formatted message at the console and throws
|
|
// The passed return code will be returned by vsreq.exe
|
|
void CVssSampleRequestor::Error(
|
|
IN INT nReturnCode,
|
|
IN const WCHAR* pwszMsgFormat,
|
|
IN ...
|
|
)
|
|
{
|
|
va_list marker;
|
|
va_start( marker, pwszMsgFormat );
|
|
vwprintf( pwszMsgFormat, marker );
|
|
va_end( marker );
|
|
|
|
// throw that return code.
|
|
throw(nReturnCode);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Utility functions
|
|
|
|
|
|
// Print a file description object
|
|
void CVssSampleRequestor::PrintFiledesc (IVssWMFiledesc *pFiledesc, LPCWSTR wszDescription)
|
|
{
|
|
CComBSTR bstrPath;
|
|
CComBSTR bstrFilespec;
|
|
CComBSTR bstrAlternate;
|
|
bool bRecursive;
|
|
|
|
CHECK_SUCCESS(pFiledesc->GetPath(&bstrPath));
|
|
CHECK_SUCCESS(pFiledesc->GetFilespec (&bstrFilespec));
|
|
CHECK_NOFAIL(pFiledesc->GetRecursive(&bRecursive));
|
|
CHECK_NOFAIL(pFiledesc->GetAlternateLocation(&bstrAlternate));
|
|
|
|
wprintf (L"%s\n"
|
|
L" Path = %s\n"
|
|
L" Filespec = %s\n"
|
|
L" Recursive = %s\n",
|
|
wszDescription,
|
|
bstrPath,
|
|
bstrFilespec,
|
|
bRecursive ? L"yes" : L"no");
|
|
|
|
if (bstrAlternate && wcslen (bstrAlternate) > 0)
|
|
wprintf(L" Alternate Location = %s\n", bstrAlternate);
|
|
}
|
|
|
|
|
|
// Convert a usage type into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromUsageType (VSS_USAGE_TYPE eUsageType)
|
|
{
|
|
LPCWSTR pwszRetString = L"UNDEFINED";
|
|
|
|
switch (eUsageType)
|
|
{
|
|
case VSS_UT_BOOTABLESYSTEMSTATE: pwszRetString = L"BootableSystemState"; break;
|
|
case VSS_UT_SYSTEMSERVICE: pwszRetString = L"SystemService"; break;
|
|
case VSS_UT_USERDATA: pwszRetString = L"UserData"; break;
|
|
case VSS_UT_OTHER: pwszRetString = L"Other"; break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return (pwszRetString);
|
|
}
|
|
|
|
|
|
// Convert a source type into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromSourceType (VSS_SOURCE_TYPE eSourceType)
|
|
{
|
|
LPCWSTR pwszRetString = L"UNDEFINED";
|
|
|
|
switch (eSourceType)
|
|
{
|
|
case VSS_ST_TRANSACTEDDB: pwszRetString = L"TransactionDb"; break;
|
|
case VSS_ST_NONTRANSACTEDDB: pwszRetString = L"NonTransactionDb"; break;
|
|
case VSS_ST_OTHER: pwszRetString = L"Other"; break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return (pwszRetString);
|
|
}
|
|
|
|
|
|
// Convert a restore method into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromRestoreMethod (VSS_RESTOREMETHOD_ENUM eRestoreMethod)
|
|
{
|
|
LPCWSTR pwszRetString = L"UNDEFINED";
|
|
|
|
switch (eRestoreMethod)
|
|
{
|
|
case VSS_RME_RESTORE_IF_NOT_THERE: pwszRetString = L"RestoreIfNotThere"; break;
|
|
case VSS_RME_RESTORE_IF_CAN_REPLACE: pwszRetString = L"RestoreIfCanReplace"; break;
|
|
case VSS_RME_STOP_RESTORE_START: pwszRetString = L"StopRestoreStart"; break;
|
|
case VSS_RME_RESTORE_TO_ALTERNATE_LOCATION: pwszRetString = L"RestoreToAlternateLocation"; break;
|
|
case VSS_RME_RESTORE_AT_REBOOT: pwszRetString = L"RestoreAtReboot"; break;
|
|
case VSS_RME_CUSTOM: pwszRetString = L"Custom"; break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return (pwszRetString);
|
|
}
|
|
|
|
|
|
// Convert a writer restore method into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromWriterRestoreMethod (VSS_WRITERRESTORE_ENUM eWriterRestoreMethod)
|
|
{
|
|
LPCWSTR pwszRetString = L"UNDEFINED";
|
|
|
|
switch (eWriterRestoreMethod)
|
|
{
|
|
case VSS_WRE_NEVER: pwszRetString = L"RestoreNever"; break;
|
|
case VSS_WRE_IF_REPLACE_FAILS: pwszRetString = L"RestoreIfReplaceFailsI"; break;
|
|
case VSS_WRE_ALWAYS: pwszRetString = L"RestoreAlways"; break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return (pwszRetString);
|
|
}
|
|
|
|
|
|
// Convert a component type into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromComponentType (VSS_COMPONENT_TYPE eComponentType)
|
|
{
|
|
LPCWSTR pwszRetString = L"UNDEFINED";
|
|
|
|
switch (eComponentType)
|
|
{
|
|
case VSS_CT_DATABASE: pwszRetString = L"Database"; break;
|
|
case VSS_CT_FILEGROUP: pwszRetString = L"FileGroup"; break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
return (pwszRetString);
|
|
}
|
|
|
|
|
|
// Convert a failure type into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromFailureType(HRESULT hrStatus)
|
|
{
|
|
LPCWSTR pwszFailureType = L"";
|
|
|
|
switch (hrStatus)
|
|
{
|
|
case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: pwszFailureType = L"VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT"; break;
|
|
case VSS_E_WRITERERROR_OUTOFRESOURCES: pwszFailureType = L"VSS_E_WRITERERROR_OUTOFRESOURCES"; break;
|
|
case VSS_E_WRITERERROR_TIMEOUT: pwszFailureType = L"VSS_E_WRITERERROR_TIMEOUT"; break;
|
|
case VSS_E_WRITERERROR_NONRETRYABLE: pwszFailureType = L"VSS_E_WRITERERROR_NONRETRYABLE"; break;
|
|
case VSS_E_WRITERERROR_RETRYABLE: pwszFailureType = L"VSS_E_WRITERERROR_RETRYABLE"; break;
|
|
case VSS_E_BAD_STATE: pwszFailureType = L"VSS_E_BAD_STATE"; break;
|
|
case VSS_E_PROVIDER_ALREADY_REGISTERED: pwszFailureType = L"VSS_E_PROVIDER_ALREADY_REGISTERED"; break;
|
|
case VSS_E_PROVIDER_NOT_REGISTERED: pwszFailureType = L"VSS_E_PROVIDER_NOT_REGISTERED"; break;
|
|
case VSS_E_PROVIDER_VETO: pwszFailureType = L"VSS_E_PROVIDER_VETO"; break;
|
|
case VSS_E_PROVIDER_IN_USE: pwszFailureType = L"VSS_E_PROVIDER_IN_USE"; break;
|
|
case VSS_E_OBJECT_NOT_FOUND: pwszFailureType = L"VSS_E_OBJECT_NOT_FOUND"; break;
|
|
case VSS_S_ASYNC_PENDING: pwszFailureType = L"VSS_S_ASYNC_PENDING"; break;
|
|
case VSS_S_ASYNC_FINISHED: pwszFailureType = L"VSS_S_ASYNC_FINISHED"; break;
|
|
case VSS_S_ASYNC_CANCELLED: pwszFailureType = L"VSS_S_ASYNC_CANCELLED"; break;
|
|
case VSS_E_VOLUME_NOT_SUPPORTED: pwszFailureType = L"VSS_E_VOLUME_NOT_SUPPORTED"; break;
|
|
case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER: pwszFailureType = L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER"; break;
|
|
case VSS_E_OBJECT_ALREADY_EXISTS: pwszFailureType = L"VSS_E_OBJECT_ALREADY_EXISTS"; break;
|
|
case VSS_E_UNEXPECTED_PROVIDER_ERROR: pwszFailureType = L"VSS_E_UNEXPECTED_PROVIDER_ERROR"; break;
|
|
case VSS_E_CORRUPT_XML_DOCUMENT: pwszFailureType = L"VSS_E_CORRUPT_XML_DOCUMENT"; break;
|
|
case VSS_E_INVALID_XML_DOCUMENT: pwszFailureType = L"VSS_E_INVALID_XML_DOCUMENT"; break;
|
|
case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED: pwszFailureType = L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED"; break;
|
|
case VSS_E_FLUSH_WRITES_TIMEOUT: pwszFailureType = L"VSS_E_FLUSH_WRITES_TIMEOUT"; break;
|
|
case VSS_E_HOLD_WRITES_TIMEOUT: pwszFailureType = L"VSS_E_HOLD_WRITES_TIMEOUT"; break;
|
|
case VSS_E_UNEXPECTED_WRITER_ERROR: pwszFailureType = L"VSS_E_UNEXPECTED_WRITER_ERROR"; break;
|
|
case VSS_E_SNAPSHOT_SET_IN_PROGRESS: pwszFailureType = L"VSS_E_SNAPSHOT_SET_IN_PROGRESS"; break;
|
|
case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED: pwszFailureType = L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED"; break;
|
|
case VSS_E_WRITER_INFRASTRUCTURE: pwszFailureType = L"VSS_E_WRITER_INFRASTRUCTURE"; break;
|
|
case VSS_E_WRITER_NOT_RESPONDING: pwszFailureType = L"VSS_E_WRITER_NOT_RESPONDING"; break;
|
|
case VSS_E_WRITER_ALREADY_SUBSCRIBED: pwszFailureType = L"VSS_E_WRITER_ALREADY_SUBSCRIBED"; break;
|
|
|
|
case NOERROR:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (pwszFailureType);
|
|
}
|
|
|
|
|
|
// Convert a writer status into a string
|
|
LPCWSTR CVssSampleRequestor::GetStringFromWriterStatus(VSS_WRITER_STATE eWriterStatus)
|
|
{
|
|
LPCWSTR pwszRetString = L"UNDEFINED";
|
|
|
|
switch (eWriterStatus)
|
|
{
|
|
case VSS_WS_STABLE: pwszRetString = L"STABLE"; break;
|
|
case VSS_WS_WAITING_FOR_FREEZE: pwszRetString = L"WAITING_FOR_FREEZE"; break;
|
|
case VSS_WS_WAITING_FOR_THAW: pwszRetString = L"WAITING_FOR_THAW"; break;
|
|
case VSS_WS_WAITING_FOR_BACKUP_COMPLETE:pwszRetString = L"VSS_WS_WAITING_FOR_BACKUP_COMPLETE"; break;
|
|
case VSS_WS_FAILED_AT_IDENTIFY: pwszRetString = L"FAILED_AT_IDENTIFY"; break;
|
|
case VSS_WS_FAILED_AT_PREPARE_BACKUP: pwszRetString = L"FAILED_AT_PREPARE_BACKUP";break;
|
|
case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT: pwszRetString = L"VSS_WS_FAILED_AT_PREPARE_SNAPSHOT"; break;
|
|
case VSS_WS_FAILED_AT_FREEZE: pwszRetString = L"FAILED_AT_FREEZE"; break;
|
|
case VSS_WS_FAILED_AT_THAW: pwszRetString = L"FAILED_AT_THAW"; break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (pwszRetString);
|
|
}
|
|
|
|
|
|
|