|
|
/*++
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 (wszNewPaths == NULL || wszNewFilter == NULL) { // if any allocation fails, then throw OOM
delete [] wszNewPaths; delete [] wszNewFilter; 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
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(); }
|