2261 lines
61 KiB
C++
2261 lines
61 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1998 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
ntfrsupg.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
Upgrade ntfrs.
|
|||
|
|
|||
|
Supports upgrading previous IDS (1717) to current IDS (~1770)
|
|||
|
|
|||
|
Basically, it builds the sysvol hierarchy, adjusts the netlogon
|
|||
|
share, and sets up the registry so the service can create the
|
|||
|
sysvols. There is no seeding.
|
|||
|
|
|||
|
Author:
|
|||
|
Isaac Heizer and Billy J. Fuller 9-Mar-1998
|
|||
|
|
|||
|
Environment
|
|||
|
User mode winnt
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
extern "C" {
|
|||
|
#include <ntreppch.h>
|
|||
|
#pragma hdrstop
|
|||
|
#include <lm.h>
|
|||
|
#include <netcan.h>
|
|||
|
#include <icanon.h>
|
|||
|
#include <dsgetdc.h>
|
|||
|
#include <dsrole.h>
|
|||
|
#include <frs.h>
|
|||
|
#include <tablefcn.h>
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* This program performs the steps necessary to configure NTFRS on a DC, prepared
|
|||
|
* to support the system 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";
|
|||
|
WCHAR FRSParameters[] = L"System\\CurrentControlSet\\Services\\NtFrs\\Parameters";
|
|||
|
|
|||
|
#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"File Replication Service"
|
|||
|
#define DSROLEP_FRS_SHORT_NAME L"NtFrs"
|
|||
|
#define DSROLEP_FRS_RECOVERY L"Catastrophic Restore at Startup"
|
|||
|
|
|||
|
#define FREE(_x_) { if (_x_) { free(_x_); (_x_) = NULL; } }
|
|||
|
|
|||
|
//
|
|||
|
// These are the static directories created within a system volume share
|
|||
|
//
|
|||
|
LPWSTR StaticSysvolDirs[] = {
|
|||
|
L"sysvol",
|
|||
|
L"domain",
|
|||
|
L"staging",
|
|||
|
L"staging areas",
|
|||
|
|
|||
|
L"staging\\domain",
|
|||
|
0
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Print out the usage message
|
|||
|
//
|
|||
|
void
|
|||
|
Usage( int argc, char *argv[] )
|
|||
|
{
|
|||
|
fprintf( stderr, "Usage: %s [-D] sysvol\n\n", argv[0] );
|
|||
|
fprintf( stderr, " -D this is the first upgraded DC in this domain\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" );
|
|||
|
fprintf( stderr, " \n");
|
|||
|
fprintf( stderr, " After running this program with the -D option, copy\n");
|
|||
|
fprintf( stderr, " the files in the scripts directory from the old\n");
|
|||
|
fprintf( stderr, " system volume into the scripts directory in the new\n");
|
|||
|
fprintf( stderr, " domain system volume. Otherwise, wait for replication to\n");
|
|||
|
fprintf( stderr, " populate the scripts directory in the new domain\n");
|
|||
|
fprintf( stderr, " system volume.\n");
|
|||
|
fprintf( stderr, " \n");
|
|||
|
fprintf( stderr, "Usage: %s [-Y] -RESTORE\n\n", argv[0] );
|
|||
|
fprintf( stderr, " -RESTORE Delete replicated directories.\n");
|
|||
|
fprintf( stderr, " -Y Delete directories without prompting.\n");
|
|||
|
fprintf( stderr, " \n");
|
|||
|
fprintf( stderr, " WARNING: FILE REPLICATION SERVICE STATE WILL BE DELETED!\n");
|
|||
|
fprintf( stderr, " WARNING: ALL REPLICATED DIRECTORIES WILL BE DELETED!\n");
|
|||
|
fprintf( stderr, " When run with the -RESTORE option, the contents of\n");
|
|||
|
fprintf( stderr, " the replicated directories and the service's state \n");
|
|||
|
fprintf( stderr, " are deleted with the expectation that the data will \n");
|
|||
|
fprintf( stderr, " be supplied by a replication partner at a later time. \n");
|
|||
|
fprintf( stderr, " The user is prompted for each replicated directory.\n");
|
|||
|
fprintf( stderr, " The contents of the replicated directory are not deleted\n");
|
|||
|
fprintf( stderr, " if you type n. The contents are deleted if you type y.\n");
|
|||
|
fprintf( stderr, " Type n if there are no replication partners.\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) / sizeof(WCHAR),
|
|||
|
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 (!WIN_SUCCESS(retval)) {
|
|||
|
errmsg( KeyName, retval );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if( ARGUMENT_PRESENT( ValueName ) ) {
|
|||
|
|
|||
|
retval = RegSetValueEx(
|
|||
|
hKey,
|
|||
|
ValueName,
|
|||
|
0,
|
|||
|
REG_SZ,
|
|||
|
(BYTE *)Value,
|
|||
|
(wcslen( Value ) + 1) * sizeof( WCHAR )
|
|||
|
);
|
|||
|
|
|||
|
if (!WIN_SUCCESS(retval)) {
|
|||
|
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 (!WIN_SUCCESS(retval)) {
|
|||
|
errmsg( KeyName, retval );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if( ARGUMENT_PRESENT( ValueName ) ) {
|
|||
|
|
|||
|
retval = RegSetValueEx(
|
|||
|
hKey,
|
|||
|
ValueName,
|
|||
|
0,
|
|||
|
REG_DWORD,
|
|||
|
(BYTE *)&Value,
|
|||
|
sizeof( Value )
|
|||
|
);
|
|||
|
|
|||
|
if (!WIN_SUCCESS(retval)) {
|
|||
|
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;
|
|||
|
BOOLEAN RetValue = FALSE;
|
|||
|
HANDLE Handle = INVALID_HANDLE_VALUE;
|
|||
|
UNICODE_STRING UnicodeNameB;
|
|||
|
UNICODE_STRING DosNameB;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
|
|||
|
PCHAR ReparseBuffer;
|
|||
|
USHORT ReparseDataLength;
|
|||
|
PWSTR FreeBuffer = NULL;
|
|||
|
|
|||
|
if( !EnsureDirectoryExists( DirA ) ||
|
|||
|
!EnsureDirectoryExists( DirB ) ) {
|
|||
|
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
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_IS_VALID(Handle)) {
|
|||
|
fprintf( stderr, "Unable to open %ws", DirA );
|
|||
|
errmsg( (LPWSTR)NULL, GetLastError() );
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the NT path name of the directory to which we want to link
|
|||
|
//
|
|||
|
if( !RtlDosPathNameToNtPathName_U(
|
|||
|
DirB,
|
|||
|
&UnicodeNameB,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
)) {
|
|||
|
errmsg( DirB, GetLastError() );
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember the unicode buffer to free.
|
|||
|
//
|
|||
|
FreeBuffer = UnicodeNameB.Buffer;
|
|||
|
|
|||
|
RtlInitUnicodeString( &DosNameB, DirB);
|
|||
|
|
|||
|
//
|
|||
|
// Set the reparse point with mount point or symbolic link tag and determine
|
|||
|
// the appropriate length of the buffer.
|
|||
|
//
|
|||
|
|
|||
|
ReparseDataLength = (USHORT) ((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) {
|
|||
|
errmsg( "Unable to allocate reparse buffer", GetLastError() );
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
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
|
|||
|
);
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// SUCCESS
|
|||
|
//
|
|||
|
RetValue = TRUE;
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
if ((HLOCAL)ReparseBufferHeader) {
|
|||
|
LocalFree((HLOCAL)ReparseBufferHeader);
|
|||
|
}
|
|||
|
|
|||
|
if (FreeBuffer != NULL) {
|
|||
|
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
|||
|
}
|
|||
|
|
|||
|
FRS_CLOSE(Handle);
|
|||
|
|
|||
|
return RetValue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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 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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
WCHAR SysVol[ MAX_PATH ];
|
|||
|
|
|||
|
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", SysVolShare );
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// sysvol share
|
|||
|
//
|
|||
|
wcscpy( SysVol, SysVolPath );
|
|||
|
wcscat( SysVol, L"\\sysvol" );
|
|||
|
|
|||
|
//
|
|||
|
// 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 = SysVol;
|
|||
|
si2.shi2_passwd = 0;
|
|||
|
|
|||
|
printf( " Add share: %ws=%ws\n", SysVolShare, SysVol );
|
|||
|
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",
|
|||
|
SysVol
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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];
|
|||
|
BOOLEAN RetValue = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// 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 (!WIN_SUCCESS(WStatus)) {
|
|||
|
errmsg("Cannot open registry", WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
WStatus = RegDeleteValue(HKey, DSROLEP_FRS_COMMITTED);
|
|||
|
if (!WIN_SUCCESS(WStatus) && WStatus != ERROR_FILE_NOT_FOUND) {
|
|||
|
errmsg("Cannot delete registry value", WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
//
|
|||
|
// Delete the subkeys
|
|||
|
//
|
|||
|
do {
|
|||
|
WStatus = RegEnumKey(HKey, 0, KeyBuf, MAX_PATH + 1);
|
|||
|
if (WIN_SUCCESS(WStatus)) {
|
|||
|
WStatus = RegDeleteKey(HKey, KeyBuf);
|
|||
|
}
|
|||
|
} while (WIN_SUCCESS(WStatus));
|
|||
|
if (WStatus != ERROR_NO_MORE_ITEMS) {
|
|||
|
errmsg("Cannot delete registry key", WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
//
|
|||
|
// SUCCESS
|
|||
|
//
|
|||
|
RetValue = TRUE;
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
if (HKey) {
|
|||
|
RegCloseKey(HKey);
|
|||
|
}
|
|||
|
return RetValue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set FRS to auto start
|
|||
|
//
|
|||
|
BOOLEAN
|
|||
|
SetFRSAutoStart( void )
|
|||
|
{
|
|||
|
SC_HANDLE ServiceHandle = NULL;
|
|||
|
SC_HANDLE SCMHandle = NULL;
|
|||
|
BOOLEAN RetValue = FALSE;
|
|||
|
|
|||
|
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());
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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());
|
|||
|
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,
|
|||
|
DSROLEP_FRS_LONG_NAME)) {
|
|||
|
errmsg("Can't set NtFrs to Auto Start", GetLastError());
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// SUCCESS
|
|||
|
//
|
|||
|
RetValue = TRUE;
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
if (SCMHandle) {
|
|||
|
CloseServiceHandle(SCMHandle);
|
|||
|
}
|
|||
|
if (ServiceHandle) {
|
|||
|
CloseServiceHandle(ServiceHandle);
|
|||
|
}
|
|||
|
return RetValue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Start FRS
|
|||
|
//
|
|||
|
BOOLEAN
|
|||
|
StartFRS( void )
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
SC_HANDLE ServiceHandle = NULL;
|
|||
|
SC_HANDLE SCMHandle = NULL;
|
|||
|
SERVICE_STATUS ServiceStatus;
|
|||
|
BOOLEAN RetValue = FALSE;
|
|||
|
|
|||
|
printf( "Start NTFRS\n" );
|
|||
|
|
|||
|
//
|
|||
|
// Contact the SC manager.
|
|||
|
//
|
|||
|
SCMHandle = OpenSCManager(NULL,
|
|||
|
NULL,
|
|||
|
SC_MANAGER_CONNECT);
|
|||
|
if (SCMHandle == NULL) {
|
|||
|
errmsg("Can't start NtFrs", GetLastError());
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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());
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// SUCCESS
|
|||
|
//
|
|||
|
RetValue = TRUE;
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
if (SCMHandle) {
|
|||
|
CloseServiceHandle(SCMHandle);
|
|||
|
}
|
|||
|
if (ServiceHandle) {
|
|||
|
CloseServiceHandle(ServiceHandle);
|
|||
|
}
|
|||
|
return RetValue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Stop FRS
|
|||
|
//
|
|||
|
BOOLEAN
|
|||
|
StopFRS( void )
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
SC_HANDLE ServiceHandle;
|
|||
|
SC_HANDLE SCMHandle;
|
|||
|
SERVICE_STATUS ServiceStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Contact the SC manager.
|
|||
|
//
|
|||
|
SCMHandle = OpenSCManager(NULL,
|
|||
|
NULL,
|
|||
|
SC_MANAGER_CONNECT);
|
|||
|
if (SCMHandle == NULL) {
|
|||
|
errmsg("Can't stop 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 Stop NtFrs", GetLastError());
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
CloseServiceHandle(SCMHandle);
|
|||
|
|
|||
|
//
|
|||
|
// stop the service
|
|||
|
//
|
|||
|
STOP_AGAIN:
|
|||
|
if (!ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus)) {
|
|||
|
WStatus = GetLastError();
|
|||
|
if (WStatus != ERROR_SERVICE_REQUEST_TIMEOUT &&
|
|||
|
WStatus != ERROR_SERVICE_NOT_ACTIVE) {
|
|||
|
errmsg("Can't Stop NtFrs", WStatus);
|
|||
|
CloseServiceHandle(ServiceHandle);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
|||
|
errmsg("Can't Stop NtFrs", GetLastError());
|
|||
|
CloseServiceHandle(ServiceHandle);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
|
|||
|
Sleep(5 * 1000);
|
|||
|
goto STOP_AGAIN;
|
|||
|
}
|
|||
|
if (ServiceStatus.dwCurrentState != SERVICE_STOPPED) {
|
|||
|
fprintf(stderr,
|
|||
|
"Can't stop NtFrs; NtFrs is in state %d\n",
|
|||
|
ServiceStatus.dwCurrentState);
|
|||
|
CloseServiceHandle(ServiceHandle);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// SUCCESS
|
|||
|
//
|
|||
|
CloseServiceHandle(ServiceHandle);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
////////
|
|||
|
//
|
|||
|
// BEGIN code that processes -restore option
|
|||
|
//
|
|||
|
///////
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
UpgSetLastNTError(
|
|||
|
NTSTATUS Status
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Translate NT status codes to WIN32 status codes for those functions that
|
|||
|
make NT calls. Map a few status values differently.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Status - the NTstatus to map.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The WIN32 status. Also puts this into LastError.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG WStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Do the standard system mapping first.
|
|||
|
//
|
|||
|
WStatus = RtlNtStatusToDosError( Status );
|
|||
|
SetLastError( WStatus );
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
UpgGetFileInfoByHandle(
|
|||
|
IN PWCHAR Name,
|
|||
|
IN HANDLE Handle,
|
|||
|
OUT PFILE_NETWORK_OPEN_INFORMATION FileOpenInfo
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Return the network file info for the specified handle.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Name - File's name for printing error messages
|
|||
|
|
|||
|
Handle - Open file handle
|
|||
|
|
|||
|
FileOpenInfo - Returns the file FILE_NETWORK_OPEN_INFORMATION data.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - FileOpenInfo contains the file's info
|
|||
|
FALSE - Contents of FileOpenInfo is undefined
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Return some file info
|
|||
|
//
|
|||
|
NtStatus = NtQueryInformationFile(Handle,
|
|||
|
&IoStatusBlock,
|
|||
|
FileOpenInfo,
|
|||
|
sizeof(FILE_NETWORK_OPEN_INFORMATION),
|
|||
|
FileNetworkOpenInformation);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
UpgSetFileAttributes(
|
|||
|
PWCHAR Name,
|
|||
|
HANDLE Handle,
|
|||
|
ULONG FileAttributes
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
This routine sets the file's attributes
|
|||
|
|
|||
|
Arguments:
|
|||
|
Name - for error messages
|
|||
|
Handle - Supplies a handle to the file that is to be marked for delete.
|
|||
|
Attributes - Attributes for the file
|
|||
|
Return Value:
|
|||
|
TRUE - attributes have been set
|
|||
|
FALSE
|
|||
|
--*/
|
|||
|
{
|
|||
|
IO_STATUS_BLOCK IoStatus;
|
|||
|
FILE_BASIC_INFORMATION BasicInformation;
|
|||
|
DWORD WStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Set the attributes
|
|||
|
//
|
|||
|
ZeroMemory(&BasicInformation, sizeof(BasicInformation));
|
|||
|
BasicInformation.FileAttributes = FileAttributes | FILE_ATTRIBUTE_NORMAL;
|
|||
|
NtStatus = NtSetInformationFile(Handle,
|
|||
|
&IoStatus,
|
|||
|
&BasicInformation,
|
|||
|
sizeof(BasicInformation),
|
|||
|
FileBasicInformation);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
UpgResetAttributesForDelete(
|
|||
|
PWCHAR Name,
|
|||
|
HANDLE Handle
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
This routine turns off the attributes that prevent deletion
|
|||
|
|
|||
|
Arguments:
|
|||
|
Name - for error messages
|
|||
|
Handle - Supplies a handle to the file that is to be marked for delete.
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
FILE_NETWORK_OPEN_INFORMATION FileInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Get the file's attributes
|
|||
|
//
|
|||
|
if (!UpgGetFileInfoByHandle(Name, Handle, &FileInfo)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Turn off the access attributes that prevent deletion and write
|
|||
|
//
|
|||
|
if (FileInfo.FileAttributes & NOREPL_ATTRIBUTES) {
|
|||
|
if (!UpgSetFileAttributes(Name, Handle,
|
|||
|
FileInfo.FileAttributes & ~NOREPL_ATTRIBUTES)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpgEnumerateDirectory(
|
|||
|
IN HANDLE DirectoryHandle,
|
|||
|
IN PWCHAR DirectoryName,
|
|||
|
IN DWORD DirectoryLevel,
|
|||
|
IN DWORD DirectoryFlags,
|
|||
|
IN PVOID Context,
|
|||
|
IN PENUMERATE_DIRECTORY_ROUTINE Function
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Enumerate the directory identified by DirectoryHandle, passing each
|
|||
|
directory record to Function. If the record is for a directory,
|
|||
|
call Function before recursing if ProcessBeforeCallingFunction
|
|||
|
is TRUE.
|
|||
|
|
|||
|
Function controls the enumeration of the CURRENT directory
|
|||
|
by setting ContinueEnumeration to TRUE (continue) or
|
|||
|
FALSE (terminate).
|
|||
|
|
|||
|
Function controls the enumeration of the entire directory
|
|||
|
tree by returning a WIN32 STATUS that is not ERROR_SUCCESS.
|
|||
|
|
|||
|
UpgEnumerateDirectory() will terminate the entire directory
|
|||
|
enumeration by returning a WIN32 STATUS other than ERROR_SUCCESS
|
|||
|
when encountering an error.
|
|||
|
|
|||
|
Context passes global info from the caller to Function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DirectoryHandle - Handle for this directory.
|
|||
|
DirectoryName - Relative name of directory
|
|||
|
DirectoryLevel - Directory level
|
|||
|
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
|
|||
|
Context - Passes global info from the caller to Function
|
|||
|
Function - Called for every record
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
WIN32 STATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
BOOL Recurse;
|
|||
|
PFILE_DIRECTORY_INFORMATION DirectoryRecord;
|
|||
|
PFILE_DIRECTORY_INFORMATION DirectoryBuffer = NULL;
|
|||
|
BOOLEAN RestartScan = TRUE;
|
|||
|
PWCHAR FileName = NULL;
|
|||
|
DWORD FileNameLength = 0;
|
|||
|
DWORD NumBuffers = 0;
|
|||
|
DWORD NumRecords = 0;
|
|||
|
UNICODE_STRING ObjectName;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// The buffer size is configurable with registry value
|
|||
|
// ENUMERATE_DIRECTORY_SIZE
|
|||
|
//
|
|||
|
DirectoryBuffer = (PFILE_DIRECTORY_INFORMATION)malloc(DEFAULT_ENUMERATE_DIRECTORY_SIZE);
|
|||
|
|
|||
|
if (DirectoryBuffer == NULL) {
|
|||
|
WStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory(DirectoryBuffer, DEFAULT_ENUMERATE_DIRECTORY_SIZE);
|
|||
|
|
|||
|
NEXT_BUFFER:
|
|||
|
//
|
|||
|
// READ A BUFFER FULL OF DIRECTORY INFORMATION
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = NtQueryDirectoryFile(DirectoryHandle, // Directory Handle
|
|||
|
NULL, // Event
|
|||
|
NULL, // ApcRoutine
|
|||
|
NULL, // ApcContext
|
|||
|
&IoStatusBlock,
|
|||
|
DirectoryBuffer,
|
|||
|
DEFAULT_ENUMERATE_DIRECTORY_SIZE,
|
|||
|
FileDirectoryInformation,
|
|||
|
FALSE, // return single entry
|
|||
|
NULL, // FileName
|
|||
|
RestartScan // restart scan
|
|||
|
);
|
|||
|
//
|
|||
|
// Enumeration Complete
|
|||
|
//
|
|||
|
if (NtStatus == STATUS_NO_MORE_FILES) {
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Error enumerating directory; return to caller
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
errmsg(DirectoryName, WStatus);
|
|||
|
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
|
|||
|
//
|
|||
|
// Don't abort the entire enumeration; just this directory
|
|||
|
//
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
++NumBuffers;
|
|||
|
|
|||
|
//
|
|||
|
// PROCESS DIRECTORY RECORDS
|
|||
|
//
|
|||
|
DirectoryRecord = DirectoryBuffer;
|
|||
|
NEXT_RECORD:
|
|||
|
|
|||
|
++NumRecords;
|
|||
|
|
|||
|
//
|
|||
|
// Filter . and ..
|
|||
|
//
|
|||
|
if (DirectoryRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|||
|
|
|||
|
//
|
|||
|
// Skip .
|
|||
|
//
|
|||
|
if (DirectoryRecord->FileNameLength == 2 &&
|
|||
|
DirectoryRecord->FileName[0] == L'.') {
|
|||
|
goto ADVANCE_TO_NEXT_RECORD;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Skip ..
|
|||
|
//
|
|||
|
if (DirectoryRecord->FileNameLength == 4 &&
|
|||
|
DirectoryRecord->FileName[0] == L'.' &&
|
|||
|
DirectoryRecord->FileName[1] == L'.') {
|
|||
|
goto ADVANCE_TO_NEXT_RECORD;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add a terminating NULL to the FileName (painful)
|
|||
|
//
|
|||
|
if (FileNameLength < DirectoryRecord->FileNameLength + sizeof(WCHAR)) {
|
|||
|
FREE(FileName);
|
|||
|
FileNameLength = DirectoryRecord->FileNameLength + sizeof(WCHAR);
|
|||
|
FileName = (PWCHAR)malloc(FileNameLength);
|
|||
|
if (FileName == NULL) {
|
|||
|
WStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
CopyMemory(FileName, DirectoryRecord->FileName, DirectoryRecord->FileNameLength);
|
|||
|
|
|||
|
FileName[DirectoryRecord->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Process the record
|
|||
|
//
|
|||
|
WStatus = (*Function)(DirectoryHandle,
|
|||
|
DirectoryName,
|
|||
|
DirectoryLevel,
|
|||
|
DirectoryRecord,
|
|||
|
DirectoryFlags,
|
|||
|
FileName,
|
|||
|
Context);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
|
|||
|
//
|
|||
|
// Don't abort the entire enumeration; just this entry
|
|||
|
//
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Abort the entire enumeration
|
|||
|
//
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ADVANCE_TO_NEXT_RECORD:
|
|||
|
//
|
|||
|
// Next record
|
|||
|
//
|
|||
|
if (DirectoryRecord->NextEntryOffset) {
|
|||
|
DirectoryRecord = (PFILE_DIRECTORY_INFORMATION)(((PCHAR)DirectoryRecord) + DirectoryRecord->NextEntryOffset);
|
|||
|
goto NEXT_RECORD;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with this buffer; go get another one
|
|||
|
// But don't restart the scan for every loop!
|
|||
|
//
|
|||
|
RestartScan = FALSE;
|
|||
|
goto NEXT_BUFFER;
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
FREE(FileName);
|
|||
|
FREE(DirectoryBuffer);
|
|||
|
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpgEnumerateDirectoryRecurse(
|
|||
|
IN HANDLE DirectoryHandle,
|
|||
|
IN PWCHAR DirectoryName,
|
|||
|
IN DWORD DirectoryLevel,
|
|||
|
IN PFILE_DIRECTORY_INFORMATION DirectoryRecord,
|
|||
|
IN DWORD DirectoryFlags,
|
|||
|
IN PWCHAR FileName,
|
|||
|
IN HANDLE FileHandle,
|
|||
|
IN PVOID Context,
|
|||
|
IN PENUMERATE_DIRECTORY_ROUTINE Function
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Open the directory identified by FileName in the directory
|
|||
|
identified by DirectoryHandle and call UpgEnumerateDirectory().
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DirectoryHandle - Handle for this directory.
|
|||
|
DirectoryName - Relative name of directory
|
|||
|
DirectoryLevel - Directory level
|
|||
|
DirectoryRecord - From UpgEnumerateRecord()
|
|||
|
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
|
|||
|
FileName - Open this directory and recurse
|
|||
|
FileHandle - Use for FileName if not INVALID_HANDLE_VALUE
|
|||
|
Context - Passes global info from the caller to Function
|
|||
|
Function - Called for every record
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
WIN32 STATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
HANDLE LocalHandle = INVALID_HANDLE_VALUE;
|
|||
|
UNICODE_STRING ObjectName;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Relative open
|
|||
|
//
|
|||
|
if (!HANDLE_IS_VALID(FileHandle)) {
|
|||
|
ZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
|
|||
|
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
|
|||
|
ObjectName.Length = (USHORT)DirectoryRecord->FileNameLength;
|
|||
|
ObjectName.MaximumLength = (USHORT)DirectoryRecord->FileNameLength;
|
|||
|
ObjectName.Buffer = DirectoryRecord->FileName;
|
|||
|
ObjectAttributes.ObjectName = &ObjectName;
|
|||
|
ObjectAttributes.RootDirectory = DirectoryHandle;
|
|||
|
NtStatus = NtCreateFile(&LocalHandle,
|
|||
|
READ_ACCESS,
|
|||
|
&ObjectAttributes,
|
|||
|
&IoStatusBlock,
|
|||
|
NULL, // AllocationSize
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
FILE_OPEN,
|
|||
|
FILE_OPEN_REPARSE_POINT |
|
|||
|
FILE_SEQUENTIAL_ONLY |
|
|||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|||
|
NULL, // EA buffer
|
|||
|
0 // EA buffer size
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Error opening directory
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
errmsg(FileName, WStatus);
|
|||
|
if (DirectoryFlags & ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE) {
|
|||
|
//
|
|||
|
// Skip this directory tree
|
|||
|
//
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
FileHandle = LocalHandle;
|
|||
|
}
|
|||
|
//
|
|||
|
// RECURSE
|
|||
|
//
|
|||
|
WStatus = UpgEnumerateDirectory(FileHandle,
|
|||
|
FileName,
|
|||
|
DirectoryLevel + 1,
|
|||
|
DirectoryFlags,
|
|||
|
Context,
|
|||
|
Function);
|
|||
|
CLEANUP:
|
|||
|
FRS_CLOSE(LocalHandle);
|
|||
|
|
|||
|
return WStatus;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpgDeleteByHandle(
|
|||
|
IN PWCHAR Name,
|
|||
|
IN HANDLE Handle
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
This routine marks a file for delete, so that when the supplied handle
|
|||
|
is closed, the file will actually be deleted.
|
|||
|
|
|||
|
Arguments:
|
|||
|
Name - for error messages
|
|||
|
Handle - Supplies a handle to the file that is to be marked for delete.
|
|||
|
|
|||
|
Return Value:
|
|||
|
Win Status
|
|||
|
--*/
|
|||
|
{
|
|||
|
FILE_DISPOSITION_INFORMATION DispositionInformation;
|
|||
|
IO_STATUS_BLOCK IoStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
DWORD WStatus;
|
|||
|
|
|||
|
if (!HANDLE_IS_VALID(Handle)) {
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Mark the file for delete. The delete happens when the handle is closed.
|
|||
|
//
|
|||
|
#undef DeleteFile
|
|||
|
DispositionInformation.DeleteFile = TRUE;
|
|||
|
NtStatus = NtSetInformationFile(Handle,
|
|||
|
&IoStatus,
|
|||
|
&DispositionInformation,
|
|||
|
sizeof(DispositionInformation),
|
|||
|
FileDispositionInformation);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpgEnumerateDirectoryDeleteWorker(
|
|||
|
IN HANDLE DirectoryHandle,
|
|||
|
IN PWCHAR DirectoryName,
|
|||
|
IN DWORD DirectoryLevel,
|
|||
|
IN PFILE_DIRECTORY_INFORMATION DirectoryRecord,
|
|||
|
IN DWORD DirectoryFlags,
|
|||
|
IN PWCHAR FileName,
|
|||
|
IN PVOID Ignored
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Empty a directory of non-replicating files and dirs if this is
|
|||
|
an ERROR_DIR_NOT_EMPTY and this is a retry change order for a
|
|||
|
directory delete.
|
|||
|
|
|||
|
Arguments:
|
|||
|
DirectoryHandle - Handle for this directory.
|
|||
|
DirectoryName - Relative name of directory
|
|||
|
DirectoryLevel - Directory level (0 == root)
|
|||
|
DirectoryFlags - See tablefcn.h, ENUMERATE_DIRECTORY_FLAGS_
|
|||
|
DirectoryRecord - Record from DirectoryHandle
|
|||
|
FileName - From DirectoryRecord (w/terminating NULL)
|
|||
|
Ignored - Context is ignored
|
|||
|
|
|||
|
Return Value:
|
|||
|
Win32 Status
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
HANDLE Handle = INVALID_HANDLE_VALUE;
|
|||
|
UNICODE_STRING ObjectName;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Depth first
|
|||
|
//
|
|||
|
if (DirectoryRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|||
|
WStatus = UpgEnumerateDirectoryRecurse(DirectoryHandle,
|
|||
|
DirectoryName,
|
|||
|
DirectoryLevel,
|
|||
|
DirectoryRecord,
|
|||
|
DirectoryFlags,
|
|||
|
FileName,
|
|||
|
INVALID_HANDLE_VALUE,
|
|||
|
Ignored,
|
|||
|
UpgEnumerateDirectoryDeleteWorker);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Relative open
|
|||
|
//
|
|||
|
ZeroMemory(&ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
|
|||
|
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
|
|||
|
ObjectName.Length = (USHORT)DirectoryRecord->FileNameLength;
|
|||
|
ObjectName.MaximumLength = (USHORT)DirectoryRecord->FileNameLength;
|
|||
|
ObjectName.Buffer = DirectoryRecord->FileName;
|
|||
|
ObjectAttributes.ObjectName = &ObjectName;
|
|||
|
ObjectAttributes.RootDirectory = DirectoryHandle;
|
|||
|
NtStatus = NtCreateFile(&Handle,
|
|||
|
GENERIC_READ |
|
|||
|
SYNCHRONIZE |
|
|||
|
DELETE |
|
|||
|
FILE_WRITE_ATTRIBUTES,
|
|||
|
&ObjectAttributes,
|
|||
|
&IoStatusBlock,
|
|||
|
NULL, // AllocationSize
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
FILE_SHARE_READ |
|
|||
|
FILE_SHARE_WRITE |
|
|||
|
FILE_SHARE_DELETE,
|
|||
|
FILE_OPEN,
|
|||
|
FILE_OPEN_REPARSE_POINT |
|
|||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|||
|
NULL, // EA buffer
|
|||
|
0 // EA buffer size
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Error opening file or directory
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
errmsg(FileName, WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
//
|
|||
|
// Turn off readonly, system, and hidden
|
|||
|
//
|
|||
|
UpgResetAttributesForDelete(FileName, Handle);
|
|||
|
|
|||
|
//
|
|||
|
// Delete the file
|
|||
|
//
|
|||
|
WStatus = UpgDeleteByHandle(FileName, Handle);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
errmsg(FileName, WStatus);
|
|||
|
}
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
FRS_CLOSE(Handle);
|
|||
|
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
UpgOpenDirectoryPath(
|
|||
|
OUT PHANDLE Handle,
|
|||
|
IN PWCHAR lpFileName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function opens the specified directory
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Handle - A pointer to a handle to return an open handle.
|
|||
|
|
|||
|
lpFileName - Represents the name of the directory to be opened.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Win32 Status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
NTSTATUS NtStatus;
|
|||
|
OBJECT_ATTRIBUTES Obja;
|
|||
|
UNICODE_STRING FileName;
|
|||
|
IO_STATUS_BLOCK IoStatusBlock;
|
|||
|
BOOLEAN TranslationStatus;
|
|||
|
RTL_RELATIVE_NAME RelativeName;
|
|||
|
PVOID FreeBuffer;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Convert the Dos name to an NT name.
|
|||
|
//
|
|||
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|||
|
lpFileName,
|
|||
|
&FileName,
|
|||
|
NULL,
|
|||
|
&RelativeName
|
|||
|
);
|
|||
|
|
|||
|
if ( !TranslationStatus ) {
|
|||
|
return ERROR_BAD_PATHNAME;
|
|||
|
}
|
|||
|
|
|||
|
FreeBuffer = FileName.Buffer;
|
|||
|
|
|||
|
if ( RelativeName.RelativeName.Length ) {
|
|||
|
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
|||
|
}
|
|||
|
else {
|
|||
|
RelativeName.ContainingDirectory = NULL;
|
|||
|
}
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&Obja,
|
|||
|
&FileName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
RelativeName.ContainingDirectory,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
NtStatus = NtCreateFile(Handle,
|
|||
|
READ_ACCESS,
|
|||
|
&Obja,
|
|||
|
&IoStatusBlock,
|
|||
|
NULL, // Initial allocation size
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
FILE_SHARE_READ |
|
|||
|
FILE_SHARE_WRITE |
|
|||
|
FILE_SHARE_DELETE,
|
|||
|
FILE_OPEN,
|
|||
|
FILE_SEQUENTIAL_ONLY |
|
|||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|||
|
NULL,
|
|||
|
0);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(NtStatus) ) {
|
|||
|
*Handle = INVALID_HANDLE_VALUE;
|
|||
|
WStatus = UpgSetLastNTError(NtStatus);
|
|||
|
} else {
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
|||
|
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
DeleteTree(
|
|||
|
IN PWCHAR Path
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Delete the contents of Path
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Path - directory path
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
WIN32 STATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
HANDLE FileHandle = INVALID_HANDLE_VALUE;
|
|||
|
|
|||
|
//
|
|||
|
// Done deleting nothing
|
|||
|
//
|
|||
|
if (Path == NULL) {
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open directory
|
|||
|
//
|
|||
|
WStatus = UpgOpenDirectoryPath(&FileHandle, Path);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
if (WStatus == ERROR_FILE_NOT_FOUND) {
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
errmsg(Path, WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// RECURSE
|
|||
|
//
|
|||
|
WStatus = UpgEnumerateDirectory(FileHandle,
|
|||
|
Path,
|
|||
|
1,
|
|||
|
ENUMERATE_DIRECTORY_FLAGS_ERROR_CONTINUE,
|
|||
|
NULL,
|
|||
|
UpgEnumerateDirectoryDeleteWorker);
|
|||
|
CLEANUP:
|
|||
|
FRS_CLOSE(FileHandle);
|
|||
|
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PWCHAR
|
|||
|
GetConfigString(
|
|||
|
IN HKEY HKey,
|
|||
|
IN PWCHAR Param
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function reads a keyword value from the registry.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
HKey - Key to be read
|
|||
|
|
|||
|
Param - item for which we want the value
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
String or NULL. Free with free().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD WStatus;
|
|||
|
DWORD Type;
|
|||
|
DWORD Len;
|
|||
|
DWORD Bytes;
|
|||
|
PWCHAR RetValue;
|
|||
|
WCHAR Value[MAX_PATH + 1];
|
|||
|
|
|||
|
//
|
|||
|
// Read the value
|
|||
|
//
|
|||
|
Len = sizeof(Value);
|
|||
|
WStatus = RegQueryValueEx(HKey, Param, NULL, &Type, (PUCHAR)&Value[0], &Len);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Duplicate the string
|
|||
|
//
|
|||
|
if (Type == REG_SZ) {
|
|||
|
Bytes = (wcslen(Value) + 1) * sizeof(WCHAR);
|
|||
|
RetValue = (PWCHAR)malloc(Bytes);
|
|||
|
if (RetValue != NULL) {
|
|||
|
CopyMemory(RetValue, Value, Bytes);
|
|||
|
}
|
|||
|
return RetValue;
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// RestoreFRS
|
|||
|
//
|
|||
|
DWORD
|
|||
|
RestoreFRS( IN BOOLEAN IsYes )
|
|||
|
{
|
|||
|
DWORD WStatus = ERROR_SUCCESS;
|
|||
|
DWORD KeyIdx;
|
|||
|
DWORD SysvolReady;
|
|||
|
HKEY HKey = 0;
|
|||
|
HKEY HSetsKey = 0;
|
|||
|
HKEY HSetKey = 0;
|
|||
|
HKEY HNetKey = 0;
|
|||
|
PWCHAR Value = NULL;
|
|||
|
BOOLEAN DeleteIt = FALSE;
|
|||
|
WCHAR KeyBuf[MAX_PATH + 1];
|
|||
|
CHAR InStr[512];
|
|||
|
PCHAR pInStr;
|
|||
|
|
|||
|
if (!StopFRS()) {
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the NtFrs parameters key
|
|||
|
//
|
|||
|
WStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|||
|
FRS_CONFIG_SECTION,
|
|||
|
0,
|
|||
|
KEY_ALL_ACCESS,
|
|||
|
&HKey);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
errmsg(FRS_CONFIG_SECTION, WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open (or create) the Replica Sets key
|
|||
|
//
|
|||
|
WStatus = RegCreateKey(HKey,
|
|||
|
FRS_SETS_KEY,
|
|||
|
&HSetsKey);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
errmsg(FRS_SETS_KEY, WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
Value = GetConfigString(HSetsKey, JET_PATH);
|
|||
|
if (!Value) {
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
//
|
|||
|
// We have a jet path; see if the user wants to
|
|||
|
// delete it.
|
|||
|
//
|
|||
|
ASK_STATE_AGAIN:
|
|||
|
if (Value) {
|
|||
|
//
|
|||
|
// The user did not specify yes to all so query
|
|||
|
//
|
|||
|
if (!IsYes) {
|
|||
|
|
|||
|
//
|
|||
|
// Describe the situation
|
|||
|
//
|
|||
|
printf("\nDelete File Replication Service state? [yn] ");
|
|||
|
//
|
|||
|
// Query the user
|
|||
|
//
|
|||
|
pInStr = gets(InStr);
|
|||
|
printf("\n");
|
|||
|
//
|
|||
|
// Query resulted in NULL?
|
|||
|
//
|
|||
|
if (!pInStr) {
|
|||
|
//
|
|||
|
// Eof; query again
|
|||
|
//
|
|||
|
if (feof(stdin)) {
|
|||
|
goto ASK_STATE_AGAIN;
|
|||
|
}
|
|||
|
//
|
|||
|
// Error; exit
|
|||
|
//
|
|||
|
if (ferror(stdin)) {
|
|||
|
fprintf(stderr, "Error reading stdin.\n");
|
|||
|
WStatus = ERROR_OPERATION_ABORTED;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Accept only y or n
|
|||
|
//
|
|||
|
if (*pInStr != 'y' &&
|
|||
|
*pInStr != 'Y' &&
|
|||
|
*pInStr != 'n' &&
|
|||
|
*pInStr != 'N') {
|
|||
|
goto ASK_STATE_AGAIN;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If yes to all or a query resulted in yes; delete root
|
|||
|
//
|
|||
|
if (IsYes || *pInStr == 'y' || *pInStr == 'Y') {
|
|||
|
WStatus = DeleteTree(Value);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
//
|
|||
|
// Access the netlogon\parameters key
|
|||
|
//
|
|||
|
WStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|||
|
NETLOGON_SECTION,
|
|||
|
0,
|
|||
|
KEY_ALL_ACCESS,
|
|||
|
&HNetKey);
|
|||
|
if (WIN_SUCCESS(WStatus)) {
|
|||
|
//
|
|||
|
// Tell NetLogon to stop sharing the sysvol
|
|||
|
//
|
|||
|
SysvolReady = 0;
|
|||
|
WStatus = RegSetValueEx(HNetKey,
|
|||
|
SYSVOL_READY,
|
|||
|
0,
|
|||
|
REG_DWORD,
|
|||
|
(PUCHAR)&SysvolReady,
|
|||
|
sizeof(DWORD));
|
|||
|
}
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
WStatus = ERROR_OPERATION_ABORTED;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
FREE(Value);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Delete the subkeys
|
|||
|
//
|
|||
|
KeyIdx = 0;
|
|||
|
do {
|
|||
|
WStatus = RegEnumKey(HSetsKey, KeyIdx, KeyBuf, MAX_PATH + 1);
|
|||
|
if (WIN_SUCCESS(WStatus)) {
|
|||
|
//
|
|||
|
// Open (or create) a replica set key
|
|||
|
//
|
|||
|
WStatus = RegCreateKey(HSetsKey,
|
|||
|
KeyBuf,
|
|||
|
&HSetKey);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
errmsg(KeyBuf, WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
} else {
|
|||
|
Value = GetConfigString(HSetKey, REPLICA_SET_ROOT);
|
|||
|
//
|
|||
|
// We have a root path; see if the user wants to
|
|||
|
// delete it.
|
|||
|
//
|
|||
|
ASK_AGAIN:
|
|||
|
if (Value) {
|
|||
|
//
|
|||
|
// The user did not specify yes to all so query
|
|||
|
//
|
|||
|
if (!IsYes) {
|
|||
|
//
|
|||
|
// Describe the situation
|
|||
|
//
|
|||
|
printf(
|
|||
|
"\nThe contents of the replicated directory %ws \n"
|
|||
|
"will be deleted with the expectation that the data \n"
|
|||
|
"can be retrieved from a replication partner at a later \n"
|
|||
|
"time. The contents of %ws should not be deleted \n"
|
|||
|
"if there are no replication partners. \n\n"
|
|||
|
"Are there replication partners that can supply the \n"
|
|||
|
"data for %ws at a later time? [yn] ",
|
|||
|
Value,
|
|||
|
Value,
|
|||
|
Value);
|
|||
|
//
|
|||
|
// Query the user
|
|||
|
//
|
|||
|
pInStr = gets(InStr);
|
|||
|
printf("\n");
|
|||
|
//
|
|||
|
// Query resulted in NULL?
|
|||
|
//
|
|||
|
if (!pInStr) {
|
|||
|
//
|
|||
|
// Eof; query again
|
|||
|
//
|
|||
|
if (feof(stdin)) {
|
|||
|
goto ASK_AGAIN;
|
|||
|
}
|
|||
|
//
|
|||
|
// Error; exit
|
|||
|
//
|
|||
|
if (ferror(stdin)) {
|
|||
|
fprintf(stderr, "Error reading stdin.\n");
|
|||
|
WStatus = ERROR_OPERATION_ABORTED;
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Accept only y or n
|
|||
|
//
|
|||
|
if (*pInStr != 'y' &&
|
|||
|
*pInStr != 'Y' &&
|
|||
|
*pInStr != 'n' &&
|
|||
|
*pInStr != 'N') {
|
|||
|
goto ASK_AGAIN;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If yes to all or a query resulted in yes; delete root
|
|||
|
//
|
|||
|
if (IsYes || *pInStr == 'y' || *pInStr == 'Y') {
|
|||
|
WStatus = DeleteTree(Value);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
}
|
|||
|
FREE(Value);
|
|||
|
}
|
|||
|
}
|
|||
|
if (HSetKey) {
|
|||
|
RegCloseKey(HSetKey);
|
|||
|
HSetKey = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
++KeyIdx;
|
|||
|
} while (WIN_SUCCESS(WStatus));
|
|||
|
|
|||
|
if (WStatus != ERROR_NO_MORE_ITEMS) {
|
|||
|
errmsg(FRS_SETS_KEY, WStatus);
|
|||
|
goto CLEANUP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// SUCCESS
|
|||
|
//
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
|
|||
|
CLEANUP:
|
|||
|
if (HKey) {
|
|||
|
RegCloseKey(HKey);
|
|||
|
}
|
|||
|
if (HSetsKey) {
|
|||
|
RegCloseKey(HSetsKey);
|
|||
|
}
|
|||
|
if (HSetKey) {
|
|||
|
RegCloseKey(HSetKey);
|
|||
|
}
|
|||
|
if (HNetKey) {
|
|||
|
RegCloseKey(HNetKey);
|
|||
|
}
|
|||
|
if (Value) {
|
|||
|
free(Value);
|
|||
|
}
|
|||
|
if (WIN_SUCCESS(WStatus)) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
////////
|
|||
|
//
|
|||
|
// END code that processes -restore option
|
|||
|
//
|
|||
|
///////
|
|||
|
|
|||
|
|
|||
|
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 (!WIN_SUCCESS(WStatus)) {
|
|||
|
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 FixSysvols = FALSE;
|
|||
|
WCHAR SysVolPath[ MAX_PATH ], stage[ MAX_PATH ], root[ MAX_PATH ];
|
|||
|
DWORD pathType;
|
|||
|
BOOLEAN IsRestore = FALSE;
|
|||
|
BOOLEAN IsYes = FALSE;
|
|||
|
WCHAR domainName[512];
|
|||
|
|
|||
|
SysVolPath[0] = 0;
|
|||
|
|
|||
|
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 'F':
|
|||
|
case 'f':
|
|||
|
FixSysvols = TRUE;
|
|||
|
break;
|
|||
|
case 'R':
|
|||
|
case 'r':
|
|||
|
//
|
|||
|
// Prepare for restore by stopping the service and
|
|||
|
// setting a registry value. The registry value
|
|||
|
// will force the service to DELETE ALL FILES
|
|||
|
// AND DIRECTORIES in the known replica sets and
|
|||
|
// then delete the database the very next time the
|
|||
|
// service starts.
|
|||
|
//
|
|||
|
if (!_stricmp(&argv[i][1], "restore")) {
|
|||
|
IsRestore = TRUE;
|
|||
|
} else {
|
|||
|
fprintf( stderr, "Unrecognized option: %s\n\n", argv[i] );
|
|||
|
Usage( argc, argv );
|
|||
|
return 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
case 'Y':
|
|||
|
case 'y':
|
|||
|
IsYes = 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 ) / sizeof(WCHAR) );
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Restore in progress; delete state
|
|||
|
//
|
|||
|
if (IsRestore) {
|
|||
|
return (RestoreFRS(IsYes));
|
|||
|
}
|
|||
|
|
|||
|
if( IsThisADC( domainName ) == FALSE ) {
|
|||
|
fprintf( stderr, "This program can only be run on a DC!\n" );
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
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" );
|
|||
|
}
|
|||
|
printf( " System Volume: %ws\n", SysVolPath );
|
|||
|
|
|||
|
//
|
|||
|
// Create the sysvol tree and share it out, if needed
|
|||
|
//
|
|||
|
if (!FixSysvols) {
|
|||
|
if( !CreateSysVolTree( SysVolPath, IsFirstDCInDomain, domainName ) ||
|
|||
|
!CreateSysVolShare( SysVolPath ) ) {
|
|||
|
|
|||
|
return 1;
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Uncommit and delete old keys
|
|||
|
//
|
|||
|
if (!DeleteRegKeys()) {
|
|||
|
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:
|
|||
|
if (!FixSysvols) {
|
|||
|
fprintf( stderr, "Warning: SYSVOL share path may have been changed.\n" );
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|