/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    fileopcr.c

Abstract:

    This module implements File open and Create APIs for Win32

Author:

    Mark Lucovsky (markl) 25-Sep-1990

Revision History:

--*/


#include "basedll.h"
#include "mountmgr.h"
#include "aclapi.h"
#include "winefs.h"

WCHAR BasepDataAttributeType[] = DATA_ATTRIBUTE_NAME;

typedef BOOL (WINAPI *ENCRYPTFILEWPTR)(LPCWSTR);
typedef BOOL (WINAPI *DECRYPTFILEWPTR)(LPCWSTR, DWORD);

extern const WCHAR AdvapiDllString[] = L"advapi32.dll";

#define BASE_OF_SHARE_MASK 0x00000070
#define TWO56K ( 256 * 1024 )
ULONG
BasepOfShareToWin32Share(
    IN ULONG OfShare
    )
{
    DWORD ShareMode;

    if ( (OfShare & BASE_OF_SHARE_MASK) == OF_SHARE_DENY_READ ) {
        ShareMode = FILE_SHARE_WRITE;
        }
    else if ( (OfShare & BASE_OF_SHARE_MASK) == OF_SHARE_DENY_WRITE ) {
        ShareMode = FILE_SHARE_READ;
        }
    else if ( (OfShare & BASE_OF_SHARE_MASK) == OF_SHARE_DENY_NONE ) {
        ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
        }
    else if ( (OfShare & BASE_OF_SHARE_MASK) == OF_SHARE_EXCLUSIVE ) {
        ShareMode = 0;
        }
    else {
        ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;;
        }
    return ShareMode;
}


typedef DWORD (WINAPI DUPLICATEENCRYPTIONINFOFILE)(
     IN LPCWSTR SrcFileName,
     IN LPCWSTR DstFileName,
     IN DWORD dwCreationDistribution,
     IN DWORD dwAttributes,
     IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
     );

DUPLICATEENCRYPTIONINFOFILE LoadDuplicateEncryptionInfoFile;
DUPLICATEENCRYPTIONINFOFILE *pfnDuplicateEncryptionInfoFile = LoadDuplicateEncryptionInfoFile;

DWORD
WINAPI
LoadDuplicateEncryptionInfoFile(
     IN LPCWSTR SrcFileName,
     IN LPCWSTR DstFileName,
     IN DWORD dwCreationDistribution,
     IN DWORD dwAttributes,
     IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
     )
{
    DUPLICATEENCRYPTIONINFOFILE *pfnTemp;
    HANDLE Advapi32 = NULL;
    BOOL ReturnSuccess = FALSE;
    DWORD ErrorReturn = 0;

    Advapi32 = LoadLibraryW( AdvapiDllString );
    if( Advapi32 == NULL ) {
        return GetLastError();
    }

    pfnTemp = (DUPLICATEENCRYPTIONINFOFILE*)
              GetProcAddress( Advapi32, "DuplicateEncryptionInfoFile" );
    if( pfnTemp == NULL ) {
        return GetLastError();
    }

    pfnDuplicateEncryptionInfoFile = pfnTemp;
    return pfnDuplicateEncryptionInfoFile( SrcFileName,
                                           DstFileName, 
                                           dwCreationDistribution, 
                                           dwAttributes, 
                                           lpSecurityAttributes );

}




PUNICODE_STRING
BaseIsThisAConsoleName(
    PCUNICODE_STRING FileNameString,
    DWORD dwDesiredAccess
    )
{
    PUNICODE_STRING FoundConsoleName;
    ULONG DeviceNameLength;
    ULONG DeviceNameOffset;
    UNICODE_STRING ConString;
    WCHAR sch,ech;

    FoundConsoleName = NULL;
    if ( FileNameString->Length ) {
        sch = FileNameString->Buffer[0];
        ech = FileNameString->Buffer[(FileNameString->Length-1)>>1];

        //
        // if CON, CONOUT$, CONIN$, \\.\CON...
        //
        //

        if ( sch == (WCHAR)'c' || sch == (WCHAR)'C' || sch == (WCHAR)'\\' ||
             ech == (WCHAR)'n' || ech == (WCHAR)'N' || ech == (WCHAR)':' || ech == (WCHAR)'$' ) {


            ConString = *FileNameString;

            DeviceNameLength = RtlIsDosDeviceName_U(ConString.Buffer);
            if ( DeviceNameLength ) {
                DeviceNameOffset = DeviceNameLength >> 16;
                DeviceNameLength &= 0x0000ffff;

                ConString.Buffer = (PWSTR)((PSZ)ConString.Buffer + DeviceNameOffset);
                ConString.Length = (USHORT)DeviceNameLength;
                ConString.MaximumLength = (USHORT)(DeviceNameLength + sizeof(UNICODE_NULL));
                }

            FoundConsoleName = NULL;
            try {

                if (RtlEqualUnicodeString(&ConString,&BaseConsoleInput,TRUE) ) {
                    FoundConsoleName = &BaseConsoleInput;
                    }
                else if (RtlEqualUnicodeString(&ConString,&BaseConsoleOutput,TRUE) ) {
                    FoundConsoleName = &BaseConsoleOutput;
                    }
                else if (RtlEqualUnicodeString(&ConString,&BaseConsoleGeneric,TRUE) ) {
                    if ((dwDesiredAccess & (GENERIC_READ|GENERIC_WRITE)) == GENERIC_READ) {
                        FoundConsoleName = &BaseConsoleInput;
                        }
                    else if ((dwDesiredAccess & (GENERIC_READ|GENERIC_WRITE)) == GENERIC_WRITE){
                        FoundConsoleName = &BaseConsoleOutput;
                        }
                    }
                }
            except (EXCEPTION_EXECUTE_HANDLER) {
                return NULL;
                }
            }
        }
    return FoundConsoleName;
}


DWORD
WINAPI
CopyReparsePoint(
    HANDLE hSourceFile,
    HANDLE hDestinationFile
    )

/*++

Routine Description:

    This is an internal routine that copies a reparse point.

Arguments:

    hSourceFile - Provides a handle to the source file.

    hDestinationFile - Provides a handle to the destination file.

Return Value:

    TRUE - The operation was successful.

    FALSE - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
   NTSTATUS Status;
   IO_STATUS_BLOCK IoStatusBlock;
   PUCHAR ReparseBuffer;
   PREPARSE_DATA_BUFFER ReparseBufferHeader;

   //
   //  Allocate the buffer to set the reparse point.
   //

   ReparseBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
   if ( ReparseBuffer == NULL ) {
       BaseSetLastNTError(STATUS_NO_MEMORY);
       return FALSE;
   }

   //
   // Get the reparse point.
   //

   Status = NtFsControlFile(
                hSourceFile,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                FSCTL_GET_REPARSE_POINT,
                NULL,                                //  Input buffer
                0,                                   //  Input buffer length
                ReparseBuffer,                       //  Output buffer
                MAXIMUM_REPARSE_DATA_BUFFER_SIZE     //  Output buffer length
                );

   if ( !NT_SUCCESS( Status ) ) {
       RtlFreeHeap(RtlProcessHeap(), 0, ReparseBuffer);
       BaseSetLastNTError(Status);
       return FALSE;
       }

   //
   // Decode the reparse point buffer.
   //

   ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;

   //
   // Set the reparse point.
   //

   Status = NtFsControlFile(
                hDestinationFile,
                NULL,
                NULL,
                NULL,
                &IoStatusBlock,
                FSCTL_SET_REPARSE_POINT,
                ReparseBuffer,
                FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + ReparseBufferHeader->ReparseDataLength,
                NULL,
                0
                );

   RtlFreeHeap(RtlProcessHeap(), 0, ReparseBuffer);

   if ( !NT_SUCCESS( Status ) ) {
       BaseSetLastNTError(Status);
       return FALSE;
       }

   return TRUE;
}


DWORD
WINAPI
CopyNameGraftNow(
    HANDLE hSourceFile,
    LPCWSTR lpExistingFileName,
    LPCWSTR lpNewFileName,
    ULONG CreateOptions,
    LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
    LPVOID lpData OPTIONAL,
    LPBOOL pbCancel OPTIONAL,
    LPDWORD lpCopyFlags
    )

/*++

Routine Description:

    This is an internal routine that copies a name grafting file/directory preserving
    its characteristics.

Arguments:

    hSourceFile - Provides a handle to the source file.
    
    lpExistingFileName - Provides the name of the existing, source file.

    lpNewFileName - Provides a name for the target file/stream. This must not
        be a UNC path name.

    lpProgressRoutine - Optionally supplies the address of a callback routine
        to be called as the copy operation progresses.

    lpData - Optionally supplies a context to be passed to the progress callback
        routine.

    pbCancel - Optionally supplies the address of a boolean to be set to TRUE
        if the caller would like the copy to abort.

    lpCopyFlags - Provides flags that modify how the copy is to proceed.  See
        CopyFileEx for details.

Return Value:

    TRUE - The operation was successful.

    FALSE - The operation failed. Extended error status is available
        using GetLastError.

--*/

{   // CopyNameGraftNow

    NTSTATUS Status;
    DWORD ReturnValue = FALSE;
    HANDLE DestFile = INVALID_HANDLE_VALUE;
    IO_STATUS_BLOCK IoStatusBlock;
    PREPARSE_DATA_BUFFER ReparseBufferHeader;
    PUCHAR ReparseBuffer = NULL;
    FILE_BASIC_INFORMATION BasicInformation;
    FILE_STANDARD_INFORMATION StandardInformation;
    COPYFILE_CONTEXT CfContext;
    UNICODE_STRING      SourceFileName;
    UNICODE_STRING      DestFileName;
    PVOID               SourceFileNameBuffer = NULL;
    PVOID               DestFileNameBuffer = NULL;
    RTL_RELATIVE_NAME   DestRelativeName;
    BOOL TranslationStatus;
    BOOL b;
    OBJECT_ATTRIBUTES Obja;
    IO_STATUS_BLOCK IoStatus;

    //
    // Set up the context if appropriate.
    //

    RtlZeroMemory(&StandardInformation, sizeof(StandardInformation));
    if ( ARGUMENT_PRESENT(lpProgressRoutine) || ARGUMENT_PRESENT(pbCancel) ) {

        CfContext.TotalFileSize = StandardInformation.EndOfFile;
        CfContext.TotalBytesTransferred.QuadPart = 0;
        CfContext.dwStreamNumber = 0;
        CfContext.lpCancel = pbCancel;
        CfContext.lpData = lpData;
        CfContext.lpProgressRoutine = lpProgressRoutine;
    }

    //
    // Allocate the buffer to set the reparse point.
    //

    ReparseBuffer = RtlAllocateHeap( 
                        RtlProcessHeap(), 
                        MAKE_TAG( TMP_TAG ), 
                        MAXIMUM_REPARSE_DATA_BUFFER_SIZE
                        );
    if ( ReparseBuffer == NULL) {
        BaseSetLastNTError(STATUS_NO_MEMORY);
        return FALSE;
        }

    try {
        //
        // Translate both names.
        //

        TranslationStatus = RtlDosPathNameToNtPathName_U(
                                lpExistingFileName,
                                &SourceFileName,
                                NULL,
                                &DestRelativeName
                                );

        if ( !TranslationStatus ) {
            SetLastError(ERROR_PATH_NOT_FOUND);
            DestFile = INVALID_HANDLE_VALUE;
            leave;
        }
        SourceFileNameBuffer = SourceFileName.Buffer;

        TranslationStatus = RtlDosPathNameToNtPathName_U(
                                lpNewFileName,
                                &DestFileName,
                                NULL,
                                &DestRelativeName
                                );

        if ( !TranslationStatus ) {
            SetLastError(ERROR_PATH_NOT_FOUND);
            DestFile = INVALID_HANDLE_VALUE;
            leave;
        }
        DestFileNameBuffer = DestFileName.Buffer;
     
        //
        // Verify that the source and target are different.
        //
   
        if ( RtlEqualUnicodeString(&SourceFileName, &DestFileName, TRUE) ) {
            //
            // Do nothing. Source and target are the same.
            //
   
            DestFile = INVALID_HANDLE_VALUE;
            leave;
        }
   
        //
        // Open the destination.
        //

        if ( DestRelativeName.RelativeName.Length ) {
            DestFileName = *(PUNICODE_STRING)&DestRelativeName.RelativeName;
        }
        else {
            DestRelativeName.ContainingDirectory = NULL;
        }

        InitializeObjectAttributes(
            &Obja,
            &DestFileName,
            OBJ_CASE_INSENSITIVE,
            DestRelativeName.ContainingDirectory,
            NULL
            );

        Status = NtCreateFile( &DestFile,
                               GENERIC_READ | GENERIC_WRITE,
                               &Obja,
                               &IoStatus,
                               NULL,
                               0,
                               FILE_SHARE_READ | FILE_SHARE_WRITE,
                               (*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ? FILE_CREATE : FILE_OPEN_IF,
                               FILE_OPEN_REPARSE_POINT | CreateOptions,
                               NULL,
                               0 );
        if( !NT_SUCCESS(Status) ) {
            DestFile = INVALID_HANDLE_VALUE;
            BaseSetLastNTError(Status);
            leave;
        }
                        
        //
        // We now have the handle to the destination.
        // We get and set the corresponding reparse point.
        //

        Status = NtFsControlFile(
                     hSourceFile,
                     NULL,
                     NULL,
                     NULL,
                     &IoStatusBlock,
                     FSCTL_GET_REPARSE_POINT,
                     NULL,                                //  Input buffer
                     0,                                   //  Input buffer length
                     ReparseBuffer,                       //  Output buffer
                     MAXIMUM_REPARSE_DATA_BUFFER_SIZE     //  Output buffer length
                     );

        if ( !NT_SUCCESS( Status ) ) {
            BaseSetLastNTError(Status);
            leave;
        }

        //
        // Defensive sanity check. The reparse buffer should be name grafting.
        //

        ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
        if ( ReparseBufferHeader->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT ) {
            BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
            leave;
        }

        //
        // Determine whether the sourse is a volume mount point.
        //

        if ( MOUNTMGR_IS_VOLUME_NAME(&SourceFileName) ) {
            //
            // Set the volume mount point and be done.
            //

            b = SetVolumeMountPointW(
                    lpNewFileName, 
                    ReparseBufferHeader->MountPointReparseBuffer.PathBuffer
                    );
            if ( !b ) {
                leave;
                }
            }
        else {
            //
            // Set the reparse point of type name junction.
            //
   
            Status = NtFsControlFile(
                         DestFile,
                         NULL,
                         NULL,
                         NULL,
                         &IoStatusBlock,
                         FSCTL_SET_REPARSE_POINT,
                         ReparseBuffer,
                         FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + ReparseBufferHeader->ReparseDataLength,
                         NULL,
                         0
                         );
            }

        if ( !(*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) &&
             ((Status == STATUS_EAS_NOT_SUPPORTED) ||
              (Status == STATUS_IO_REPARSE_TAG_MISMATCH)) ) {
            //
            // In either of these error conditions, the correct behavior is to
            // first delete the destination file and then copy the name graft.
            //
            // Re-open the destination for the deletion without inhibiting the
            // reparse behavior.
            //

            BOOL DeleteStatus = FALSE;

            CloseHandle(DestFile);
            DestFile = INVALID_HANDLE_VALUE;

            DeleteStatus = DeleteFileW(
                               lpNewFileName
                               );

            if ( !DeleteStatus ) {
                leave;
                }

            //
            // Create the destination name graft. 
            // Notice that either a file or a directory may be created.
            //

            Status = NtCreateFile( &DestFile,
                                   GENERIC_READ | GENERIC_WRITE,
                                   &Obja,
                                   &IoStatus,
                                   NULL,
                                   0,
                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
                                   FILE_CREATE,
                                   FILE_OPEN_REPARSE_POINT | CreateOptions,
                                   NULL,
                                   0 );
            if( !NT_SUCCESS( Status )) {
                BaseSetLastNTError( Status );
                leave;
            }

            //
            // Set the reparse point.
            //

            Status = NtFsControlFile(
                         DestFile,
                         NULL,
                         NULL,
                         NULL,
                         &IoStatusBlock,
                         FSCTL_SET_REPARSE_POINT,
                         ReparseBuffer,
                         FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + ReparseBufferHeader->ReparseDataLength,
                         NULL,
                         0
                         );
        }   // if ( !(*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ...

        //
        // Close the destination file and return appropriatelly.
        //

        if ( !NT_SUCCESS( Status ) ) {
            BaseSetLastNTError(Status);
            leave;
            }

        //
        // The name graft was copied. Set the last write time for the file
        // so that it matches the input file.
        //

        Status = NtQueryInformationFile(
                     hSourceFile,
                     &IoStatusBlock,
                     (PVOID) &BasicInformation,
                     sizeof(BasicInformation),
                     FileBasicInformation
                     );

        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            leave;
        }

        BasicInformation.CreationTime.QuadPart = 0;
        BasicInformation.LastAccessTime.QuadPart = 0;
        BasicInformation.FileAttributes = 0;

        //
        // If the time cannot be set for whatever reason, we still return
        // TRUE.
        //

        Status = NtSetInformationFile(
                     DestFile,
                     &IoStatusBlock,
                     &BasicInformation,
                     sizeof(BasicInformation),
                     FileBasicInformation
                     );

        if ( Status == STATUS_SHARING_VIOLATION ) {

            //
            // IBM PC Lan Program (and other MS-NET servers) return
            // STATUS_SHARING_VIOLATION if an application attempts to perform
            // an NtSetInformationFile on a file handle opened for GENERIC_READ
            // or GENERIC_WRITE.
            //
            // If we get a STATUS_SHARING_VIOLATION on this API we want to:
            //
            //   1) Close the handle to the destination
            //   2) Re-open the file for FILE_WRITE_ATTRIBUTES
            //   3) Re-try the operation.
            //

            CloseHandle(DestFile);

            //
            // Re-Open the destination file inhibiting the reparse behavior as
            // we know that it is a symbolic link.  Please note that we do this
            // using the CreateFileW API.  The CreateFileW API allows you to
            // pass NT native desired access flags, even though it is not
            // documented to work in this manner.
            //

            Status = NtCreateFile( &DestFile,
                                   FILE_WRITE_ATTRIBUTES,
                                   &Obja,
                                   &IoStatus,
                                   NULL,
                                   0,
                                   0,
                                   FILE_OPEN,
                                   FILE_OPEN_REPARSE_POINT | CreateOptions,
                                   NULL,
                                   0 );

            if ( NT_SUCCESS( Status )) {

                //
                // If the open succeeded, we update the file information on
                // the new file.
                //
                // Note that we ignore any errors from this point on.
                //

                Status = NtSetInformationFile(
                             DestFile,
                             &IoStatusBlock,
                             &BasicInformation,
                             sizeof(BasicInformation),
                             FileBasicInformation
                             );

            }
        }

        ReturnValue = TRUE;

    } finally {
        if( INVALID_HANDLE_VALUE != DestFile )
            CloseHandle( DestFile );
        RtlFreeHeap( RtlProcessHeap(), 0, SourceFileNameBuffer );
        RtlFreeHeap( RtlProcessHeap(), 0, DestFileNameBuffer );
        RtlFreeHeap(RtlProcessHeap(), 0, ReparseBuffer);
    }

    return ReturnValue;
}


BOOL
WINAPI
CopyFileA(
    LPCSTR lpExistingFileName,
    LPCSTR lpNewFileName,
    BOOL bFailIfExists
    )

/*++

Routine Description:

    ANSI thunk to CopyFileW

--*/

{
    PUNICODE_STRING StaticUnicode;
    UNICODE_STRING DynamicUnicode;
    BOOL b;

    StaticUnicode = Basep8BitStringToStaticUnicodeString( lpExistingFileName );
    if (StaticUnicode == NULL) {
        return FALSE;
    }

    if (!Basep8BitStringToDynamicUnicodeString( &DynamicUnicode, lpNewFileName )) {
        return FALSE;
    }

    b = CopyFileExW(
            (LPCWSTR)StaticUnicode->Buffer,
            (LPCWSTR)DynamicUnicode.Buffer,
            (LPPROGRESS_ROUTINE)NULL,
            (LPVOID)NULL,
            (LPBOOL)NULL,
            bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0
            );

    RtlFreeUnicodeString(&DynamicUnicode);

    return b;
}

BOOL
WINAPI
CopyFileW(
    LPCWSTR lpExistingFileName,
    LPCWSTR lpNewFileName,
    BOOL bFailIfExists
    )

/*++

Routine Description:

    A file, its extended attributes, alternate data streams, and any other
    attributes can be copied using CopyFile.

Arguments:

    lpExistingFileName - Supplies the name of an existing file that is to be
        copied.

    lpNewFileName - Supplies the name where a copy of the existing
        files data and attributes are to be stored.

    bFailIfExists - Supplies a flag that indicates how this operation is
        to proceed if the specified new file already exists.  A value of
        TRUE specifies that this call is to fail.  A value of FALSE
        causes the call to the function to succeed whether or not the
        specified new file exists.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    BOOL b;

    b = CopyFileExW(
            lpExistingFileName,
            lpNewFileName,
            (LPPROGRESS_ROUTINE)NULL,
            (LPVOID)NULL,
            (LPBOOL)NULL,
            bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0
            );

    return b;
}

BOOL
WINAPI
CopyFileExA(
    LPCSTR lpExistingFileName,
    LPCSTR lpNewFileName,
    LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
    LPVOID lpData OPTIONAL,
    LPBOOL pbCancel OPTIONAL,
    DWORD dwCopyFlags
    )

/*++

Routine Description:

    ANSI thunk to CopyFileExW

--*/

{
    PUNICODE_STRING StaticUnicode;
    UNICODE_STRING DynamicUnicode;
    BOOL b;

    StaticUnicode = Basep8BitStringToStaticUnicodeString( lpExistingFileName );
    if (StaticUnicode == NULL) {
        return FALSE;
    }

    if (!Basep8BitStringToDynamicUnicodeString( &DynamicUnicode, lpNewFileName )) {
        return FALSE;
    }

    b = CopyFileExW(
            (LPCWSTR)StaticUnicode->Buffer,
            (LPCWSTR)DynamicUnicode.Buffer,
            lpProgressRoutine,
            lpData,
            pbCancel,
            dwCopyFlags
            );

    RtlFreeUnicodeString(&DynamicUnicode);

    return b;
}





#define COPY_FILE_VALID_FLAGS (COPY_FILE_FAIL_IF_EXISTS | \
                               COPY_FILE_RESTARTABLE    | \
                               COPY_FILE_OPEN_SOURCE_FOR_WRITE | \
                               COPY_FILE_ALLOW_DECRYPTED_DESTINATION)




NTSTATUS
BasepProcessNameGrafting( HANDLE SourceFile,
                          PBOOL IsNameGrafting,
                          PBOOL bCopyRawSourceFile,
                          PBOOL bOpenFilesAsReparsePoint,
                          PFILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation )
/*++

Routine Description:

    During CopyFile, check to see if the source is a symlink which
    requires special processing during copy.

Arguments:

    SourceFile - Handle for the source of the copy.

    IsNameGrafting - If true on return, the source file is grafted.

    bCopyRawSourceFile - If true on return, the source file needn't be
        reopened.  If false, the file should be reopened without the
        FILE_OPEN_REPARSE_POINT flag.

    bOpenFilesAsReparsePoint - If true on return, source/dest named
        streams should be opened/created with FILE_OPEN_REPARSE_POINT
        specified.

    FileTagInformation - Pointer to location to hold the results of
        NtQueryInformationFile(FileAttributeTagInformation).

Return Value:

    NTSTATUS

--*/


{
    IO_STATUS_BLOCK IoStatus;
    NTSTATUS Status = STATUS_SUCCESS;

    Status = NtQueryInformationFile(
                SourceFile,
                &IoStatus,
                (PVOID) FileTagInformation,
                sizeof(*FileTagInformation),
                FileAttributeTagInformation
                );

    if ( !NT_SUCCESS(Status) ) {
        //
        //  Not all File Systems implement all information classes.
        //  The value STATUS_INVALID_PARAMETER is returned when a non-supported
        //  information class is requested to a back-level File System. As all
        //  the parameters to NtQueryInformationFile are correct, we can infer
        //  in this case that we found a back-level system.
        //

        if ( (Status != STATUS_INVALID_PARAMETER) &&
             (Status != STATUS_NOT_IMPLEMENTED) ) {
            return( Status );
        }
        Status = STATUS_SUCCESS;

        //
        //  If FileAttributeTagInformation is not supported, we assume that
        //  the file at hand is not a reparse point nor a symbolic link.
        //  The copy of these files is the same as the raw copy of a file.
        //  The target file is opened without inhibiting the reparse point
        //  behavior.
        //

        *bCopyRawSourceFile = TRUE;
    } else {
       //
       //  The source file is opened and the file system supports the
       //  FileAttributeTagInformation information class.
       //

       if ( FileTagInformation->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) {
           //
           //  We have a reparse point at hand.
           //

           if ( FileTagInformation->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT ) {
               //
               //  We found a name grafting operation.
               //

               *IsNameGrafting = TRUE;
           }

       } else {
           //
           //  We have a valid handle.
           //  The underlying file system supports reparse points.
           //  The source file is not a reparse point.
           //  This is the case of a normal file in NT 5.0.
           //  The SourceFile handle can be used for the copy. The copy of
           //  these files is the same as the raw copy of a reparse point.
           //  The target file is opened without inhibiting the reparse
           //  point behavior.
           //

           *bCopyRawSourceFile = TRUE;
       }
    }

    return( Status );
}



BOOL
BasepCopySecurityInformation( LPCWSTR lpExistingFileName,
                              HANDLE SourceFile,
                              ACCESS_MASK SourceFileAccess,
                              LPCWSTR lpNewFileName,
                              HANDLE DestFile,
                              ACCESS_MASK DestFileAccess,
                              SECURITY_INFORMATION SecurityInformation,
                              LPCOPYFILE_CONTEXT Context,
                              DWORD DestFileFsAttributes,
                              PBOOL Canceled );

BOOL
BasepCopyFileCallback( BOOL ContinueByDefault,
                       DWORD Win32ErrorOnStopOrCancel,
                       LPCOPYFILE_CONTEXT Context,
                       PLARGE_INTEGER StreamBytesCopied OPTIONAL,
                       DWORD CallbackReason,
                       HANDLE SourceFile,
                       HANDLE DestFile,
                       OPTIONAL PBOOL Canceled );



BOOL
BasepCopyFileExW(
    LPCWSTR lpExistingFileName,
    LPCWSTR lpNewFileName,
    LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
    LPVOID lpData OPTIONAL,
    LPBOOL pbCancel OPTIONAL,
    DWORD dwCopyFlags,
    DWORD dwPrivCopyFlags,  // From PrivCopyFileExW
    LPHANDLE phSource,
    LPHANDLE phDest
    )
{
    HANDLE SourceFile = INVALID_HANDLE_VALUE;
    HANDLE DestFile = INVALID_HANDLE_VALUE;
    DWORD b = FALSE;
    BOOL IsNameGrafting = FALSE;
    BOOL bCopyRawSourceFile = FALSE;
    BOOL bOpenFilesAsReparsePoint = FALSE;
    ULONG CopySize;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatus;
    FILE_STANDARD_INFORMATION FileInformation;
    FILE_BASIC_INFORMATION BasicInformation;
    PFILE_STREAM_INFORMATION StreamInfo;
    PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
    UNICODE_STRING StreamName;
    HANDLE OutputStream;
    HANDLE StreamHandle;
    ULONG StreamInfoSize;
    COPYFILE_CONTEXT CfContext;
    LPCOPYFILE_CONTEXT CopyFileContext = NULL;
    RESTART_STATE RestartState;
    DWORD SourceFileAttributes = 0;
    DWORD FlagsAndAttributes = 0;
    DWORD FileFlagBackupSemantics = 0;
    DWORD DestFileFsAttributes = 0;
    DWORD SourceFileAccessDefault;
    DWORD SourceFileAccess = 0;
    DWORD SourceFileFlagsAndAttributes = 0;
    DWORD SourceFileSharing = 0;
    DWORD SourceFileSharingDefault = 0;
    BOOL  CheckedForNameGrafting = FALSE;
    FILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation;

    //
    // Ensure that only valid flags were passed.
    //

    if ( dwCopyFlags & ~COPY_FILE_VALID_FLAGS ) {
        BaseSetLastNTError(STATUS_INVALID_PARAMETER);
        return FALSE;
    }

    if ( dwPrivCopyFlags & ~PRIVCOPY_FILE_VALID_FLAGS ) {
        BaseSetLastNTError(STATUS_INVALID_PARAMETER);
        return FALSE;
    }

    // Make sure the copy_file and privcopy_file flags don't overlap
    // in winbase.w.
    ASSERT( (PRIVCOPY_FILE_VALID_FLAGS & COPY_FILE_VALID_FLAGS) == 0 );
    dwCopyFlags |= dwPrivCopyFlags;

    try {

        //
        //  We first establish whether we are copying a reparse point:
        //  (1) obtain a handle inhibiting the reparse point behavior
        //  (2) establish whether a symbolic link was found
        //  (3) establish whether a reparse point that is not a symbolic link
        //      is to be copied in raw format or re-enabling the reparse point
        //      behavior
        //

        // Determine if backup-intent should be set.
        if( (PRIVCOPY_FILE_DIRECTORY|PRIVCOPY_FILE_BACKUP_SEMANTICS) & dwCopyFlags ) {
            FileFlagBackupSemantics = FILE_FLAG_BACKUP_SEMANTICS;
        }

        SourceFileAccessDefault = GENERIC_READ;
        SourceFileAccessDefault |= (dwCopyFlags & COPY_FILE_OPEN_SOURCE_FOR_WRITE) ? GENERIC_WRITE : 0;
        SourceFileAccessDefault |= (dwCopyFlags & PRIVCOPY_FILE_SACL) ? ACCESS_SYSTEM_SECURITY : 0;

        SourceFileFlagsAndAttributes = FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_SEQUENTIAL_SCAN | FileFlagBackupSemantics;
        CheckedForNameGrafting = FALSE;
        SourceFileSharingDefault = FILE_SHARE_READ;

retry_open_SourceFile:

        SourceFileAccess = SourceFileAccessDefault;
        SourceFileSharing = SourceFileSharingDefault;

        while( TRUE ) {

            SourceFile = CreateFileW(
                            lpExistingFileName,
                            SourceFileAccess,
                            SourceFileSharing,
                            NULL,
                            OPEN_EXISTING,
                            SourceFileFlagsAndAttributes,
                            NULL
                            );

            if ( SourceFile == INVALID_HANDLE_VALUE ) {

                // If we tried to get ACCESS_SYSTEM_SECURITY access, that
                // might cause an access or privilege error.
                if( ( GetLastError() == ERROR_PRIVILEGE_NOT_HELD
                      ||
                      GetLastError() == ERROR_ACCESS_DENIED
                    )
                    &&
                    (SourceFileAccess & ACCESS_SYSTEM_SECURITY) ) {

                    // Turn it off
                    SourceFileAccess &= ~ACCESS_SYSTEM_SECURITY;
                }


                // Maybe we should stop requesting write access (done for
                // COPYFILE_OPEN_SOURCE_FOR_WRITE
                else if( ( GetLastError() == ERROR_ACCESS_DENIED ||
                           GetLastError() == ERROR_SHARING_VIOLATION ) &&
                         (GENERIC_WRITE & SourceFileAccess) ) {

                    // Turn it off, but if originally requested,
                    // turn access_system_security back on.
                    SourceFileAccess &= ~GENERIC_WRITE;

                    if( SourceFileAccessDefault & ACCESS_SYSTEM_SECURITY ) {
                        SourceFileAccess |= ACCESS_SYSTEM_SECURITY;
                    }
                }

                // Try sharing for writing.
                else if( !(FILE_SHARE_WRITE & SourceFileSharing) ) {
                    // Add write-sharing
                    SourceFileSharing |= FILE_SHARE_WRITE;

                    // Start back over wrt the access flags
                    SourceFileAccess = SourceFileAccessDefault;
                }

                //
                //  There is the case when we still do not get the file opened and we
                //  do want to proceed with the copy. Pre NT 5.0 systems do not support
                //  FILE_FLAG_OPEN_REPARSE_POINT. If this happens, by initialization we
                //  have that:
                //        IsNameGrafting            is FALSE  and
                //        bCopyRawSourceFile        is FALSE  and
                //        bOpenFilesAsReparsePoint  is FALSE
                //

                else if( FILE_FLAG_OPEN_REPARSE_POINT & SourceFileFlagsAndAttributes ) {
                    // Turn off open-reparse
                    SourceFileFlagsAndAttributes &= ~FILE_FLAG_OPEN_REPARSE_POINT;

                    // Reset the access & sharing back to default
                    SourceFileAccess = SourceFileAccessDefault;
                    SourceFileSharing = SourceFileSharingDefault;
                }


                // Otherwise there's nothing more we can try.
                else {
                    leave;
                }


            }   // if ( SourceFile == INVALID_HANDLE_VALUE )

            // We've opened the source file.  If we haven't yet checked for
            // name grafting (symbolic links), do so now.

            else if( !CheckedForNameGrafting ) {

                CheckedForNameGrafting = TRUE;

                //
                //  Find out whether the file is a symbolic link and whether a reparse
                //  point can be copied with the reparse behavior inhibited.
                //

                Status = BasepProcessNameGrafting( SourceFile,
                                                   &IsNameGrafting,
                                                   &bCopyRawSourceFile,
                                                   &bOpenFilesAsReparsePoint,
                                                   &FileTagInformation );
                if( !NT_SUCCESS(Status) ) {
                    CloseHandle( SourceFile );
                    SourceFile = INVALID_HANDLE_VALUE;
                    BaseSetLastNTError(Status);
                    leave;
                }

                if ( IsNameGrafting ) {
                    //
                    //  Do now the copy of a name grafting file/directory.
                    //

                    Status = CopyNameGraftNow(
                                 SourceFile,
                                 lpExistingFileName,
                                 lpNewFileName,
                                 (PRIVCOPY_FILE_DIRECTORY & dwPrivCopyFlags)
                                     ? (FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT)
                                     : 0,
                                 lpProgressRoutine,
                                 lpData,
                                 pbCancel,
                                 &dwCopyFlags
                                 );

                    CloseHandle(SourceFile);
                    SourceFile = INVALID_HANDLE_VALUE;

                    if( !Status ) {
                        return FALSE;
                    }

                    return TRUE;

                }

                // If we're doing a raw copy, we can start doing the copy with this
                // SourceFile handle.

                if ( bCopyRawSourceFile ) {
                    break; // while( TRUE )
                }

                // Otherwise, we need to reopen without FILE_FLAG_OPEN_REPARSE_POINT;
                else {
                    // Turn off open-as-reparse
                    SourceFileFlagsAndAttributes &= ~FILE_FLAG_OPEN_REPARSE_POINT;

                    CloseHandle( SourceFile );
                    SourceFile = INVALID_HANDLE_VALUE;

                    // Since SourceFileAccess & SourceFileSharing are already set,
                    // the next CreateFile attempt should succeed.
                }

            }   // else if( !CheckedForNameGrafting )

            // Otherwise, we have the file open, and we're done checking for grafting
            else {
                break;
            }

        }   // while( TRUE )


        //
        //  Size the source file to determine how much data is to be copied
        //

        Status = NtQueryInformationFile(
                    SourceFile,
                    &IoStatus,
                    (PVOID) &FileInformation,
                    sizeof(FileInformation),
                    FileStandardInformation
                    );

        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            leave;
        }

        //
        //  Get the timestamp info as well.
        //

        Status = NtQueryInformationFile(
                    SourceFile,
                    &IoStatus,
                    (PVOID) &BasicInformation,
                    sizeof(BasicInformation),
                    FileBasicInformation
                    );

        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            leave;
        }

        SourceFileAttributes = BasicInformation.FileAttributes; // Cache for later use

        //
        // Set up the context if appropriate.
        //

        if ( ARGUMENT_PRESENT(lpProgressRoutine) || ARGUMENT_PRESENT(pbCancel) ) {

            CfContext.TotalFileSize = FileInformation.EndOfFile;
            CfContext.TotalBytesTransferred.QuadPart = 0;
            CfContext.dwStreamNumber = 0;
            CfContext.lpCancel = pbCancel;
            CfContext.lpData = lpData;
            CfContext.lpProgressRoutine = lpProgressRoutine;
            CopyFileContext = &CfContext;
        }

        //
        //  Obtain the full set of streams we have to copy.  Since the Io subsystem does
        //  not provide us a way to find out how much space this information will take,
        //  we must iterate the call, doubling the buffer size upon each failure.
        //
        //  If the underlying file system does not support stream enumeration, we end up
        //  with a NULL buffer.  This is acceptable since we have at least a default
        //  data stream.
        //
        //  We also allocate one more character than necessary since we use the returned
        //  stream names in place when calling BaseCopyStream and we must NUL-terminate
        //  the names
        //

        StreamInfoSize = 4096;
        do {
            StreamInfoBase = RtlAllocateHeap( RtlProcessHeap(),
                                              MAKE_TAG( TMP_TAG ),
                                              StreamInfoSize );

            if ( !StreamInfoBase ) {
                BaseSetLastNTError( STATUS_NO_MEMORY );
                leave;
            }

            Status = NtQueryInformationFile(
                        SourceFile,
                        &IoStatus,
                        (PVOID) StreamInfoBase,
                        StreamInfoSize - sizeof( WCHAR ),
                        FileStreamInformation
                        );

            if ( !NT_SUCCESS(Status) ) {
                //
                //  We failed the call.  Free up the previous buffer and set up
                //  for another pass with a buffer twice as large
                //

                RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
                StreamInfoBase = NULL;
                StreamInfoSize *= 2;
            }
            else if( IoStatus.Information == 0 ) {
                //
                // There are no streams (SourceFile must be a directory).
                //
                RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
                StreamInfoBase = NULL;
            }

        } while ( Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL );

        //
        //  If a progress routine or a restartable copy was requested, obtain the
        //  full size of the entire file, including its alternate data streams, etc.
        //

        if ( ARGUMENT_PRESENT(lpProgressRoutine) ||
             (dwCopyFlags & COPY_FILE_RESTARTABLE) ) {

            if ( dwCopyFlags & COPY_FILE_RESTARTABLE ) {

                RestartState.Type = 0x7a9b;
                RestartState.Size = sizeof( RESTART_STATE );
                RestartState.CreationTime = BasicInformation.CreationTime;
                RestartState.WriteTime = BasicInformation.LastWriteTime;
                RestartState.EndOfFile = FileInformation.EndOfFile;
                RestartState.FileSize = FileInformation.EndOfFile;
                RestartState.NumberOfStreams = 0;
                RestartState.CurrentStream = 0;
                RestartState.LastKnownGoodOffset.QuadPart = 0;
            }

            if ( StreamInfoBase != NULL ) {

                ULONGLONG TotalSize = 0;

                StreamInfo = StreamInfoBase;
                while (TRUE) {
                    //
                    // Account for the size of this stream in the overall size of
                    // the file.
                    //

                    TotalSize += StreamInfo->StreamSize.QuadPart;
                    RestartState.NumberOfStreams++;

                    if (StreamInfo->NextEntryOffset == 0) {
                        break;
                    }
                    StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo + StreamInfo->NextEntryOffset);
                }

                RestartState.FileSize.QuadPart =
                    CfContext.TotalFileSize.QuadPart = TotalSize;
                RestartState.NumberOfStreams--;
            }
        }

        //
        //  Set the Basic Info to change only the WriteTime
        //
        BasicInformation.CreationTime.QuadPart = 0;
        BasicInformation.LastAccessTime.QuadPart = 0;
        BasicInformation.FileAttributes = 0;

        //
        // Determine whether or not the copy operation should really be restartable
        // based on the actual, total file size.
        //

        if ( (dwCopyFlags & COPY_FILE_RESTARTABLE) &&
            ( RestartState.FileSize.QuadPart < (512 * 1024) ||
              (SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )) {
            dwCopyFlags &= ~COPY_FILE_RESTARTABLE;
        }

        //
        // Copy the default data stream, EAs, etc. to the output file
        //

        b = BaseCopyStream(
                lpExistingFileName,
                SourceFile,
                SourceFileAccess,
                lpNewFileName,
                NULL,
                &FileInformation.EndOfFile,
                &dwCopyFlags,
                &DestFile,
                &CopySize,
                &CopyFileContext,
                &RestartState,
                bOpenFilesAsReparsePoint,
                FileTagInformation.ReparseTag,
                &DestFileFsAttributes   // In: 0, Out: Correct value
                );

        if ( bOpenFilesAsReparsePoint &&
             !b &&
             !((GetLastError() == ERROR_FILE_EXISTS) && (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS)) ) {

            //
            //  Clean up.
            //

            if (!(SourceFileAttributes & FILE_ATTRIBUTE_READONLY)) {
                BaseMarkFileForDelete(DestFile, FILE_ATTRIBUTE_NORMAL);
            }

            if (DestFile != INVALID_HANDLE_VALUE) {
                CloseHandle( DestFile );
                DestFile = INVALID_HANDLE_VALUE;
            }

            if (SourceFileAttributes & FILE_ATTRIBUTE_READONLY) {

                //  Delete the destination file before retry
                //  Some servers (like Win9x) won't let us set file attributes
                //  on the handle we already have opened.  SetFileAttributesW
                //  can do the job nicely, so we'll call that to make sure that
                //  the read-only bit isn't set.
                //  We had to close DestFile before doing this because it was
                //  possibly opened share-exclusive.
                SetFileAttributesW(lpNewFileName, FILE_ATTRIBUTE_NORMAL);
                (void) DeleteFileW(lpNewFileName);
            }

            if (SourceFile != INVALID_HANDLE_VALUE) {
                CloseHandle( SourceFile );
                SourceFile = INVALID_HANDLE_VALUE;
            }

            RtlFreeHeap( RtlProcessHeap(), 0, StreamInfoBase );
            StreamInfoBase = NULL ;

            //
            //  Try again the copy operation without inhibiting the reparse
            //  behavior for the source.
            //

            SourceFileFlagsAndAttributes &= ~FILE_FLAG_OPEN_REPARSE_POINT;
            bOpenFilesAsReparsePoint = FALSE;

            //
            //  Go to re-open the source file without inhibiting the reparse
            //  point behavior and try the copy again.
            //

            goto retry_open_SourceFile;
        }

        if ( b ) {

            //
            // Attempt to determine whether or not this file has any alternate
            // data streams associated with it.  If it does, attempt to copy each
            // to the output file.  Note that the stream information may have
            // already been obtained if a progress routine was requested.
            //

            if ( StreamInfoBase != NULL ) {
                DWORD StreamCount = 0;
                BOOLEAN CheckedForStreamCapable = FALSE;
                BOOLEAN IsStreamCapable = FALSE;
                StreamInfo = StreamInfoBase;

                while (TRUE) {

                    FILE_STREAM_INFORMATION DestStreamInformation;
                    Status = STATUS_SUCCESS;

                    //
                    //  Skip the default data stream since we've already copied
                    //  it.  Alas, this code is NTFS specific and documented
                    //  nowhere in the Io spec.
                    //

                    if (StreamInfo->StreamNameLength <= sizeof(WCHAR) ||
                        StreamInfo->StreamName[1] == ':') {
                        if (StreamInfo->NextEntryOffset == 0)
                            break;      // Done with streams
                        StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo +
                                                                StreamInfo->NextEntryOffset);
                        continue;   // Move on to the next stream
                    }

                    StreamCount++;

                    if ( b == SUCCESS_RETURNED_STATE && CopyFileContext ) {
                        if ( StreamCount < RestartState.CurrentStream ) {
                            CopyFileContext->TotalBytesTransferred.QuadPart += StreamInfo->StreamSize.QuadPart;
                            }
                        else {
                            CopyFileContext->TotalBytesTransferred.QuadPart += RestartState.LastKnownGoodOffset.QuadPart;
                            }
                        }

                    //
                    // If we haven't already, verify that both the source and destination
                    // are really stream capable.
                    //

                    if( !CheckedForStreamCapable ) {

                        struct {
                            FILE_FS_ATTRIBUTE_INFORMATION Info;
                            WCHAR Buffer[ MAX_PATH ];
                        } FileFsAttrInfoBuffer;

                        CheckedForStreamCapable = TRUE;

                        // Check for the supports-streams bit in the dest filesystem.

                        Status = NtQueryVolumeInformationFile( DestFile,
                                                               &IoStatus,
                                                               &FileFsAttrInfoBuffer.Info,
                                                               sizeof(FileFsAttrInfoBuffer),
                                                               FileFsAttributeInformation );
                        if( NT_SUCCESS(Status) &&
                            (FileFsAttrInfoBuffer.Info.FileSystemAttributes & FILE_NAMED_STREAMS) ) {

                            // It seems redundant to check to see if the source is stream capable,
                            // since we already got back a successful stream enumeration, but some
                            // SMB servers (SCO VisionFS) return success but don't really support
                            // streams.
                        
                            Status = NtQueryVolumeInformationFile( SourceFile,
                                                                   &IoStatus,
                                                                   &FileFsAttrInfoBuffer.Info,
                                                                   sizeof(FileFsAttrInfoBuffer),
                                                                   FileFsAttributeInformation );
                        }


                        if( !NT_SUCCESS(Status) ||
                            !(FileFsAttrInfoBuffer.Info.FileSystemAttributes & FILE_NAMED_STREAMS) ) {

                            if( NT_SUCCESS(Status) ) {
                                Status = STATUS_NOT_SUPPORTED;
                            }

                            if( dwCopyFlags & PRIVCOPY_FILE_VALID_FLAGS ) {
                                if( !BasepCopyFileCallback( TRUE,    // Continue by default
                                                            RtlNtStatusToDosError(Status),
                                                            CopyFileContext,
                                                            NULL,
                                                            PRIVCALLBACK_STREAMS_NOT_SUPPORTED,
                                                            SourceFile,
                                                            DestFile,
                                                            NULL )) {

                                    // LastError has been set, but we need it in Status
                                    // for compatibility with the rest of this routine.
                                    PTEB Teb = NtCurrentTeb();
                                    if ( Teb ) {
                                        Status = Teb->LastStatusValue;
                                    } else {
                                        Status = STATUS_INVALID_PARAMETER;
                                    }

                                    b = FALSE;
                                } else {
                                    // Ignore the named stream loss
                                    Status = STATUS_SUCCESS;
                                }
                            } else {
                                // Ignore the named stream loss.  We'll still try to copy the 
                                // streams, though, since the target might be NT4 which didn't support
                                // the FILE_NAMED_STREAMS bit.  But since IsStreamCapable is FALSE,
                                // if there's an error, we'll ignore it.

                                Status = STATUS_SUCCESS;
                            }
                        }
                        else {
                            Status = STATUS_SUCCESS;
                            IsStreamCapable = TRUE;
                        }
                    }   // if( !CheckedForStreamCapable )


                    if ( b == TRUE ||
                        (b == SUCCESS_RETURNED_STATE &&
                         RestartState.CurrentStream == StreamCount) ) {

                        if ( b != SUCCESS_RETURNED_STATE ) {
                            RestartState.CurrentStream = StreamCount;
                            RestartState.LastKnownGoodOffset.QuadPart = 0;
                            }

                        //
                        // Build a string descriptor for the name of the stream.
                        //

                        StreamName.Buffer = &StreamInfo->StreamName[0];
                        StreamName.Length = (USHORT) StreamInfo->StreamNameLength;
                        StreamName.MaximumLength = StreamName.Length;

                        //
                        // Open the source stream.
                        //

                        InitializeObjectAttributes(
                            &ObjectAttributes,
                            &StreamName,
                            0,
                            SourceFile,
                            NULL
                            );

                        //
                        // Inhibit reparse behavior when appropriate.
                        //

                        FlagsAndAttributes = FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY;
                        if ( bOpenFilesAsReparsePoint ) {
                            FlagsAndAttributes |= FILE_OPEN_REPARSE_POINT;
                        }

                        Status = NtCreateFile(
                                    &StreamHandle,
                                    GENERIC_READ | SYNCHRONIZE,
                                    &ObjectAttributes,
                                    &IoStatus,
                                    NULL,
                                    0,
                                    FILE_SHARE_READ,
                                    FILE_OPEN,
                                    FlagsAndAttributes,
                                    NULL,
                                    0
                                    );

                        //If we got a share violation, try again with
                        // FILE_SHARE_WRITE.
                        if ( Status == STATUS_SHARING_VIOLATION ) {
                            DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;

                            Status = NtCreateFile(
                                        &StreamHandle,
                                        GENERIC_READ | SYNCHRONIZE,
                                        &ObjectAttributes,
                                        &IoStatus,
                                        NULL,
                                        0,
                                        dwShare,
                                        FILE_OPEN,
                                        FlagsAndAttributes,
                                        NULL,
                                        0
                                        );
                        }


                        if ( NT_SUCCESS(Status) ) {
                            DWORD dwCopyFlagsNamedStreams;
                            WCHAR LastChar = StreamName.Buffer[StreamName.Length / sizeof( WCHAR )];

                            StreamName.Buffer[StreamName.Length / sizeof( WCHAR )] = L'\0';

                            OutputStream = (HANDLE)NULL;

                            //
                            // For named streams, ignore the fail-if-exists flag.  If the dest
                            // file already existed at the time the copy started, then
                            // we would have failed on the copy of the unnamed stream.  So if
                            // a named stream exists, that means that it was created by some
                            // other process while we were copying the unnamed stream.  The
                            // assumption is that such a stream should be overwritten (this 
                            // scenario can occur with SFM).
                            //

                            dwCopyFlagsNamedStreams = dwCopyFlags & ~COPY_FILE_FAIL_IF_EXISTS;

                            b = BaseCopyStream(
                                    lpExistingFileName,
                                    StreamHandle,
                                    SourceFileAccess,
                                    StreamName.Buffer,
                                    DestFile,
                                    &StreamInfo->StreamSize,
                                    &dwCopyFlagsNamedStreams,
                                    &OutputStream,
                                    &CopySize,
                                    &CopyFileContext,
                                    &RestartState,
                                    bOpenFilesAsReparsePoint,
                                    FileTagInformation.ReparseTag,
                                    &DestFileFsAttributes   // Set by first call to BaseCopyStream
                                    );
                            
                            StreamName.Buffer[StreamName.Length / sizeof( WCHAR )] = LastChar;
                            
                            NtClose(StreamHandle);
                            if ( OutputStream ) {

                                //
                                //  We set the last write time on all streams
                                //  since there is a problem with RDR caching
                                //  open handles and closing them out of order.
                                //

                                if ( b ) {
                                    Status = NtSetInformationFile(
                                                OutputStream,
                                                &IoStatus,
                                                &BasicInformation,
                                                sizeof(BasicInformation),
                                                FileBasicInformation
                                                );
                                }
                                NtClose(OutputStream);
                            }

                        }   // Status = NtCreateFile; if( NT_SUCCESS(Status) )
                    }   // if ( b == TRUE || ...

                    if ( !NT_SUCCESS(Status) ) {
                        b = FALSE;
                        BaseSetLastNTError(Status);
                    }

                    if ( !b ) {

                        // If the target is known to be capable of multi-stream files,
                        // then this is a fatal error.  Otherwise we'll ignore it.

                        if( IsStreamCapable ) {
                            BaseMarkFileForDelete(DestFile,0);
                            break;  // while( TRUE )
                        } else {
                            Status = STATUS_SUCCESS;
                            b = TRUE;
                        }
                    }

                    if (StreamInfo->NextEntryOffset == 0) {
                        break;
                    }

                    StreamInfo =
                        (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo +
                                                   StreamInfo->NextEntryOffset);
                }   // while (TRUE)
            }   // if ( StreamInfoBase != NULL )
        }   // b = BaseCopyStream; if ( b ) ...


        //
        //  If the copy operation was successful, and it was restartable, and the
        //  output file was large enough that it was actually copied in a
        //  restartable manner, then copy the initial part of the file to its
        //  output.
        //
        //  Restartability is accomplished by placing a restart header at the
        //  head of the default data stream.  When the copy is complete, we
        //  overwite this header with the real user data.
        //

        if ( b && (dwCopyFlags & COPY_FILE_RESTARTABLE) ) {

            DWORD BytesToRead, BytesRead;
            DWORD BytesWritten;
            FILE_END_OF_FILE_INFORMATION EofInformation;

            SetFilePointer( SourceFile, 0, NULL, FILE_BEGIN );
            SetFilePointer( DestFile, 0, NULL, FILE_BEGIN );

            BytesToRead = sizeof(RESTART_STATE);
            if ( FileInformation.EndOfFile.QuadPart < sizeof(RESTART_STATE) ) {
                BytesToRead = FileInformation.EndOfFile.LowPart;
            }

            //
            //  Grab true data from the source stream
            //

            b = ReadFile(
                    SourceFile,
                    &RestartState,
                    BytesToRead,
                    &BytesRead,
                    NULL
                    );

            if ( b && (BytesRead == BytesToRead) ) {

                //
                //  Overwrite the restart header in the destination.
                //  After this point, the copy is no longer restartable
                //

                b = WriteFile(
                        DestFile,
                        &RestartState,
                        BytesRead,
                        &BytesWritten,
                        NULL
                        );

                if ( b && (BytesRead == BytesWritten) ) {
                    if ( BytesRead < sizeof(RESTART_STATE) ) {
                        EofInformation.EndOfFile.QuadPart = BytesWritten;
                        Status = NtSetInformationFile(
                                    DestFile,
                                    &IoStatus,
                                    &EofInformation,
                                    sizeof(EofInformation),
                                    FileEndOfFileInformation
                                    );
                        if ( !NT_SUCCESS(Status) ) {
                            BaseMarkFileForDelete(DestFile,0);
                            b = FALSE;
                        }
                    }
                } else {
                    BaseMarkFileForDelete(DestFile,0);
                    b = FALSE;
                }
            } else {
                BaseMarkFileForDelete(DestFile,0);
                b = FALSE;
            }
        }

        //
        // If the copy operation was successful, set the last write time for the
        // default steam so that it matches the input file.
        //

        if ( b ) {
            Status = NtSetInformationFile(
                DestFile,
                &IoStatus,
                &BasicInformation,
                sizeof(BasicInformation),
                FileBasicInformation
                );

            if ( Status == STATUS_SHARING_VIOLATION ) {

                //
                // IBM PC Lan Program (and other MS-NET servers) return
                // STATUS_SHARING_VIOLATION if an application attempts to perform
                // an NtSetInformationFile on a file handle opened for GENERIC_READ
                // or GENERIC_WRITE.
                //
                // If we get a STATUS_SHARING_VIOLATION on this API we want to:
                //
                //   1) Close the handle to the destination
                //   2) Re-open the file for FILE_WRITE_ATTRIBUTES
                //   3) Re-try the operation.
                //

                CloseHandle(DestFile);
                DestFile = INVALID_HANDLE_VALUE;

                //
                //  Re-Open the destination file.  Please note that we do this
                //  using the CreateFileW API.  The CreateFileW API allows you to
                //  pass NT native desired access flags, even though it is not
                //  documented to work in this manner.
                //
                //  Inhibit reparse behavior when appropriate.
                //

                FlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
                if ( bOpenFilesAsReparsePoint ) {
                    FlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
                }

                DestFile = CreateFileW(
                            lpNewFileName,
                            FILE_WRITE_ATTRIBUTES,
                            0,
                            NULL,
                            OPEN_EXISTING,
                            FlagsAndAttributes | FileFlagBackupSemantics,
                            NULL
                            );

                if ( DestFile != INVALID_HANDLE_VALUE ) {

                    //
                    //  If the open succeeded, we update the file information on
                    //  the new file.
                    //
                    //  Note that we ignore any errors from this point on.
                    //

                    NtSetInformationFile(
                        DestFile,
                        &IoStatus,
                        &BasicInformation,
                        sizeof(BasicInformation),
                        FileBasicInformation
                        );

                }
            }
        }

    } finally {

        *phSource = SourceFile;
        *phDest = DestFile;

        RtlFreeHeap( RtlProcessHeap(), 0, StreamInfoBase );
    }

    return b;
}

BOOL
CopyFileExW(
    LPCWSTR lpExistingFileName,
    LPCWSTR lpNewFileName,
    LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
    LPVOID lpData OPTIONAL,
    LPBOOL pbCancel OPTIONAL,
    DWORD dwCopyFlags
    )

/*

Routine Description:

    A file, its extended attributes, alternate data streams, and any other
    attributes can be copied using CopyFileEx.  CopyFileEx also provides
    callbacks and cancellability.

Arguments:

    lpExistingFileName - Supplies the name of an existing file that is to be
        copied.

    lpNewFileName - Supplies the name where a copy of the existing
        files data and attributes are to be stored.

    lpProgressRoutine - Optionally supplies the address of a callback routine
        to be called as the copy operation progresses.

    lpData - Optionally supplies a context to be passed to the progress callback
        routine.

    lpCancel - Optionally supplies the address of a boolean to be set to TRUE
        if the caller would like the copy to abort.

    dwCopyFlags - Specifies flags that modify how the file is to be copied:

        COPY_FILE_FAIL_IF_EXISTS - Indicates that the copy operation should
            fail immediately if the target file already exists.

        COPY_FILE_RESTARTABLE - Indicates that the file should be copied in
            restartable mode; i.e., progress of the copy should be tracked in
            the target file in case the copy fails for some reason.  It can
            then be restarted at a later date.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

*/

{
    HANDLE DestFile = INVALID_HANDLE_VALUE;
    HANDLE SourceFile = INVALID_HANDLE_VALUE;
    BOOL b;

    try
    {
        b = BasepCopyFileExW(
                lpExistingFileName,
                lpNewFileName,
                lpProgressRoutine OPTIONAL,
                lpData OPTIONAL,
                pbCancel OPTIONAL,
                dwCopyFlags,
                0,  // PrivCopyFile flags
                &DestFile,
                &SourceFile
                );

    }
    finally
    {
        if (DestFile != INVALID_HANDLE_VALUE) {
            CloseHandle( DestFile );
        }

        if (SourceFile != INVALID_HANDLE_VALUE) {
            CloseHandle( SourceFile );
        }
    }

    return(b);
}



BOOL
PrivCopyFileExW(
    LPCWSTR lpExistingFileName,
    LPCWSTR lpNewFileName,
    LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
    LPVOID lpData OPTIONAL,
    LPBOOL pbCancel OPTIONAL,
    DWORD dwCopyFlags
    )

/*

Routine Description:

    A file, its extended attributes, alternate data streams, and any other
    attributes can be copied using CopyFileEx.  CopyFileEx also provides
    callbacks and cancellability.

Arguments:

    lpExistingFileName - Supplies the name of an existing file that is to be
        copied.

    lpNewFileName - Supplies the name where a copy of the existing
        files data and attributes are to be stored.

    lpProgressRoutine - Optionally supplies the address of a callback routine
        to be called as the copy operation progresses.

    lpData - Optionally supplies a context to be passed to the progress callback
        routine.

    lpCancel - Optionally supplies the address of a boolean to be set to TRUE
        if the caller would like the copy to abort.

    dwCopyFlags - Specifies flags that modify how the file is to be copied:

        COPY_FILE_FAIL_IF_EXISTS - Indicates that the copy operation should
            fail immediately if the target file already exists.

        COPY_FILE_RESTARTABLE - Indicates that the file should be copied in
            restartable mode; i.e., progress of the copy should be tracked in
            the target file in case the copy fails for some reason.  It can
            then be restarted at a later date.

Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

*/

{
    HANDLE DestFile = INVALID_HANDLE_VALUE;
    HANDLE SourceFile = INVALID_HANDLE_VALUE;
    BOOL b;

    if( (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) &&
        (dwCopyFlags & PRIVCOPY_FILE_SUPERSEDE) ) {
        SetLastError( ERROR_INVALID_PARAMETER );
        return( FALSE );
    }

    try
    {
        b = BasepCopyFileExW(
                lpExistingFileName,
                lpNewFileName,
                lpProgressRoutine OPTIONAL,
                lpData OPTIONAL,
                pbCancel OPTIONAL,
                dwCopyFlags & COPY_FILE_VALID_FLAGS,    // Copy flags
                dwCopyFlags & ~COPY_FILE_VALID_FLAGS,   // Priv copy flags
                &DestFile,
                &SourceFile
                );

    }
    finally
    {
        if (DestFile != INVALID_HANDLE_VALUE) {
            CloseHandle( DestFile );
        }

        if (SourceFile != INVALID_HANDLE_VALUE) {
            CloseHandle( SourceFile );
        }
    }

    return(b);
}







DWORD
BasepChecksum(
    PUSHORT Source,
    ULONG Length
    )

/*++

Routine Description:

    Compute a partial checksum on a structure.

Arguments:

    Source - Supplies a pointer to the array of words for which the
        checksum is computed.

    Length - Supplies the length of the array in words.

Return Value:

    The computed checksum value is returned as the function value.

--*/

{

    ULONG PartialSum = 0;

    //
    // Compute the word wise checksum allowing carries to occur into the
    // high order half of the checksum longword.
    //

    while (Length--) {
        PartialSum += *Source++;
        PartialSum = (PartialSum >> 16) + (PartialSum & 0xffff);
    }

    //
    // Fold final carry into a single word result and return the resultant
    // value.
    //

    return (((PartialSum >> 16) + PartialSum) & 0xffff);
}

BOOL
BasepRemoteFile(
    HANDLE SourceFile,
    HANDLE DestinationFile
    )

{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatus;
    FILE_FS_DEVICE_INFORMATION DeviceInformation;

    DeviceInformation.Characteristics = 0;
    Status = NtQueryVolumeInformationFile(
                SourceFile,
                &IoStatus,
                &DeviceInformation,
                sizeof(DeviceInformation),
                FileFsDeviceInformation
                );

    if ( NT_SUCCESS(Status) &&
         (DeviceInformation.Characteristics & FILE_REMOTE_DEVICE) ) {

        return TRUE;

    }

    Status = NtQueryVolumeInformationFile(
                    DestinationFile,
                    &IoStatus,
                    &DeviceInformation,
                    sizeof(DeviceInformation),
                    FileFsDeviceInformation
                    );
    if ( NT_SUCCESS(Status) &&
         DeviceInformation.Characteristics & FILE_REMOTE_DEVICE ) {

        return TRUE;
    }

    return FALSE;
}



DWORD
WINAPI
BasepOpenRestartableFile(
            HANDLE hSourceFile,
            LPCWSTR lpNewFileName,
            PHANDLE DestFile,
            DWORD CopyFlags,
            LPRESTART_STATE lpRestartState,
            LARGE_INTEGER *lpFileSize,
            LPCOPYFILE_CONTEXT *lpCopyFileContext,
            DWORD FlagsAndAttributes,
            BOOL OpenAsReparsePoint )

{   // BasepRestartCopyFile

    LPCOPYFILE_CONTEXT Context = *lpCopyFileContext;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING UnicodeString;
    HANDLE OverwriteHandle;
    IO_STATUS_BLOCK IoStatus;
    RESTART_STATE RestartState;
    DWORD b = TRUE;
    ULONG BytesRead = 0;


    try {

        //
        // Note that setting the sequential scan flag is an optimization
        // here that works because of the way that the Cache Manager on
        // the target works vis-a-vis unmapping segments of the file
        // behind write operations.  This eventually allows the restart
        // section and the end of the file to both be mapped, which is
        // the desired result.
        //
        // Inhibit reparse behavior when appropriate.
        //

        FlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;

        if ( OpenAsReparsePoint ) {
            //
            // The target has to be opened as reparse point. If
            // this fails the source is to be closed and re-opened
            // without inhibiting the reparse point behavior.
            //

            FlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
        }

        *DestFile = CreateFileW(
                       lpNewFileName,
                       GENERIC_READ | GENERIC_WRITE | DELETE,
                       FILE_SHARE_READ | FILE_SHARE_WRITE,
                       NULL,
                       OPEN_EXISTING,
                       FlagsAndAttributes,
                       hSourceFile
                       );

        if( *DestFile == INVALID_HANDLE_VALUE ) {

            // Caller should attempt to create/overwrite the dest file
            b = TRUE;
            leave;
        }

        //
        //  The target file already exists, so determine whether
        //  a restartable copy was already proceeding.  If so,
        //  then continue;  else, check to see whether or not
        //  the target file can be replaced.  If not, bail with
        //  an error, otherwise simply overwrite the output file.
        //

        b = ReadFile(
                *DestFile,
                &RestartState,
                sizeof(RESTART_STATE),
                &BytesRead,
                NULL
                );
        if ( !b || BytesRead != sizeof(RESTART_STATE) ) {

            //
            // The file could not be read, or there were not
            // enough bytes to contain a restart record.  In
            // either case, if the output file cannot be
            // replaced, simply return an error now.
            //

            if ( CopyFlags & COPY_FILE_FAIL_IF_EXISTS ) {
                SetLastError( ERROR_ALREADY_EXISTS );
                b = FALSE;  // Fatal error
                leave;
            }

            // The caller should create/overwrite the dest file.
            b = TRUE;
            CloseHandle( *DestFile );
            *DestFile = INVALID_HANDLE_VALUE;
            leave;

        }

        //
        // Check the contents of the restart state just
        // read against the known contents of what would
        // be there if this were the same copy operation.
        //

        if ( RestartState.Type != 0x7a9b ||
             RestartState.Size != sizeof(RESTART_STATE) ||
             RestartState.FileSize.QuadPart != lpRestartState->FileSize.QuadPart ||
             RestartState.EndOfFile.QuadPart != lpRestartState->EndOfFile.QuadPart ||
             RestartState.NumberOfStreams != lpRestartState->NumberOfStreams ||
             RestartState.CreationTime.QuadPart != lpRestartState->CreationTime.QuadPart ||
             RestartState.WriteTime.QuadPart != lpRestartState->WriteTime.QuadPart ||
             RestartState.Checksum != BasepChecksum((PUSHORT)&RestartState,FIELD_OFFSET(RESTART_STATE,Checksum) >> 1) ) {

            if ( CopyFlags & COPY_FILE_FAIL_IF_EXISTS ) {
                b = FALSE;  // Fatal error
                SetLastError( ERROR_ALREADY_EXISTS );
                leave;
            }

            // The caller should create/overwrite the dest file.
            b = TRUE;
            CloseHandle( *DestFile );
            *DestFile = INVALID_HANDLE_VALUE;
            leave;

        }

        //
        // A valid restart state has been found.  Copy
        // the appropriate values into the internal
        // restart state so the operation can continue
        // from there.
        //

        lpRestartState->CurrentStream = RestartState.CurrentStream;
        lpRestartState->LastKnownGoodOffset.QuadPart = RestartState.LastKnownGoodOffset.QuadPart;
        if ( !RestartState.CurrentStream ) {

            // We were in the middle of copying the unnamed data stream.

            if ( Context ) {
                Context->TotalBytesTransferred.QuadPart = RestartState.LastKnownGoodOffset.QuadPart;
            }

            // We'll leave the handle in *DestFile, and the caller and pick up the
            // copy of this stream.

            b = TRUE;

        } else {

            // We were in the middle of copying a named data stream.

            if ( Context ) {
                ULONG ReturnCode;

                Context->TotalBytesTransferred.QuadPart = lpFileSize->QuadPart;
                Context->dwStreamNumber = RestartState.CurrentStream;

                if ( Context->lpProgressRoutine ) {
                    ReturnCode = Context->lpProgressRoutine(
                                    Context->TotalFileSize,
                                    Context->TotalBytesTransferred,
                                    *lpFileSize,
                                    Context->TotalBytesTransferred,
                                    1,
                                    CALLBACK_STREAM_SWITCH,
                                    hSourceFile,
                                    *DestFile,
                                    Context->lpData
                                    );
                } else {
                    ReturnCode = PROGRESS_CONTINUE;
                }

                if ( ReturnCode == PROGRESS_CANCEL ||
                    (Context->lpCancel && *Context->lpCancel) ) {
                    BaseMarkFileForDelete(
                        *DestFile,
                        0
                        );
                    BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                    b = FALSE;
                    leave;
                }

                if ( ReturnCode == PROGRESS_STOP ) {
                    BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                    b = FALSE;
                    leave;
                }

                if ( ReturnCode == PROGRESS_QUIET ) {
                    Context = NULL;
                    *lpCopyFileContext = NULL;
                }
            }

            b = SUCCESS_RETURNED_STATE;

        }   // if ( !RestartState.CurrentStream ) ... else
    }
    finally {

        if( b == FALSE &&
            *DestFile != INVALID_HANDLE_VALUE ) {
            CloseHandle( *DestFile );
            *DestFile = INVALID_HANDLE_VALUE;
        }


    }

    return( b );

}





BOOL
WINAPI
BasepCopyCompression( HANDLE hSourceFile,
                      HANDLE DestFile,
                      DWORD SourceFileAttributes,
                      DWORD DestFileAttributes,
                      DWORD DestFileFsAttributes,
                      DWORD CopyFlags,
                      LPCOPYFILE_CONTEXT *lpCopyFileContext )
/*++

Routine Description:

    This is an internal routine that copies the compression state during
    a copyfile.  If the source is compressed, that same compression
    algorithm is copied to the dest.  If that fails, an attempt is made
    to set the default compression.  Depending on the copy flags, it
    may alternatively be necessary to decompress the destination.


Arguments:

    hSourceFile - Provides a handle to the source file.

    DestFile - Provides a handle to the destination file.

    SourceFileAttributes - FileBasicInformation attributes queried from the
        source file.

    DestFileAttributes - FileBasicInformation attributes for the current
        state of the destination file.

    DestFileFsAttributes - FileFsAttributeInformation.FileSystemAttributes
        for the file system of the dest file.

    CopyFlags - Provides flags that modify how the copy is to proceed.  See
        CopyFileEx for details.

    lpCopyFileContext - Provides a pointer to a pointer to the context
        information to track callbacks, file sizes, etc. across streams during
        the copy operation.


Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.  The DestFile has already been marked
        for delete.

--*/

{   // BasepCopyCompression

    IO_STATUS_BLOCK IoStatus;
    NTSTATUS Status = STATUS_SUCCESS;
    LPCOPYFILE_CONTEXT Context = *lpCopyFileContext;
    BOOL SuccessReturn = FALSE;
    BOOL Canceled = FALSE;

    try
    {
        if( !(SourceFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) {

            // The source file is not compressed.  If necessary, decompress
            // the target.

            if( (DestFileAttributes & FILE_ATTRIBUTE_COMPRESSED) &&
                (CopyFlags & PRIVCOPY_FILE_SUPERSEDE) ) {

                // The source isn't compressed, but the dest is, and we don't
                // want to acquire attributes from the dest.  So we need to manually
                // decompress it.

                ULONG CompressionType = COMPRESSION_FORMAT_NONE;

                Status = NtFsControlFile(
                             DestFile,
                             NULL,
                             NULL,
                             NULL,
                             &IoStatus,
                             FSCTL_SET_COMPRESSION,
                             &CompressionType,                    //  Input buffer
                             sizeof(CompressionType),             //  Input buffer length
                             NULL,                                //  Output buffer
                             0                                    //  Output buffer length
                             );
                if( !NT_SUCCESS(Status) ) {
                    // See if it's OK to ignore the error
                    if( !BasepCopyFileCallback( TRUE,    // Continue by default
                                                RtlNtStatusToDosError(Status),
                                                Context,
                                                NULL,
                                                PRIVCALLBACK_COMPRESSION_NOT_SUPPORTED,
                                                hSourceFile,
                                                DestFile,
                                                &Canceled )) {


                        BaseMarkFileForDelete( DestFile, 0 );
                        BaseSetLastNTError( Status );
                        leave;
                    } else {
                        Status = STATUS_SUCCESS;
                    }
                }

            }

        }   // if( !(SourceFileAttributes & FILE_ATTRIBUTE_COMPRESSED) )

        else {

            // The source file is compressed.  Does the target filesystem
            // even support compression?

            if( !(FILE_FILE_COMPRESSION & DestFileFsAttributes) ) {

                // No, it won't be compressable.  See if it's OK to continue.

                if( !BasepCopyFileCallback( TRUE,    // Continue by default
                                            ERROR_NOT_SUPPORTED,
                                            Context,
                                            NULL,
                                            PRIVCALLBACK_COMPRESSION_NOT_SUPPORTED,
                                            hSourceFile,
                                            DestFile,
                                            &Canceled )) {

                    if( Canceled ) {
                        BaseMarkFileForDelete(
                            DestFile,
                            0 );
                    }
                    leave;
                }
            }   // if( !(FILE_FILE_COMPRESSION & *DestFileFsAttributes) )

            else {

                // Target volume supports compression.  Compress the target file if
                // it's not already.

                if( !(DestFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) {

                    USHORT CompressionType;

                    // Get the source file's compression type

                    Status = NtFsControlFile(
                                 hSourceFile,
                                 NULL,
                                 NULL,
                                 NULL,
                                 &IoStatus,
                                 FSCTL_GET_COMPRESSION,
                                 NULL,                                //  Input buffer
                                 0,                                   //  Input buffer length
                                 &CompressionType,                    //  Output buffer
                                 sizeof(CompressionType)              //  Output buffer length
                                 );
                    if( NT_SUCCESS(Status) ) {

                        // Set the compression type on the target

                        Status = NtFsControlFile(
                                     DestFile,
                                     NULL,
                                     NULL,
                                     NULL,
                                     &IoStatus,
                                     FSCTL_SET_COMPRESSION,
                                     &CompressionType,                    //  Input buffer
                                     sizeof(CompressionType),             //  Input buffer length
                                     NULL,                                //  Output buffer
                                     0                                    //  Output buffer length
                                     );

                        // If that didn't work, try the default compression
                        // format (maybe we're copying from uplevel to downlevel).

                        if( !NT_SUCCESS(Status) &&
                            COMPRESSION_FORMAT_DEFAULT != CompressionType ) {

                            CompressionType = COMPRESSION_FORMAT_DEFAULT;
                            Status = NtFsControlFile(
                                         DestFile,
                                         NULL,
                                         NULL,
                                         NULL,
                                         &IoStatus,
                                         FSCTL_SET_COMPRESSION,
                                         &CompressionType,                    //  Input buffer
                                         sizeof(CompressionType),             //  Input buffer length
                                         NULL,                                //  Output buffer
                                         0                                    //  Output buffer length
                                         );
                        }
                    }   // FSCTL_GET_COMPRESSION ... if( NT_SUCCESS(Status) )

                    // If something went wrong and we couldn't compress it, there's a good
                    // chance that the caller doesn't want this to be fatal.  Ask and find
                    // out.

                    if( !NT_SUCCESS(Status) ) {
                        BOOL Canceled = FALSE;

                        if( !BasepCopyFileCallback( TRUE,    // Continue by default
                                                    RtlNtStatusToDosError(Status),
                                                    Context,
                                                    NULL,
                                                    PRIVCALLBACK_COMPRESSION_FAILED,
                                                    hSourceFile,
                                                    DestFile,
                                                    &Canceled )) {
                            if( Canceled ) {
                                BaseMarkFileForDelete(
                                    DestFile,
                                    0 );
                            }
                            leave;
                        }
                    }
                }   // if( !(DestFileAttributes & FILE_FILE_COMPRESSION) )
            }   // if( !(FILE_FILE_COMPRESSION & *DestFileFsAttributes) )
        }   // if( !(SourceFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) ... else

        SuccessReturn = TRUE;
    }
    finally
    {
    }

    return( SuccessReturn );
}



NTSTATUS
BasepCreateDispositionToWin32( DWORD CreateDisposition, DWORD *Win32CreateDisposition )

/*++
Routine Description:

    This is an internal routine used by BaseCopyStream.  It is used to translate
    from NT API CreateDisposition flags to Win32 CreateDisposition flags (this was 
    added in order to use the NT CreateDisposition in a call to DuplicateEncryptionInformation).

    This routine does the inverse of the Win32->NT mapping in CreateFile, except that there is
    no way to obtain TRUNCATE_EXISTING from an NT flag.  The FILE_SUPERSEDE and FILE_OVERWRITE
    flags are not supported by this routine.

Arguments:

    CreateDisposition - The NT CreateDisposition flags.

Returns:

    STATUS_INVALID_PARAMETER if an unsupported NT flag is passed in.
    STATUS_SUCCESS otherwise.

++*/


{
    switch ( CreateDisposition ) {

    case FILE_CREATE :
        *Win32CreateDisposition = CREATE_NEW;
        break;
    case FILE_OVERWRITE_IF:
        *Win32CreateDisposition = CREATE_ALWAYS;
        break;
    case FILE_OPEN:
        *Win32CreateDisposition = OPEN_EXISTING;
        break;
    case FILE_OPEN_IF:
        *Win32CreateDisposition = OPEN_ALWAYS;
        break;
    default :
        return STATUS_INVALID_PARAMETER;
    }

    return STATUS_SUCCESS;

}



BOOL
CheckAllowDecryptedRemoteDestinationPolicy()

/*++

Routine Description:

    This routine is used by BasepCopyEncryption (part of CopyFile), when
    an attempt has been made to copy an encrypted file to a destination that
    for some reason can't support encryption (e.g. it's FAT, not trusted for
    delegation, NT4, etc).  By default, copyfile fails for this scenario.  The
    way to override that default is to pass the COPY_FILE_ALLOW_DECRYPTED_DESTINATION
    flag to CopyFile.  The other way to override that default (if you can't update
    your copy utilities to use the new flag), is to set the
    CopyFileAllowDecryptedRemoteDestination system policy.  This routine checks that policy.

    This routine caches the result of the registry check per process.  So an update
    to the policy may require a reboot to take effect in existing processes.

Arguments:

    None

Return Value:

    TRUE  - The decrypted destination is allowed

    FALSE - The destination may not be left decrypted

--*/


{
    // Static flags indicating if we've already been called once, and if
    // so what the answer was.  These are static so that we need to do the registry
    // call only once per process.

    static BOOL Allowed = FALSE;
    static BOOL AlreadyChecked = FALSE;

    NTSTATUS Status;
    HANDLE Key;

    BYTE QueryBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)];
    PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo =
        (PKEY_VALUE_PARTIAL_INFORMATION) QueryBuffer;

    ULONG ActualSize;

    const static UNICODE_STRING KeyName =
        RTL_CONSTANT_STRING( L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows\\System" );

    const static OBJECT_ATTRIBUTES ObjectAttributes =
        RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);

    const static UNICODE_STRING ValueName =
        RTL_CONSTANT_STRING( L"CopyFileAllowDecryptedRemoteDestination" );


    // Check to see if we've already been called once in this process.  If so, 
    // return the value that was calculated then (thus this process needs a reboot
    // to reflect a change to this policy).  Technically there's a race condition here, 
    // but assuming the registry isn't being updated during the call, each call to this
    // routine will get the same answer anyway.

    if( AlreadyChecked )
        return Allowed;

    // We need to do the check.

    // Try to open the system policy key.
    // If it doesn't exist, then we'll just fall through and return false.

    Status = NtOpenKey( &Key,
                        KEY_QUERY_VALUE,
                        (POBJECT_ATTRIBUTES) &ObjectAttributes);

    if (NT_SUCCESS(Status)) {

        // We have the system policy key.  Now try to open the value.  If it
        // doesn't exist, we'll just fall through, and return false.

        Status = NtQueryValueKey(
                    Key,
                    (PUNICODE_STRING) &ValueName,
                    KeyValuePartialInformation,
                    KeyValueInfo,
                    sizeof(QueryBuffer),
                    &ActualSize);

        if (NT_SUCCESS(Status)) {

            // The value exists.  If it's the right shape and value, then 
            // we'll allow the decrypted destination.

            if( KeyValueInfo->Type == REG_DWORD &&
                KeyValueInfo->DataLength == sizeof(DWORD) &&
                *((PDWORD) KeyValueInfo->Data) == 1) {

                Allowed = TRUE;
            }
        }

        NtClose( Key );
    }

    // Update the static so that we don't execute this code again.
    AlreadyChecked = TRUE;

    return Allowed;
}


typedef BOOL (WINAPI *ENCRYPTFILEWPTR)(LPCWSTR);
typedef BOOL (WINAPI *DECRYPTFILEWPTR)(LPCWSTR, DWORD);

BOOL
WINAPI
BasepCopyEncryption( HANDLE hSourceFile,
                     LPCWSTR lpNewFileName,
                     PHANDLE DestFile,
                     POBJECT_ATTRIBUTES Obja,
                     DWORD DestFileAccess,
                     DWORD DestFileSharing,
                     DWORD CreateDisposition,
                     DWORD CreateOptions,
                     DWORD SourceFileAttributes,
                     DWORD SourceFileAttributesMask,
                     PDWORD DestFileAttributes,
                     DWORD DestFileFsAttributes,
                     DWORD CopyFlags,
                     LPCOPYFILE_CONTEXT *lpCopyFileContext )
/*++

Routine Description:

    This is an internal routine that copies the encryption state during
    a copyfile.  Depending on the copy flags, it may be necessary to
    decompress the destination.  To encrypt/decrypt a file it is necessary
    to close the current handle, encrypt/decrypt, and reopen.

Arguments:

    hSourceFile - Provides a handle to the source file.

    lpNewFileName - Provides a name for the target file/stream.

    Obja - ObjectAttributes structure for the destination file.

    DestFileAccess - ACCESS_MASK to use when opening the dest.

    DestFileSharing - Sharing options to use when openting the dest.

    CreateDisposition - Creation/disposition options for opening the dest.

    SourceFileAttributes - FileBasicInformation attributes queried from the
        source file.

    SourceFileAttributesMask - the attributes from the source that are intended
        to be set on the dest.

    DestFileAttributes - FileBasicInformation attributes for the current
        state of the destination file.  This value is updated to reflect
        changes to the encryption state of the dest file.

    DestFileFsAttributes - FileFsAttributeInformation.FileSystemAttributes
        for the file system of the dest file.

    CopyFlags - Provides flags that modify how the copy is to proceed.  See
        CopyFileEx for details.

    lpCopyFileContext - Provides a pointer to a pointer to the context
        information to track callbacks, file sizes, etc. across streams during
        the copy operation.


Return Value:

    TRUE - The operation was successful.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.  The DestFile has already been marked
        for delete.

--*/

{   // BasepCopyEncryption

    NTSTATUS Status = 0;
    BOOL SuccessReturn = FALSE;
    BOOL EncryptFile = FALSE;
    BOOL DecryptFile = FALSE;
    HANDLE Advapi32 = NULL;
    BOOL RestoreReadOnly = FALSE;
    ENCRYPTFILEWPTR EncryptFileWPtr = NULL;
    DECRYPTFILEWPTR DecryptFileWPtr = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    LPCOPYFILE_CONTEXT Context = *lpCopyFileContext;
    FILE_BASIC_INFORMATION FileBasicInformationData;

    try
    {
        // Check to see if we need to do some encryption or decryption,
        // and set EncryptFile/DescryptFile bools if set.

        if( (SourceFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
            (SourceFileAttributesMask & FILE_ATTRIBUTE_ENCRYPTED) &&
            !(*DestFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ) {

            // We tried to copy over encryption, but it didn't stick:
            // *  This may be a system file, encryption is not supported on
            //    system files.
            // *  If this is a non-directory file, then encryption is not
            //    supported on the target file system.
            // *  If this is a directory file, then we must try to encrypt
            //    it manually (since we opened it, rather than creating it).
            //    It still may not be possible but we'll have to try to
            //    find out.

            if( (SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                &&
                !(*DestFileAttributes & FILE_ATTRIBUTE_SYSTEM) ) {
                EncryptFile = TRUE;
            }

        } else if( !(SourceFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
                   (*DestFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
                   (CopyFlags & PRIVCOPY_FILE_SUPERSEDE) ) {

            // The source is decrypted, the destination was encrypted, and the
            // caller specified that the source should be copied as-is.  So
            // we must manually decrypt the destination.  This can happen if
            // the dest file already existed and was encrypted.

            DecryptFile = TRUE;
        }


        // If we decided above to either encrypt or decrypt, then we have
        // more work to do.

        if( DecryptFile || EncryptFile ) {

            // If the destination file is read-only, we have to take it off
            // until we do the encrypt/decrypt (and restore it later).

            if( *DestFileAttributes & FILE_ATTRIBUTE_READONLY ) {

                RestoreReadOnly = TRUE;
                RtlZeroMemory(&FileBasicInformationData, sizeof(FileBasicInformationData));                
                FileBasicInformationData.FileAttributes = (*DestFileAttributes) & ~FILE_ATTRIBUTE_READONLY;

                Status = NtSetInformationFile(
                          *DestFile,
                          &IoStatusBlock,
                          &FileBasicInformationData,
                          sizeof(FileBasicInformationData),
                          FileBasicInformation
                          );
                if( !NT_SUCCESS(Status) ) {
                    BaseMarkFileForDelete( *DestFile, 0 );
                    BaseSetLastNTError(Status);
                    leave;
                }
            }

            // Close the file so that we can call EncryptFile/DecryptFile

            NtClose( *DestFile );
            *DestFile = INVALID_HANDLE_VALUE;

            // Load the EncryptFile/DecryptFile API, and make the call

            Advapi32 = LoadLibraryW(AdvapiDllString);
            if( Advapi32 == NULL ) {
                leave;
            }

            if( EncryptFile ) {
                EncryptFileWPtr = (ENCRYPTFILEWPTR)GetProcAddress(Advapi32, "EncryptFileW");
                if( EncryptFileWPtr == NULL ) {
                    leave;
                }

                if( EncryptFileWPtr(lpNewFileName) )
                    *DestFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED;
            } else {
                DecryptFileWPtr = (DECRYPTFILEWPTR)GetProcAddress(Advapi32, "DecryptFileW");
                if( DecryptFileWPtr == NULL ) {
                    leave;
                }

                if( DecryptFileWPtr(lpNewFileName, 0) )
                    *DestFileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED;
            }

            // The encrypt/decrypt call was successful, so we can reopen the file.

            Status = NtCreateFile(
                        DestFile,
                        DestFileAccess,
                        Obja,
                        &IoStatusBlock,
                        NULL,
                        SourceFileAttributes & FILE_ATTRIBUTE_VALID_FLAGS & SourceFileAttributesMask,
                        DestFileSharing,
                        CreateDisposition,
                        CreateOptions,
                        NULL,
                        0
                        );
            if( !NT_SUCCESS(Status) ) {
                *DestFile = INVALID_HANDLE_VALUE;
                BaseSetLastNTError(Status);
                leave;
            }

            // If we took off the read-only bit above, put it back on now.

            if( RestoreReadOnly ) {

                FileBasicInformationData.FileAttributes |= FILE_ATTRIBUTE_READONLY;

                Status = NtSetInformationFile(
                          *DestFile,
                          &IoStatusBlock,
                          &FileBasicInformationData,
                          sizeof(FileBasicInformationData),
                          FileBasicInformation
                          );

                if( !NT_SUCCESS(Status) ) {
                    BaseMarkFileForDelete( *DestFile, 0 );
                    BaseSetLastNTError(Status);
                    leave;
                }
            }
        }   // if( DecryptFile || EncryptFile )

        // If it's still not encrypted, see if it's OK to leave it that way.

        if( (SourceFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
            && !(*DestFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ) {

            // Either there was an encryption problem (e.g. no keys available)
            // or the target just doesn't support encryption.  See if it's OK
            // to continue with the copy by checking the CopyFlags, by making
            // a callback, or by checking policy.

            BOOL Canceled = FALSE;
            DWORD dwCallbackReason = 0;
            LONG lError = ERROR_ENCRYPTION_FAILED;

            // If the COPY_FILE_ALLOW_DECRYPTED_DESTINATION flag is set, then
            // we can fall through and return success.  Otherwise, we need to do some
            // more checking.

            if( !(CopyFlags & COPY_FILE_ALLOW_DECRYPTED_DESTINATION) ) {

                // There's a policy in the registry which may be set indicating
                // that we can ignore loss of encryption on network targets.
                // If that's set, and this is a remote destination, then the
                // copy can continue.  We check the policy first, because it
                // caches its result.  Consequently, in the typical case, we only
                // check the registry once, and we never make the NtQueryVolInfoFile
                // call.

                if( CheckAllowDecryptedRemoteDestinationPolicy() ) {

                    IO_STATUS_BLOCK IoStatus;
                    FILE_FS_DEVICE_INFORMATION DeviceInformation;

                    // See if the destination is remote

                    DeviceInformation.Characteristics = 0;
                    Status = NtQueryVolumeInformationFile(
                                *DestFile,
                                &IoStatus,
                                &DeviceInformation,
                                sizeof(DeviceInformation),
                                FileFsDeviceInformation
                                );
                    if( NT_SUCCESS(Status) &&
                        (DeviceInformation.Characteristics & FILE_REMOTE_DEVICE) )
                    {
                        // Yes, it's remote, and the policy is set, so 
                        // it's OK to continue.

                        SuccessReturn = TRUE;
                    }
                } // if( CheckAllowDecryptedRemoteDestinationPolicy() )

                // If that didn't work, do we have a callback on which we can
                // check for permission to drop?  We checked the policy first, 
                // because if it allows the copy, we needn't even call the
                // callback.

                if( !SuccessReturn
                    && Context != NULL 
                    && Context->lpProgressRoutine != NULL
                    && (CopyFlags & PRIVCOPY_FILE_METADATA) ) {

                    // Yes, we have an applicable callback.

                    // Figure out what the explanation (dwCallbackReason)
                    // is for the problem.

                    if( DestFileFsAttributes & FILE_SUPPORTS_ENCRYPTION ) {

                        if( !(SourceFileAttributesMask & FILE_ATTRIBUTE_ENCRYPTED) ) {
                            // We opened the file with encryption turned off, so we must
                            // have gotten an access-denied on the first try.

                            dwCallbackReason = PRIVCALLBACK_ENCRYPTION_FAILED;
                        }

                        else if( *DestFileAttributes & FILE_ATTRIBUTE_SYSTEM )
                            dwCallbackReason = PRIVCALLBACK_CANT_ENCRYPT_SYSTEM_FILE;
                        else
                            dwCallbackReason = PRIVCALLBACK_ENCRYPTION_FAILED;
                    }
                    else
                        dwCallbackReason = PRIVCALLBACK_ENCRYPTION_NOT_SUPPORTED;

                    // Make the callback.

                    if( BasepCopyFileCallback( FALSE, // Fail by default
                                               lError,
                                               Context,
                                               NULL,
                                               dwCallbackReason,
                                               hSourceFile,
                                               *DestFile,
                                               &Canceled )) {
                        // We've been given permission to drop the encryption
                        SuccessReturn = TRUE;
                    }
                }   // if( Context != NULL 


                // We checked everything, and nothing allows us to contine,
                // so fail the call.

                if( !SuccessReturn ) {
	            BaseMarkFileForDelete(
		            *DestFile,
		            0 );
	            SetLastError( lError );
	            leave;
                }

            }   // if( !(CopyFlags & COPY_FILE_ALLOW_DECRYPTED_DESTINATION) )
        }   // if( (SourceFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)

        SuccessReturn = TRUE;

    }
    finally
    {
        if (Advapi32 != NULL) {
            FreeLibrary( Advapi32 );
        }
    }

    return( SuccessReturn );
}

DWORD
WINAPI
BaseCopyStream(
    OPTIONAL LPCWSTR lpExistingFileName,
    HANDLE hSourceFile,
    ACCESS_MASK SourceFileAccess OPTIONAL,
    LPCWSTR lpNewFileName,
    HANDLE hTargetFile OPTIONAL,
    LARGE_INTEGER *lpFileSize,
    LPDWORD lpCopyFlags,
    LPHANDLE lpDestFile,
    LPDWORD lpCopySize,
    LPCOPYFILE_CONTEXT *lpCopyFileContext,
    LPRESTART_STATE lpRestartState OPTIONAL,
    BOOL OpenFileAsReparsePoint,
    DWORD dwReparseTag,
    PDWORD DestFileFsAttributes
    )

/*++

Routine Description:

    This is an internal routine that copies an entire file (default data stream
    only), or a single stream of a file.  If the hTargetFile parameter is
    present, then only a single stream of the output file is copied.  Otherwise,
    the entire file is copied.

Arguments:

    hSourceFile - Provides a handle to the source file.

    SourceFileAccess - The ACCESS_MASK bits used to open the source file handle.
        This variable is only used with the PRIVCOPY_FILE_* flags.

    lpNewFileName - Provides a name for the target file/stream.

    hTargetFile - Optionally provides a handle to the target file.  If the
        stream being copied is an alternate data stream, then this handle must
        be provided.

    lpFileSize - Provides the size of the input stream.

    lpCopyFlags - Provides flags that modify how the copy is to proceed.  See
        CopyFileEx for details.

    lpDestFile - Provides a variable to store the handle to the target file.

    lpCopySize - Provides variable to store size of copy chunks to be used in
        copying the streams.  This is set for the file, and then reused on
        alternate streams.

    lpCopyFileContext - Provides a pointer to a pointer to the context
        information to track callbacks, file sizes, etc. across streams during
        the copy operation.

    lpRestartState - Optionally provides storage to maintain restart state
        during the copy operation.  This pointer is only valid if the caller
        has specified the COPY_FILE_RESTARTABLE flag in the lpCopyFlags word.

    OpenFileAsReparsePoint - Flag to indicate whether the target file is to
        be opened as a reparse point or not.

    DestFileFsAttributes - If hTargetFile is present, provides a location to
        store the destination file's filesystem attributes.  If hTargetFile
        is not present, provides those attributes to this routine.

Return Value:

    TRUE - The operation was successful.

    SUCCESS_RETURNED_STATE - The operation was successful, but extended
        information was returned in the restart state structure.

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{   // BaseCopyStream

    HANDLE DestFile = INVALID_HANDLE_VALUE;
    HANDLE Section;
    NTSTATUS Status;
    PVOID SourceBase, IoDestBase;
    PCHAR SourceBuffer;
    LARGE_INTEGER SectionOffset;
    LARGE_INTEGER BytesWritten;
    SIZE_T BigViewSize;
    ULONG ViewSize;
    ULONG BytesToWrite;
    ULONG BytesRead;
    FILE_BASIC_INFORMATION FileBasicInformationData;
    FILE_END_OF_FILE_INFORMATION EndOfFileInformation;
    IO_STATUS_BLOCK IoStatus;
    LPCOPYFILE_CONTEXT Context = *lpCopyFileContext;
    DWORD ReturnCode;
    DWORD b;
    BOOL Restartable;
    DWORD ReturnValue = FALSE;
    DWORD WriteCount = 0;
    DWORD FlagsAndAttributes;
    DWORD DesiredAccess;
    DWORD DestFileAccess;
    DWORD DestFileSharing;
    DWORD DesiredCreateDisposition;
    DWORD CreateDisposition;
    BOOL Canceled = FALSE;
    DWORD SourceFileAttributes;
    DWORD SourceFileAttributesMask;
    DWORD BlockSize;
    BOOL fSkipBlock;
    UNICODE_STRING DestFileName;
    PVOID DestFileNameBuffer = NULL;
    RTL_RELATIVE_NAME DestRelativeName;
    OBJECT_ATTRIBUTES Obja;
    SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
    FILE_EA_INFORMATION EaInfo;
    PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
    ULONG EaSize = 0;
    BOOL EasDropped = FALSE;
    IO_STATUS_BLOCK IoStatusBlock;
    WCHAR SaveStaticUnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];


    // Default the size of copy chunks
    *lpCopySize = BASE_COPY_FILE_CHUNK;

    // The lpExistingFileName sits in the TEB buffer, which has a tendency
    // to get trashed (e.g. LoadLibaryW).  So use a local buffer.

    if( lpExistingFileName == NtCurrentTeb()->StaticUnicodeBuffer ) {

        memcpy( SaveStaticUnicodeBuffer,
                NtCurrentTeb()->StaticUnicodeBuffer,
                STATIC_UNICODE_BUFFER_LENGTH );
        lpExistingFileName = SaveStaticUnicodeBuffer;
    }

    //
    //  Get times and attributes for the file if the entire file is being
    //  copied
    //

    Status = NtQueryInformationFile(
                hSourceFile,
                &IoStatus,
                (PVOID) &FileBasicInformationData,
                sizeof(FileBasicInformationData),
                FileBasicInformation
                );

    SourceFileAttributes = NT_SUCCESS(Status) ?
                             FileBasicInformationData.FileAttributes :
                             0;

    if ( !ARGUMENT_PRESENT(hTargetFile) ) {

        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            return FALSE;
        }
    } else {

        //
        //  A zero in the file's attributes informs latter DeleteFile that
        //  this code does not know what the actual file attributes are so
        //  that this code does not actually have to retrieve them for each
        //  stream, nor does it have to remember them across streams.  The
        //  error path will simply get them if needed.
        //

        FileBasicInformationData.FileAttributes = 0;
    }

    //
    // We don't allow restartable copies of directory files, because the
    // unnamed data stream is used to store restart context, and directory files
    // don't have an unnamed data stream.
    //

    Restartable = (*lpCopyFlags & COPY_FILE_RESTARTABLE) != 0;
    if( Restartable && SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
        Restartable = FALSE;
        *lpCopyFlags &= ~COPY_FILE_RESTARTABLE;
    }


    try {

        //
        // Create the destination file or alternate data stream
        //

        SourceBase = NULL;
        IoDestBase = NULL;
        Section = NULL;

        if ( !ARGUMENT_PRESENT(hTargetFile) ) {

            ULONG CreateOptions = 0, DesiredCreateOptions = 0;
            BOOL TranslationStatus = FALSE;
            PFILE_FULL_EA_INFORMATION EaBufferToUse = NULL;
            DWORD SourceFileFsAttributes = 0;
            ULONG EaSizeToUse = 0;

            // We're being called to copy the unnamed stream of the file, and
            // we need to create the file itself.

            DWORD DestFileAttributes = 0;
            struct {
                FILE_FS_ATTRIBUTE_INFORMATION Info;
                WCHAR Buffer[ MAX_PATH ];
            } FileFsAttrInfoBuffer;

            //
            // Begin by determining how the target file is to be opened based
            // on whether or not the copy operation is to be restartable.
            //

            if ( Restartable ) {

                b = BasepOpenRestartableFile( hSourceFile,
                                              lpNewFileName,
                                              &DestFile,
                                              *lpCopyFlags,
                                              lpRestartState,
                                              lpFileSize,
                                              lpCopyFileContext,
                                              FileBasicInformationData.FileAttributes,
                                              OpenFileAsReparsePoint );

                if( b == SUCCESS_RETURNED_STATE ) {
                    // We've picked up in the middle of a restartable copy.
                    // The destination file handle is in DestFile, which will
                    // be given back to our caller below in the finally.

                    if ( BasepRemoteFile(hSourceFile,DestFile) ) {
                        *lpCopySize = BASE_COPY_FILE_CHUNK - 4096;
                    }
                    ReturnValue = b;
                    leave;
                } else if( b == FALSE ) {
                    // There was a fatal error.
                    leave;
                }

                // Otherwise we should copy the first stream.  If we are to restart copying
                // in that stream, DestFile will be valid.

            }

            //
            // If the dest file is not already opened (the restart case), open it now.
            //

            if( DestFile == INVALID_HANDLE_VALUE ) {

                BOOL EndsInSlash = FALSE;
                UNICODE_STRING Win32NewFileName;
                PUNICODE_STRING lpConsoleName = NULL;
                FILE_BASIC_INFORMATION DestBasicInformation;

                //
                // Determine the create options
                //

                CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;

                if( SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
                    CreateOptions |= FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT;
                else
                    CreateOptions |= FILE_NON_DIRECTORY_FILE  | FILE_SEQUENTIAL_ONLY;

                if( *lpCopyFlags & (PRIVCOPY_FILE_BACKUP_SEMANTICS|PRIVCOPY_FILE_OWNER_GROUP) )
                    CreateOptions |= FILE_OPEN_FOR_BACKUP_INTENT;


                //
                // Determine the create disposition
                //
                // Directory files are copied with merge semantics.  The rationale
                // is that copying of a directory tree has merge semantics wrt the
                // contained files, so copying of a directory file should also have
                // merge semantics.
                //

                if( SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
                    CreateDisposition = (*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ? FILE_CREATE : FILE_OPEN_IF;
                else
                    CreateDisposition = (*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ? FILE_CREATE : FILE_OVERWRITE_IF;


                //
                // Determine what access is necessary based on what is being copied
                //

                DesiredAccess = SYNCHRONIZE | FILE_READ_ATTRIBUTES | GENERIC_WRITE | DELETE;

                if( SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
                    // We may or may not be able to get FILE_WRITE_DATA access, necessary for
                    // setting compression.
                    DesiredAccess &= ~GENERIC_WRITE;
                    DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_LIST_DIRECTORY;
                }


                if( *lpCopyFlags & PRIVCOPY_FILE_METADATA ) {
                    // We need read access for compression, write_dac for the DACL
                    DesiredAccess |= GENERIC_READ | WRITE_DAC;
                }

                if( *lpCopyFlags & PRIVCOPY_FILE_OWNER_GROUP ) {
                    DesiredAccess |= WRITE_OWNER;
                }

                if( (*lpCopyFlags & PRIVCOPY_FILE_SACL)
                    &&
                    (SourceFileAccess & ACCESS_SYSTEM_SECURITY) ) {
                    // Don't bother trying to get access_system_security unless it was
                    // successfully obtained on the source (requires SeSecurityPrivilege)
                    DesiredAccess |= ACCESS_SYSTEM_SECURITY;
                }

                SourceFileAttributesMask = ~0;

                if ( OpenFileAsReparsePoint ) {
                    //
                    // The target has to be opened as reparse point. If the open
                    // below fails, the source is to be closed and re-opened
                    // without inhibiting the reparse point behavior.
                    //

                    CreateOptions |= FILE_OPEN_REPARSE_POINT;
                    DesiredAccess = (DesiredAccess & ~DELETE) | GENERIC_READ;
                    CreateDisposition = (*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ? FILE_CREATE : FILE_OPEN_IF;
                }

                DesiredCreateOptions = CreateOptions;
                DesiredCreateDisposition = CreateDisposition;
		
                //
                // Get the Win32 path in a unicode_string, and get the NT path
                //

                RtlInitUnicodeString( &Win32NewFileName, lpNewFileName );

                if ( lpNewFileName[(Win32NewFileName.Length >> 1)-1] == (WCHAR)'\\' ) {
                    EndsInSlash = TRUE;
                }
                else {
                    EndsInSlash = FALSE;
                }

                TranslationStatus = RtlDosPathNameToNtPathName_U(
                                        lpNewFileName,
                                        &DestFileName,
                                        NULL,
                                        &DestRelativeName
                                        );

                if ( !TranslationStatus ) {
                    SetLastError(ERROR_PATH_NOT_FOUND);
                    DestFile = INVALID_HANDLE_VALUE;
                    leave;
                    }
                DestFileNameBuffer = DestFileName.Buffer;

                if ( DestRelativeName.RelativeName.Length ) {
                    DestFileName = *(PUNICODE_STRING)&DestRelativeName.RelativeName;
                }
                else {
                    DestRelativeName.ContainingDirectory = NULL;
                }

                InitializeObjectAttributes(
                    &Obja,
                    &DestFileName,
                    OBJ_CASE_INSENSITIVE,
                    DestRelativeName.ContainingDirectory,
                    NULL
                    );

                SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
                SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
                SecurityQualityOfService.EffectiveOnly = TRUE;
                SecurityQualityOfService.Length = sizeof( SECURITY_QUALITY_OF_SERVICE );

                Obja.SecurityQualityOfService = &SecurityQualityOfService;

                //
                //  Get the EAs
                //

                EaBuffer = NULL;
                EaSize = 0;

                Status = NtQueryInformationFile(
                            hSourceFile,
                            &IoStatusBlock,
                            &EaInfo,
                            sizeof(EaInfo),
                            FileEaInformation
                            );
                if ( NT_SUCCESS(Status) && EaInfo.EaSize ) {

                    EaSize = EaInfo.EaSize;

                    do {

                        EaSize *= 2;
                        EaBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), EaSize);
                        if ( !EaBuffer ) {
                            BaseSetLastNTError(STATUS_NO_MEMORY);
                            leave;
                        }

                        Status = NtQueryEaFile(
                                    hSourceFile,
                                    &IoStatusBlock,
                                    EaBuffer,
                                    EaSize,
                                    FALSE,
                                    (PVOID)NULL,
                                    0,
                                    (PULONG)NULL,
                                    TRUE
                                    );

                        if ( !NT_SUCCESS(Status) ) {
                            RtlFreeHeap(RtlProcessHeap(), 0,EaBuffer);
                            EaBuffer = NULL;
                            IoStatusBlock.Information = 0;
                        }

                    } while ( Status == STATUS_BUFFER_OVERFLOW ||
                              Status == STATUS_BUFFER_TOO_SMALL );


                    EaSize = (ULONG)IoStatusBlock.Information;

                }   // if ( NT_SUCCESS(Status) && EaInfo.EaSize )


                //
                // Open the destination file.  If the destination is a console name,
                // open as such, otherwise loop until we find a way to open it with
                // NtCreateFile.
                //

                DestFileAccess = DesiredAccess;
                DestFileSharing = 0;
                EaBufferToUse = EaBuffer;
                EaSizeToUse = EaSize;

                if( (lpConsoleName = BaseIsThisAConsoleName( &Win32NewFileName, GENERIC_WRITE )) ) {

                    DestFileAccess = DesiredAccess = GENERIC_WRITE;
                    DestFileSharing = FILE_SHARE_READ | FILE_SHARE_WRITE;

                    if( EaBuffer != NULL )
                        EasDropped = TRUE;  // We're not copying the EAs

                    DestFile= OpenConsoleW( lpConsoleName->Buffer,
                                            DestFileAccess,
                                            FALSE,  // Not inheritable
                                            DestFileSharing
                                           );

                    if ( DestFile == INVALID_HANDLE_VALUE ) {
                        BaseSetLastNTError(STATUS_ACCESS_DENIED);
                        NtClose( DestFile );
                        DestFile = INVALID_HANDLE_VALUE;
                        leave;
                    }

                }

                //
                // Even if the source is offline, the destination should
                // not be (at least not as part of the copy).
                //
                SourceFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE;


                //
                //  If the source file was encrypted and if we are intending
                //  to create/overwrite/supersede the destination, attempt
                //  to establish the encryption state first by calling 
                //  DuplicateEncryptionInfoFile.  This API not only makes
                //  the target file encrypted, it also copies over the source's
                //  $efs stream (i.e. everyone who had access to the source file
                //  will have access to the dest file).
                //
                //

                if (!(SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                    && (SourceFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
                    && (SourceFileAttributesMask & FILE_ATTRIBUTE_ENCRYPTED)
                    && (CreateDisposition == FILE_CREATE
                        || CreateDisposition == FILE_OVERWRITE_IF)) {

                    // We'll attempt the DuplicateEncryptionInfoCall.

                    DWORD Win32CreateDisposition;
                    DWORD LastError;

                    // Convert the NT create-disposition flags into a Win32 version.

                    Status = BasepCreateDispositionToWin32( CreateDisposition,
                                                            &Win32CreateDisposition );
                    if( !NT_SUCCESS(Status) ) {
                        BaseSetLastNTError( Status );

                    } else {

                        // Mask out the read-only bit for now, so that we can
                        // do an NtCreateFile after this DuplicateEncryptionInfoFile
                        
                        SourceFileAttributesMask &= ~FILE_ATTRIBUTE_READONLY;
                        
                        // DuplicateEncryptionInfoFile returns the error code.
                        // The "pfn" version of this API is a lazy-loader, so we
                        // don't have to implicitely link against advapi32.

                        LastError = pfnDuplicateEncryptionInfoFile(
                                                      lpExistingFileName, 
                                                      lpNewFileName, 
                                                      Win32CreateDisposition, 
                                                      SourceFileAttributes
                                                        & FILE_ATTRIBUTE_VALID_FLAGS
                                                        & SourceFileAttributesMask,
                                                      NULL );
                        if( LastError != 0 ) {
                            
                            // 
                            // We'll fall through and try using NtCreateFile.  That,
                            // at least, will try to encrypt the target via the
                            // FILE_ATTRIBUTE_ENCRYPTED bit.  Not as good as
                            // DupEncInfo, but better than leaving plain text.
                            //
                            SetLastError( LastError );
                        } else {
                        
                            //
                            //  Destination was created.  Now make it open
                            //
                        
                            CreateDisposition = FILE_OPEN;
                        }
                    }
                }   // if (!(SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                
                
                //
                // Open the destination file. This can take some effort & retries,
                // because there are so many scenarios for the target file
                // (e.g. different destination servers have different capabilities).
                //

                while( DestFile == NULL || DestFile == INVALID_HANDLE_VALUE ) {

                    // Attempt to create the destination

                    Status = NtCreateFile(
                                &DestFile,
                                DestFileAccess,
                                &Obja,
                                &IoStatusBlock,
                                NULL,
                                SourceFileAttributes
                                    & FILE_ATTRIBUTE_VALID_FLAGS
                                    & SourceFileAttributesMask,
                                DestFileSharing,
                                CreateDisposition,
                                CreateOptions,
                                EaBufferToUse,
                                EaSizeToUse
                                );

                    if( !NT_SUCCESS(Status) ) {

                        // Set the last error and fall through.  We will attempt below to
                        // resolve the problem and try again.

                        BaseSetLastNTError( Status );


                    } else {

                        //
                        // We successfully created the file.  For some special cases,
                        // we must post-process this create before continuing with the copy.
                        //

                        if( (SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
                                 CreateDisposition == FILE_OPEN &&
                                 (DestFileAccess & FILE_WRITE_DATA) == FILE_WRITE_DATA &&
                                 (CreateOptions & FILE_DIRECTORY_FILE) == FILE_DIRECTORY_FILE ) {

                            //
                            // If we're copying to NT4, a previous iteration through this
                            // large while loop switched the CreateDisposition from
                            // FILE_OPENIF to FILE_OPEN; otherwise, NT4 fails the open
                            // (when passing FILE_OPENIF and FILE_WRITE_DATA to a directory
                            // file that already exists).  The open worked, but the problem
                            // is that now if we need to set compression on the target, we'll
                            // get status_invalid_parameter because the FILE_DIRECTORY_FILE
                            // CreateOption was set.  So, to allow compression to work, and
                            // since at this point we already know the target is a directory
                            // file, we can re-open it without that create option.
                            //

                            CreateOptions &= ~FILE_DIRECTORY_FILE;

                            NtClose( DestFile );
                            Status = NtCreateFile(
                                        &DestFile,
                                        DestFileAccess,
                                        &Obja,
                                        &IoStatusBlock,
                                        NULL,
                                        SourceFileAttributes & FILE_ATTRIBUTE_VALID_FLAGS & SourceFileAttributesMask,
                                        DestFileSharing,
                                        CreateDisposition,
                                        CreateOptions,
                                        EaBufferToUse,
                                        EaSizeToUse
                                        );
                            if( !NT_SUCCESS(Status) ) {

                                // But if that didn't work, go back to the combination that
                                // did (this happens on Samba servers).

                                CreateOptions |= FILE_DIRECTORY_FILE;
                                Status = NtCreateFile(
                                            &DestFile,
                                            DestFileAccess,
                                            &Obja,
                                            &IoStatusBlock,
                                            NULL,
                                            SourceFileAttributes & FILE_ATTRIBUTE_VALID_FLAGS & SourceFileAttributesMask,
                                            DestFileSharing,
                                            CreateDisposition,
                                            CreateOptions,
                                            EaBufferToUse,
                                            EaSizeToUse
                                            );

                                if( !NT_SUCCESS(Status) ) {
                                    DestFile = INVALID_HANDLE_VALUE;
                                    BaseSetLastNTError( Status );
                                    leave;
                                }
                            }
                        }
                        else if( (SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
                                 CreateDisposition == FILE_OPEN_IF &&
                                 lpConsoleName == NULL ) {
                        
                            //
                            // Compatibility hack:  We successfully created the target, but 
                            // some servers (SCO VisionFS) get confused by the FILE_OPEN_IF
                            // flag and create a non-directory file instead.  Check to see if
                            // this hapenned, and if so deleted it and re-create with FILE_CREATE
                            // instead.  This is a perf hit that we have to query the file attributes,
                            // but at least it is not a net round-trip because the rdr caches the
                            // file attributes in Create&X.
                            //


                            FILE_BASIC_INFORMATION NewDestInfo;

                            Status = NtQueryInformationFile( DestFile,
                                                             &IoStatus,
                                                             &NewDestInfo,
                                                             sizeof(NewDestInfo),
                                                             FileBasicInformation );
                            if( !NT_SUCCESS(Status) ) {
                                BaseMarkFileForDelete( DestFile, 0 );
                                BaseSetLastNTError(Status);
                                leave;
                            }

                            if( !(NewDestInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {

                                // Yes, a non-directory file got created.  Delete it, then
                                // try again without FILE_OPEN_IF.

                                BaseMarkFileForDelete( DestFile,
                                                       NewDestInfo.FileAttributes );
                                NtClose( DestFile );
                                DestFile = INVALID_HANDLE_VALUE;

                                CreateDisposition = FILE_CREATE;

                                // Also, if we request FILE_WRITE_DATA access, the 
                                // directory gets created but the NtCreateFile call
                                // returns status_object_name_collision.  Since this
                                // is a very VisionFS-specific workaround, we'll just
                                // turn off that bit

                                DestFileAccess &= ~FILE_WRITE_DATA;

                                continue;
                            }
                        }

                        if( (FileBasicInformationData.FileAttributes & FILE_ATTRIBUTE_READONLY)
                            &&
                            !(SourceFileAttributesMask & FILE_ATTRIBUTE_READONLY) ) {

                            // The read-only bit was turned off, and must now be
                            // reset (it gets turned off when we call DuplicateEncryptionInfo,
                            // since that API does not return a handle).

                            Status = NtSetInformationFile(
                                      DestFile,
                                      &IoStatus,
                                      &FileBasicInformationData,
                                      sizeof(FileBasicInformationData),
                                      FileBasicInformation
                                      );
                            if( !NT_SUCCESS(Status) ) {
                                BaseMarkFileForDelete( DestFile, 0 );
                                BaseSetLastNTError(Status);
                                leave;
                            }
                        }

                        break;  // while( TRUE )

                    }   // NtCreateFile ... if( !NT_SUCCESS(Status) ) ... else

                    // If we reach this point, some error has occurred in the attempt to 
                    // create the file.


                    //
                    // If a file/directory already exists and we can't overwrite it,
                    // abort now.
                    //

                    if ( (*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) &&
                         (STATUS_OBJECT_NAME_COLLISION == Status) ) {

                        // Not allowed to overwrite an existing file.
                        SetLastError( ERROR_FILE_EXISTS );
                        DestFile = INVALID_HANDLE_VALUE;
                        leave;

                    } else if ( Status == STATUS_FILE_IS_A_DIRECTORY ) {

                        // Not allowed to overwrite a directory with a file.
                        if ( EndsInSlash ) {
                            SetLastError(ERROR_PATH_NOT_FOUND);
                        }
                        else {
                            SetLastError(ERROR_ACCESS_DENIED);
                        }
                        DestFile = INVALID_HANDLE_VALUE;
                        leave;
                    }

                    //
                    // If we're trying to create a directory, and a non-directory
                    // file already exists by that name, we need to manually delete
                    // it (FILE_OVERWRITE isn't valid for a directory file).
                    //

                    if( (*lpCopyFlags & PRIVCOPY_FILE_DIRECTORY) &&
                        Status == STATUS_NOT_A_DIRECTORY &&
                        !(*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ) {

                        Status = NtCreateFile(
                                    &DestFile,
                                    DELETE|SYNCHRONIZE,
                                    &Obja,
                                    &IoStatusBlock,
                                    NULL,
                                    FILE_ATTRIBUTE_NORMAL,
                                    0,
                                    FILE_OPEN,
                                    FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT,
                                    NULL,
                                    0
                                    );
                        if( !NT_SUCCESS(Status) ) {
                            BaseSetLastNTError(Status);
                            DestFile = INVALID_HANDLE_VALUE;
                            leave;
                        }

                        NtClose( DestFile );
                        DestFile = INVALID_HANDLE_VALUE;

                        continue;
                    }


                    //
                    // Some sharing and access errors can be handled
                    // by reducing the access we request on the target
                    // file.
                    //

                    if( GetLastError() == ERROR_SHARING_VIOLATION ||
                        GetLastError() == ERROR_ACCESS_DENIED ) {

                        //
                        // If the create failed because of a sharing violation or because access
                        // was denied, attempt to open the file and allow other readers and
                        // writers.
                        //

                        if( (DestFileSharing & (FILE_SHARE_READ|FILE_SHARE_WRITE))
                            != (FILE_SHARE_READ|FILE_SHARE_WRITE) ) {

                            DestFileSharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
                            continue;
                        }

                        //
                        // If this failed as well, then attempt to open w/o specifying
                        // delete access.  It is probably not necessary to have delete
                        // access to the file anyway, since it will not be able to clean
                        // it up because it's probably open.  However, this is not
                        // necessarily the case.
                        //

                        else if ( (DestFileAccess & DELETE) ) {

                            DestFileAccess &= ~DELETE;
                            continue;
                        }

                    }



                    //
                    // If the destination has not been successfully created/opened, see
                    // if it's because EAs aren't supported
                    //

                    if( EaBufferToUse != NULL
                        &&
                        GetLastError() == ERROR_EAS_NOT_SUPPORTED ) {

                        // Attempt the create again, but don't use the EAs

                        EasDropped = TRUE;
                        EaBufferToUse = NULL;
                        EaSizeToUse = 0;
                        DestFileAccess = DesiredAccess;
                        DestFileSharing = 0;
                        continue;

                    }   // if( EaBufferToUse != NULL ...

                    // If we still have an access-denied problem, try dropping
                    // the WRITE_DAC or WRITE_OWNER access

                    if(( GetLastError() == ERROR_ACCESS_DENIED  ) 
                       && (DestFileAccess & (WRITE_DAC | WRITE_OWNER)) ) {

                        // If WRITE_DAC is set, try turning it off.

                        if( DestFileAccess & WRITE_DAC ) {
                            DestFileAccess &= ~WRITE_DAC;
                        }

                        // Or, if WRITE_OWNER is set, try turning it off.  We'll
                        // turn WRITE_DAC back on if it was previously turned off.  Then,
                        // if this still doesn't work, then the next iteration will turn
                        // WRITE_DAC back off, thus covering both scenarios.

                        else if( DestFileAccess & WRITE_OWNER ) {
                            DestFileAccess &= ~WRITE_OWNER;
                            DestFileAccess |= (DesiredAccess & WRITE_DAC);
                        }

                        DestFileSharing = 0;
                        continue;
                    } 


                    //
                    // 
                    // We might be having a problem copying encryption.  E.g.
                    // we might get an access-denied because the remote target machine
                    // isn't trusted for delegation.
                    // We'll try copying without encryption.  If that works, then later, in
                    // BasepCopyEncryption, we'll see if it's OK that we lost
                    // encryption.
                    //

                    if ( (SourceFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) 
                         && (SourceFileAttributesMask & FILE_ATTRIBUTE_ENCRYPTED) )
                    {

                        // Try taking the encryption bit out of the
                        // attributes we pass to NtCreateFile.

                        SourceFileAttributesMask &= ~FILE_ATTRIBUTE_ENCRYPTED;
                        CreateOptions = DesiredCreateOptions;
                        DestFileAccess = DesiredAccess;
                        DestFileSharing = 0;

                        continue;
                    }


                    //
                    // NT4 returns invalid-parameter error on an attempt to open 
                    // a directory file with both FILE_WRITE_DATA and FILE_OPEN_IF.
                    // Samba 2.x returns ERROR_ALREADY_EXISTS, even though
                    // the semantics of FILE_OPEN_IF says that it should open the
                    // existing directory.
                    // For both cases, we'll try it with FILE_OPEN.
                    //

                    if( ( GetLastError() == ERROR_INVALID_PARAMETER  ||
                          GetLastError() == ERROR_ALREADY_EXISTS ) &&
                        (SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
                        CreateDisposition == FILE_OPEN_IF )  {
                        
                        CreateDisposition = FILE_OPEN;

                        SourceFileAttributesMask = ~0;
                        CreateOptions = DesiredCreateOptions;
                        DestFileAccess = DesiredAccess;
                        DestFileSharing = 0;
                        continue;
                    }

                    //
                    // Some downlevel servers don't allow a directory to be opened for write_data
                    // access.  We need write_data in order to set compression, but the
                    // downlevel server likely won't support that anyway.  (This happens on
                    // NTFS4 if the target directory file doesn't already exist.  In this
                    // case the compression will get copied over anyway as part of the create.)
                    //

                    if( (SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
                        (DestFileAccess & FILE_WRITE_DATA) ) {

                        DestFileAccess = DesiredAccess & ~FILE_WRITE_DATA;

                        CreateDisposition = DesiredCreateDisposition;
                        CreateOptions = DesiredCreateOptions;
                        DestFileSharing = 0;
                        continue;
                    }

                    // If we reach this point, we've run out of options and must give up.
                    DestFile = INVALID_HANDLE_VALUE;
                    leave;

                }   // while( DestFile == INVALID_HANDLE_VALUE )
                // If we reach this point, we've successfully opened the dest file.

                //
                // If we lost the EAs, check to see if that's OK before carrying on.
                //

                if( EasDropped && (*lpCopyFlags & PRIVCOPY_FILE_METADATA) ) {

                    // Check to see if it's OK that we skip the EAs.

                    if( !BasepCopyFileCallback( TRUE,    // Continue by default
                                                ERROR_EAS_NOT_SUPPORTED,
                                                Context,
                                                NULL,
                                                PRIVCALLBACK_EAS_NOT_SUPPORTED,
                                                hSourceFile,
                                                INVALID_HANDLE_VALUE,
                                                &Canceled
                                                ) ) {
                        // Not OK.  The last error has already been set.
                        if( Canceled ) {
                            BaseMarkFileForDelete(
                                DestFile,
                                0
                                );
                        }
                        NtClose( DestFile );
                        DestFile = INVALID_HANDLE_VALUE;
                        leave;
                    }
                }
		
                //
                // When appropriate, copy the reparse point.
                //

                if ( OpenFileAsReparsePoint &&
                     (DestFile != INVALID_HANDLE_VALUE)) {
                    DWORD CopyResult = FALSE;

                    CopyResult = CopyReparsePoint(
                                     hSourceFile,
                                     DestFile
                                     );

                    if ( !CopyResult ) {
                        //
                        // Note that when OpenFileAsReparsePoint is TRUE, by
                        // exiting at this point the effect is that the caller
                        // will re-start the copy without inhibiting the reparse
                        // behavior.
                        //

                        //If we fail here, we may be leaving a newly created
                        // file around at the destination.  If
                        // COPY_FILE_FAIL_IF_EXISTS has been specified,
                        // further retries will fail.  Therefore we need to
                        // try to delete the new file here.
                        if (*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS)
                        {
                            FILE_DISPOSITION_INFORMATION Disposition = {TRUE};

                            Status = NtSetInformationFile(
                                DestFile,
                                &IoStatus,
                                &Disposition,
                                sizeof(Disposition),
                                FileDispositionInformation
                                );
                            //Ignore an error if there is one.

                        }
                        *lpDestFile = DestFile;
                        leave;
                    }
                }   // if ( OpenFileAsReparsePoint &&(DestFile != INVALID_HANDLE_VALUE))


                //
                // Get the File & FileSys attributes for the target volume, plus
                // the FileSys attributes for the source volume.  Ignore errors in
                // the target, e.g. it might be a printer and not support these calls
                // (just assume the attrs in this case are zero).
                //

                *DestFileFsAttributes = 0;
                SourceFileFsAttributes = 0;
                DestFileAttributes = 0;

                Status = NtQueryVolumeInformationFile( DestFile,
                                                       &IoStatus,
                                                       &FileFsAttrInfoBuffer.Info,
                                                       sizeof(FileFsAttrInfoBuffer),
                                                       FileFsAttributeInformation );

                if( NT_SUCCESS(Status) ) {
                    *DestFileFsAttributes = FileFsAttrInfoBuffer.Info.FileSystemAttributes;
                }

                if( lpConsoleName == NULL ) {
                    Status = NtQueryInformationFile( DestFile,
                                                     &IoStatus,
                                                     &DestBasicInformation,
                                                     sizeof(DestBasicInformation),
                                                     FileBasicInformation );
                    if( NT_SUCCESS(Status) ) {
                        DestFileAttributes = DestBasicInformation.FileAttributes;
                    }
                }

                Status = NtQueryVolumeInformationFile( hSourceFile,
                                                       &IoStatus,
                                                       &FileFsAttrInfoBuffer.Info,
                                                       sizeof(FileFsAttrInfoBuffer),
                                                       FileFsAttributeInformation );
                if( NT_SUCCESS(Status) ) {
                    SourceFileFsAttributes = FileFsAttrInfoBuffer.Info.FileSystemAttributes;
                } else {
                    BaseMarkFileForDelete( DestFile, 0 );
                    BaseSetLastNTError(Status);
                    leave;
                }

                //
                // If requested and applicable, copy one or more of the the DACL, SACL, owner, and group.
                // If the source doesn't support persistent ACLs, assume that that means that
                // it doesn't support any of DACL, SACL, and owner/group.
                //

                if( (SourceFileFsAttributes & FILE_PERSISTENT_ACLS)
                    &&
                    (*lpCopyFlags & (PRIVCOPY_FILE_METADATA | PRIVCOPY_FILE_SACL | PRIVCOPY_FILE_OWNER_GROUP)) ) {

                    SECURITY_INFORMATION SecurityInformation = 0;

                    if( *lpCopyFlags & PRIVCOPY_FILE_METADATA
                        && !(*lpCopyFlags & PRIVCOPY_FILE_SKIP_DACL) ) {

                        // Copy the DACL if metadata flag is set, but skip_dacl is not.
                        // The skip_dacl flag is a temporary workaround for a problem
                        // in CSC & roaming profiles.

                        SecurityInformation |= DACL_SECURITY_INFORMATION;
                    }

                    if( *lpCopyFlags & PRIVCOPY_FILE_SACL )
                        SecurityInformation |= SACL_SECURITY_INFORMATION;

                    if( *lpCopyFlags & PRIVCOPY_FILE_OWNER_GROUP )
                        SecurityInformation |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION;

                    if( SecurityInformation != 0 ) {

                        if( !BasepCopySecurityInformation( lpExistingFileName,
                                                           hSourceFile,
                                                           SourceFileAccess,
                                                           lpNewFileName,
                                                           DestFile,
                                                           DestFileAccess,
                                                           SecurityInformation,
                                                           Context,
                                                           *DestFileFsAttributes,
                                                           &Canceled )) {

                            if( Canceled ) {
                                BaseMarkFileForDelete(
                                    DestFile,
                                    0
                                    );
                            }
                            leave;
                        }
                    }
                }

                //
                // Copy compression and encryption
                //

                if( (*lpCopyFlags & PRIVCOPY_FILE_METADATA) ) {

                    BOOL DoCompression = FALSE;
                    int i = 0;

                    // Compression and encryption must be handled in the proper
                    // order, since a file can't be both at once.  For example,
                    // if copying (with supersede) a compressed/unencrypted file over an
                    // uncompressed/encrypted file, we must decrypt the dest
                    // before attempting to compress it.

                    if( DestFileAttributes & FILE_ATTRIBUTE_COMPRESSED ) {
                        // Handle compression first
                        DoCompression = TRUE;
                    }

                    for( i = 0; i < 2; i++ ) {

                        if( DoCompression ) {

                            DoCompression = FALSE;
                            b = BasepCopyCompression( hSourceFile,
                                                      DestFile,
                                                      SourceFileAttributes,
                                                      DestFileAttributes,
                                                      *DestFileFsAttributes,
                                                      *lpCopyFlags,
                                                      &Context );

                        } else {

                            DoCompression = TRUE;
                            b = BasepCopyEncryption( hSourceFile,
                                                     lpNewFileName,
                                                     &DestFile,
                                                     &Obja,
                                                     DestFileAccess,
                                                     DestFileSharing,
                                                     CreateDisposition,
                                                     CreateOptions,
                                                     SourceFileAttributes,
                                                     SourceFileAttributesMask,
                                                     &DestFileAttributes,
                                                     *DestFileFsAttributes,
                                                     *lpCopyFlags,
                                                     &Context );
                        }

                        if( !b ) {
                            // The dest file is already marked for delete and
                            // last error has been set.
                            leave;
                        }
                    }   // for( i = 0; i < 2; i++ )

                }   // if( (*lpCopyFlags & PRIVCOPY_FILE_METADATA) )
                else {

                    // 
                    // For the public copyfile, we still need to handle encryption.
                    //

                    b = BasepCopyEncryption( hSourceFile,
                                             lpNewFileName,
                                             &DestFile,
                                             &Obja,
                                             DestFileAccess,
                                             DestFileSharing,
                                             CreateDisposition,
                                             CreateOptions,
                                             SourceFileAttributes,
                                             SourceFileAttributesMask,
                                             &DestFileAttributes,
                                             *DestFileFsAttributes,
                                             *lpCopyFlags,
                                             &Context );

                    if( !b ) {
                        // The dest file is already marked for delete and
                        // last error has been set.
                        leave;
                    }
                }   // if( (*lpCopyFlags & PRIVCOPY_FILE_METADATA) ) ... else


                //
                // If copying a directory file, see if any attributes need to be
                // added.  For non-directory files, this is handled in the NtCreateFile since
                // either FILE_CREATE or FILE_OVERWRITE_IF is specified.
                //

                if( SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {

                    //
                    // But before copying attributes, in the supersede case, the target's
                    // named streams should be removed.  We need to do this first,
                    // in case copying the attributes sets the read-only bit.
                    //

                    if( *lpCopyFlags & PRIVCOPY_FILE_SUPERSEDE ) {

                        ULONG StreamInfoSize;
                        PFILE_STREAM_INFORMATION StreamInfo;
                        PFILE_STREAM_INFORMATION StreamInfoBase = NULL;

                        // Get the dest file's streams

                        StreamInfoSize = 4096;
                        do {
                            StreamInfoBase = RtlAllocateHeap( RtlProcessHeap(),
                                                              MAKE_TAG( TMP_TAG ),
                                                              StreamInfoSize );

                            if ( !StreamInfoBase ) {
                                BaseSetLastNTError( STATUS_NO_MEMORY );
                                leave;
                            }

                            Status = NtQueryInformationFile(
                                        DestFile,
                                        &IoStatus,
                                        (PVOID) StreamInfoBase,
                                        StreamInfoSize,
                                        FileStreamInformation
                                        );

                            if ( !NT_SUCCESS(Status) ) {
                                //
                                //  We failed the call.  Free up the previous buffer and set up
                                //  for another pass with a buffer twice as large
                                //

                                RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
                                StreamInfoBase = NULL;
                                StreamInfoSize *= 2;
                            }
                            else if( IoStatus.Information == 0 ) {
                                // There are no streams
                                RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
                                StreamInfoBase = NULL;
                            }

                        } while ( Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL );

                        // If there were any streams, delete them.

                        if( StreamInfoBase != NULL ) {
                            StreamInfo = StreamInfoBase;
                            while (TRUE) {

                                OBJECT_ATTRIBUTES Obja;
                                UNICODE_STRING StreamName;
                                HANDLE DestStream;

                                StreamName.Length = (USHORT) StreamInfo->StreamNameLength;
                                StreamName.MaximumLength = (USHORT) StreamName.Length;
                                StreamName.Buffer = StreamInfo->StreamName;

                                InitializeObjectAttributes(
                                    &Obja,
                                    &StreamName,
                                    OBJ_CASE_INSENSITIVE,
                                    DestFile,
                                    NULL
                                    );

                                // Relative-open the stream to be deleted.

                                Status = NtCreateFile(
                                            &DestStream,
                                            DELETE|SYNCHRONIZE,
                                            &Obja,
                                            &IoStatusBlock,
                                            NULL,
                                            0,
                                            FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                                            FILE_OPEN,
                                            FILE_DELETE_ON_CLOSE | FILE_SYNCHRONOUS_IO_NONALERT,
                                            NULL,
                                            0
                                            );
                                if( !NT_SUCCESS(Status) ) {
                                    RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
                                    BaseMarkFileForDelete( DestFile, 0 );
                                    BaseSetLastNTError( Status );
                                    leave;
                                }

                                // Delete the stream
                                NtClose( DestStream );

                                if (StreamInfo->NextEntryOffset == 0) {
                                    break;
                                }
                                StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo + StreamInfo->NextEntryOffset);
                            }   // while (TRUE)

                            RtlFreeHeap(RtlProcessHeap(), 0, StreamInfoBase);
                        }   // if( StreamInfoBase != NULL )
                    }   // if( *lpCopyFlags & PRIVCOPY_FILE_SUPERSEDE )

                    // Now, if necessary, copy over attributes.

                    if( SourceFileAttributes != DestFileAttributes ) {

                        DestFileAttributes |= SourceFileAttributes;

                        RtlZeroMemory( &DestBasicInformation, sizeof(DestBasicInformation) );
                        DestBasicInformation.FileAttributes = DestFileAttributes;
                        Status = NtSetInformationFile( DestFile,
                                                       &IoStatus,
                                                       &DestBasicInformation,
                                                       sizeof(DestBasicInformation),
                                                       FileBasicInformation );
                        if( !NT_SUCCESS(Status) ) {
                            BaseMarkFileForDelete( DestFile, 0 );
                            BaseSetLastNTError(Status);
                            leave;
                        }

                        DestFileAttributes = 0;
                        Status = NtQueryInformationFile( DestFile,
                                                         &IoStatus,
                                                         &DestBasicInformation,
                                                         sizeof(DestBasicInformation),
                                                         FileBasicInformation );
                        if( NT_SUCCESS(Status) ) {
                            DestFileAttributes = DestBasicInformation.FileAttributes;
                        } else {
                            BaseMarkFileForDelete( DestFile, 0 );
                            BaseSetLastNTError(Status);
                            leave;
                        }
                    }

                }   // if( SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY )


            }   // if( DestFile != INVALID_HANDLE_VALUE )

            //
            // If this is a directory file, there is nothing left to copy
            //

            if( SourceFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
                BOOL Canceled = FALSE;

                if( !BasepCopyFileCallback( TRUE,   // ContinueByDefault
                                            RtlNtStatusToDosError(STATUS_REQUEST_ABORTED),
                                            Context,
                                            NULL,
                                            CALLBACK_STREAM_SWITCH,
                                            hSourceFile,
                                            DestFile,
                                            &Canceled ) ) {
                    ReturnValue = FALSE;
                    if( Canceled ) {
                        BaseMarkFileForDelete(
                            DestFile,
                            0
                            );
                    }
                } else {
                    ReturnValue = TRUE;
                }
                leave;

            }


        } else {    // if ( !ARGUMENT_PRESENT(hTargetFile) )

            // We're copying a named stream.

            OBJECT_ATTRIBUTES ObjectAttributes;
            UNICODE_STRING StreamName;
            IO_STATUS_BLOCK IoStatus;
            ULONG Disposition;

            //
            // Create the output stream relative to the file specified by the
            // hTargetFile file handle.
            //

            RtlInitUnicodeString(&StreamName, lpNewFileName);
            InitializeObjectAttributes(
                &ObjectAttributes,
                &StreamName,
                0,
                hTargetFile,
                (PSECURITY_DESCRIPTOR)NULL
                );

            //
            // Determine the disposition type.
            //

            if ( *lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS ) {
                Disposition = FILE_CREATE;
            } else {
                Disposition = FILE_OVERWRITE_IF;
            }

            if ( Restartable ) {
                if ( lpRestartState->LastKnownGoodOffset.QuadPart ) {
                    Disposition = FILE_OPEN;
                } else {
                    Disposition = FILE_OVERWRITE_IF;
                }
            }

            //
            // Inhibit reparse behavior when appropriate.
            //

            FlagsAndAttributes = FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY;
            DesiredAccess = GENERIC_WRITE | SYNCHRONIZE;
            if ( OpenFileAsReparsePoint ) {
                //
                // The target has to be opened as reparse point. If
                // this fails the source is to be closed and re-opened
                // without inhibiting the reparse point behavior.
                //

                FlagsAndAttributes |= FILE_OPEN_REPARSE_POINT;
                DesiredAccess |= GENERIC_READ;
                if ( !(*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ||
                     !(Restartable && (lpRestartState->LastKnownGoodOffset.QuadPart)) ) {
                    Disposition = FILE_OPEN_IF;
                }
            }

            Status = NtCreateFile(
                        &DestFile,
                        DesiredAccess,
                        &ObjectAttributes,
                        &IoStatus,
                        lpFileSize,
                        0,
                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                        Disposition,
                        FlagsAndAttributes,
                        (PVOID)NULL,
                        0);

            if ( !NT_SUCCESS(Status) ) {

                // If we failed the create with an invalid name error, it might be becuase
                // we tried to copy an NTFS5 property set to pre-NTFS5 (and pre-NTFS4/SP4)
                // To detect this, we first check the error, and that the prefix character
                // of the stream name is a reserved ole character.

                if( Status == STATUS_OBJECT_NAME_INVALID
                    &&
                    StreamName.Buffer[1] <= 0x1f
                    &&
                    StreamName.Buffer[1] >= 1 ) {

                    // Now we check to see if we're copying to pre-NTFS5.
                    // If so, we'll assume that the leading ole character is
                    // the cause of the problem, and will silently fail the
                    // copy of this stream just as NT4 did.

                    NTSTATUS StatusT = STATUS_SUCCESS;
                    IO_STATUS_BLOCK Iosb;
                    FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;

                    StatusT = NtQueryVolumeInformationFile( hTargetFile, &Iosb,
                                                            &FsAttrInfo,
                                                            sizeof(FsAttrInfo),
                                                            FileFsAttributeInformation );


                    // We should always get a buffer-overflow error here, because we don't
                    // provide enough buffer for the file system name, but that's OK because
                    // we don't need it (status_buffer_overflow is just a warning, so the rest
                    // of the data is good).

                    if( !NT_SUCCESS(StatusT) && STATUS_BUFFER_OVERFLOW != StatusT) {
                        Status = StatusT;
                        BaseSetLastNTError(Status);
                        leave;
                    }

                    // If this is pre-NTFS5, then silently ignore the error.
                    if( !(FILE_SUPPORTS_OBJECT_IDS & FsAttrInfo.FileSystemAttributes) ) {

                        Status = STATUS_SUCCESS;
                        ReturnValue = TRUE;
                        leave;
                    }
                }


                if ( Status != STATUS_ACCESS_DENIED ) {
                    BaseSetLastNTError(Status);
                    leave;
                }

                //
                // Determine whether or not this failed because the file
                // is a readonly file.  If so, change it to read/write,
                // re-attempt the open, and set it back to readonly again.
                //

                Status = NtQueryInformationFile(
                            hTargetFile,
                            &IoStatus,
                            (PVOID) &FileBasicInformationData,
                            sizeof(FileBasicInformationData),
                            FileBasicInformation
                            );

                if ( !NT_SUCCESS(Status) ) {
                    leave;
                }

                if ( FileBasicInformationData.FileAttributes & FILE_ATTRIBUTE_READONLY ) {
                    ULONG attributes = FileBasicInformationData.FileAttributes;

                    RtlZeroMemory( &FileBasicInformationData,
                                   sizeof(FileBasicInformationData)
                                );
                    FileBasicInformationData.FileAttributes = FILE_ATTRIBUTE_NORMAL;
                    (VOID) NtSetInformationFile(
                              hTargetFile,
                              &IoStatus,
                              &FileBasicInformationData,
                              sizeof(FileBasicInformationData),
                              FileBasicInformation
                              );
                    Status = NtCreateFile(
                                &DestFile,
                                DesiredAccess,
                                &ObjectAttributes,
                                &IoStatus,
                                lpFileSize,
                                0,
                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                Disposition,
                                FlagsAndAttributes,
                                (PVOID)NULL,
                                0);
                    FileBasicInformationData.FileAttributes = attributes;
                    (VOID) NtSetInformationFile(
                                hTargetFile,
                                &IoStatus,
                                &FileBasicInformationData,
                                sizeof(FileBasicInformationData),
                                FileBasicInformation
                                );
                    if ( !NT_SUCCESS(Status) ) {
                        leave;
                    }
                } else {
                    leave;
                }
            }

            //
            // Adjust the file length in the case of a destination open with the
            // reparse behavior inhibited. This is needed because of the incompatibility
            // between FILE_OPEN_REPARSE_POINT and FILE_OVERWRITE_IF.
            //

            if ( OpenFileAsReparsePoint ) {
               if ( !(*lpCopyFlags & COPY_FILE_FAIL_IF_EXISTS) ||
                    !(Restartable && (lpRestartState->LastKnownGoodOffset.QuadPart)) ) {
                   SetFilePointer(DestFile,0,NULL,FILE_BEGIN);
               }
            }

        }   // if ( !ARGUMENT_PRESENT(hTargetFile) ) ... else

        //
        // Adjust the notion of restartability and chunk size based on whether
        // or not one of the files is remote.
        //

        if ( Restartable || lpFileSize->QuadPart >= BASE_COPY_FILE_CHUNK ) {
            if ( BasepRemoteFile(hSourceFile,DestFile) ) {
                *lpCopySize = BASE_COPY_FILE_CHUNK - 4096;
            } else if ( Restartable ) {
                *lpCopyFlags &= ~COPY_FILE_RESTARTABLE;
                Restartable = FALSE;
            }
        }

        //
        // Preallocate the size of this file/stream so that extends do not
        // occur.
        //

        if ( !(Restartable && lpRestartState->LastKnownGoodOffset.QuadPart) &&
            lpFileSize->QuadPart) {

            EndOfFileInformation.EndOfFile = *lpFileSize;
            Status = NtSetInformationFile(
                        DestFile,
                        &IoStatus,
                        &EndOfFileInformation,
                        sizeof(EndOfFileInformation),
                        FileEndOfFileInformation
                        );
            if ( Status == STATUS_DISK_FULL ) {
                BaseSetLastNTError(Status);
                BaseMarkFileForDelete(
                    DestFile,
                    FileBasicInformationData.FileAttributes
                    );
                CloseHandle(DestFile);
                DestFile = INVALID_HANDLE_VALUE;
                leave;
            }
        }

        //
        // If the caller has a progress routine, invoke it and indicate that the
        // output file or alternate data stream has been created.  Note that a
        // stream number of 1 means that the file itself has been created.
        //

        BytesWritten.QuadPart = 0;
        if ( Context ) {
            if ( Context->lpProgressRoutine ) {
                Context->dwStreamNumber += 1;
                ReturnCode = Context->lpProgressRoutine(
                                Context->TotalFileSize,
                                Context->TotalBytesTransferred,
                                *lpFileSize,
                                BytesWritten,
                                Context->dwStreamNumber,
                                CALLBACK_STREAM_SWITCH,
                                hSourceFile,
                                DestFile,
                                Context->lpData
                                );
            } else {
                ReturnCode = PROGRESS_CONTINUE;
            }

            if ( ReturnCode == PROGRESS_CANCEL ||
                (Context->lpCancel && *Context->lpCancel) ) {
                BaseMarkFileForDelete(
                    hTargetFile ? hTargetFile : DestFile,
                    FileBasicInformationData.FileAttributes
                    );
                BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                leave;
            }

            if ( ReturnCode == PROGRESS_STOP ) {
                BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                leave;
            }

            if ( ReturnCode == PROGRESS_QUIET ) {
                Context = NULL;
                *lpCopyFileContext = NULL;
            }
        }


        if (!Restartable) {

            while (!lpFileSize->HighPart && (lpFileSize->LowPart < TWO56K)) {

                // If there's nothing to copy, then we're done (this happens when
                // copying directory files, as there's no unnamed data stream).

                if( lpFileSize->LowPart == 0 ) {
                    ReturnValue = TRUE;
                    leave;
                }

                //
                // Create a section and map the source file.  If anything fails,
                // then drop into an I/O system copy mode.
                //

                Status = NtCreateSection(
                    &Section,
                        SECTION_ALL_ACCESS,
                        NULL,
                        NULL,
                        PAGE_READONLY,
                        SEC_COMMIT,
                        hSourceFile
                    );
                if ( !NT_SUCCESS(Status) ) {
                    break;
                }

                SectionOffset.LowPart = 0;
                SectionOffset.HighPart = 0;
                ViewSize = 0;
                BigViewSize = 0;

                Status = NtMapViewOfSection(
                    Section,
                    NtCurrentProcess(),
                    &SourceBase,
                    0L,
                    0L,
                    &SectionOffset,
                    &BigViewSize,
                    ViewShare,
                    0L,
                    PAGE_READONLY
                    );
                NtClose(Section);
                Section = NULL;
                if ( !NT_SUCCESS(Status) ) {
                    break;
                }

                //
                // note that this is OK since ViewSize will never be > 256k in this path
                //

                ViewSize = (ULONG)BigViewSize;

                //
                // Everything is mapped, so copy the stream
                //

                SourceBuffer = SourceBase;
                BytesToWrite = lpFileSize->LowPart;

                //
                //  Since we are playing with user memory here, the user
                //  may decommit or unmap it on us.  We wrap the access
                //  in try/except to clean up if anything goes wrong
                //
                //  We set ReturnCode inside the try/except so that we
                //  can detect failure and leave from the enclosing try/finally.
                //

                ReturnCode = TRUE;

                try {

                    while (BytesToWrite) {
                        if (BytesToWrite > *lpCopySize) {
                            ViewSize = *lpCopySize;
                        } else {
                            ViewSize = BytesToWrite;
                        }

                        if ( !WriteFile(DestFile,SourceBuffer,ViewSize, &ViewSize, NULL) ) {
                            if ( !ARGUMENT_PRESENT(hTargetFile) &&
                                GetLastError() != ERROR_NO_MEDIA_IN_DRIVE ) {

                                BaseMarkFileForDelete(
                                    DestFile,
                                    FileBasicInformationData.FileAttributes
                                    );
                            }
                            ReturnCode = PROGRESS_STOP;
                            leave;
                        }

                        BytesToWrite -= ViewSize;
                        SourceBuffer += ViewSize;

                        //
                        // If the caller has a progress routine, invoke it for this
                        // chunk's completion.
                        //

                        if ( Context ) {
                            if ( Context->lpProgressRoutine ) {
                                BytesWritten.QuadPart += ViewSize;
                                Context->TotalBytesTransferred.QuadPart += ViewSize;
                                ReturnCode = Context->lpProgressRoutine(
                                    Context->TotalFileSize,
                                    Context->TotalBytesTransferred,
                                    *lpFileSize,
                                    BytesWritten,
                                    Context->dwStreamNumber,
                                    CALLBACK_CHUNK_FINISHED,
                                    hSourceFile,
                                    DestFile,
                                    Context->lpData
                                    );
                            } else {
                                ReturnCode = PROGRESS_CONTINUE;
                            }

                            if ( ReturnCode == PROGRESS_CANCEL ||
                                 (Context->lpCancel && *Context->lpCancel) ) {
                                if ( !ARGUMENT_PRESENT(hTargetFile) ) {
                                    BaseMarkFileForDelete(
                                        hTargetFile ? hTargetFile : DestFile,
                                        FileBasicInformationData.FileAttributes
                                        );
                                    BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                                }
                                ReturnCode = PROGRESS_STOP;
                                leave;
                            }

                            if ( ReturnCode == PROGRESS_STOP ) {
                                BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                                ReturnCode = PROGRESS_STOP;
                                leave;
                            }

                            if ( ReturnCode == PROGRESS_QUIET ) {
                                Context = NULL;
                                *lpCopyFileContext = NULL;
                            }
                        }
                    }   // while (BytesToWrite)

                } except(EXCEPTION_EXECUTE_HANDLER) {
                    if ( !ARGUMENT_PRESENT(hTargetFile) ) {
                        BaseMarkFileForDelete(
                            DestFile,
                            FileBasicInformationData.FileAttributes
                            );
                    }
                    BaseSetLastNTError(GetExceptionCode());
                    ReturnCode = PROGRESS_STOP;
                }

                if (ReturnCode != PROGRESS_STOP) {
                    ReturnValue = TRUE;
                }

                leave;

            }   // while (!lpFileSize->HighPart && (lpFileSize->LowPart < TWO56K)
        }   // if (!Restartable)

        if ( Restartable ) {

            //
            // A restartable operation is being performed.  Reset the state
            // of the copy to the last known good offset that was written
            // to the output file to continue the operation.
            //

            SetFilePointer(
                hSourceFile,
                lpRestartState->LastKnownGoodOffset.LowPart,
                &lpRestartState->LastKnownGoodOffset.HighPart,
                FILE_BEGIN
                );
            SetFilePointer(
                DestFile,
                lpRestartState->LastKnownGoodOffset.LowPart,
                &lpRestartState->LastKnownGoodOffset.HighPart,
                FILE_BEGIN
                );
            BytesWritten.QuadPart = lpRestartState->LastKnownGoodOffset.QuadPart;
        }

        IoDestBase = RtlAllocateHeap(
                        RtlProcessHeap(),
                        MAKE_TAG( TMP_TAG ),
                        *lpCopySize
                        );
        if ( !IoDestBase ) {
            if ( !ARGUMENT_PRESENT(hTargetFile) && !Restartable ) {
                BaseMarkFileForDelete(
                    DestFile,
                    FileBasicInformationData.FileAttributes
                    );
            }
            BaseSetLastNTError(STATUS_NO_MEMORY);
            leave;
        }



        do {

            BlockSize = *lpCopySize;
            fSkipBlock = FALSE;


            if (!fSkipBlock) {
                b = ReadFile(hSourceFile,IoDestBase,BlockSize, &ViewSize, NULL);
            } else {
                LARGE_INTEGER BytesRead;
                BytesRead = BytesWritten;

                if (BytesRead.QuadPart > lpFileSize->QuadPart) {
                    BlockSize = 0;
                } else if (BytesRead.QuadPart + BlockSize >= lpFileSize->QuadPart) {
                    BlockSize = (ULONG)(lpFileSize->QuadPart - BytesRead.QuadPart);
                }

                BytesRead.QuadPart += BlockSize;
                if ( SetFilePointer(hSourceFile,
                                    BytesRead.LowPart,
                                    &BytesRead.HighPart,
                                    FILE_BEGIN) != 0xffffffff ) {
                } else {
                    if (GetLastError() != NO_ERROR)
                        b = FALSE;
                }
                ViewSize = BlockSize;
            }

            if (!b || !ViewSize)
                break;

            if (!fSkipBlock) {
                if ( !WriteFile(DestFile,IoDestBase,ViewSize, &ViewSize, NULL) ) {
                    if ( !ARGUMENT_PRESENT(hTargetFile) &&
                         GetLastError() != ERROR_NO_MEDIA_IN_DRIVE &&
                         !Restartable ) {

                        BaseMarkFileForDelete(
                                             DestFile,
                                             FileBasicInformationData.FileAttributes
                                             );
                    }

                    leave;
                }
                BytesWritten.QuadPart += ViewSize;
            } else {
                BytesWritten.QuadPart += ViewSize;
                if (( SetFilePointer(DestFile,
                                     BytesWritten.LowPart,
                                     &BytesWritten.HighPart,
                                     FILE_BEGIN) == 0xffffffff ) &&
                    ( GetLastError() != NO_ERROR )) {
                    b = FALSE;
                    break;
                }
            }

            WriteCount++;

            if ( Restartable &&
                 (((WriteCount & 3) == 0 &&
                   BytesWritten.QuadPart ) ||
                  BytesWritten.QuadPart == lpFileSize->QuadPart) ) {

                LARGE_INTEGER SavedOffset;
                DWORD Bytes;
                HANDLE DestinationFile = hTargetFile ? hTargetFile : DestFile;

                //
                // Another 256kb has been written to the target file, or
                // this stream of the file has been completely copied, so
                // update the restart state in the output file accordingly.
                //

                NtFlushBuffersFile(DestinationFile,&IoStatus);
                SavedOffset.QuadPart = BytesWritten.QuadPart;
                SetFilePointer(DestinationFile,0,NULL,FILE_BEGIN);
                lpRestartState->LastKnownGoodOffset.QuadPart = BytesWritten.QuadPart;
                lpRestartState->Checksum = BasepChecksum((PUSHORT)lpRestartState,FIELD_OFFSET(RESTART_STATE,Checksum) >> 1);
                b = WriteFile(
                             DestinationFile,
                             lpRestartState,
                             sizeof(RESTART_STATE),
                             &Bytes,
                             NULL
                             );
                if ( !b || Bytes != sizeof(RESTART_STATE) ) {
                    leave;
                }
                NtFlushBuffersFile(DestinationFile,&IoStatus);
                SetFilePointer(
                              DestinationFile,
                              SavedOffset.LowPart,
                              &SavedOffset.HighPart,
                              FILE_BEGIN
                              );
            }

            //
            // If the caller has a progress routine, invoke it for this
            // chunk's completion.
            //

            if ( Context ) {
                if ( Context->lpProgressRoutine ) {
                    Context->TotalBytesTransferred.QuadPart += ViewSize;
                    ReturnCode = Context->lpProgressRoutine(
                                                           Context->TotalFileSize,
                                                           Context->TotalBytesTransferred,
                                                           *lpFileSize,
                                                           BytesWritten,
                                                           Context->dwStreamNumber,
                                                           CALLBACK_CHUNK_FINISHED,
                                                           hSourceFile,
                                                           DestFile,
                                                           Context->lpData
                                                           );
                } else {
                    ReturnCode = PROGRESS_CONTINUE;
                }
                if ( ReturnCode == PROGRESS_CANCEL ||
                     (Context->lpCancel && *Context->lpCancel) ) {
                    if ( !ARGUMENT_PRESENT(hTargetFile) ) {
                        BaseMarkFileForDelete(
                                             hTargetFile ? hTargetFile : DestFile,
                                             FileBasicInformationData.FileAttributes
                                             );
                        BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                        leave;
                    }
                }

                if ( ReturnCode == PROGRESS_STOP ) {
                    BaseSetLastNTError(STATUS_REQUEST_ABORTED);
                    leave;
                }

                if ( ReturnCode == PROGRESS_QUIET ) {
                    Context = NULL;
                    *lpCopyFileContext = NULL;
                }
            }
        } while (TRUE);

        if ( !b && !ARGUMENT_PRESENT(hTargetFile) ) {
            if ( !Restartable ) {
                BaseMarkFileForDelete(
                    DestFile,
                    FileBasicInformationData.FileAttributes
                    );
            }
            leave;
        }

        ReturnValue = TRUE;
    } finally {
        if ( DestFile != INVALID_HANDLE_VALUE ) {
            *lpDestFile = DestFile;
        }
        if ( Section ) {
            NtClose(Section);
        }
        if ( SourceBase ) {
            NtUnmapViewOfSection(NtCurrentProcess(),SourceBase);
        }
        RtlFreeHeap(RtlProcessHeap(), 0,IoDestBase);
        RtlFreeHeap(RtlProcessHeap(), 0, DestFileNameBuffer );
        RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer );

        // If the TEB buffer was saved, restore it now.
        if( lpExistingFileName == SaveStaticUnicodeBuffer ) {

            memcpy( NtCurrentTeb()->StaticUnicodeBuffer,
                    SaveStaticUnicodeBuffer,
                    STATIC_UNICODE_BUFFER_LENGTH );
        }

    }

    return ReturnValue;
}

HANDLE
WINAPI
CreateFileA(
    LPCSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile
    )

/*++

Routine Description:

    ANSI thunk to CreateFileW

--*/

{

    PUNICODE_STRING Unicode;

    Unicode = Basep8BitStringToStaticUnicodeString( lpFileName );
    if (Unicode == NULL) {
        return INVALID_HANDLE_VALUE;
    }

    return ( CreateFileW( Unicode->Buffer,
                          dwDesiredAccess,
                          dwShareMode,
                          lpSecurityAttributes,
                          dwCreationDisposition,
                          dwFlagsAndAttributes,
                          hTemplateFile
                        )
           );
}

HANDLE
WINAPI
CreateFileW(
    LPCWSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile
    )

/*++

Routine Description:

    A file can be created, opened, or truncated, and a handle opened to
    access the new file using CreateFile.

    This API is used to create or open a file and obtain a handle to it
    that allows reading data, writing data, and moving the file pointer.

    This API allows the caller to specify the following creation
    dispositions:

      - Create a new file and fail if the file exists ( CREATE_NEW )

      - Create a new file and succeed if it exists ( CREATE_ALWAYS )

      - Open an existing file ( OPEN_EXISTING )

      - Open and existing file or create it if it does not exist (
        OPEN_ALWAYS )

      - Truncate and existing file ( TRUNCATE_EXISTING )

    If this call is successful, a handle is returned that has
    appropriate access to the specified file.

    If as a result of this call, a file is created,

      - The attributes of the file are determined by the value of the
        FileAttributes parameter or'd with the FILE_ATTRIBUTE_ARCHIVE bit.

      - The length of the file will be set to zero.

      - If the hTemplateFile parameter is specified, any extended
        attributes associated with the file are assigned to the new file.

    If a new file is not created, then the hTemplateFile is ignored as
    are any extended attributes.

    For DOS based systems running share.exe the file sharing semantics
    work as described above.  Without share.exe no share level
    protection exists.

    This call is logically equivalent to DOS (int 21h, function 5Bh), or
    DOS (int 21h, function 3Ch) depending on the value of the
    FailIfExists parameter.

Arguments:

    lpFileName - Supplies the file name of the file to open.  Depending on
        the value of the FailIfExists parameter, this name may or may
        not already exist.

    dwDesiredAccess - Supplies the caller's desired access to the file.

        DesiredAccess Flags:

        GENERIC_READ - Read access to the file is requested.  This
            allows data to be read from the file and the file pointer to
            be modified.

        GENERIC_WRITE - Write access to the file is requested.  This
            allows data to be written to the file and the file pointer to
            be modified.

    dwShareMode - Supplies a set of flags that indicates how this file is
        to be shared with other openers of the file.  A value of zero
        for this parameter indicates no sharing of the file, or
        exclusive access to the file is to occur.

        ShareMode Flags:

        FILE_SHARE_READ - Other open operations may be performed on the
            file for read access.

        FILE_SHARE_WRITE - Other open operations may be performed on the
            file for write access.

    lpSecurityAttributes - An optional parameter that, if present, and
        supported on the target file system supplies a security
        descriptor for the new file.

    dwCreationDisposition - Supplies a creation disposition that
        specifies how this call is to operate.  This parameter must be
        one of the following values.

        dwCreationDisposition Value:

        CREATE_NEW - Create a new file.  If the specified file already
            exists, then fail.  The attributes for the new file are what
            is specified in the dwFlagsAndAttributes parameter or'd with
            FILE_ATTRIBUTE_ARCHIVE.  If the hTemplateFile is specified,
            then any extended attributes associated with that file are
            propogated to the new file.

        CREATE_ALWAYS - Always create the file.  If the file already
            exists, then it is overwritten.  The attributes for the new
            file are what is specified in the dwFlagsAndAttributes
            parameter or'd with FILE_ATTRIBUTE_ARCHIVE.  If the
            hTemplateFile is specified, then any extended attributes
            associated with that file are propogated to the new file.

        OPEN_EXISTING - Open the file, but if it does not exist, then
            fail the call.

        OPEN_ALWAYS - Open the file if it exists.  If it does not exist,
            then create the file using the same rules as if the
            disposition were CREATE_NEW.

        TRUNCATE_EXISTING - Open the file, but if it does not exist,
            then fail the call.  Once opened, the file is truncated such
            that its size is zero bytes.  This disposition requires that
            the caller open the file with at least GENERIC_WRITE access.

    dwFlagsAndAttributes - Specifies flags and attributes for the file.
        The attributes are only used when the file is created (as
        opposed to opened or truncated).  Any combination of attribute
        flags is acceptable except that all other attribute flags
        override the normal file attribute, FILE_ATTRIBUTE_NORMAL.  The
        FILE_ATTRIBUTE_ARCHIVE flag is always implied.

        dwFlagsAndAttributes Flags:

        FILE_ATTRIBUTE_NORMAL - A normal file should be created.

        FILE_ATTRIBUTE_READONLY - A read-only file should be created.

        FILE_ATTRIBUTE_HIDDEN - A hidden file should be created.

        FILE_ATTRIBUTE_SYSTEM - A system file should be created.

        FILE_FLAG_WRITE_THROUGH - Indicates that the system should
            always write through any intermediate cache and go directly
            to the file.  The system may still cache writes, but may not
            lazily flush the writes.

        FILE_FLAG_OVERLAPPED - Indicates that the system should initialize
            the file so that ReadFile and WriteFile operations that may
            take a significant time to complete will return ERROR_IO_PENDING.
            An event will be set to the signalled state when the operation
            completes. When FILE_FLAG_OVERLAPPED is specified the system will
            not maintain the file pointer. The position to read/write from
            is passed to the system as part of the OVERLAPPED structure
            which is an optional parameter to ReadFile and WriteFile.

        FILE_FLAG_NO_BUFFERING - Indicates that the file is to be opened
            with no intermediate buffering or caching done by the
            system.  Reads and writes to the file must be done on sector
            boundries.  Buffer addresses for reads and writes must be
            aligned on at least disk sector boundries in memory.

        FILE_FLAG_RANDOM_ACCESS - Indicates that access to the file may
            be random. The system cache manager may use this to influence
            its caching strategy for this file.

        FILE_FLAG_SEQUENTIAL_SCAN - Indicates that access to the file
            may be sequential.  The system cache manager may use this to
            influence its caching strategy for this file.  The file may
            in fact be accessed randomly, but the cache manager may
            optimize its cacheing policy for sequential access.

        FILE_FLAG_DELETE_ON_CLOSE - Indicates that the file is to be
            automatically deleted when the last handle to it is closed.

        FILE_FLAG_BACKUP_SEMANTICS - Indicates that the file is being opened
            or created for the purposes of either a backup or a restore
            operation.  Thus, the system should make whatever checks are
            appropriate to ensure that the caller is able to override
            whatever security checks have been placed on the file to allow
            this to happen.

        FILE_FLAG_POSIX_SEMANTICS - Indicates that the file being opened
            should be accessed in a manner compatible with the rules used
            by POSIX.  This includes allowing multiple files with the same
            name, differing only in case.  WARNING:  Use of this flag may
            render it impossible for a DOS, WIN-16, or WIN-32 application
            to access the file.

        FILE_FLAG_OPEN_REPARSE_POINT - Indicates that the file being opened
            should be accessed as if it were a reparse point.  WARNING:  Use
            of this flag may inhibit the operation of file system filter drivers
            present in the I/O subsystem.

        FILE_FLAG_OPEN_NO_RECALL - Indicates that all the state of the file
            should be acessed without changing its storage location.  Thus,
            in the case of files that have parts of its state stored at a
            remote servicer, no permanent recall of data is to happen.

    Security Quality of Service information may also be specified in
        the dwFlagsAndAttributes parameter.  These bits are meaningful
        only if the file being opened is the client side of a Named
        Pipe.  Otherwise they are ignored.

        SECURITY_SQOS_PRESENT - Indicates that the Security Quality of
            Service bits contain valid values.

    Impersonation Levels:

        SECURITY_ANONYMOUS - Specifies that the client should be impersonated
            at Anonymous impersonation level.

        SECURITY_IDENTIFICAION - Specifies that the client should be impersonated
            at Identification impersonation level.

        SECURITY_IMPERSONATION - Specifies that the client should be impersonated
            at Impersonation impersonation level.

        SECURITY_DELEGATION - Specifies that the client should be impersonated
            at Delegation impersonation level.

    Context Tracking:

        SECURITY_CONTEXT_TRACKING - A boolean flag that when set,
            specifies that the Security Tracking Mode should be
            Dynamic, otherwise Static.

        SECURITY_EFFECTIVE_ONLY - A boolean flag indicating whether
            the entire security context of the client is to be made
            available to the server or only the effective aspects of
            the context.

    hTemplateFile - An optional parameter, then if specified, supplies a
        handle with GENERIC_READ access to a template file.  The
        template file is used to supply extended attributes for the file
        being created.  When the new file is created, the relevant attributes
        from the template file are used in creating the new file.

Return Value:

    Not -1 - Returns an open handle to the specified file.  Subsequent
        access to the file is controlled by the DesiredAccess parameter.

    0xffffffff - The operation failed. Extended error status is available
        using GetLastError.

--*/
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    HANDLE Handle;
    UNICODE_STRING FileName;
    IO_STATUS_BLOCK IoStatusBlock;
    BOOLEAN TranslationStatus;
    RTL_RELATIVE_NAME RelativeName;
    PVOID FreeBuffer;
    ULONG CreateDisposition;
    ULONG CreateFlags;
    FILE_ALLOCATION_INFORMATION AllocationInfo;
    FILE_EA_INFORMATION EaInfo;
    PFILE_FULL_EA_INFORMATION EaBuffer;
    ULONG EaSize;
    PUNICODE_STRING lpConsoleName;
    BOOL bInheritHandle;
    BOOL EndsInSlash;
    DWORD SQOSFlags;
    BOOLEAN ContextTrackingMode = FALSE;
    BOOLEAN EffectiveOnly = FALSE;
    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel = 0;
    SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;

    switch ( dwCreationDisposition ) {
        case CREATE_NEW        :
            CreateDisposition = FILE_CREATE;
            break;
        case CREATE_ALWAYS     :
            CreateDisposition = FILE_OVERWRITE_IF;
            break;
        case OPEN_EXISTING     :
            CreateDisposition = FILE_OPEN;
            break;
        case OPEN_ALWAYS       :
            CreateDisposition = FILE_OPEN_IF;
            break;
        case TRUNCATE_EXISTING :
            CreateDisposition = FILE_OPEN;
            if ( !(dwDesiredAccess & GENERIC_WRITE) ) {
                BaseSetLastNTError(STATUS_INVALID_PARAMETER);
                return INVALID_HANDLE_VALUE;
                }
            break;
        default :
            BaseSetLastNTError(STATUS_INVALID_PARAMETER);
            return INVALID_HANDLE_VALUE;
        }

    // temporary routing code

    RtlInitUnicodeString(&FileName,lpFileName);

    if ( FileName.Length > 1 && lpFileName[(FileName.Length >> 1)-1] == (WCHAR)'\\' ) {
        EndsInSlash = TRUE;
        }
    else {
        EndsInSlash = FALSE;
        }

    if ((lpConsoleName = BaseIsThisAConsoleName(&FileName,dwDesiredAccess)) ) {

        Handle = INVALID_HANDLE_VALUE;

        bInheritHandle = FALSE;
        if ( ARGUMENT_PRESENT(lpSecurityAttributes) ) {
                bInheritHandle = lpSecurityAttributes->bInheritHandle;
            }

        Handle = OpenConsoleW(lpConsoleName->Buffer,
                           dwDesiredAccess,
                           bInheritHandle,
                           FILE_SHARE_READ | FILE_SHARE_WRITE //dwShareMode
                          );

        if ( Handle == INVALID_HANDLE_VALUE ) {
            BaseSetLastNTError(STATUS_ACCESS_DENIED);
            return INVALID_HANDLE_VALUE;
            }
        else {
            SetLastError(0);
             return Handle;
            }
        }
    // end temporary code

    CreateFlags = 0;


    TranslationStatus = RtlDosPathNameToNtPathName_U(
                            lpFileName,
                            &FileName,
                            NULL,
                            &RelativeName
                            );

    if ( !TranslationStatus ) {
        SetLastError(ERROR_PATH_NOT_FOUND);
        return INVALID_HANDLE_VALUE;
        }

    FreeBuffer = FileName.Buffer;

    if ( RelativeName.RelativeName.Length ) {
        FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
        }
    else {
        RelativeName.ContainingDirectory = NULL;
        }

    InitializeObjectAttributes(
        &Obja,
        &FileName,
        dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS ? 0 : OBJ_CASE_INSENSITIVE,
        RelativeName.ContainingDirectory,
        NULL
        );

    SQOSFlags = dwFlagsAndAttributes & SECURITY_VALID_SQOS_FLAGS;

    if ( SQOSFlags & SECURITY_SQOS_PRESENT ) {

        SQOSFlags &= ~SECURITY_SQOS_PRESENT;

        if (SQOSFlags & SECURITY_CONTEXT_TRACKING) {

            SecurityQualityOfService.ContextTrackingMode = (SECURITY_CONTEXT_TRACKING_MODE) TRUE;
            SQOSFlags &= ~SECURITY_CONTEXT_TRACKING;

        } else {

            SecurityQualityOfService.ContextTrackingMode = (SECURITY_CONTEXT_TRACKING_MODE) FALSE;
        }

        if (SQOSFlags & SECURITY_EFFECTIVE_ONLY) {

            SecurityQualityOfService.EffectiveOnly = TRUE;
            SQOSFlags &= ~SECURITY_EFFECTIVE_ONLY;

        } else {

            SecurityQualityOfService.EffectiveOnly = FALSE;
        }

        SecurityQualityOfService.ImpersonationLevel = SQOSFlags >> 16;


    } else {

        SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
        SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
        SecurityQualityOfService.EffectiveOnly = TRUE;
    }

    SecurityQualityOfService.Length = sizeof( SECURITY_QUALITY_OF_SERVICE );
    Obja.SecurityQualityOfService = &SecurityQualityOfService;

    if ( ARGUMENT_PRESENT(lpSecurityAttributes) ) {
        Obja.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor;
        if ( lpSecurityAttributes->bInheritHandle ) {
            Obja.Attributes |= OBJ_INHERIT;
            }
        }

    EaBuffer = NULL;
    EaSize = 0;

    if ( ARGUMENT_PRESENT(hTemplateFile) ) {
        Status = NtQueryInformationFile(
                    hTemplateFile,
                    &IoStatusBlock,
                    &EaInfo,
                    sizeof(EaInfo),
                    FileEaInformation
                    );
        if ( NT_SUCCESS(Status) && EaInfo.EaSize ) {
            EaSize = EaInfo.EaSize;
            do {
                EaSize *= 2;
                EaBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), EaSize);
                if ( !EaBuffer ) {
                    RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
                    BaseSetLastNTError(STATUS_NO_MEMORY);
                    return INVALID_HANDLE_VALUE;
                    }
                Status = NtQueryEaFile(
                            hTemplateFile,
                            &IoStatusBlock,
                            EaBuffer,
                            EaSize,
                            FALSE,
                            (PVOID)NULL,
                            0,
                            (PULONG)NULL,
                            TRUE
                            );
                if ( !NT_SUCCESS(Status) ) {
                    RtlFreeHeap(RtlProcessHeap(), 0,EaBuffer);
                    EaBuffer = NULL;
                    IoStatusBlock.Information = 0;
                    }
                } while ( Status == STATUS_BUFFER_OVERFLOW ||
                          Status == STATUS_BUFFER_TOO_SMALL );
            EaSize = (ULONG)IoStatusBlock.Information;
            }
        }

    CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING ? FILE_NO_INTERMEDIATE_BUFFERING : 0 );
    CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH ? FILE_WRITE_THROUGH : 0 );
    CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED ? 0 : FILE_SYNCHRONOUS_IO_NONALERT );
    CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN ? FILE_SEQUENTIAL_ONLY : 0 );
    CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS ? FILE_RANDOM_ACCESS : 0 );
    CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS ? FILE_OPEN_FOR_BACKUP_INTENT : 0 );

    if ( dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE ) {
        CreateFlags |= FILE_DELETE_ON_CLOSE;
        dwDesiredAccess |= DELETE;
        }

    if ( dwFlagsAndAttributes & FILE_FLAG_OPEN_REPARSE_POINT ) {
        CreateFlags |= FILE_OPEN_REPARSE_POINT;
        }

    if ( dwFlagsAndAttributes & FILE_FLAG_OPEN_NO_RECALL ) {
        CreateFlags |= FILE_OPEN_NO_RECALL;
        }

    //
    // Backup semantics allow directories to be opened
    //

    if ( !(dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS) ) {
        CreateFlags |= FILE_NON_DIRECTORY_FILE;
        }
    else {

        //
        // Backup intent was specified... Now look to see if we are to allow
        // directory creation
        //

        if ( (dwFlagsAndAttributes & FILE_ATTRIBUTE_DIRECTORY  ) &&
             (dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS ) &&
             (CreateDisposition == FILE_CREATE) ) {
             CreateFlags |= FILE_DIRECTORY_FILE;
             }
        }

    Status = NtCreateFile(
                &Handle,
                (ACCESS_MASK)dwDesiredAccess | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
                &Obja,
                &IoStatusBlock,
                NULL,
                dwFlagsAndAttributes & (FILE_ATTRIBUTE_VALID_FLAGS & ~FILE_ATTRIBUTE_DIRECTORY),
                dwShareMode,
                CreateDisposition,
                CreateFlags,
                EaBuffer,
                EaSize
                );

    RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);

    RtlFreeHeap(RtlProcessHeap(), 0, EaBuffer);

    if ( !NT_SUCCESS(Status) ) {
        BaseSetLastNTError(Status);
        if ( Status == STATUS_OBJECT_NAME_COLLISION ) {
            SetLastError(ERROR_FILE_EXISTS);
            }
        else if ( Status == STATUS_FILE_IS_A_DIRECTORY ) {
            if ( EndsInSlash ) {
                SetLastError(ERROR_PATH_NOT_FOUND);
                }
            else {
                SetLastError(ERROR_ACCESS_DENIED);
                }
            }
        return INVALID_HANDLE_VALUE;
        }

    //
    // if NT returns supersede/overwritten, it means that a create_always, openalways
    // found an existing copy of the file. In this case ERROR_ALREADY_EXISTS is returned
    //

    if ( (dwCreationDisposition == CREATE_ALWAYS && IoStatusBlock.Information == FILE_OVERWRITTEN) ||
         (dwCreationDisposition == OPEN_ALWAYS && IoStatusBlock.Information == FILE_OPENED) ){
        SetLastError(ERROR_ALREADY_EXISTS);
        }
    else {
        SetLastError(0);
        }

    //
    // Truncate the file if required
    //

    if ( dwCreationDisposition == TRUNCATE_EXISTING) {

        AllocationInfo.AllocationSize.QuadPart = 0;
        Status = NtSetInformationFile(
                    Handle,
                    &IoStatusBlock,
                    &AllocationInfo,
                    sizeof(AllocationInfo),
                    FileAllocationInformation
                    );
        if ( !NT_SUCCESS(Status) ) {
            BaseSetLastNTError(Status);
            NtClose(Handle);
            Handle = INVALID_HANDLE_VALUE;
            }
        }

    //
    // Deal with hTemplateFile
    //

    return Handle;
}

UINT
GetErrorMode();

HFILE
WINAPI
OpenFile(
    LPCSTR lpFileName,
    LPOFSTRUCT lpReOpenBuff,
    UINT uStyle
    )
{

    BOOL b;
    FILETIME LastWriteTime;
    HANDLE hFile;
    DWORD DesiredAccess;
    DWORD ShareMode;
    DWORD CreateDisposition;
    DWORD PathLength;
    LPSTR FilePart;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_FS_DEVICE_INFORMATION DeviceInfo;
    NTSTATUS Status;
    OFSTRUCT OriginalReOpenBuff;
    BOOL SearchFailed;

    SearchFailed = FALSE;
    OriginalReOpenBuff = *lpReOpenBuff;
    hFile = (HANDLE)-1;
    try {
        SetLastError(0);

        if ( uStyle & OF_PARSE ) {
            PathLength = GetFullPathName(lpFileName,(OFS_MAXPATHNAME - 1),lpReOpenBuff->szPathName,&FilePart);
            if ( PathLength > (OFS_MAXPATHNAME - 1) ) {
                SetLastError(ERROR_INVALID_DATA);
                hFile = (HANDLE)-1;
                goto finally_exit;
                }
            lpReOpenBuff->cBytes = sizeof(*lpReOpenBuff);
            lpReOpenBuff->fFixedDisk = 1;
            lpReOpenBuff->nErrCode = 0;
            lpReOpenBuff->Reserved1 = 0;
            lpReOpenBuff->Reserved2 = 0;
            hFile = (HANDLE)0;
            goto finally_exit;
            }
        //
        // Compute Desired Access
        //

        if ( uStyle & OF_WRITE ) {
            DesiredAccess = GENERIC_WRITE;
            }
        else {
            DesiredAccess = GENERIC_READ;
            }
        if ( uStyle & OF_READWRITE ) {
            DesiredAccess |= (GENERIC_READ | GENERIC_WRITE);
            }

        //
        // Compute ShareMode
        //

        ShareMode = BasepOfShareToWin32Share(uStyle);

        //
        // Compute Create Disposition
        //

        CreateDisposition = OPEN_EXISTING;
        if ( uStyle & OF_CREATE ) {
            CreateDisposition = CREATE_ALWAYS;
            DesiredAccess = (GENERIC_READ | GENERIC_WRITE);
            }

        //
        // if this is anything other than a re-open, fill the re-open buffer
        // with the full pathname for the file
        //

        if ( !(uStyle & OF_REOPEN) ) {
            PathLength = SearchPath(NULL,lpFileName,NULL,OFS_MAXPATHNAME-1,lpReOpenBuff->szPathName,&FilePart);
            if ( PathLength > (OFS_MAXPATHNAME - 1) ) {
                SetLastError(ERROR_INVALID_DATA);
                hFile = (HANDLE)-1;
                goto finally_exit;
                }
            if ( PathLength == 0 ) {
                SearchFailed = TRUE;
                PathLength = GetFullPathName(lpFileName,(OFS_MAXPATHNAME - 1),lpReOpenBuff->szPathName,&FilePart);
                if ( !PathLength || PathLength > (OFS_MAXPATHNAME - 1) ) {
                    SetLastError(ERROR_INVALID_DATA);
                    hFile = (HANDLE)-1;
                    goto finally_exit;
                    }
                }
            }

        //
        // Special case, Delete, Exist, and Parse
        //

        if ( uStyle & OF_EXIST ) {
            if ( !(uStyle & OF_CREATE) ) {
                DWORD FileAttributes;

                if (SearchFailed) {
                    SetLastError(ERROR_FILE_NOT_FOUND);
                    hFile = (HANDLE)-1;
                    goto finally_exit;
                    }

                FileAttributes = GetFileAttributesA(lpReOpenBuff->szPathName);
                if ( FileAttributes == 0xffffffff ) {
                    SetLastError(ERROR_FILE_NOT_FOUND);
                    hFile = (HANDLE)-1;
                    goto finally_exit;
                    }
                if ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
                    SetLastError(ERROR_ACCESS_DENIED);
                    hFile = (HANDLE)-1;
                    goto finally_exit;
                    }
                else {
                    hFile = (HANDLE)1;
                    lpReOpenBuff->cBytes = sizeof(*lpReOpenBuff);
                    goto finally_exit;
                    }
                }
            }

        if ( uStyle & OF_DELETE ) {
            if ( DeleteFile(lpReOpenBuff->szPathName) ) {
                lpReOpenBuff->nErrCode = 0;
                lpReOpenBuff->cBytes = sizeof(*lpReOpenBuff);
                hFile = (HANDLE)1;
                goto finally_exit;
                }
            else {
                lpReOpenBuff->nErrCode = ERROR_FILE_NOT_FOUND;
                hFile = (HANDLE)-1;
                goto finally_exit;
                }
            }


        //
        // Open the file
        //

retry_open:
        hFile = CreateFile(
                    lpReOpenBuff->szPathName,
                    DesiredAccess,
                    ShareMode,
                    NULL,
                    CreateDisposition,
                    0,
                    NULL
                    );

        if ( hFile == INVALID_HANDLE_VALUE ) {

            if ( uStyle & OF_PROMPT && !(GetErrorMode() & SEM_NOOPENFILEERRORBOX) ) {
                {
                    DWORD WinErrorStatus;
                    NTSTATUS st,HardErrorStatus;
                    ULONG_PTR ErrorParameter;
                    ULONG ErrorResponse;
                    ANSI_STRING AnsiString;
                    UNICODE_STRING UnicodeString;

                    WinErrorStatus = GetLastError();
                    if ( WinErrorStatus == ERROR_FILE_NOT_FOUND ) {
                        HardErrorStatus = STATUS_NO_SUCH_FILE;
                        }
                    else if ( WinErrorStatus == ERROR_PATH_NOT_FOUND ) {
                        HardErrorStatus = STATUS_OBJECT_PATH_NOT_FOUND;
                        }
                    else {
                        goto finally_exit;
                        }

                    //
                    // Hard error time
                    //

                    RtlInitAnsiString(&AnsiString,lpReOpenBuff->szPathName);
                    st = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
                    if ( !NT_SUCCESS(st) ) {
                        goto finally_exit;
                        }
                    ErrorParameter = (ULONG_PTR)&UnicodeString;

                    HardErrorStatus = NtRaiseHardError(
                                        HardErrorStatus | HARDERROR_OVERRIDE_ERRORMODE,
                                        1,
                                        1,
                                        &ErrorParameter,
                                        OptionRetryCancel,
                                        &ErrorResponse
                                        );
                    RtlFreeUnicodeString(&UnicodeString);
                    if ( NT_SUCCESS(HardErrorStatus) && ErrorResponse == ResponseRetry ) {
                        goto retry_open;
                        }
                    }
                }
            goto finally_exit;
            }

        if ( uStyle & OF_EXIST ) {
            CloseHandle(hFile);
            hFile = (HANDLE)1;
            lpReOpenBuff->cBytes = sizeof(*lpReOpenBuff);
            goto finally_exit;
            }

        //
        // Determine if this is a hard disk.
        //

        Status = NtQueryVolumeInformationFile(
                    hFile,
                    &IoStatusBlock,
                    &DeviceInfo,
                    sizeof(DeviceInfo),
                    FileFsDeviceInformation
                    );
        if ( !NT_SUCCESS(Status) ) {
            CloseHandle(hFile);
            BaseSetLastNTError(Status);
            hFile = (HANDLE)-1;
            goto finally_exit;
            }
        switch ( DeviceInfo.DeviceType ) {

            case FILE_DEVICE_DISK:
            case FILE_DEVICE_DISK_FILE_SYSTEM:
                if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) {
                    lpReOpenBuff->fFixedDisk = 0;
                    }
                else {
                    lpReOpenBuff->fFixedDisk = 1;
                    }
                break;

            default:
                lpReOpenBuff->fFixedDisk = 0;
                break;
            }

        //
        // Capture the last write time and save in the open struct.
        //

        b = GetFileTime(hFile,NULL,NULL,&LastWriteTime);

        if ( !b ) {
            lpReOpenBuff->Reserved1 = 0;
            lpReOpenBuff->Reserved2 = 0;
            }
        else {
            b = FileTimeToDosDateTime(
                    &LastWriteTime,
                    &lpReOpenBuff->Reserved1,
                    &lpReOpenBuff->Reserved2
                    );
            if ( !b ) {
                lpReOpenBuff->Reserved1 = 0;
                lpReOpenBuff->Reserved2 = 0;
                }
            }

        lpReOpenBuff->cBytes = sizeof(*lpReOpenBuff);

        //
        // The re-open buffer is completely filled in. Now
        // see if we are quitting (parsing), verifying, or
        // just returning with the file opened.
        //

        if ( uStyle & OF_VERIFY ) {
            if ( OriginalReOpenBuff.Reserved1 == lpReOpenBuff->Reserved1 &&
                 OriginalReOpenBuff.Reserved2 == lpReOpenBuff->Reserved2 &&
                 !strcmp(OriginalReOpenBuff.szPathName,lpReOpenBuff->szPathName) ) {
                goto finally_exit;
                }
            else {
                *lpReOpenBuff = OriginalReOpenBuff;
                CloseHandle(hFile);
                hFile = (HANDLE)-1;
                goto finally_exit;
                }
            }
finally_exit:;
        }
    finally {
        lpReOpenBuff->nErrCode = (WORD)GetLastError();
        }
    return (HFILE)HandleToUlong(hFile);
}




typedef DWORD (WINAPI *GETNAMEDSECURITYINFOWPTR)(
    IN  LPCWSTR                pObjectName,
    IN  SE_OBJECT_TYPE         ObjectType,
    IN  SECURITY_INFORMATION   SecurityInfo,
    OUT PSID                 * ppsidOwner,
    OUT PSID                 * ppsidGroup,
    OUT PACL                 * ppDacl,
    OUT PACL                 * ppSacl,
    OUT PSECURITY_DESCRIPTOR * ppSecurityDescriptor
    );

typedef DWORD (WINAPI *SETNAMEDSECURITYINFOWPTR)(
    IN LPCWSTR               pObjectName,
    IN SE_OBJECT_TYPE        ObjectType,
    IN SECURITY_INFORMATION  SecurityInfo,
    IN PSID                  psidOwner,
    IN PSID                  psidGroup,
    IN PACL                  pDacl,
    IN PACL                  pSacl
    );

typedef BOOL (WINAPI *GETSECURITYDESCRIPTORCONTROLPTR)(
    IN  PSECURITY_DESCRIPTOR           pSecurityDescriptor,
    OUT PSECURITY_DESCRIPTOR_CONTROL   pControl,
    OUT LPDWORD                        lpdwRevision
    );

BOOL
BasepCopySecurityInformation( LPCWSTR lpExistingFileName,
                              HANDLE SourceFile,
                              ACCESS_MASK SourceFileAccess,
                              LPCWSTR lpNewFileName,
                              HANDLE DestFile,
                              ACCESS_MASK DestFileAccess,
                              SECURITY_INFORMATION SecurityInformation,
                              LPCOPYFILE_CONTEXT Context,
                              DWORD DestFileFsAttributes,
                              PBOOL DeleteDest )

/*++

Routine Description:

    This is an internal routine that copies one or more of the DACL,
    SACL, owner, and group from the source to the dest file.

Arguments:

    lpExistingFileName - Provides the name of the source file.

    SourceFile - Provides a handle to that source file.

    SourceFileAccess - The access flags that were used to open SourceFile.

    lpNewFileName - Provides the name of the destination file.

    DestFile - Provides a handle to that destination file.

    DestFileAccess - The access flags that were used to open DestFile.

    SecurityInformation - Specifies what security should be copied (bit
        flag of the *_SECURITY_INFORMATION defines).

    Context - All the information necessary to call the CopyFile callback routine.

    DestFileFsAttributes - Provides the FILE_FS_ATTRIBUTE_INFORMATION.FileSystemAttributes
        for the dest file's volume.

    DeleteDest - Contains a pointer to a value that will be set to TRUE if this the dest
        file should be deleted.  This is the case if there is an error or the user
        cancels the operation.  If the user stops the operation, this routine still
        returns an error, but the dest file is not deleted.


Return Value:

    TRUE - The operation was successful.

    FALSE- The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    BOOLEAN Succeeded = FALSE;

    PACL Dacl = NULL;
    PACL Sacl = NULL;
    PSID Owner = NULL;
    PSID Group = NULL;
    PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
    DWORD dwError = 0;

    HANDLE Advapi32 = NULL;
    GETNAMEDSECURITYINFOWPTR GetNamedSecurityInfoWPtr = NULL;
    SETNAMEDSECURITYINFOWPTR SetNamedSecurityInfoWPtr = NULL;
    GETSECURITYDESCRIPTORCONTROLPTR GetSecurityDescriptorControlPtr = NULL;

    SECURITY_DESCRIPTOR_CONTROL Control = 0;
    DWORD dwRevision = 0;

    // If the source file isn't identified, there's nothing we can do.

    if( lpExistingFileName == NULL || lpNewFileName == NULL ) {
        Succeeded = TRUE;
        goto Exit;
    }

    // If the destination doesn't support ACLs, assume it doesn't
    // support any such security information (i.e. owner/group).

    if( !(FILE_PERSISTENT_ACLS & DestFileFsAttributes ) ) {

        if( BasepCopyFileCallback( TRUE,   // Continue (ignore the problem) by default
                                   ERROR_NOT_SUPPORTED,
                                   Context,
                                   NULL,
                                   PRIVCALLBACK_SECURITY_INFORMATION_NOT_SUPPORTED,
                                   SourceFile,
                                   DestFile,
                                   DeleteDest )) {
            // The caller wants to coninue on despite this.
            Succeeded = TRUE;
        }

        goto Exit;
    }

    // Check that DACL is copy-able if necessary

    if( SecurityInformation & DACL_SECURITY_INFORMATION ) {

        // We're supposed to copy the DACL.  Do we have enough access?
        if( !( SourceFileAccess & GENERIC_READ ) ||
            !( DestFileAccess & WRITE_DAC ) ) {

            SecurityInformation &= ~DACL_SECURITY_INFORMATION;

            if( !BasepCopyFileCallback( TRUE,   // Continue (ignore the problem) by default
                                        ERROR_ACCESS_DENIED,
                                        Context,
                                        NULL,
                                        PRIVCALLBACK_DACL_ACCESS_DENIED,
                                        SourceFile,
                                        DestFile,
                                        DeleteDest )) {
                goto Exit;
            }


        }
    }

    // Check that owner & group is copy-able if necessary

    if( (SecurityInformation & OWNER_SECURITY_INFORMATION) ||
        (SecurityInformation & GROUP_SECURITY_INFORMATION) ) {

        // We're supposed to copy owner & group.  Do we have enough access?

        if( !( SourceFileAccess & GENERIC_READ ) ||
            !( DestFileAccess & WRITE_OWNER ) ) {

            SecurityInformation &= ~(OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION);

            if( !BasepCopyFileCallback( TRUE,   // Continue (ignore the problem) by default
                                        ERROR_ACCESS_DENIED,
                                        Context,
                                        NULL,
                                        PRIVCALLBACK_OWNER_GROUP_ACCESS_DENIED,
                                        SourceFile,
                                        DestFile,
                                        DeleteDest )) {
                goto Exit;
            }


        }
    }

    // Check that SACL is copy-able if necessary

    if( SecurityInformation & SACL_SECURITY_INFORMATION ) {

        // We're supposed to copy the SACL.  Do we have enough rights?

        if( !(SourceFileAccess & ACCESS_SYSTEM_SECURITY) ||
            !(DestFileAccess & ACCESS_SYSTEM_SECURITY) ) {

            SecurityInformation &= ~SACL_SECURITY_INFORMATION;

            if( !BasepCopyFileCallback( TRUE,   // Continue (ignore the problem) by default
                                        ERROR_PRIVILEGE_NOT_HELD,
                                        Context,
                                        NULL,
                                        PRIVCALLBACK_SACL_ACCESS_DENIED,
                                        SourceFile,
                                        DestFile,
                                        DeleteDest )) {
                goto Exit;
            }

        }
    }

    // If nothing was copyable (and all was ignorable), then we're done.

    if( SecurityInformation == 0 ) {
        Succeeded = TRUE;
        goto Exit;
    }

    // Get the advapi32 APIs.

    Advapi32 = LoadLibraryW(AdvapiDllString);
    if( NULL == Advapi32 ) {
        *DeleteDest = TRUE;
        goto Exit;
    }

    
    GetNamedSecurityInfoWPtr     = (GETNAMEDSECURITYINFOWPTR) GetProcAddress( Advapi32,
                                                                              "GetNamedSecurityInfoW" );
    SetNamedSecurityInfoWPtr     = (SETNAMEDSECURITYINFOWPTR) GetProcAddress( Advapi32,
                                                                              "SetNamedSecurityInfoW" );

    GetSecurityDescriptorControlPtr = (GETSECURITYDESCRIPTORCONTROLPTR) GetProcAddress( Advapi32,
        "GetSecurityDescriptorControl" );

    if( GetNamedSecurityInfoWPtr == NULL ||
        GetSecurityDescriptorControlPtr == NULL ||
        SetNamedSecurityInfoWPtr == NULL ) {

        SetLastError( ERROR_INVALID_DLL );
        *DeleteDest = TRUE;
        goto Exit;
    }


    // Read in the security information from the source files

    dwError = GetNamedSecurityInfoWPtr( lpExistingFileName,
                                        SE_FILE_OBJECT,
                                        SecurityInformation,
                                        &Owner,
                                        &Group,
                                        &Dacl,
                                        &Sacl,
                                        &SecurityDescriptor );
    if( dwError != ERROR_SUCCESS ) {
        SetLastError( dwError );
        *DeleteDest = TRUE;
        goto Exit;
    }


    // We may have requested a DACL or SACL from a file that didn't have one.  If so,
    // don't try to set it (because it will cause a parameter error).

    if( Dacl == NULL ) {
        SecurityInformation &= ~DACL_SECURITY_INFORMATION;
    }
    if( Sacl == NULL ) {
        SecurityInformation &= ~SACL_SECURITY_INFORMATION;
    }

    if (SecurityInformation & (DACL_SECURITY_INFORMATION |
                               SACL_SECURITY_INFORMATION)) {
    
        if ( !GetSecurityDescriptorControlPtr( SecurityDescriptor, &Control, &dwRevision )) {
            // GetSecurityDescriptorControl calls BaseSetLastNTError on error
            *DeleteDest = TRUE;
            goto Exit;
        }
    }

    if (SecurityInformation & DACL_SECURITY_INFORMATION) {
        if (Control & SE_DACL_PROTECTED) {
            SecurityInformation |= PROTECTED_DACL_SECURITY_INFORMATION;
        } else {
            SecurityInformation |= UNPROTECTED_DACL_SECURITY_INFORMATION;
        }
    }
    if (SecurityInformation & SACL_SECURITY_INFORMATION) {
        if (Control & SE_SACL_PROTECTED) {
            SecurityInformation |= PROTECTED_SACL_SECURITY_INFORMATION;
        } else {
            SecurityInformation |= UNPROTECTED_SACL_SECURITY_INFORMATION;
        }
    }

    // Set the security on the dest file.  This loops because it may
    // have to back off on what it requests.

    while( TRUE && SecurityInformation != 0 ) {


        dwError = SetNamedSecurityInfoWPtr( lpNewFileName,
                                            SE_FILE_OBJECT,
                                            SecurityInformation,
                                            Owner,
                                            Group,
                                            Dacl,
                                            Sacl );

        // Even if we have WRITE_OWNER access, the SID we're setting might not
        // be valid.  If so, see if we can retry without them.

        if( dwError == ERROR_SUCCESS ) {
            break;
        } else {

            if( SecurityInformation & (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION) ) {

                if( !BasepCopyFileCallback( TRUE,   // Continue by default
                                            dwError,
                                            Context,
                                            NULL,
                                            PRIVCALLBACK_OWNER_GROUP_FAILED,
                                            SourceFile,
                                            DestFile,
                                            DeleteDest )) {
                    goto Exit;
                }

                // It's OK to ignore the owner/group.  Try again with them turned off.
                SecurityInformation &= ~(OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION);

            } else {

                // Samba 2.x says that it supports ACLs, but returns not-supported.
                if( !BasepCopyFileCallback( TRUE,   // Continue by default
                                            dwError,
                                            Context,
                                            NULL,
                                            PRIVCALLBACK_SECURITY_INFORMATION_NOT_SUPPORTED,
                                            SourceFile,
                                            DestFile,
                                            DeleteDest )) {
                    goto Exit;
                }

                SecurityInformation = 0;
            }

        }
    }   // while( TRUE && SecurityInformation != 0 )

    Succeeded = TRUE;

Exit:

    if( SecurityDescriptor != NULL ) {
        LocalFree( SecurityDescriptor );
    }

    if( Advapi32 != NULL ) {
        FreeLibrary( Advapi32 );
    }

    return( Succeeded );
}



BOOL
BasepCopyFileCallback( BOOL ContinueByDefault,
                       DWORD Win32ErrorOnStopOrCancel,
                       LPCOPYFILE_CONTEXT Context,
                       PLARGE_INTEGER StreamBytesCopied OPTIONAL,
                       DWORD CallbackReason,
                       HANDLE SourceFile,
                       HANDLE DestFile,
                       OPTIONAL PBOOL Canceled )
/*++

Routine Description:

    During CopyFile, call the CopyFileProgressCallback routine.

Arguments:

    ContinueByDefault - Value to use as the return code of this
        function if there is no callback function or the callback
        returns PROGRESS_REASON_NOT_HANDLED.

    Win32ErrorOnStopOrCancel - If the callback returns PROGRESS_STOP
        or PROGRESS_CANCEL set this as the last error.

    Context - Structure with the information necessary to call
        the callback.

    StreamBytesCopied - If provided, passed to the callback.  If not
        provided, zero is passed.

    CallbackReason - Passed to the callback as the dwReasonCode.

    SourceFile - The source of the CopyFile.

    DestFile - The destination of the CopyFile.

    Canceled - Pointer to a bool that on return indicates that the copy operation
                has been canceled by the user.

Return Value:

    TRUE - The CopyFile should continue.

    FALSE - The CopyFile should be aborted.  The last error will be set
        before this routine returns.

--*/

{ // BasepCopyFileCallback

    PLARGE_INTEGER StreamBytes;
    LARGE_INTEGER Zero;
    DWORD ReturnCode;
    BOOL Continue = ContinueByDefault;

    // If there's no callback context or it's been quieted, then
    // there's nothing to do.

    if( Context == NULL || Context->lpProgressRoutine == NULL )
        return( Continue );

    // If the caller didn't provide a StreamBytesCopied, use zero.

    if( StreamBytesCopied == NULL ) {
        StreamBytes = &Zero;
        StreamBytes->QuadPart = 0;
    } else {
        StreamBytes = StreamBytesCopied;
    }

    // Call the callback

    ReturnCode = Context->lpProgressRoutine(
                    Context->TotalFileSize,
                    Context->TotalBytesTransferred,
                    Context->TotalFileSize,
                    *StreamBytes,
                    Context->dwStreamNumber,
                    CallbackReason,
                    SourceFile,
                    DestFile,
                    Context->lpData
                    );

    if( Canceled ) {
        *Canceled = FALSE;
    }

    switch( ReturnCode )
    {
    case PROGRESS_QUIET:
        Context->lpProgressRoutine = NULL;
        Continue = TRUE;
        break;

    case PROGRESS_CANCEL:
        if( Canceled ) {
            *Canceled = TRUE;
        }
        // Fall through

    case PROGRESS_STOP:
        SetLastError( Win32ErrorOnStopOrCancel );
        Continue = FALSE;
        break;

    case PROGRESS_CONTINUE:
        Continue = TRUE;
        break;

    case PRIVPROGRESS_REASON_NOT_HANDLED:
    default:

        if( !Continue ) {
            SetLastError( Win32ErrorOnStopOrCancel );
        }
        break;
    }

    return( Continue );

}





BOOL
WINAPI
ReplaceFileA(
    LPCSTR  lpReplacedFileName,
    LPCSTR  lpReplacementFileName,
    LPCSTR  lpBackupFileName OPTIONAL,
    DWORD   dwReplaceFlags,
    LPVOID  lpExclude,
    LPVOID  lpReserved
    )

/*++

Routine Description:

    ANSI thunk to ReplaceFileW

--*/

{
    UNICODE_STRING DynamicUnicodeReplaced;
    UNICODE_STRING DynamicUnicodeReplacement;
    UNICODE_STRING DynamicUnicodeBackup;
    BOOL b = FALSE;

    //
    // Parameter validation.
    //

    if(NULL == lpReplacedFileName || NULL == lpReplacementFileName ||
       NULL != lpExclude || NULL != lpReserved ||
       dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if (!Basep8BitStringToDynamicUnicodeString( &DynamicUnicodeReplaced, lpReplacedFileName )) {
        return FALSE;
    }

    if (!Basep8BitStringToDynamicUnicodeString( &DynamicUnicodeReplacement, lpReplacementFileName )) {
        goto end1;
    }

    if (lpBackupFileName) {
        if (!Basep8BitStringToDynamicUnicodeString( &DynamicUnicodeBackup, lpBackupFileName )) {
            goto end2;
        }
    } else {
        DynamicUnicodeBackup.Buffer = NULL;
    }

    b = ReplaceFileW(DynamicUnicodeReplaced.Buffer,
                     DynamicUnicodeReplacement.Buffer,
                     DynamicUnicodeBackup.Buffer,
                     dwReplaceFlags,
                     lpExclude,
                     lpReserved);

    if(lpBackupFileName) {
        RtlFreeUnicodeString(&DynamicUnicodeBackup);
    }

end2:
    RtlFreeUnicodeString(&DynamicUnicodeReplacement);
end1:
    RtlFreeUnicodeString(&DynamicUnicodeReplaced);

    return b;
}

BOOL
WINAPI
ReplaceFileW(
    LPCWSTR lpReplacedFileName,
    LPCWSTR lpReplacementFileName,
    LPCWSTR lpBackupFileName OPTIONAL,
    DWORD   dwReplaceFlags,
    LPVOID  lpExclude,
    LPVOID  lpReserved
    )

/*++

Routine Description:

    Replace a file with a new file. The original file's attributes, alternate
    data streams, oid, acl, compression/encryption are transfered to the new
    file. If a backup file name is supplied, the original file is left at the
    backup file specified. Object ID, Create time/date, and file shortnames are
    tunneled by the system.

Arguments:

    lpReplacementFileName - name of the new file.

    lpReplacedFileName - name of the file to be replaced.

    lpBackupFileName - optional. If not NULL, the original file can be found
        under this name.

    dwReplaceFlags - specifies how the file is to be replaced. Currently, the
        possible values are:
        REPLACEFILE_WRITE_THROUGH   Setting this flag guarantees that any
                                    tunneled information is flushed to disk
                                    before the function returns.
        REPLACEFILE_IGNORE_MERGE_ERRORS Setting this flag lets the routine
                                        continue on with the operation even
                                        when merge error occurs. If this flag
                                        is set, GetLastError will not return
                                        ERROR_UNABLE_TO_MERGE_DATA.

    lpExclude - Reserved for future use. Must be set to NULL.

    lpReserved - for future use. Must be set to NULL.

Return Value:

    TRUE - The operation was successful.
    FALSE - The operation failed. Extended error status is available
        using GetLastError.

Error Code:

    ERROR_UNABLE_TO_REMOVE_REPLACED     The replacement file has inherited the
                                        replaced file's attributes and streams.
                                        the replaced file is unchanged. Both
                                        files still exist under their original
                                        names. No backup file exists.

    ERROR_UNABLE_TO_MOVE_REPLACEMENT    Same as above. Except that backup file
                                        exists if requested.

    ERROR_UNABLE_TO_MOVE_REPLACEMENT_2  The replacement file has inherited the
                                        replaced file's attributes and streams.
                                        It's still under its original name.
                                        Replaced file exists under the name of
                                        the backup file.

    All other error codes               Both replacement file and replaced file
                                        exist under their original names. The
                                        replacement file may have inherited
                                        none of, or part of, or all of the
                                        replaced file's attributes and streams.
                                        No backup file exists.

--*/

{
    HANDLE                          advapi32LibHandle = INVALID_HANDLE_VALUE;
    ENCRYPTFILEWPTR                 EncryptFileWPtr = NULL;
    DECRYPTFILEWPTR                 DecryptFileWPtr = NULL;
    HANDLE                          ReplacedFile = INVALID_HANDLE_VALUE;
    HANDLE                          ReplacementFile = INVALID_HANDLE_VALUE;
    HANDLE                          StreamHandle = INVALID_HANDLE_VALUE;
    HANDLE                          OutputStreamHandle = INVALID_HANDLE_VALUE;
    UNICODE_STRING                  ReplacedFileNTName;
    UNICODE_STRING                  ReplacementFileNTName;
    UNICODE_STRING                  StreamNTName;
    UNICODE_STRING                  BackupNTFileName;
    RTL_RELATIVE_NAME               ReplacedRelativeName;
    RTL_RELATIVE_NAME               ReplacementRelativeName;
    OBJECT_ATTRIBUTES               ReplacedObjAttr;
    OBJECT_ATTRIBUTES               ReplacementObjAttr;
    OBJECT_ATTRIBUTES               StreamObjAttr;
    IO_STATUS_BLOCK                 IoStatusBlock;
    NTSTATUS                        status;
    BOOL                            fSuccess = FALSE;
    BOOL                            fDoCopy;
    PVOID                           ReplacedFreeBuffer = NULL;
    PVOID                           ReplacementFreeBuffer = NULL;
    FILE_BASIC_INFORMATION          ReplacedBasicInfo;
    FILE_BASIC_INFORMATION          ReplacementBasicInfo;
    DWORD                           ReplacementFileAccess;
    DWORD                           ReplacedFileAccess;
    FILE_COMPRESSION_INFORMATION    ReplacedCompressionInfo;
    PSECURITY_DESCRIPTOR            ReplacedSecDescPtr = NULL;
    DWORD                           dwSizeNeeded;
    ULONG                           cInfo;
    PFILE_STREAM_INFORMATION        ReplacedStreamInfo = NULL;
    PFILE_STREAM_INFORMATION        ReplacementStreamInfo = NULL;
    PFILE_STREAM_INFORMATION        ScannerStreamInfoReplaced = NULL;
    PFILE_STREAM_INFORMATION        ScannerStreamInfoReplacement = NULL;
    DWORD                           dwCopyFlags = COPY_FILE_FAIL_IF_EXISTS;
    DWORD                           dwCopySize = 0;
    PFILE_RENAME_INFORMATION        BackupReplaceRenameInfo = NULL;
    PFILE_RENAME_INFORMATION        ReplaceRenameInfo = NULL;
    LPCOPYFILE_CONTEXT              context = NULL;
    BOOL                            fQueryReplacedFileFail = FALSE;
    BOOL                            fQueryReplacementFileFail = FALSE;
    BOOL                            fReplacedFileIsEncrypted = FALSE;
    BOOL                            fReplacedFileIsCompressed = FALSE;
    BOOL                            fReplacementFileIsEncrypted = FALSE;
    BOOL                            fReplacementFileIsCompressed = FALSE;
    WCHAR *                         pwszTempBackupFile = NULL;
    DWORD                           DestFileFsAttributes = 0;
    WCHAR                           SavedLastChar;

    struct {
        FILE_FS_ATTRIBUTE_INFORMATION   Info;
        WCHAR                           Buffer[MAX_PATH];
    } ReplacementFsAttrInfoBuffer;

    //
    // Initialization
    //

    RtlInitUnicodeString(&BackupNTFileName, NULL);

    //
    // Parameter validation.
    //

    if(NULL == lpReplacedFileName || NULL == lpReplacementFileName ||
       NULL != lpExclude || NULL != lpReserved ||
       dwReplaceFlags & ~(REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        goto Exit;
    }

    try {

        //
        // Open the to-be-replaced file
        //

        RtlInitUnicodeString(&ReplacedFileNTName, NULL);
        if(!RtlDosPathNameToNtPathName_U(lpReplacedFileName,
                                         &ReplacedFileNTName,
                                         NULL,
                                         &ReplacedRelativeName)) {
            SetLastError(ERROR_PATH_NOT_FOUND);
            leave;
        }
        ReplacedFreeBuffer = ReplacedFileNTName.Buffer;
        if(ReplacedRelativeName.RelativeName.Length) {
            ReplacedFileNTName = *(PUNICODE_STRING)&ReplacedRelativeName.RelativeName;
        }
        else {
            ReplacedRelativeName.ContainingDirectory = NULL;
        }
        InitializeObjectAttributes(&ReplacedObjAttr,
                                   &ReplacedFileNTName,
                                   OBJ_CASE_INSENSITIVE,
                                   ReplacedRelativeName.ContainingDirectory,
                                   NULL);

        ReplacedFileAccess = GENERIC_READ | DELETE | SYNCHRONIZE | ACCESS_SYSTEM_SECURITY;

        status = NtOpenFile(&ReplacedFile,
                            ReplacedFileAccess,
                            &ReplacedObjAttr,
                            &IoStatusBlock,
                            FILE_SHARE_READ |
                            FILE_SHARE_WRITE |
                            FILE_SHARE_DELETE,
                            FILE_NON_DIRECTORY_FILE |
                            FILE_SYNCHRONOUS_IO_NONALERT);

        if (!NT_SUCCESS(status)) {
            ReplacedFileAccess &= ~ACCESS_SYSTEM_SECURITY;

            status = NtOpenFile(&ReplacedFile,
                            ReplacedFileAccess,
                            &ReplacedObjAttr,
                            &IoStatusBlock,
                            FILE_SHARE_READ |
                            FILE_SHARE_WRITE |
                            FILE_SHARE_DELETE,
                            FILE_NON_DIRECTORY_FILE |
                            FILE_SYNCHRONOUS_IO_NONALERT);
        }
        
        if(!NT_SUCCESS(status))
        {
            BaseSetLastNTError(status);
            leave;
        }

        //
        // Open the replacement file
        //

        if(!RtlDosPathNameToNtPathName_U(lpReplacementFileName,
                                         &ReplacementFileNTName,
                                         NULL,
                                         &ReplacementRelativeName)) {
            SetLastError(ERROR_PATH_NOT_FOUND);
            leave;
        }
        ReplacementFreeBuffer = ReplacementFileNTName.Buffer;
        if(ReplacementRelativeName.RelativeName.Length) {
            ReplacementFileNTName = *(PUNICODE_STRING)&ReplacementRelativeName.RelativeName;
        }
        else {
            ReplacementRelativeName.ContainingDirectory = NULL;
        }
        InitializeObjectAttributes(&ReplacementObjAttr,
                                   &ReplacementFileNTName,
                                   OBJ_CASE_INSENSITIVE,
                                   ReplacementRelativeName.ContainingDirectory,
                                   NULL);

        if ((ReplacedFileAccess & ACCESS_SYSTEM_SECURITY)) {
            ReplacementFileAccess = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY;

            status = NtOpenFile(&ReplacementFile,
                            ReplacementFileAccess,
                            &ReplacementObjAttr,
                            &IoStatusBlock,
                            0,
                            FILE_NON_DIRECTORY_FILE |
                            FILE_SYNCHRONOUS_IO_NONALERT);
        }
        else status = STATUS_ACCESS_DENIED;  // force the open

        if (!NT_SUCCESS(status)) {
            ReplacementFileAccess = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE | DELETE | WRITE_DAC;
            status = NtOpenFile(&ReplacementFile,
                            ReplacementFileAccess,
                            &ReplacementObjAttr,
                            &IoStatusBlock,
                            0,
                            FILE_NON_DIRECTORY_FILE |
                            FILE_SYNCHRONOUS_IO_NONALERT);
        }

        if (STATUS_ACCESS_DENIED == status &&


           dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS) {
            ReplacementFileAccess = SYNCHRONIZE | GENERIC_READ | DELETE | WRITE_DAC;
            status = NtOpenFile(&ReplacementFile,
                                ReplacementFileAccess,
                                &ReplacementObjAttr,
                                &IoStatusBlock,
                                0,
                                FILE_NON_DIRECTORY_FILE |
                                FILE_SYNCHRONOUS_IO_NONALERT);
        }

        if(STATUS_ACCESS_DENIED == status &&
           dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)
        {   // try again without WRITE_DAC access
            ReplacementFileAccess = SYNCHRONIZE | GENERIC_READ | DELETE;
            status = NtOpenFile(&ReplacementFile,
                                ReplacementFileAccess,
                                &ReplacementObjAttr,
                                &IoStatusBlock,
                                0,
                                FILE_NON_DIRECTORY_FILE |
                                FILE_SYNCHRONOUS_IO_NONALERT);
        }

        if(!NT_SUCCESS(status))
        {
            BaseSetLastNTError(status);
            leave;
        }

        //
        // Get the attributes of the to-be-replaced file and set them on the
        // replacement file. FILE_ATTRIBUTE_COMPRESSED and
        // FILE_ATTRIBUTE_ENCRYPTED can be obtained by NtQueryInformationFile,
        // but can't be set by NtSetInformationFile. Compression and
        // encryption will be handled later.
        //

        status = NtQueryInformationFile(ReplacedFile,
                                        &IoStatusBlock,
                                        &ReplacedBasicInfo,
                                        sizeof(ReplacedBasicInfo),
                                        FileBasicInformation);
        if(!NT_SUCCESS(status)) {
            if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                BaseSetLastNTError(status);
                leave;
            }
            fQueryReplacedFileFail = TRUE;
        }
        else {
            // don't replace read-only files. See bug 38426
            if ((ReplacedBasicInfo.FileAttributes & FILE_ATTRIBUTE_READONLY)) {
                status = STATUS_ACCESS_DENIED;
                BaseSetLastNTError(status);  // ERROR_ACCESS_DENIED
                leave;
            }

            status = NtQueryInformationFile(ReplacementFile,
                                            &IoStatusBlock,
                                            &ReplacementBasicInfo,
                                            sizeof(ReplacementBasicInfo),
                                            FileBasicInformation);
            if(!NT_SUCCESS(status)) {
                if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                    BaseSetLastNTError(status);
                    leave;
                }
                fQueryReplacementFileFail = TRUE;
            }

            //
            // Creation time is the only time we want to preserve. So zero out
            // all the other times.
            //
            ReplacedBasicInfo.LastAccessTime.QuadPart = 0;
            ReplacedBasicInfo.LastWriteTime.QuadPart = 0;
            ReplacedBasicInfo.ChangeTime.QuadPart = 0;
            status = NtSetInformationFile(ReplacementFile,
                                          &IoStatusBlock,
                                          &ReplacedBasicInfo,
                                          sizeof(ReplacedBasicInfo),
                                          FileBasicInformation);
            if(!NT_SUCCESS(status) &&
               !(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                BaseSetLastNTError(status);
                leave;
            }
        }

        //
        // Transfer ACLs from the to-be-replaced file to the replacement file.
        //

        status = NtQueryVolumeInformationFile(ReplacementFile,
                                              &IoStatusBlock,
                                              &ReplacementFsAttrInfoBuffer.Info,
                                              sizeof(ReplacementFsAttrInfoBuffer),
                                              FileFsAttributeInformation);
        if(!NT_SUCCESS(status)) {
            if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                BaseSetLastNTError(status);
                leave;
            }
        }
        else
        {
            BOOL Delete = FALSE;
            if( !BasepCopySecurityInformation( lpReplacedFileName,
                                               ReplacedFile,
                                               ReplacedFileAccess,
                                               lpReplacementFileName,
                                               ReplacementFile,
                                               ReplacementFileAccess,
                                               DACL_SECURITY_INFORMATION |
                                               SACL_SECURITY_INFORMATION,
                                               NULL,
                                               ReplacementFsAttrInfoBuffer.Info.FileSystemAttributes,
                                               &Delete )) {
                leave;
    
            }
        }


        //
        // If the to-be-replaced file has alternate data streams, and they do
        // not exist in the replacement file, copy them into the replacement
        // file.
        //

        cInfo = 4096;
        do {
            ReplacedStreamInfo = RtlAllocateHeap(RtlProcessHeap(),
                                                  MAKE_TAG(TMP_TAG),
                                                  cInfo);
            if (!ReplacedStreamInfo) {
                break;
            }
            status = NtQueryInformationFile(ReplacedFile,
                                            &IoStatusBlock,
                                            ReplacedStreamInfo,
                                            cInfo,
                                            FileStreamInformation);
            if (!NT_SUCCESS(status)) {
                RtlFreeHeap(RtlProcessHeap(), 0, ReplacedStreamInfo);
                ReplacedStreamInfo = NULL;
                cInfo *= 2;
            }
        } while(status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL);
        if(NULL == ReplacedStreamInfo) {
            if(status != STATUS_INVALID_PARAMETER &&
               status != STATUS_NOT_IMPLEMENTED) {
                if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                    BaseSetLastNTError(status);
                    leave;
                }
            }
        }
        else {
            if(!NT_SUCCESS(status)) {
                if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                    BaseSetLastNTError(status);
                    leave;
                }
            }
            else {
                // The outer loop enumerates streams in the to-be-replaced file.
                ScannerStreamInfoReplaced = ReplacedStreamInfo;
                while(TRUE) {
                    // Skip the default stream.
                    if(ScannerStreamInfoReplaced->StreamNameLength <= sizeof(WCHAR) ||
                        ScannerStreamInfoReplaced->StreamName[1] == ':') {
                        if(0 == ScannerStreamInfoReplaced->NextEntryOffset) {
                            break;
                        }
                        ScannerStreamInfoReplaced = (PFILE_STREAM_INFORMATION)((PCHAR)ScannerStreamInfoReplaced + ScannerStreamInfoReplaced->NextEntryOffset);
                        continue;
                    }

                    // Query replacement file stream information if we haven't done so.
                    // We wait until now to do this query because we don't want to do
                    // it unless it's absolutely necessary.
                    if(NULL == ReplacementStreamInfo) {
                        cInfo = 4096;
                        do {
                            ReplacementStreamInfo = RtlAllocateHeap(RtlProcessHeap(),
                                                                     MAKE_TAG(TMP_TAG),
                                                             cInfo);
                            if (!ReplacementStreamInfo) {
                                break;
                            }
                            status = NtQueryInformationFile(ReplacementFile,
                                                            &IoStatusBlock,
                                                            ReplacementStreamInfo,
                                                            cInfo - sizeof( WCHAR ),
                                                            FileStreamInformation);
                            if (!NT_SUCCESS(status)) {
                                RtlFreeHeap(RtlProcessHeap(), 0, ReplacementStreamInfo);
                                ReplacementStreamInfo = NULL;
                                cInfo *= 2;
                            }
                        } while(status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL);
                        if(NULL == ReplacementStreamInfo ||
                           !NT_SUCCESS(status)) {
                            if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                                BaseSetLastNTError(status);
                                leave;
                            }
                            break;
                        }
                    }

                    // The inner loop enumerates the replacement file streams.
                    ScannerStreamInfoReplacement = ReplacementStreamInfo;
                    fDoCopy = TRUE;
                    while(TRUE) {
                        if(ScannerStreamInfoReplaced->StreamNameLength == ScannerStreamInfoReplacement->StreamNameLength &&
                           _wcsnicmp(ScannerStreamInfoReplaced->StreamName, ScannerStreamInfoReplacement->StreamName, ScannerStreamInfoReplacement->StreamNameLength / sizeof(WCHAR)) == 0) {
                            // The stream already exists in the replacement file.
                            fDoCopy = FALSE;
                            break;
                        }
                        if(0 == ScannerStreamInfoReplacement->NextEntryOffset) {
                            // end of the stream information
                            break;
                        }
                        ScannerStreamInfoReplacement = (PFILE_STREAM_INFORMATION)((PCHAR)ScannerStreamInfoReplacement + ScannerStreamInfoReplacement->NextEntryOffset);
                    }

                    // We copy the stream if it doesn't exist in the replacement file.
                    if(TRUE == fDoCopy) {
                        StreamNTName.Buffer = &ScannerStreamInfoReplaced->StreamName[0];
                        StreamNTName.Length = (USHORT)ScannerStreamInfoReplaced->StreamNameLength;
                        StreamNTName.MaximumLength = StreamNTName.Length;

                        // Open the stream in the to-be-replaced file.
                        InitializeObjectAttributes(&StreamObjAttr,
                                                   &StreamNTName,
                                                   0,
                                                   ReplacedFile,
                                                   NULL);
                        status = NtOpenFile(&StreamHandle,
                                            SYNCHRONIZE |
                                            GENERIC_READ,
                                            &StreamObjAttr,
                                            &IoStatusBlock,
                                            FILE_SHARE_READ |
                                            FILE_SHARE_WRITE |
                                            FILE_SHARE_DELETE,
                                            FILE_SYNCHRONOUS_IO_NONALERT |
                                            FILE_SEQUENTIAL_ONLY);
                        if(!NT_SUCCESS(status)) {
                            if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                                BaseSetLastNTError(status);
                                leave;
                            }

                            if(0 == ScannerStreamInfoReplaced->NextEntryOffset) {
                                break;
                            }

                            ScannerStreamInfoReplaced = (PFILE_STREAM_INFORMATION)((PCHAR)ScannerStreamInfoReplaced + ScannerStreamInfoReplaced->NextEntryOffset);
                            continue;
                        }

                        // Copy the stream;
                        SavedLastChar = StreamNTName.Buffer[StreamNTName.Length / sizeof( WCHAR )];
                        StreamNTName.Buffer[StreamNTName.Length / sizeof( WCHAR )] = L'\0';
                        OutputStreamHandle = INVALID_HANDLE_VALUE;
                        if(!BaseCopyStream(NULL,
                                           StreamHandle,
                                           SYNCHRONIZE | GENERIC_READ,
                                           StreamNTName.Buffer,
                                           ReplacementFile,
                                           &ScannerStreamInfoReplaced->StreamSize,
                                           &dwCopyFlags,
                                           &OutputStreamHandle,
                                           &dwCopySize,
                                           &context,
                                           NULL,
                                           FALSE,
                                           0,
                                           &DestFileFsAttributes )) {
                            
                            StreamNTName.Buffer[StreamNTName.Length / sizeof( WCHAR )] = SavedLastChar;
                            
                            if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                                leave;
                            }
                        }
                        
                        StreamNTName.Buffer[StreamNTName.Length / sizeof( WCHAR )] = SavedLastChar;
                        
                        NtClose(StreamHandle);
                        StreamHandle = INVALID_HANDLE_VALUE;
                        if (INVALID_HANDLE_VALUE != OutputStreamHandle) {
                            NtClose(OutputStreamHandle);
                            OutputStreamHandle = INVALID_HANDLE_VALUE;
                        }
                    } // copy stream

                    if(0 == ScannerStreamInfoReplaced->NextEntryOffset) {
                        break;
                    }

                    ScannerStreamInfoReplaced = (PFILE_STREAM_INFORMATION)((PCHAR)ScannerStreamInfoReplaced + ScannerStreamInfoReplaced->NextEntryOffset);
                } // outer loop
            }
        }

        //
        // Compression/Encryption.
        //

        // If we successfully read the to-be-replaced file's attributes, we
        // do the necessary compression/encryption. Otherwise we do nothing.
        // If we don't know the replacement files attributes
        // (fQueryReplacementFileFail is TRUE), to be on the safe side, we will
        // try to (un)compress/(un)encrypt it if the to-be-replaced file is
        // (un)compressed/(un)encrypted.
        if(!fQueryReplacedFileFail) {

            fReplacedFileIsEncrypted = ReplacedBasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED;
            fReplacedFileIsCompressed = ReplacedBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED;
            if(!fQueryReplacementFileFail) {
                fReplacementFileIsEncrypted = ReplacementBasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED;
                fReplacementFileIsCompressed = ReplacementBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED;
            }
            else {
                // If we don't know the file attributes of the replacement
                // file, we'll assume the replacement file has opposite
                // encryption/compression attributes as the replaced file
                // so that encryption/compression operations will be forced
                // on the replacement file.
                fReplacementFileIsEncrypted = !fReplacedFileIsEncrypted;
                fReplacementFileIsCompressed = !fReplacedFileIsCompressed;
            }

            //
            // Encryption.
            //

            // If the to-be-replaced file is encrypted and either the
            // replacement file is encrypted or we don't know it's encryption
            // status, we try to encrypt the replacement file.
            if(fReplacedFileIsEncrypted && !fReplacementFileIsEncrypted) {
                NtClose(ReplacementFile);
                ReplacementFile = INVALID_HANDLE_VALUE;
                // There's no way to encrypt a file based on its handle.  We
                // must use the Win32 API (which calls over to the EFS service).
                advapi32LibHandle = LoadLibraryW(AdvapiDllString);
                if(NULL == advapi32LibHandle) {
                    if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                        leave;
                    }
                }
                else {
                    EncryptFileWPtr = (ENCRYPTFILEWPTR)GetProcAddress(advapi32LibHandle, "EncryptFileW");
                    if(NULL == EncryptFileWPtr) {
                        if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                            leave;
                        }
                    }
                    else {
                        if((EncryptFileWPtr)(lpReplacementFileName)) {
                            // Encryption operation automatically decompresses
                            // compressed files. We need this flag for the
                            // case when the replaced file is encrypted and
                            // the replacement file is compressed. At this
                            // point, the replacement file is encrypted.
                            // Because a file is automatically decompressed
                            // when it's encrypted, we don't want to
                            // decompress it again, otherwise we'll get an
                            // error.
                            fReplacementFileIsCompressed = FALSE;
                        } else if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                            leave;
                        }
                    }
                }
                status = NtOpenFile(&ReplacementFile,
                                    SYNCHRONIZE |
                                    GENERIC_READ |
                                    GENERIC_WRITE |
                                    WRITE_DAC |
                                    DELETE,
                                    &ReplacementObjAttr,
                                    &IoStatusBlock,
                                    0,
                                    FILE_NON_DIRECTORY_FILE |
                                    FILE_SYNCHRONOUS_IO_NONALERT);

                if (STATUS_ACCESS_DENIED == status &&

                   dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS) {
                    status = NtOpenFile(&ReplacementFile,
                                        SYNCHRONIZE |
                                        GENERIC_READ |
                                        DELETE |
                                        WRITE_DAC,
                                        &ReplacementObjAttr,
                                        &IoStatusBlock,
                                        0,
                                        FILE_NON_DIRECTORY_FILE |
                                        FILE_SYNCHRONOUS_IO_NONALERT);
                }
                if(STATUS_ACCESS_DENIED == status &&
                   dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS) {
                    status = NtOpenFile(&ReplacementFile,
                                        SYNCHRONIZE |
                                        GENERIC_READ |
                                        DELETE,
                                        &ReplacementObjAttr,
                                        &IoStatusBlock,
                                        0,
                                        FILE_NON_DIRECTORY_FILE |
                                        FILE_SYNCHRONOUS_IO_NONALERT);
                }

                // We leave without attempt to rename the files because we know
                // we can't rename the replacement file without it being opened
                // first.
                if(!NT_SUCCESS(status)) {
                    BaseSetLastNTError(status);
                    leave;
                }
            }
            else if(!fReplacedFileIsEncrypted && fReplacementFileIsEncrypted) {
                // If the to-be-replaced file is not encrypted and the
                // replacement file is encrypted, decrypt the replacement file.
                NtClose(ReplacementFile);
                ReplacementFile = INVALID_HANDLE_VALUE;
                advapi32LibHandle = LoadLibraryW(AdvapiDllString);
                if(NULL == advapi32LibHandle) {
                    if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                        leave;
                    }
                }
                else {
                    DecryptFileWPtr = (DECRYPTFILEWPTR)GetProcAddress(advapi32LibHandle, "DecryptFileW");
                    if(NULL == DecryptFileWPtr) {
                        if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                            leave;
                        }
                    }
                    else {
                        if((DecryptFileWPtr)(lpReplacementFileName, 0)) {
                            fReplacementFileIsEncrypted = FALSE;
                        } else if(!(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                            leave;
                        }
                    }
                }
                status = NtOpenFile(&ReplacementFile,
                                    SYNCHRONIZE |
                                    GENERIC_READ |
                                    GENERIC_WRITE |
                                    WRITE_DAC |
                                    DELETE,
                                    &ReplacementObjAttr,
                                    &IoStatusBlock,
                                    0,
                                    FILE_NON_DIRECTORY_FILE |
                                    FILE_SYNCHRONOUS_IO_NONALERT);

                if(STATUS_ACCESS_DENIED == status &&
                   dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS) {
                    status = NtOpenFile(&ReplacementFile,
                                        SYNCHRONIZE |
                                        GENERIC_READ |
                                        DELETE |
                                        WRITE_DAC,
                                        &ReplacementObjAttr,
                                        &IoStatusBlock,
                                        0,
                                        FILE_NON_DIRECTORY_FILE |
                                        FILE_SYNCHRONOUS_IO_NONALERT);
                }

                if(STATUS_ACCESS_DENIED == status &&
                   dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS) {
                    status = NtOpenFile(&ReplacementFile,
                                        SYNCHRONIZE |
                                        GENERIC_READ |
                                        DELETE,
                                        &ReplacementObjAttr,
                                        &IoStatusBlock,
                                        0,
                                        FILE_NON_DIRECTORY_FILE |
                                        FILE_SYNCHRONOUS_IO_NONALERT);
                }

                // We leave without attempt to rename the files because we know
                // we can't rename the replacement file without it being opened
                // first.
                if(!NT_SUCCESS(status)) {
                    BaseSetLastNTError(status);
                    leave;
                }
            }

            //
            // Compression.
            //

            // If the to-be-replaced file is compressed, and the replacement
            // file is not, we compress the replacement file. In the case that
            // we don't know if the replacement file is compressed or not
            // (fQueryReplacementFileFail is TRUE), we will
            // try to compress it anyway and ignore the error if it's already
            // compressed.
            if(fReplacedFileIsCompressed && !fReplacementFileIsCompressed) {
                // Get the compression method mode.
                status = NtQueryInformationFile(ReplacedFile,
                                                &IoStatusBlock,
                                                &ReplacedCompressionInfo,
                                                sizeof(FILE_COMPRESSION_INFORMATION),
                                                FileCompressionInformation);
                if(!NT_SUCCESS(status)) {
                    // We couldn't get the compression method code. if the
                    // ignore merge error flag is on, we continue on to
                    // encryption. Otherwise, we set last error and leave.
                    if(!fQueryReplacementFileFail &&
                       !(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                        BaseSetLastNTError(status);
                        leave;
                    }
                }
                else {
                    // Do the compression. If we fail and ignore failure flag
                    // is not on, set error and leave. Otherwise continue on
                    // to encryption.
                    status = NtFsControlFile(ReplacementFile,
                                             NULL,
                                             NULL,
                                             NULL,
                                             &IoStatusBlock,
                                             FSCTL_SET_COMPRESSION,
                                             &ReplacedCompressionInfo.CompressionFormat,
                                             sizeof(ReplacedCompressionInfo.CompressionFormat),
                                             NULL,
                                             0);
                    if(!fQueryReplacementFileFail && !NT_SUCCESS(status) &&
                       !(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                            BaseSetLastNTError(status);
                            leave;
                    }
                }
            }
            else if(!fReplacedFileIsCompressed && fReplacementFileIsCompressed && !fReplacementFileIsEncrypted) {
                // The replaced file is not compressed, the replacement file
                // is compressed (or that the query information for replacement
                // file failed and we don't know if it's compressed or not),
                // decompress the replacement file.
                USHORT      CompressionFormat = 0;
                status = NtFsControlFile(ReplacementFile,
                                         NULL,
                                         NULL,
                                         NULL,
                                         &IoStatusBlock,
                                         FSCTL_SET_COMPRESSION,
                                         &CompressionFormat,
                                         sizeof(CompressionFormat),
                                         NULL,
                                         0);
                if(!fQueryReplacementFileFail && !NT_SUCCESS(status) &&
                   !(dwReplaceFlags & REPLACEFILE_IGNORE_MERGE_ERRORS)) {
                        BaseSetLastNTError(status);
                        leave;
                }
            }
        } // if querying replaced file attribute failed.

        //
        // Do the renames.
        //

        if (NULL == lpBackupFileName) {
            HANDLE hFile = INVALID_HANDLE_VALUE;
            DWORD  dwCounter = 0;
            DWORD  dwReplacedFileLength = lstrlenW(lpReplacedFileName) * 
                                          sizeof(WCHAR);
            WCHAR  wcsSuffix [16];

            pwszTempBackupFile = RtlAllocateHeap(RtlProcessHeap(),
                                    MAKE_TAG(TMP_TAG),
                                    dwReplacedFileLength + sizeof(wcsSuffix));

            if(NULL == pwszTempBackupFile) {
                SetLastError(ERROR_UNABLE_TO_REMOVE_REPLACED);
                leave;
            }

            while (hFile == INVALID_HANDLE_VALUE && dwCounter < 16) {
                swprintf (wcsSuffix, L"~RF%4x.TMP", dwCounter + GetTickCount());
                lstrcpyW (pwszTempBackupFile, lpReplacedFileName);
                lstrcatW (pwszTempBackupFile, wcsSuffix);

                hFile = CreateFileW ( pwszTempBackupFile,
                          GENERIC_WRITE | DELETE, // file access
                          0,             // share mode
                          NULL,          // SD
                          CREATE_NEW,    // how to create
                          FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY,
                          NULL);         // handle to template file

                dwCounter++;
            }

            if (hFile != INVALID_HANDLE_VALUE) {
                CloseHandle (hFile);         // immediately close temp file
            } else {
                SetLastError(ERROR_UNABLE_TO_REMOVE_REPLACED);
                leave;
            }

        }
        else {
            pwszTempBackupFile = (WCHAR *) lpBackupFileName;
        }

        // If backup file requested, rename the to-be-replaced file to backup.
        // Otherwise delete it.

        if(!RtlDosPathNameToNtPathName_U(pwszTempBackupFile,
                                             &BackupNTFileName,
                                             NULL,
                                             NULL)) {
            SetLastError(ERROR_PATH_NOT_FOUND);
            leave;
        }

        BackupReplaceRenameInfo = RtlAllocateHeap(RtlProcessHeap(),
                                              MAKE_TAG(TMP_TAG),
                                              BackupNTFileName.Length +
                                              sizeof(*BackupReplaceRenameInfo));
        if(NULL == BackupReplaceRenameInfo)
        {
            SetLastError(ERROR_UNABLE_TO_REMOVE_REPLACED);
            leave;
        }
        BackupReplaceRenameInfo->ReplaceIfExists = TRUE;
        BackupReplaceRenameInfo->RootDirectory = NULL;
        BackupReplaceRenameInfo->FileNameLength = BackupNTFileName.Length;
        RtlCopyMemory(BackupReplaceRenameInfo->FileName, BackupNTFileName.Buffer, BackupNTFileName.Length);
        status = NtSetInformationFile(ReplacedFile,
                                          &IoStatusBlock,
                                          BackupReplaceRenameInfo,
                                          BackupNTFileName.Length +
                                          sizeof(*BackupReplaceRenameInfo),
                                          FileRenameInformation);
        if(!NT_SUCCESS(status)) {
            SetLastError(ERROR_UNABLE_TO_REMOVE_REPLACED);
            leave;
        }

        // Rename the replacement file to the replaced file.
        ReplaceRenameInfo = RtlAllocateHeap(RtlProcessHeap(),
                                      MAKE_TAG(TMP_TAG),
                                      ReplacedFileNTName.Length +
                                      sizeof(*ReplaceRenameInfo));
        if(NULL == ReplaceRenameInfo)
        {
            SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT);
            leave;
        }
        ReplaceRenameInfo->ReplaceIfExists = TRUE;
        ReplaceRenameInfo->RootDirectory = NULL;
        ReplaceRenameInfo->FileNameLength = ReplacedFileNTName.Length;
        RtlCopyMemory(ReplaceRenameInfo->FileName, ReplacedFileNTName.Buffer, ReplacedFileNTName.Length);
        status = NtSetInformationFile(ReplacementFile,
                                      &IoStatusBlock,
                                      ReplaceRenameInfo,
                                      ReplacedFileNTName.Length +
                                      sizeof(*ReplaceRenameInfo),
                                      FileRenameInformation);
        if(!NT_SUCCESS(status)) {
            // If we failed to rename the replacement file, and a backup file
            // for the original file exists, we try to restore the original
            // file from the backup file.
            if(lpBackupFileName) {
                status = NtSetInformationFile(ReplacedFile,
                                              &IoStatusBlock,
                                              ReplaceRenameInfo,
                                              ReplacedFileNTName.Length +
                                              sizeof(*ReplaceRenameInfo),
                                              FileRenameInformation);
                if(!NT_SUCCESS(status)) {
                    SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT_2);
                }
                else {
                    SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT);
                }
                leave;
            }
            else {
                SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT);
                leave;
            }
        }

        //
        // All is well. We set the return code to TRUE. And flush the files if
        // necessary.
        //

        if(dwReplaceFlags & REPLACEFILE_WRITE_THROUGH) {
            NtFlushBuffersFile(ReplacedFile, &IoStatusBlock);
        }

        fSuccess = TRUE;

    } finally {

        if(INVALID_HANDLE_VALUE != advapi32LibHandle && NULL != advapi32LibHandle) {
            FreeLibrary(advapi32LibHandle);
        }

        if(INVALID_HANDLE_VALUE != ReplacedFile) {
            NtClose(ReplacedFile);
        }
        if(INVALID_HANDLE_VALUE != ReplacementFile) {
            NtClose(ReplacementFile);
        }
        if(INVALID_HANDLE_VALUE != StreamHandle) {
            NtClose(StreamHandle);
        }
        if(INVALID_HANDLE_VALUE != OutputStreamHandle) {
            NtClose(OutputStreamHandle);
        }

        RtlFreeHeap(RtlProcessHeap(), 0, ReplacedFreeBuffer);
        RtlFreeHeap(RtlProcessHeap(), 0, ReplacementFreeBuffer);
        RtlFreeHeap(RtlProcessHeap(), 0, BackupNTFileName.Buffer);

        RtlFreeHeap(RtlProcessHeap(), 0, ReplacedStreamInfo);
        RtlFreeHeap(RtlProcessHeap(), 0, ReplacementStreamInfo);
        RtlFreeHeap(RtlProcessHeap(), 0, ReplaceRenameInfo);
        RtlFreeHeap(RtlProcessHeap(), 0, BackupReplaceRenameInfo);

        if (pwszTempBackupFile != NULL && 
            pwszTempBackupFile != lpBackupFileName) {
            DeleteFileW (pwszTempBackupFile);
            RtlFreeHeap(RtlProcessHeap(), 0, pwszTempBackupFile);
        }

    }

Exit:

    return fSuccess;
}


VOID
BaseMarkFileForDelete(
    HANDLE File,
    DWORD FileAttributes
    )

/*++

Routine Description:

    This routine marks a file for delete, so that when the supplied handle
    is closed, the file will actually be deleted.

Arguments:

    File - Supplies a handle to the file that is to be marked for delete.

    FileAttributes - Attributes for the file, if known.  Zero indicates they
        are unknown.

Return Value:

    None.

--*/

{
    #undef DeleteFile

    FILE_DISPOSITION_INFORMATION DispositionInformation;
    IO_STATUS_BLOCK IoStatus;
    FILE_BASIC_INFORMATION BasicInformation;

    if (!FileAttributes) {
        BasicInformation.FileAttributes = 0;
        NtQueryInformationFile(
            File,
            &IoStatus,
            &BasicInformation,
            sizeof(BasicInformation),
            FileBasicInformation
            );
        FileAttributes = BasicInformation.FileAttributes;
        }

    if (FileAttributes & FILE_ATTRIBUTE_READONLY) {
        RtlZeroMemory(&BasicInformation, sizeof(BasicInformation));
        BasicInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL;
        NtSetInformationFile(
            File,
            &IoStatus,
            &BasicInformation,
            sizeof(BasicInformation),
            FileBasicInformation
            );
        }

    DispositionInformation.DeleteFile = TRUE;
    NtSetInformationFile(
        File,
        &IoStatus,
        &DispositionInformation,
        sizeof(DispositionInformation),
        FileDispositionInformation
        );

}