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.
969 lines
27 KiB
969 lines
27 KiB
extern "C" {
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntioapi.h>
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <lm.h>
|
|
#include <netcan.h>
|
|
#include <icanon.h>
|
|
#include <dsgetdc.h>
|
|
#include <dsrole.h>
|
|
}
|
|
|
|
/*
|
|
* This program performs the steps necessary to configure NTFRS on a DC, prepared
|
|
* to support the system and enterprise volumes.
|
|
*
|
|
* It was created as an interim tool to support the initialization of NTFRS on a DC
|
|
* which was running NT5 generation software before NTFRS was available. After upgrading
|
|
* that DC with the latest NT5 version, this tool must be manually run to complete the
|
|
* initialization of NTFRS and system volumes.
|
|
*/
|
|
|
|
|
|
WCHAR SysVolShare[] = L"SYSVOL";
|
|
WCHAR SysVolRemark[] = L"System Volume Share (Migrated)";
|
|
WCHAR FRSSysvol[] = L"System\\CurrentControlSet\\Services\\NtFrs\\Parameters\\Sysvol";
|
|
|
|
#define DSROLEP_FRS_COMMAND L"Replica Set Command"
|
|
#define DSROLEP_FRS_NAME L"Replica Set Name"
|
|
#define DSROLEP_FRS_TYPE L"Replica Set Type"
|
|
#define DSROLEP_FRS_PRIMARY L"Replica Set Primary"
|
|
#define DSROLEP_FRS_STAGE L"Replica Set Stage"
|
|
#define DSROLEP_FRS_ROOT L"Replica Set Root"
|
|
#define DSROLEP_FRS_CREATE L"Create"
|
|
#define DSROLEP_FRS_DELETE L"Delete"
|
|
#define DSROLEP_FRS_COMMITTED L"SysVol Information is Committed"
|
|
#define DSROLEP_FRS_LONG_NAME L"Microsoft File Replication Service"
|
|
#define DSROLEP_FRS_SHORT_NAME L"NtFrs"
|
|
|
|
//
|
|
// These are the static directories created within a system volume share
|
|
//
|
|
LPWSTR StaticSysvolDirs[] = {
|
|
L"sysvol",
|
|
L"domain",
|
|
L"enterprise",
|
|
L"staging",
|
|
L"staging areas",
|
|
|
|
L"sysvol\\enterprise",
|
|
L"staging\\domain",
|
|
L"staging\\enterprise",
|
|
0
|
|
};
|
|
|
|
//
|
|
// Print out the usage message
|
|
//
|
|
void
|
|
Usage( int argc, char *argv[] )
|
|
{
|
|
fprintf( stderr, "Usage: %s [-D] [-E] sysvol\n\n", argv[0] );
|
|
fprintf( stderr, " -D this is the first upgraded DC in this domain\n\n" );
|
|
fprintf( stderr, " -E this is the first upgraded DC in this enterprise\n\n" );
|
|
fprintf( stderr, " sysvol is the path for the system volume share.\n" );
|
|
fprintf( stderr, " The system volume must reside on NTFS version 5\n" );
|
|
}
|
|
|
|
//
|
|
// Print 'text' and render 'code' into an error message
|
|
//
|
|
void
|
|
errmsg( char *text, ULONG code )
|
|
{
|
|
int i;
|
|
char msg[ 100 ];
|
|
|
|
i = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | sizeof( msg ),
|
|
NULL,
|
|
code,
|
|
0,
|
|
msg,
|
|
sizeof(msg),
|
|
NULL );
|
|
|
|
if( i )
|
|
fprintf( stderr, "%s: %s\n", text ? text : "", msg );
|
|
else
|
|
fprintf( stderr, "%s: error %d\n", text ? text : "", code );
|
|
}
|
|
|
|
//
|
|
// Print unicode 'text' and render 'code' into an error message
|
|
//
|
|
void
|
|
errmsg( LPWSTR text, ULONG code )
|
|
{
|
|
int i;
|
|
WCHAR msg[ 100 ];
|
|
|
|
i = FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | sizeof( msg ),
|
|
NULL,
|
|
code,
|
|
0,
|
|
msg,
|
|
sizeof(msg),
|
|
NULL );
|
|
|
|
if( i )
|
|
fprintf( stderr, "%ws: %ws\n", text ? text : L"", msg );
|
|
else
|
|
fprintf( stderr, "%ws: error %d\n", text ? text : L"", code );
|
|
}
|
|
|
|
//
|
|
// Write a string value to the registry
|
|
//
|
|
BOOLEAN
|
|
WriteRegistry( LPWSTR KeyName, LPWSTR ValueName, LPWSTR Value )
|
|
{
|
|
HKEY hKey;
|
|
DWORD disposition;
|
|
LONG retval;
|
|
|
|
//
|
|
// First ensure that 'keyname' exists in the registry
|
|
//
|
|
|
|
retval = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
KeyName,
|
|
NULL,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hKey,
|
|
&disposition
|
|
);
|
|
|
|
if( retval != ERROR_SUCCESS ) {
|
|
errmsg( KeyName, retval );
|
|
return FALSE;
|
|
}
|
|
|
|
if( ARGUMENT_PRESENT( ValueName ) ) {
|
|
|
|
retval = RegSetValueEx(
|
|
hKey,
|
|
ValueName,
|
|
0,
|
|
REG_SZ,
|
|
(BYTE *)Value,
|
|
(wcslen( Value ) + 1) * sizeof( WCHAR )
|
|
);
|
|
|
|
if( retval != ERROR_SUCCESS ) {
|
|
errmsg( ValueName, retval );
|
|
RegCloseKey( hKey );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Write a DWORD value to the registry
|
|
//
|
|
BOOLEAN
|
|
WriteRegistry( LPWSTR KeyName, LPWSTR ValueName, DWORD Value )
|
|
{
|
|
HKEY hKey;
|
|
DWORD disposition;
|
|
LONG retval;
|
|
|
|
//
|
|
// First ensure that 'keyname' exists in the registry
|
|
//
|
|
|
|
retval = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
KeyName,
|
|
NULL,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hKey,
|
|
&disposition
|
|
);
|
|
|
|
if( retval != ERROR_SUCCESS ) {
|
|
errmsg( KeyName, retval );
|
|
return FALSE;
|
|
}
|
|
|
|
if( ARGUMENT_PRESENT( ValueName ) ) {
|
|
|
|
retval = RegSetValueEx(
|
|
hKey,
|
|
ValueName,
|
|
0,
|
|
REG_DWORD,
|
|
(BYTE *)&Value,
|
|
sizeof( Value )
|
|
);
|
|
|
|
if( retval != ERROR_SUCCESS ) {
|
|
errmsg( ValueName, retval );
|
|
RegCloseKey( hKey );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Make sure that 'DirName' exists. Create it if it doesn't
|
|
//
|
|
BOOLEAN
|
|
EnsureDirectoryExists( LPWSTR DirName )
|
|
{
|
|
DWORD retval;
|
|
|
|
retval = GetFileAttributes( DirName );
|
|
|
|
if( retval == 0xFFFFFFFF ) {
|
|
printf( " Create directory: %ws\n", DirName );
|
|
if( !CreateDirectory( DirName, NULL ) ) {
|
|
retval = GetLastError();
|
|
errmsg( DirName, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
} else if( !(retval & FILE_ATTRIBUTE_DIRECTORY) ) {
|
|
fprintf( stderr, "Not a directory: %ws\n", DirName );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
LinkAToB( LPWSTR DirA, LPWSTR DirB )
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
UNICODE_STRING UnicodeNameB;
|
|
UNICODE_STRING DosNameB;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PREPARSE_DATA_BUFFER ReparseBufferHeader;
|
|
PCHAR ReparseBuffer;
|
|
USHORT ReparseDataLength;
|
|
|
|
if( !EnsureDirectoryExists( DirA ) ||
|
|
!EnsureDirectoryExists( DirB ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
Handle = CreateFile( DirA,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL
|
|
);
|
|
|
|
if( Handle == INVALID_HANDLE_VALUE ) {
|
|
fprintf( stderr, "Unable to open %ws", DirA );
|
|
errmsg( (LPWSTR)NULL, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the NT path name of the directory to which we want to link
|
|
//
|
|
if( !RtlDosPathNameToNtPathName_U(
|
|
DirB,
|
|
&UnicodeNameB,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
errmsg( DirB, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
RtlInitUnicodeString( &DosNameB, DirB);
|
|
|
|
//
|
|
// Set the reparse point with mount point or symbolic link tag and determine
|
|
// the appropriate length of the buffer.
|
|
//
|
|
|
|
ReparseDataLength = (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
|
|
REPARSE_DATA_BUFFER_HEADER_SIZE) +
|
|
UnicodeNameB.Length + sizeof(UNICODE_NULL) +
|
|
DosNameB.Length + sizeof(UNICODE_NULL);
|
|
|
|
//
|
|
// Allocate a buffer to set the reparse point.
|
|
//
|
|
|
|
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)LocalAlloc( LPTR,
|
|
REPARSE_DATA_BUFFER_HEADER_SIZE +
|
|
ReparseDataLength
|
|
);
|
|
|
|
if (ReparseBufferHeader == NULL) {
|
|
CloseHandle( Handle );
|
|
errmsg( "Unable to allocate reparse buffer", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
ReparseBufferHeader->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
|
ReparseBufferHeader->ReparseDataLength = (USHORT)ReparseDataLength;
|
|
ReparseBufferHeader->Reserved = 0;
|
|
|
|
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
|
|
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength = UnicodeNameB.Length;
|
|
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameOffset = UnicodeNameB.Length + sizeof( UNICODE_NULL );
|
|
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength = DosNameB.Length;
|
|
RtlCopyMemory(
|
|
ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer,
|
|
UnicodeNameB.Buffer,
|
|
UnicodeNameB.Length
|
|
);
|
|
RtlCopyMemory(
|
|
(PCHAR)(ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer)+
|
|
UnicodeNameB.Length + sizeof(UNICODE_NULL),
|
|
DosNameB.Buffer,
|
|
DosNameB.Length
|
|
);
|
|
|
|
Status = NtFsControlFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_SET_REPARSE_POINT,
|
|
ReparseBufferHeader,
|
|
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseBufferHeader->ReparseDataLength,
|
|
NULL, // no output buffer
|
|
0 // output buffer length
|
|
);
|
|
|
|
LocalFree( (HLOCAL)ReparseBufferHeader );
|
|
|
|
CloseHandle( Handle );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
switch( Status ) {
|
|
case STATUS_VOLUME_NOT_UPGRADED:
|
|
case STATUS_INVALID_DEVICE_REQUEST:
|
|
printf( "%ws must be on an NT5 NTFS volume.\n", DirA );
|
|
break;
|
|
|
|
default:
|
|
printf( "Unable to set reparse point data, status %X", Status );
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Create the system volume subtree
|
|
//
|
|
BOOLEAN
|
|
CreateSysVolTree( LPWSTR SysVolPath, BOOLEAN IsFirstDCInDomain , PWCHAR domainName)
|
|
{
|
|
DWORD i;
|
|
WCHAR bufA[ MAX_PATH ];
|
|
WCHAR bufB[ MAX_PATH ];
|
|
|
|
printf( "Checking %ws subtree at %ws\n", SysVolShare, SysVolPath );
|
|
|
|
if( !EnsureDirectoryExists( SysVolPath) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// First create the static system volume directories
|
|
//
|
|
for( i = 0; StaticSysvolDirs[i]; i++ ) {
|
|
wcscpy( bufA, SysVolPath );
|
|
wcscat( bufA, L"\\" );
|
|
wcscat( bufA, StaticSysvolDirs[i] );
|
|
|
|
if( !EnsureDirectoryExists( bufA ) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the DNS domain link for the sysvol share
|
|
//
|
|
wcscpy( bufA, SysVolPath );
|
|
wcscat( bufA, L"\\sysvol\\" );
|
|
wcscat( bufA, domainName );
|
|
|
|
wcscpy( bufB, SysVolPath );
|
|
wcscat( bufB, L"\\domain" );
|
|
|
|
if( !LinkAToB( bufA, bufB ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Create the enterprise link for the sysvol share
|
|
//
|
|
wcscpy( bufA, SysVolPath );
|
|
wcscat( bufA, L"\\sysvol\\enterprise" );
|
|
|
|
wcscpy( bufB, SysVolPath );
|
|
wcscat( bufB, L"\\enterprise" );
|
|
|
|
if( !LinkAToB( bufA, bufB ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Create the DNS domain link in the staging area
|
|
//
|
|
wcscpy( bufA, SysVolPath );
|
|
wcscat( bufA, L"\\staging areas\\" );
|
|
wcscat( bufA, domainName );
|
|
|
|
wcscpy( bufB, SysVolPath );
|
|
wcscat( bufB, L"\\staging\\domain" );
|
|
|
|
if( !LinkAToB( bufA, bufB ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Create the enterprise link in the staging area
|
|
//
|
|
wcscpy( bufA, SysVolPath );
|
|
wcscat( bufA, L"\\staging areas\\enterprise" );
|
|
|
|
wcscpy( bufB, SysVolPath );
|
|
wcscat( bufB, L"\\staging\\enterprise" );
|
|
|
|
if( !LinkAToB( bufA, bufB ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Finally, if we are the first DC initialized in this domain,
|
|
// we need to create the scripts directory
|
|
//
|
|
if( IsFirstDCInDomain ) {
|
|
|
|
wcscpy( bufA, SysVolPath );
|
|
wcscat( bufA, L"\\domain\\scripts" );
|
|
|
|
if( !EnsureDirectoryExists( bufA ) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Create the system volume share.
|
|
//
|
|
BOOLEAN
|
|
CreateSysVolShare( LPWSTR SysVolPath )
|
|
{
|
|
DWORD dwType, retval;
|
|
SHARE_INFO_2 si2, *psi2;
|
|
|
|
printf( "Creating system volume share:\n" );
|
|
|
|
//
|
|
// Blow away the current sysvol share if it exists
|
|
//
|
|
retval = NetShareGetInfo( NULL, SysVolShare, 2, (LPBYTE *)&psi2 );
|
|
|
|
if( retval == NO_ERROR ) {
|
|
if( psi2->shi2_type != STYPE_DISKTREE ) {
|
|
fprintf( stderr, "%ws is shared, but is not a disk share!\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
printf( " Delete current share: %ws=%ws\n", psi2->shi2_netname, psi2->shi2_path );
|
|
|
|
NetApiBufferFree( psi2 );
|
|
|
|
//
|
|
// Try to delete this share
|
|
//
|
|
retval = NetShareDel( NULL, SysVolShare, 0 );
|
|
if( retval != NO_ERROR ) {
|
|
errmsg( "Unable to delete sysvol share", retval );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add the new sysvol share
|
|
//
|
|
si2.shi2_netname = SysVolShare;
|
|
si2.shi2_type = STYPE_DISKTREE;
|
|
si2.shi2_remark = SysVolRemark;
|
|
si2.shi2_permissions = 0;
|
|
si2.shi2_max_uses = (DWORD)-1;
|
|
si2.shi2_current_uses = 0;
|
|
si2.shi2_path = SysVolPath;
|
|
si2.shi2_passwd = 0;
|
|
|
|
printf( " Add share: %ws=%ws\n", SysVolShare, SysVolPath );
|
|
retval = NetShareAdd( NULL, 2, (LPBYTE)&si2, &dwType );
|
|
|
|
if( retval != NO_ERROR ) {
|
|
errmsg( "Unable to share new sysvol share", retval );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Add the registry key telling netlogon to share this out as the system volume share
|
|
//
|
|
printf( " Add netlogon sysvol registry key\n" );
|
|
|
|
return WriteRegistry( L"System\\CurrentControlSet\\Services\\Netlogon\\Parameters",
|
|
L"SysVol",
|
|
SysVolPath
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add the registry keys needed for NTFRS. Do what DcPromo would have done
|
|
//
|
|
BOOLEAN
|
|
AddRegKeys(
|
|
IN PWCHAR ReplicaSetName,
|
|
IN PWCHAR ReplicaSetType,
|
|
IN DWORD ReplicaSetPrimary,
|
|
IN PWCHAR ReplicaSetStage,
|
|
IN PWCHAR ReplicaSetRoot )
|
|
{
|
|
WCHAR KeyName[512];
|
|
|
|
//
|
|
// Make sure the NTFRS section is there
|
|
//
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs", 0, (DWORD)0 );
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters", 0, (DWORD)0 );
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters\\Sysvol", 0, (DWORD)0 );
|
|
|
|
//
|
|
// Sysvol key + values
|
|
//
|
|
wcscpy( KeyName, L"System\\CurrentControlSet\\services\\NtFrs\\Parameters\\Sysvol\\" );
|
|
wcscat( KeyName, ReplicaSetName );
|
|
WriteRegistry( KeyName, DSROLEP_FRS_COMMAND, DSROLEP_FRS_CREATE );
|
|
WriteRegistry( KeyName, DSROLEP_FRS_NAME, ReplicaSetName );
|
|
WriteRegistry( KeyName, DSROLEP_FRS_TYPE, ReplicaSetType );
|
|
WriteRegistry( KeyName, DSROLEP_FRS_PRIMARY, (DWORD)ReplicaSetPrimary );
|
|
WriteRegistry( KeyName, DSROLEP_FRS_ROOT, ReplicaSetRoot );
|
|
WriteRegistry( KeyName, DSROLEP_FRS_STAGE, ReplicaSetStage );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Commit the registry keys so that if NtFrs is running it can now
|
|
// pick up a consistent set of values.
|
|
//
|
|
BOOLEAN
|
|
CommitRegKeys(
|
|
VOID )
|
|
{
|
|
//
|
|
// Make sure the NTFRS section is there
|
|
//
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs", 0, (DWORD)0 );
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters", 0, (DWORD)0 );
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters\\Sysvol", 0, (DWORD)0 );
|
|
|
|
//
|
|
// Commit both sysvols
|
|
//
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters\\Sysvol",
|
|
DSROLEP_FRS_COMMITTED, (DWORD)1 );
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Commit the registry keys so that if NtFrs is running it can now
|
|
// pick up a consistent set of values.
|
|
//
|
|
BOOLEAN
|
|
DeleteRegKeys(
|
|
VOID )
|
|
{
|
|
DWORD WStatus;
|
|
HKEY HKey = 0;
|
|
WCHAR KeyBuf[MAX_PATH + 1];
|
|
|
|
printf("Delete registry keys.\n");
|
|
|
|
//
|
|
// Make sure the NTFRS section is there
|
|
//
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs", 0, (DWORD)0 );
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters", 0, (DWORD)0 );
|
|
WriteRegistry( L"System\\CurrentControlSet\\services\\NtFrs\\Parameters\\Sysvol", 0, (DWORD)0 );
|
|
|
|
WStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\services\\NtFrs\\Parameters\\Sysvol",
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&HKey);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
errmsg("Cannot open registry", WStatus);
|
|
return FALSE;
|
|
}
|
|
WStatus = RegDeleteValue(HKey, DSROLEP_FRS_COMMITTED);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_FILE_NOT_FOUND) {
|
|
errmsg("Cannot delete registry value", WStatus);
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Delete the subkeys
|
|
//
|
|
do {
|
|
WStatus = RegEnumKey(HKey, 0, KeyBuf, MAX_PATH + 1);
|
|
if (WStatus == ERROR_SUCCESS) {
|
|
WStatus = RegDeleteKey(HKey, KeyBuf);
|
|
}
|
|
} while (WStatus == ERROR_SUCCESS);
|
|
if (WStatus != ERROR_NO_MORE_ITEMS) {
|
|
errmsg("Cannot delete registry key", WStatus);
|
|
return FALSE;
|
|
}
|
|
RegCloseKey(HKey);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Set FRS to auto start
|
|
//
|
|
BOOLEAN
|
|
SetFRSAutoStart( void )
|
|
{
|
|
SC_HANDLE ServiceHandle;
|
|
SC_HANDLE SCMHandle;
|
|
|
|
printf( "Set NTFRS to Auto Start\n" );
|
|
|
|
//
|
|
// Contact the SC manager.
|
|
//
|
|
SCMHandle = OpenSCManager(NULL,
|
|
NULL,
|
|
SC_MANAGER_CONNECT);
|
|
if (SCMHandle == NULL) {
|
|
errmsg("Can't set NtFrs to Auto Start", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Contact the NtFrs service.
|
|
//
|
|
ServiceHandle = OpenService(SCMHandle,
|
|
DSROLEP_FRS_SHORT_NAME,
|
|
SERVICE_INTERROGATE |
|
|
SERVICE_PAUSE_CONTINUE |
|
|
SERVICE_QUERY_STATUS |
|
|
SERVICE_START |
|
|
SERVICE_STOP |
|
|
SERVICE_CHANGE_CONFIG);
|
|
if (ServiceHandle == NULL) {
|
|
errmsg("Can't set NtFrs to Auto Start", GetLastError());
|
|
return FALSE;
|
|
}
|
|
CloseServiceHandle(SCMHandle);
|
|
|
|
//
|
|
// Service starts automatically at startup
|
|
//
|
|
if (!ChangeServiceConfig(ServiceHandle,
|
|
SERVICE_NO_CHANGE,
|
|
SERVICE_AUTO_START,
|
|
SERVICE_NO_CHANGE,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
DSROLEP_FRS_LONG_NAME)) {
|
|
errmsg("Can't set NtFrs to Auto Start", GetLastError());
|
|
return FALSE;
|
|
}
|
|
CloseServiceHandle(ServiceHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Start FRS
|
|
//
|
|
BOOLEAN
|
|
StartFRS( void )
|
|
{
|
|
DWORD WStatus;
|
|
SC_HANDLE ServiceHandle;
|
|
SC_HANDLE SCMHandle;
|
|
SERVICE_STATUS ServiceStatus;
|
|
|
|
printf( "Start NTFRS\n" );
|
|
|
|
//
|
|
// Contact the SC manager.
|
|
//
|
|
SCMHandle = OpenSCManager(NULL,
|
|
NULL,
|
|
SC_MANAGER_CONNECT);
|
|
if (SCMHandle == NULL) {
|
|
errmsg("Can't start NtFrs", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Contact the NtFrs service.
|
|
//
|
|
ServiceHandle = OpenService(SCMHandle,
|
|
DSROLEP_FRS_SHORT_NAME,
|
|
SERVICE_INTERROGATE |
|
|
SERVICE_PAUSE_CONTINUE |
|
|
SERVICE_QUERY_STATUS |
|
|
SERVICE_START |
|
|
SERVICE_STOP |
|
|
SERVICE_CHANGE_CONFIG);
|
|
if (ServiceHandle == NULL) {
|
|
errmsg("Can't start NtFrs", GetLastError());
|
|
return FALSE;
|
|
}
|
|
CloseServiceHandle(SCMHandle);
|
|
|
|
//
|
|
// stop the service
|
|
//
|
|
ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus);
|
|
|
|
//
|
|
// Start the service
|
|
//
|
|
if (!StartService(ServiceHandle, 0, NULL)) {
|
|
//
|
|
// May be shutting down; retry in a bit
|
|
//
|
|
Sleep(3 * 1000);
|
|
if (!StartService(ServiceHandle, 0, NULL)) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_SERVICE_ALREADY_RUNNING) {
|
|
errmsg("Can't start NtFrs", WStatus);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
CloseServiceHandle(ServiceHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsThisADC(
|
|
IN PWCHAR domainName )
|
|
{
|
|
DWORD WStatus;
|
|
PWCHAR p;
|
|
DSROLE_PRIMARY_DOMAIN_INFO_BASIC *DsRole;
|
|
|
|
//
|
|
// Is this a domain controller?
|
|
//
|
|
WStatus = DsRoleGetPrimaryDomainInformation(NULL,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE *)&DsRole);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
errmsg("Can't get primary domain information", WStatus);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Domain Controller (DC)
|
|
//
|
|
if (DsRole->MachineRole == DsRole_RoleBackupDomainController ||
|
|
DsRole->MachineRole == DsRole_RolePrimaryDomainController) {
|
|
if (!DsRole->DomainNameDns) {
|
|
errmsg( "Unable to get domain name", ERROR_PATH_NOT_FOUND );
|
|
return FALSE;
|
|
}
|
|
wcscpy(domainName, DsRole->DomainNameDns);
|
|
DsRoleFreeMemory(DsRole);
|
|
for( p = domainName; *p != L'\0'; p++ );
|
|
if( *(p-1) == L'.' ) {
|
|
*(p-1) = L'\0';
|
|
}
|
|
return TRUE;
|
|
}
|
|
DsRoleFreeMemory(DsRole);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Make it so NTFRS will run on this DC
|
|
*/
|
|
__cdecl
|
|
main( int argc, char *argv[] )
|
|
{
|
|
DWORD i;
|
|
LONG retval;
|
|
BOOLEAN IsFirstDCInDomain = FALSE;
|
|
BOOLEAN IsFirstDCInEnterprise = FALSE;
|
|
WCHAR SysVolPath[ MAX_PATH ], stage[ MAX_PATH ], root[ MAX_PATH ];
|
|
DWORD pathType;
|
|
WCHAR domainName[512];
|
|
|
|
SysVolPath[0] = 0;
|
|
|
|
if( IsThisADC( domainName ) == FALSE ) {
|
|
fprintf( stderr, "This program can only be run on a DC!\n" );
|
|
return 1;
|
|
}
|
|
|
|
for( i = 1; i < (DWORD)argc; i++ ) {
|
|
switch( argv[i][0] ) {
|
|
case '/':
|
|
case '-':
|
|
switch( argv[i][1] ) {
|
|
case 'D':
|
|
case 'd':
|
|
IsFirstDCInDomain = TRUE;
|
|
break;
|
|
case 'E':
|
|
case 'e':
|
|
IsFirstDCInEnterprise = TRUE;
|
|
IsFirstDCInDomain = TRUE;
|
|
break;
|
|
default:
|
|
fprintf( stderr, "Unrecognized option: %c\n\n", argv[i][1] );
|
|
Usage( argc, argv );
|
|
return 1;
|
|
}
|
|
break;
|
|
default:
|
|
|
|
if( SysVolPath[0] != 0 ) {
|
|
fprintf( stderr, "Too many 'sysvol' paths! Need quotes?\n\n" );
|
|
Usage( argc, argv );
|
|
return 1;
|
|
}
|
|
|
|
mbstowcs( SysVolPath, argv[i], sizeof( SysVolPath ) );
|
|
|
|
//
|
|
// Make sure the system volume path is reasonable
|
|
//
|
|
retval = NetpPathType( NULL, SysVolPath, &pathType, 0 );
|
|
|
|
if( retval != NO_ERROR || pathType != ITYPE_PATH_ABSD ) {
|
|
fprintf( stderr, "Invalid system volume path. Must be an absolute path.\n" );
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( SysVolPath[0] == 0 ) {
|
|
Usage( argc, argv );
|
|
return 1;
|
|
}
|
|
|
|
printf( "Initializing the NT MultiMaster File Replication Service:\n" );
|
|
printf( " Domain: %ws\n", domainName );
|
|
|
|
if( IsFirstDCInDomain ) {
|
|
printf( " First DC in the domain\n" );
|
|
}
|
|
if( IsFirstDCInEnterprise ) {
|
|
printf( " First DC in the enterprise\n" );
|
|
}
|
|
printf( " System Volume: %ws\n", SysVolPath );
|
|
|
|
//
|
|
// Create the sysvol tree and share it out
|
|
//
|
|
if( !CreateSysVolTree( SysVolPath, IsFirstDCInDomain, domainName ) ||
|
|
!CreateSysVolShare( SysVolPath ) ) {
|
|
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Add the registry keys for the NTFRS enterprise volume
|
|
//
|
|
wcscpy( stage, SysVolPath );
|
|
wcscat( stage, L"\\staging areas\\enterprise" );
|
|
|
|
wcscpy( root, SysVolPath );
|
|
wcscat( root, L"\\sysvol\\enterprise" );
|
|
|
|
if( !AddRegKeys( L"enterprise",
|
|
L"enterprise",
|
|
IsFirstDCInEnterprise,
|
|
stage,
|
|
root ) ) {
|
|
goto errout;
|
|
}
|
|
|
|
//
|
|
// Add the registry keys for the NTFRS domain volume
|
|
//
|
|
wcscpy( stage, SysVolPath );
|
|
wcscat( stage, L"\\staging areas\\" );
|
|
wcscat( stage, domainName );
|
|
|
|
wcscpy( root, SysVolPath );
|
|
wcscat( root, L"\\sysvol\\" );
|
|
wcscat( root, domainName );
|
|
|
|
if( !AddRegKeys( domainName,
|
|
L"domain",
|
|
IsFirstDCInDomain,
|
|
stage,
|
|
root ) ) {
|
|
goto errout;
|
|
}
|
|
|
|
//
|
|
// Commit the keys only after all of the values are set without error.
|
|
// Otherwise, a running NtFrs might pick up the keys while they are in
|
|
// an incomplete state.
|
|
//
|
|
if( !CommitRegKeys()) {
|
|
goto errout;
|
|
}
|
|
|
|
//
|
|
// Now ensure that the replication service is running, and will run at each boot
|
|
//
|
|
if( !SetFRSAutoStart() || !StartFRS() ) {
|
|
goto errout;
|
|
}
|
|
|
|
printf( "Success!\n" );
|
|
|
|
return 0;
|
|
|
|
errout:
|
|
fprintf( stderr, "Warning: SYSVOL share path may have been changed.\n" );
|
|
return 1;
|
|
}
|