windows-nt/Source/XPSP1/NT/ds/security/dsrole/server/sysvol.c
2020-09-26 16:20:57 +08:00

1965 lines
49 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
sysvol.c
Abstract:
Miscellaneous routines to manage and manipulate the system volume tree
Author:
Mac McLain (MacM) Oct 16, 1997
Environment:
User Mode
Revision History:
--*/
#include <setpch.h>
#include <dssetp.h>
#include <loadfn.h>
#include <ntfrsipi.h>
#include <shlwapi.h>
#include "sysvol.h"
//
// Local function prototypes
//
DWORD
DsRolepCreateSysVolLinks(
IN LPWSTR Path,
IN LPWSTR DnsDomainName
);
DWORD
DsRolepRemoveDirectoryOrLink(
IN LPWSTR Path
);
DWORD
DsRolepTreeCopy(
IN LPWSTR Source,
IN LPWSTR Dest
);
DWORD
DsRolepValidatePath(
IN LPWSTR Path,
IN ULONG ValidationCriteria,
OUT PULONG MatchingCriteria
)
/*++
Routine Description:
This function will validate the path against the specified criteria. This can include
whether it is local or not, whether it is NTFS, etc.
If the function returns success, the MatchingCriteria can be examined to find out which
of the ValidationCriteria are set
Arguments:
Path - Path to validate
ValidationCriteria - What to check for. Refer to DSROLEP_PATH_VALIDATE_*.
MatchingCriteria - This is where the indications of validity are returned. If the path
meets the check, the corresponding bit from the ValidationCriteria is turned on
here.
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
DWORD Info, Flags, Len;
WCHAR PathRoot[ 4 ];
WCHAR Type[ 6 ];
DsRolepLogPrint(( DEB_TRACE,
"Validating path %ws.\n",
Path ));
*MatchingCriteria = 0;
if ( FLAG_ON( ValidationCriteria, DSROLEP_PATH_VALIDATE_EXISTENCE ) ) {
Info = GetFileAttributes( Path );
if ( Info == 0xFFFFFFFF ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_ERROR,
"\tCan't get file attributes (%lu)\n",
Win32Err ));
} else if ( FLAG_ON( Info, FILE_ATTRIBUTE_DIRECTORY ) ) {
*MatchingCriteria |= DSROLEP_PATH_VALIDATE_EXISTENCE;
DsRolepLogPrint(( DEB_TRACE,
"\tPath is a directory\n" ));
} else {
DsRolepLogPrint(( DEB_WARN,
"\tPath is a NOT directory\n" ));
}
}
if ( Win32Err == ERROR_SUCCESS ) {
wcsncpy( PathRoot, Path, 3 );
PathRoot[ 3 ] = UNICODE_NULL;
}
if ( Win32Err == ERROR_SUCCESS &&
FLAG_ON( ValidationCriteria, DSROLEP_PATH_VALIDATE_LOCAL ) ) {
Info = GetDriveType( PathRoot );
if ( Info == DRIVE_FIXED ) {
*MatchingCriteria |= DSROLEP_PATH_VALIDATE_LOCAL;
DsRolepLogPrint(( DEB_TRACE,
"\tPath is on a fixed disk drive.\n" ));
} else {
DsRolepLogPrint(( DEB_WARN,
"\tPath is NOT on a fixed disk drive.\n" ));
}
}
if ( Win32Err == ERROR_SUCCESS &&
FLAG_ON( ValidationCriteria, DSROLEP_PATH_VALIDATE_NTFS ) ) {
if ( GetVolumeInformation( PathRoot, NULL, 0, NULL, &Len,
&Flags, Type, sizeof( Type ) / sizeof( WCHAR ) ) == FALSE ) {
Win32Err = GetLastError();
//
// If we've already failed to validate the information, we'll return ERROR_SUCCESS.
//
if ( *MatchingCriteria != ( ValidationCriteria & ~DSROLEP_PATH_VALIDATE_NTFS ) ) {
Win32Err = ERROR_SUCCESS;
} else {
DsRolepLogPrint(( DEB_TRACE,
"\tCan't determine if path is on an NTFS volume.\n" ));
}
} else {
if ( _wcsicmp( Type, L"NTFS" ) == 0 ) {
*MatchingCriteria |= DSROLEP_PATH_VALIDATE_NTFS;
DsRolepLogPrint(( DEB_TRACE,
"\tPath is on an NTFS volume\n" ));
} else {
DsRolepLogPrint(( DEB_WARN,
"\tPath is NOT on an NTFS volume\n" ));
}
}
}
return( Win32Err );
}
#define DSROLEP_SV_SYSVOL L"sysvol"
#define DSROLEP_SV_DOMAIN L"domain"
#define DSROLEP_SV_STAGING_AREA L"staging areas"
#define DSROLEP_SV_STAGING L"staging"
#define DSROLEP_SV_SCRIPTS L"scripts"
#define DSROLEP_LONG_PATH_PREFIX L"\\\\?\\"
DWORD
DsRolepCreateSysVolPath(
IN LPWSTR Path,
IN LPWSTR DnsDomainName,
IN LPWSTR FrsReplicaServer, OPTIONAL
IN LPWSTR Account,
IN LPWSTR Password,
IN PWSTR Site,
IN BOOLEAN FirstDc
)
/*++
Routine Description:
This function will create the system volume tree for use by NTFRS.
Arguments:
Path - Root path under which to create the system volume tree
DnsDomainName - Dns domain name
FrsReplicaServer - The OPTIONAL name of the server to replicate the sysvol from
Site - Site this Dc is in
FirstDc - If TRUE, this is the first Dc in a domain
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS, Win32Err2;
PWSTR RelativePaths[] = {
DSROLEP_SV_DOMAIN,
DSROLEP_SV_DOMAIN L"\\" DSROLEP_SV_SCRIPTS, // DO NOT CHANGE THIS POSITION without also
// updating ScriptsIndex below
DSROLEP_SV_STAGING_AREA,
DSROLEP_SV_STAGING,
DSROLEP_SV_STAGING L"\\" DSROLEP_SV_DOMAIN,
DSROLEP_SV_SYSVOL // This must always be the last thing
// in the list
};
ULONG ScriptsIndex = 1; // DO NOT CHANGE THIS with out changing the position of the
// DOMAIN\\SCRIPTS entry above
PWSTR CreatePath = NULL, PathEnd = NULL;
PWSTR StagingPath = NULL, StagingPathEnd;
ULONG MaxPathLen, i;
BOOLEAN RootCreated = FALSE;
//
// Make sure the buffer is big enough to hold everything. The
// longest path is the domain root under the staging area
//
MaxPathLen = sizeof( DSROLEP_LONG_PATH_PREFIX ) +
( wcslen( Path ) * sizeof( WCHAR ) ) +
sizeof( WCHAR ) +
sizeof( DSROLEP_SV_STAGING_AREA ) +
sizeof( WCHAR ) +
( ( wcslen( DnsDomainName ) + 1 ) * sizeof( WCHAR ) );
CreatePath = RtlAllocateHeap( RtlProcessHeap(), 0, MaxPathLen );
if ( CreatePath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
//
// The path exceeds max path, so prepend the \\?\ that allows
// for paths greater than max path
//
if ( MaxPathLen > MAX_PATH * sizeof( WCHAR ) ) {
swprintf( CreatePath,
L"\\\\?\\%ws",
Path );
} else {
wcscpy( CreatePath, Path );
}
}
//
// Create the root path, if it doesn't exist
//
if ( Win32Err == ERROR_SUCCESS ) {
PathEnd = CreatePath + wcslen( CreatePath );
if ( CreateDirectory( CreatePath, NULL ) == FALSE ) {
Win32Err = GetLastError();
if ( Win32Err == ERROR_ALREADY_EXISTS) {
//
// The path exists, so delete it...
//
DsRolepLogPrint(( DEB_TRACE,
"Deleting current sysvol path %ws \n",
CreatePath ));
Win32Err = DsRolepDelnodePath( CreatePath,
MaxPathLen,
FALSE );
if ( Win32Err == ERROR_INVALID_PARAMETER ) {
Win32Err = ERROR_SUCCESS;
}
} else if ( Win32Err == ERROR_ACCESS_DENIED && PathIsRoot(CreatePath) ){
//The sysvol cannot be path at a root directry (i.e. d:\)
//note: d:\sysvol would be legal
DSROLEP_FAIL0( Win32Err, DSROLERES_FAILED_SYSVOL_CANNOT_BE_ROOT_DIRECTORY )
goto Exit;
} else {
DsRolepLogPrint(( DEB_TRACE,
"Failed to create path %ws: %lu\n",
CreatePath,
Win32Err ));
}
} else {
RootCreated = TRUE;
}
}
if ( Win32Err == ERROR_SUCCESS ) {
*PathEnd = L'\\';
PathEnd++;
} else {
//
// Bail, with a specific error
//
DSROLEP_FAIL0( Win32Err, DSROLERES_SYSVOL_DIR_ERROR )
goto Exit;
}
//
// Now, create the rest of the paths...
//
for ( i = 0;
i < sizeof( RelativePaths ) / sizeof( PWSTR ) &&
Win32Err == ERROR_SUCCESS;
i++ ) {
//
// Only create the scripts directory on the first dc
//
if ( i == ScriptsIndex && !FirstDc ) {
continue;
}
wcscpy( PathEnd, RelativePaths[ i ] );
if( CreateDirectory( CreatePath, NULL ) == FALSE ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_TRACE,
"Failed to create path %ws: %lu\n",
CreatePath,
Win32Err ));
break;
}
}
//
// Then, create the symbolic links
//
if ( Win32Err == ERROR_SUCCESS ) {
*PathEnd = UNICODE_NULL;
Win32Err = DsRolepCreateSysVolLinks( Path, DnsDomainName );
}
//
// Prepare for replication of sysvol
//
if ( Win32Err == ERROR_SUCCESS ) {
//
// Make sure the path for the staging area is large enough
//
StagingPath = RtlAllocateHeap( RtlProcessHeap(), 0, MaxPathLen );
if ( StagingPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
//
// The path exceeds max path, so prepend the \\?\ that allows
// for paths greater than max path
//
swprintf( StagingPath,
L"\\\\?\\%ws",
Path );
}
if ( Win32Err == ERROR_SUCCESS ) {
StagingPathEnd = StagingPath + wcslen( StagingPath );
if ( *StagingPathEnd != L'\\' ) {
*StagingPathEnd = L'\\';
StagingPathEnd++;
}
DSROLE_GET_SETUP_FUNC( Win32Err, DsrNtFrsApi_PrepareForPromotionW );
if ( Win32Err == ERROR_SUCCESS ) {
ASSERT( DsrNtFrsApi_PrepareForPromotionW );
Win32Err = ( *DsrNtFrsApi_PrepareForPromotionW )( DsRolepStringErrorUpdateCallback );
if ( Win32Err == ERROR_SUCCESS ) {
//
// Build the domain sysvol
//
swprintf( StagingPathEnd,
L"%ws\\%ws",
DSROLEP_SV_STAGING_AREA,
DnsDomainName );
swprintf( PathEnd,
L"%ws\\%ws",
DSROLEP_SV_SYSVOL,
DnsDomainName );
Win32Err = ( *DsrNtFrsApi_StartPromotionW )(
FrsReplicaServer,
Account,
Password,
DsRolepStringUpdateCallback,
DsRolepStringErrorUpdateCallback,
DnsDomainName,
NTFRSAPI_REPLICA_SET_TYPE_DOMAIN,
FirstDc,
StagingPath,
CreatePath );
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"NtFrsApi_StartPromotionW on %ws / %ws / %ws failed with %lu\n",
DnsDomainName,
StagingPath,
CreatePath,
Win32Err ));
Win32Err2 = DsRolepFinishSysVolPropagation( FALSE, TRUE );
ASSERT( Win32Err2 == ERROR_SUCCESS );
}
} else {
DsRolepLogPrint(( DEB_ERROR,
"NtFrsApi_PrepareForPromotionW failed with %lu\n",
Win32Err ));
}
}
}
}
//
// If something failed, delete the created sysvol tree
//
if ( Win32Err != ERROR_SUCCESS ) {
Win32Err2 = DsRolepDelnodePath( CreatePath,
MaxPathLen,
RootCreated );
if ( Win32Err2 != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_TRACE,
"Failed to delete path %ws: %lu\n",
CreatePath,
Win32Err2 ));
}
}
Exit:
//
// Free the path buffers if allocated
//
if ( CreatePath ) {
RtlFreeHeap( RtlProcessHeap(), 0, CreatePath );
}
if ( StagingPath ) {
RtlFreeHeap( RtlProcessHeap(), 0, StagingPath );
}
return( Win32Err );
}
DWORD
DsRolepRemoveSysVolPath(
IN LPWSTR Path,
IN LPWSTR DnsDomainName,
IN GUID *DomainGuid
)
/*++
Routine Description:
This function will remote the create system volume tree
Arguments:
Path - Root path under which to create the system volume tree
DnsDomainName - Dns domain name
DomainGuid - The Guid of the new domain
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
//
// If we can't reset the FRS domain guid, do NOT remove the tree. Otherwise, this
// delete will propagate around!
//
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = DsRolepDelnodePath( Path, ( wcslen( Path ) + 1 ) * sizeof( WCHAR ), TRUE );
}
return( Win32Err );
}
#define DSROLEP_ALL_STR L"\\*.*"
DWORD
DsRolepDelnodePath(
IN LPWSTR Path,
IN ULONG BufferSize,
IN BOOLEAN DeleteRoot
)
/*++
Routine Description:
This function removes the specified file path
Arguments:
Path - Root path to delete
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
WIN32_FIND_DATA FindData;
HANDLE FindHandle = INVALID_HANDLE_VALUE;
ULONG Len, PathLen = wcslen( Path );
PWSTR FullPath, FindPath;
WCHAR PathBuff[ MAX_PATH + 1];
//
// See if we need to allocate a buffer
//
Len = sizeof( DSROLEP_ALL_STR ) + ( PathLen * sizeof( WCHAR ) );
if ( BufferSize >= Len ) {
FindPath = Path;
wcscat( FindPath, DSROLEP_ALL_STR );
} else {
FindPath = RtlAllocateHeap( RtlProcessHeap(), 0, Len );
if ( FindPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
swprintf( FindPath, L"%ws%ws", Path, DSROLEP_ALL_STR );
}
}
if ( Win32Err == ERROR_SUCCESS ) {
FindHandle = FindFirstFile( FindPath, &FindData );
if ( FindHandle == INVALID_HANDLE_VALUE ) {
Win32Err = GetLastError();
//
// If we get back a path not found error, it's probably a link that we delete the
// supporting storage for. This is not considered an error.
//
if ( Win32Err == ERROR_PATH_NOT_FOUND ) {
Win32Err = ERROR_NO_MORE_FILES;
}
if ( Win32Err != ERROR_SUCCESS && Win32Err != ERROR_NO_MORE_FILES ) {
DsRolepLogPrint(( DEB_ERROR,
"FindFirstFile on %ws failed with %lu\n",
FindPath, Win32Err ));
}
}
}
while ( Win32Err == ERROR_SUCCESS ) {
if ( wcscmp( FindData.cFileName, L"." ) &&
wcscmp( FindData.cFileName, L".." ) ) {
Len = ( wcslen( FindData.cFileName ) + 1 + PathLen + 1 ) * sizeof( WCHAR );
if ( Len > sizeof( FullPath ) ) {
FullPath = RtlAllocateHeap( RtlProcessHeap(), 0, Len );
if ( FullPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
FullPath = PathBuff;
}
if ( Win32Err == ERROR_SUCCESS ) {
Path[ PathLen ] = UNICODE_NULL;
swprintf( FullPath, L"%ws\\%ws", Path, FindData.cFileName );
if ( FLAG_ON( FindData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY ) ) {
Win32Err = DsRolepDelnodePath( FullPath, Len, TRUE );
} else {
//
// Remove the readonly/hidden bits
//
SetFileAttributes( FullPath,
FILE_ATTRIBUTE_NORMAL );
if ( DeleteFileW( FullPath ) == FALSE ) {
Win32Err = GetLastError();
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"DeleteFileW on %ws failed with %lu\n",
FullPath, Win32Err ));
}
}
}
}
if ( FullPath != PathBuff ) {
RtlFreeHeap( RtlProcessHeap(), 0, FullPath );
}
}
if ( Win32Err == ERROR_SUCCESS ) {
if ( FindNextFile( FindHandle, &FindData ) == FALSE ) {
Win32Err = GetLastError();
}
if ( Win32Err != ERROR_SUCCESS && Win32Err != ERROR_NO_MORE_FILES ) {
DsRolepLogPrint(( DEB_ERROR,
"FindNextFile after on %ws failed with %lu\n",
FindData.cFileName, Win32Err ));
}
}
}
//
// Close the handle before trying to remove the directory
//
if ( FindHandle != INVALID_HANDLE_VALUE ) {
FindClose( FindHandle );
}
if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_NO_MORE_FILES ) {
Win32Err = ERROR_SUCCESS;
}
//
// Remove the directory
//
if ( DeleteRoot && Win32Err == ERROR_SUCCESS ) {
Win32Err = DsRolepRemoveDirectoryOrLink( Path );
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Removal of path %ws failed with %lu\n",
Path, Win32Err ));
}
}
//
// Cleanup
//
if ( FindPath != Path ) {
RtlFreeHeap( RtlProcessHeap(), 0, FindPath );
}
return( Win32Err );
}
DWORD
DsRolepRemoveDirectoryOrLink(
IN LPWSTR Path
)
/*++
Routine Description:
This function removes the symbolic link or directory indicated
Arguments:
Path - Path to remove
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Attributes;
UNICODE_STRING NtPath;
OBJECT_ATTRIBUTES ObjectAttrs;
HANDLE Handle;
IO_STATUS_BLOCK IOSb;
FILE_DISPOSITION_INFORMATION Disposition = {
TRUE
};
Attributes = GetFileAttributes( Path );
Attributes &= ~( FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM |
FILE_ATTRIBUTE_READONLY );
if ( !SetFileAttributes( Path, Attributes ) ) {
return( GetLastError() );
}
//
// Initialize
//
NtPath.Buffer = NULL;
//
// Convert the name
//
if ( RtlDosPathNameToNtPathName_U( Path, &NtPath, NULL, NULL ) == FALSE ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Open the object
//
if ( NT_SUCCESS( Status ) ) {
InitializeObjectAttributes( &ObjectAttrs, &NtPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
Status = NtOpenFile( &Handle,
SYNCHRONIZE | FILE_READ_DATA | DELETE,
&ObjectAttrs,
&IOSb,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_OPEN_REPARSE_POINT |
FILE_SYNCHRONOUS_IO_NONALERT );
if ( NT_SUCCESS( Status ) ) {
Status = NtSetInformationFile( Handle,
&IOSb,
&Disposition,
sizeof( Disposition ),
FileDispositionInformation );
NtClose( Handle );
}
}
//
// Free the memory
//
if ( NtPath.Buffer ) {
RtlFreeUnicodeString( &NtPath );
}
if ( !NT_SUCCESS( Status ) ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to delete %ws: 0x%lx\n",
Path,
Status ));
}
return( RtlNtStatusToDosError( Status ) );
}
#pragma warning(push)
#pragma warning(disable:4701)
DWORD
DsRolepCreateSymLink(
IN LPWSTR LinkPath,
IN LPWSTR LinkValue
)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING Link, Value, DosValue;
OBJECT_ATTRIBUTES ObjectAttrs;
HANDLE Handle;
IO_STATUS_BLOCK IOSb;
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
PCHAR ReparseBuffer = NULL;
ULONG Len;
//
// Initialize
//
Link.Buffer = NULL;
Value.Buffer = NULL;
//
// Convert the names
//
if ( RtlDosPathNameToNtPathName_U( LinkPath, &Link, NULL, NULL ) ) {
if ( RtlDosPathNameToNtPathName_U( LinkValue, &Value, NULL, NULL ) ) {
RtlInitUnicodeString( &DosValue, LinkValue );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Open the object
//
if ( NT_SUCCESS( Status ) ) {
InitializeObjectAttributes( &ObjectAttrs, &Link, OBJ_CASE_INSENSITIVE, NULL, NULL );
Status = NtCreateFile( &Handle,
SYNCHRONIZE | FILE_WRITE_DATA,
&ObjectAttrs,
&IOSb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_OPEN_REPARSE_POINT,
NULL,
0 );
if ( NT_SUCCESS( Status ) ) {
Len = ( FIELD_OFFSET( REPARSE_DATA_BUFFER,
MountPointReparseBuffer.PathBuffer ) -
REPARSE_DATA_BUFFER_HEADER_SIZE ) +
Value.Length + sizeof(UNICODE_NULL) +
DosValue.Length + sizeof(UNICODE_NULL);
ReparseBufferHeader = RtlAllocateHeap( RtlProcessHeap(),
0,
REPARSE_DATA_BUFFER_HEADER_SIZE + Len );
if ( ReparseBufferHeader == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
ReparseBufferHeader->ReparseDataLength = (USHORT)Len;
ReparseBufferHeader->Reserved = 0;
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength =
Value.Length;
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameOffset =
Value.Length + sizeof( UNICODE_NULL );
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength =
DosValue.Length;
RtlCopyMemory( ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer,
Value.Buffer,
Value.Length );
RtlCopyMemory( (PCHAR)(ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer)+
Value.Length + sizeof(UNICODE_NULL),
DosValue.Buffer,
DosValue.Length );
ReparseBufferHeader->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
Status = NtFsControlFile( Handle,
NULL,
NULL,
NULL,
&IOSb,
FSCTL_SET_REPARSE_POINT,
ReparseBufferHeader,
REPARSE_DATA_BUFFER_HEADER_SIZE +
ReparseBufferHeader->ReparseDataLength,
NULL,
0 );
RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
}
NtClose( Handle );
}
}
//
// Free any allocated strings
//
if ( Link.Buffer ) {
RtlFreeUnicodeString( &Link );
}
if ( Value.Buffer ) {
RtlFreeUnicodeString( &Value );
}
if ( !NT_SUCCESS( Status ) ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to create the link between %ws and %ws: 0x%lx\n",
LinkPath,
LinkValue,
Status ));
}
return( RtlNtStatusToDosError( Status ) );
}
#pragma warning(pop)
DWORD
DsRolepCreateSysVolLinks(
IN LPWSTR Path,
IN PWSTR DnsDomainName
)
/*++
Routine Description:
This function creates the symbolic links used by the system volume tree
Arguments:
Path - Root path under which to create the links
DnsDomainName - The Dns domain name of the new domain
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
WCHAR DestPathBuf[ MAX_PATH + 5];
WCHAR LinkPathBuf[ MAX_PATH + 5];
PWSTR DestPath = DestPathBuf, LinkPath = LinkPathBuf;
PWSTR DestPathEnd = NULL, LinkPathEnd = NULL;
ULONG MaxPathLen, DnsDomainNameSize, Len = wcslen( Path );
if ( * ( Path + Len - 1 ) == L'\\' ) {
Len--;
*( Path + Len ) = UNICODE_NULL;
}
//
// The longest destination path is the path\\staging\\DnsDomainName
//
MaxPathLen = (ULONG)(( sizeof( DSROLEP_SV_STAGING L"\\" ) + 1 ) +
( ( wcslen ( DnsDomainName ) + 1 ) * sizeof( WCHAR ) ) +
( ( Len + 5 ) * sizeof( WCHAR ) ));
if ( MaxPathLen > sizeof( DestPathBuf ) / 4 ) {
DestPath = RtlAllocateHeap( RtlProcessHeap(), 0, MaxPathLen );
if ( DestPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
//
// The path exceeds max path, so prepend the \\?\ that allows
// for paths greater than max path
//
swprintf( DestPath,
L"\\\\?\\%ws\\",
Path );
}
} else {
swprintf( DestPath, L"%ws\\", Path );
}
//
// The longest link path is the domain named one
//
if ( Win32Err == ERROR_SUCCESS ) {
DestPathEnd = DestPath + wcslen( DestPath );
DnsDomainNameSize = wcslen( DnsDomainName ) * sizeof( WCHAR );
MaxPathLen = (ULONG)(sizeof( DSROLEP_SV_STAGING_AREA ) + 1 +
sizeof( DSROLEP_SV_SYSVOL ) +
( ( wcslen( Path ) + 5 ) * sizeof( WCHAR ) )+
DnsDomainNameSize);
if ( MaxPathLen > sizeof( LinkPathBuf ) / 4 ) {
LinkPath = RtlAllocateHeap( RtlProcessHeap(), 0, MaxPathLen );
if ( LinkPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
//
// The path exceeds max path, so prepend the \\?\ that allows
// for paths greater than max path
//
swprintf( LinkPath,
L"\\\\?\\%ws\\%ws\\",
Path,
DSROLEP_SV_SYSVOL );
}
} else {
swprintf( LinkPath, L"%ws\\%ws\\", Path, DSROLEP_SV_SYSVOL );
}
}
//
// Then, the domain path
//
if ( Win32Err == ERROR_SUCCESS ) {
LinkPathEnd = LinkPath + wcslen( LinkPath );
wcscpy( DestPathEnd, DSROLEP_SV_DOMAIN );
wcscpy( LinkPathEnd, DnsDomainName );
if ( CreateDirectory( LinkPath, NULL ) == FALSE ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_ERROR,
"Failed to create the link directory %ws: %lu\n",
LinkPath,
Win32Err ));
} else {
Win32Err = DsRolepCreateSymLink( LinkPath, DestPath );
}
}
//
// Finally, the domain link for the staging area.
//
if ( Win32Err == ERROR_SUCCESS ) {
LinkPathEnd--;
while ( *( LinkPathEnd - 1 ) != L'\\' ) {
LinkPathEnd--;
}
swprintf( DestPathEnd, L"%ws\\%ws", DSROLEP_SV_STAGING, DSROLEP_SV_DOMAIN );
swprintf( LinkPathEnd, L"%ws\\%ws", DSROLEP_SV_STAGING_AREA, DnsDomainName );
if ( CreateDirectory( LinkPath, NULL ) == FALSE ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_ERROR,
"Failed to create the link directory %ws: %lu\n",
LinkPath,
Win32Err ));
} else {
Win32Err = DsRolepCreateSymLink( LinkPath, DestPath );
}
}
//
// Clean up any allocated buffers
//
if ( DestPath != DestPathBuf ) {
RtlFreeHeap( RtlProcessHeap(), 0, DestPath );
}
if ( LinkPath != LinkPathBuf ) {
RtlFreeHeap( RtlProcessHeap(), 0, LinkPath );
}
return( Win32Err );
}
#define DSROLEP_FRS_PATH \
L"\\Registry\\Machine\\System\\CurrentControlSet\\services\\NtFrs\\parameters\\sysvol\\"
#define DSROLEP_FRS_COMMAND L"ReplicaSetCommand"
#define DSROLEP_FRS_NAME L"ReplicaSetName"
#define DSROLEP_FRS_TYPE L"ReplicaSetType"
#define DSROLEP_FRS_SITE L"ReplicaSetSite"
#define DSROLEP_FRS_PRIMARY L"ReplicaSetPrimary"
#define DSROLEP_FRS_STAGE L"ReplicationStagePath"
#define DSROLEP_FRS_ROOT L"ReplicationRootPath"
#define DSROLEP_FRS_CREATE L"Create"
#define DSROLEP_FRS_DELETE L"Delete"
#define DSROLEP_NETLOGON_PATH \
L"System\\CurrentControlSet\\services\\Netlogon\\parameters\\"
#define DSROLEP_NETLOGON_SYSVOL L"SysVol"
#define DSROLEP_NETLOGON_SCRIPTS L"Scripts"
DWORD
DsRolepGetNetlogonScriptsPath(
IN HKEY NetlogonHandle,
OUT LPWSTR *ScriptsPath
)
/*++
Routine Description:
This function reads the old netlogon scripts path and expands it to a valid path
Arguments:
NetlogonHandle - Open handle to the netlogon parameters registry key
ScriptsPath -- Where the expanded path is retunred.
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR TempPath = NULL;
ULONG Type, Length = 0;
//
// First, get the current scripts path
//
Win32Err = RegQueryValueEx( NetlogonHandle,
DSROLEP_NETLOGON_SCRIPTS,
0, // reserved
&Type,
0,
&Length );
if ( Win32Err == ERROR_SUCCESS ) {
*ScriptsPath = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
if ( *ScriptsPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
Win32Err = RegQueryValueEx( NetlogonHandle,
DSROLEP_NETLOGON_SCRIPTS,
0,
&Type,
( PBYTE )*ScriptsPath,
&Length );
if ( Win32Err == ERROR_SUCCESS && Type == REG_EXPAND_SZ ) {
Length = ExpandEnvironmentStrings( *ScriptsPath,
TempPath,
0 );
if ( Length == 0 ) {
Win32Err = GetLastError();
} else {
TempPath = RtlAllocateHeap( RtlProcessHeap(), 0,
( Length + 1 ) * sizeof( WCHAR ) );
if ( TempPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
Length = ExpandEnvironmentStrings( *ScriptsPath,
TempPath,
Length );
if ( Length == 0 ) {
Win32Err = GetLastError();
RtlFreeHeap( RtlProcessHeap(), 0, TempPath );
} else {
RtlFreeHeap( RtlProcessHeap(), 0, *ScriptsPath );
*ScriptsPath = TempPath;
}
}
}
}
}
}
return( Win32Err );
}
DWORD
DsRolepSetNetlogonSysVolPath(
IN LPWSTR SysVolRoot,
IN LPWSTR DnsDomainName,
IN BOOLEAN IsUpgrade,
IN OUT PBOOLEAN OkToCleanup
)
/*++
Routine Description:
This function sets the root of the system volume in the Netlogon parameters section
of the registry. The value is set under the key SysVol.
Arguments:
SysVolRoot - Path to the root of the system volume to be set
DnsDomainName - Name of the dns domain name
IsUpgrade - If TRUE, this means that logon scripts are moved
OkToCleanup - A flag is returned here indicating whether the old scripts can be cleaned up
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
HKEY NetlogonHandle = NULL;
PWSTR OldScriptsPath = NULL, NewScriptsPath = NULL, TempPath, FullSysVolPath = NULL;
ULONG Type, Length;
if ( OkToCleanup ) {
*OkToCleanup = FALSE;
}
//
// Build the full scripts path
//
FullSysVolPath = RtlAllocateHeap( RtlProcessHeap(), 0,
( wcslen( SysVolRoot ) + 1 ) * sizeof( WCHAR ) +
sizeof( DSROLEP_SV_SYSVOL ) );
if ( FullSysVolPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
wcscpy( FullSysVolPath, SysVolRoot );
if ( FullSysVolPath[ wcslen( FullSysVolPath ) - 1 ] != L'\\' ) {
wcscat( FullSysVolPath, L"\\" );
}
wcscat( FullSysVolPath, DSROLEP_SV_SYSVOL );
SysVolRoot = FullSysVolPath;
}
//
// Open the netlogon key
//
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
DSROLEP_NETLOGON_PATH,
0,
KEY_READ | KEY_WRITE,
&NetlogonHandle );
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to open %ws: %lu\n", DSROLEP_NETLOGON_PATH, Win32Err ));
return( Win32Err );
}
//
// First, set the sysvol key
//
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = RegSetValueEx( NetlogonHandle,
DSROLEP_NETLOGON_SYSVOL,
0,
REG_SZ,
( CONST PBYTE )SysVolRoot,
( wcslen( SysVolRoot ) + 1 ) * sizeof( WCHAR ) );
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to set %ws: %lu\n", DSROLEP_NETLOGON_SYSVOL, Win32Err ));
}
}
}
//
// If this is an upgrade, move the scripts...
//
if ( Win32Err == ERROR_SUCCESS && IsUpgrade ) {
Win32Err = DsRolepGetNetlogonScriptsPath( NetlogonHandle,
&OldScriptsPath );
if ( Win32Err == ERROR_SUCCESS ) {
//
// Build the new scripts path
//
Length = wcslen( SysVolRoot ) + 1 + wcslen( DnsDomainName ) + 1 +
( sizeof( DSROLEP_NETLOGON_SCRIPTS ) / sizeof( WCHAR ) + 1 );
if ( Length > MAX_PATH ) {
Length += 5;
}
NewScriptsPath = RtlAllocateHeap( RtlProcessHeap(), 0,
Length * sizeof( WCHAR ) );
if ( NewScriptsPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
if ( Length > MAX_PATH ) {
wcscpy( NewScriptsPath, L"\\\\?\\" );
} else {
*NewScriptsPath = UNICODE_NULL;
}
wcscat( NewScriptsPath, SysVolRoot );
if ( NewScriptsPath[ wcslen( SysVolRoot ) - 1 ] != L'\\' ) {
wcscat( NewScriptsPath, L"\\" );
}
wcscat( NewScriptsPath, DnsDomainName );
wcscat( NewScriptsPath, L"\\" );
wcscat( NewScriptsPath, DSROLEP_NETLOGON_SCRIPTS );
}
}
//
// Now, the copy...
//
if ( Win32Err == ERROR_SUCCESS ) {
DSROLEP_CURRENT_OP2( DSROLEEVT_MOVE_SCRIPTS, OldScriptsPath, NewScriptsPath );
Win32Err = DsRolepTreeCopy( OldScriptsPath, NewScriptsPath );
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"DsRolepTreeCopy from %ws to %ws failed with %lu\n",
OldScriptsPath,
NewScriptsPath,
Win32Err ));
}
DSROLEP_CURRENT_OP0( DSROLEEVT_SCRIPTS_MOVED );
}
if ( Win32Err != ERROR_SUCCESS ) {
//
// Raise the an event
//
SpmpReportEvent( TRUE,
EVENTLOG_WARNING_TYPE,
DSROLERES_FAIL_SCRIPT_COPY,
0,
sizeof( ULONG ),
&Win32Err,
2,
OldScriptsPath,
NewScriptsPath );
DSROLEP_SET_NON_FATAL_ERROR( Win32Err );
Win32Err = ERROR_SUCCESS;
}
RtlFreeHeap( RtlProcessHeap(), 0, OldScriptsPath );
RtlFreeHeap( RtlProcessHeap(), 0, NewScriptsPath );
}
if ( OkToCleanup ) {
*OkToCleanup = TRUE;
}
//
// Close the handle
//
if ( NetlogonHandle ) {
RegCloseKey( NetlogonHandle );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullSysVolPath );
return( Win32Err );
}
DWORD
DsRolepCleanupOldNetlogonInformation(
VOID
)
/*++
Routine Description:
This function cleans up the old netlogon scripts information, including deleting the
registry key and deleting the old scripts. It should only be called after netlogon has'
been successfully upgraded
Arguments:
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err, Win32Err2;
HKEY NetlogonHandle = NULL;
PWSTR OldScriptsPath = NULL;
DsRolepLogPrint(( DEB_TRACE,
"Cleaning up old Netlogon information\n"));
//
// Open the netlogon key
//
Win32Err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
DSROLEP_NETLOGON_PATH,
0,
KEY_READ | KEY_WRITE,
&NetlogonHandle );
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to open %ws: %lu\n", DSROLEP_NETLOGON_PATH, Win32Err ));
} else {
Win32Err = DsRolepGetNetlogonScriptsPath( NetlogonHandle,
&OldScriptsPath );
if ( ERROR_FILE_NOT_FOUND == Win32Err) {
Win32Err = ERROR_SUCCESS;
goto cleanup;
}
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = DsRolepDelnodePath( OldScriptsPath, wcslen( OldScriptsPath), FALSE );
}
//
// Finally, delete the scripts key
//
Win32Err2 = RegDeleteValue( NetlogonHandle, DSROLEP_NETLOGON_SCRIPTS );
if ( Win32Err2 != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"Failed to delete registry key %ws: %lu\n",
DSROLEP_NETLOGON_SCRIPTS, Win32Err2 ));
}
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = Win32Err2;
}
}
cleanup:
if ( NetlogonHandle ) {
RegCloseKey( NetlogonHandle );
}
if ( OldScriptsPath ) {
RtlFreeHeap( RtlProcessHeap(), 0, OldScriptsPath );
}
return( Win32Err );
}
DWORD
DsRolepFinishSysVolPropagation(
IN BOOLEAN Commit,
IN BOOLEAN Promote
)
/*++
Routine Description:
This function will commit or abort an NTFRS initial propagation
Arguments:
Commit - If TRUE, the operation is committed. If FALSE, the operation is aborted
Promote - If TRUE, the operation is a promotion. If FALSE, the operation is a demotion
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
if ( Commit ) {
if ( Promote ) {
ASSERT( DsrNtFrsApi_WaitForPromotionW );
Win32Err = ( *DsrNtFrsApi_WaitForPromotionW )( INFINITE,
DsRolepStringErrorUpdateCallback );
if ( Win32Err == ERROR_SUCCESS ) {
ASSERT( DsrNtFrsApi_CommitPromotionW );
Win32Err = ( *DsrNtFrsApi_CommitPromotionW )( INFINITE,
DsRolepStringErrorUpdateCallback );
}
} else {
ASSERT( DsrNtFrsApi_WaitForDemotionW );
Win32Err = ( *DsrNtFrsApi_WaitForDemotionW )( INFINITE,
DsRolepStringErrorUpdateCallback );
if ( Win32Err == ERROR_SUCCESS ) {
ASSERT( DsrNtFrsApi_CommitDemotionW );
Win32Err = ( *DsrNtFrsApi_CommitDemotionW )( INFINITE,
DsRolepStringErrorUpdateCallback );
}
}
} else {
if ( Promote ) {
ASSERT( DsrNtFrsApi_AbortPromotionW );
Win32Err = ( *DsrNtFrsApi_AbortPromotionW )();
} else {
ASSERT( DsrNtFrsApi_AbortDemotionW );
Win32Err = ( *DsrNtFrsApi_AbortDemotionW )();
}
}
if ( Win32Err != ERROR_SUCCESS ) {
DsRolepLogPrint(( DEB_ERROR,
"DsRolepFinishSysVolPropagation (%S %S) failed with %lu\n",
Commit ? "Commit" : "Abort",
Promote ? "Promote" : "Demote",
Win32Err ));
}
return( Win32Err );
}
DWORD
DsRolepAllocAndCopyPath(
IN LPWSTR Source,
IN LPWSTR Component,
OUT LPWSTR *FullPath
)
{
DWORD Win32Err = ERROR_SUCCESS;
ULONG Len = 0;
BOOL ExtPath = FALSE;
Len = wcslen( Source ) + 1 + wcslen( Component ) + 1;
if ( Len > MAX_PATH ) {
Len += 5;
ExtPath = TRUE;
}
*FullPath = RtlAllocateHeap( RtlProcessHeap(), 0, Len * sizeof( WCHAR ) );
if ( *FullPath == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
if ( ExtPath ) {
swprintf( *FullPath, L"\\\\?\\%ws\\%ws", Source, Component );
} else {
swprintf( *FullPath, L"%ws\\%ws", Source, Component );
}
}
return( Win32Err );
}
DWORD
DsRolepTreeCopy(
IN LPWSTR Source,
IN LPWSTR Dest
)
/*++
Routine Description:
This function will do a tree copy from the source directory to the destination
Arguments:
Source - Source dir
Dest - Dest dir
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
WIN32_FIND_DATA FindData;
PWSTR SourcePath = NULL, DestPath = NULL, TempPath;
HANDLE FindHandle = INVALID_HANDLE_VALUE;
//
// Build the path for findfirst/findnext
//
Win32Err = DsRolepAllocAndCopyPath( Source,
L"*.*",
&SourcePath );
if ( Win32Err != ERROR_SUCCESS ) {
goto TreeCopyError;
}
//
// Now, enumerate the paths
//
FindHandle = FindFirstFile( SourcePath, &FindData );
if ( FindHandle == INVALID_HANDLE_VALUE ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_ERROR,
"FindFirstFile on %ws failed with %lu\n",
Source, Win32Err ));
goto TreeCopyError;
}
while ( Win32Err == ERROR_SUCCESS ) {
if ( wcscmp( FindData.cFileName, L"." ) &&
wcscmp( FindData.cFileName, L".." ) ) {
//
// Build the source path
//
Win32Err = DsRolepAllocAndCopyPath( Source,
FindData.cFileName,
&TempPath );
if ( Win32Err == ERROR_SUCCESS ) {
RtlFreeHeap( RtlProcessHeap(), 0, SourcePath );
SourcePath = TempPath;
} else {
goto TreeCopyError;
}
//
// Build the destination path
//
Win32Err = DsRolepAllocAndCopyPath( Dest,
FindData.cFileName,
&TempPath );
if ( Win32Err == ERROR_SUCCESS ) {
RtlFreeHeap( RtlProcessHeap(), 0, DestPath );
DestPath = TempPath;
} else {
goto TreeCopyError;
}
//
// Now, either do the copy, or copy the directory
//
if ( FLAG_ON( FindData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY ) ) {
if ( CreateDirectory( DestPath, NULL ) == FALSE ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_ERROR,
"CreateDirectory on %ws failed with %lu\n",
DestPath, Win32Err ));
} else {
Win32Err = DsRolepTreeCopy( SourcePath, DestPath );
}
} else {
if ( CopyFile( SourcePath, DestPath, FALSE ) == FALSE ) {
Win32Err = GetLastError();
DsRolepLogPrint(( DEB_ERROR,
"CopyFile from %ws to %ws failed with %lu\n",
SourcePath, DestPath, Win32Err ));
}
}
}
if ( Win32Err == ERROR_SUCCESS ) {
if ( FindNextFile( FindHandle, &FindData ) == FALSE ) {
Win32Err = GetLastError();
}
if ( Win32Err != ERROR_SUCCESS && Win32Err != ERROR_NO_MORE_FILES ) {
DsRolepLogPrint(( DEB_ERROR,
"FindNextFile after on %ws failed with %lu\n",
FindData.cFileName, Win32Err ));
}
}
}
TreeCopyError:
//
// Close the handle
//
if ( FindHandle != INVALID_HANDLE_VALUE ) {
FindClose( FindHandle );
}
if ( Win32Err == ERROR_NO_MORE_FILES ) {
Win32Err = ERROR_SUCCESS;
}
//
// Cleanup
//
if ( SourcePath ) {
RtlFreeHeap( RtlProcessHeap(), 0, SourcePath );
}
if ( DestPath ) {
RtlFreeHeap( RtlProcessHeap(), 0, DestPath );
}
return( Win32Err );
}