/*++ 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 #include #include #include #include #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 ); }