Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

888 lines
24 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
critdrv.cpp
Abstract:
This module contains routines create a list of the critical volumes on a
system. This is lifted directly from base\fs\utils\ntback50\ui.
Author:
Brian Berkowitz (brianb) 10-Mar-2000
Environment:
User-mode only.
Revision History:
10-Mar-2000 brianb
Initial creation
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <objbase.h>
#include <initguid.h>
#include <frsapip.h>
#include <critdrv.h>
// FRS iteration class. Used to iterate through replica sets to
// determine the paths for these replica sets
// constructor
CFRSIter::CFRSIter() :
m_fInitialized(FALSE),
m_hLib(NULL),
m_pfnFrsInitBuRest(NULL),
m_pfnFrsEndBuRest(NULL),
m_pfnFrsGetSets(NULL),
m_pfnFrsEnumSets(NULL),
m_pfnFrsIsSetSysVol(NULL),
m_pfnFrsGetPath(NULL),
m_pfnFrsGetOtherPaths(NULL),
m_stateIteration(x_IterNotStarted)
{
}
// destructor
CFRSIter::~CFRSIter()
{
if (m_stateIteration == x_IterStarted)
CleanupIteration();
if (m_hLib)
FreeLibrary(m_hLib);
}
// initialize entry points and load library
void CFRSIter::Init()
{
if (m_fInitialized)
return;
// load library
m_hLib = LoadLibrary(L"ntfrsapi.dll");
if (m_hLib)
{
// assign etntry points
m_pfnFrsInitBuRest = (PF_FRS_INIT) GetProcAddress(m_hLib, "NtFrsApiInitializeBackupRestore");
m_pfnFrsEndBuRest = (PF_FRS_DESTROY) GetProcAddress(m_hLib, "NtFrsApiDestroyBackupRestore");
m_pfnFrsGetSets = (PF_FRS_GET_SETS) GetProcAddress(m_hLib, "NtFrsApiGetBackupRestoreSets");
m_pfnFrsEnumSets = (PF_FRS_ENUM_SETS) GetProcAddress(m_hLib, "NtFrsApiEnumBackupRestoreSets");
m_pfnFrsIsSetSysVol = (PF_FRS_IS_SYSVOL) GetProcAddress(m_hLib, "NtFrsApiIsBackupRestoreSetASysvol");
m_pfnFrsGetPath = (PF_FRS_GET_PATH) GetProcAddress(m_hLib, "NtFrsApiGetBackupRestoreSetDirectory");
m_pfnFrsGetOtherPaths = (PF_FRS_GET_OTHER_PATHS) GetProcAddress(m_hLib, "NtFrsApiGetBackupRestoreSetPaths");
if (m_pfnFrsInitBuRest == NULL ||
m_pfnFrsEndBuRest == NULL ||
m_pfnFrsGetSets == NULL ||
m_pfnFrsEnumSets == NULL ||
m_pfnFrsIsSetSysVol == NULL ||
m_pfnFrsGetOtherPaths == NULL ||
m_pfnFrsGetPath == NULL)
{
// if we can't get to any entry point, free library and
// fail operation
FreeLibrary(m_hLib);
m_hLib = NULL;
}
}
// indicate that operation is successful
m_fInitialized = TRUE;
}
// initialize the iterator. Return FALSE if iterator is known to be empty
//
BOOL CFRSIter::BeginIteration()
{
ASSERT(m_stateIteration == x_IterNotStarted);
DWORD status;
if (m_hLib == NULL)
{
// if we are not initialized, then there is nothing to iterate
// over
m_stateIteration = x_IterComplete;
return FALSE;
}
// initialize FRS backup restore apis
status = m_pfnFrsInitBuRest
(
NULL,
NTFRSAPI_BUR_FLAGS_NORMAL|NTFRSAPI_BUR_FLAGS_BACKUP,
&m_frs_context
);
if (status != ERROR_SUCCESS)
{
// if this fails then we are done
m_stateIteration = x_IterComplete;
return FALSE;
}
// indicate that we started the iteration
m_stateIteration = x_IterStarted;
status = m_pfnFrsGetSets(m_frs_context);
if (status != ERROR_SUCCESS)
{
// if there are no sets, then indicate we are done
CleanupIteration();
return FALSE;
}
// start at first set
m_iset = 0;
return TRUE;
}
// cleanup iteration after scanning the last element
void CFRSIter::CleanupIteration()
{
m_pfnFrsEndBuRest(&m_frs_context, NTFRSAPI_BUR_FLAGS_NONE, NULL, NULL, NULL);
m_stateIteration = x_IterComplete;
}
// get next iteration set returning the path to the set
// NULL indicates end of iteration
// If fSkipToSysVol is TRUE then ignore non SysVol replication sets
LPWSTR CFRSIter::GetNextSet(BOOL fSkipToSysVol, LPWSTR *pwszPaths)
{
ASSERT(pwszPaths);
ASSERT(m_stateIteration != x_IterNotStarted);
if (m_stateIteration == x_IterComplete)
// if iteration is complete, then we are done
return NULL;
PVOID frs_set;
while(TRUE)
{
// get a set
DWORD status = m_pfnFrsEnumSets(m_frs_context, m_iset, &frs_set);
if (status != ERROR_SUCCESS)
{
// if this fails, then we are done
CleanupIteration();
return NULL;
}
if (fSkipToSysVol)
{
// we are looking for system volumes
BOOL fSysVol;
// test whether this is a system volume
status = m_pfnFrsIsSetSysVol(m_frs_context, frs_set, &fSysVol);
if (status != ERROR_SUCCESS)
{
// if this operation fails, terminate iteration
CleanupIteration();
return NULL;
}
if (!fSysVol)
{
// if not a system volume, then skip to the next
// replica set
m_iset++;
continue;
}
}
// scratch pad for path
WCHAR wsz[MAX_PATH];
DWORD cbPath = MAX_PATH * sizeof(WCHAR);
// get path to root of the replica set
status = m_pfnFrsGetPath
(
m_frs_context,
frs_set,
&cbPath,
wsz
);
WCHAR *wszNew = NULL;
// allocate memory for root
if (status == ERROR_SUCCESS || status == ERROR_INSUFFICIENT_BUFFER)
{
wszNew = new WCHAR[cbPath/sizeof(WCHAR)];
// if allocation fails, then throw OOM
if (wszNew == NULL)
throw E_OUTOFMEMORY;
if (status == ERROR_SUCCESS)
// if the operation was successful, then copy
// path into memory
memcpy(wszNew, wsz, cbPath);
else
{
// otherwise redo the operation
status = m_pfnFrsGetPath
(
m_frs_context,
frs_set,
&cbPath,
wszNew
);
if (status != ERROR_SUCCESS)
{
// if operation failed then second time, then
// delete allocated memory and terminate iteration
delete wszNew;
CleanupIteration();
return NULL;
}
}
}
else
{
// if operation failed due to any error other than
// insufficient buffer, then terminate the iteration
CleanupIteration();
return NULL;
}
// scratch pad for filters
WCHAR wszFilter[MAX_PATH];
DWORD cbFilter = MAX_PATH * sizeof(WCHAR);
// length of scratch pad for paths
cbPath = MAX_PATH * sizeof(WCHAR);
// obtain other paths
status = m_pfnFrsGetOtherPaths
(
m_frs_context,
frs_set,
&cbPath,
wsz,
&cbFilter,
wszFilter
);
WCHAR *wszNewPaths = NULL;
WCHAR *wszNewFilter = NULL;
if (status == ERROR_SUCCESS || status == ERROR_INSUFFICIENT_BUFFER)
{
// allocate space for paths
wszNewPaths = new WCHAR[cbPath/sizeof(WCHAR)];
// allocate space for filters
wszNewFilter = new WCHAR[cbFilter/sizeof(WCHAR)];
if (wszNew == NULL || wszFilter == NULL)
{
// if any allocation fails, then throw OOM
delete wszNew;
throw E_OUTOFMEMORY;
}
if (status == ERROR_SUCCESS)
{
// if operation was successful, then copy
// in allocated paths
memcpy(wszNewPaths, wsz, cbPath);
memcpy(wszNewFilter, wszFilter, cbFilter);
}
else
{
status = m_pfnFrsGetOtherPaths
(
m_frs_context,
frs_set,
&cbPath,
wszNew,
&cbFilter,
wszNewFilter
);
if (status != ERROR_SUCCESS)
{
delete wszNew;
delete wszNewFilter;
CleanupIteration();
return NULL;
}
}
}
else
{
// if any error other than success or INSUFFICENT_BUFFER
// then terminate iteration
CleanupIteration();
return NULL;
}
// delete allocated filter
delete wszNewFilter;
// set iteration to next set
m_iset++;
// return pointer to paths
*pwszPaths = wszNewPaths;
// return path of root of replicated set
return wszNew;
}
}
// terminate iteration, cleaning up anything that needs to be
// cleaned up
//
void CFRSIter::EndIteration()
{
ASSERT(m_stateIteration != x_IterNotStarted);
if (m_stateIteration == x_IterStarted)
CleanupIteration();
// indicate that iteration is no longer in progress
m_stateIteration = x_IterNotStarted;
}
// constructor for string data structure
CWStringData::CWStringData()
{
m_psdlFirst = NULL;
m_psdlCur = NULL;
}
// destructor
CWStringData::~CWStringData()
{
while(m_psdlFirst)
{
WSTRING_DATA_LINK *psdl = m_psdlFirst;
m_psdlFirst = m_psdlFirst->m_psdlNext;
delete psdl;
}
}
// allocate a new link
void CWStringData::AllocateNewLink()
{
WSTRING_DATA_LINK *psdl = new WSTRING_DATA_LINK;
if (psdl == NULL)
throw E_OUTOFMEMORY;
psdl->m_psdlNext = NULL;
if (m_psdlCur)
{
ASSERT(m_psdlFirst);
m_psdlCur->m_psdlNext = psdl;
m_psdlCur = psdl;
}
else
{
ASSERT(m_psdlFirst == NULL);
m_psdlFirst = m_psdlCur = psdl;
}
m_ulNextString = 0;
}
// allocate a string
LPWSTR CWStringData::AllocateString(unsigned cwc)
{
ASSERT(cwc <= sizeof(m_psdlCur->rgwc));
if (m_psdlCur == NULL)
AllocateNewLink();
if (sizeof(m_psdlCur->rgwc) <= (cwc + 1 + m_ulNextString) * sizeof(WCHAR))
AllocateNewLink();
unsigned ulOff = m_ulNextString;
m_ulNextString += cwc + 1;
return m_psdlCur->rgwc + ulOff;
}
// copy a string
LPWSTR CWStringData::CopyString(LPCWSTR wsz)
{
unsigned cwc = (wsz == NULL) ? 0 : wcslen(wsz);
LPWSTR wszNew = AllocateString(cwc);
memcpy(wszNew, wsz, cwc * sizeof(WCHAR));
wszNew[cwc] = '\0';
return wszNew;
}
// constructor for volume list
CVolumeList::CVolumeList() :
m_rgwszVolumes(NULL), // array of volumes
m_cwszVolumes(0), // # of volumes in array
m_cwszVolumesMax(0), // size of array
m_rgwszPaths(NULL), // array of paths
m_cwszPaths(0), // # of paths in array
m_cwszPathsMax(0) // size of array
{
}
// destructor
CVolumeList::~CVolumeList()
{
delete m_rgwszPaths; // delete paths array
delete m_rgwszVolumes; // delete volumes array
}
// add a path to the list if it is not already there
// return TRUE if it is a new path
// return FALSE if path is already in list
//
BOOL CVolumeList::AddPathToList(LPWSTR wszPath)
{
// look for path in list. If found, then return FALSE
for(unsigned iwsz = 0; iwsz < m_cwszPaths; iwsz++)
{
if (_wcsicmp(wszPath, m_rgwszPaths[iwsz]) == 0)
return FALSE;
}
// grow pat array if needed
if (m_cwszPaths == m_cwszPathsMax)
{
// grow path array
LPCWSTR *rgwsz = new LPCWSTR[m_cwszPaths + x_cwszPathsInc];
// throw OOM if memory allocation fails
if (rgwsz == NULL)
throw(E_OUTOFMEMORY);
memcpy(rgwsz, m_rgwszPaths, m_cwszPaths * sizeof(LPCWSTR));
delete m_rgwszPaths;
m_rgwszPaths = rgwsz;
m_cwszPathsMax += x_cwszPathsInc;
}
// add path to array
m_rgwszPaths[m_cwszPaths++] = m_sd.CopyString(wszPath);
return TRUE;
}
// add a volume to the list if it is not already there
// return TRUE if it is added
// return FALSE if it is already on the list
//
BOOL CVolumeList::AddVolumeToList(LPCWSTR wszVolume)
{
// look for volume in array. If found then return FALSE
for(unsigned iwsz = 0; iwsz < m_cwszVolumes; iwsz++)
{
if (_wcsicmp(wszVolume, m_rgwszVolumes[iwsz]) == 0)
return FALSE;
}
// grow volume array if necessary
if (m_cwszVolumes == m_cwszVolumesMax)
{
// grow volume array
LPCWSTR *rgwsz = new LPCWSTR[m_cwszVolumes + x_cwszVolumesInc];
if (rgwsz == NULL)
throw(E_OUTOFMEMORY);
memcpy(rgwsz, m_rgwszVolumes, m_cwszVolumes * sizeof(LPCWSTR));
delete m_rgwszVolumes;
m_rgwszVolumes = rgwsz;
m_cwszVolumesMax += x_cwszVolumesInc;
}
// add volume name to array
m_rgwszVolumes[m_cwszVolumes++] = m_sd.CopyString(wszVolume);
return TRUE;
}
const WCHAR x_wszVolumeRootName[] = L"\\\\?\\GlobalRoot\\Device\\";
const unsigned x_cwcVolumeRootName = sizeof(x_wszVolumeRootName)/sizeof(WCHAR) - 1;
// add a path to our tracking list. If the path is new add it to the
// paths list. If it is a mount point or the root of a volume, then
// determine the volume and add the volume to the list of volumes
//
// can throw E_OUTOFMEMORY
//
void CVolumeList::AddPath(LPWSTR wszTop)
{
// if path is known about then return
if (!AddPathToList(wszTop))
return;
// length of path
unsigned cwc = wcslen(wszTop);
// copy path so that we can add backslash to the end of the path
LPWSTR wszCopy = new WCHAR[cwc + 2];
// if fails, then throw OOM
if (wszCopy == NULL)
throw E_OUTOFMEMORY;
// copyh in original path
memcpy(wszCopy, wszTop, cwc * sizeof(WCHAR));
// append backslash
wszCopy[cwc] = L'\\';
wszCopy[cwc + 1] = L'\0';
while(TRUE)
{
// check for a device root
unsigned cwc = wcslen(wszCopy);
if ((cwc == 3 && wszCopy[1] == ':') ||
(cwc > x_cwcVolumeRootName &&
memcmp(wszCopy, x_wszVolumeRootName, x_cwcVolumeRootName * sizeof(WCHAR)) == 0))
{
// call TryAddVolume with TRUE indicating this is a volume root
TryAddVolumeToList(wszCopy, TRUE);
break;
}
// call TryAddVolume indicating this is not a known device root
if (TryAddVolumeToList(wszCopy, FALSE))
break;
// move back to previous backslash
WCHAR *pch = wszCopy + cwc - 2;
while(--pch > wszTop)
{
if (pch[1] == L'\\')
{
pch[2] = L'\0';
break;
}
}
if (pch == wszTop)
break;
// if path is known about then return
if (!AddPathToList(wszCopy))
break;
}
}
// determine if a path is a volume. If so then add it to the volume
// list and return TRUE. If not, then return FALSE. fVolumeRoot indicates
// that the path is of the form x:\. Otherwise the path is potentially
// an mount point. Validate that it is a reparse point and then try
// finding its volume guid. If this fails, then assume that it is not
// a volume root. If it succeeds, then add the volume guid to the volumes
// list and return TRUE.
//
BOOL CVolumeList::TryAddVolumeToList(LPCWSTR wszPath, BOOL fVolumeRoot)
{
WCHAR wszVolume[256];
if (fVolumeRoot)
{
if (!GetVolumeNameForVolumeMountPoint(wszPath, wszVolume, sizeof(wszVolume)/sizeof(WCHAR)))
// might be the EFI system partition, just pass in the path as the volume string.
wcscpy( wszVolume, wszPath );
//throw E_UNEXPECTED;
}
else
{
DWORD dw = GetFileAttributes(wszPath);
if (dw == -1)
return FALSE;
if ((dw & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
return FALSE;
if (!GetVolumeNameForVolumeMountPoint(wszPath, wszVolume, sizeof(wszVolume)/sizeof(WCHAR)))
return FALSE;
}
AddVolumeToList(wszVolume);
return TRUE;
}
// add a file to the volume list. Simply finds the parent path and adds it
//
void CVolumeList::AddFile(LPWSTR wsz)
{
unsigned cwc = wcslen(wsz);
WCHAR *pwc = wsz + cwc - 1;
while(pwc[1] != L'\\' && pwc != wsz)
continue;
pwc[1] = '\0';
AddPath(wsz);
}
// obtain list of volumes as a MULTI_SZ, caller is responsible for freeing
// the string
//
LPWSTR CVolumeList::GetVolumeList()
{
unsigned cwc = 1;
// compute length of volume list it is length of each string +
// null character + null charactor for last double NULL
for(unsigned iwsz = 0; iwsz < m_cwszVolumes; iwsz++)
cwc += wcslen(m_rgwszVolumes[iwsz]) + 1;
// allocate string
LPWSTR wsz = new WCHAR[cwc];
// throw OOM if memory allocation failed
if (wsz == NULL)
throw E_OUTOFMEMORY;
// copy in strings
WCHAR *pwc = wsz;
for(unsigned iwsz = 0; iwsz < m_cwszVolumes; iwsz++)
{
cwc = wcslen(m_rgwszVolumes[iwsz]) + 1;
memcpy(pwc, m_rgwszVolumes[iwsz], cwc * sizeof(WCHAR));
/* replace \\?\ with \??\ */
memcpy(pwc, L"\\??", sizeof(WCHAR) * 3);
// delete trailing backslash if it exists
if (pwc[cwc - 2] == L'\\')
{
pwc[cwc-2] = L'\0';
cwc--;
}
pwc += cwc;
}
// last null termination
*pwc = L'\0';
return wsz;
}
// path to volume of boot device is
// HKEY_LOCAL_MACHINE\System\Setup
//with Value of SystemPartition parametr
LPCWSTR x_SetupRoot = L"System\\Setup";
// magic perfix for volume devices
WCHAR x_wszWin32VolumePrefix[] = L"\\\\?\\GlobalRoot";
const unsigned x_cwcWin32VolumePrefix = sizeof(x_wszWin32VolumePrefix)/sizeof(WCHAR) - 1;
// structure representing a path from
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
// and a value to look up
typedef struct _SVCPARM
{
LPCWSTR wszPath;
LPCWSTR wszValue;
} SVCPARM;
const SVCPARM x_rgdbparms[] =
{
{L"CertSvc\\Configuration", L"DBDirectory"},
{L"CertSvc\\Configuration", L"DBLogDirectory"},
{L"CertSvc\\Configuration", L"DBSystemDirectory"},
{L"CertSvc\\Configuration", L"DBTempDirectory"},
{L"DHCPServer\\Parameters", L"DatabasePath"},
{L"DHCPServer\\Parameters", L"DatabaseName"},
{L"DHCPServer\\Parameters", L"BackupDatabasePath"},
{L"NTDS\\Parameters", L"Database backup path"},
{L"NTDS\\Parameters", L"Databases log files path"},
{L"Ntfrs\\Parameters\\Replica Sets", L"Database Directory"}
};
const unsigned x_cdbparms = sizeof(x_rgdbparms)/sizeof(SVCPARM);
LPCWSTR x_wszSvcRoot = L"System\\CurrentControlSet\\Services";
// add roots for various services
BOOL AddServiceRoots(CVolumeList &vl)
{
HKEY hkeyRoot;
// open HKLM\System\CurrentControlSet\Services
if (RegOpenKey(HKEY_LOCAL_MACHINE, x_wszSvcRoot, &hkeyRoot) != ERROR_SUCCESS)
return FALSE;
// loop through individual paths
for(unsigned i = 0; i < x_cdbparms; i++)
{
WCHAR wsz[MAX_PATH*4];
LPCWSTR wszPath = x_rgdbparms[i].wszPath;
LPCWSTR wszValue = x_rgdbparms[i].wszValue;
HKEY hkey;
DWORD cb = sizeof(wsz);
DWORD type;
// open path, skip if open fails
if (RegOpenKey(hkeyRoot, wszPath, &hkey) != ERROR_SUCCESS)
continue;
// add path to volume list if query succeeds
if (RegQueryValueEx
(
hkey,
wszValue,
NULL,
&type,
(BYTE *) wsz,
&cb
) == ERROR_SUCCESS)
vl.AddPath(wsz);
// close key
RegCloseKey(hkey);
}
// close root key
RegCloseKey(hkeyRoot);
return TRUE;
}
// add volume root of SystemDrive (drive wee boot off of
BOOL AddSystemPartitionRoot(CVolumeList &vl)
{
HKEY hkeySetup;
WCHAR wsz[MAX_PATH];
// open HKLM\System\Setup
if (RegOpenKey(HKEY_LOCAL_MACHINE, x_SetupRoot, &hkeySetup) != ERROR_SUCCESS)
return FALSE;
DWORD cb = sizeof(wsz);
DWORD type;
// query SystemPartition value
if (RegQueryValueEx
(
hkeySetup,
L"SystemPartition",
NULL,
&type,
(BYTE *) wsz,
&cb
) != ERROR_SUCCESS)
{
// if fails, return FALSE
RegCloseKey(hkeySetup);
return FALSE;
}
// compute size of needed buffer
unsigned cwc = wcslen(wsz);
unsigned cwcNew = x_cwcWin32VolumePrefix + cwc + 1;
LPWSTR wszNew = new WCHAR[cwcNew];
// return failure if memory allocation fials
if (wszNew == NULL)
return FALSE;
// append \\?\GlobalRoot\ to device name
memcpy(wszNew, x_wszWin32VolumePrefix, x_cwcWin32VolumePrefix * sizeof(WCHAR));
memcpy(wszNew + x_cwcWin32VolumePrefix, wsz, cwc * sizeof(WCHAR));
RegCloseKey(hkeySetup);
wszNew[cwcNew-1] = L'\0';
try {
// add path based on device root
vl.AddPath(wszNew);
} catch(...)
{
delete wszNew;
return FALSE;
}
// delete allocated memory
delete wszNew;
return TRUE;
}
// find critical volumes. Return multistring of volume names
// using guid naming convention
LPWSTR pFindCriticalVolumes()
{
WCHAR wsz[MAX_PATH * 4];
// find location of system root
if (!ExpandEnvironmentStrings(L"%systemroot%", wsz, sizeof(wsz)/sizeof(WCHAR)))
{
wprintf(L"ExpandEnvironmentStrings failed for reason %d", GetLastError());
return NULL;
}
CVolumeList vl;
LPWSTR wszPathsT = NULL;
LPWSTR wszT = NULL;
try
{
// add boot drive
if (!AddSystemPartitionRoot(vl))
return NULL;
// add roots for various services
if (!AddServiceRoots(vl))
return NULL;
// add systemroot drive
vl.AddPath(wsz);
{
// add roots for SYSVOL
CFRSIter fiter;
fiter.Init();
fiter.BeginIteration();
while(TRUE)
{
wszT = fiter.GetNextSet(TRUE, &wszPathsT);
if (wszT == NULL)
break;
vl.AddPath(wszT);
LPWSTR wszPathT = wszPathsT;
while(*wszPathT != NULL)
{
vl.AddPath(wszPathT);
wszPathT += wcslen(wszPathT);
}
delete wszT;
delete wszPathsT;
wszT = NULL;
wszPathsT = NULL;
}
fiter.EndIteration();
}
}
catch(...)
{
delete wszT;
delete wszPathsT;
}
// return volume list
return vl.GetVolumeList();
}