/*++

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 );
}