You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8445 lines
264 KiB
8445 lines
264 KiB
/*++
|
|
|
|
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>
|
|
|
|
#include <overflow.h>
|
|
|
|
|
|
CRITICAL_SECTION NtFrsApi_GlobalLock;
|
|
BOOLEAN NtFrsApi_GlobalLock_Initialized = FALSE;
|
|
CRITICAL_SECTION NtFrsApi_ThreadLock;
|
|
BOOLEAN NtFrsApi_ThreadLock_Initialized = FALSE;
|
|
DWORD NtFrsApi_State;
|
|
DWORD NtFrsApi_ServiceState;
|
|
DWORD NtFrsApi_ServiceWaitHint;
|
|
HANDLE NtFrsApi_ShutDownEvent;
|
|
|
|
PWCHAR NtFrsApi_ServiceLongName = NULL;
|
|
|
|
//
|
|
// 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; } }
|
|
|
|
//
|
|
// Only close valid registry key handles and then set the handle invalid.
|
|
// FRS_REG_CLOSE(handle);
|
|
//
|
|
#define FRS_REG_CLOSE(_Handle) \
|
|
if (HANDLE_IS_VALID(_Handle)) { \
|
|
RegCloseKey(_Handle); \
|
|
(_Handle) = INVALID_HANDLE_VALUE; \
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
BOOLEAN NtFrsApi_Dbg_Lock_Initialized = FALSE;
|
|
|
|
//
|
|
// 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 if there is
|
|
// enough room in the buffer. "0xFFFFFFFF " needs 12 characters
|
|
// including the null.
|
|
//
|
|
if ((Length - strlen(Buffer)) >= 12) {
|
|
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.
|
|
--*/
|
|
{
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
|
|
if(InitializeCriticalSectionAndSpinCount(&NtFrsApi_Dbg_Lock,
|
|
NTFRS_CRITSEC_SPIN_COUNT)) {
|
|
NtFrsApi_Dbg_Lock_Initialized = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
#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.
|
|
--*/
|
|
{
|
|
if(NtFrsApi_Dbg_Lock_Initialized) {
|
|
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) {
|
|
Line[LineSize-1] = '\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;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if ((wcslen(NtFrsApi_Dbg_LogFile) + wcslen(NTFRSAPI_DBG_LOG_FILE) + 1) <= (MAX_PATH + 1)) {
|
|
wcscat(NtFrsApi_Dbg_LogFile, NTFRSAPI_DBG_LOG_FILE);
|
|
}else {
|
|
return;
|
|
}
|
|
|
|
NtFrsApi_Dbg_LogFILE = _wfopen(NtFrsApi_Dbg_LogFile, L"ac");
|
|
if (!NtFrsApi_Dbg_LogFILE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
#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)) {
|
|
//
|
|
// Calc offset to start of free buffer space.
|
|
// And calc max space left in buffer.
|
|
//
|
|
Line = ((PCHAR)Info) + Info->OffsetToFree;
|
|
LineSize = (Info->SizeInChars - (ULONG)(Line - (PCHAR)Info));
|
|
|
|
if (LineSize <= 0 || _vsnprintf(Line, LineSize, Format, argptr) < 0) {
|
|
//
|
|
// Buffer is filled.
|
|
//
|
|
SetFlag(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL);
|
|
} else {
|
|
|
|
LineLen = strlen(Line) + 1;
|
|
if (Info->CharsToSkip > 0) {
|
|
//
|
|
// Still skipping chars that we previously returned. Barf.
|
|
//
|
|
Info->CharsToSkip = (LineLen > Info->CharsToSkip) ?
|
|
0 : Info->CharsToSkip - LineLen;
|
|
} else {
|
|
//
|
|
// The line fits. Bump freespace offset and TotalChars returned.
|
|
//
|
|
Info->OffsetToFree += 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) {
|
|
// If format message fails then call the call back with
|
|
// empty string.
|
|
(ErrorCallBack)(L"", WStatus);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Produce message: "ObjectName: Error Code Message"
|
|
//
|
|
FinalSize = (wcslen(ObjectName) +
|
|
wcslen(MsgBuf) +
|
|
wcslen(NTFRSAPI_ERROR_MESSAGE_DELIMITER) +
|
|
1) * sizeof(WCHAR);
|
|
try {
|
|
FinalMsg = NtFrsApi_Alloc(FinalSize);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
return;
|
|
}
|
|
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_ThreadLock.
|
|
|
|
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);
|
|
//
|
|
// Decrement count only if the thread was correctly
|
|
// initialized.
|
|
//
|
|
--NtFrsApi_NumberOfThreads;
|
|
}
|
|
|
|
if (HANDLE_IS_VALID(Thread->DoneEvent)) {
|
|
SetEvent(Thread->DoneEvent);
|
|
CloseHandle(Thread->DoneEvent);
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
0,
|
|
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, ...
|
|
//
|
|
if (Thread) {
|
|
try {
|
|
try {
|
|
EnterCriticalSection(&NtFrsApi_ThreadLock);
|
|
Thread = NtFrsApi_FreeThread(Thread);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
|
|
}
|
|
} finally {
|
|
//
|
|
// Release lock
|
|
//
|
|
LeaveCriticalSection(&NtFrsApi_ThreadLock);
|
|
}
|
|
}
|
|
|
|
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
|
|
--*/
|
|
{
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
|
|
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
|
|
//
|
|
if(!InitializeCriticalSectionAndSpinCount(&NtFrsApi_GlobalLock,
|
|
NTFRS_CRITSEC_SPIN_COUNT)) {
|
|
return FALSE;
|
|
}
|
|
|
|
NtFrsApi_GlobalLock_Initialized = TRUE;
|
|
|
|
//
|
|
// Thread subsystem
|
|
//
|
|
if(!InitializeCriticalSectionAndSpinCount(&NtFrsApi_ThreadLock,
|
|
NTFRS_CRITSEC_SPIN_COUNT)) {
|
|
return FALSE;
|
|
}
|
|
|
|
NtFrsApi_ThreadLock_Initialized = TRUE;
|
|
|
|
//
|
|
// 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);
|
|
|
|
if(NtFrsApi_GlobalLock_Initialized) {
|
|
DeleteCriticalSection(&NtFrsApi_GlobalLock);
|
|
}
|
|
|
|
if(NtFrsApi_ThreadLock_Initialized) {
|
|
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
|
|
//
|
|
if (OutHandle == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
*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
|
|
//
|
|
if (OutHandle == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
*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_BindLocal(
|
|
IN DWORD ErrorCallBack(IN PWCHAR, IN ULONG), OPTIONAL
|
|
OUT handle_t *OutHandle
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Bind to the NtFrs service on this machine
|
|
with authenticated, encrypted packets.
|
|
|
|
Arguments:
|
|
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_BindLocal:"
|
|
DWORD WStatus, WStatus1;
|
|
DWORD ComputerLen;
|
|
handle_t Handle = NULL;
|
|
PWCHAR PrincName = NULL;
|
|
PWCHAR BindingString = NULL;
|
|
PWCHAR LocalComputerName = NULL;
|
|
|
|
try {
|
|
NTFRSAPI_DBG_PRINT1("Bind Local: %ws\n", LocalComputerName);
|
|
//
|
|
// Return value
|
|
//
|
|
if (OutHandle == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
*OutHandle = NULL;
|
|
|
|
//
|
|
// Get computer name
|
|
//
|
|
ComputerLen = MAX_COMPUTERNAME_LENGTH + 2;
|
|
LocalComputerName = NtFrsApi_Alloc(ComputerLen * sizeof(WCHAR));
|
|
|
|
if (!GetComputerName(LocalComputerName, &ComputerLen)) {
|
|
WStatus = GetLastError();
|
|
NTFRSAPI_DBG_PRINT1("Bind Local: GetComputerName(); %d\n", WStatus);
|
|
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
|
}
|
|
|
|
//
|
|
// Create a binding string to NtFrs on some machine. Trim leading \\
|
|
//
|
|
FRS_TRIM_LEADING_2SLASH(LocalComputerName);
|
|
|
|
NTFRSAPI_DBG_PRINT1("Bind Local: compose to %ws\n", LocalComputerName);
|
|
|
|
WStatus = RpcStringBindingCompose(NULL, PROTSEQ_LRPC, LocalComputerName,
|
|
NULL, NULL, &BindingString);
|
|
|
|
NTFRSAPI_DBG_PRINT2("Bind Local: compose done to %ws; %d\n", LocalComputerName, 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 Local: RpcBindingFromStringBinding(%ws); %d\n",
|
|
LocalComputerName, WStatus);
|
|
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
|
}
|
|
//
|
|
// Resolve the binding to the dynamic endpoint
|
|
//
|
|
NTFRSAPI_DBG_PRINT1("Bind Local: resolve to %ws\n", LocalComputerName);
|
|
WStatus = RpcEpResolveBinding(Handle, NtFrsApi_ClientIfHandle);
|
|
|
|
NTFRSAPI_DBG_PRINT2("Bind Local: resolve done to %ws; %d\n", LocalComputerName, WStatus);
|
|
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
|
|
|
//
|
|
// Find the principle name
|
|
//
|
|
NTFRSAPI_DBG_PRINT1("Bind Local: princname to %ws\n", LocalComputerName);
|
|
WStatus = RpcMgmtInqServerPrincName(Handle, RPC_C_AUTHN_WINNT, &PrincName);
|
|
|
|
NTFRSAPI_DBG_PRINT2("Bind Local: princname done to %ws; %d\n", LocalComputerName, WStatus);
|
|
CLEANUP_CB(ErrorCallBack, NtFrsApi_ServiceLongName, WStatus, CLEANUP);
|
|
//
|
|
// Set authentication info
|
|
//
|
|
NTFRSAPI_DBG_PRINT2("Bind Local: auth to %ws (princname %ws)\n",
|
|
LocalComputerName, PrincName);
|
|
WStatus = RpcBindingSetAuthInfo(Handle,
|
|
PrincName,
|
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
|
RPC_C_AUTHN_WINNT,
|
|
NULL,
|
|
RPC_C_AUTHZ_NONE);
|
|
NTFRSAPI_DBG_PRINT2("Bind Local: set auth done to %ws; %d\n", LocalComputerName, 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 (LocalComputerName) {
|
|
FREE(LocalComputerName);
|
|
}
|
|
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 Local 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;
|
|
|
|
if (ServiceHandle == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// 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;
|
|
|
|
if (OutHKey == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// 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;
|
|
HKEY HKey = INVALID_HANDLE_VALUE;
|
|
HKEY HNetKey = INVALID_HANDLE_VALUE;
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Insure access to the ntfrs parameters\sysvol section in the registry
|
|
//
|
|
NTFRSAPI_DBG_PRINT0("Prepare: FRS Registry\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
|
|
// 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);
|
|
}
|
|
FRS_REG_CLOSE(HKey);
|
|
FRS_REG_CLOSE(HNetKey);
|
|
} 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.
|
|
//
|
|
// The presence of the LDAP_OPT_DNSDOMAIN_NAME flag tells the ldap client
|
|
// to ensure that the server is really a DC (By constructing the three
|
|
// part SPN when performing the LDAP bind.) This enables mutual
|
|
// authentication.
|
|
//
|
|
|
|
//
|
|
// 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);
|
|
//
|
|
// Request mutual auth.
|
|
//
|
|
LStatus = ldap_set_option(DsDeleteLdap, LDAP_OPT_DNSDOMAIN_NAME, &DomainName);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
NTFRSAPI_DBG_PRINT2("ERROR - Can not set option LDAP_OPT_DNSDOMAIN_NAME while connecting to the DS on %ws; %ws)\n",
|
|
DomainControllerName, ldap_err2string(LStatus));
|
|
ldap_unbind_s(DsDeleteLdap);
|
|
DsDeleteLdap = NULL;
|
|
} else {
|
|
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);
|
|
//
|
|
// Request mutual auth.
|
|
//
|
|
LStatus = ldap_set_option(DsDeleteLdap, LDAP_OPT_DNSDOMAIN_NAME, &DomainName);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
NTFRSAPI_DBG_PRINT2("ERROR - Can not set option LDAP_OPT_DNSDOMAIN_NAME while connecting to the DS on %ws; %ws)\n",
|
|
DomainControllerName, ldap_err2string(LStatus));
|
|
ldap_unbind_s(DsDeleteLdap);
|
|
DsDeleteLdap = NULL;
|
|
} else {
|
|
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;
|
|
}
|
|
if (wcslen(LdapValue) >= sizeof(DsDeleteConfigDn)) {
|
|
NTFRSAPI_DBG_PRINT1("ERROR - Buffer too small for 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;
|
|
}
|
|
|
|
if (wcslen(LdapValues[0]) >= sizeof(DsDeleteDefaultDn)) {
|
|
NTFRSAPI_DBG_PRINT1("ERROR - Buffer too small for Domain 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+1];
|
|
|
|
//
|
|
// 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) {
|
|
Dn[Count] = UNICODE_NULL;
|
|
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 = INVALID_HANDLE_VALUE;
|
|
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);
|
|
}
|
|
FRS_REG_CLOSE(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 = INVALID_HANDLE_VALUE;
|
|
HKEY HSubKey = INVALID_HANDLE_VALUE;
|
|
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_BindWithAuth(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 {
|
|
FRS_REG_CLOSE(HKey);
|
|
FRS_REG_CLOSE(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;
|
|
|
|
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);
|
|
}
|
|
|
|
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 = INVALID_HANDLE_VALUE;
|
|
HKEY HSubKey = INVALID_HANDLE_VALUE;
|
|
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_BindWithAuth(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 {
|
|
FRS_REG_CLOSE(HKey);
|
|
FRS_REG_CLOSE(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);
|
|
}
|
|
|
|
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 = INVALID_HANDLE_VALUE;
|
|
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);
|
|
}
|
|
FRS_REG_CLOSE(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 = INVALID_HANDLE_VALUE;
|
|
HKEY HNetKey = INVALID_HANDLE_VALUE;
|
|
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");
|
|
//
|
|
// Need to bind with NTLM over LRPC because
|
|
// Kerberos is not available at this point.
|
|
// Actually NTLM authentication would also fail, but the loopback
|
|
// optimization gets around that.
|
|
//
|
|
WStatus = NtFrsApi_BindLocal(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
|
|
//
|
|
#if 0
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
FRS_REG_CLOSE(HKey);
|
|
FRS_REG_CLOSE(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 == 0) {
|
|
SizeInChars = NTFRSAPI_DEFAULT_INFO_SIZE;
|
|
} else
|
|
if (SizeInChars < NTFRSAPI_MINIMUM_INFO_SIZE) {
|
|
SizeInChars = NTFRSAPI_MINIMUM_INFO_SIZE;
|
|
}
|
|
|
|
if(SizeInChars > NTFRSAPI_DEFAULT_INFO_SIZE) {
|
|
SizeInChars = NTFRSAPI_DEFAULT_INFO_SIZE;
|
|
}
|
|
|
|
//
|
|
// Check params
|
|
//
|
|
Info = *NtFrsApiInfo;
|
|
if (Info != NULL) {
|
|
TypeOfInfo = Info->TypeOfInfo;
|
|
}
|
|
|
|
//
|
|
// Allocate a large text buffer
|
|
//
|
|
if (Info != NULL) {
|
|
if (!NtFrsApi_InfoMoreW(Info)) {
|
|
NtFrsApi_InfoFreeW(NtFrsApiInfo);
|
|
WStatus = ERROR_SUCCESS;
|
|
goto CLEANUP;
|
|
}
|
|
//
|
|
// Set charstoskip to the value of cumulative count of chars
|
|
// returned. Note: In SP3 and XP TotalChars is now a context
|
|
// handle so this is a no-op. TotalChars is saved in the caller's
|
|
// server side context block.
|
|
//
|
|
Info->CharsToSkip = Info->TotalChars;
|
|
} else {
|
|
//
|
|
// Allocate a buffer and insert the DLL major/minor rev,
|
|
// and the structure size and the type of info request.
|
|
//
|
|
Info = NtFrsApi_Alloc(SizeInChars);
|
|
Info->Major = NTFRS_MAJOR;
|
|
Info->Minor = NTFRS_MINOR;
|
|
Info->SizeInChars = SizeInChars;
|
|
Info->TypeOfInfo = TypeOfInfo;
|
|
//
|
|
// Return the buffer address to the caller.
|
|
//
|
|
*NtFrsApiInfo = Info;
|
|
}
|
|
//
|
|
// Clear the flags and reset the buffer to empty.
|
|
//
|
|
Info->Flags = 0;
|
|
Info->OffsetToFree = (ULONG)(Info->Lines - (PCHAR)Info);
|
|
Info->OffsetToLines = Info->OffsetToFree;
|
|
|
|
//
|
|
// 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_IPRINT2(Info, " NtFrsApi Compiled on: %s %s\n",
|
|
NtFrsApi_Date, NtFrsApi_Time);
|
|
#if NTFRS_TEST
|
|
NTFRSAPI_IPRINT0(Info, " NTFRS_TEST Enabled\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 {
|
|
//
|
|
// Are we sending the entire info buffer over the wire to the
|
|
// service v.s. just the header?????
|
|
//
|
|
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 {
|
|
//
|
|
// Are we sending the entire info buffer over the wire to the
|
|
// service v.s. just the header?????
|
|
//
|
|
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 == NULL) {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (NtFrsApiInfo == NULL) {
|
|
*InOutLine = NULL;
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
NextLine = *InOutLine;
|
|
//
|
|
// Offset to first line in the returned buffer.
|
|
//
|
|
FirstLine = ((PCHAR)NtFrsApiInfo) + NtFrsApiInfo->OffsetToLines;
|
|
//
|
|
// Offset to first unused byte in the return buffer.
|
|
//
|
|
FreeLine = ((PCHAR)NtFrsApiInfo) + NtFrsApiInfo->OffsetToFree;
|
|
|
|
//
|
|
// Get start of next line to return.
|
|
//
|
|
if (NextLine == NULL) {
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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;
|
|
HKEY HBurMvKey = INVALID_HANDLE_VALUE;
|
|
HKEY HBurStopKey = INVALID_HANDLE_VALUE;
|
|
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);
|
|
FRS_REG_CLOSE(HBurMvKey);
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
FRS_REG_CLOSE(HBurMvKey);
|
|
FRS_REG_CLOSE(HBurStopKey);
|
|
FREE(ServiceConfig);
|
|
} 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;
|
|
HKEY HCumusKey = INVALID_HANDLE_VALUE;
|
|
HKEY HCumuKey = INVALID_HANDLE_VALUE;
|
|
HKEY HBurCumusKey = INVALID_HANDLE_VALUE;
|
|
HKEY HBurCumuKey = INVALID_HANDLE_VALUE;
|
|
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)) {
|
|
FRS_REG_CLOSE(HBurCumusKey);
|
|
}
|
|
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:
|
|
FRS_REG_CLOSE(HCumuKey);
|
|
|
|
FRS_REG_CLOSE(HBurCumuKey);
|
|
FREE(CumuPath);
|
|
FREE(BurCumuPath);
|
|
++KeyIdx;
|
|
} while (TRUE);
|
|
|
|
//
|
|
// SUCCESS
|
|
//
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
CLEANUP:
|
|
//
|
|
// Clean up any handles, events, memory, ...
|
|
//
|
|
FRS_REG_CLOSE(HCumuKey);
|
|
FRS_REG_CLOSE(HBurCumuKey);
|
|
FRS_REG_CLOSE(HCumusKey);
|
|
FRS_REG_CLOSE(HBurCumusKey);
|
|
FREE(CumuPath);
|
|
FREE(BurCumuPath);
|
|
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 = INVALID_HANDLE_VALUE;
|
|
PWCHAR BurSetPath = 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 {
|
|
FRS_REG_CLOSE(HBurSetKey);
|
|
FREE(BurSetPath);
|
|
} 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);
|
|
}
|
|
|
|
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;
|
|
KeyLen = sizeof(WCHAR) *
|
|
(wcslen(FRS_BACKUP_RESTORE_MV_SECTION) + 1);
|
|
if (KeyLen > *KeyPathSizeInBytes) {
|
|
WStatus = ERROR_INSUFFICIENT_BUFFER;
|
|
goto CLEANUP;
|
|
} else {
|
|
*KeyPathSizeInBytes = KeyLen;
|
|
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);
|
|
}
|
|
|
|
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 = INVALID_HANDLE_VALUE;
|
|
HKEY HSetKey = INVALID_HANDLE_VALUE;
|
|
PNTFRSAPI_BUR_SET LocalBurSet = NULL;
|
|
WCHAR RegBuf[MAX_PATH + 1];
|
|
PWCHAR RegBufPtr = NULL;
|
|
|
|
*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)*sizeof(WCHAR);
|
|
RegBufPtr = NtFrsApi_Alloc(RegBytes);
|
|
WStatus = RegQueryValueEx(HSetKey,
|
|
REPLICA_SET_ROOT,
|
|
NULL,
|
|
&RegType,
|
|
(PUCHAR)RegBufPtr,
|
|
&RegBytes);
|
|
|
|
if (WStatus == ERROR_MORE_DATA) {
|
|
// If the buffer is not big enough then the required size
|
|
// is returned in the RegBytes parameter.
|
|
FREE(RegBufPtr);
|
|
RegBufPtr = NtFrsApi_Alloc(RegBytes);
|
|
WStatus = RegQueryValueEx(HSetKey,
|
|
REPLICA_SET_ROOT,
|
|
NULL,
|
|
&RegType,
|
|
(PUCHAR)RegBufPtr,
|
|
&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(RegBufPtr);
|
|
FREE(RegBufPtr);
|
|
|
|
//
|
|
// LocalBurSet->BurSetStage
|
|
//
|
|
RegBytes = (MAX_PATH + 1)*sizeof(WCHAR);
|
|
RegBufPtr = NtFrsApi_Alloc(RegBytes);
|
|
WStatus = RegQueryValueEx(HSetKey,
|
|
REPLICA_SET_STAGE,
|
|
NULL,
|
|
&RegType,
|
|
(PUCHAR)RegBufPtr,
|
|
&RegBytes);
|
|
|
|
if (WStatus == ERROR_MORE_DATA) {
|
|
// If the buffer is not big enough then the required size
|
|
// is returned in the RegBytes parameter.
|
|
FREE(RegBufPtr);
|
|
RegBufPtr = NtFrsApi_Alloc(RegBytes);
|
|
|
|
WStatus = RegQueryValueEx(HSetKey,
|
|
REPLICA_SET_STAGE,
|
|
NULL,
|
|
&RegType,
|
|
(PUCHAR)RegBufPtr,
|
|
&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(RegBufPtr);
|
|
FREE(RegBufPtr);
|
|
|
|
//
|
|
// Link to list of BurSets
|
|
//
|
|
LocalBurSet->BurSetNext = *OutBurSets;
|
|
*OutBurSets = LocalBurSet;
|
|
LocalBurSet = NULL;
|
|
|
|
CLEANUP_DURING_LOOP:
|
|
FRS_REG_CLOSE(HSetKey);
|
|
FREE(SetPath);
|
|
++KeyIdx;
|
|
} while (TRUE);
|
|
|
|
//
|
|
// SUCCESS
|
|
//
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
CLEANUP:;
|
|
//
|
|
// Clean up any handles, events, memory, ...
|
|
//
|
|
FRS_REG_CLOSE(HSetsKey);
|
|
FRS_REG_CLOSE(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 = INVALID_HANDLE_VALUE;
|
|
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
|
|
//
|
|
try{
|
|
RegBuf = NtFrsApi_Alloc(RegBytes);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
goto CLEANUP;
|
|
}
|
|
|
|
WStatus = RegQueryValueEx(HFilesKey,
|
|
SERVICE_NAME,
|
|
NULL,
|
|
&RegType,
|
|
(PUCHAR)RegBuf,
|
|
&RegBytes);
|
|
if (WStatus == ERROR_MORE_DATA) {
|
|
FREE(RegBuf);
|
|
try{
|
|
RegBuf = NtFrsApi_Alloc(RegBytes);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
goto CLEANUP;
|
|
}
|
|
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, ...
|
|
//
|
|
FRS_REG_CLOSE(HFilesKey);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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) +
|
|
wcslen(CLASS_MEMBER) +
|
|
wcslen(ATTR_COMPUTER_REF) +
|
|
wcslen(ATTR_SERVER_REF) +
|
|
wcslen(L"(&(|(=)(=)))")));
|
|
|
|
|
|
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) +
|
|
wcslen(CLASS_MEMBER) +
|
|
wcslen(ATTR_SERVER_REF) +
|
|
wcslen(L"(&(=))")));
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
NtFrsApi_IsPathReplicated(
|
|
IN OPTIONAL PWCHAR ComputerName,
|
|
IN PWCHAR Path,
|
|
IN ULONG ReplicaSetTypeOfInterest,
|
|
OUT BOOL *Replicated,
|
|
OUT ULONG *Primary,
|
|
OUT BOOL *Root,
|
|
OUT GUID *ReplicaSetGuid
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Checks if the Path given is part of a replica set of type
|
|
ReplicaSetTypeOfInterest. If ReplicaSetTypeOfInterest is 0, will match for
|
|
any replica set type.On successful execution the OUT parameters are set as
|
|
follows:
|
|
|
|
Replicated == TRUE iff Path is part of a replica set of type
|
|
ReplicaSetTypeOfInterest
|
|
|
|
Primary == 0 if this machine is not the primary for the replica set
|
|
1 if this machine is the primary for the replica set
|
|
2 if there is no primary for the replica set
|
|
|
|
Root == TRUE iff Path is the root path for the replica
|
|
|
|
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.
|
|
|
|
Path - the local path to check
|
|
|
|
ReplicaSetTypeOfInterest - The type of replica set to match against. Set to
|
|
0 to match any replica set.
|
|
|
|
Replicated - set TRUE iff Path is part of a replica set of type
|
|
ReplicaSetTypeOfInterest.
|
|
If Replicated is FALSE, the other OUT parameters are not set.
|
|
|
|
Primary - set to 0 if this machine is not the primary for the replica set
|
|
1 if this machine is the primary for the replica set
|
|
2 if there is no primary for the replica set
|
|
|
|
Root - set TRUE iff Path is the root path for the replica.
|
|
|
|
ReplicaSetGuid - GUID of the matching replica set.
|
|
|
|
Return Value:
|
|
|
|
Win32 Status
|
|
|
|
--*/
|
|
{
|
|
#undef NTFRSAPI_MODULE
|
|
#define NTFRSAPI_MODULE "NtFrsApi_IsPathReplicated:"
|
|
|
|
DWORD WStatus, WStatus1;
|
|
DWORD AuthWStatus;
|
|
handle_t AuthHandle = NULL;
|
|
ULONG ulReplicated, ulRoot;
|
|
|
|
//
|
|
// Bind to the service with authentication
|
|
//
|
|
AuthWStatus = NtFrsApi_BindWithAuth(ComputerName, NULL, &AuthHandle);
|
|
if (!WIN_SUCCESS(AuthWStatus)) {
|
|
NTFRSAPI_DBG_PRINT3("ERROR - Cannot bind w/authentication to computer, %ws; %08x (%d)\n",
|
|
ComputerName, AuthWStatus, AuthWStatus);
|
|
}
|
|
|
|
//
|
|
// Send Authenticated RPC request to service
|
|
//
|
|
if (HANDLE_IS_VALID(AuthHandle)) {
|
|
try {
|
|
WStatus = NtFrsApi_Rpc_IsPathReplicated(AuthHandle, Path, ReplicaSetTypeOfInterest, &ulReplicated, Primary, &ulRoot, ReplicaSetGuid);
|
|
*Replicated = (ulReplicated==0)?FALSE:TRUE;
|
|
*Root = (ulRoot==0)?FALSE:TRUE;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
}
|
|
} else {
|
|
WStatus = ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
if (!WIN_SUCCESS(WStatus)) {
|
|
NTFRSAPI_DBG_PRINT3("ERROR - Cannot RPC to computer, %ws; %08x (%d)\n",
|
|
ComputerName, WStatus, WStatus);
|
|
goto CLEANUP;
|
|
}
|
|
|
|
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
CLEANUP:
|
|
//
|
|
// Clean up any handles, events, memory, ...
|
|
//
|
|
try {
|
|
//
|
|
// Unbind
|
|
//
|
|
if (AuthHandle) {
|
|
WStatus1 = RpcBindingFree(&AuthHandle);
|
|
NtFrsApiCheckRpcError(WStatus1, "RpcBindingFree");
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Exception (may be RPC)
|
|
//
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
}
|
|
return WStatus;
|
|
|
|
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
NtFrsApi_WriterCommand(
|
|
IN PWCHAR ComputerName, OPTIONAL
|
|
IN ULONG Command
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
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.
|
|
|
|
Command - Command to send to the FRS service.
|
|
Only two commands are currently in use.
|
|
freeze and thaw.
|
|
|
|
Return Value:
|
|
Win32 Status
|
|
--*/
|
|
{
|
|
DWORD WStatus, WStatus1;
|
|
DWORD AuthWStatus;
|
|
DWORD NoAuthWStatus;
|
|
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_WriterCommand(AuthHandle, Command);
|
|
} 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_WriterCommand(AuthHandle, Command);
|
|
} 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
|
|
NtFrsApiGetBackupRestoreSetType(
|
|
IN PVOID BurContext,
|
|
IN PVOID BurSet,
|
|
OUT PWCHAR SetType,
|
|
IN OUT DWORD *SetTypeSizeInBytes
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return the type of the replica set. The type is
|
|
returned as a string. The types are defined in the
|
|
frsapip.h file.
|
|
|
|
Arguments:
|
|
BurContext - From NtFrsApiInitializeBackupRestore()
|
|
BurSet - Opaque struct representing a replicating directory.
|
|
Returned by NtFrsApiEnumBackupRestoreSets(). Not
|
|
valid across calls to NtFrsApiGetBackupRestoreSets().
|
|
SetType - type of the replica set in a string format.
|
|
SetTypeSizeInBytes - Length of the passed in buffer.
|
|
|
|
Return Value:
|
|
|
|
ERROR_MORE_DATE - If the passed in buffer is not big enough
|
|
to hold the type. SetTypeSizeInBytes is set
|
|
to the size of buffer required.
|
|
ERROR_INVALID_PARAMETER - Parameter validation.
|
|
ERROR_NOT_FOUND - If passed in set is not found in the context
|
|
or if it does not have a type specified in
|
|
the registry.
|
|
ERROR_SUCCESS - When everything goes fine. SetTypeSizeInBytes
|
|
returns the number of bytes written to the
|
|
buffer.
|
|
Win32 Status
|
|
--*/
|
|
{
|
|
#undef NTFRSAPI_MODULE
|
|
#define NTFRSAPI_MODULE "NtFrsApiGetBackupRestoreSetType:"
|
|
DWORD WStatus;
|
|
PNTFRSAPI_BUR_SET LocalBurSet;
|
|
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
|
DWORD RequiredSizeInBytes;
|
|
|
|
try {
|
|
//
|
|
// VERIFY THE PARAMETERS
|
|
//
|
|
|
|
//
|
|
// Context
|
|
//
|
|
if (!LocalBurContext) {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto CLEANUP;
|
|
}
|
|
if (!BurSet) {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Locate BurSet
|
|
//
|
|
for (LocalBurSet = LocalBurContext->BurSets;
|
|
LocalBurSet && (LocalBurSet != BurSet);
|
|
LocalBurSet = LocalBurSet->BurSetNext) {
|
|
}
|
|
if ((LocalBurSet == NULL) ||
|
|
(LocalBurSet->BurSetType == NULL)) {
|
|
WStatus = ERROR_NOT_FOUND;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Check if the passed in buffer is large enough.
|
|
//
|
|
|
|
RequiredSizeInBytes = (wcslen(LocalBurSet->BurSetType)+1) *
|
|
sizeof(WCHAR);
|
|
|
|
if ((SetType == NULL) ||
|
|
(SetTypeSizeInBytes == NULL) ||
|
|
(*SetTypeSizeInBytes < RequiredSizeInBytes) ){
|
|
|
|
SetType = NULL;
|
|
*SetTypeSizeInBytes = RequiredSizeInBytes;
|
|
WStatus = ERROR_MORE_DATA;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// SUCCESS
|
|
//
|
|
|
|
*SetTypeSizeInBytes = RequiredSizeInBytes;
|
|
wcscpy(SetType, LocalBurSet->BurSetType);
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
CLEANUP:;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Exception (may be RPC)
|
|
//
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
NtFrsApiGetBackupRestoreSetGuid(
|
|
IN PVOID BurContext,
|
|
IN PVOID BurSet,
|
|
OUT PWCHAR SetGuid,
|
|
IN OUT DWORD *SetGuidSizeInBytes
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return the type of the replica set. The type is
|
|
returned as a string. The types are defined in the
|
|
frsapip.h file.
|
|
|
|
Arguments:
|
|
BurContext - From NtFrsApiInitializeBackupRestore()
|
|
BurSet - Opaque struct representing a replicating directory.
|
|
Returned by NtFrsApiEnumBackupRestoreSets(). Not
|
|
valid across calls to NtFrsApiGetBackupRestoreSets().
|
|
SetGuid - Guid of the replica set in a string format.
|
|
SetGuidSizeInBytes - Length of the passed in buffer.
|
|
|
|
Return Value:
|
|
|
|
ERROR_MORE_DATE - If the passed in buffer is not big enough
|
|
to hold the guid. SetGuidSizeInBytes is set
|
|
to the size of buffer required.
|
|
ERROR_INVALID_PARAMETER - Parameter validation.
|
|
ERROR_NOT_FOUND - If passed in set is not found in the context.
|
|
ERROR_SUCCESS - When everything goes fine. SetGuidSizeInBytes
|
|
returns the number of bytes written to the
|
|
buffer.
|
|
Win32 Status
|
|
--*/
|
|
{
|
|
#undef NTFRSAPI_MODULE
|
|
#define NTFRSAPI_MODULE "NtFrsApiGetBackupRestoreSetGuid:"
|
|
DWORD WStatus;
|
|
PNTFRSAPI_BUR_SET LocalBurSet;
|
|
PNTFRSAPI_BUR_CONTEXT LocalBurContext = BurContext;
|
|
DWORD RequiredSizeInBytes;
|
|
|
|
try {
|
|
//
|
|
// VERIFY THE PARAMETERS
|
|
//
|
|
|
|
//
|
|
// Context
|
|
//
|
|
if (!LocalBurContext) {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto CLEANUP;
|
|
}
|
|
if (!BurSet) {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Locate BurSet
|
|
//
|
|
for (LocalBurSet = LocalBurContext->BurSets;
|
|
LocalBurSet && (LocalBurSet != BurSet);
|
|
LocalBurSet = LocalBurSet->BurSetNext) {
|
|
}
|
|
if ((LocalBurSet == NULL) ||
|
|
(LocalBurSet->BurSetGuid == NULL)) {
|
|
WStatus = ERROR_NOT_FOUND;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Check if the passed in buffer is large enough.
|
|
//
|
|
|
|
RequiredSizeInBytes = (wcslen(LocalBurSet->BurSetGuid)+1) *
|
|
sizeof(WCHAR);
|
|
|
|
if ((SetGuid == NULL) ||
|
|
(SetGuidSizeInBytes == NULL) ||
|
|
(*SetGuidSizeInBytes < RequiredSizeInBytes) ){
|
|
|
|
SetGuid = NULL;
|
|
*SetGuidSizeInBytes = RequiredSizeInBytes;
|
|
WStatus = ERROR_MORE_DATA;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// SUCCESS
|
|
//
|
|
|
|
*SetGuidSizeInBytes = RequiredSizeInBytes;
|
|
wcscpy(SetGuid, LocalBurSet->BurSetGuid);
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
CLEANUP:;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Exception (may be RPC)
|
|
//
|
|
GET_EXCEPTION_CODE(WStatus);
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|