7940 lines
239 KiB
C
7940 lines
239 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
api.c
|
||
|
||
Abstract:
|
||
Application Programmer's Interface
|
||
|
||
The dcpromo APIs are used when promoting or demoting a machine. The
|
||
APIs seed the sysvols after promotion. The sysvols are deleted
|
||
during demotion.
|
||
|
||
The poke API forces the service on the indicated machine to
|
||
immediately poll the DS.
|
||
|
||
Author:
|
||
Billy J. Fuller 31-Dec-1997
|
||
|
||
Environment
|
||
User mode winnt
|
||
|
||
--*/
|
||
|
||
#include <ntreppch.h>
|
||
#pragma hdrstop
|
||
#include <frs.h>
|
||
#include <ntfrsapi.h>
|
||
#include <lmaccess.h>
|
||
#include <lmapibuf.h>
|
||
#include <frssup.h>
|
||
|
||
|
||
CRITICAL_SECTION NtFrsApi_GlobalLock;
|
||
CRITICAL_SECTION NtFrsApi_ThreadLock;
|
||
DWORD NtFrsApi_State;
|
||
DWORD NtFrsApi_ServiceState;
|
||
DWORD NtFrsApi_ServiceWaitHint;
|
||
HANDLE NtFrsApi_ShutDownEvent;
|
||
|
||
PWCHAR NtFrsApi_ServiceLongName = SERVICE_LONG_NAME;
|
||
|
||
//
|
||
// API Version Information
|
||
//
|
||
PCHAR NtFrsApi_Module = __FILE__;
|
||
PCHAR NtFrsApi_Date = __DATE__;
|
||
PCHAR NtFrsApi_Time = __TIME__;
|
||
|
||
|
||
// Seed the sysvol after dcpromo
|
||
// Update the registry, delete existing sysvols (w/o error),
|
||
// set service to auto-start
|
||
//
|
||
|
||
//
|
||
// NTFRSAPI States
|
||
//
|
||
#define NTFRSAPI_LOADED (00)
|
||
#define NTFRSAPI_PREPARING (10)
|
||
#define NTFRSAPI_PREPARED_SERVICE (15)
|
||
#define NTFRSAPI_PREPARED (20)
|
||
#define NTFRSAPI_COMMITTING (30)
|
||
#define NTFRSAPI_COMMITTED (40)
|
||
#define NTFRSAPI_ABORTING (50)
|
||
#define NTFRSAPI_ABORTED (60)
|
||
|
||
//
|
||
// Useful macros
|
||
//
|
||
#undef GET_EXCEPTION_CODE
|
||
#define GET_EXCEPTION_CODE(_x_) \
|
||
{ \
|
||
(_x_) = GetExceptionCode(); \
|
||
if (((LONG)(_x_)) < 0) { \
|
||
(_x_) = FRS_ERR_INTERNAL_API; \
|
||
} \
|
||
NTFRSAPI_DBG_PRINT2("Exception caught: %d, 0x%08x\n", (_x_), (_x_)); \
|
||
}
|
||
|
||
#define FREE(_x_) { if (_x_) { LocalFree(_x_); (_x_) = NULL; } }
|
||
|
||
//
|
||
// Status Polling Interval
|
||
//
|
||
#define STATUS_POLLING_INTERVAL (1 * 1000) // 1 second
|
||
|
||
//
|
||
// Ldap timeout
|
||
//
|
||
#define NTFRSAPI_LDAP_CONNECT_TIMEOUT 30 // 30 seconds.
|
||
|
||
|
||
//
|
||
// DEBUG LOGGING
|
||
//
|
||
#define NTFRSAPI_DBG_LOG_DIR L"%SystemRoot%\\debug"
|
||
#define NTFRSAPI_DBG_LOG_FILE L"\\NtFrsApi.log"
|
||
WCHAR NtFrsApi_Dbg_LogFile[MAX_PATH + 1];
|
||
FILE *NtFrsApi_Dbg_LogFILE;
|
||
CRITICAL_SECTION NtFrsApi_Dbg_Lock;
|
||
|
||
//
|
||
// Semaphore name used to serialize backup restore operations.
|
||
//
|
||
#define NTFRS_BACKUP_RESTORE_SEMAPHORE L"NtFrs Backup Restore Semaphore"
|
||
|
||
|
||
#define CLEANUP_CB(_cb_func, _str, _wstatus, _branch) \
|
||
if (!WIN_SUCCESS(_wstatus)) { \
|
||
NtFrsApi_CallBackOnWStatus((_cb_func), (_str), _wstatus); \
|
||
goto _branch; \
|
||
}
|
||
|
||
|
||
#define MAX_DN (8 * MAX_PATH)
|
||
|
||
WCHAR DsDeleteDefaultDn[MAX_PATH + 1];
|
||
WCHAR DsDeleteConfigDn[MAX_PATH + 1];
|
||
WCHAR DsDeleteComputerName[MAX_COMPUTERNAME_LENGTH + 2];
|
||
WCHAR DsDeleteDomainDnsName[MAX_PATH + 2];
|
||
PLDAP DsDeleteLdap;
|
||
|
||
//
|
||
// Name components for FQDN substitution. Used to build a string substitution
|
||
// array that is driven by a table to build the desired FQDN.
|
||
//
|
||
typedef enum _FQDN_ARG_STRING {
|
||
FQDN_END = 0, // Null
|
||
FQDN_ComputerName, // DsDeleteComputerName,
|
||
FQDN_ConfigName, // DsDeleteConfigDn,
|
||
FQDN_RepSetName, // Thread->ReplicaSetName,
|
||
FQDN_DefaultDn, // DsDeleteDefaultDn,
|
||
FQDN_CN_SYSVOLS, // CN_SYSVOLS,
|
||
FQDN_CN_SERVICES, // CN_SERVICES
|
||
FQDN_CN_DOMAIN_SYSVOL, // CN_DOMAIN_SYSVOL,
|
||
FQDN_CN_NTFRS_SETTINGS, // CN_NTFRS_SETTINGS,
|
||
FQDN_CN_SYSTEM, // CN_SYSTEM,
|
||
FQDN_CN_SUBSCRIPTIONS, // CN_SUBSCRIPTIONS,
|
||
FQDN_CN_COMPUTERS, // CN_COMPUTERS
|
||
FQDN_MAX_COUNT
|
||
} FQDN_ARG_STRING;
|
||
|
||
PWCHAR FQDN_StdArgTable[FQDN_MAX_COUNT] = {
|
||
L"InvalidFqdnArgument",
|
||
DsDeleteComputerName,
|
||
DsDeleteConfigDn,
|
||
L"InvalidFqdnArgument", // Thread->ReplicaSetName,
|
||
DsDeleteDefaultDn,
|
||
CN_SYSVOLS,
|
||
CN_SERVICES,
|
||
CN_DOMAIN_SYSVOL,
|
||
CN_NTFRS_SETTINGS,
|
||
CN_SYSTEM,
|
||
CN_SUBSCRIPTIONS,
|
||
CN_COMPUTERS
|
||
};
|
||
|
||
typedef struct _FQDN_CONSTRUCTION_TABLE {
|
||
|
||
PCHAR Description; // For error messages.
|
||
PWCHAR Format; // Format string used to construct the FQDN
|
||
BYTE Arg[8]; // Array of offsets into the arg table ordered by the FQDN
|
||
|
||
} FQDN_CONSTRUCTION_TABLE, *PFQDN_CONSTRUCTION_TABLE;
|
||
|
||
//
|
||
// This table describes the FQDNs for FRS objects that need to be deleted.
|
||
// The entries contain the object names for both the Beta 2 and Beta 3 versions.
|
||
// The objects are deleted in the order specified by the table entries.
|
||
//
|
||
FQDN_CONSTRUCTION_TABLE FrsDsObjectDeleteTable[] = {
|
||
|
||
{"MemberDn(B2)", L"cn=%ws,cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_ComputerName, FQDN_RepSetName, FQDN_CN_SYSVOLS,
|
||
FQDN_CN_SERVICES, FQDN_ConfigName, FQDN_END},
|
||
|
||
{"MemberDn(B3)", L"cn=%ws,cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_ComputerName, FQDN_CN_DOMAIN_SYSVOL, FQDN_CN_NTFRS_SETTINGS,
|
||
FQDN_CN_SYSTEM, FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SetDn for(B2)", L"cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_RepSetName, FQDN_CN_SYSVOLS, FQDN_CN_SERVICES,
|
||
FQDN_ConfigName, FQDN_END},
|
||
|
||
{"SetDn for(B3)", L"cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_CN_DOMAIN_SYSVOL, FQDN_CN_NTFRS_SETTINGS, FQDN_CN_SYSTEM,
|
||
FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SettingsDn(B2)", L"cn=%ws,cn=%ws,%ws",
|
||
FQDN_CN_SYSVOLS, FQDN_CN_SERVICES, FQDN_ConfigName, FQDN_END},
|
||
|
||
{"SubscriberDn(B2)", L"cn=%ws,cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_RepSetName, FQDN_CN_SUBSCRIPTIONS, FQDN_ComputerName,
|
||
FQDN_CN_COMPUTERS, FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SubscriberDn(B3)", L"cn=%ws,cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_CN_DOMAIN_SYSVOL, FQDN_CN_SUBSCRIPTIONS, FQDN_ComputerName,
|
||
FQDN_CN_COMPUTERS, FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SubscriberDn$(B2)", L"cn=%ws,cn=%ws,cn=%ws$,cn=%ws,%ws",
|
||
FQDN_RepSetName, FQDN_CN_SUBSCRIPTIONS, FQDN_ComputerName,
|
||
FQDN_CN_COMPUTERS, FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SubscriberDn$(B3)", L"cn=%ws,cn=%ws,cn=%ws$,cn=%ws,%ws",
|
||
FQDN_CN_DOMAIN_SYSVOL, FQDN_CN_SUBSCRIPTIONS, FQDN_ComputerName,
|
||
FQDN_CN_COMPUTERS, FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SubscriptionsDn", L"cn=%ws,cn=%ws,cn=%ws,%ws",
|
||
FQDN_CN_SUBSCRIPTIONS, FQDN_ComputerName, FQDN_CN_COMPUTERS,
|
||
FQDN_DefaultDn, FQDN_END},
|
||
|
||
{"SubscriptionsDn$", L"cn=%ws,cn=%ws$,cn=%ws,%ws",
|
||
FQDN_CN_SUBSCRIPTIONS, FQDN_ComputerName, FQDN_CN_COMPUTERS,
|
||
FQDN_DefaultDn, FQDN_END},
|
||
|
||
{NULL, NULL, FQDN_END}
|
||
};
|
||
|
||
|
||
|
||
|
||
//
|
||
// return flags from DsGetDCInfo() & DsGetDcName() too?
|
||
//
|
||
FLAG_NAME_TABLE NtFrsApi_DsGetDcInfoFlagNameTable[] = {
|
||
{DS_PDC_FLAG , "DCisPDCofDomain " },
|
||
{DS_GC_FLAG , "DCIsGCofForest " },
|
||
{DS_LDAP_FLAG , "ServerSupportsLDAP_Server " },
|
||
{DS_DS_FLAG , "DCSupportsDSAndIsA_DC " },
|
||
{DS_KDC_FLAG , "DCIsRunningKDCSvc " },
|
||
{DS_TIMESERV_FLAG , "DCIsRunningTimeSvc " },
|
||
{DS_CLOSEST_FLAG , "DCIsInClosestSiteToClient " },
|
||
{DS_WRITABLE_FLAG , "DCHasWritableDS " },
|
||
{DS_GOOD_TIMESERV_FLAG , "DCRunningTimeSvcWithClockHW " },
|
||
{DS_DNS_CONTROLLER_FLAG , "DCNameIsDNSName " },
|
||
{DS_DNS_DOMAIN_FLAG , "DomainNameIsDNSName " },
|
||
{DS_DNS_FOREST_FLAG , "DnsForestNameIsDNSName " },
|
||
|
||
{0, NULL}
|
||
};
|
||
|
||
|
||
//
|
||
// Note: More replicated friggen code because the build environment for this
|
||
// api file is all messed up.
|
||
//
|
||
VOID
|
||
FrsFlagsToStr(
|
||
IN DWORD Flags,
|
||
IN PFLAG_NAME_TABLE NameTable,
|
||
IN ULONG Length,
|
||
OUT PSTR Buffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Routine to convert a Flags word to a descriptor string using the
|
||
supplied NameTable.
|
||
|
||
Arguments:
|
||
|
||
Flags - flags to convert.
|
||
|
||
NameTable - An array of FLAG_NAME_TABLE structs.
|
||
|
||
Length - Size of buffer in bytes.
|
||
|
||
Buffer - buffer with returned string.
|
||
|
||
Return Value:
|
||
|
||
Buffer containing printable string.
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "FrsFlagsToStr:"
|
||
|
||
PFLAG_NAME_TABLE pNT = NameTable;
|
||
LONG Remaining = Length-1;
|
||
|
||
|
||
//FRS_ASSERT((Length > 4) && (Buffer != NULL));
|
||
|
||
*Buffer = '\0';
|
||
if (Flags == 0) {
|
||
strncpy(Buffer, "<Flags Clear>", Length);
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Build a string for each bit set in the Flag name table.
|
||
//
|
||
while ((Flags != 0) && (pNT->Flag != 0)) {
|
||
|
||
if ((pNT->Flag & Flags) != 0) {
|
||
Remaining -= strlen(pNT->Name);
|
||
|
||
if (Remaining < 0) {
|
||
//
|
||
// Out of string buffer. Tack a "..." at the end.
|
||
//
|
||
Remaining += strlen(pNT->Name);
|
||
if (Remaining > 3) {
|
||
strcat(Buffer, "..." );
|
||
} else {
|
||
strcpy(&Buffer[Length-4], "...");
|
||
}
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Tack the name onto the buffer and clear the flag bit so we
|
||
// know what is left set when we run out of table.
|
||
//
|
||
strcat(Buffer, pNT->Name);
|
||
ClearFlag(Flags, pNT->Flag);
|
||
}
|
||
|
||
pNT += 1;
|
||
}
|
||
|
||
if (Flags != 0) {
|
||
//
|
||
// If any flags are still set give them back in hex.
|
||
//
|
||
sprintf( &Buffer[strlen(Buffer)], "0x%08x ", Flags );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
#define NTFRSAPI_DBG_INITIALIZE() NtFrsApi_Dbg_Initialize()
|
||
VOID
|
||
NtFrsApi_Dbg_Initialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Initialize the debug subsystem at load.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
InitializeCriticalSection(&NtFrsApi_Dbg_Lock);
|
||
}
|
||
|
||
#define NTFRSAPI_DBG_UNINITIALIZE() NtFrsApi_Dbg_UnInitialize()
|
||
VOID
|
||
NtFrsApi_Dbg_UnInitialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Shutdown the debug sunsystem when dll is detached.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
DeleteCriticalSection(&NtFrsApi_Dbg_Lock);
|
||
}
|
||
|
||
#define NTFRSAPI_DBG_UNPREPARE() NtFrsApi_Dbg_UnPrepare()
|
||
VOID
|
||
NtFrsApi_Dbg_UnPrepare(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
All done; close the debug subsystem.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
if (!NtFrsApi_Dbg_LogFILE) {
|
||
return;
|
||
}
|
||
fflush(NtFrsApi_Dbg_LogFILE);
|
||
fclose(NtFrsApi_Dbg_LogFILE);
|
||
NtFrsApi_Dbg_LogFILE = NULL;
|
||
}
|
||
|
||
#define NTFRSAPI_DBG_FLUSH() NtFrsApi_Dbg_Flush()
|
||
VOID
|
||
NtFrsApi_Dbg_Flush(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Flush the log file.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
if (!NtFrsApi_Dbg_LogFILE) {
|
||
return;
|
||
}
|
||
fflush(NtFrsApi_Dbg_LogFILE);
|
||
}
|
||
|
||
|
||
BOOL
|
||
NtFrsApi_Dbg_FormatLine(
|
||
IN PCHAR DebSub,
|
||
IN UINT LineNo,
|
||
IN PCHAR Line,
|
||
IN ULONG LineSize,
|
||
IN PUCHAR Format,
|
||
IN va_list argptr
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Format the line of debug output.
|
||
|
||
Arguments:
|
||
Not documented.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
ULONG LineUsed;
|
||
SYSTEMTIME SystemTime;
|
||
BOOL Ret = TRUE;
|
||
|
||
try {
|
||
//
|
||
// Increment the line count here to prevent counting
|
||
// the several DPRINTs that don't have a newline.
|
||
//
|
||
GetLocalTime(&SystemTime);
|
||
if (_snprintf(Line, LineSize, "<%-31s%4u: %5u: %02d:%02d:%02d> ",
|
||
(DebSub) ? DebSub : "NoName",
|
||
GetCurrentThreadId(),
|
||
LineNo,
|
||
SystemTime.wHour,
|
||
SystemTime.wMinute,
|
||
SystemTime.wSecond) < 0) {
|
||
Ret = FALSE;
|
||
} else {
|
||
LineUsed = strlen(Line);
|
||
if (((LineUsed + 1) >= LineSize) ||
|
||
(_vsnprintf(Line + LineUsed,
|
||
LineSize - LineUsed,
|
||
Format,
|
||
argptr) < 0)) {
|
||
Ret = FALSE;
|
||
}
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Ret = FALSE;
|
||
}
|
||
return Ret;
|
||
}
|
||
|
||
|
||
#define NTFRSAPI_DBG_PRINT0(_Format) \
|
||
NtFrsApi_Dbg_Print((PUCHAR)_Format, NTFRSAPI_MODULE, __LINE__)
|
||
|
||
#define NTFRSAPI_DBG_PRINT1(_Format, _p1) \
|
||
NtFrsApi_Dbg_Print((PUCHAR)_Format, NTFRSAPI_MODULE, __LINE__, \
|
||
_p1)
|
||
|
||
#define NTFRSAPI_DBG_PRINT2(_Format, _p1, _p2) \
|
||
NtFrsApi_Dbg_Print((PUCHAR)_Format, NTFRSAPI_MODULE, __LINE__, \
|
||
_p1, _p2)
|
||
|
||
#define NTFRSAPI_DBG_PRINT3(_Format, _p1, _p2, _p3) \
|
||
NtFrsApi_Dbg_Print((PUCHAR)_Format, NTFRSAPI_MODULE, __LINE__, \
|
||
_p1, _p2, _p3)
|
||
|
||
#define NTFRSAPI_DBG_PRINT4(_Format, _p1, _p2, _p3, _p4) \
|
||
NtFrsApi_Dbg_Print((PUCHAR)_Format, NTFRSAPI_MODULE, __LINE__, \
|
||
_p1, _p2, _p3, _p4)
|
||
|
||
VOID
|
||
NtFrsApi_Dbg_Print(
|
||
IN PUCHAR Format,
|
||
IN PCHAR DebSub,
|
||
IN UINT LineNo,
|
||
IN ... )
|
||
/*++
|
||
Routine Description:
|
||
Format and print a line of debug output to the log file.
|
||
|
||
Arguments:
|
||
Format - printf format
|
||
DebSub - module name
|
||
LineNo - file's line number
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
CHAR Line[512];
|
||
|
||
//
|
||
// varargs stuff
|
||
//
|
||
va_list argptr;
|
||
va_start(argptr, LineNo);
|
||
|
||
//
|
||
// Print the line to the log file
|
||
//
|
||
try {
|
||
EnterCriticalSection(&NtFrsApi_Dbg_Lock);
|
||
if (NtFrsApi_Dbg_LogFILE) {
|
||
if (NtFrsApi_Dbg_FormatLine(DebSub, LineNo, Line, sizeof(Line),
|
||
Format, argptr)) {
|
||
fprintf(NtFrsApi_Dbg_LogFILE, "%s", Line);
|
||
fflush(NtFrsApi_Dbg_LogFILE);
|
||
}
|
||
}
|
||
} finally {
|
||
LeaveCriticalSection(&NtFrsApi_Dbg_Lock);
|
||
}
|
||
va_end(argptr);
|
||
}
|
||
|
||
|
||
#define NTFRSAPI_DBG_PREPARE() NtFrsApi_Dbg_Prepare()
|
||
VOID
|
||
NtFrsApi_Dbg_Prepare(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Prepare the debug subsystem at NtFrsApi_Prepare().
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Dbg_Prepare:"
|
||
DWORD WStatus;
|
||
DWORD Len;
|
||
WCHAR TimeBuf[MAX_PATH];
|
||
|
||
Len = ExpandEnvironmentStrings(NTFRSAPI_DBG_LOG_DIR,
|
||
NtFrsApi_Dbg_LogFile,
|
||
MAX_PATH + 1);
|
||
if (Len == 0) {
|
||
return;
|
||
}
|
||
//
|
||
// Create the debug directory
|
||
//
|
||
if (!CreateDirectory(NtFrsApi_Dbg_LogFile, NULL)) {
|
||
WStatus = GetLastError();
|
||
if (!WIN_ALREADY_EXISTS(WStatus)) {
|
||
return;
|
||
}
|
||
}
|
||
wcscat(NtFrsApi_Dbg_LogFile, NTFRSAPI_DBG_LOG_FILE);
|
||
|
||
NtFrsApi_Dbg_LogFILE = _wfopen(NtFrsApi_Dbg_LogFile, L"ac");
|
||
if (!NtFrsApi_Dbg_LogFILE) {
|
||
return;
|
||
}
|
||
|
||
TimeBuf[0] = L'\0';
|
||
GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, NULL, NULL, TimeBuf, MAX_PATH);
|
||
}
|
||
|
||
|
||
#define NTFRSAPI_IPRINT0(_Info, _Format) \
|
||
NtFrsApi_Iprint(_Info, _Format)
|
||
|
||
#define NTFRSAPI_IPRINT1(_Info, _Format, _p1) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1)
|
||
|
||
#define NTFRSAPI_IPRINT2(_Info, _Format, _p1, _p2) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1, _p2)
|
||
|
||
#define NTFRSAPI_IPRINT3(_Info, _Format, _p1, _p2, _p3) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1, _p2, _p3)
|
||
|
||
#define NTFRSAPI_IPRINT4(_Info, _Format, _p1, _p2, _p3, _p4) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1, _p2, _p3, _p4)
|
||
|
||
#define NTFRSAPI_IPRINT5(_Info, _Format, _p1, _p2, _p3, _p4, _p5) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1, _p2, _p3, _p4, _p5)
|
||
|
||
#define NTFRSAPI_IPRINT6(_Info, _Format, _p1, _p2, _p3, _p4, _p5, _p6) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1, _p2, _p3, _p4, _p5, _p6)
|
||
|
||
#define NTFRSAPI_IPRINT7(_Info, _Format, _p1, _p2, _p3, _p4, _p5, _p6, _p7) \
|
||
NtFrsApi_Iprint(_Info, _Format, _p1, _p2, _p3, _p4, _p5, _p6, _p7)
|
||
|
||
VOID
|
||
NtFrsApi_Iprint(
|
||
IN PNTFRSAPI_INFO Info,
|
||
IN PCHAR Format,
|
||
IN ... )
|
||
/*++
|
||
Routine Description:
|
||
Format and print a line of information output into the info buffer.
|
||
|
||
Arguments:
|
||
Info - Info buffer
|
||
Format - printf format
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
PCHAR Line;
|
||
ULONG LineLen;
|
||
LONG LineSize;
|
||
|
||
//
|
||
// varargs stuff
|
||
//
|
||
va_list argptr;
|
||
va_start(argptr, Format);
|
||
|
||
//
|
||
// Print the line into the info buffer
|
||
//
|
||
try {
|
||
if (!FlagOn(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
|
||
Line = ((PCHAR)Info) + Info->OffsetToFree;
|
||
LineSize = (Info->SizeInChars - (ULONG)(Line - (PCHAR)Info)) - 1;
|
||
if (LineSize <= 0 ||
|
||
_vsnprintf(Line, LineSize, Format, argptr) < 0) {
|
||
SetFlag(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL);
|
||
} else {
|
||
LineLen = strlen(Line) + 1;
|
||
if (Info->CharsToSkip) {
|
||
if (LineLen > Info->CharsToSkip) {
|
||
Info->CharsToSkip = 0;
|
||
} else {
|
||
Info->CharsToSkip -= LineLen;
|
||
}
|
||
} else {
|
||
Info->OffsetToFree += LineLen;
|
||
Info->TotalChars += LineLen;
|
||
}
|
||
}
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
}
|
||
va_end(argptr);
|
||
}
|
||
|
||
|
||
BOOL
|
||
NtFrsApiCheckRpcError(
|
||
RPC_STATUS RStatus,
|
||
PCHAR Msg
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Print rpc error message
|
||
|
||
Arguments:
|
||
RStatus - Status return from RPC call.
|
||
Msg - message string. Optional.
|
||
|
||
Return Value:
|
||
True if there is an error else False.
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiCheckRpcError:"
|
||
|
||
if (RStatus != RPC_S_OK) {
|
||
if (Msg != NULL) {
|
||
NTFRSAPI_DBG_PRINT2("RpcError (%d) - %s\n", RStatus, Msg);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
DWORD
|
||
NtFrsApi_Fix_Comm_WStatus(
|
||
IN DWORD WStatus
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
If WStatus is an FRS error code, return it unaltered. Otherwise,
|
||
map the rpc status into the generic FRS_ERR_SERVICE_COMM.
|
||
|
||
Arguments:
|
||
WStatus - status from the rpc call.
|
||
|
||
Return Value:
|
||
Fixed WStatus
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Fix_Comm_WStatus:"
|
||
// TODO: replace these constants with symbollic values from winerror.h
|
||
if ( (WStatus < 8000) || (WStatus >= 8200) ) {
|
||
NTFRSAPI_DBG_PRINT1("Comm WStatus: not FRS (%d)\n", WStatus);
|
||
WStatus = FRS_ERR_SERVICE_COMM;
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
PVOID
|
||
NtFrsApi_Alloc(
|
||
IN DWORD Size
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Allocate fixed, zeroed memory. Raise an exception if memory
|
||
cannot be allocated.
|
||
|
||
Arguments:
|
||
Size - size of memory request
|
||
|
||
Return Value:
|
||
Raise an exception if memory cannot be allocated.
|
||
--*/
|
||
{
|
||
PVOID Va;
|
||
|
||
Va = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, Size);
|
||
if (!Va) {
|
||
RaiseException(GetLastError(), 0, 0, NULL);
|
||
}
|
||
return Va;
|
||
}
|
||
|
||
|
||
HANDLE
|
||
NtFrsApi_CreateEvent(
|
||
IN BOOL ManualReset,
|
||
IN BOOL InitialState
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Support routine to create an event.
|
||
|
||
Arguments:
|
||
ManualReset - TRUE if ResetEvent is required
|
||
InitialState - TRUE if signaled
|
||
|
||
Return Value:
|
||
Address of the created event handle.
|
||
--*/
|
||
{
|
||
HANDLE Handle;
|
||
|
||
Handle = CreateEvent(NULL, ManualReset, InitialState, NULL);
|
||
if (!HANDLE_IS_VALID(Handle)) {
|
||
RaiseException(GetLastError(), 0, 0, NULL);
|
||
}
|
||
return Handle;
|
||
}
|
||
|
||
|
||
PWCHAR
|
||
NtFrsApi_Dup(
|
||
IN PWCHAR Src
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Duplicate the string. Raise an exception if memory cannot be allocated.
|
||
|
||
Arguments:
|
||
Size - size of memory request
|
||
|
||
Return Value:
|
||
Raise an exception if memory cannot be allocated.
|
||
--*/
|
||
{
|
||
PWCHAR Dst;
|
||
DWORD Size;
|
||
|
||
if (!Src) {
|
||
return NULL;
|
||
}
|
||
|
||
Size = (wcslen(Src) + 1) * sizeof(WCHAR);
|
||
Dst = NtFrsApi_Alloc(Size);
|
||
CopyMemory(Dst, Src, Size);
|
||
return Dst;
|
||
}
|
||
|
||
|
||
#define NTFRSAPI_ERROR_MESSAGE_DELIMITER L": "
|
||
VOID
|
||
WINAPI
|
||
NtFrsApi_CallBackOnWStatus(
|
||
IN DWORD (*ErrorCallBack)(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR ObjectName, OPTIONAL
|
||
IN DWORD WStatus
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_CallBackOnWStatus:"
|
||
DWORD MsgBufSize;
|
||
DWORD FinalSize;
|
||
PWCHAR FinalMsg = NULL;
|
||
WCHAR MsgBuf[MAX_PATH + 1];
|
||
|
||
//
|
||
// Nothing to report
|
||
//
|
||
if (!ObjectName || !ErrorCallBack) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Format the error code
|
||
//
|
||
MsgBufSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
|
||
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||
NULL,
|
||
WStatus,
|
||
0,
|
||
MsgBuf,
|
||
MAX_PATH + 1,
|
||
NULL);
|
||
if (!MsgBufSize) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Produce message: "ObjectName: Error Code Message"
|
||
//
|
||
FinalSize = (wcslen(ObjectName) +
|
||
wcslen(MsgBuf) +
|
||
wcslen(NTFRSAPI_ERROR_MESSAGE_DELIMITER) +
|
||
1) * sizeof(WCHAR);
|
||
FinalMsg = NtFrsApi_Alloc(FinalSize);
|
||
FinalMsg[0] = L'\0';
|
||
wcscat(FinalMsg, ObjectName);
|
||
wcscat(FinalMsg, NTFRSAPI_ERROR_MESSAGE_DELIMITER);
|
||
wcscat(FinalMsg, MsgBuf);
|
||
//
|
||
// Record message with caller
|
||
//
|
||
(ErrorCallBack)(FinalMsg, WStatus);
|
||
FREE(FinalMsg);
|
||
}
|
||
|
||
|
||
//
|
||
// NTFRSAPI Thread Struct
|
||
//
|
||
typedef struct _NTFRSAPI_THREAD NTFRSAPI_THREAD, *PNTFRSAPI_THREAD;
|
||
struct _NTFRSAPI_THREAD {
|
||
//
|
||
// Thread state
|
||
//
|
||
PNTFRSAPI_THREAD Next; // Singly linked list
|
||
HANDLE ThreadHandle; // returned by CreateThread()
|
||
DWORD ThreadId; // returned by CreateThread()
|
||
HANDLE DoneEvent; // Set when thread is done
|
||
DWORD ThreadWStatus; // Win32 Status of this thread
|
||
|
||
//
|
||
// From NtFrs Service
|
||
//
|
||
ULONG ServiceState; // State of promotion/demotion
|
||
ULONG ServiceWStatus; // Win32 Status of promotion/demotion
|
||
PWCHAR ServiceDisplay; // Display string
|
||
|
||
//
|
||
// From NtFrsApi_StartPromotion/Demotion
|
||
//
|
||
PWCHAR ParentComputer;
|
||
PWCHAR ParentAccount;
|
||
PWCHAR ParentPassword;
|
||
DWORD (*DisplayCallBack)(IN PWCHAR Display);
|
||
DWORD (*ErrorCallBack)(IN PWCHAR, IN ULONG);
|
||
PWCHAR ReplicaSetName;
|
||
PWCHAR ReplicaSetType;
|
||
DWORD ReplicaSetPrimary;
|
||
PWCHAR ReplicaSetStage;
|
||
PWCHAR ReplicaSetRoot;
|
||
} *NtFrsApi_Threads;
|
||
DWORD NtFrsApi_NumberOfThreads;
|
||
|
||
|
||
PVOID
|
||
NtFrsApi_FreeThread(
|
||
IN PNTFRSAPI_THREAD Thread
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Abort a thread and free its thread struct.
|
||
Caller must hold the NtFrsApi_TheadLock.
|
||
|
||
Arguments:
|
||
Thread - represents the thread
|
||
|
||
Return Value:
|
||
NULL
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Clean up the handles
|
||
// Cancel the RPC requests.
|
||
// Give the thread a little time to clean up.
|
||
// Terminate the thread.
|
||
// Set and close the thread's done event.
|
||
//
|
||
if (HANDLE_IS_VALID(Thread->ThreadHandle)) {
|
||
RpcCancelThread(Thread->ThreadHandle);
|
||
WaitForSingleObject(Thread->ThreadHandle, 5 * 1000);
|
||
TerminateThread(Thread->ThreadHandle, ERROR_OPERATION_ABORTED);
|
||
CloseHandle(Thread->ThreadHandle);
|
||
}
|
||
|
||
if (HANDLE_IS_VALID(Thread->DoneEvent)) {
|
||
SetEvent(Thread->DoneEvent);
|
||
CloseHandle(Thread->DoneEvent);
|
||
}
|
||
|
||
//
|
||
// One less thread
|
||
//
|
||
--NtFrsApi_NumberOfThreads;
|
||
|
||
//
|
||
// Clean up memory
|
||
//
|
||
FREE(Thread->ParentComputer);
|
||
FREE(Thread->ParentAccount);
|
||
FREE(Thread->ParentPassword);
|
||
FREE(Thread->ReplicaSetName);
|
||
FREE(Thread->ReplicaSetType);
|
||
FREE(Thread->ReplicaSetStage);
|
||
FREE(Thread->ReplicaSetRoot);
|
||
FREE(Thread);
|
||
return NULL;
|
||
}
|
||
|
||
|
||
DWORD
|
||
NtFrsApi_CreateThread(
|
||
IN DWORD Entry(IN PVOID Arg),
|
||
IN PWCHAR ParentComputer,
|
||
IN PWCHAR ParentAccount,
|
||
IN PWCHAR ParentPassword,
|
||
IN DWORD DisplayCallBack(IN PWCHAR Display),
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG),
|
||
IN PWCHAR ReplicaSetName,
|
||
IN PWCHAR ReplicaSetType,
|
||
IN DWORD ReplicaSetPrimary,
|
||
IN PWCHAR ReplicaSetStage,
|
||
IN PWCHAR ReplicaSetRoot
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Create a thread for promotion/demotion.
|
||
|
||
Arguments:
|
||
Entry - Entry function
|
||
ParentComputer - An RPC-bindable name of the computer that is
|
||
supplying the Directory Service (DS) with its
|
||
initial state. The files and directories for
|
||
the system volume are replicated from this
|
||
parent computer.
|
||
ParentAccount - A logon account on ParentComputer.
|
||
ParentPassword - The logon account's password on ParentComputer.
|
||
DisplayCallBack - Called peridically with a progress display.
|
||
ErrorCallBack - Called with additional error info
|
||
ReplicaSetName - Name of the replica set.
|
||
ReplicaSetType - Type of replica set (enterprise or domain)
|
||
ReplicaSetPrimary - Is this the primary member of the replica set?
|
||
ReplicaSetStage - Staging path.
|
||
ReplicaSetRoot - Root path.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
DWORD WStatus;
|
||
PNTFRSAPI_THREAD Thread;
|
||
|
||
try {
|
||
//
|
||
// Allocate a local thread structure
|
||
//
|
||
Thread = NtFrsApi_Alloc(sizeof(NTFRSAPI_THREAD));
|
||
//
|
||
// Thread sets this event when it is done.
|
||
//
|
||
Thread->DoneEvent = NtFrsApi_CreateEvent(TRUE, FALSE);
|
||
Thread->ParentComputer = NtFrsApi_Dup(ParentComputer);
|
||
Thread->ParentAccount = NtFrsApi_Dup(ParentAccount);
|
||
Thread->ParentPassword = NtFrsApi_Dup(ParentPassword);
|
||
Thread->DisplayCallBack = DisplayCallBack;
|
||
Thread->ErrorCallBack = ErrorCallBack;
|
||
Thread->ReplicaSetName = NtFrsApi_Dup(ReplicaSetName);
|
||
Thread->ReplicaSetType = NtFrsApi_Dup(ReplicaSetType);
|
||
Thread->ReplicaSetPrimary = ReplicaSetPrimary;
|
||
Thread->ReplicaSetStage = NtFrsApi_Dup(ReplicaSetStage);
|
||
Thread->ReplicaSetRoot = NtFrsApi_Dup(ReplicaSetRoot);
|
||
Thread->ThreadWStatus = ERROR_SUCCESS;
|
||
Thread->ServiceWStatus = ERROR_SUCCESS;
|
||
Thread->ServiceState = NTFRSAPI_SERVICE_STATE_IS_UNKNOWN;
|
||
|
||
//
|
||
// Kick off the thread
|
||
//
|
||
Thread->ThreadHandle = (HANDLE) CreateThread(NULL,
|
||
10000,
|
||
Entry,
|
||
(PVOID)Thread,
|
||
0,
|
||
&Thread->ThreadId);
|
||
//
|
||
// FAILED
|
||
//
|
||
if (!HANDLE_IS_VALID(Thread->ThreadHandle)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("CreateThread(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
}
|
||
|
||
//
|
||
// SUCCEEDED
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_ThreadLock);
|
||
++NtFrsApi_NumberOfThreads;
|
||
Thread->Next = NtFrsApi_Threads;
|
||
NtFrsApi_Threads = Thread;
|
||
Thread = NULL;
|
||
LeaveCriticalSection(&NtFrsApi_ThreadLock);
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (Thread) {
|
||
Thread = NtFrsApi_FreeThread(Thread);
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
PWCHAR
|
||
NtFrsApi_FrsGetResourceStr(
|
||
IN HINSTANCE hInstance,
|
||
IN LONG Id
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine Loads the specified resource string.
|
||
It allocates a buffer and returns the ptr.
|
||
|
||
Arguments:
|
||
|
||
Id - An FRS_IDS_xxx identifier.
|
||
|
||
Return Value:
|
||
|
||
Ptr to allocated string.
|
||
The caller must free the buffer with a call to FrsFree().
|
||
|
||
--*/
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_FrsGetResourceStr:"
|
||
{
|
||
|
||
LONG N;
|
||
WCHAR WStr[200];
|
||
|
||
//
|
||
// ID Must be Valid.
|
||
//
|
||
if ((Id <= IDS_TABLE_START) || (Id >= IDS_TABLE_END)) {
|
||
NTFRSAPI_DBG_PRINT1("Resource string ID is out of range - %d\n", Id);
|
||
Id = IDS_MISSING_STRING;
|
||
}
|
||
|
||
WStr[0] = UNICODE_NULL;
|
||
|
||
N = LoadString(hInstance, Id, WStr, sizeof(WStr)/sizeof(WCHAR));
|
||
|
||
if (N == 0) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - Failed to get resource string. WStatus = %d\n",
|
||
GetLastError());
|
||
}
|
||
|
||
return NtFrsApi_Dup(WStr);
|
||
}
|
||
|
||
|
||
BOOL
|
||
WINAPI
|
||
NtFrsApi_Initialize(
|
||
HINSTANCE hinstDLL,
|
||
DWORD fdwReason,
|
||
LPVOID lpvReserved
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Called when this DLL is attached and detached.
|
||
|
||
Arguments:
|
||
hinstDLL handle to DLL module
|
||
fdwReason reason for calling function
|
||
lpvReserved reserved
|
||
|
||
Return Value:
|
||
TRUE - no problems
|
||
FALSE - DLL is not attached
|
||
--*/
|
||
{
|
||
switch (fdwReason) {
|
||
case DLL_PROCESS_ATTACH :
|
||
//
|
||
// No initialization needed per thread
|
||
//
|
||
DisableThreadLibraryCalls(hinstDLL);
|
||
|
||
//
|
||
// Get the translated long service name for error messages.
|
||
//
|
||
NtFrsApi_ServiceLongName =
|
||
NtFrsApi_FrsGetResourceStr(hinstDLL, IDS_SERVICE_LONG_NAME);
|
||
|
||
//
|
||
// Shutdown event
|
||
//
|
||
NtFrsApi_ShutDownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||
if (!HANDLE_IS_VALID(NtFrsApi_ShutDownEvent)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// General purpose critical section
|
||
//
|
||
InitializeCriticalSection(&NtFrsApi_GlobalLock);
|
||
|
||
//
|
||
// Thread subsystem
|
||
//
|
||
InitializeCriticalSection(&NtFrsApi_ThreadLock);
|
||
|
||
//
|
||
// Debug subsystem
|
||
//
|
||
NTFRSAPI_DBG_INITIALIZE();
|
||
|
||
//
|
||
// Not prepared for promotion or demotion, yet
|
||
//
|
||
NtFrsApi_State = NTFRSAPI_LOADED;
|
||
|
||
break;
|
||
case DLL_THREAD_ATTACH :
|
||
break;
|
||
case DLL_THREAD_DETACH :
|
||
break;
|
||
case DLL_PROCESS_DETACH :
|
||
|
||
FREE(NtFrsApi_ServiceLongName);
|
||
DeleteCriticalSection(&NtFrsApi_GlobalLock);
|
||
DeleteCriticalSection(&NtFrsApi_ThreadLock);
|
||
if (NtFrsApi_ShutDownEvent) {
|
||
CloseHandle(NtFrsApi_ShutDownEvent);
|
||
}
|
||
NTFRSAPI_DBG_UNINITIALIZE();
|
||
break;
|
||
default:
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
PVOID
|
||
MIDL_user_allocate(
|
||
IN size_t Bytes
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Allocate memory for RPC.
|
||
|
||
Arguments:
|
||
Bytes - Number of bytes to allocate.
|
||
|
||
Return Value:
|
||
NULL - memory could not be allocated.
|
||
!NULL - address of allocated memory.
|
||
--*/
|
||
{
|
||
return LocalAlloc(LMEM_FIXED, Bytes);
|
||
}
|
||
|
||
|
||
VOID
|
||
MIDL_user_free(
|
||
IN PVOID Buffer
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Free memory for RPC.
|
||
|
||
Arguments:
|
||
Buffer - Address of memory allocated with MIDL_user_allocate().
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
FREE(Buffer);
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_Bind(
|
||
IN PWCHAR ComputerName, OPTIONAL
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
OUT handle_t *OutHandle
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Bind to the NtFrs service on ComputerName (this machine if NULL).
|
||
|
||
Arguments:
|
||
ComputerName - Bind to the service on this computer. The computer
|
||
name can be any RPC-bindable name. Usually, the
|
||
NetBIOS or DNS name works just fine. The NetBIOS
|
||
name can be found with GetComputerName() or
|
||
hostname. The DNS name can be found with
|
||
gethostbyname() or ipconfig /all. If NULL, the
|
||
service on this computer is contacted. The service
|
||
is contacted using Secure RPC.
|
||
ErrorCallBack - Ignored if NULL. Otherwise called with extra info
|
||
about an error.
|
||
OutHandle - Bound, resolved, authenticated handle
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Bind:"
|
||
DWORD WStatus, WStatus1;
|
||
DWORD ComputerLen;
|
||
handle_t Handle = NULL;
|
||
PWCHAR LocalName = NULL;
|
||
PWCHAR BindingString = NULL;
|
||
|
||
try {
|
||
NTFRSAPI_DBG_PRINT1("Bind: %ws\n", ComputerName);
|
||
//
|
||
// Return value
|
||
//
|
||
*OutHandle = NULL;
|
||
|
||
//
|
||
// If needed, get computer name
|
||
//
|
||
if (ComputerName == NULL) {
|
||
ComputerLen = MAX_COMPUTERNAME_LENGTH + 2;
|
||
LocalName = NtFrsApi_Alloc(ComputerLen * sizeof(WCHAR));
|
||
if (!GetComputerName(LocalName, &ComputerLen)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Bind: GetComputerName(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
ComputerName = LocalName;
|
||
}
|
||
|
||
//
|
||
// Create a binding string to NtFrs on some machine. Trim leading \\
|
||
//
|
||
FRS_TRIM_LEADING_2SLASH(ComputerName);
|
||
|
||
NTFRSAPI_DBG_PRINT1("Bind: compose to %ws\n", ComputerName);
|
||
|
||
WStatus = RpcStringBindingCompose(NULL, PROTSEQ_TCP_IP, ComputerName,
|
||
NULL, NULL, &BindingString);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind: compose done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
|
||
//
|
||
// Store the binding in the handle
|
||
//
|
||
WStatus = RpcBindingFromStringBinding(BindingString, &Handle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT2("Bind: RpcBindingFromStringBinding(%ws); %d\n",
|
||
ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
//
|
||
// Resolve the binding to the dynamic endpoint
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Bind: resolve to %ws\n", ComputerName);
|
||
WStatus = RpcEpResolveBinding(Handle, NtFrsApi_ClientIfHandle);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind: resolve done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
*OutHandle = Handle;
|
||
Handle = NULL;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (LocalName) {
|
||
FREE(LocalName);
|
||
}
|
||
if (BindingString) {
|
||
WStatus1 = RpcStringFreeW(&BindingString);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcStringFreeW");
|
||
}
|
||
if (Handle) {
|
||
WStatus1 = RpcBindingFree(&Handle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Bind done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_BindWithAuth(
|
||
IN PWCHAR ComputerName, OPTIONAL
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
OUT handle_t *OutHandle
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Bind to the NtFrs service on ComputerName (this machine if NULL)
|
||
with authenticated, encrypted packets.
|
||
|
||
Arguments:
|
||
ComputerName - Bind to the service on this computer. The computer
|
||
name can be any RPC-bindable name. Usually, the
|
||
NetBIOS or DNS name works just fine. The NetBIOS
|
||
name can be found with GetComputerName() or
|
||
hostname. The DNS name can be found with
|
||
gethostbyname() or ipconfig /all. If NULL, the
|
||
service on this computer is contacted. The service
|
||
is contacted using Secure RPC.
|
||
ErrorCallBack - Ignored if NULL. Otherwise called with extra info
|
||
about an error.
|
||
OutHandle - Bound, resolved, authenticated handle
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_BindWithAuth:"
|
||
DWORD WStatus, WStatus1;
|
||
DWORD ComputerLen;
|
||
handle_t Handle = NULL;
|
||
PWCHAR LocalName = NULL;
|
||
PWCHAR PrincName = NULL;
|
||
PWCHAR BindingString = NULL;
|
||
|
||
try {
|
||
NTFRSAPI_DBG_PRINT1("Bind With Auth: %ws\n", ComputerName);
|
||
//
|
||
// Return value
|
||
//
|
||
*OutHandle = NULL;
|
||
|
||
//
|
||
// If needed, get computer name
|
||
//
|
||
if (ComputerName == NULL) {
|
||
ComputerLen = MAX_COMPUTERNAME_LENGTH + 2;
|
||
LocalName = NtFrsApi_Alloc(ComputerLen * sizeof(WCHAR));
|
||
|
||
if (!GetComputerName(LocalName, &ComputerLen)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Bind With Auth: GetComputerName(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
}
|
||
ComputerName = LocalName;
|
||
}
|
||
|
||
//
|
||
// Create a binding string to NtFrs on some machine. Trim leading \\
|
||
//
|
||
FRS_TRIM_LEADING_2SLASH(ComputerName);
|
||
|
||
NTFRSAPI_DBG_PRINT1("Bind With Auth: compose to %ws\n", ComputerName);
|
||
|
||
WStatus = RpcStringBindingCompose(NULL, PROTSEQ_TCP_IP, ComputerName,
|
||
NULL, NULL, &BindingString);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind With Auth: compose done to %ws; %d\n", ComputerName, WStatus);
|
||
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
|
||
//
|
||
// Store the binding in the handle
|
||
//
|
||
WStatus = RpcBindingFromStringBinding(BindingString, &Handle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT2("Bind With Auth: RpcBindingFromStringBinding(%ws); %d\n",
|
||
ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Resolve the binding to the dynamic endpoint
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Bind With Auth: resolve to %ws\n", ComputerName);
|
||
WStatus = RpcEpResolveBinding(Handle, NtFrsApi_ClientIfHandle);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind With Auth: resolve done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
|
||
//
|
||
// Find the principle name
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Bind With Auth: princname to %ws\n", ComputerName);
|
||
WStatus = RpcMgmtInqServerPrincName(Handle, RPC_C_AUTHN_GSS_NEGOTIATE, &PrincName);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind With Auth: princname done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
//
|
||
// Set authentication info
|
||
//
|
||
NTFRSAPI_DBG_PRINT2("Bind With Auth: auth to %ws (princname %ws)\n",
|
||
ComputerName, PrincName);
|
||
WStatus = RpcBindingSetAuthInfo(Handle,
|
||
PrincName,
|
||
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
||
RPC_C_AUTHN_GSS_NEGOTIATE,
|
||
NULL,
|
||
RPC_C_AUTHZ_NONE);
|
||
NTFRSAPI_DBG_PRINT2("Bind With Auth: set auth done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
*OutHandle = Handle;
|
||
Handle = NULL;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (LocalName) {
|
||
FREE(LocalName);
|
||
}
|
||
if (BindingString) {
|
||
WStatus1 = RpcStringFreeW(&BindingString);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcStringFreeW");
|
||
}
|
||
if (PrincName) {
|
||
RpcStringFree(&PrincName);
|
||
}
|
||
if (Handle) {
|
||
WStatus1 = RpcBindingFree(&Handle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Bind With Auth done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_BindForDcpromo(
|
||
IN PWCHAR ComputerName, OPTIONAL
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
OUT handle_t *OutHandle
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Bind to the NtFrs service on ComputerName (this machine if NULL)
|
||
with packets capable of being impersonated.
|
||
|
||
Arguments:
|
||
ComputerName - Bind to the service on this computer. The computer
|
||
name can be any RPC-bindable name. Usually, the
|
||
NetBIOS or DNS name works just fine. The NetBIOS
|
||
name can be found with GetComputerName() or
|
||
hostname. The DNS name can be found with
|
||
gethostbyname() or ipconfig /all. If NULL, the
|
||
service on this computer is contacted. The service
|
||
is contacted using Secure RPC.
|
||
ErrorCallBack - Ignored if NULL. Otherwise called with extra info
|
||
about an error.
|
||
OutHandle - Bound, resolved, authenticated handle
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_BindForDcpromo:"
|
||
DWORD WStatus, WStatus1;
|
||
DWORD ComputerLen;
|
||
handle_t Handle = NULL;
|
||
PWCHAR LocalName = NULL;
|
||
PWCHAR BindingString = NULL;
|
||
|
||
try {
|
||
NTFRSAPI_DBG_PRINT1("Bind Dcpromo: %ws\n", ComputerName);
|
||
//
|
||
// Return value
|
||
//
|
||
*OutHandle = NULL;
|
||
|
||
//
|
||
// If needed, get computer name
|
||
//
|
||
if (ComputerName == NULL) {
|
||
ComputerLen = MAX_COMPUTERNAME_LENGTH + 2;
|
||
LocalName = NtFrsApi_Alloc(ComputerLen * sizeof(WCHAR));
|
||
if (!GetComputerName(LocalName, &ComputerLen)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Bind Dcpromo: GetComputerName(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
}
|
||
ComputerName = LocalName;
|
||
}
|
||
|
||
//
|
||
// Create a binding string to NtFrs on some machine. Trim leading \\
|
||
//
|
||
FRS_TRIM_LEADING_2SLASH(ComputerName);
|
||
|
||
NTFRSAPI_DBG_PRINT1("Bind Dcpromo: compose to %ws\n", ComputerName);
|
||
|
||
//
|
||
// DOC: Why are named pipes used here but tcp/ip used everywhere else?
|
||
//
|
||
WStatus = RpcStringBindingCompose(NULL, PROTSEQ_NAMED_PIPE, ComputerName,
|
||
NULL, NULL, &BindingString);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind Dcpromo: compose done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
|
||
//
|
||
// Store the binding in the handle
|
||
//
|
||
WStatus = RpcBindingFromStringBinding(BindingString, &Handle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT2("Bind Dcpromo: RpcBindingFromStringBinding(%ws); %d\n",
|
||
ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Resolve the binding to the dynamic endpoint
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Bind Dcpromo: resolve to %ws\n", ComputerName);
|
||
WStatus = RpcEpResolveBinding(Handle, NtFrsApi_ClientIfHandle);
|
||
|
||
NTFRSAPI_DBG_PRINT2("Bind Dcpromo: resolve done to %ws; %d\n", ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
|
||
//
|
||
// Set authentication info
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Bind Dcpromo: set auth to %ws\n", ComputerName);
|
||
WStatus = RpcBindingSetAuthInfo(Handle,
|
||
NULL,
|
||
RPC_C_AUTHN_LEVEL_NONE,
|
||
RPC_C_AUTHN_NONE,
|
||
NULL,
|
||
RPC_C_AUTHZ_NONE);
|
||
NTFRSAPI_DBG_PRINT2("Bind Dcpromo: set auth done to %ws; %d\n",
|
||
ComputerName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
*OutHandle = Handle;
|
||
Handle = NULL;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (LocalName) {
|
||
FREE(LocalName);
|
||
}
|
||
if (BindingString) {
|
||
WStatus1 = RpcStringFreeW(&BindingString);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcStringFreeW");
|
||
}
|
||
if (Handle) {
|
||
WStatus1 = RpcBindingFree(&Handle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Bind Dcpromo Done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
NtFrsApi_GetServiceHandle(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
OUT SC_HANDLE *ServiceHandle
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Open a service on a machine.
|
||
|
||
Arguments:
|
||
ServiceHandle - Openned handle to ServiceName
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_GetServiceHandle:"
|
||
DWORD WStatus;
|
||
SC_HANDLE SCMHandle;
|
||
|
||
//
|
||
// Contact the SC manager.
|
||
//
|
||
SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
||
if (!HANDLE_IS_VALID(SCMHandle)) {
|
||
WStatus = GetLastError();
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, L"Service Controller", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
//
|
||
// Contact the NtFrs service.
|
||
//
|
||
*ServiceHandle = OpenService(SCMHandle,
|
||
SERVICE_NAME,
|
||
SERVICE_INTERROGATE |
|
||
SERVICE_PAUSE_CONTINUE |
|
||
SERVICE_QUERY_STATUS |
|
||
SERVICE_QUERY_CONFIG |
|
||
SERVICE_START |
|
||
SERVICE_STOP |
|
||
SERVICE_CHANGE_CONFIG);
|
||
if (!HANDLE_IS_VALID(*ServiceHandle)) {
|
||
WStatus = GetLastError();
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus);
|
||
} else {
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
CloseServiceHandle(SCMHandle);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
#define MAX_WAIT_HINT (120 * 1000) // 120 seconds
|
||
DWORD
|
||
NtFrsApi_WaitForService(
|
||
IN SC_HANDLE ServiceHandle,
|
||
IN DWORD WaitHint,
|
||
IN DWORD PendingState,
|
||
IN DWORD FinalState
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Wait for the indicated service to transition from PendingState
|
||
to State. The service is polled once a second for up to WaitHint
|
||
seconds (WaitHint is in milliseconds). The maximum WaitHint is
|
||
120 seconds.
|
||
|
||
Arguments:
|
||
ServiceHandle - indicates the service.
|
||
WaitHint - From the service status (in milliseconds)
|
||
PendingState - Expected pending state (E.g., SERVICE_START_PENDING)
|
||
FinalState - Expected final state (E.g., SERVICE_RUNNING)
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_WaitForService:"
|
||
DWORD WStatus;
|
||
SERVICE_STATUS ServiceStatus;
|
||
|
||
//
|
||
// Don't wait too long; dcpromo is an interactive app
|
||
//
|
||
if (WaitHint > MAX_WAIT_HINT || !WaitHint) {
|
||
WaitHint = MAX_WAIT_HINT;
|
||
}
|
||
|
||
//
|
||
// Get the service's status
|
||
//
|
||
again:
|
||
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
||
return GetLastError();
|
||
}
|
||
//
|
||
// Done
|
||
//
|
||
if (ServiceStatus.dwCurrentState == FinalState) {
|
||
return ERROR_SUCCESS;
|
||
}
|
||
//
|
||
// Not in pending state; error
|
||
//
|
||
if (ServiceStatus.dwCurrentState != PendingState) {
|
||
return ERROR_OPERATION_ABORTED;
|
||
}
|
||
//
|
||
// Can't wait any longer
|
||
//
|
||
if (WaitHint < 1000) {
|
||
return ERROR_OPERATION_ABORTED;
|
||
}
|
||
//
|
||
// Wait a second
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Waiting for service.\n");
|
||
Sleep(1000);
|
||
WaitHint -= 1000;
|
||
//
|
||
// Try again
|
||
//
|
||
goto again;
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
NtFrsApi_StopService(
|
||
IN SC_HANDLE ServiceHandle,
|
||
OUT LPSERVICE_STATUS ServiceStatus
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Stop the FRS service.
|
||
|
||
Arguments:
|
||
|
||
ServiceHandle - An open handle to the service controller.
|
||
|
||
ServiceStatus - ptr to struct for returned service status.
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_StopService:"
|
||
|
||
DWORD WStatus;
|
||
|
||
//
|
||
// Stop the FRS service.
|
||
// Double stop used to deal with shutdown hang.
|
||
//
|
||
if (!ControlService(ServiceHandle, SERVICE_CONTROL_STOP, ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
if (WStatus == ERROR_SERVICE_REQUEST_TIMEOUT) {
|
||
WStatus = ERROR_SUCCESS;
|
||
if (!ControlService(ServiceHandle, SERVICE_CONTROL_STOP, ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
if (WStatus == ERROR_SERVICE_NOT_ACTIVE) {
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Wait for the stop to finish.
|
||
//
|
||
WStatus = NtFrsApi_WaitForService(ServiceHandle,
|
||
NtFrsApi_ServiceWaitHint,
|
||
SERVICE_STOP_PENDING,
|
||
SERVICE_STOPPED);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = FRS_ERR_STOPPING_SERVICE;
|
||
}
|
||
|
||
return WStatus;
|
||
}
|
||
|
||
PWCHAR
|
||
WINAPI
|
||
NtFrsApi_Cats(
|
||
IN PWCHAR Name1, OPTIONAL
|
||
IN PWCHAR Name2, OPTIONAL
|
||
IN PWCHAR Name3 OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Cats:"
|
||
DWORD FinalSize;
|
||
PWCHAR FinalMsg;
|
||
|
||
//
|
||
// sizeof(Names) + sizeof(terminating NULL)
|
||
//
|
||
FinalSize = (((Name1) ? wcslen(Name1) : 0) +
|
||
((Name2) ? wcslen(Name2) : 0) +
|
||
((Name3) ? wcslen(Name3) : 0) +
|
||
1) * sizeof(WCHAR);
|
||
//
|
||
// Nothing but the terminating UNICODE NULL; ignore
|
||
//
|
||
if (FinalSize <= sizeof(WCHAR)) {
|
||
return NULL;
|
||
}
|
||
//
|
||
// Allocate string and concatenate
|
||
//
|
||
FinalMsg = NtFrsApi_Alloc(FinalSize);
|
||
FinalMsg[0] = L'\0';
|
||
if (Name1) {
|
||
wcscat(FinalMsg, Name1);
|
||
}
|
||
if (Name2) {
|
||
wcscat(FinalMsg, Name2);
|
||
}
|
||
if (Name3) {
|
||
wcscat(FinalMsg, Name3);
|
||
}
|
||
return (FinalMsg);
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiOpenKeyEx(
|
||
IN PCHAR NtFrsApiModule,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR KeyPath,
|
||
IN DWORD KeyAccess,
|
||
OUT HKEY *OutHKey
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Open KeyPath.
|
||
|
||
Arguments:
|
||
NtFrsApiModule - String to identify caller
|
||
ErrorCallBack - Ignored if NULL
|
||
KeyPath - Path of registry key (HKEY_LOCAL_MACHINE)
|
||
KeyAccess - for RegOpenKeyEx()
|
||
OutHKey - From RegOpenKeyEx()
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiOpenKeyEx:"
|
||
DWORD WStatus;
|
||
|
||
//
|
||
// Open KeyPath
|
||
//
|
||
*OutHKey = 0;
|
||
WStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyPath, 0, KeyAccess, OutHKey);
|
||
//
|
||
// Report error
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, KeyPath, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegOpenKeyEx(%ws); %d\n",
|
||
NtFrsApiModule, KeyPath, WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiCreateKey(
|
||
IN PCHAR NtFrsApiModule,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR KeyPath,
|
||
IN HKEY HKey,
|
||
IN PWCHAR KeyName,
|
||
OUT HKEY *OutHKey
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Create KeyPath.
|
||
|
||
Arguments:
|
||
NtFrsApiModule - String to identify caller
|
||
ErrorCallBack - Ignored if NULL
|
||
KeyPath - Path of registry key (HKEY_LOCAL_MACHINE)
|
||
KeyAccess - for RegCreateKey()
|
||
OutHKey - From RegCreateKey()
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiCreateKey:"
|
||
DWORD WStatus;
|
||
PWCHAR ObjectName;
|
||
|
||
//
|
||
// Open KeyPath
|
||
//
|
||
*OutHKey = 0;
|
||
WStatus = RegCreateKey(HKey, KeyName, OutHKey);
|
||
//
|
||
// Report error
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
if (KeyPath) {
|
||
ObjectName = NtFrsApi_Cats(KeyPath, L"\\", KeyName);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegCreateKey(%ws); %d\n",
|
||
NtFrsApiModule, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
} else {
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, KeyName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegCreateKey(%ws); %d\n",
|
||
NtFrsApiModule, KeyName, WStatus);
|
||
}
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiSetValueEx(
|
||
IN PCHAR NtFrsApiModule,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR KeyPath,
|
||
IN HKEY HKey,
|
||
IN PWCHAR ValueName,
|
||
IN DWORD RegType,
|
||
IN PCHAR RegValue,
|
||
IN DWORD RegSize
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Set value
|
||
|
||
Arguments:
|
||
NtFrsApiModule - String to identify caller
|
||
ErrorCallBack - Ignored if NULL
|
||
KeyPath - Path of registry key (HKEY_LOCAL_MACHINE)
|
||
HKey - For call to RegSetValueEx()
|
||
ValueName - For Call to RegSetValueEx()
|
||
RegType - For Call to RegSetValueEx()
|
||
RegValue - For Call to RegSetValueEx()
|
||
RegSize - For Call to RegSetValueEx()
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiSetValueEx:"
|
||
DWORD WStatus;
|
||
PWCHAR ObjectName;
|
||
|
||
//
|
||
// Set the value
|
||
//
|
||
WStatus = RegSetValueEx(HKey, ValueName, 0, RegType, RegValue, RegSize);
|
||
//
|
||
// Report error
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
if (KeyPath) {
|
||
ObjectName = NtFrsApi_Cats(KeyPath, L"->", ValueName);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegSetValueEx(%ws); %d\n",
|
||
NtFrsApiModule, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
} else {
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ValueName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegSetValueEx(%ws); %d\n",
|
||
NtFrsApiModule, ValueName, WStatus);
|
||
}
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiDeleteValue(
|
||
IN PCHAR NtFrsApiModule,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR KeyPath,
|
||
IN HKEY HKey,
|
||
IN PWCHAR ValueName
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Delete value.
|
||
|
||
Arguments:
|
||
NtFrsApiModule - String to identify caller
|
||
ErrorCallBack - Ignored if NULL
|
||
KeyPath - Path of registry key (HKEY_LOCAL_MACHINE)
|
||
HKey - For call to RegDeleteValue()
|
||
ValueName - For Call to RegDeleteValue()
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiDeleteValue:"
|
||
DWORD WStatus;
|
||
PWCHAR ObjectName;
|
||
|
||
//
|
||
// Set the value
|
||
//
|
||
WStatus = RegDeleteValue(HKey, ValueName);
|
||
//
|
||
// Report error
|
||
//
|
||
if (!WIN_SUCCESS(WStatus) && WStatus != ERROR_FILE_NOT_FOUND) {
|
||
if (KeyPath) {
|
||
ObjectName = NtFrsApi_Cats(KeyPath, L"->", ValueName);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegDeleteValue(%ws); %d\n",
|
||
NtFrsApiModule, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
} else {
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ValueName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegDeleteValue(%ws); %d\n",
|
||
NtFrsApiModule, ValueName, WStatus);
|
||
}
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiDeleteKey(
|
||
IN PCHAR NtFrsApiModule,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR KeyPath,
|
||
IN HKEY HKey,
|
||
IN PWCHAR KeyName
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Delete key.
|
||
|
||
Arguments:
|
||
NtFrsApiModule - String to identify caller
|
||
ErrorCallBack - Ignored if NULL
|
||
KeyPath - Path of registry key (HKEY_LOCAL_MACHINE)
|
||
HKey - For call to RegDeleteKey()
|
||
KeyName - For Call to RegDeleteKey()
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiDeleteKey:"
|
||
DWORD WStatus;
|
||
PWCHAR ObjectName;
|
||
|
||
//
|
||
// Set the value
|
||
//
|
||
WStatus = RegDeleteKey(HKey, KeyName);
|
||
//
|
||
// Report error
|
||
//
|
||
if (!WIN_SUCCESS(WStatus) && WStatus != ERROR_FILE_NOT_FOUND) {
|
||
if (KeyPath) {
|
||
ObjectName = NtFrsApi_Cats(KeyPath, L"\\", KeyName);
|
||
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
|
||
NTFRSAPI_DBG_PRINT3("%s RegDeleteKey(%ws); %d\n",
|
||
NtFrsApiModule, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
} else {
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, KeyName, WStatus);
|
||
NTFRSAPI_DBG_PRINT3("%s RegDeleteKey(%ws); %d\n",
|
||
NtFrsApiModule, KeyName, WStatus);
|
||
}
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_Prepare(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN BOOL IsDemote
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC) and stops replicating
|
||
the system volumes when a DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's
|
||
replica set.
|
||
|
||
This function prepares the NtFrs service on this machine by
|
||
stopping the service, deleting old state in the registry,
|
||
and restarting the service. The service's current state is
|
||
retained and restored if the promotion or demotion are
|
||
aborted.
|
||
|
||
Arguments:
|
||
IsDemote - TRUE: prepare for demotion
|
||
FALSE: prepare for promotion
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Prepare:"
|
||
DWORD WStatus;
|
||
SERVICE_STATUS ServiceStatus;
|
||
DWORD ValueLen;
|
||
DWORD ValueType;
|
||
DWORD SysvolReady;
|
||
PWCHAR ObjectName = NULL;
|
||
HKEY HKey = 0;
|
||
HKEY HNetKey = 0;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
WCHAR KeyBuf[MAX_PATH + 1];
|
||
|
||
|
||
try {
|
||
//
|
||
// Acquire global lock within a try-finally
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Prepare:\n");
|
||
|
||
//
|
||
// This function is designed to be called once!
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_LOADED) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
NtFrsApi_State = NTFRSAPI_PREPARING;
|
||
|
||
//
|
||
// Stop the service, delete old state from the registry,
|
||
// and restart the service
|
||
//
|
||
try {
|
||
//
|
||
// Set the RPC cancel timeout to "now"
|
||
//
|
||
WStatus = RpcMgmtSetCancelTimeout(0);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT1("Prepare: RpcMgmtSetCancelTimeout(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
|
||
//
|
||
// Open the ntfrs parameters\sysvol section in the registry
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Prepare: Global registry options\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Insure access to the netlogon\parameters key
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Prepare: Netlogon registry\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
NETLOGON_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HNetKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Tell NetLogon to stop sharing the sysvol
|
||
// NtFrs will reset the value if a seeded sysvol is
|
||
// detected at startup.
|
||
//
|
||
SysvolReady = 0;
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
NETLOGON_SECTION,
|
||
HNetKey,
|
||
SYSVOL_READY,
|
||
REG_DWORD,
|
||
(PCHAR)&SysvolReady,
|
||
sizeof(DWORD));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Open the service
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Prepare: Service\n");
|
||
WStatus = NtFrsApi_GetServiceHandle(ErrorCallBack, &ServiceHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Get the service's status
|
||
//
|
||
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Prepare: QueryServiceStatus(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
//
|
||
// Remember the current state
|
||
//
|
||
NtFrsApi_ServiceState = ServiceStatus.dwCurrentState;
|
||
NtFrsApi_ServiceWaitHint = ServiceStatus.dwWaitHint;
|
||
NtFrsApi_State = NTFRSAPI_PREPARED_SERVICE;
|
||
|
||
//
|
||
// Stop the service
|
||
//
|
||
if (ServiceStatus.dwCurrentState != SERVICE_STOPPED) {
|
||
WStatus = NtFrsApi_StopService(ServiceHandle, &ServiceStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Delete old state from the registry
|
||
//
|
||
//
|
||
// Open the ntfrs parameters\sysvol section in the registry
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Prepare: Registry\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Delete the value that indicates the sysvol subkeys are valid
|
||
//
|
||
WStatus = NtFrsApiDeleteValue(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
SYSVOL_INFO_IS_COMMITTED);
|
||
if (!WIN_SUCCESS(WStatus) && WStatus != ERROR_FILE_NOT_FOUND) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Delete the subkeys
|
||
//
|
||
do {
|
||
WStatus = RegEnumKey(HKey, 0, KeyBuf, MAX_PATH + 1);
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApiDeleteKey(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
KeyBuf);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
} while (WIN_SUCCESS(WStatus));
|
||
|
||
if (WStatus != ERROR_NO_MORE_ITEMS) {
|
||
NTFRSAPI_DBG_PRINT2("Prepare: RegEnumKey(%ws); %d\n",
|
||
FRS_SYSVOL_SECTION, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, FRS_SYSVOL_SECTION, WStatus, cleanup);
|
||
}
|
||
|
||
//
|
||
// Restart the service
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Prepare: Restart service\n");
|
||
if (!StartService(ServiceHandle, 0, NULL)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT2("Prepare: StartService(%ws); %d\n",
|
||
NtFrsApi_ServiceLongName, WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
//
|
||
// Wait for the service to start
|
||
//
|
||
WStatus = NtFrsApi_WaitForService(ServiceHandle,
|
||
NtFrsApi_ServiceWaitHint,
|
||
SERVICE_START_PENDING,
|
||
SERVICE_RUNNING);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = FRS_ERR_STARTING_SERVICE;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Success
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
NtFrsApi_State = NTFRSAPI_PREPARED;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (ServiceHandle) {
|
||
CloseServiceHandle(ServiceHandle);
|
||
}
|
||
if (HANDLE_IS_VALID(HKey)) {
|
||
RegCloseKey(HKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HNetKey)) {
|
||
RegCloseKey(HNetKey);
|
||
}
|
||
FREE(ObjectName);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Prepare done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_PrepareForPromotionW(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC). The files and directories
|
||
for the system volume come from the same machine that is supplying
|
||
the initial Directory Service (DS).
|
||
|
||
This function prepares the NtFrs service on this machine for
|
||
promotion by stopping the service, deleting old promotion
|
||
state in the registry, and restarting the service.
|
||
|
||
This function is not idempotent and isn't MT safe.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_PrepareForPromotionW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PREPARE();
|
||
|
||
NTFRSAPI_DBG_PRINT0("\n");
|
||
NTFRSAPI_DBG_PRINT0("=============== Promotion Start:\n");
|
||
NTFRSAPI_DBG_PRINT0("\n");
|
||
|
||
NTFRSAPI_DBG_PRINT0("Prepare promotion:\n");
|
||
WStatus = NtFrsApi_Prepare(ErrorCallBack, FALSE);
|
||
NTFRSAPI_DBG_PRINT1("Prepare promotion done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
PVOID *
|
||
DsDeleteFindValues(
|
||
IN PLDAP Ldap,
|
||
IN PLDAPMessage LdapEntry,
|
||
IN PWCHAR DesiredAttr
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Return the DS values for one attribute in an entry.
|
||
|
||
Arguments:
|
||
Ldap - An open, bound ldap port.
|
||
LdapEntry - An ldap entry returned by ldap_search_s()
|
||
DesiredAttr - Return values for this attribute.
|
||
|
||
Return Value:
|
||
An array of char pointers that represents the values for the attribute.
|
||
The caller must free the array with LDAP_FREE_VALUES().
|
||
NULL if unsuccessful.
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "DsDeleteFindValues:"
|
||
PWCHAR Attr; // Retrieved from an ldap entry
|
||
BerElement *Ber; // Needed for scanning attributes
|
||
|
||
//
|
||
// Search the entry for the desired attribute
|
||
//
|
||
for (Attr = ldap_first_attribute(Ldap, LdapEntry, &Ber);
|
||
Attr != NULL;
|
||
Attr = ldap_next_attribute(Ldap, LdapEntry, Ber)) {
|
||
if (WSTR_EQ(DesiredAttr, Attr)) {
|
||
//
|
||
// Return the values for DesiredAttr
|
||
//
|
||
return ldap_get_values(Ldap, LdapEntry, Attr);
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
DsDeletePrepare(
|
||
IN SEC_WINNT_AUTH_IDENTITY *Credentials OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Called from NtFrsApi_PrepareForDemotionW().
|
||
|
||
Squirrel away an ldap binding to another DS. After the
|
||
demotion is committed, the settings, set, member, subscriptions,
|
||
and subscriber objects will be deleted.
|
||
|
||
Arguments:
|
||
|
||
Credentials -- Credentionals to use in ldap binding call, if supplied.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "DsDeletePrepare:"
|
||
DWORD WStatus;
|
||
DWORD Idx;
|
||
DWORD LStatus = LDAP_SUCCESS;
|
||
PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
|
||
PLDAPMessage LdapEntry;
|
||
PLDAPMessage LdapMsg = NULL;
|
||
PWCHAR *LdapValues = NULL;
|
||
PWCHAR LdapValue;
|
||
PWCHAR Attrs[3];
|
||
PWCHAR DomainName;
|
||
PWCHAR DomainControllerName = NULL;
|
||
PWCHAR DomainControllerAddress = NULL;
|
||
struct l_timeval Timeout;
|
||
|
||
DWORD InfoFlags;
|
||
CHAR FlagBuffer[220];
|
||
ULONG ulOptions;
|
||
|
||
DsDeleteLdap = NULL;
|
||
|
||
//
|
||
// computer name
|
||
//
|
||
Idx = MAX_COMPUTERNAME_LENGTH + 2;
|
||
if (!GetComputerNameEx(ComputerNameNetBIOS, DsDeleteComputerName, &Idx)) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - Can't get computer name; %d\n", GetLastError());
|
||
goto CLEANUP;
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Computer name is %ws\n", DsDeleteComputerName);
|
||
//
|
||
// domain name
|
||
//
|
||
Idx = MAX_PATH + 2;
|
||
if (!GetComputerNameEx(ComputerNameDnsDomain, DsDeleteDomainDnsName, &Idx)) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - Can't get domain name; %d\n", GetLastError());
|
||
goto CLEANUP;
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Domain name is %ws\n", DsDeleteDomainDnsName);
|
||
|
||
//
|
||
// Find any DC in our hierarchy of DCs
|
||
//
|
||
DomainName = DsDeleteDomainDnsName;
|
||
FIND_DC:
|
||
NTFRSAPI_DBG_PRINT1("Trying domain name is %ws\n", DomainName);
|
||
WStatus = DsGetDcName(NULL, // Computer to remote to
|
||
DomainName,
|
||
NULL, // Domain Guid
|
||
NULL, // Site Guid
|
||
DS_DIRECTORY_SERVICE_REQUIRED |
|
||
DS_WRITABLE_REQUIRED |
|
||
DS_BACKGROUND_ONLY |
|
||
DS_AVOID_SELF,
|
||
&DcInfo); // Return info
|
||
//
|
||
// Report the error and retry for any DC
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
DcInfo = NULL;
|
||
NTFRSAPI_DBG_PRINT2("WARN - Could not get DC Info for %ws; WStatus %d\n",
|
||
DomainName, WStatus);
|
||
//
|
||
// Try the parent domain
|
||
//
|
||
while (*DomainName && *DomainName != L'.') {
|
||
++DomainName;
|
||
}
|
||
if (*DomainName) {
|
||
++DomainName;
|
||
goto FIND_DC;
|
||
}
|
||
goto CLEANUP;
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("DomainControllerName : %ws\n", DcInfo->DomainControllerName);
|
||
NTFRSAPI_DBG_PRINT1("DomainControllerAddress: %ws\n", DcInfo->DomainControllerAddress);
|
||
NTFRSAPI_DBG_PRINT1("DomainControllerType : %08x\n",DcInfo->DomainControllerAddressType);
|
||
NTFRSAPI_DBG_PRINT1("DomainName : %ws\n", DcInfo->DomainName);
|
||
NTFRSAPI_DBG_PRINT1("DcSiteName : %ws\n", DcInfo->DcSiteName);
|
||
NTFRSAPI_DBG_PRINT1("ClientSiteName : %ws\n", DcInfo->ClientSiteName);
|
||
|
||
InfoFlags = DcInfo->Flags;
|
||
FrsFlagsToStr(InfoFlags, NtFrsApi_DsGetDcInfoFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
|
||
NTFRSAPI_DBG_PRINT2("Flags : %08x Flags [%s]\n",InfoFlags, FlagBuffer);
|
||
|
||
//
|
||
// Open and bind to the DS
|
||
//
|
||
//
|
||
// if ldap_open is called with a server name the api will call DsGetDcName
|
||
// passing the server name as the domainname parm...bad, because
|
||
// DsGetDcName will make a load of DNS queries based on the server name,
|
||
// it is designed to construct these queries from a domain name...so all
|
||
// these queries will be bogus, meaning they will waste network bandwidth,
|
||
// time to fail, and worst case cause expensive on demand links to come up
|
||
// as referrals/forwarders are contacted to attempt to resolve the bogus
|
||
// names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
|
||
// after the ldap_init but before any other operation using the ldap
|
||
// handle from ldap_init, the delayed connection setup will not call
|
||
// DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
|
||
// will detect that and use the address directly.
|
||
//
|
||
|
||
//
|
||
// Trim the leadinf \\ because ldap does not like it.
|
||
//
|
||
|
||
// DsDeleteLdap = ldap_open(DcInfo->DomainControllerName, LDAP_PORT);
|
||
|
||
ulOptions = PtrToUlong(LDAP_OPT_ON);
|
||
Timeout.tv_sec = NTFRSAPI_LDAP_CONNECT_TIMEOUT;
|
||
Timeout.tv_usec = 0;
|
||
|
||
//
|
||
// Try using DomainControllerName first.
|
||
//
|
||
|
||
if (DcInfo->DomainControllerName != NULL) {
|
||
DomainControllerName = DcInfo->DomainControllerName;
|
||
FRS_TRIM_LEADING_2SLASH(DomainControllerName);
|
||
|
||
DsDeleteLdap = ldap_init(DomainControllerName, LDAP_PORT);
|
||
if (DsDeleteLdap != NULL) {
|
||
ldap_set_option(DsDeleteLdap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
|
||
LStatus = ldap_connect(DsDeleteLdap, &Timeout);
|
||
if (LStatus != LDAP_SUCCESS) {
|
||
NTFRSAPI_DBG_PRINT2("ERROR - Connecting to the DS on %ws; %ws)\n",
|
||
DomainControllerName, ldap_err2string(LStatus));
|
||
ldap_unbind_s(DsDeleteLdap);
|
||
DsDeleteLdap = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Try using DomainControllerAddress next.
|
||
//
|
||
if ((DsDeleteLdap == NULL) && (DcInfo->DomainControllerAddress != NULL)) {
|
||
|
||
DomainControllerAddress = DcInfo->DomainControllerAddress;
|
||
FRS_TRIM_LEADING_2SLASH(DomainControllerAddress);
|
||
|
||
DsDeleteLdap = ldap_init(DomainControllerAddress, LDAP_PORT);
|
||
if (DsDeleteLdap != NULL) {
|
||
ldap_set_option(DsDeleteLdap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
|
||
LStatus = ldap_connect(DsDeleteLdap, &Timeout);
|
||
if (LStatus != LDAP_SUCCESS) {
|
||
NTFRSAPI_DBG_PRINT2("ERROR - Connecting to the DS on %ws; %ws)\n",
|
||
DomainControllerAddress, ldap_err2string(LStatus));
|
||
ldap_unbind_s(DsDeleteLdap);
|
||
DsDeleteLdap = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Can not connect to DC. Give up.
|
||
//
|
||
if (DsDeleteLdap == NULL) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
LStatus = ldap_bind_s(DsDeleteLdap, NULL, (PWCHAR) Credentials, LDAP_AUTH_NEGOTIATE);
|
||
if (LStatus != LDAP_SUCCESS) {
|
||
NTFRSAPI_DBG_PRINT2("ERROR - Binding to the DS on %ws; %ws)\n",
|
||
DcInfo->DomainControllerName, ldap_err2string(LStatus));
|
||
ldap_unbind_s(DsDeleteLdap);
|
||
DsDeleteLdap = NULL;
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Fetch the naming contexts
|
||
//
|
||
Attrs[0] = ATTR_NAMING_CONTEXTS;
|
||
Attrs[1] = ATTR_DEFAULT_NAMING_CONTEXT;
|
||
Attrs[2] = NULL;
|
||
LStatus = ldap_search_s(DsDeleteLdap,
|
||
CN_ROOT,
|
||
LDAP_SCOPE_BASE,
|
||
CATEGORY_ANY,
|
||
Attrs,
|
||
0,
|
||
&LdapMsg);
|
||
if (LStatus != LDAP_SUCCESS) {
|
||
NTFRSAPI_DBG_PRINT2("ERROR - Getting naming contexts from %ws; %ws\n",
|
||
DcInfo->DomainControllerName, ldap_err2string(LStatus));
|
||
goto CLEANUP;
|
||
}
|
||
|
||
LdapEntry = ldap_first_entry(DsDeleteLdap, LdapMsg);
|
||
if (!LdapEntry) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - No naming contexts for %ws\n",
|
||
DcInfo->DomainControllerName);
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// ATTR_NAMING_CONTEXTS
|
||
// Configuration, Schema, and ???
|
||
//
|
||
LdapValues = (PWCHAR *)DsDeleteFindValues(DsDeleteLdap,
|
||
LdapEntry,
|
||
ATTR_NAMING_CONTEXTS);
|
||
if (!LdapValues) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - no values for naming contexts for %ws\n",
|
||
DcInfo->DomainControllerName);
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Now, find the naming context that begins with "cn=configuration"
|
||
//
|
||
Idx = ldap_count_values(LdapValues);
|
||
while (Idx--) {
|
||
if (!LdapValues[Idx]) {
|
||
continue;
|
||
}
|
||
_wcslwr(LdapValues[Idx]);
|
||
LdapValue = wcsstr(LdapValues[Idx], CONFIG_NAMING_CONTEXT);
|
||
if (LdapValue && LdapValue == LdapValues[Idx]) {
|
||
break;
|
||
} else {
|
||
LdapValue = NULL;
|
||
}
|
||
}
|
||
if (!LdapValue) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - No configuration naming context from %ws\n",
|
||
DcInfo->DomainControllerName);
|
||
goto CLEANUP;
|
||
}
|
||
wcscpy(DsDeleteConfigDn, LdapValue);
|
||
NTFRSAPI_DBG_PRINT1("Configuration naming context is %ws\n", DsDeleteConfigDn);
|
||
ldap_value_free(LdapValues);
|
||
LdapValues = NULL;
|
||
|
||
|
||
//
|
||
// ATTR_DEFAULT_NAMING_CONTEXT
|
||
//
|
||
LdapValues = (PWCHAR *)DsDeleteFindValues(DsDeleteLdap,
|
||
LdapEntry,
|
||
ATTR_DEFAULT_NAMING_CONTEXT);
|
||
if (!LdapValues || !LdapValues[0]) {
|
||
NTFRSAPI_DBG_PRINT1("ERROR - No values for default naming context from %ws\n",
|
||
DcInfo->DomainControllerName);
|
||
goto CLEANUP;
|
||
}
|
||
wcscpy(DsDeleteDefaultDn, LdapValues[0]);
|
||
NTFRSAPI_DBG_PRINT1("Default naming context is %ws\n", DsDeleteDefaultDn);
|
||
ldap_value_free(LdapValues);
|
||
LdapValues = NULL;
|
||
|
||
CLEANUP:
|
||
if (LdapMsg) {
|
||
ldap_msgfree(LdapMsg);
|
||
}
|
||
if (LdapValues) {
|
||
ldap_value_free(LdapValues);
|
||
}
|
||
if (DcInfo) {
|
||
NetApiBufferFree(DcInfo);
|
||
DcInfo = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
DsDeleteCommit(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Called from NtFrsApi_CommitDemotionW().
|
||
|
||
Use the binding squirreled away by DsDeletePrepare() to attempt
|
||
the deletion of the settings, set, member, subscriptions, and
|
||
subscriber objects.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "DsDeleteCommit:"
|
||
DWORD Idx;
|
||
DWORD LStatus;
|
||
LONG Count;
|
||
PNTFRSAPI_THREAD Thread;
|
||
PFQDN_CONSTRUCTION_TABLE Entry;
|
||
PWCHAR ArgTable[FQDN_MAX_COUNT];
|
||
WCHAR Dn[MAX_DN];
|
||
|
||
//
|
||
// No binding; done
|
||
//
|
||
if (!DsDeleteLdap) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// For each sysvol
|
||
//
|
||
for (Thread = NtFrsApi_Threads, Idx = 0;
|
||
Thread && Idx < NtFrsApi_NumberOfThreads;
|
||
Thread = Thread->Next, ++Idx) {
|
||
|
||
//
|
||
// First make a copy of the standard argument table and then update the
|
||
// entries to point to variable arguments. Most of the standard argument
|
||
// entries point to contstant name strings or to Global Strings.
|
||
//
|
||
CopyMemory(ArgTable, FQDN_StdArgTable, sizeof(ArgTable));
|
||
|
||
if (Thread->ReplicaSetName != NULL) {
|
||
ArgTable[FQDN_RepSetName] = Thread->ReplicaSetName;;
|
||
}
|
||
|
||
//
|
||
// Loop thru the entries in the FrsDsObjectDeleteTable, build each
|
||
// FQDN string and make the call to delete the object.
|
||
//
|
||
Entry = FrsDsObjectDeleteTable;
|
||
|
||
while (Entry->Description != NULL) {
|
||
|
||
//
|
||
// Construct the FQDN string for the object.
|
||
//
|
||
Count = _snwprintf(Dn, MAX_DN, Entry->Format,
|
||
ArgTable[Entry->Arg[0]], ArgTable[Entry->Arg[1]],
|
||
ArgTable[Entry->Arg[2]], ArgTable[Entry->Arg[3]],
|
||
ArgTable[Entry->Arg[4]], ArgTable[Entry->Arg[5]],
|
||
ArgTable[Entry->Arg[6]], ArgTable[Entry->Arg[7]]);
|
||
//
|
||
// Delete the object.
|
||
//
|
||
if (Count > 0) {
|
||
NTFRSAPI_DBG_PRINT2("%s: %ws\n", Entry->Description, Dn);
|
||
|
||
LStatus = ldap_delete_s(DsDeleteLdap, Dn);
|
||
|
||
if (LStatus != LDAP_SUCCESS && LStatus != LDAP_NO_SUCH_OBJECT) {
|
||
NTFRSAPI_DBG_PRINT4("ERROR - Can't delete %s (%ws) for %ws; %ws\n",
|
||
Entry->Description, Dn, ArgTable[FQDN_RepSetName],
|
||
ldap_err2string(LStatus));
|
||
}
|
||
} else {
|
||
NTFRSAPI_DBG_PRINT2("ERROR - Can't construct %s for %ws\n",
|
||
Entry->Description, ArgTable[FQDN_RepSetName]);
|
||
}
|
||
|
||
Entry++;
|
||
}
|
||
|
||
}
|
||
|
||
CLEANUP:
|
||
if (DsDeleteLdap) {
|
||
ldap_unbind_s(DsDeleteLdap);
|
||
DsDeleteLdap = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_PrepareForDemotionW(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This function prepares the NtFrs service on this machine for
|
||
demotion by stopping the service, deleting old demotion
|
||
state in the registry, and restarting the service.
|
||
|
||
This function is not idempotent and isn't MT safe.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_PrepareForDemotionW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PREPARE();
|
||
|
||
NTFRSAPI_DBG_PRINT0("\n");
|
||
NTFRSAPI_DBG_PRINT0("=============== Demotion Starting: \n");
|
||
NTFRSAPI_DBG_PRINT0("\n");
|
||
|
||
NTFRSAPI_DBG_PRINT0("Prepare demotion:\n");
|
||
WStatus = NtFrsApi_Prepare(ErrorCallBack, TRUE);
|
||
NTFRSAPI_DBG_PRINT1("Prepare demotion done: %d\n", WStatus);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Prepare delete:\n");
|
||
DsDeletePrepare(NULL);
|
||
NTFRSAPI_DBG_PRINT1("Prepare delete done: %d\n", WStatus);
|
||
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_PrepareForDemotionUsingCredW(
|
||
IN SEC_WINNT_AUTH_IDENTITY *Credentials, OPTIONAL
|
||
IN HANDLE ClientToken,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This function prepares the NtFrs service on this machine for
|
||
demotion by stopping the service, deleting old demotion
|
||
state in the registry, and restarting the service.
|
||
|
||
This function is not idempotent and isn't MT safe.
|
||
|
||
Arguments:
|
||
|
||
Credentials -- Credentionals to use in ldap binding call, if supplied.
|
||
|
||
ClientToken -- Impersonation token to use if no Credentials supplied.
|
||
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_PrepareForDemotionUsingCredW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PREPARE();
|
||
|
||
NTFRSAPI_DBG_PRINT0("\n");
|
||
NTFRSAPI_DBG_PRINT0("=============== Demotion Starting: \n");
|
||
NTFRSAPI_DBG_PRINT0("\n");
|
||
|
||
NTFRSAPI_DBG_PRINT0("Prepare demotion:\n");
|
||
WStatus = NtFrsApi_Prepare(ErrorCallBack, TRUE);
|
||
NTFRSAPI_DBG_PRINT1("Prepare demotion done: %d\n", WStatus);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Prepare delete:\n");
|
||
|
||
if ((Credentials == NULL) && HANDLE_IS_VALID(ClientToken)) {
|
||
if (ImpersonateLoggedOnUser( ClientToken )) {
|
||
DsDeletePrepare(Credentials);
|
||
NTFRSAPI_DBG_PRINT0("Prepare delete done.\n");
|
||
RevertToSelf();
|
||
} else {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Prepare delete: ImpersonateLoggedOnUser failed: %d\n", WStatus);
|
||
}
|
||
} else {
|
||
DsDeletePrepare(Credentials);
|
||
NTFRSAPI_DBG_PRINT0("Prepare delete done.\n");
|
||
}
|
||
|
||
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_Abort(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC) and stops replication
|
||
when a server is demoted from a DC by tombstoning the system
|
||
volume's replica set.
|
||
|
||
This function aborts the seeding or tombstoning process by
|
||
stopping the service, deleting the state from the registry,
|
||
cleaning up the active threads and the active RPC calls,
|
||
and finally resetting the service to its pre-seeding/tombstoning
|
||
state.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Abort:"
|
||
DWORD WStatus;
|
||
SERVICE_STATUS ServiceStatus;
|
||
PNTFRSAPI_THREAD Thread;
|
||
HKEY HKey = 0;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
WCHAR KeyBuf[MAX_PATH + 1];
|
||
|
||
try {
|
||
//
|
||
// Acquire all locks
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
EnterCriticalSection(&NtFrsApi_ThreadLock);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Abort: \n");
|
||
|
||
//
|
||
// This function is designed to be called once!
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_PREPARED &&
|
||
NtFrsApi_State != NTFRSAPI_PREPARED_SERVICE) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
NtFrsApi_State = NTFRSAPI_ABORTING;
|
||
|
||
//
|
||
// Stop the service, kill off the active threads,
|
||
// delete old state from the registry, and restart
|
||
// the service if it was running prior to the call
|
||
// to NtFrsApi_PrepareForPromotionW.
|
||
//
|
||
try {
|
||
//
|
||
// Set the shutdown event
|
||
//
|
||
SetEvent(NtFrsApi_ShutDownEvent);
|
||
|
||
//
|
||
// Abort the threads
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Abort: threads\n");
|
||
while (Thread = NtFrsApi_Threads) {
|
||
NtFrsApi_Threads = Thread->Next;
|
||
NtFrsApi_FreeThread(Thread);
|
||
}
|
||
|
||
//
|
||
// Open the service
|
||
//
|
||
WStatus = NtFrsApi_GetServiceHandle(NULL, &ServiceHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the service's state
|
||
//
|
||
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Abort: QueryServiceStatus(); %d\n", WStatus);
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Stop the service
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Abort: service\n");
|
||
|
||
if (ServiceStatus.dwCurrentState != SERVICE_STOPPED) {
|
||
WStatus = NtFrsApi_StopService(ServiceHandle, &ServiceStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Delete old state from the registry
|
||
//
|
||
//
|
||
// Open the ntfrs parameters\sysvol section in the registry
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Abort: registry\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
NULL,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Delete the value that indicates the sysvol subkeys are valid
|
||
//
|
||
WStatus = NtFrsApiDeleteValue(NTFRSAPI_MODULE,
|
||
NULL,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
SYSVOL_INFO_IS_COMMITTED);
|
||
if (!WIN_SUCCESS(WStatus) && WStatus != ERROR_FILE_NOT_FOUND) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Delete the subkeys
|
||
//
|
||
do {
|
||
WStatus = RegEnumKey(HKey, 0, KeyBuf, MAX_PATH + 1);
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApiDeleteKey(NTFRSAPI_MODULE,
|
||
NULL,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
KeyBuf);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
} while (WIN_SUCCESS(WStatus));
|
||
|
||
if (WStatus != ERROR_NO_MORE_ITEMS) {
|
||
NTFRSAPI_DBG_PRINT2("Abort: RegEnumKey(%ws); %d\n",
|
||
FRS_SYSVOL_SECTION, WStatus);
|
||
CLEANUP_CB(NULL, FRS_SYSVOL_SECTION, WStatus, cleanup);
|
||
}
|
||
|
||
//
|
||
// Restart the service if needed
|
||
//
|
||
if (NtFrsApi_ServiceState == SERVICE_RUNNING) {
|
||
NTFRSAPI_DBG_PRINT0("Abort: restarting\n");
|
||
if (!StartService(ServiceHandle, 0, NULL)) {
|
||
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT2("Abort: StartService(%ws); %d\n",
|
||
NtFrsApi_ServiceLongName, WStatus);
|
||
CLEANUP_CB(NULL, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
|
||
//
|
||
// Wait for the service to start
|
||
//
|
||
WStatus = NtFrsApi_WaitForService(ServiceHandle,
|
||
NtFrsApi_ServiceWaitHint,
|
||
SERVICE_START_PENDING,
|
||
SERVICE_RUNNING);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = FRS_ERR_STARTING_SERVICE;
|
||
goto cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Success
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
NtFrsApi_State = NTFRSAPI_ABORTED;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (ServiceHandle) {
|
||
CloseServiceHandle(ServiceHandle);
|
||
}
|
||
if (HANDLE_IS_VALID(HKey)) {
|
||
RegCloseKey(HKey);
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
LeaveCriticalSection(&NtFrsApi_ThreadLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Abort done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_AbortPromotionW(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC). The files and directories
|
||
for the system volume come from the same machine that is supplying
|
||
the initial Directory Service (DS).
|
||
|
||
This function aborts the seeding process by stopping the service,
|
||
deleting the promotion state out of the registry, cleaning up
|
||
the active threads and the active RPC calls, and finally resetting
|
||
the service to its pre-seeding state.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_AbortPromotionW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PRINT0("Abort promotion: \n");
|
||
WStatus = NtFrsApi_Abort();
|
||
NTFRSAPI_DBG_PRINT1("Abort promotion done: %d\n", WStatus);
|
||
NTFRSAPI_DBG_UNPREPARE();
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_AbortDemotionW(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This function aborts the tombstoning process by stopping the service,
|
||
deleting the demotion state out of the registry, cleaning up
|
||
the active threads and the active RPC calls, and finally resetting
|
||
the service to its pre-tombstoning state.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_AbortDemotionW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PRINT0("Abort demotion:\n");
|
||
WStatus = NtFrsApi_Abort();
|
||
NTFRSAPI_DBG_PRINT1("Abort demotion done: %d\n", WStatus);
|
||
NTFRSAPI_DBG_UNPREPARE();
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_StartPromotion_Thread(
|
||
IN PNTFRSAPI_THREAD Thread
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
THIS FUNCTION IS A THREAD ENTRY!
|
||
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC). The files and directories
|
||
for the system volume come from the same machine that is supplying
|
||
the initial Directory Service (DS).
|
||
|
||
This thread that updates the sysvol information in the registry
|
||
and initiates the seeding process. The thread tracks the progress
|
||
of the seeding and periodically informs the caller.
|
||
|
||
The threads started by NtFrsApi_StartPromotionW can be forcefully
|
||
terminated with NtFrsApi_AbortPromotionW.
|
||
|
||
The threads started by NtFrsApi_StartPromotionW can be waited on
|
||
with NtFrsApi_WaitForPromotionW.
|
||
|
||
Arguments:
|
||
Thread - thread context
|
||
|
||
Return Value:
|
||
Win32 Status and updates to the thread context.
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_StartPromotion_Thread:"
|
||
DWORD WStatus, WStatus1;
|
||
DWORD WaitStatus;
|
||
HKEY HKey = 0;
|
||
HKEY HSubKey = 0;
|
||
handle_t Handle = NULL;
|
||
|
||
try {
|
||
try {
|
||
//
|
||
// Abort client RPC calls on demand
|
||
//
|
||
WStatus = RpcMgmtSetCancelTimeout(0);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: RpcMgmtSetCancelTimeout(); %d\n",
|
||
WStatus);
|
||
CLEANUP_CB(Thread->ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Parent %ws\n", Thread->ParentComputer);
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Account %ws\n", Thread->ParentAccount);
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Set %ws\n", Thread->ReplicaSetName);
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Type %ws\n", Thread->ReplicaSetType);
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Primary %d\n", Thread->ReplicaSetPrimary);
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Stage %ws\n", Thread->ReplicaSetStage);
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread start: Root %ws\n", Thread->ReplicaSetRoot);
|
||
|
||
//
|
||
// Update the registry and initiate the seeding process
|
||
//
|
||
|
||
//
|
||
// Set new state in the registry
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Create the subkey for this set
|
||
//
|
||
WStatus = NtFrsApiCreateKey(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
Thread->ReplicaSetName,
|
||
&HSubKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Set the subkey's values
|
||
//
|
||
//
|
||
// Replica set parent
|
||
//
|
||
if (Thread->ParentComputer) {
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_PARENT,
|
||
REG_SZ,
|
||
(PCHAR)Thread->ParentComputer,
|
||
(wcslen(Thread->ParentComputer) + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
//
|
||
// Replica set command
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_COMMAND,
|
||
REG_SZ,
|
||
(PCHAR)L"Create",
|
||
(wcslen(L"Create") + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Replica set name
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_NAME,
|
||
REG_SZ,
|
||
(PCHAR)Thread->ReplicaSetName,
|
||
(wcslen(Thread->ReplicaSetName) + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Replica set type
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_TYPE,
|
||
REG_SZ,
|
||
(PCHAR)Thread->ReplicaSetType,
|
||
(wcslen(Thread->ReplicaSetType) + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Replica set primary
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_PRIMARY,
|
||
REG_DWORD,
|
||
(PCHAR)&Thread->ReplicaSetPrimary,
|
||
sizeof(DWORD));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Replica set root
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_ROOT,
|
||
REG_SZ,
|
||
(PCHAR)Thread->ReplicaSetRoot,
|
||
(wcslen(Thread->ReplicaSetRoot) + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Replica set stage
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_STAGE,
|
||
REG_SZ,
|
||
(PCHAR)Thread->ReplicaSetStage,
|
||
(wcslen(Thread->ReplicaSetStage) + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Bind to the service
|
||
//
|
||
WStatus = NtFrsApi_BindForDcpromo(NULL, NULL, &Handle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
//
|
||
// Ignore errors until reboot
|
||
//
|
||
Thread->ServiceState = NTFRSAPI_SERVICE_DONE;
|
||
Thread->ServiceWStatus = ERROR_SUCCESS;
|
||
WStatus = ERROR_SUCCESS;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Tell the service to start the promotion by demoting
|
||
// existing sysvols.
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread rpc demote: Set %ws\n",
|
||
Thread->ReplicaSetName);
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_StartDemotionW(Handle, L"");
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
NTFRSAPI_DBG_PRINT2("Promotion thread rpc demote done: %d (%08x)\n",
|
||
WStatus, WStatus);
|
||
//
|
||
// Ignore errors; sysvol will be seeded after promotion
|
||
//
|
||
// if (!WIN_SUCCESS(WStatus)) {
|
||
// WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
// goto cleanup;
|
||
// }
|
||
//
|
||
//
|
||
Thread->ServiceState = NTFRSAPI_SERVICE_DONE;
|
||
Thread->ServiceWStatus = ERROR_SUCCESS;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
//
|
||
// Success
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (HANDLE_IS_VALID(HKey)) {
|
||
RegCloseKey(HKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HSubKey)) {
|
||
RegCloseKey(HSubKey);
|
||
}
|
||
if (Handle) {
|
||
WStatus1 = RpcBindingFree(&Handle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} finally {
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
Thread->ThreadWStatus = WStatus;
|
||
NTFRSAPI_DBG_PRINT1("Promotion thread complete: Set %ws\n", Thread->ReplicaSetName);
|
||
NTFRSAPI_DBG_PRINT2("Promotion thread complete: Thread %d, Service %d\n",
|
||
Thread->ThreadWStatus, Thread->ServiceWStatus);
|
||
SetEvent(Thread->DoneEvent);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_StartPromotionW(
|
||
IN PWCHAR ParentComputer, OPTIONAL
|
||
IN PWCHAR ParentAccount, OPTIONAL
|
||
IN PWCHAR ParentPassword, OPTIONAL
|
||
IN DWORD DisplayCallBack(IN PWCHAR Display), OPTIONAL
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN PWCHAR ReplicaSetName,
|
||
IN PWCHAR ReplicaSetType,
|
||
IN DWORD ReplicaSetPrimary,
|
||
IN PWCHAR ReplicaSetStage,
|
||
IN PWCHAR ReplicaSetRoot
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC). The files and directories
|
||
for the system volume come from the same machine that is supplying
|
||
the initial Directory Service (DS).
|
||
|
||
This function kicks off a thread that updates the sysvol information
|
||
in the registry and initiates the seeding process. The thread tracks
|
||
the progress of the seeding and periodically informs the caller.
|
||
|
||
The threads started by NtFrsApi_StartPromotionW can be forcefully
|
||
terminated with NtFrsApi_AbortPromotionW.
|
||
|
||
The threads started by NtFrsApi_StartPromotionW can be waited on
|
||
with NtFrsApi_WaitForPromotionW.
|
||
|
||
Arguments:
|
||
ParentComputer - An RPC-bindable name of the computer that is
|
||
supplying the Directory Service (DS) with its
|
||
initial state. The files and directories for
|
||
the system volume are replicated from this
|
||
parent computer.
|
||
ParentAccount - A logon account on ParentComputer.
|
||
NULL == use the caller's credentials
|
||
ParentPassword - The logon account's password on ParentComputer.
|
||
DisplayCallBack - Called periodically with a progress display.
|
||
ReplicaSetName - Name of the replica set.
|
||
ReplicaSetType - Type of replica set (enterprise or domain)
|
||
ReplicaSetPrimary - Is this the primary member of the replica set?
|
||
ReplicaSetStage - Staging path.
|
||
ReplicaSetRoot - Root path.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_StartPromotionW:"
|
||
DWORD WStatus, WStatus1;
|
||
handle_t Handle = NULL;
|
||
|
||
try {
|
||
//
|
||
// Acquire global lock within a try-finally
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Parent %ws\n", ParentComputer);
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Account %ws\n", ParentAccount);
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Set %ws\n", ReplicaSetName);
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Type %ws\n", ReplicaSetType);
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Primary %d\n", ReplicaSetPrimary);
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Stage %ws\n", ReplicaSetStage);
|
||
NTFRSAPI_DBG_PRINT1("Promotion start: Root %ws\n", ReplicaSetRoot);
|
||
|
||
ParentAccount = NULL;
|
||
ParentPassword = NULL;
|
||
|
||
//
|
||
// This function is designed to be called once!
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_PREPARED) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Update the registry and initiate the seeding process
|
||
//
|
||
try {
|
||
//
|
||
// Check parameters
|
||
//
|
||
// What about kerberos,delegation,impersonation,no account?
|
||
//
|
||
if (!ReplicaSetName ||
|
||
!ReplicaSetType ||
|
||
!ReplicaSetStage ||
|
||
!ReplicaSetRoot ||
|
||
(!ParentComputer && ReplicaSetPrimary != 1)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
if (WSTR_NE(ReplicaSetType, NTFRSAPI_REPLICA_SET_TYPE_ENTERPRISE) &&
|
||
WSTR_NE(ReplicaSetType, NTFRSAPI_REPLICA_SET_TYPE_DOMAIN)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
if (ReplicaSetPrimary != 0 && ReplicaSetPrimary != 1) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
|
||
WStatus = NtFrsApi_CreateThread(NtFrsApi_StartPromotion_Thread,
|
||
ParentComputer,
|
||
ParentAccount,
|
||
ParentPassword,
|
||
DisplayCallBack,
|
||
ErrorCallBack,
|
||
ReplicaSetName,
|
||
ReplicaSetType,
|
||
ReplicaSetPrimary,
|
||
ReplicaSetStage,
|
||
ReplicaSetRoot);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Success
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (Handle) {
|
||
WStatus1 = RpcBindingFree(&Handle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT2("Promotion start done: Set %ws, %d\n",
|
||
ReplicaSetName, WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_StartDemotion_Thread(
|
||
IN PNTFRSAPI_THREAD Thread
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
THIS FUNCTION IS A THREAD ENTRY!
|
||
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This thread stops replicating the system volume by telling
|
||
the NtFrs service on this machine to tombstone the system
|
||
volume's replica set.
|
||
|
||
The threads started by NtFrsApi_StartDemotionW can be forcefully
|
||
terminated with NtFrsApi_AbortDemotionW.
|
||
|
||
The threads started by NtFrsApi_StartDemotionW can be waited on
|
||
with NtFrsApi_WaitForDemotionW.
|
||
|
||
Arguments:
|
||
Thread - thread context
|
||
|
||
Return Value:
|
||
Win32 Status and updated thread context
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_StartDemotion_Thread:"
|
||
DWORD WStatus, WStatus1;
|
||
HKEY HKey = 0;
|
||
HKEY HSubKey = 0;
|
||
handle_t Handle = NULL;
|
||
|
||
|
||
try {
|
||
try {
|
||
//
|
||
// Abort client RPC calls on demand
|
||
//
|
||
WStatus = RpcMgmtSetCancelTimeout(0);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT1("Demotion thread start: RpcMgmtSetCancelTimeout(); %d\n", WStatus);
|
||
CLEANUP_CB(Thread->ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Demotion thread start: Set %ws\n", Thread->ReplicaSetName);
|
||
|
||
//
|
||
// Update the registry and initiate the seeding process
|
||
//
|
||
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Create the subkey for this set
|
||
//
|
||
WStatus = NtFrsApiCreateKey(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
Thread->ReplicaSetName,
|
||
&HSubKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Set the subkey's values
|
||
//
|
||
|
||
//
|
||
// Replica set command
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_COMMAND,
|
||
REG_SZ,
|
||
(PCHAR)L"Delete",
|
||
(wcslen(L"Delete") + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Replica set name
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
Thread->ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HSubKey,
|
||
REPLICA_SET_NAME,
|
||
REG_SZ,
|
||
(PCHAR)Thread->ReplicaSetName,
|
||
(wcslen(Thread->ReplicaSetName) + 1) * sizeof(WCHAR));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Bind to the service
|
||
//
|
||
WStatus = NtFrsApi_BindForDcpromo(NULL,
|
||
Thread->ErrorCallBack,
|
||
&Handle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Tell the service to demote the sysvol
|
||
//
|
||
NTFRSAPI_DBG_PRINT1("Demotion thread rpc start: Set %ws\n",
|
||
Thread->ReplicaSetName);
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_StartDemotionW(Handle,
|
||
Thread->ReplicaSetName);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
NTFRSAPI_DBG_PRINT3("Demotion thread rpc start done: Set %ws, %d (%08x)\n",
|
||
Thread->ReplicaSetName, WStatus, WStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Success
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (HANDLE_IS_VALID(HKey)) {
|
||
RegCloseKey(HKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HSubKey)) {
|
||
RegCloseKey(HSubKey);
|
||
}
|
||
if (Handle) {
|
||
WStatus1 = RpcBindingFree(&Handle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} finally {
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
Thread->ThreadWStatus = WStatus;
|
||
Thread->ServiceState = NTFRSAPI_SERVICE_DONE;
|
||
Thread->ServiceWStatus = ERROR_SUCCESS;
|
||
NTFRSAPI_DBG_PRINT1("Demotion thread done: Set %ws\n", Thread->ReplicaSetName);
|
||
NTFRSAPI_DBG_PRINT2("Demotion thread done: Thread %d, Service %d\n",
|
||
Thread->ThreadWStatus, Thread->ServiceWStatus);
|
||
SetEvent(Thread->DoneEvent);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_StartDemotionW(
|
||
IN PWCHAR ReplicaSetName,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This function kicks off a thread that stops replicating the
|
||
system volume by telling the NtFrs service on this machine
|
||
to tombstone the system volume's replica set.
|
||
|
||
The threads started by NtFrsApi_StartDemotionW can be forcefully
|
||
terminated with NtFrsApi_AbortDemotionW.
|
||
|
||
The threads started by NtFrsApi_StartDemotionW can be waited on
|
||
with NtFrsApi_WaitForDemotionW.
|
||
|
||
Arguments:
|
||
ReplicaSetName - Name of the replica set.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_StartDemotionW:"
|
||
DWORD WStatus;
|
||
|
||
try {
|
||
//
|
||
// Acquire global lock within a try-finally
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
NTFRSAPI_DBG_PRINT1("Demotion start: Set %ws\n", ReplicaSetName);
|
||
|
||
//
|
||
// This function is designed to be called once!
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_PREPARED) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Update the registry and initiate the seeding process
|
||
//
|
||
try {
|
||
//
|
||
// Check parameters
|
||
//
|
||
if (!ReplicaSetName) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Create the demotion thread
|
||
//
|
||
WStatus = NtFrsApi_CreateThread(NtFrsApi_StartDemotion_Thread,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
ErrorCallBack,
|
||
ReplicaSetName,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
NULL);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Success
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT2("Demotion start done: Set %ws %d\n",
|
||
ReplicaSetName, WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_Wait(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN DWORD TimeoutInMilliSeconds
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC) and stops replicating
|
||
the system volumes when a DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This function waits for the seeding or tombstoning to finish
|
||
or to stop w/error.
|
||
|
||
NOT MT-SAFE.
|
||
|
||
Arguments:
|
||
TimeoutInMilliSeconds - Timeout in milliseconds for waiting for
|
||
seeding/tombstoning to finish.
|
||
ErrorCallBack - Ignored if NULL. Called with additional
|
||
info about an error.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Wait:"
|
||
DWORD WStatus;
|
||
DWORD WaitStatus;
|
||
DWORD i;
|
||
PNTFRSAPI_THREAD Thread;
|
||
HANDLE *Handles = NULL;
|
||
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
try {
|
||
//
|
||
// Acquire all locks
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
EnterCriticalSection(&NtFrsApi_ThreadLock);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Wait: \n");
|
||
|
||
//
|
||
// Nothing to wait on
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_PREPARED) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
try {
|
||
|
||
//
|
||
// Collect the done events
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Wait: Threads\n");
|
||
Handles = NtFrsApi_Alloc(NtFrsApi_NumberOfThreads * sizeof(HANDLE));
|
||
for (Thread = NtFrsApi_Threads, i = 0;
|
||
Thread && i < NtFrsApi_NumberOfThreads;
|
||
Thread = Thread->Next, ++i) {
|
||
Handles[i] = Thread->DoneEvent;
|
||
}
|
||
|
||
//
|
||
// Wait on the threads' done events
|
||
//
|
||
WaitStatus = WaitForMultipleObjects(NtFrsApi_NumberOfThreads,
|
||
Handles,
|
||
TRUE,
|
||
TimeoutInMilliSeconds);
|
||
//
|
||
// Timeout
|
||
//
|
||
if (WaitStatus == WAIT_TIMEOUT) {
|
||
WStatus = ERROR_TIMEOUT;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Wait failed
|
||
//
|
||
if (WaitStatus == WAIT_FAILED) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("%s WaitForMultipleObjects(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
|
||
//
|
||
// Return the threads' status
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Wait: Status\n");
|
||
for (Thread = NtFrsApi_Threads; Thread; Thread = Thread->Next) {
|
||
//
|
||
// Thread error
|
||
//
|
||
WStatus = Thread->ThreadWStatus;
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
//
|
||
// Service error
|
||
//
|
||
WStatus = Thread->ServiceWStatus;
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
FREE(Handles);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
LeaveCriticalSection(&NtFrsApi_ThreadLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Wait done: %d\n", WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_WaitForPromotionW(
|
||
IN DWORD TimeoutInMilliSeconds,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC). The files and directories
|
||
for the system volume come from the same machine that is supplying
|
||
the initial Directory Service (DS).
|
||
|
||
This function waits for the seeding to finish or to stop w/error.
|
||
|
||
Arguments:
|
||
TimeoutInMilliSeconds - Timeout in milliseconds for waiting for
|
||
seeding to finish.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_WaitForPromotionW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PRINT0("Wait promotion: \n");
|
||
WStatus = NtFrsApi_Wait(ErrorCallBack, TimeoutInMilliSeconds);
|
||
NTFRSAPI_DBG_PRINT1("Wait promotion done: %d\n", WStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NtFrsApi_AbortPromotionW();
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_WaitForDemotionW(
|
||
IN DWORD TimeoutInMilliSeconds,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server.
|
||
Replication is stopped by tombstoning the system volume's replica
|
||
set.
|
||
|
||
This function waits for the tombstoning to finish or to stop w/error.
|
||
|
||
Arguments:
|
||
TimeoutInMilliSeconds - Timeout in milliseconds for waiting for
|
||
tombstoning to finish.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_WaitForDemotionW:"
|
||
DWORD WStatus;
|
||
|
||
NTFRSAPI_DBG_PRINT0("Wait demotion: \n");
|
||
WStatus = NtFrsApi_Wait(ErrorCallBack, TimeoutInMilliSeconds);
|
||
NTFRSAPI_DBG_PRINT1("Wait demotion done: %d\n", WStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NtFrsApi_AbortDemotionW();
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_CommitPromotionW(
|
||
IN DWORD TimeoutInMilliSeconds,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
WARNING - This function assumes the caller will reboot the system
|
||
soon after this call!
|
||
|
||
The NtFrs service seeds the system volume during the promotion
|
||
of a server to a Domain Controller (DC). The files and directories
|
||
for the system volume come from the same machine that is supplying
|
||
the initial Directory Service (DS).
|
||
|
||
This function waits for the seeding to finish, stops the service,
|
||
and commits the state in the registry. On reboot, the NtFrs Service
|
||
updates the DS on this machine with the information in the registry.
|
||
|
||
Arguments:
|
||
TimeoutInMilliSeconds - Timeout in milliseconds for waiting for
|
||
seeding to finish.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_CommitPromotionW:"
|
||
DWORD WStatus;
|
||
SERVICE_STATUS ServiceStatus;
|
||
DWORD SysVolInfoIsCommitted = 1;
|
||
DWORD SysvolReady = 0;
|
||
HKEY HKey = 0;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
|
||
//
|
||
// Wait for the seeding to finish.
|
||
//
|
||
WStatus = NtFrsApi_WaitForPromotionW(TimeoutInMilliSeconds, ErrorCallBack);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT1("Commit promotion aborted: %d\n", WStatus);
|
||
NTFRSAPI_DBG_FLUSH();
|
||
return WStatus;
|
||
}
|
||
|
||
try {
|
||
//
|
||
// Acquire global lock within a try-finally
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Commit promotion:\n");
|
||
|
||
//
|
||
// This function is designed to be called once!
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_PREPARED) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
NtFrsApi_State = NTFRSAPI_COMMITTING;
|
||
|
||
//
|
||
// Stop the service and commit the new state in the registry
|
||
//
|
||
try {
|
||
//
|
||
// Open the service
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit promotion: service\n");
|
||
WStatus = NtFrsApi_GetServiceHandle(ErrorCallBack, &ServiceHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Stop the service
|
||
//
|
||
WStatus = NtFrsApi_StopService(ServiceHandle, &ServiceStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Commit the new state in the registry
|
||
//
|
||
//
|
||
// Open the ntfrs parameters\sysvol section in the registry
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit promotion: registry\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Set the value that indicates the sysvol subkeys are valid
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
SYSVOL_INFO_IS_COMMITTED,
|
||
REG_DWORD,
|
||
(PCHAR)&SysVolInfoIsCommitted,
|
||
sizeof(SysVolInfoIsCommitted));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Service starts automatically at startup
|
||
//
|
||
if (!ChangeServiceConfig(ServiceHandle,
|
||
SERVICE_NO_CHANGE,
|
||
SERVICE_AUTO_START,
|
||
SERVICE_NO_CHANGE,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NtFrsApi_ServiceLongName)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Commit promotion: no auto %d\n", WStatus);
|
||
}
|
||
|
||
//
|
||
// Success
|
||
//
|
||
NtFrsApi_State = NTFRSAPI_COMMITTED;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (ServiceHandle) {
|
||
CloseServiceHandle(ServiceHandle);
|
||
}
|
||
if (HANDLE_IS_VALID(HKey)) {
|
||
RegCloseKey(HKey);
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Commit promotion done: %d\n", WStatus);
|
||
NTFRSAPI_DBG_UNPREPARE();
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_CommitDemotionW(
|
||
IN DWORD TimeoutInMilliSeconds,
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
WARNING - This function assumes the caller will reboot the system
|
||
soon after this call!
|
||
|
||
The NtFrs service replicates the enterprise system volume to all
|
||
Domain Controllers (DCs) and replicates the domain system volume
|
||
to the DCs in a domain until the DC is demoted to a member server
|
||
by tombstoning the system volume's replica set.
|
||
|
||
This function waits for the tombstoning to finish, tells the service
|
||
to forcibly delete the system volumes' replica sets, stops the service,
|
||
and commits the state in the registry. On reboot, the NtFrs Service
|
||
updates the DS on this machine with the information in the registry.
|
||
|
||
Arguments:
|
||
TimeoutInMilliSeconds - Timeout in milliseconds for waiting for
|
||
tombstoning to finish.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_CommitDemotionW:"
|
||
DWORD WStatus, WStatus1;
|
||
SERVICE_STATUS ServiceStatus;
|
||
DWORD SysVolInfoIsCommitted = 1;
|
||
DWORD SysvolReady = 0;
|
||
HKEY HKey = 0;
|
||
HKEY HNetKey = 0;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
handle_t RpcHandle = NULL;
|
||
|
||
|
||
//
|
||
// Wait for the demotion to finish.
|
||
//
|
||
WStatus = NtFrsApi_WaitForDemotionW(TimeoutInMilliSeconds, ErrorCallBack);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT1("Commit demotion aborted: %d\n", WStatus);
|
||
NTFRSAPI_DBG_FLUSH();
|
||
return WStatus;
|
||
}
|
||
|
||
try {
|
||
//
|
||
// Acquire global lock within a try-finally
|
||
//
|
||
EnterCriticalSection(&NtFrsApi_GlobalLock);
|
||
|
||
NTFRSAPI_DBG_PRINT0("Commit demotion:\n");
|
||
|
||
//
|
||
// This function is designed to be called once!
|
||
//
|
||
if (NtFrsApi_State != NTFRSAPI_PREPARED) {
|
||
WStatus = FRS_ERR_INVALID_API_SEQUENCE;
|
||
goto done;
|
||
}
|
||
NtFrsApi_State = NTFRSAPI_COMMITTING;
|
||
|
||
//
|
||
// Stop the service and commit the new state in the registry
|
||
//
|
||
try {
|
||
//
|
||
// Set the RPC cancel timeout to "now"
|
||
//
|
||
WStatus = RpcMgmtSetCancelTimeout(0);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_DBG_PRINT1("Commit demotion: RpcMgmtSetCancelTimeout(); %d\n", WStatus);
|
||
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, cleanup);
|
||
}
|
||
|
||
//
|
||
// Bind to the service
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit demotion: service\n");
|
||
WStatus = NtFrsApi_BindForDcpromo(NULL, ErrorCallBack, &RpcHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Tell the service to commit the demotion
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit demotion rpc start:\n");
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_CommitDemotionW(RpcHandle);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
NTFRSAPI_DBG_PRINT2("Commit demotion rpc done: %d (%08x)\n",
|
||
WStatus, WStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Open the service
|
||
//
|
||
WStatus = NtFrsApi_GetServiceHandle(ErrorCallBack, &ServiceHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Stop the service
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit demotion: stop service\n");
|
||
|
||
WStatus = NtFrsApi_StopService(ServiceHandle, &ServiceStatus);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Commit the new state in the registry
|
||
//
|
||
//
|
||
// Open the ntfrs parameters\sysvol section in the registry
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit demotion: registry\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
#if 0
|
||
// Don't bother committing the "Delete sysvol" registry values because,
|
||
// after the reboot, the computer will not have sufficient rights to
|
||
// delete the sysvol from the Ds. Hence the call to DsDeleteCommit()
|
||
// below. Leave the code as a place holder for now.
|
||
//
|
||
// Set the value that indicates the sysvol subkeys are valid
|
||
//
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SYSVOL_SECTION,
|
||
HKey,
|
||
SYSVOL_INFO_IS_COMMITTED,
|
||
REG_DWORD,
|
||
(PCHAR)&SysVolInfoIsCommitted,
|
||
sizeof(SysVolInfoIsCommitted));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto cleanup;
|
||
}
|
||
#endif 0
|
||
|
||
//
|
||
// Service starts automatically at startup
|
||
//
|
||
if (!ChangeServiceConfig(ServiceHandle,
|
||
SERVICE_NO_CHANGE,
|
||
SERVICE_AUTO_START,
|
||
SERVICE_NO_CHANGE,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NtFrsApi_ServiceLongName)) {
|
||
WStatus = GetLastError();
|
||
NTFRSAPI_DBG_PRINT1("Commit demotion: no auto %d\n", WStatus);
|
||
}
|
||
|
||
//
|
||
// Insure access to the netlogon\parameters key
|
||
//
|
||
NTFRSAPI_DBG_PRINT0("Commit Demotion: Netlogon registry\n");
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
NULL,
|
||
NETLOGON_SECTION,
|
||
KEY_ALL_ACCESS,
|
||
&HNetKey);
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
//
|
||
// Tell NetLogon to stop sharing the sysvol
|
||
//
|
||
SysvolReady = 0;
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
NULL,
|
||
NETLOGON_SECTION,
|
||
HNetKey,
|
||
SYSVOL_READY,
|
||
REG_DWORD,
|
||
(PCHAR)&SysvolReady,
|
||
sizeof(DWORD));
|
||
}
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
//
|
||
// Delete our DS objects in some other DS. We cannot delete
|
||
// the objects from the DS on this DC because this DS is
|
||
// going away. We cannot delete the objects in another DS
|
||
// after rebooting because, as a member server, we no longer
|
||
// have permissions to delete our objects.
|
||
//
|
||
// The service will, however, continue to retry the deletes
|
||
// just in case this computer comes up as a DC after the
|
||
// demotion completed. BSTS.
|
||
//
|
||
DsDeleteCommit();
|
||
|
||
//
|
||
// Success
|
||
//
|
||
NtFrsApi_State = NTFRSAPI_COMMITTED;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (ServiceHandle) {
|
||
CloseServiceHandle(ServiceHandle);
|
||
}
|
||
if (HANDLE_IS_VALID(HKey)) {
|
||
RegCloseKey(HKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HNetKey)) {
|
||
RegCloseKey(HNetKey);
|
||
}
|
||
if (RpcHandle) {
|
||
WStatus1 = RpcBindingFree(&RpcHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
done:;
|
||
} finally {
|
||
//
|
||
// Release locks
|
||
//
|
||
LeaveCriticalSection(&NtFrsApi_GlobalLock);
|
||
if (AbnormalTermination()) {
|
||
WStatus = FRS_ERR_INTERNAL_API;
|
||
}
|
||
}
|
||
NTFRSAPI_DBG_PRINT1("Commit demotion done: %d\n", WStatus);
|
||
NTFRSAPI_DBG_UNPREPARE();
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_Set_DsPollingIntervalW(
|
||
IN PWCHAR ComputerName, OPTIONAL
|
||
IN ULONG UseShortInterval,
|
||
IN ULONG LongInterval,
|
||
IN ULONG ShortInterval
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service polls the DS occasionally for configuration changes.
|
||
This API alters the polling interval and, if the service is not
|
||
in the middle of a polling cycle, forces the service to begin a
|
||
polling cycle.
|
||
|
||
The service uses the long interval by default. The short interval
|
||
is used after the ds configuration has been successfully
|
||
retrieved and the service is now verifying that the configuration
|
||
is not in flux. This API can be used to force the service to use
|
||
the short interval until a stable configuration has been retrieved.
|
||
After which, the service reverts back to the long interval.
|
||
|
||
The default values for ShortInterval and LongInterval can be
|
||
changed by setting the parameters to a non-zero value. If zero,
|
||
the current values remain unchanged and a polling cycle is initiated.
|
||
|
||
Arguments:
|
||
ComputerName - Poke the service on this computer. The computer
|
||
name can be any RPC-bindable name. Usually, the
|
||
NetBIOS or DNS name works just fine. The NetBIOS
|
||
name can be found with GetComputerName() or
|
||
hostname. The DNS name can be found with
|
||
gethostbyname() or ipconfig /all. If NULL, the
|
||
service on this computer is contacted. The service
|
||
is contacted using Secure RPC.
|
||
|
||
UseShortInterval - If non-zero, the service switches to the short
|
||
interval until a stable configuration is retrieved
|
||
from the DS or another call to this API is made.
|
||
Otherwise, the service uses the long interval.
|
||
|
||
LongInterval - Minutes between polls of the DS. The value must fall
|
||
between NTFRSAPI_MIN_INTERVAL and NTFRSAPI_MAX_INTERVAL,
|
||
inclusive. If 0, the interval is unchanged.
|
||
|
||
ShortInterval - Minutes between polls of the DS. The value must fall
|
||
between NTFRSAPI_MIN_INTERVAL and NTFRSAPI_MAX_INTERVAL,
|
||
inclusive. If 0, the interval is unchanged.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
DWORD WStatus, WStatus1;
|
||
DWORD AuthWStatus;
|
||
DWORD NoAuthWStatus;
|
||
handle_t AuthHandle = NULL;
|
||
handle_t NoAuthHandle = NULL;
|
||
|
||
try {
|
||
//
|
||
// Check LongInterval
|
||
//
|
||
if (LongInterval &&
|
||
(LongInterval < NTFRSAPI_MIN_INTERVAL ||
|
||
LongInterval > NTFRSAPI_MAX_INTERVAL)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Check ShortInterval
|
||
//
|
||
if (ShortInterval &&
|
||
(ShortInterval < NTFRSAPI_MIN_INTERVAL ||
|
||
ShortInterval > NTFRSAPI_MAX_INTERVAL)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Bind to the service with and without authentication
|
||
//
|
||
AuthWStatus = NtFrsApi_BindWithAuth(ComputerName, NULL, &AuthHandle);
|
||
NoAuthWStatus = NtFrsApi_Bind(ComputerName, NULL, &NoAuthHandle);
|
||
|
||
//
|
||
// Send Authenticated RPC request to service
|
||
//
|
||
if (HANDLE_IS_VALID(AuthHandle)) {
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_Set_DsPollingIntervalW(AuthHandle,
|
||
UseShortInterval,
|
||
LongInterval,
|
||
ShortInterval);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} else {
|
||
WStatus = ERROR_ACCESS_DENIED;
|
||
}
|
||
if (WStatus == ERROR_ACCESS_DENIED) {
|
||
//
|
||
// Send Unauthenticated RPC request to service
|
||
//
|
||
if (HANDLE_IS_VALID(NoAuthHandle)) {
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_Set_DsPollingIntervalW(NoAuthHandle,
|
||
UseShortInterval,
|
||
LongInterval,
|
||
ShortInterval);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} else {
|
||
WStatus = NoAuthWStatus;
|
||
}
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
goto CLEANUP;
|
||
}
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
//
|
||
// Unbind
|
||
//
|
||
if (AuthHandle) {
|
||
WStatus1 = RpcBindingFree(&AuthHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
if (NoAuthHandle) {
|
||
WStatus1 = RpcBindingFree(&NoAuthHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_Get_DsPollingIntervalW(
|
||
IN PWCHAR ComputerName, OPTIONAL
|
||
OUT ULONG *Interval,
|
||
OUT ULONG *LongInterval,
|
||
OUT ULONG *ShortInterval
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The NtFrs service polls the DS occasionally for configuration changes.
|
||
This API returns the values the service uses for polling intervals.
|
||
|
||
The service uses the long interval by default. The short interval
|
||
is used after the ds configuration has been successfully
|
||
retrieved and the service is now verifying that the configuration
|
||
is not in flux. The short interval is also used if the
|
||
NtFrsApi_Set_DsPollingIntervalW() is used to force usage of the short
|
||
interval until a stable configuration has been retrieved. After which,
|
||
the service reverts back to the long interval.
|
||
|
||
The value returned in Interval is the polling interval currently in
|
||
use.
|
||
|
||
Arguments:
|
||
ComputerName - Poke the service on this computer. The computer
|
||
name can be any RPC-bindable name. Usually, the
|
||
NetBIOS or DNS name works just fine. The NetBIOS
|
||
name can be found with GetComputerName() or
|
||
hostname. The DNS name can be found with
|
||
gethostbyname() or ipconfig /all. If NULL, the
|
||
service on this computer is contacted. The service
|
||
is contacted using Secure RPC.
|
||
|
||
Interval - The current polling interval in minutes.
|
||
|
||
LongInterval - The long interval in minutes.
|
||
|
||
ShortInterval - The short interval in minutes.
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_Get_DsPollingIntervalW:"
|
||
DWORD WStatus, WStatus1;
|
||
DWORD NoAuthWStatus;
|
||
DWORD AuthWStatus;
|
||
handle_t AuthHandle = NULL;
|
||
handle_t NoAuthHandle = NULL;
|
||
|
||
try {
|
||
//
|
||
// Bind to the service with and without authentication
|
||
//
|
||
AuthWStatus = NtFrsApi_BindWithAuth(ComputerName, NULL, &AuthHandle);
|
||
NoAuthWStatus = NtFrsApi_Bind(ComputerName, NULL, &NoAuthHandle);
|
||
|
||
//
|
||
// Send Authenticated RPC request to service
|
||
//
|
||
if (HANDLE_IS_VALID(AuthHandle)) {
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_Get_DsPollingIntervalW(AuthHandle,
|
||
Interval,
|
||
LongInterval,
|
||
ShortInterval);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} else {
|
||
WStatus = ERROR_ACCESS_DENIED;
|
||
}
|
||
if (WStatus == ERROR_ACCESS_DENIED || WStatus == RPC_S_CALL_FAILED_DNE) {
|
||
//
|
||
// Send Unauthenticated RPC request to service
|
||
//
|
||
if (HANDLE_IS_VALID(NoAuthHandle)) {
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_Get_DsPollingIntervalW(NoAuthHandle,
|
||
Interval,
|
||
LongInterval,
|
||
ShortInterval);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} else {
|
||
WStatus = NoAuthWStatus;
|
||
}
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApi_Fix_Comm_WStatus(WStatus);
|
||
goto CLEANUP;
|
||
}
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
//
|
||
// Unbind
|
||
//
|
||
if (AuthHandle) {
|
||
WStatus1 = RpcBindingFree(&AuthHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
if (NoAuthHandle) {
|
||
WStatus1 = RpcBindingFree(&NoAuthHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_InfoW(
|
||
IN PWCHAR ComputerName, OPTIONAL
|
||
IN ULONG TypeOfInfo,
|
||
IN ULONG SizeInChars,
|
||
IN OUT PVOID *NtFrsApiInfo
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Return a buffer full of the requested information. The information
|
||
can be extracted from the buffer with NtFrsApi_InfoLineW().
|
||
|
||
*NtFrsApiInfo should be NULL on the first call. On subsequent calls,
|
||
*NtFrsApiInfo will be filled in with more data if any is present.
|
||
Otherwise, *NtFrsApiInfo is set to NULL and the memory is freed.
|
||
|
||
The SizeInChars is a suggested size; the actual memory usage
|
||
may be different. The function chooses the memory usage if
|
||
SizeInChars is 0.
|
||
|
||
The format of the returned information can change without notice.
|
||
|
||
Arguments:
|
||
ComputerName - Poke the service on this computer. The computer
|
||
name can be any RPC-bindable name. Usually, the
|
||
NetBIOS or DNS name works just fine. The NetBIOS
|
||
name can be found with GetComputerName() or
|
||
hostname. The DNS name can be found with
|
||
gethostbyname() or ipconfig /all. If NULL, the
|
||
service on this computer is contacted. The service
|
||
is contacted using Secure RPC.
|
||
|
||
TypeOfInfo - See the constants beginning with NTFRSAPI_INFO_
|
||
in ntfrsapi.h.
|
||
|
||
SizeInChars - Suggested memory usage; actual may be different.
|
||
0 == Function chooses memory usage
|
||
|
||
NtFrsApiInfo - Opaque. Parse with NtFrsApi_InfoLineW().
|
||
Free with NtFrsApi_InfoFreeW();
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_InfoW:"
|
||
DWORD WStatus, WStatus1;
|
||
DWORD NoAuthWStatus;
|
||
DWORD AuthWStatus;
|
||
PNTFRSAPI_INFO Info = NULL;
|
||
handle_t AuthHandle = NULL;
|
||
handle_t NoAuthHandle = NULL;
|
||
|
||
try {
|
||
//
|
||
// Adjust memory usage
|
||
//
|
||
if (!SizeInChars) {
|
||
SizeInChars = NTFRSAPI_DEFAULT_INFO_SIZE;
|
||
} else if (SizeInChars < NTFRSAPI_MINIMUM_INFO_SIZE) {
|
||
SizeInChars = NTFRSAPI_MINIMUM_INFO_SIZE;
|
||
}
|
||
|
||
//
|
||
// Check params
|
||
//
|
||
Info = *NtFrsApiInfo;
|
||
if (Info) {
|
||
TypeOfInfo = Info->TypeOfInfo;
|
||
}
|
||
|
||
//
|
||
// Allocate a large text buffer
|
||
//
|
||
if (Info) {
|
||
if (!NtFrsApi_InfoMoreW(Info)) {
|
||
NtFrsApi_InfoFreeW(NtFrsApiInfo);
|
||
WStatus = ERROR_SUCCESS;
|
||
goto CLEANUP;
|
||
}
|
||
Info->CharsToSkip = Info->TotalChars;
|
||
Info->Flags = 0;
|
||
Info->OffsetToFree = (ULONG)(Info->Lines - (PCHAR)Info);
|
||
Info->OffsetToLines = Info->OffsetToFree;
|
||
} else {
|
||
Info = NtFrsApi_Alloc(SizeInChars);
|
||
Info->Major = NTFRS_MAJOR;
|
||
Info->Minor = NTFRS_MINOR;
|
||
Info->SizeInChars = SizeInChars;
|
||
Info->OffsetToFree = (ULONG)(Info->Lines - (PCHAR)Info);
|
||
Info->OffsetToLines = Info->OffsetToFree;
|
||
Info->TypeOfInfo = TypeOfInfo;
|
||
*NtFrsApiInfo = Info;
|
||
}
|
||
|
||
//
|
||
// Caller only wants info on the api; deliver it
|
||
//
|
||
if (TypeOfInfo == NTFRSAPI_INFO_TYPE_VERSION) {
|
||
NTFRSAPI_IPRINT0(Info, "NtFrsApi Version Information\n");
|
||
NTFRSAPI_IPRINT1(Info, " NtFrsApi Major : %d\n", NTFRS_MAJOR);
|
||
NTFRSAPI_IPRINT1(Info, " NtFrsApi Minor : %d\n", NTFRS_MINOR);
|
||
// NTFRSAPI_IPRINT1(Info, " NtFrsApi Module : %hs\n", NtFrsApi_Module);
|
||
NTFRSAPI_IPRINT2(Info, " NtFrsApi Compiled on: %s %s\n",
|
||
NtFrsApi_Date, NtFrsApi_Time);
|
||
#if NTFRS_TEST
|
||
NTFRSAPI_IPRINT0(Info, " NTFRS_TEST Enabled\n");
|
||
#else NTFRS_TEST
|
||
NTFRSAPI_IPRINT0(Info, " NTFRS_TEST Disabled\n");
|
||
#endif NTFRS_TEST
|
||
|
||
}
|
||
|
||
//
|
||
// Bind to the service with and without authentication
|
||
//
|
||
AuthWStatus = NtFrsApi_BindWithAuth(ComputerName, NULL, &AuthHandle);
|
||
if (!WIN_SUCCESS(AuthWStatus)) {
|
||
NTFRSAPI_IPRINT3(Info, "ERROR - Cannot bind w/authentication to computer, %ws; %08x (%d)\n",
|
||
ComputerName, AuthWStatus, AuthWStatus);
|
||
}
|
||
NoAuthWStatus = NtFrsApi_Bind(ComputerName, NULL, &NoAuthHandle);
|
||
if (!WIN_SUCCESS(NoAuthWStatus)) {
|
||
NTFRSAPI_IPRINT3(Info, "ERROR - Cannot bind w/o authentication to computer, %ws; %08x (%d)\n",
|
||
ComputerName, NoAuthWStatus, NoAuthWStatus);
|
||
}
|
||
|
||
//
|
||
// Send Authenticated RPC request to service
|
||
//
|
||
if (HANDLE_IS_VALID(AuthHandle)) {
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_InfoW(AuthHandle,
|
||
Info->SizeInChars,
|
||
(PBYTE)Info);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} else {
|
||
WStatus = ERROR_ACCESS_DENIED;
|
||
}
|
||
if (WStatus == ERROR_ACCESS_DENIED ||
|
||
WStatus == RPC_S_CALL_FAILED_DNE) {
|
||
//
|
||
// Send Unauthenticated RPC request to service
|
||
//
|
||
if (HANDLE_IS_VALID(NoAuthHandle)) {
|
||
try {
|
||
WStatus = NtFrsApi_Rpc_InfoW(NoAuthHandle,
|
||
Info->SizeInChars,
|
||
(PBYTE)Info);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
} else {
|
||
WStatus = NoAuthWStatus;
|
||
}
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NTFRSAPI_IPRINT3(Info, "ERROR - Cannot RPC to computer, %ws; %08x (%d)\n",
|
||
ComputerName, WStatus, WStatus);
|
||
WStatus = ERROR_SUCCESS;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
WStatus = ERROR_SUCCESS;
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
//
|
||
// Unbind
|
||
//
|
||
if (AuthHandle) {
|
||
WStatus1 = RpcBindingFree(&AuthHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
if (NoAuthHandle) {
|
||
WStatus1 = RpcBindingFree(&NoAuthHandle);
|
||
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
FREE(*NtFrsApiInfo);
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_InfoLineW(
|
||
IN PNTFRSAPI_INFO NtFrsApiInfo,
|
||
IN OUT PVOID *InOutLine
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Extract the wchar lines of information from NtFrsApiInformation.
|
||
|
||
Returns the address of the next L'\0' terminated line of information.
|
||
NULL if none.
|
||
|
||
Arguments:
|
||
NtFrsApiInfo - Opaque. Returned by NtFrsApi_InfoW().
|
||
Parse with NtFrsApi_InfoLineW().
|
||
Free with NtFrsApi_InfoFreeW().
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_InfoLineW:"
|
||
DWORD WStatus;
|
||
PCHAR NextLine;
|
||
PCHAR FirstLine;
|
||
PCHAR FreeLine;
|
||
|
||
try {
|
||
//
|
||
// Check input params
|
||
//
|
||
if (!InOutLine) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
if (!NtFrsApiInfo) {
|
||
*InOutLine = NULL;
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
NextLine = *InOutLine;
|
||
FirstLine = ((PCHAR)NtFrsApiInfo) + NtFrsApiInfo->OffsetToLines;
|
||
FreeLine = ((PCHAR)NtFrsApiInfo) + NtFrsApiInfo->OffsetToFree;
|
||
|
||
if (!NextLine) {
|
||
NextLine = FirstLine;
|
||
} else {
|
||
if (NextLine < FirstLine) {
|
||
*InOutLine = NULL;
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
if (NextLine < FreeLine) {
|
||
NextLine += strlen(NextLine) + 1;
|
||
}
|
||
}
|
||
if (NextLine >= FreeLine) {
|
||
*InOutLine = NULL;
|
||
} else {
|
||
*InOutLine = NextLine;
|
||
}
|
||
WStatus = ERROR_SUCCESS;
|
||
cleanup:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_InfoFreeW(
|
||
IN PVOID *NtFrsApiInfo
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Free the information buffer allocated by NtFrsApi_InfoW();
|
||
|
||
Arguments:
|
||
NtFrsApiInfo - Opaque. Returned by NtFrsApi_InfoW().
|
||
Parse with NtFrsApi_InfoLineW().
|
||
Free with NtFrsApi_InfoFreeW().
|
||
|
||
Return Value:
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_InfoFreeW:"
|
||
DWORD WStatus;
|
||
|
||
try {
|
||
FREE(*NtFrsApiInfo);
|
||
WStatus = ERROR_SUCCESS;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
BOOL
|
||
WINAPI
|
||
NtFrsApi_InfoMoreW(
|
||
IN PNTFRSAPI_INFO NtFrsApiInfo
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
All of the information may not have fit in the buffer. The additional
|
||
information can be fetched by calling NtFrsApi_InfoW() again with the
|
||
same NtFrsApiInfo struct. NtFrsApi_InfoW() will return NULL in
|
||
NtFrsApiInfo if there is no more information.
|
||
|
||
However, the information returned in subsequent calls to _InfoW() may be
|
||
out of sync with the previous information. If the user requires a
|
||
coherent information set, then the information buffer should be freed
|
||
with NtFrsApi_InfoFreeW() and another call made to NtFrsApi_InfoW()
|
||
with an increased SizeInChars. Repeat the procedure until
|
||
NtFrsApi_InfoMoreW() returns FALSE.
|
||
|
||
Arguments:
|
||
NtFrsApiInfo - Opaque. Returned by NtFrsApi_InfoW().
|
||
Parse with NtFrsApi_InfoLineW().
|
||
Free with NtFrsApi_InfoFreeW().
|
||
|
||
Return Value:
|
||
TRUE - The information buffer does *NOT* contain all of the info.
|
||
FALSE - The information buffer does contain all of the info.
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_InfoMoreW:"
|
||
DWORD WStatus;
|
||
BOOL BStatus = FALSE;
|
||
|
||
try {
|
||
//
|
||
// If we have an info buffer
|
||
// and the info buffer is full
|
||
// and there was at least one line in the info buffer
|
||
// then there is more info.
|
||
//
|
||
if (NtFrsApiInfo &&
|
||
FlagOn(NtFrsApiInfo->Flags, NTFRSAPI_INFO_FLAGS_FULL) &&
|
||
NtFrsApiInfo->OffsetToLines != NtFrsApiInfo->OffsetToFree) {
|
||
BStatus = TRUE;
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return BStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiStopServiceForRestore(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN DWORD BurFlags
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Old code left over from Version 1.0 of the Backup/restore api.
|
||
Used as subroutine for Version 3.0.
|
||
|
||
Stop the service and update the registry.
|
||
|
||
Arguments:
|
||
|
||
ErrorCallBack - Ignored if NULL.
|
||
Address of function provided by the caller. If
|
||
not NULL, this function calls back with a formatted
|
||
error message and the error code that caused the
|
||
error.
|
||
|
||
BurFlags - Callers Backup/Restore flags
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiStopServiceForRestore:"
|
||
DWORD WStatus;
|
||
DWORD SizeInChars;
|
||
DWORD CharsNeeded;
|
||
DWORD Hint;
|
||
BOOL IsAutoStart;
|
||
BOOL IsRunning;
|
||
DWORD BurStopDisposition;
|
||
PWCHAR ObjectName = NULL;
|
||
HKEY HBurMvKey = 0;
|
||
HKEY HBurStopKey = 0;
|
||
SERVICE_STATUS ServiceStatus;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
QUERY_SERVICE_CONFIG *ServiceConfig = NULL;
|
||
|
||
try {
|
||
//
|
||
// STOP THE SERVICE
|
||
//
|
||
//
|
||
// Open the service
|
||
//
|
||
WStatus = NtFrsApi_GetServiceHandle(ErrorCallBack, &ServiceHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Get Service config
|
||
//
|
||
SizeInChars = 1024;
|
||
QUERY_SERVICE_AGAIN:
|
||
ServiceConfig = NtFrsApi_Alloc(SizeInChars);
|
||
if (!QueryServiceConfig(ServiceHandle, ServiceConfig, SizeInChars, &CharsNeeded)) {
|
||
WStatus = GetLastError();
|
||
|
||
if (WIN_BUF_TOO_SMALL(WStatus) && CharsNeeded > SizeInChars) {
|
||
SizeInChars = CharsNeeded;
|
||
FREE(ServiceConfig);
|
||
goto QUERY_SERVICE_AGAIN;
|
||
}
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
IsAutoStart = (ServiceConfig->dwStartType == SERVICE_AUTO_START);
|
||
|
||
//
|
||
// Get Service status
|
||
//
|
||
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
IsRunning = (ServiceStatus.dwCurrentState == SERVICE_RUNNING);
|
||
Hint = ServiceStatus.dwWaitHint;
|
||
|
||
//
|
||
// Stop the service
|
||
//
|
||
if (IsRunning) {
|
||
if (!ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus)) {
|
||
|
||
WStatus = GetLastError();
|
||
|
||
if (WStatus == ERROR_SERVICE_REQUEST_TIMEOUT) {
|
||
WStatus = ERROR_SUCCESS;
|
||
if (!ControlService(ServiceHandle,
|
||
SERVICE_CONTROL_STOP,
|
||
&ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
if (WStatus == ERROR_SERVICE_NOT_ACTIVE) {
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
WStatus = NtFrsApi_WaitForService(ServiceHandle,
|
||
Hint,
|
||
SERVICE_STOP_PENDING,
|
||
SERVICE_STOPPED);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = FRS_ERR_STOPPING_SERVICE;
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update the registry
|
||
//
|
||
|
||
//
|
||
// Open the ntfrs parameters\backup/restore\Startup section
|
||
//
|
||
WStatus = RegCreateKey(HKEY_LOCAL_MACHINE,
|
||
FRS_BACKUP_RESTORE_MV_SECTION,
|
||
&HBurMvKey);
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
RegCloseKey(HBurMvKey);
|
||
HBurMvKey = 0;
|
||
}
|
||
//
|
||
// Re-open using reduced access
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_BACKUP_RESTORE_MV_SECTION,
|
||
KEY_SET_VALUE,
|
||
&HBurMvKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_BACKUP_RESTORE_MV_SECTION,
|
||
HBurMvKey,
|
||
FRS_VALUE_BURFLAGS,
|
||
REG_DWORD,
|
||
(PCHAR)&BurFlags,
|
||
sizeof(DWORD));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Create the volatile key to prevent the service from starting
|
||
//
|
||
WStatus = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
||
FRS_BACKUP_RESTORE_STOP_SECTION,
|
||
0,
|
||
NULL,
|
||
REG_OPTION_VOLATILE,
|
||
KEY_ALL_ACCESS,
|
||
NULL,
|
||
&HBurStopKey,
|
||
&BurStopDisposition);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, FRS_BACKUP_RESTORE_STOP_SECTION, WStatus);
|
||
//
|
||
// Ignore errors
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (ServiceHandle) {
|
||
CloseServiceHandle(ServiceHandle);
|
||
}
|
||
if (HANDLE_IS_VALID(HBurMvKey)) {
|
||
RegCloseKey(HBurMvKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HBurStopKey)) {
|
||
RegCloseKey(HBurStopKey);
|
||
}
|
||
FREE(ServiceConfig);
|
||
FREE(ObjectName);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiStartServiceAfterRestore(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Old code from Version 1.0 of the Backup/Restore API. Used
|
||
as subroutine in Version 3.0.
|
||
|
||
Restart the service after a restore has completed.
|
||
|
||
Arguments:
|
||
|
||
ErrorCallBack - Ignored if NULL.
|
||
Address of function provided by the caller. If
|
||
not NULL, this function calls back with a formatted
|
||
error message and the error code that caused the
|
||
error.
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiStartServiceAfterRestore:"
|
||
DWORD WStatus;
|
||
DWORD SizeInChars;
|
||
DWORD CharsNeeded;
|
||
DWORD Hint;
|
||
BOOL IsAutoStart;
|
||
SERVICE_STATUS ServiceStatus;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
QUERY_SERVICE_CONFIG *ServiceConfig = NULL;
|
||
|
||
try {
|
||
//
|
||
// Delete the volatile key that prevents the service from starting
|
||
//
|
||
WStatus = NtFrsApiDeleteKey(NTFRSAPI_MODULE,
|
||
NULL,
|
||
NULL,
|
||
HKEY_LOCAL_MACHINE,
|
||
FRS_BACKUP_RESTORE_STOP_SECTION);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
//
|
||
// Ignore errors
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// START THE SERVICE
|
||
//
|
||
//
|
||
// Open the service
|
||
//
|
||
WStatus = NtFrsApi_GetServiceHandle(ErrorCallBack, &ServiceHandle);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Get Service config
|
||
//
|
||
SizeInChars = 1024;
|
||
QUERY_SERVICE_AGAIN:
|
||
ServiceConfig = NtFrsApi_Alloc(SizeInChars);
|
||
if (!QueryServiceConfig(ServiceHandle, ServiceConfig, SizeInChars, &CharsNeeded)) {
|
||
WStatus = GetLastError();
|
||
if (WIN_BUF_TOO_SMALL(WStatus) && CharsNeeded > SizeInChars) {
|
||
SizeInChars = CharsNeeded;
|
||
FREE(ServiceConfig);
|
||
goto QUERY_SERVICE_AGAIN;
|
||
}
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
IsAutoStart = (ServiceConfig->dwStartType == SERVICE_AUTO_START);
|
||
|
||
//
|
||
// Get Service status
|
||
//
|
||
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
||
WStatus = GetLastError();
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
Hint = ServiceStatus.dwWaitHint;
|
||
|
||
//
|
||
// Restart the service
|
||
//
|
||
if (IsAutoStart) {
|
||
if (!StartService(ServiceHandle, 0, NULL)) {
|
||
WStatus = GetLastError();
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
|
||
WStatus = NtFrsApi_WaitForService(ServiceHandle,
|
||
Hint,
|
||
SERVICE_START_PENDING,
|
||
SERVICE_RUNNING);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = FRS_ERR_STARTING_SERVICE;
|
||
CLEANUP_CB(ErrorCallBack, SERVICE_NAME, WStatus, CLEANUP);
|
||
}
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (ServiceHandle) {
|
||
CloseServiceHandle(ServiceHandle);
|
||
}
|
||
FREE(ServiceConfig);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiMoveCumulativeSets(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG) OPTIONAL
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Move the cumulative replica sets currently in the registry into the
|
||
backup/restore key that will (might) be moved into the restored
|
||
registry at the end of restore. This is to maintain as much state
|
||
as possible about un-deleted replica sets. An old registry may
|
||
lack info about new sets that will appear once the current restored
|
||
DS is updated from its partners.
|
||
|
||
Arguments:
|
||
ErrorCallBack - Ignored if NULL. Otherwise, call on error.
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiMoveCumulativeSets:"
|
||
DWORD WStatus;
|
||
DWORD KeyIdx;
|
||
DWORD RegType;
|
||
DWORD RegBytes;
|
||
PWCHAR CumuPath = NULL;
|
||
PWCHAR BurCumuPath = NULL;
|
||
PWCHAR ObjectName = NULL;
|
||
HKEY HCumusKey = 0;
|
||
HKEY HCumuKey = 0;
|
||
HKEY HBurCumusKey = 0;
|
||
HKEY HBurCumuKey = 0;
|
||
WCHAR RegBuf[MAX_PATH + 1];
|
||
|
||
//
|
||
// Open CUMULATIVE REPLICA SETS
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_CUMULATIVE_SETS_SECTION,
|
||
KEY_ENUMERATE_SUB_KEYS,
|
||
&HCumusKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Open BACKUP RESTORE CUMULATIVE REPLICA SETS
|
||
//
|
||
WStatus = RegCreateKey(HKEY_LOCAL_MACHINE,
|
||
FRS_BACKUP_RESTORE_MV_CUMULATIVE_SETS_SECTION,
|
||
&HBurCumusKey);
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
RegCloseKey(HBurCumusKey);
|
||
HBurCumusKey = 0;
|
||
}
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_BACKUP_RESTORE_MV_CUMULATIVE_SETS_SECTION,
|
||
KEY_CREATE_SUB_KEY,
|
||
&HBurCumusKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
// Enumerate the Cumulative Replica Sets
|
||
//
|
||
KeyIdx = 0;
|
||
do {
|
||
WStatus = RegEnumKey(HCumusKey, KeyIdx, RegBuf, MAX_PATH + 1);
|
||
if (WStatus == ERROR_NO_MORE_ITEMS) {
|
||
break;
|
||
}
|
||
CLEANUP_CB(ErrorCallBack, FRS_CUMULATIVE_SETS_SECTION, WStatus, CLEANUP);
|
||
|
||
//
|
||
// Full path of both the source and target key
|
||
//
|
||
CumuPath = NtFrsApi_Cats(FRS_CUMULATIVE_SETS_SECTION, L"\\", RegBuf);
|
||
BurCumuPath = NtFrsApi_Cats(FRS_BACKUP_RESTORE_MV_CUMULATIVE_SETS_SECTION,
|
||
L"\\",
|
||
RegBuf);
|
||
//
|
||
// Open the cumulative replica set
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
CumuPath,
|
||
KEY_READ,
|
||
&HCumuKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP_DURING_LOOP;
|
||
}
|
||
//
|
||
// Create the backup restore cumulative replica set
|
||
//
|
||
WStatus = RegCreateKey(HKEY_LOCAL_MACHINE, BurCumuPath, &HBurCumuKey);
|
||
CLEANUP_CB(ErrorCallBack, BurCumuPath, WStatus, CLEANUP_DURING_LOOP);
|
||
|
||
CLEANUP_DURING_LOOP:
|
||
if (HANDLE_IS_VALID(HCumuKey)) {
|
||
RegCloseKey(HCumuKey);
|
||
HCumuKey = 0;
|
||
}
|
||
if (HANDLE_IS_VALID(HBurCumuKey)) {
|
||
RegCloseKey(HBurCumuKey);
|
||
HBurCumuKey = 0;
|
||
}
|
||
FREE(CumuPath);
|
||
FREE(BurCumuPath);
|
||
FREE(ObjectName);
|
||
++KeyIdx;
|
||
} while (TRUE);
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
if (HANDLE_IS_VALID(HCumuKey)) {
|
||
RegCloseKey(HCumuKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HBurCumuKey)) {
|
||
RegCloseKey(HBurCumuKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HCumusKey)) {
|
||
RegCloseKey(HCumusKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HBurCumusKey)) {
|
||
RegCloseKey(HBurCumusKey);
|
||
}
|
||
FREE(CumuPath);
|
||
FREE(BurCumuPath);
|
||
FREE(ObjectName);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
typedef struct _NTFRSAPI_BUR_SET NTFRSAPI_BUR_SET, *PNTFRSAPI_BUR_SET;
|
||
struct _NTFRSAPI_BUR_SET {
|
||
PNTFRSAPI_BUR_SET BurSetNext; // next in list of sets
|
||
PWCHAR BurSetGuid; // member guid is also name of registry key
|
||
PWCHAR BurSetRoot; // root path
|
||
PWCHAR BurSetStage; // stage path
|
||
PWCHAR BurSetType; // type of set (domain, enterprise, ...)
|
||
};
|
||
|
||
//
|
||
// Context generated by NtFrsApiInitializeBackupRestore() and freed by
|
||
// NtFrsApiDestroyBackupRestore(). Used for all other function calls.
|
||
//
|
||
typedef struct _NTFRSAPI_BUR_CONTEXT {
|
||
DWORD BurFlags; // from caller
|
||
PNTFRSAPI_BUR_SET BurSets; // See NtFrsApiGetBackupRestoreSets
|
||
DWORD BurFiltersSizeInBytes; // Size of BurFilters
|
||
PWCHAR BurFilters; // From registry
|
||
BOOL HaveBurSemaphore; // Holding the semaphore
|
||
HANDLE BurSemaphore; // This is a single thread API
|
||
DWORD (*ErrorCallBack)(IN PWCHAR, IN ULONG); // from caller
|
||
} NTFRSAPI_BUR_CONTEXT, *PNTFRSAPI_BUR_CONTEXT;
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
NtFrsApiFreeBurSets(
|
||
IN PNTFRSAPI_BUR_SET *BurSets
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Free the linked list of BurSets and set *BurSets to NULL.
|
||
|
||
Arguments:
|
||
BurSets - Linked list of BurSets
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiFreeBurSets:"
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
|
||
while (LocalBurSet = *BurSets) {
|
||
*BurSets = LocalBurSet->BurSetNext;
|
||
FREE(LocalBurSet->BurSetGuid);
|
||
FREE(LocalBurSet->BurSetRoot);
|
||
FREE(LocalBurSet->BurSetStage);
|
||
FREE(LocalBurSet->BurSetType);
|
||
FREE(LocalBurSet);
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiInitializeBackupRestore(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
IN DWORD BurFlags,
|
||
OUT PVOID *BurContext
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Called once in the lifetime of a backup/restore process. Must be
|
||
matched with a subsequent call to NtFrsApiDestroyBackupRestore().
|
||
|
||
Prepare the system for the backup or restore specified by BurFlags.
|
||
Currently, the following combinations are supported:
|
||
ASR - Automated System Recovery
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_SYSTEM |
|
||
NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES |
|
||
NTFRSAPI_BUR_FLAGS_PRIMARY or NTFRSAPI_BUR_FLAGS_NON_AUTHORITATIVE
|
||
|
||
DSR - Distributed Services Restore (all sets)
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY |
|
||
NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES |
|
||
NTFRSAPI_BUR_FLAGS_PRIMARY or NTFRSAPI_BUR_FLAGS_NON_AUTHORITATIVE
|
||
|
||
DSR - Distributed Services Restore (just the sysvol)
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY
|
||
(may be followed by subsequent calls to NtFrsApiRestoringDirectory())
|
||
|
||
Normal Restore - System is up and running; just restoring files
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_NORMAL |
|
||
NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES |
|
||
NTFRSAPI_BUR_FLAGS_AUTHORITATIVE
|
||
|
||
Normal Backup
|
||
NTFRSAPI_BUR_FLAGS_BACKUP |
|
||
NTFRSAPI_BUR_FLAGS_NORMAL
|
||
|
||
Arguments:
|
||
ErrorCallBack - Ignored if NULL.
|
||
Address of function provided by the caller. If
|
||
not NULL, this function calls back with a formatted
|
||
error message and the error code that caused the
|
||
error.
|
||
BurFlags - See above for the supported combinations
|
||
BurContext - Opaque context for this process
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiInitializeBackupRestore:"
|
||
DWORD WStatus;
|
||
DWORD WaitStatus;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = NULL;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Must be one of backup or restore
|
||
//
|
||
if (!(BurFlags & (NTFRSAPI_BUR_FLAGS_BACKUP |
|
||
NTFRSAPI_BUR_FLAGS_RESTORE))) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags ~(BACKUP|RESTORE)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// RESTORE
|
||
//
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_RESTORE) {
|
||
//
|
||
// Can't be both backup and restore
|
||
//
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_BACKUP) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags (RESTORE|BACKUP)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Restore supports a few flags
|
||
//
|
||
if (BurFlags & ~NTFRSAPI_BUR_FLAGS_SUPPORTED_RESTORE) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags ONE OR MORE FLAGS", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Select only one type of restore
|
||
//
|
||
switch (BurFlags & NTFRSAPI_BUR_FLAGS_TYPES_OF_RESTORE) {
|
||
//
|
||
// Authoritative
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_AUTHORITATIVE:
|
||
if (BurFlags & (NTFRSAPI_BUR_FLAGS_SYSTEM |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY)) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags (SYSTEM | ACTIVE | AUTHORITATIVE)", WStatus, CLEANUP);
|
||
}
|
||
break;
|
||
//
|
||
// Non-Authoritative
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_NON_AUTHORITATIVE:
|
||
//
|
||
// NORMAL
|
||
//
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_NORMAL) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags (NORMAL | NON-AUTHORITATIVE)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// _ACTIVE_DIRECTORY and not ALL
|
||
//
|
||
if ((BurFlags & NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY) &&
|
||
(!(BurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES))) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags (ACTIVE DIRECTORY | NON-AUTHORITATIVE w/o ALL)", WStatus, CLEANUP);
|
||
}
|
||
break;
|
||
//
|
||
// Primary
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_PRIMARY:
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_NORMAL) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags (NORMAL | PRIMARY)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// _ACTIVE_DIRECTORY and not ALL
|
||
//
|
||
if ((BurFlags & NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY) &&
|
||
(!(BurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES))) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags (ACTIVE DIRECTORY | PRIMARY w/o ALL)", WStatus, CLEANUP);
|
||
}
|
||
break;
|
||
//
|
||
// None
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_NONE:
|
||
if ((BurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES) ||
|
||
!(BurFlags & NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY)) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags TOO FEW TYPES", WStatus, CLEANUP);
|
||
}
|
||
break;
|
||
//
|
||
// More than one or none
|
||
//
|
||
default:
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags TOO MANY TYPES", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Select only one mode of restore
|
||
//
|
||
switch (BurFlags & NTFRSAPI_BUR_FLAGS_MODES_OF_RESTORE) {
|
||
//
|
||
// System
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_SYSTEM:
|
||
if (!(BurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES)) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags SYSTEM without ALL", WStatus, CLEANUP);
|
||
}
|
||
break;
|
||
//
|
||
// Active Directory
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY:
|
||
#if 0
|
||
if (!(BurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES)) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags ACTIVE DIRECTORY without ALL", WStatus, CLEANUP);
|
||
}
|
||
#endif 0
|
||
break;
|
||
//
|
||
// Normal
|
||
//
|
||
case NTFRSAPI_BUR_FLAGS_NORMAL:
|
||
if (!(BurFlags & NTFRSAPI_BUR_FLAGS_ALL_DIRECTORIES_AND_VOLUMES)) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags NORMAL without ALL", WStatus, CLEANUP);
|
||
}
|
||
break;
|
||
//
|
||
// More than one
|
||
//
|
||
default:
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags TOO MANY/FEW MODES", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// BACKUP
|
||
//
|
||
} else {
|
||
//
|
||
// Backup supports a subset of BurFlags
|
||
//
|
||
if (BurFlags & ~NTFRSAPI_BUR_FLAGS_SUPPORTED_BACKUP) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags unsupported BACKUP Flag(s)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Normal must be set
|
||
//
|
||
if (!(BurFlags & NTFRSAPI_BUR_FLAGS_NORMAL)) {
|
||
WStatus = ERROR_CALL_NOT_IMPLEMENTED;
|
||
CLEANUP_CB(ErrorCallBack, L"BurFlags BACKUP without NORMAL", WStatus, CLEANUP);
|
||
}
|
||
}
|
||
//
|
||
// Must have someplace to return the context
|
||
//
|
||
if (!BurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(ErrorCallBack, L"BurContext", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// No context, yet
|
||
//
|
||
*BurContext = NULL;
|
||
|
||
|
||
//
|
||
// Allocate a context
|
||
//
|
||
LocalBurContext = NtFrsApi_Alloc(sizeof(NTFRSAPI_BUR_CONTEXT));
|
||
LocalBurContext->ErrorCallBack = ErrorCallBack,
|
||
LocalBurContext->BurFlags = BurFlags;
|
||
|
||
//
|
||
// Only one backup/restore is allowed at a time
|
||
//
|
||
LocalBurContext->BurSemaphore = CreateSemaphore(NULL,
|
||
1,
|
||
1,
|
||
NTFRS_BACKUP_RESTORE_SEMAPHORE);
|
||
if (!HANDLE_IS_VALID(LocalBurContext->BurSemaphore)) {
|
||
LocalBurContext->BurSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS,
|
||
FALSE,
|
||
NTFRS_BACKUP_RESTORE_SEMAPHORE);
|
||
}
|
||
if (!HANDLE_IS_VALID(LocalBurContext->BurSemaphore)) {
|
||
WStatus = GetLastError();
|
||
CLEANUP_CB(ErrorCallBack, NTFRS_BACKUP_RESTORE_SEMAPHORE, WStatus, CLEANUP);
|
||
}
|
||
WaitStatus = WaitForSingleObject(LocalBurContext->BurSemaphore, 1 * 1000);
|
||
if (WaitStatus != WAIT_OBJECT_0) {
|
||
if (WaitStatus == WAIT_TIMEOUT) {
|
||
WStatus = ERROR_BUSY;
|
||
} else if (WaitStatus == WAIT_ABANDONED){
|
||
WStatus = ERROR_SEM_OWNER_DIED;
|
||
} else {
|
||
WStatus = GetLastError();
|
||
}
|
||
CLEANUP_CB(ErrorCallBack, NTFRS_BACKUP_RESTORE_SEMAPHORE, WStatus, CLEANUP);
|
||
}
|
||
LocalBurContext->HaveBurSemaphore = TRUE;
|
||
|
||
//
|
||
// Stop the service and set the appropriate registry value
|
||
//
|
||
// THE RESTORE IS EFFECTIVELY COMMITTED AT THIS TIME!
|
||
//
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_RESTORE &&
|
||
!(BurFlags & NTFRSAPI_BUR_FLAGS_NORMAL)) {
|
||
WStatus = NtFrsApiStopServiceForRestore(ErrorCallBack, BurFlags);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Save the current set of replica sets
|
||
//
|
||
WStatus = NtFrsApiMoveCumulativeSets(NULL);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
// Ignore error; caller may not care
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
*BurContext = LocalBurContext;
|
||
LocalBurContext = NULL;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
//
|
||
// Release semaphore
|
||
//
|
||
if (LocalBurContext && HANDLE_IS_VALID(LocalBurContext->BurSemaphore)) {
|
||
if (LocalBurContext->HaveBurSemaphore) {
|
||
ReleaseSemaphore(LocalBurContext->BurSemaphore, 1, NULL);
|
||
}
|
||
CloseHandle(LocalBurContext->BurSemaphore);
|
||
}
|
||
//
|
||
// Context
|
||
//
|
||
if (LocalBurContext) {
|
||
NtFrsApiFreeBurSets(&LocalBurContext->BurSets);
|
||
LocalBurContext->BurFiltersSizeInBytes = 0;
|
||
FREE(LocalBurContext->BurFilters);
|
||
FREE(LocalBurContext);
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiRestoringDirectory(
|
||
IN PVOID BurContext,
|
||
IN PVOID BurSet,
|
||
IN DWORD BurFlags
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
The backup/restore application is about to restore the directory
|
||
specified by BurSet (See NtFrsApiEnumBackupRestoreSets()). Matched
|
||
with a later call to NtFrsApiFinishedRestoringDirectory().
|
||
|
||
This call is supported only if NtFrsApiInitializeBackupRestore()
|
||
were called with the flags:
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY
|
||
|
||
BurFlags can be NTFRSAPI_BUR_FLAGS_PRIMARY or
|
||
NTFRSAPI_BUR_FLAGS_NON_AUTHORITATIVE and overrides any value
|
||
specified in the call to NtFrsApiInitializeBackupRestore().
|
||
|
||
Arguments:
|
||
BurContext - Opaque context from NtFrsApiInitializeBackupRestore()
|
||
BurSet - Opaque set from NtFrsApiEnumBackupRestoreSets();
|
||
BurFlags - See above for the supported combinations
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiRestoringDirectory:"
|
||
DWORD WStatus;
|
||
DWORD WaitStatus;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
HKEY HBurSetKey = 0;
|
||
PWCHAR BurSetPath = NULL;
|
||
PWCHAR ObjectName = NULL;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Must be one of primary or nonauth
|
||
//
|
||
if (!(BurFlags & (NTFRSAPI_BUR_FLAGS_PRIMARY |
|
||
NTFRSAPI_BUR_FLAGS_NON_AUTHORITATIVE))) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurFlags not (PRIMARY|NON-AUTH)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Can only be one of primary or nonauth
|
||
//
|
||
if (BurFlags & ~(NTFRSAPI_BUR_FLAGS_PRIMARY |
|
||
NTFRSAPI_BUR_FLAGS_NON_AUTHORITATIVE)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurFlags not just (PRIMARY|NON-AUTH)", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Must be a restore context
|
||
//
|
||
if (!(LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_RESTORE)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurContext not RESTORE", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Must be an active directory context
|
||
//
|
||
if (!(LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurContext not ACTIVE_DIRECTORY", WStatus, CLEANUP);
|
||
}
|
||
|
||
//
|
||
// Re-locate the correct BurSet
|
||
//
|
||
for (LocalBurSet = LocalBurContext->BurSets;
|
||
LocalBurSet && (LocalBurSet != BurSet);
|
||
LocalBurSet = LocalBurSet->BurSetNext) {
|
||
}
|
||
if (!LocalBurSet) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurSet", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Corrupted BurSet
|
||
//
|
||
if (!LocalBurSet->BurSetGuid) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurSet Id", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Full path to registry key
|
||
//
|
||
BurSetPath = NtFrsApi_Cats(FRS_BACKUP_RESTORE_MV_SETS_SECTION,
|
||
L"\\",
|
||
LocalBurSet->BurSetGuid);
|
||
WStatus = RegCreateKey(HKEY_LOCAL_MACHINE, BurSetPath, &HBurSetKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, BurSetPath, WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Retain _RESTORE and _ACTIVE_DIRECTORY in the registry value
|
||
//
|
||
BurFlags |= LocalBurContext->BurFlags &
|
||
(NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY);
|
||
WStatus = NtFrsApiSetValueEx(NTFRSAPI_MODULE,
|
||
LocalBurContext->ErrorCallBack,
|
||
BurSetPath,
|
||
HBurSetKey,
|
||
FRS_VALUE_BURFLAGS,
|
||
REG_DWORD,
|
||
(PCHAR)&BurFlags,
|
||
sizeof(DWORD));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
if (HANDLE_IS_VALID(HBurSetKey)) {
|
||
RegCloseKey(HBurSetKey);
|
||
}
|
||
FREE(BurSetPath);
|
||
FREE(ObjectName);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiFinishedRestoringDirectory(
|
||
IN PVOID BurContext,
|
||
IN PVOID BurSet,
|
||
IN DWORD BurFlags
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Finished restoring directory for BurSet. Matched by a previous call
|
||
to NtFrsApiRestoringDirectory().
|
||
|
||
This call is supported only if NtFrsApiInitializeBackupRestore()
|
||
were called with the flags:
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY
|
||
|
||
BurFlags must be NTFRSAPI_BUR_FLAGS_NONE.
|
||
|
||
Arguments:
|
||
BurContext - Opaque context from NtFrsApiInitializeBackupRestore()
|
||
BurSet - Opaque set from NtFrsApiEnumBackupRestoreSets();
|
||
BurFlags - See above for the supported combinations
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiRestoringDirectory:"
|
||
DWORD WStatus;
|
||
DWORD WaitStatus;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Must be one of primary or nonauth
|
||
//
|
||
if (BurFlags != NTFRSAPI_BUR_FLAGS_NONE) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurFlags not NONE", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Must be restore context
|
||
//
|
||
if (!(LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_RESTORE)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurContext not RESTORE", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// Must be active directory context
|
||
//
|
||
if (!(LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY)) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurContext not ACTIVE_DIRECTORY", WStatus, CLEANUP);
|
||
}
|
||
|
||
//
|
||
// Re-locate BurSet
|
||
//
|
||
for (LocalBurSet = LocalBurContext->BurSets;
|
||
LocalBurSet && (LocalBurSet != BurSet);
|
||
LocalBurSet = LocalBurSet->BurSetNext) {
|
||
}
|
||
if (!LocalBurSet) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurSet", WStatus, CLEANUP);
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiDestroyBackupRestore(
|
||
IN PVOID *BurContext,
|
||
IN DWORD BurFlags,
|
||
OUT HKEY *HKey,
|
||
IN OUT DWORD *KeyPathSizeInBytes,
|
||
OUT PWCHAR KeyPath
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Called once in the lifetime of a backup/restore process. Must be
|
||
matched with a previous call to NtFrsApiInitializeBackupRestore().
|
||
|
||
If NtFrsApiInitializeBackupRestore() was called with:
|
||
NTFRSAPI_BUR_FLAGS_RESTORE |
|
||
NTFRSAPI_BUR_FLAGS_SYSTEM or NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY
|
||
then BurFlags may be set to one of:
|
||
NTFRSAPI_BUR_FLAGS_NONE - Do not restart the service. The key
|
||
specified by (HKey, KeyPath) must be moved into the final
|
||
registry.
|
||
NTFRSAPI_BUR_FLAGS_RESTART - Restart the service. HKey,
|
||
KeyPathSizeInBytes, and KeyPath must be NULL.
|
||
|
||
If NtFrsApiInitializeBackupRestore() was not called the above flags,
|
||
then BurFlags must be NTFRSAPI_BUR_FLAGS_NONE and HKey, KeyPathSizeInBytes,
|
||
and KeyPath must be NULL.
|
||
|
||
Arguments:
|
||
BurContext - Returned by previous call to
|
||
NtFrsApiInitializeBackupRestore().
|
||
|
||
BurFlags - Backup/Restore Flags. See Routine Description.
|
||
|
||
HKey - Address of a HKEY for that will be set to
|
||
HKEY_LOCAL_MACHINE, ...
|
||
NULL if BurContext is not for a System or
|
||
Active Directory restore or Restart is set.
|
||
|
||
KeyPathSizeInBytes - Address of of a DWORD specifying the size of
|
||
KeyPath. Set to the actual number of bytes
|
||
needed by KeyPath. ERROR_INSUFFICIENT_BUFFER
|
||
is returned if the size of KeyPath is too small.
|
||
NULL if BurContext is not for a System or
|
||
Active Directory restore or Restart is set.
|
||
|
||
KeyPath - Buffer to receive the path of the registry key.
|
||
NULL if BurContext is not for a System or
|
||
Active Directory restore or Restart is set.
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiDestroyBackupRestore:"
|
||
DWORD WStatus;
|
||
DWORD KeyLen;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Context
|
||
//
|
||
if (!BurContext || !*BurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
LocalBurContext = *BurContext;
|
||
|
||
//
|
||
// Restart is the only supported flag
|
||
//
|
||
if (LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_RESTORE) {
|
||
//
|
||
// RESTORE
|
||
//
|
||
if (BurFlags & ~NTFRSAPI_BUR_FLAGS_RESTART) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurFlags TOO MANY FLAGS", WStatus, CLEANUP);
|
||
}
|
||
if (LocalBurContext->BurFlags & (NTFRSAPI_BUR_FLAGS_SYSTEM |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY)) {
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_RESTART) {
|
||
if (HKey || KeyPathSizeInBytes || KeyPath) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"HKey, KeyPathSizeInBytes, KeyPath", WStatus, CLEANUP);
|
||
}
|
||
} else {
|
||
if (!HKey || !KeyPathSizeInBytes || !KeyPath) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"No HKey, KeyPathSizeInBytes, KeyPath", WStatus, CLEANUP);
|
||
}
|
||
KeyLen = sizeof(WCHAR) *
|
||
(wcslen(FRS_BACKUP_RESTORE_MV_SECTION) + 1);
|
||
if (KeyLen > *KeyPathSizeInBytes) {
|
||
*KeyPathSizeInBytes = KeyLen;
|
||
WStatus = ERROR_INSUFFICIENT_BUFFER;
|
||
goto CLEANUP;
|
||
}
|
||
}
|
||
} else if (HKey || KeyPathSizeInBytes || KeyPath) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"HKey, KeyPathSizeInBytes, KeyPath", WStatus, CLEANUP);
|
||
}
|
||
//
|
||
// BACKUP
|
||
//
|
||
} else {
|
||
if (BurFlags) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"BurFlags TOO MANY FLAGS", WStatus, CLEANUP);
|
||
}
|
||
if (HKey || KeyPathSizeInBytes || KeyPath) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
CLEANUP_CB(LocalBurContext->ErrorCallBack, L"HKey, KeyPathSizeInBytes, KeyPath",
|
||
WStatus, CLEANUP);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Restart service or return the key
|
||
//
|
||
if (LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_RESTORE) {
|
||
if (LocalBurContext->BurFlags & (NTFRSAPI_BUR_FLAGS_SYSTEM |
|
||
NTFRSAPI_BUR_FLAGS_ACTIVE_DIRECTORY)) {
|
||
if (BurFlags & NTFRSAPI_BUR_FLAGS_RESTART) {
|
||
//
|
||
// Restart service; no key to move over
|
||
//
|
||
WStatus = NtFrsApiStartServiceAfterRestore(LocalBurContext->ErrorCallBack);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
} else {
|
||
//
|
||
// Key hierarchy to move into final registry
|
||
//
|
||
*HKey = HKEY_LOCAL_MACHINE;
|
||
*KeyPathSizeInBytes = sizeof(WCHAR) *
|
||
(wcslen(FRS_BACKUP_RESTORE_MV_SECTION) + 1);
|
||
CopyMemory(KeyPath, FRS_BACKUP_RESTORE_MV_SECTION, *KeyPathSizeInBytes);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
if (HANDLE_IS_VALID(LocalBurContext->BurSemaphore)) {
|
||
if (LocalBurContext->HaveBurSemaphore) {
|
||
ReleaseSemaphore(LocalBurContext->BurSemaphore, 1, NULL);
|
||
}
|
||
CloseHandle(LocalBurContext->BurSemaphore);
|
||
}
|
||
NtFrsApiFreeBurSets(&LocalBurContext->BurSets);
|
||
LocalBurContext->BurFiltersSizeInBytes = 0;
|
||
FREE(LocalBurContext->BurFilters);
|
||
FREE(LocalBurContext);
|
||
*BurContext = LocalBurContext;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiGetBurSets(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
OUT PNTFRSAPI_BUR_SET *OutBurSets
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Retrieve the replica sets from the registry. Ignore tombstoned
|
||
sets.
|
||
|
||
Arguments:
|
||
ErrorCallBack - Ignored if NULL. Otherwise, call on error.
|
||
OutBurSets - Linked list of BurSets
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiGetBurSets:"
|
||
DWORD WStatus;
|
||
DWORD KeyIdx;
|
||
DWORD RegType;
|
||
DWORD RegBytes;
|
||
DWORD ReplicaSetTombstoned;
|
||
PWCHAR SetPath = NULL;
|
||
PWCHAR ObjectName = NULL;
|
||
HKEY HSetsKey = 0;
|
||
HKEY HSetKey = 0;
|
||
PNTFRSAPI_BUR_SET LocalBurSet = NULL;
|
||
WCHAR RegBuf[MAX_PATH + 1];
|
||
|
||
*OutBurSets = NULL;
|
||
|
||
//
|
||
// Open the ntfrs parameters\replica sets section in the registry
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_SETS_SECTION,
|
||
KEY_ENUMERATE_SUB_KEYS,
|
||
&HSetsKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Enumerate the Replica Sets
|
||
//
|
||
KeyIdx = 0;
|
||
do {
|
||
WStatus = RegEnumKey(HSetsKey, KeyIdx, RegBuf, MAX_PATH + 1);
|
||
if (WStatus == ERROR_NO_MORE_ITEMS) {
|
||
break;
|
||
}
|
||
CLEANUP_CB(ErrorCallBack, FRS_SETS_SECTION, WStatus, CLEANUP);
|
||
|
||
//
|
||
// LocalBurSet->BurSetGuid (name of registry key)
|
||
//
|
||
LocalBurSet = NtFrsApi_Alloc(sizeof(NTFRSAPI_BUR_SET));
|
||
LocalBurSet->BurSetGuid = NtFrsApi_Dup(RegBuf);
|
||
SetPath = NtFrsApi_Cats(FRS_SETS_SECTION, L"\\", RegBuf);
|
||
//
|
||
// Open registry key for the Replica Set
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
SetPath,
|
||
KEY_READ,
|
||
&HSetKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP_DURING_LOOP;
|
||
}
|
||
|
||
//
|
||
// ReplicaSetTombstoned
|
||
// Ignore tombstoned replica sets
|
||
//
|
||
RegBytes = sizeof(DWORD);
|
||
WStatus = RegQueryValueEx(HSetKey,
|
||
REPLICA_SET_TOMBSTONED,
|
||
NULL,
|
||
&RegType,
|
||
(PUCHAR)&ReplicaSetTombstoned,
|
||
&RegBytes);
|
||
if (WIN_SUCCESS(WStatus) && RegType != REG_DWORD) {
|
||
ReplicaSetTombstoned = 0;
|
||
}
|
||
if (WIN_SUCCESS(WStatus) && ReplicaSetTombstoned) {
|
||
goto CLEANUP_DURING_LOOP;
|
||
}
|
||
|
||
//
|
||
// LocalBurSet->BurSetType
|
||
//
|
||
RegBytes = sizeof(RegBuf);
|
||
WStatus = RegQueryValueEx(HSetKey,
|
||
REPLICA_SET_TYPE,
|
||
NULL,
|
||
&RegType,
|
||
(PUCHAR)&RegBuf,
|
||
&RegBytes);
|
||
if (WIN_SUCCESS(WStatus) && RegType != REG_SZ) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
ObjectName = NtFrsApi_Cats(SetPath, L"->", REPLICA_SET_TYPE);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
goto CLEANUP_DURING_LOOP;
|
||
}
|
||
LocalBurSet->BurSetType = NtFrsApi_Dup(RegBuf);
|
||
|
||
//
|
||
// LocalBurSet->BurSetRoot
|
||
//
|
||
RegBytes = MAX_PATH + 1;
|
||
WStatus = RegQueryValueEx(HSetKey,
|
||
REPLICA_SET_ROOT,
|
||
NULL,
|
||
&RegType,
|
||
(PUCHAR)&RegBuf,
|
||
&RegBytes);
|
||
if (WIN_SUCCESS(WStatus) && RegType != REG_SZ) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
ObjectName = NtFrsApi_Cats(SetPath, L"->", REPLICA_SET_ROOT);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
goto CLEANUP_DURING_LOOP;
|
||
}
|
||
LocalBurSet->BurSetRoot = NtFrsApi_Dup(RegBuf);
|
||
|
||
//
|
||
// LocalBurSet->BurSetStage
|
||
//
|
||
RegBytes = MAX_PATH + 1;
|
||
WStatus = RegQueryValueEx(HSetKey,
|
||
REPLICA_SET_STAGE,
|
||
NULL,
|
||
&RegType,
|
||
(PUCHAR)&RegBuf,
|
||
&RegBytes);
|
||
if (WIN_SUCCESS(WStatus) && RegType != REG_SZ) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
ObjectName = NtFrsApi_Cats(SetPath, L"->", REPLICA_SET_STAGE);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
goto CLEANUP_DURING_LOOP;
|
||
}
|
||
LocalBurSet->BurSetStage = NtFrsApi_Dup(RegBuf);
|
||
|
||
//
|
||
// Link to list of BurSets
|
||
//
|
||
LocalBurSet->BurSetNext = *OutBurSets;
|
||
*OutBurSets = LocalBurSet;
|
||
LocalBurSet = NULL;
|
||
|
||
CLEANUP_DURING_LOOP:
|
||
RegCloseKey(HSetKey);
|
||
HSetKey = 0;
|
||
FREE(SetPath);
|
||
++KeyIdx;
|
||
} while (TRUE);
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
if (HANDLE_IS_VALID(HSetsKey)) {
|
||
RegCloseKey(HSetsKey);
|
||
}
|
||
if (HANDLE_IS_VALID(HSetKey)) {
|
||
RegCloseKey(HSetKey);
|
||
}
|
||
if (LocalBurSet) {
|
||
FREE(LocalBurSet->BurSetGuid);
|
||
FREE(LocalBurSet->BurSetRoot);
|
||
FREE(LocalBurSet->BurSetStage);
|
||
FREE(LocalBurSet->BurSetType);
|
||
FREE(LocalBurSet);
|
||
}
|
||
FREE(SetPath);
|
||
FREE(ObjectName);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiGetBurFilters(
|
||
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
||
OUT DWORD *OutBurFiltersSizeInBytes,
|
||
OUT PWCHAR *OutBurFilters
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Retrieve the ntfrs filter from FilesNotToBackup
|
||
|
||
Arguments:
|
||
ErrorCallBack - Ignored if NULL. Otherwise, call on error.
|
||
OutBurFiltersSizeInBytes - Size of *OutBurFiltes in bytes
|
||
OutBurFilters - Multistring filters
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiGetBurFilters:"
|
||
DWORD WStatus;
|
||
DWORD RegType;
|
||
PWCHAR ObjectName;
|
||
HKEY HFilesKey = 0;
|
||
DWORD RegBytes = 16;
|
||
PWCHAR RegBuf = NULL;
|
||
|
||
*OutBurFiltersSizeInBytes = 0;
|
||
*OutBurFilters = NULL;
|
||
|
||
//
|
||
// Open the ntfrs parameters\replica sets section in the registry
|
||
//
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_NEW_FILES_NOT_TO_BACKUP,
|
||
KEY_READ,
|
||
&HFilesKey);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
WStatus = NtFrsApiOpenKeyEx(NTFRSAPI_MODULE,
|
||
ErrorCallBack,
|
||
FRS_OLD_FILES_NOT_TO_BACKUP,
|
||
KEY_QUERY_VALUE,
|
||
&HFilesKey);
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
|
||
//
|
||
// NtFrs Filters from FilesNotToBackup
|
||
//
|
||
RegBuf = NtFrsApi_Alloc(RegBytes);
|
||
WStatus = RegQueryValueEx(HFilesKey,
|
||
SERVICE_NAME,
|
||
NULL,
|
||
&RegType,
|
||
(PUCHAR)RegBuf,
|
||
&RegBytes);
|
||
if (WStatus == ERROR_MORE_DATA) {
|
||
FREE(RegBuf);
|
||
RegBuf = NtFrsApi_Alloc(RegBytes);
|
||
WStatus = RegQueryValueEx(HFilesKey,
|
||
SERVICE_NAME,
|
||
NULL,
|
||
&RegType,
|
||
(PUCHAR)RegBuf,
|
||
&RegBytes);
|
||
}
|
||
if (WIN_SUCCESS(WStatus) && RegType != REG_MULTI_SZ) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
}
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
ObjectName = NtFrsApi_Cats(FRS_NEW_FILES_NOT_TO_BACKUP, L"->", SERVICE_NAME);
|
||
NtFrsApi_CallBackOnWStatus(ErrorCallBack, ObjectName, WStatus);
|
||
FREE(ObjectName);
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
*OutBurFiltersSizeInBytes = RegBytes;
|
||
*OutBurFilters = RegBuf;
|
||
RegBuf = NULL;
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
if (HANDLE_IS_VALID(HFilesKey)) {
|
||
RegCloseKey(HFilesKey);
|
||
}
|
||
if (RegBuf) {
|
||
FREE(RegBuf);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiGetBackupRestoreSets(
|
||
IN PVOID BurContext
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Cannot be called if BurContext is for a System restore.
|
||
|
||
Retrieves information about the current replicated directories
|
||
(AKA replica sets).
|
||
|
||
Arguments:
|
||
BurContext - From NtFrsApiInitializeBackupRestore()
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiGetBackupRestoreSets:"
|
||
DWORD WStatus;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Context
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
if (LocalBurContext->BurFlags & NTFRSAPI_BUR_FLAGS_SYSTEM) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Free the current filters, if any
|
||
//
|
||
LocalBurContext->BurFiltersSizeInBytes = 0;
|
||
FREE(LocalBurContext->BurFilters);
|
||
|
||
//
|
||
// Free current BurSets, if any
|
||
//
|
||
NtFrsApiFreeBurSets(&LocalBurContext->BurSets);
|
||
|
||
//
|
||
// Fetch the backup restore sets
|
||
//
|
||
WStatus = NtFrsApiGetBurSets(LocalBurContext->ErrorCallBack,
|
||
&LocalBurContext->BurSets);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Fetch the backup restore filters
|
||
//
|
||
WStatus = NtFrsApiGetBurFilters(LocalBurContext->ErrorCallBack,
|
||
&LocalBurContext->BurFiltersSizeInBytes,
|
||
&LocalBurContext->BurFilters);
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
// Ignore errors
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiEnumBackupRestoreSets(
|
||
IN PVOID BurContext,
|
||
IN DWORD BurSetIndex,
|
||
OUT PVOID *BurSet
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Returns ERROR_NO_MORE_ITEMS if BurSetIndex exceeds the number of
|
||
sets returned by NtFrsApiGetBackupRestoreSets().
|
||
|
||
Arguments:
|
||
BurContext - From NtFrsApiInitializeBackupRestore()
|
||
BurSetIndex - Index of set. Starts at 0.
|
||
BurSet - Opaque struct representing a replicating directory.
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiEnumBackupRestoreSets:"
|
||
DWORD WStatus;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Context
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!BurSet) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
*BurSet = NULL;
|
||
|
||
//
|
||
// Find the correct set
|
||
//
|
||
for (LocalBurSet = LocalBurContext->BurSets;
|
||
LocalBurSet && BurSetIndex;
|
||
LocalBurSet = LocalBurSet->BurSetNext, --BurSetIndex) {
|
||
}
|
||
if (!LocalBurSet) {
|
||
WStatus = ERROR_NO_MORE_ITEMS;
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
*BurSet = LocalBurSet;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
//
|
||
// Cleanup handles, memory, ...
|
||
//
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiIsBackupRestoreSetASysvol(
|
||
IN PVOID BurContext,
|
||
IN PVOID BurSet,
|
||
OUT BOOL *IsSysvol
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Does the specified BurSet represent a replicating SYSVOL share?
|
||
|
||
Arguments:
|
||
BurContext - From NtFrsApiInitializeBackupRestore()
|
||
BurSet - Opaque struct representing a replicating directory.
|
||
Returned by NtFrsApiEnumBackupRestoreSets(). Not
|
||
valid across calls to NtFrsApiGetBackupRestoreSets().
|
||
IsSysvol - TRUE : set is a system volume (AKA SYSVOL).
|
||
FALSE: set is a not a system volume (AKA SYSVOL).
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiBackupRestoreSetIsSysvol:"
|
||
DWORD WStatus;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Context
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!BurSet) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!IsSysvol) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Locate BurSet
|
||
//
|
||
for (LocalBurSet = LocalBurContext->BurSets;
|
||
LocalBurSet && (LocalBurSet != BurSet);
|
||
LocalBurSet = LocalBurSet->BurSetNext) {
|
||
}
|
||
if (!LocalBurSet) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// If a type were specified and it is Enterprise or Domain
|
||
//
|
||
if (LocalBurSet->BurSetType &&
|
||
(WSTR_EQ(LocalBurSet->BurSetType, NTFRSAPI_REPLICA_SET_TYPE_ENTERPRISE) ||
|
||
WSTR_EQ(LocalBurSet->BurSetType, NTFRSAPI_REPLICA_SET_TYPE_DOMAIN))) {
|
||
*IsSysvol = TRUE;
|
||
} else {
|
||
*IsSysvol = FALSE;
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
//
|
||
// Exception (may be RPC)
|
||
//
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
//
|
||
// Cleanup handles, memory, ...
|
||
//
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiGetBackupRestoreSetDirectory(
|
||
IN PVOID BurContext,
|
||
IN PVOID BurSet,
|
||
IN OUT DWORD *DirectoryPathSizeInBytes,
|
||
OUT PWCHAR DirectoryPath
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Return the path of the replicating directory represented by BurSet.
|
||
|
||
Arguments:
|
||
BurContext - From NtFrsApiInitializeBackupRestore()
|
||
BurSet - Opaque struct representing a replicating directory.
|
||
Returned by NtFrsApiEnumBackupRestoreSets(). Not
|
||
valid across calls to NtFrsApiGetBackupRestoreSets().
|
||
DirectoryPathSizeInBytes - Address of DWORD giving size of
|
||
DirectoryPath. Cannot be NULL.
|
||
Set to the number of bytes needed
|
||
to return DirectoryPath.
|
||
ERROR_INSUFFICIENT_BUFFER is returned if
|
||
DirectoryPath is too small.
|
||
DirectoryPath - Buffer that is *DirectoryPathSizeInBytes
|
||
bytes in length. Contains path of replicating
|
||
directory.
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiGetBackupRestoreSetDirectory:"
|
||
DWORD WStatus;
|
||
DWORD DirectorySize;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Context
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!BurSet) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!DirectoryPathSizeInBytes) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (*DirectoryPathSizeInBytes && !DirectoryPath) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Re-locate BurSet
|
||
//
|
||
for (LocalBurSet = LocalBurContext->BurSets;
|
||
LocalBurSet && (LocalBurSet != BurSet);
|
||
LocalBurSet = LocalBurSet->BurSetNext) {
|
||
}
|
||
if (!LocalBurSet) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
DirectorySize = (wcslen(LocalBurSet->BurSetRoot) + 1) * sizeof(WCHAR);
|
||
if (DirectorySize > *DirectoryPathSizeInBytes) {
|
||
WStatus = ERROR_INSUFFICIENT_BUFFER;
|
||
*DirectoryPathSizeInBytes = DirectorySize;
|
||
goto CLEANUP;
|
||
}
|
||
*DirectoryPathSizeInBytes = DirectorySize;
|
||
CopyMemory(DirectoryPath, LocalBurSet->BurSetRoot, DirectorySize);
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApiGetBackupRestoreSetPaths(
|
||
IN PVOID BurContext,
|
||
IN PVOID BurSet,
|
||
IN OUT DWORD *PathsSizeInBytes,
|
||
OUT PWCHAR Paths,
|
||
IN OUT DWORD *FiltersSizeInBytes,
|
||
OUT PWCHAR Filters
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Return a multistring that contains the paths to other files
|
||
and directories needed for proper operation of the replicated
|
||
directory represented by BurSet. Return another multistring
|
||
that details the backup filters to be applied to the paths
|
||
returned by this function and the path returned by
|
||
NtFrsApiGetBackupRestoreSetDirectory().
|
||
|
||
The paths may overlap the replicated directory.
|
||
|
||
The paths may contain nested entries.
|
||
|
||
Filters is a multistring in the same format as the values for
|
||
the registry key FilesNotToBackup.
|
||
|
||
The replicated directory can be found with
|
||
NtFrsApiGetBackupRestoreSetDirectory(). The replicated directory
|
||
may overlap one or more entries in Paths.
|
||
|
||
ERROR_PATH_NOT_FOUND is returned if the paths could not be
|
||
determined.
|
||
|
||
Arguments:
|
||
BurContext - From NtFrsApiInitializeBackupRestore()
|
||
BurSet - Opaque struct representing a replicating directory.
|
||
Returned by NtFrsApiEnumBackupRestoreSets(). Not
|
||
valid across calls to NtFrsApiGetBackupRestoreSets().
|
||
|
||
PathsSizeInBytes - Address of DWORD giving size of Paths.
|
||
Cannot be NULL. Set to the number of bytes
|
||
needed to return Paths.
|
||
ERROR_INSUFFICIENT_BUFFER is returned if
|
||
Paths is too small.
|
||
|
||
Paths - Buffer that is *PathsSizeInBytes
|
||
bytes in length. Contains the paths of the
|
||
other files and directories needed for proper
|
||
operation of the replicated directory.
|
||
|
||
FiltersSizeInBytes - Address of DWORD giving size of Filters.
|
||
Cannot be NULL. Set to the number of bytes
|
||
needed to return Filters.
|
||
ERROR_INSUFFICIENT_BUFFER is returned if
|
||
Filters is too small.
|
||
|
||
Filters - Buffer that is *FiltersSizeInBytes bytes in
|
||
length. Contains the backup filters to be
|
||
applied to Paths, the contents of directories
|
||
in Paths, and the replicated directory.
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApiGetBackupRestoreSetPaths:"
|
||
DWORD WStatus;
|
||
DWORD PathsSize;
|
||
LONG NChars;
|
||
PWCHAR Path;
|
||
PNTFRSAPI_BUR_SET LocalBurSet;
|
||
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
||
|
||
try {
|
||
//
|
||
// VERIFY THE PARAMETERS
|
||
//
|
||
|
||
//
|
||
// Context
|
||
//
|
||
if (!LocalBurContext) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!BurSet) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (!PathsSizeInBytes) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
if (*PathsSizeInBytes && !Paths) {
|
||
WStatus = ERROR_INVALID_PARAMETER;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Re-locate BurSet
|
||
//
|
||
for (LocalBurSet = LocalBurContext->BurSets;
|
||
LocalBurSet && (LocalBurSet != BurSet);
|
||
LocalBurSet = LocalBurSet->BurSetNext) {
|
||
}
|
||
if (!LocalBurSet) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Sysvol; return sysvol root
|
||
//
|
||
if (LocalBurSet->BurSetType &&
|
||
(WSTR_EQ(LocalBurSet->BurSetType, NTFRSAPI_REPLICA_SET_TYPE_ENTERPRISE) ||
|
||
WSTR_EQ(LocalBurSet->BurSetType, NTFRSAPI_REPLICA_SET_TYPE_DOMAIN))) {
|
||
Path = LocalBurSet->BurSetRoot;
|
||
//
|
||
// Skip trailing \'s
|
||
//
|
||
NChars = wcslen(Path) - 1;
|
||
while (NChars >= 0) {
|
||
if (Path[NChars] != L'\\') {
|
||
break;
|
||
}
|
||
--NChars;
|
||
}
|
||
//
|
||
// Find the last \ that isn't a trailing \
|
||
//
|
||
while (NChars >= 0) {
|
||
if (Path[NChars] == L'\\') {
|
||
break;
|
||
}
|
||
--NChars;
|
||
}
|
||
//
|
||
// Skip dup \'s
|
||
//
|
||
while (NChars >= 0) {
|
||
if (Path[NChars] != L'\\') {
|
||
break;
|
||
}
|
||
--NChars;
|
||
}
|
||
//
|
||
// Convert index into number of chars
|
||
//
|
||
++NChars;
|
||
|
||
//
|
||
// Sysvol path must contain at least 3 chars; <driver letter>:\
|
||
//
|
||
if (NChars < 4) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
} else {
|
||
//
|
||
// Not a Sysvol; return staging path
|
||
//
|
||
Path = LocalBurSet->BurSetStage;
|
||
NChars = wcslen(Path);
|
||
}
|
||
|
||
//
|
||
// Is the Paths and Filters buffers big enough?
|
||
//
|
||
PathsSize = (NChars + 1 + 1) * sizeof(WCHAR);
|
||
if (PathsSize > *PathsSizeInBytes ||
|
||
LocalBurContext->BurFiltersSizeInBytes > *FiltersSizeInBytes) {
|
||
*PathsSizeInBytes = PathsSize;
|
||
*FiltersSizeInBytes = LocalBurContext->BurFiltersSizeInBytes;
|
||
WStatus = ERROR_INSUFFICIENT_BUFFER;
|
||
goto CLEANUP;
|
||
}
|
||
//
|
||
// Yep; buffers are big enough
|
||
//
|
||
*PathsSizeInBytes = PathsSize;
|
||
*FiltersSizeInBytes = LocalBurContext->BurFiltersSizeInBytes;
|
||
|
||
//
|
||
// Copy the sysvol or staging path
|
||
//
|
||
CopyMemory(Paths, Path, NChars * sizeof(WCHAR));
|
||
Paths[NChars + 0] = L'\0';
|
||
Paths[NChars + 1] = L'\0';
|
||
|
||
//
|
||
// Filters
|
||
//
|
||
if (LocalBurContext->BurFiltersSizeInBytes) {
|
||
CopyMemory(Filters, LocalBurContext->BurFilters, LocalBurContext->BurFiltersSizeInBytes);
|
||
}
|
||
|
||
//
|
||
// SUCCESS
|
||
//
|
||
WStatus = ERROR_SUCCESS;
|
||
|
||
CLEANUP:;
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
//
|
||
// Clean up any handles, events, memory, ...
|
||
//
|
||
try {
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
DWORD
|
||
WINAPI
|
||
NtFrsApi_DeleteSysvolMember(
|
||
IN PSEC_WINNT_AUTH_IDENTITY_W pCreds,
|
||
IN PWCHAR BindingDC,
|
||
IN PWCHAR NTDSSettingsDn,
|
||
IN OPTIONAL PWCHAR ComputerDn
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This API is written to be called from NTDSUTIL.EXE to remove
|
||
FRS member and subscriber object for a server that is being
|
||
removed (without dcpromo-demote) from the list of DCs.
|
||
|
||
Arguments:
|
||
|
||
pCreds p Credentials used to bind to the DS.
|
||
BindingDC - Name of a DC to perform the delete on.
|
||
NTDSSettingsDn - Dn of the "NTDS Settings" object for the server
|
||
that is being removed from the sysvol replica set.
|
||
ComputerDn - Dn of the computer object for the server that is
|
||
being removed from the sysvol replica set.
|
||
|
||
Return Value:
|
||
|
||
Win32 Status
|
||
--*/
|
||
{
|
||
#undef NTFRSAPI_MODULE
|
||
#define NTFRSAPI_MODULE "NtFrsApi_DeleteSysvolMember:"
|
||
|
||
PLDAP pLdap = NULL;
|
||
DWORD LStatus = LDAP_SUCCESS;
|
||
DWORD WStatus = ERROR_SUCCESS;
|
||
PWCHAR DefaultNcDn = NULL;
|
||
PWCHAR SystemDn = NULL;
|
||
PWCHAR NtfrsSettingsDn = NULL;
|
||
PWCHAR ReplicaSetDn = NULL;
|
||
PWCHAR MemberDn = NULL;
|
||
PWCHAR ComputerRef = NULL;
|
||
PWCHAR SubscriberDn = NULL;
|
||
PLDAPMessage LdapEntry;
|
||
PLDAPMessage LdapMsg = NULL;
|
||
PWCHAR *Values = NULL;
|
||
PWCHAR Attrs[2];
|
||
PWCHAR SearchFilter = NULL;
|
||
DWORD NoOfMembers;
|
||
DWORD NoOfSubscribers;
|
||
PWCHAR MemberAttrs[4];
|
||
PWCHAR SubscriberAttrs[3];
|
||
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
||
|
||
|
||
if ((BindingDC == NULL) || (NTDSSettingsDn == NULL)) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
WStatus = FrsSupBindToDC (BindingDC, pCreds, &pLdap);
|
||
|
||
if (WStatus != ERROR_SUCCESS) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Find the naming contexts and the default naming context (objectCategory=*)
|
||
//
|
||
MK_ATTRS_1(Attrs, ATTR_DEFAULT_NAMING_CONTEXT);
|
||
|
||
if (!FrsSupLdapSearch(pLdap, CN_ROOT, LDAP_SCOPE_BASE, CATEGORY_ANY,
|
||
Attrs, 0, &LdapMsg)) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
LdapEntry = ldap_first_entry(pLdap, LdapMsg);
|
||
if (LdapEntry == NULL) {
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Find the default naming context
|
||
//
|
||
Values = (PWCHAR *)FrsSupFindValues(pLdap, LdapEntry, ATTR_DEFAULT_NAMING_CONTEXT, FALSE);
|
||
if (Values == NULL) {
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
DefaultNcDn = FrsSupWcsDup(Values[0]);
|
||
SystemDn = FrsSupExtendDn(DefaultNcDn, CN_SYSTEM);
|
||
NtfrsSettingsDn = FrsSupExtendDn(SystemDn, CN_NTFRS_SETTINGS);
|
||
ReplicaSetDn = FrsSupExtendDn(NtfrsSettingsDn, CN_DOMAIN_SYSVOL);
|
||
|
||
if (ReplicaSetDn == NULL) {
|
||
WStatus = ERROR_OUTOFMEMORY;
|
||
}
|
||
|
||
//
|
||
// Find member DN
|
||
//
|
||
if (ComputerDn != NULL) {
|
||
SearchFilter = (PWCHAR)malloc(sizeof(WCHAR) * (1 + wcslen(ComputerDn) +
|
||
wcslen(NTDSSettingsDn) + MAX_PATH));
|
||
if (SearchFilter == NULL) {
|
||
WStatus = ERROR_OUTOFMEMORY;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// e.g. (&(objectClass=nTFRSmember)
|
||
// (|(frsComputerReference=<computerdn>)(serverReference=<ntdssettingsdn>)))
|
||
//
|
||
wcscpy(SearchFilter, L"(&");
|
||
wcscat(SearchFilter, CLASS_MEMBER);
|
||
wcscat(SearchFilter, L"(|(");
|
||
wcscat(SearchFilter, ATTR_COMPUTER_REF);
|
||
wcscat(SearchFilter, L"=");
|
||
wcscat(SearchFilter, ComputerDn);
|
||
wcscat(SearchFilter, L")(");
|
||
wcscat(SearchFilter, ATTR_SERVER_REF);
|
||
wcscat(SearchFilter, L"=");
|
||
wcscat(SearchFilter, NTDSSettingsDn);
|
||
wcscat(SearchFilter, L")))");
|
||
} else {
|
||
SearchFilter = (PWCHAR)malloc(sizeof(WCHAR) * (1 + wcslen(NTDSSettingsDn) + MAX_PATH));
|
||
if (SearchFilter == NULL) {
|
||
WStatus = ERROR_OUTOFMEMORY;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// e.g. (&(objectClass=nTFRSmember)(serverReference=<ntdssettingsdn>))
|
||
//
|
||
wcscpy(SearchFilter, L"(&");
|
||
wcscat(SearchFilter, CLASS_MEMBER);
|
||
wcscat(SearchFilter, L"(");
|
||
wcscat(SearchFilter, ATTR_SERVER_REF);
|
||
wcscat(SearchFilter, L"=");
|
||
wcscat(SearchFilter, NTDSSettingsDn);
|
||
wcscat(SearchFilter, L"))");
|
||
}
|
||
|
||
MK_ATTRS_3(MemberAttrs, ATTR_DN, ATTR_COMPUTER_REF, ATTR_SERVER_REF);
|
||
|
||
if (!FrsSupLdapSearchInit(pLdap,
|
||
ReplicaSetDn,
|
||
LDAP_SCOPE_SUBTREE,
|
||
SearchFilter,
|
||
MemberAttrs,
|
||
0,
|
||
&FrsSearchContext)) {
|
||
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
NoOfMembers = FrsSearchContext.EntriesInPage;
|
||
|
||
if (NoOfMembers == 0) {
|
||
|
||
FrsSupLdapSearchClose(&FrsSearchContext);
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
if (NoOfMembers > 1) {
|
||
|
||
FrsSupLdapSearchClose(&FrsSearchContext);
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto CLEANUP;
|
||
}
|
||
|
||
//
|
||
// Scan the entries returned from ldap_search
|
||
//
|
||
for (LdapEntry = FrsSupLdapSearchNext(pLdap, &FrsSearchContext);
|
||
LdapEntry != NULL;
|
||
LdapEntry = FrsSupLdapSearchNext(pLdap, &FrsSearchContext)) {
|
||
|
||
MemberDn = FrsSupFindValue(pLdap, LdapEntry, ATTR_DN);
|
||
if (ComputerDn == NULL) {
|
||
ComputerRef = FrsSupFindValue(pLdap, LdapEntry, ATTR_COMPUTER_REF);
|
||
} else {
|
||
ComputerRef = FrsSupWcsDup(ComputerDn);
|
||
}
|
||
|
||
}
|
||
|
||
FrsSupLdapSearchClose(&FrsSearchContext);
|
||
|
||
//
|
||
// Find subscriber DN. Delete the member even if subscriber is not
|
||
// found.
|
||
//
|
||
FRS_SUP_FREE(SearchFilter);
|
||
|
||
if (ComputerRef == NULL) {
|
||
goto DODELETE;
|
||
}
|
||
|
||
SearchFilter = (PWCHAR)malloc(sizeof(WCHAR) * (1 + wcslen(MemberDn) + MAX_PATH));
|
||
if (SearchFilter == NULL) {
|
||
WStatus = ERROR_OUTOFMEMORY;
|
||
goto DODELETE;
|
||
}
|
||
|
||
wcscpy(SearchFilter, L"(&");
|
||
wcscat(SearchFilter, CLASS_SUBSCRIBER);
|
||
wcscat(SearchFilter, L"(");
|
||
wcscat(SearchFilter, ATTR_MEMBER_REF);
|
||
wcscat(SearchFilter, L"=");
|
||
wcscat(SearchFilter, MemberDn);
|
||
wcscat(SearchFilter, L"))");
|
||
|
||
MK_ATTRS_2(SubscriberAttrs, ATTR_DN, ATTR_MEMBER_REF);
|
||
|
||
if (!FrsSupLdapSearchInit(pLdap,
|
||
ComputerRef,
|
||
LDAP_SCOPE_SUBTREE,
|
||
SearchFilter,
|
||
SubscriberAttrs,
|
||
0,
|
||
&FrsSearchContext)) {
|
||
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto DODELETE;
|
||
}
|
||
|
||
NoOfSubscribers = FrsSearchContext.EntriesInPage;
|
||
|
||
if (NoOfSubscribers != 1) {
|
||
|
||
FrsSupLdapSearchClose(&FrsSearchContext);
|
||
WStatus = ERROR_NOT_FOUND;
|
||
goto DODELETE;
|
||
}
|
||
|
||
//
|
||
// Scan the entries returned from ldap_search
|
||
//
|
||
for (LdapEntry = FrsSupLdapSearchNext(pLdap, &FrsSearchContext);
|
||
LdapEntry != NULL;
|
||
LdapEntry = FrsSupLdapSearchNext(pLdap, &FrsSearchContext)) {
|
||
|
||
SubscriberDn = FrsSupFindValue(pLdap, LdapEntry, ATTR_DN);
|
||
|
||
}
|
||
|
||
FrsSupLdapSearchClose(&FrsSearchContext);
|
||
|
||
|
||
DODELETE:
|
||
//
|
||
// Now we have both the member dn and the subscriber dn.
|
||
//
|
||
|
||
if (SubscriberDn != NULL) {
|
||
LStatus = ldap_delete_s(pLdap, SubscriberDn);
|
||
if (LStatus != LDAP_SUCCESS) {
|
||
WStatus = ERROR_INTERNAL_ERROR;
|
||
}
|
||
}
|
||
|
||
if (MemberDn != NULL) {
|
||
LStatus = ldap_delete_s(pLdap, MemberDn);
|
||
if (LStatus != LDAP_SUCCESS) {
|
||
WStatus = ERROR_INTERNAL_ERROR;
|
||
}
|
||
}
|
||
|
||
CLEANUP:
|
||
|
||
if (pLdap != NULL) {
|
||
ldap_unbind_s(pLdap);
|
||
}
|
||
|
||
LDAP_FREE_VALUES(Values);
|
||
LDAP_FREE_MSG(LdapMsg);
|
||
|
||
FRS_SUP_FREE(SearchFilter);
|
||
FRS_SUP_FREE(DefaultNcDn);
|
||
FRS_SUP_FREE(SystemDn);
|
||
FRS_SUP_FREE(NtfrsSettingsDn);
|
||
FRS_SUP_FREE(ReplicaSetDn);
|
||
FRS_SUP_FREE(MemberDn);
|
||
FRS_SUP_FREE(ComputerRef);
|
||
FRS_SUP_FREE(SubscriberDn);
|
||
|
||
return WStatus;
|
||
|
||
}
|
||
|