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.
605 lines
22 KiB
605 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
writer.h
|
|
|
|
Abstract:
|
|
Implementation file for FRS writer
|
|
|
|
Author:
|
|
Reuven Lax 17-Sep-2002
|
|
|
|
--*/
|
|
|
|
#include "writer.h"
|
|
|
|
CFrsWriter* CFrsWriter::m_pWriterInstance = NULL;
|
|
|
|
DWORD InitializeFrsWriter()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called by the FRS service to initialize the writer.
|
|
|
|
Return Value:
|
|
DWORD
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "InitializeFrsWriter:"
|
|
|
|
DPRINT(4, "Initializing the FRS Writer\n");
|
|
|
|
// initialize COM
|
|
HRESULT hr = S_OK;
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "CoInitializeEx failed with hresult 0x%08lx\n", hr);
|
|
return HRESULT_CODE(hr);
|
|
}
|
|
|
|
// create the writer
|
|
hr = CFrsWriter::CreateWriter();
|
|
if (FAILED(hr))
|
|
DPRINT1(1, "CFrsWriter::CreateWriter failed with hresult 0x%08lx\n", hr);
|
|
|
|
CoUninitialize();
|
|
return HRESULT_CODE(hr);
|
|
}
|
|
|
|
|
|
void ShutDownFrsWriter()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called by the FRS service to shutdown the writer.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "ShutDownFrsWriter:"
|
|
|
|
DPRINT(4, "Shutting down the FRS writer\n");
|
|
|
|
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
if (FAILED(hr))
|
|
DPRINT1(1, "CoInitializeEx failed with hresult 0x%08lx\n", hr);
|
|
|
|
CFrsWriter::DestroyWriter();
|
|
|
|
if (SUCCEEDED(hr))
|
|
CoUninitialize();
|
|
}
|
|
|
|
bool STDMETHODCALLTYPE CFrsWriter::OnIdentify(IN IVssCreateWriterMetadata *pMetadata)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called in response to an Identify event being sent to this writer. The writer
|
|
is responsible for reporting on all of its metadata in this routine.
|
|
|
|
Arguments:
|
|
pMetadata - Interface used to report on metadata
|
|
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::OnIdentify:"
|
|
|
|
DPRINT(4, "Writer received OnIdentify event\n");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// set the restore method
|
|
hr = pMetadata->SetRestoreMethod(VSS_RME_CUSTOM, // method
|
|
NULL, // wszService
|
|
NULL, // wszUserProcedure
|
|
VSS_WRE_NEVER, // writerRestore
|
|
false // bRebootRequired
|
|
);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "IVssCreateWriterMetadata::SetRestoreMethod failed with hresult 0x%08lx\n", hr);
|
|
return false;
|
|
}
|
|
|
|
// initialize FRS backup API
|
|
DWORD winStatus = 0;
|
|
void* context = NULL;
|
|
winStatus = ::NtFrsApiInitializeBackupRestore(NULL,
|
|
NTFRSAPI_BUR_FLAGS_BACKUP | NTFRSAPI_BUR_FLAGS_NORMAL,
|
|
&context);
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "NtFrsApiInitializeBackupRestore failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
FRS_ASSERT(context != NULL);
|
|
|
|
// stick the backup context into an auto object to ensure that it's always destroyed
|
|
CAutoFrsBackupRestore autoContext(context);
|
|
|
|
// get an enumeration for all the replica sets
|
|
winStatus = ::NtFrsApiGetBackupRestoreSets(autoContext.m_context);
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "NtFrsApiGetBackupRestoreSets failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
// process each replica set
|
|
DWORD index = 0;
|
|
void* replicaSet = NULL;
|
|
winStatus = ::NtFrsApiEnumBackupRestoreSets(autoContext.m_context, index, &replicaSet);
|
|
while (WIN_SUCCESS(winStatus)) {
|
|
FRS_ASSERT(replicaSet != NULL);
|
|
|
|
// each replica set reports the same excludes. only add them once.
|
|
CAutoFrsPointer<WCHAR> filters = NULL;
|
|
if (!ProcessReplicaSet(autoContext.m_context, replicaSet, pMetadata, filters.GetAddress()))
|
|
return false;
|
|
if (filters != NULL && index == 0) {
|
|
if (!AddExcludes(pMetadata, filters)) {
|
|
DPRINT(1, "failed to add filtered exclude files\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
++index;
|
|
winStatus = ::NtFrsApiEnumBackupRestoreSets(autoContext.m_context, index, &replicaSet);
|
|
}
|
|
|
|
if (winStatus != ERROR_NO_MORE_ITEMS) {
|
|
DPRINT1(1, "NtFrsApiEnumBackupRestoreSets failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CFrsWriter::AddExcludes(IVssCreateWriterMetadata* pMetadata, WCHAR* filters)
|
|
/*++
|
|
Routine Description:
|
|
This is a helper routine used by ProcessReplicaSet to create the exclude-file list.
|
|
Arguments:
|
|
pMetadata - Interface used to report on metadata
|
|
filters - list of exclude files
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::AddExcludes:"
|
|
|
|
WCHAR* currentFilter = filters;
|
|
// for each filtered filespec, add an exclude specification to the writer metadata
|
|
while (*currentFilter) {
|
|
WCHAR* path = NULL;
|
|
WCHAR* filespec = NULL;
|
|
bool recursive = false;
|
|
|
|
size_t excludeLength = wcslen(currentFilter); // --- grab size before we modify the string
|
|
|
|
if (!ParseExclude(currentFilter, &path, &filespec, &recursive)) {
|
|
DPRINT(1, "filtered exclude file has an incorrect format\n");
|
|
return false;
|
|
}
|
|
|
|
HRESULT hr = pMetadata->AddExcludeFiles(path, filespec, recursive);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "IVssBackupComponents::AddExcludeFiles failed with hresult 0x%08lx\n", hr);
|
|
return false;
|
|
}
|
|
|
|
currentFilter += excludeLength + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CFrsWriter::ParseExclude(WCHAR* exclude, WCHAR** path, WCHAR** filespec, bool* recursive)
|
|
/*++
|
|
Routine Description:
|
|
This is a helper routine used to parse an exclude specification.
|
|
Arguments:
|
|
exclude - the specification for the exclude file
|
|
path -OUT the root path of the exclude file
|
|
filespec - OUT the exclude filespec
|
|
recursive - OUT whether this is a recursive specification or not
|
|
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::ParseExclude:"
|
|
|
|
const WCHAR* RecursionSpec = L" /s";
|
|
const WCHAR DirSeperator = L'\\';
|
|
|
|
//verify parameters
|
|
FRS_ASSERT(exclude && path && filespec && recursive);
|
|
*path = *filespec = NULL;
|
|
*recursive = false;
|
|
|
|
// find the last wack in the path
|
|
WCHAR* loc = wcsrchr(exclude, DirSeperator);
|
|
if (loc == NULL)
|
|
return false;
|
|
|
|
// setup the return values
|
|
*loc = L'\0';
|
|
*path = exclude;
|
|
*filespec = loc + 1;
|
|
|
|
// check to see if this is a recursive specification
|
|
loc = wcsstr(*filespec, RecursionSpec);
|
|
if (loc != NULL) {
|
|
*loc = L'\0';
|
|
*recursive = true;
|
|
}
|
|
|
|
return (wcslen(*path) > 0) && (wcslen(*filespec) > 0);
|
|
}
|
|
|
|
bool CFrsWriter::ProcessReplicaSet(void* context, void* replicaSet, IVssCreateWriterMetadata* pMetadata, WCHAR** retFilters)
|
|
/*++
|
|
Routine Description:
|
|
This is a helper routine used by OnIdentify to create the writer metadata.
|
|
Arguments:
|
|
context - The context that identifies us to FRS.
|
|
replicaSet - The indentifier for the current replica set
|
|
pMetadata - Interface used to report on metadata
|
|
filters - return the list of filtered files
|
|
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::ProcessReplicaSet:"
|
|
|
|
FRS_ASSERT(retFilters);
|
|
*retFilters = NULL;
|
|
|
|
// constants that determine component names
|
|
const WCHAR* SysvolLogicalPath = L"SYSVOL";
|
|
|
|
DWORD winStatus = 0;
|
|
|
|
// all of these need to be defined here, since otherwise destructors don't get called when
|
|
// and SEH exception is thrown
|
|
CAutoFrsPointer<WCHAR> setType;
|
|
DWORD typeSize = 0;
|
|
|
|
CAutoFrsPointer<WCHAR> setGuid;
|
|
DWORD guidSize = 0;
|
|
|
|
CAutoFrsPointer<WCHAR> directory;
|
|
DWORD dirSize = 0;
|
|
|
|
CAutoFrsPointer<WCHAR> paths;
|
|
DWORD pathSize = 0;
|
|
|
|
CAutoFrsPointer<WCHAR> filters;
|
|
DWORD filterSize = 0;
|
|
|
|
__try {
|
|
// figure out what type of replica set this is
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetType(context, replicaSet, NULL, &typeSize);
|
|
FRS_ASSERT(winStatus == ERROR_MORE_DATA);
|
|
|
|
setType = (WCHAR*)::FrsAlloc(typeSize);
|
|
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetType(context, replicaSet, setType, &typeSize);
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "NtFrsApiGetBackupRestoreSetType failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
// figure out what the name of this replica set is
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetGuid(context, replicaSet, NULL, &guidSize);
|
|
FRS_ASSERT(winStatus == ERROR_MORE_DATA);
|
|
|
|
setGuid = (WCHAR*)::FrsAlloc(guidSize);
|
|
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetGuid(context, replicaSet, setGuid, &guidSize);
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "NtFrsApiGetBackupRestoreSetGuid failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
const WCHAR* logicalPath = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
if (wcscmp(setType, NTFRSAPI_REPLICA_SET_TYPE_ENTERPRISE) == 0 ||
|
|
wcscmp(setType, NTFRSAPI_REPLICA_SET_TYPE_DOMAIN) == 0) {
|
|
// if this is a SYSVOL replica set, add a component with the SYSVOL logical path
|
|
logicalPath = SysvolLogicalPath;
|
|
hr = pMetadata->AddComponent(VSS_CT_FILEGROUP, // type
|
|
logicalPath, // wszLogicalPath
|
|
setGuid, // wszComponentName
|
|
NULL, // wszCaption
|
|
NULL, // pbIcon
|
|
0, // cbIcon
|
|
false, // bRestoreMetadata
|
|
true, // bNotifyOnBackupComplete
|
|
true, // bSelectable
|
|
true // bSelectableForRestore
|
|
);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "IVssCreateWriterMetadata::AddComponent failed with hresult 0x%08lx\n", hr);
|
|
return false;
|
|
}
|
|
} else {
|
|
// otherwise, add a component a component with the generic logical path
|
|
logicalPath = setType;
|
|
hr = pMetadata->AddComponent(VSS_CT_FILEGROUP, // type
|
|
logicalPath, // wszLogicalPath
|
|
setGuid, // wszComponentName
|
|
NULL, // wszCaption
|
|
NULL, // pbIcon
|
|
0, // cbIcon
|
|
false, // bRestoreMetadata
|
|
true, // bNotifyOnBackupComplete
|
|
true, // bSelectable
|
|
true // bSelectableForRestore
|
|
);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "IVssCreateWriterMetadata::AddComponent failed with hresult 0x%08lx\n", hr);
|
|
return false;
|
|
}
|
|
|
|
// add the root replication directory to the filegroup. This isn't necessary for SYSVOL since
|
|
// that will be included in the call to NtFrsApiGetBackupRestoreSetPaths.
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetDirectory(context, replicaSet, &dirSize, NULL);
|
|
FRS_ASSERT(winStatus == ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
directory = (WCHAR*)::FrsAlloc(dirSize);
|
|
|
|
// I assume that the directory cannot change in this short windows. If so, we must loop.
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetDirectory(context, replicaSet, &dirSize, directory);
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "NtFrsApiGetBackupRestoreSetDirectory failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
hr = pMetadata->AddFilesToFileGroup(logicalPath, // wszLogicalPath
|
|
setGuid, // wszGroupName
|
|
directory, // wszPath
|
|
L"*", // wszFilespec
|
|
true, // bRecursive
|
|
NULL // wszAlternateLocation
|
|
);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "IVssCreateWriterMetadata::AddFilesToFileGroup failed with hresult 0x%08lx\n", hr);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetPaths(context,
|
|
replicaSet,
|
|
&pathSize,
|
|
NULL,
|
|
&filterSize,
|
|
NULL
|
|
);
|
|
FRS_ASSERT(winStatus == ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
paths = (WCHAR*)::FrsAlloc(pathSize);
|
|
filters = (WCHAR*)::FrsAlloc(filterSize);
|
|
|
|
// once again, I assume that the sizes won't change in this window
|
|
winStatus = ::NtFrsApiGetBackupRestoreSetPaths(context,
|
|
replicaSet,
|
|
&pathSize,
|
|
paths,
|
|
&filterSize,
|
|
filters
|
|
);
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "NtFrsApiGetBackupRestoreSetPaths failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
// add all of the paths to the group
|
|
WCHAR* currentPath = paths;
|
|
while (*currentPath) {
|
|
hr = pMetadata->AddFilesToFileGroup(logicalPath, // wszLogicalPath
|
|
setGuid, // wszGroupName
|
|
currentPath, // wszPath
|
|
L"*", // wszFilespec
|
|
true, // bRecursive
|
|
NULL // wszAlternateLocation
|
|
);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "IVssCreateWriterMetadata::AddFilesToFileGroup failed with hresult 0x%08lx\n", hr);
|
|
return false;
|
|
}
|
|
|
|
currentPath += wcslen(currentPath) + 1;
|
|
}
|
|
|
|
*retFilters = filters.Detach();
|
|
return true;
|
|
} __except(GetExceptionCode() == ERROR_OUTOFMEMORY) {
|
|
DPRINT(1, "Out of memory\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool STDMETHODCALLTYPE CFrsWriter::OnPrepareSnapshot()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called in response to an Identify event being sent to this writer. The writer
|
|
will freeze FRS in this event.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::OnPrepareSnapshot:"
|
|
|
|
DPRINT(4, "Received OnPrepareSnapshot event\n");
|
|
|
|
DWORD winStatus = ::FrsFreezeForBackup();
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "FrsFreezeForBackup failed with status 0x%08lx\n", winStatus);
|
|
CVssWriter::SetWriterFailure(VSS_E_WRITERERROR_RETRYABLE);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool STDMETHODCALLTYPE CFrsWriter::OnFreeze()
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::OnFreeze:"
|
|
|
|
DPRINT(4, "Received OnFreeze event\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool STDMETHODCALLTYPE CFrsWriter::OnThaw()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called in response to a Thaw event being sent to this writer. The writer
|
|
will thaw FRS in this event.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::OnThaw:"
|
|
|
|
DPRINT(4, "Received OnThaw event\n");
|
|
|
|
DWORD winStatus = ::FrsThawAfterBackup();
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "FrsThawAfterBackup failed with status 0x%08lx\n", winStatus);
|
|
CVssWriter::SetWriterFailure(VSS_E_WRITERERROR_RETRYABLE);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool STDMETHODCALLTYPE CFrsWriter::OnAbort()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called in response to an Abort event being sent to this writer. The writer
|
|
will thaw FRS in this event.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
boolean
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::OnAbort:"
|
|
|
|
DPRINT(4, "Received OnAbort event\n");
|
|
|
|
DWORD winStatus = ::FrsThawAfterBackup();
|
|
if (!WIN_SUCCESS(winStatus)) {
|
|
DPRINT1(1, "FrsThawAfterBackup failed with status 0x%08lx\n", winStatus);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
HRESULT CFrsWriter::CreateWriter()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called to create and initialize the FRS writer. It must be called from
|
|
a thread that has COM initialized with multi-threaded apartments.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
HRESULT
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::CreateWriter:"
|
|
|
|
// initialization is idempotent
|
|
if (m_pWriterInstance != NULL)
|
|
return S_OK;
|
|
|
|
// try and create the writer
|
|
m_pWriterInstance = new CFrsWriter();
|
|
if (m_pWriterInstance == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// try and initialize the writer
|
|
HRESULT hr = S_OK;
|
|
hr = m_pWriterInstance->Initialize();
|
|
if (FAILED(hr)) {
|
|
delete m_pWriterInstance;
|
|
m_pWriterInstance = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CFrsWriter::DestroyWriter()
|
|
/*++
|
|
Routine Description:
|
|
This routine is called to destroy the FRS writer.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
HRESULT
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::DestroyWriter:"
|
|
|
|
delete m_pWriterInstance;
|
|
m_pWriterInstance = NULL;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CFrsWriter::Initialize()
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::Initialize:"
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = CVssWriter::Initialize(WriterId, // WriterID
|
|
WriterName, // wszWriterName
|
|
VSS_UT_BOOTABLESYSTEMSTATE, // usage type
|
|
VSS_ST_OTHER, // source type
|
|
VSS_APP_SYSTEM // nLevel
|
|
);
|
|
if (FAILED(hr)) {
|
|
DPRINT1(1, "CVssWriter::Initialize failed with hresult 0x%08lx\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
hr = CVssWriter::Subscribe();
|
|
if (FAILED(hr))
|
|
DPRINT1(1, "CVssWriter::Subscribe failed with hresult 0x%08lx\n", hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CFrsWriter::Uninitialize()
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "CFrsWriter::Uninitialize:"
|
|
|
|
HRESULT hr = CVssWriter::Unsubscribe();
|
|
if (FAILED(hr))
|
|
DPRINT1(1, "CVssWriter::Unsubscribe failed with hresult 0x%08lx\n", hr);
|
|
}
|
|
|