/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    copy.c

Abstract:

    This is for supporting copying files, creating new files, and copying the registries to
    the remote server.

Author:

    Sean Selitrennikoff - 4/5/98

Revision History:

--*/


#include "precomp.h"
#pragma hdrstop

WCHAR pConfigPath[MAX_PATH];
WCHAR pSystemPath[MAX_PATH];
WCHAR pCSDVersion[128];
WCHAR pProcessorArchitecture[64];
WCHAR pCurrentType[128];
WCHAR pHalName[128];

#if 0
IMIRROR_MODIFY_DS_INFO ModifyInfo;
#endif

NTSTATUS
AddCopyToDoItems(
    VOID
    )

/*++

Routine Description:

    This routine adds all the to do items necessary for copying files and registries to
    a server installation point, as well as creating new files necessary for remote boot.

Arguments:

    None

Return Value:

    STATUS_SUCCESS if it completes adding all the to do items properly.

--*/

{
    NTSTATUS Status;
    Status = AddToDoItem(CheckPartitions, NULL, 0);

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, IMirrorInitialize);
        return Status;
    }

    Status = AddToDoItem(CopyPartitions, NULL, 0);

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, IMirrorInitialize);
        return Status;
    }
#if 0
    Status = AddToDoItem(PatchDSEntries, &ModifyInfo, sizeof(ModifyInfo)); // NOTE: This MUST come before MungeRegistry

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, IMirrorInitialize);
        return Status;
    }
#endif
    return STATUS_SUCCESS;
}










//
// Functions for processing each TO DO item
//

NTSTATUS
CopyCopyPartitions(
    IN PVOID pBuffer,
    IN ULONG Length
    )

/*++

Routine Description:

    This routine verifies that any partition on the same disk as in the parameter pBuffer, has
    enough free space to hold all the files in the partition that is also in pBuffer.

Arguments:

    pBuffer - Pointer to any arguments passed in the to do item.

    Length - Length, in bytes of the arguments.

Return Value:

    STATUS_SUCCESS if it completes adding all the to do items properly.

--*/

{
    NTSTATUS Status;
    PLIST_ENTRY listEntry;
    PMIRROR_VOLUME_INFO mirrorVolInfo;
    ULONG numberPartitions;
    BOOLEAN copyRegistry = FALSE;
    ULONG NameLength;
    ULONG baseLength = 0;
    BOOLEAN gotUncPath = FALSE;

    IMirrorNowDoing(CopyPartitions, NULL);

    if (GlobalMirrorCfgInfo == NULL) {

        Status = CheckForPartitions();

        if (!NT_SUCCESS(Status)) {
            IMirrorHandleError(Status, CopyPartitions);
            return Status;
        }
    }

    ASSERT(GlobalMirrorCfgInfo != NULL);

    if (GlobalMirrorCfgInfo == NULL) {
        Status = STATUS_INVALID_PARAMETER;
        IMirrorHandleError(Status, CopyPartitions);
        return(Status);
    }
    
    listEntry = GlobalMirrorCfgInfo->MirrorVolumeList.Flink;

    numberPartitions = 0;
    Status = STATUS_SUCCESS;

    while (listEntry != &GlobalMirrorCfgInfo->MirrorVolumeList) {

        mirrorVolInfo = (PMIRROR_VOLUME_INFO) CONTAINING_RECORD(
                                                listEntry,
                                                MIRROR_VOLUME_INFO,
                                                ListEntry
                                                );
        listEntry = listEntry->Flink;

        if (mirrorVolInfo->MirrorUncPath == NULL) {

            if (! gotUncPath) {

                gotUncPath = TRUE;
                NameLength = WCHARSIZE(TmpBuffer);

                Status = IMirrorGetMirrorDir( (PWCHAR)TmpBuffer,
                                             &NameLength);

                if (!NT_SUCCESS(Status)) {

                    IMirrorHandleError(Status, CopyPartitions);
                    return Status;
                }

                baseLength = lstrlenW( (PWCHAR) TmpBuffer );
            }

            swprintf(   (PWCHAR)TmpBuffer2,
                        L"\\Mirror%d",
                        mirrorVolInfo->MirrorTableIndex );

            *((PWCHAR)(TmpBuffer)+baseLength) = L'\0';

            if (wcslen((PWCHAR)TmpBuffer) + wcslen((PWCHAR)TmpBuffer2) + 1 > WCHARSIZE(TmpBuffer)) {
                Status = STATUS_NO_MEMORY;
                IMirrorHandleError(Status, CopyPartitions);
                return Status;
            }
            lstrcatW( (PWCHAR)TmpBuffer, (PWCHAR)TmpBuffer2 );

            NameLength = (lstrlenW( (PWCHAR)TmpBuffer ) + 1) * sizeof(WCHAR);

            mirrorVolInfo->MirrorUncPath = IMirrorAllocMem(NameLength);

            if (mirrorVolInfo->MirrorUncPath == NULL) {

                Status = STATUS_NO_MEMORY;
                IMirrorHandleError(Status, CopyPartitions);
                return Status;
            }

            RtlMoveMemory( mirrorVolInfo->MirrorUncPath,
                           TmpBuffer,
                           NameLength );
        }

        IMirrorNowDoing(CopyPartitions, mirrorVolInfo->MirrorUncPath);

        if (mirrorVolInfo->IsBootDisk) {

            copyRegistry = TRUE;
        }

        Status = AddToDoItem(CopyFiles, &mirrorVolInfo->DriveLetter, sizeof(WCHAR));

        if (!NT_SUCCESS(Status)) {
            IMirrorHandleError(Status, CheckPartitions);
            return Status;
        }

        numberPartitions++;
    }

    if (copyRegistry) {

        Status = AddToDoItem(CopyRegistry, pBuffer, Length);

        if (!NT_SUCCESS(Status)) {
            IMirrorHandleError(Status, CopyPartitions);
            return Status;
        }
    }

    //
    //  write out the mirror config file locally so that if we restart, we
    //  can retrieve the same config again.
    //

    if (numberPartitions && ! GlobalMirrorCfgInfo->SysPrepImage) {

        //
        // Write it out to \\SystemRoot\System32\IMirror.dat
        //

        Status = GetBaseDeviceName(L"\\SystemRoot", (PWCHAR)TmpBuffer2, sizeof(TmpBuffer2));

        wcscat((PWCHAR)TmpBuffer2, L"\\System32\\");
        wcscat((PWCHAR)TmpBuffer2, IMIRROR_DAT_FILE_NAME );

        Status = NtPathToDosPath((PWCHAR)TmpBuffer2, (PWCHAR)TmpBuffer, WCHARSIZE(TmpBuffer), FALSE, FALSE);

        if (NT_SUCCESS(Status)) {

            Status = WriteMirrorConfigFile((PWCHAR) TmpBuffer);

            if (!NT_SUCCESS(Status)) {
                IMirrorHandleError(Status, CopyPartitions);
            }
        }
    }

    return Status;
}



NTSTATUS
CopyCopyFiles(
    IN PVOID pBuffer,
    IN ULONG Length
    )

/*++

Routine Description:

    This routine copies all the files on the given drive to the remote server.

Arguments:

    pBuffer - Pointer to any arguments passed in the to do item.
              The argument must be the drive letter

    Length - Length, in bytes of the arguments.  Should be sizeof(WCHAR)

Return Value:

    STATUS_SUCCESS if it completes the to do item properly.

--*/

{
    PLIST_ENTRY listEntry;
    PMIRROR_VOLUME_INFO mirrorVolInfo = NULL;
    NTSTATUS Status;
    WCHAR LocalBuffer[TMP_BUFFER_SIZE];
    WCHAR SourcePath[8];
    PCOPY_TREE_CONTEXT copyContext = NULL;
    BOOLEAN BackupPriviledged = FALSE;

    Status = IMirrorNowDoing(CopyFiles, NULL);
    if ( Status != NO_ERROR ) {
        return Status;
    }

    if (GlobalMirrorCfgInfo == NULL) {

        Status = CheckForPartitions();

        if (!NT_SUCCESS(Status)) {
            IMirrorHandleError(Status, CopyPartitions);
            return Status;
        }
    }

    ASSERT(GlobalMirrorCfgInfo != NULL);

    if (GlobalMirrorCfgInfo == NULL) {
        Status = STATUS_INVALID_PARAMETER;
        IMirrorHandleError(Status, CopyPartitions);
        return(Status);
    }

    if (Length != sizeof(WCHAR) || pBuffer == NULL) {

        IMirrorHandleError(ERROR_INVALID_DRIVE, CopyFiles);
        return ERROR_INVALID_DRIVE;
    }

    listEntry = GlobalMirrorCfgInfo->MirrorVolumeList.Flink;

    while (listEntry != &GlobalMirrorCfgInfo->MirrorVolumeList) {

        mirrorVolInfo = (PMIRROR_VOLUME_INFO) CONTAINING_RECORD(
                                                listEntry,
                                                MIRROR_VOLUME_INFO,
                                                ListEntry
                                                );
        listEntry = listEntry->Flink;

        if (mirrorVolInfo->DriveLetter == *(PWCHAR)pBuffer) {
            break;
        }

        mirrorVolInfo = NULL;
    }
    if (mirrorVolInfo == NULL) {

        IMirrorHandleError(ERROR_INVALID_DRIVE, CopyFiles);
        return ERROR_INVALID_DRIVE;
    }

    //
    // Create root directory, don't fail if it already exists
    //
    if (!CreateDirectory(mirrorVolInfo->MirrorUncPath, NULL)) {

        Status = GetLastError();

        if (Status != ERROR_ALREADY_EXISTS) {
            IMirrorHandleError(Status, CopyFiles);
            return Status;
        }
    }

    //
    //   create the config file in this mirror root
    //

    if (_snwprintf(LocalBuffer,
                   WCHARSIZE(LocalBuffer),
                   L"%s\\%s",
                   mirrorVolInfo->MirrorUncPath,
                   IMIRROR_DAT_FILE_NAME) < 0) {
        Status = STATUS_BUFFER_OVERFLOW;
        IMirrorHandleError(Status, CopyFiles);
        return Status;
    }
    LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';

    Status = WriteMirrorConfigFile(LocalBuffer);

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, CopyFiles);
        return Status;
    }

    if (!GlobalMirrorCfgInfo->SysPrepImage) {

        //
        //  create staging directory if one is required
        //
        if (_snwprintf(LocalBuffer,
                   WCHARSIZE(LocalBuffer),
                   L"%s\\Staging",
                   mirrorVolInfo->MirrorUncPath) < 0) {
            Status = STATUS_BUFFER_OVERFLOW;
            IMirrorHandleError(Status, CopyFiles);
            return Status;
        }
        LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';

        if (!CreateDirectory( LocalBuffer, NULL)) {
            Status = GetLastError();

            if (Status != ERROR_ALREADY_EXISTS) {
                IMirrorHandleError(Status, CopyFiles);
                return Status;
            }
        }
    }

    if (mirrorVolInfo->PartitionActive) {

        //
        //  copy the boot code to the server
        //

        if (_snwprintf(LocalBuffer,
                   WCHARSIZE(LocalBuffer),
                   L"%s\\BootCode.dat",
                   mirrorVolInfo->MirrorUncPath) < 0) {
            Status = STATUS_BUFFER_OVERFLOW;
            IMirrorHandleError(Status, CopyFiles);
            return Status;
        }
        LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';
        
        Status = SaveBootSector(    mirrorVolInfo->DiskNumber,
                                    mirrorVolInfo->PartitionNumber,
                                    mirrorVolInfo->BlockSize,
                                    LocalBuffer );

        if (Status != STATUS_SUCCESS) {
            IMirrorHandleError(Status, CopyFiles);
            return Status;
        }
    }

    //
    // Create UserData directory, don't fail if it already exists
    //
    if (_snwprintf(LocalBuffer,
                   WCHARSIZE(LocalBuffer),
                   L"%s\\UserData",
                   mirrorVolInfo->MirrorUncPath) < 0) {
        Status = STATUS_BUFFER_OVERFLOW;
        IMirrorHandleError(Status, CopyFiles);
        return Status;
    }
    LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';

    if (!CreateDirectory( LocalBuffer, NULL)) {
        Status = GetLastError();

        if (Status != ERROR_ALREADY_EXISTS) {
            IMirrorHandleError(Status, CopyFiles);
            return Status;
        }
    }

    //
    //  set up the dest path ready for really long file names.
    //

    if (*(mirrorVolInfo->MirrorUncPath) == L'\\' &&
        *(mirrorVolInfo->MirrorUncPath+1) == L'\\') {

        if (*(mirrorVolInfo->MirrorUncPath+2) == L'?') {

            // dest is \\?\..., it's of the correct format
            if (_snwprintf(LocalBuffer,
                   WCHARSIZE(LocalBuffer),
                   L"%s\\UserData",
                   mirrorVolInfo->MirrorUncPath) < 0) {
                Status = STATUS_BUFFER_OVERFLOW;
                IMirrorHandleError(Status, CopyFiles);
                return Status;
            }
            LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';
        } else {

            // dest is \\billg1\imirror
            // format should be \\?\UNC\billg1\imirror
            if (_snwprintf(LocalBuffer,
                   WCHARSIZE(LocalBuffer),
                   L"\\\\?\\UNC%s\\UserData",
                   mirrorVolInfo->MirrorUncPath +1) < 0) {
                Status = STATUS_BUFFER_OVERFLOW;
                IMirrorHandleError(Status, CopyFiles);
                return Status;
            }
            LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';
        }
    } else {

        // dest is something like X:
        // format should be \\?\X:

        if (_snwprintf(LocalBuffer,
               WCHARSIZE(LocalBuffer),
               L"\\\\?\\%s\\UserData",
               mirrorVolInfo->MirrorUncPath) < 0) {
            Status = STATUS_BUFFER_OVERFLOW;
            IMirrorHandleError(Status, CopyFiles);
            return Status;
        }
        LocalBuffer[WCHARSIZE(LocalBuffer)-1] = L'\0';
    }

    SourcePath[0] = L'\\';  // format is L"\\\\?\\E:"
    SourcePath[1] = L'\\';
    SourcePath[2] = L'?';
    SourcePath[3] = L'\\';
    SourcePath[4] = mirrorVolInfo->DriveLetter;
    SourcePath[5] = L':';
    SourcePath[6] = L'\\';
    SourcePath[7] = L'\0';

    //
    // Copy all the files
    //

    Status = AllocateCopyTreeContext( &copyContext, TRUE );

    if (Status != ERROR_SUCCESS) {

        IMirrorHandleError(Status, CopyFiles);
        return Status;
    }

    if (RTEnableBackupRestorePrivilege()) {
        BackupPriviledged = TRUE;
    }

    Status = CopyTree( copyContext,
                       (BOOLEAN) (mirrorVolInfo->PartitionType == PARTITION_IFS),
                       &SourcePath[0],
                       LocalBuffer );

    if (BackupPriviledged) {
        RTDisableBackupRestorePrivilege();
    }

    if (copyContext->Cancelled) {
        //
        //  since the copy was cancelled, let's bail on the rest of the processing.
        //

        ClearAllToDoItems(TRUE);
    }

    FreeCopyTreeContext( copyContext );

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, CopyFiles);
        return(Status);
    }
    return Status;
}

NTSTATUS
CopyCopyRegistry(
    IN PVOID pBuffer,
    IN ULONG Length
    )

/*++

Routine Description:

    This routine copies the currently running registries to the server.

Arguments:

    pBuffer - Pointer to any arguments passed in the to do item.

    Length - Length, in bytes of the arguments.

Return Value:

    STATUS_SUCCESS if it completes the to do item properly.

--*/

{
    NTSTATUS Status;
    PLIST_ENTRY listEntry;
    ULONG Error;
    PMIRROR_VOLUME_INFO mirrorVolInfo = NULL;

    UNREFERENCED_PARAMETER(pBuffer);
    UNREFERENCED_PARAMETER(Length);

    IMirrorNowDoing(CopyRegistry, NULL);

    if (GlobalMirrorCfgInfo == NULL) {

        Status = CheckForPartitions();

        if (!NT_SUCCESS(Status)) {
            IMirrorHandleError(Status, CopyPartitions);
            return Status;
        }
    }

    ASSERT(GlobalMirrorCfgInfo != NULL);

    if (GlobalMirrorCfgInfo == NULL) {
        Status = STATUS_INVALID_PARAMETER;
        IMirrorHandleError(Status, CopyPartitions);
        return(Status);
    }

    listEntry = GlobalMirrorCfgInfo->MirrorVolumeList.Flink;

    while (listEntry != &GlobalMirrorCfgInfo->MirrorVolumeList) {

        mirrorVolInfo = (PMIRROR_VOLUME_INFO) CONTAINING_RECORD(
                                                listEntry,
                                                MIRROR_VOLUME_INFO,
                                                ListEntry
                                                );
        listEntry = listEntry->Flink;

        if (mirrorVolInfo->IsBootDisk) {
            break;
        }

        mirrorVolInfo = NULL;
    }
    if (mirrorVolInfo == NULL) {

        IMirrorHandleError(ERROR_INVALID_DRIVE, CopyRegistry);
        return ERROR_INVALID_DRIVE;
    }

    //
    // Now do registry backup
    //

    Error = DoFullRegBackup( mirrorVolInfo->MirrorUncPath );
    if (Error != NO_ERROR) {
        IMirrorHandleError(Error, CopyRegistry);
        return STATUS_UNSUCCESSFUL;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
WriteMirrorConfigFile(
    PWCHAR DestFile
    )
{
    ULONG bufferSize;
    PLIST_ENTRY listEntry;
    PMIRROR_VOLUME_INFO mirrorVolInfo;
    PMIRROR_VOLUME_INFO_FILE mirrorVolInfoFile;
    PMIRROR_CFG_INFO_FILE mirrorInfoFile;
    ULONG pathLength;
    ULONG systemPathLength;
    ULONG csdVersionLength;
    ULONG processorArchitectureLength;
    ULONG currentTypeLength;
    ULONG halNameLength;
    PWCHAR nextString;
    NTSTATUS Status;
    HANDLE fileHandle;

retryWriteConfig:

    mirrorInfoFile = NULL;
    Status = STATUS_SUCCESS;
    fileHandle = INVALID_HANDLE_VALUE;

    ASSERT(GlobalMirrorCfgInfo != NULL);

    if (GlobalMirrorCfgInfo == NULL) {
        Status = STATUS_INVALID_PARAMETER;
        goto exitWriteFile;
    }

    systemPathLength = (lstrlenW( GlobalMirrorCfgInfo->SystemPath ) + 1) * sizeof(WCHAR);
    csdVersionLength = (lstrlenW( GlobalMirrorCfgInfo->CSDVersion ) + 1) * sizeof(WCHAR);
    processorArchitectureLength = (lstrlenW( GlobalMirrorCfgInfo->ProcessorArchitecture ) + 1) * sizeof(WCHAR);
    currentTypeLength = (lstrlenW( GlobalMirrorCfgInfo->CurrentType ) + 1) * sizeof(WCHAR);
    halNameLength = (lstrlenW( GlobalMirrorCfgInfo->HalName ) + 1) * sizeof(WCHAR);

    bufferSize = sizeof( MIRROR_CFG_INFO_FILE ) +
                (sizeof( MIRROR_VOLUME_INFO_FILE ) *
                 (GlobalMirrorCfgInfo->NumberVolumes - 1)) +
                 systemPathLength +
                 csdVersionLength +
                 processorArchitectureLength +
                 currentTypeLength +
                 halNameLength;

    listEntry = GlobalMirrorCfgInfo->MirrorVolumeList.Flink;

    while (listEntry != &GlobalMirrorCfgInfo->MirrorVolumeList) {

        mirrorVolInfo = (PMIRROR_VOLUME_INFO) CONTAINING_RECORD(
                                                listEntry,
                                                MIRROR_VOLUME_INFO,
                                                ListEntry
                                                );
        listEntry = listEntry->Flink;
        bufferSize += ((lstrlenW( mirrorVolInfo->MirrorUncPath ) + 1) * sizeof(WCHAR)) +
                      ((lstrlenW( mirrorVolInfo->VolumeLabel ) + 1) * sizeof(WCHAR)) +
                      ((lstrlenW( mirrorVolInfo->NtName ) + 1) * sizeof(WCHAR)) +
                      ((lstrlenW( mirrorVolInfo->ArcName ) + 1) * sizeof(WCHAR));
    }

    mirrorInfoFile = (PMIRROR_CFG_INFO_FILE) IMirrorAllocMem( bufferSize );

    if (mirrorInfoFile == NULL) {

        Status = STATUS_NO_MEMORY;
        goto exitWriteFile;
    }

    mirrorInfoFile->MirrorVersion = IMIRROR_CURRENT_VERSION;
    mirrorInfoFile->FileLength = bufferSize;
    mirrorInfoFile->NumberVolumes = GlobalMirrorCfgInfo->NumberVolumes;
    mirrorInfoFile->SystemPathLength = systemPathLength;
    mirrorInfoFile->CSDVersionLength = csdVersionLength;
    mirrorInfoFile->ProcessorArchitectureLength = processorArchitectureLength;
    mirrorInfoFile->CurrentTypeLength = currentTypeLength;
    mirrorInfoFile->HalNameLength = halNameLength;
    mirrorInfoFile->SysPrepImage = GlobalMirrorCfgInfo->SysPrepImage;
    mirrorInfoFile->Debug = GlobalMirrorCfgInfo->Debug;
    mirrorInfoFile->MajorVersion = GlobalMirrorCfgInfo->MajorVersion;
    mirrorInfoFile->MinorVersion = GlobalMirrorCfgInfo->MinorVersion;
    mirrorInfoFile->BuildNumber = GlobalMirrorCfgInfo->BuildNumber;
    mirrorInfoFile->KernelFileVersionMS = GlobalMirrorCfgInfo->KernelFileVersionMS;
    mirrorInfoFile->KernelFileVersionLS = GlobalMirrorCfgInfo->KernelFileVersionLS;
    mirrorInfoFile->KernelFileFlags = GlobalMirrorCfgInfo->KernelFileFlags;

    mirrorVolInfoFile = &mirrorInfoFile->Volumes[mirrorInfoFile->NumberVolumes];
    nextString = (PWCHAR) mirrorVolInfoFile;

    mirrorInfoFile->SystemPathOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
    lstrcpyW( nextString, GlobalMirrorCfgInfo->SystemPath );
    nextString += systemPathLength / sizeof(WCHAR);

    mirrorInfoFile->CSDVersionOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
    lstrcpyW( nextString, GlobalMirrorCfgInfo->CSDVersion );
    nextString += csdVersionLength / sizeof(WCHAR);

    mirrorInfoFile->ProcessorArchitectureOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
    lstrcpyW( nextString, GlobalMirrorCfgInfo->ProcessorArchitecture );
    nextString += processorArchitectureLength / sizeof(WCHAR);

    mirrorInfoFile->CurrentTypeOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
    lstrcpyW( nextString, GlobalMirrorCfgInfo->CurrentType );
    nextString += currentTypeLength / sizeof(WCHAR);

    mirrorInfoFile->HalNameOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
    lstrcpyW( nextString, GlobalMirrorCfgInfo->HalName );
    nextString += halNameLength / sizeof(WCHAR);

    listEntry = GlobalMirrorCfgInfo->MirrorVolumeList.Flink;

    mirrorVolInfoFile = &mirrorInfoFile->Volumes[0];

    while (listEntry != &GlobalMirrorCfgInfo->MirrorVolumeList) {

        mirrorVolInfo = (PMIRROR_VOLUME_INFO) CONTAINING_RECORD(
                                                listEntry,
                                                MIRROR_VOLUME_INFO,
                                                ListEntry
                                                );
        listEntry = listEntry->Flink;

        mirrorVolInfoFile->MirrorTableIndex = mirrorVolInfo->MirrorTableIndex;
        mirrorVolInfoFile->DriveLetter = mirrorVolInfo->DriveLetter;
        mirrorVolInfoFile->PartitionType = mirrorVolInfo->PartitionType;
        mirrorVolInfoFile->PartitionActive = mirrorVolInfo->PartitionActive;
        mirrorVolInfoFile->IsBootDisk = mirrorVolInfo->IsBootDisk;
        mirrorVolInfoFile->CompressedVolume = mirrorVolInfo->CompressedVolume;
        mirrorVolInfoFile->DiskSignature = mirrorVolInfo->DiskSignature;
        mirrorVolInfoFile->BlockSize = mirrorVolInfo->BlockSize;
        mirrorVolInfoFile->LastUSNMirrored = mirrorVolInfo->LastUSNMirrored;
        mirrorVolInfoFile->FileSystemFlags = mirrorVolInfo->FileSystemFlags;
        wcscpy(mirrorVolInfoFile->FileSystemName, mirrorVolInfo->FileSystemName);
        mirrorVolInfoFile->DiskSpaceUsed = mirrorVolInfo->DiskSpaceUsed;
        mirrorVolInfoFile->StartingOffset = mirrorVolInfo->StartingOffset;
        mirrorVolInfoFile->PartitionSize = mirrorVolInfo->PartitionSize;
        mirrorVolInfoFile->DiskNumber = mirrorVolInfo->DiskNumber;
        mirrorVolInfoFile->PartitionNumber = mirrorVolInfo->PartitionNumber;

        // set the path in the config file relative to the root of this
        // image.  As example, set it to L"\Mirror1\UserData"
        swprintf(   (PWCHAR)TmpBuffer2,
                    L"\\Mirror%d\\UserData",
                    mirrorVolInfo->MirrorTableIndex );

        pathLength = (lstrlenW( (PWCHAR)TmpBuffer2 ) + 1) * sizeof(WCHAR);
        mirrorVolInfoFile->MirrorUncLength = pathLength;
        mirrorVolInfoFile->MirrorUncPathOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
        lstrcpyW( nextString, (PWCHAR)TmpBuffer2 );
        nextString += pathLength / sizeof(WCHAR);

        pathLength = (lstrlenW( mirrorVolInfo->VolumeLabel ) + 1) * sizeof(WCHAR);
        mirrorVolInfoFile->VolumeLabelLength = pathLength;
        mirrorVolInfoFile->VolumeLabelOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
        lstrcpyW( nextString, mirrorVolInfo->VolumeLabel );
        nextString += pathLength / sizeof(WCHAR);

        pathLength = (lstrlenW( mirrorVolInfo->NtName ) + 1) * sizeof(WCHAR);
        mirrorVolInfoFile->NtNameLength = pathLength;
        mirrorVolInfoFile->NtNameOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
        lstrcpyW( nextString, mirrorVolInfo->NtName );
        nextString += pathLength / sizeof(WCHAR);

        pathLength = (lstrlenW( mirrorVolInfo->ArcName ) + 1) * sizeof(WCHAR);
        mirrorVolInfoFile->ArcNameLength = pathLength;
        mirrorVolInfoFile->ArcNameOffset = (ULONG)((PCHAR) nextString - (PCHAR) mirrorInfoFile);
        lstrcpyW( nextString, mirrorVolInfo->ArcName );
        nextString += pathLength / sizeof(WCHAR);

        mirrorVolInfoFile++;

        //
        //  call off to the wizard to tell him what the system directory
        //  is if we have a valid one.
        //

        if (mirrorVolInfo->IsBootDisk && (systemPathLength > 3 * sizeof(WCHAR))) {

            //
            //  pass it in the form of "MirrorX\UserData\WinNT"
            //  so we have to skip the C: in the systempath
            //

            swprintf(   (PWCHAR)TmpBuffer2,
                        L"Mirror%d\\UserData",
                        mirrorVolInfo->MirrorTableIndex );

            lstrcatW( (PWCHAR)TmpBuffer2, GlobalMirrorCfgInfo->SystemPath + 2 );
            IMirrorSetSystemPath(   (PWCHAR) TmpBuffer2,
                                    (lstrlenW( (PWCHAR) TmpBuffer2)) );
        }
    }

    fileHandle = CreateFile(    DestFile,
                                GENERIC_WRITE,
                                0,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_ARCHIVE,
                                NULL );

    if (fileHandle == INVALID_HANDLE_VALUE) {

        Status = GetLastError();
        goto exitWriteFile;
    }

    if (!WriteFile( fileHandle,
                    mirrorInfoFile,
                    bufferSize,
                    &bufferSize,
                    NULL)) {

        Status = GetLastError();
        goto exitWriteFile;
    }

exitWriteFile:

    if (fileHandle != INVALID_HANDLE_VALUE) {

        CloseHandle( fileHandle );
        fileHandle = INVALID_HANDLE_VALUE;
    }

    if (mirrorInfoFile) {
        IMirrorFreeMem( mirrorInfoFile );
        mirrorInfoFile = NULL;
    }

    if (Status != ERROR_SUCCESS) {

        DWORD errorCase;

        errorCase = ReportCopyError(   NULL,
                                       DestFile,
                                       COPY_ERROR_ACTION_CREATE_FILE,
                                       Status );
        if (errorCase == STATUS_RETRY) {
            goto retryWriteConfig;
        }
        if ( errorCase == ERROR_SUCCESS ) {
            Status = ERROR_SUCCESS;
        }
    }
    return Status;
}

#define MIN_BOOT_SECTOR_BLOCK_SIZE  512

NTSTATUS
SaveBootSector(
    DWORD DiskNumber,
    DWORD PartitionNumber,
    DWORD BlockSize,
    PWCHAR DestFile
    )
{
    NTSTATUS Status;
    UNICODE_STRING UnicodeString;
    IO_STATUS_BLOCK IoStatusBlock;
    DWORD bufferSize;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE bootSectorHandle = INVALID_HANDLE_VALUE;
    HANDLE fileHandle = INVALID_HANDLE_VALUE;
    PUCHAR AlignedBuffer;
    PUCHAR allocatedBuffer = NULL;

    swprintf((PWCHAR)TmpBuffer, L"\\Device\\Harddisk%d\\Partition%d", DiskNumber, PartitionNumber );

    RtlInitUnicodeString(&UnicodeString, (PWCHAR)TmpBuffer);

    InitializeObjectAttributes(&ObjectAttributes,
                               &UnicodeString,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL
                              );

    Status = NtCreateFile(&bootSectorHandle,
                          (ACCESS_MASK)FILE_GENERIC_READ,
                          &ObjectAttributes,
                          &IoStatusBlock,
                          NULL,
                          FILE_ATTRIBUTE_NORMAL,
                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                          FILE_OPEN,
                          FILE_SYNCHRONOUS_IO_NONALERT,
                          NULL,
                          0
                         );

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, CopyPartitions);
        goto exitSaveBootSector;
    }

    if (BlockSize < MIN_BOOT_SECTOR_BLOCK_SIZE) {

        BlockSize = MIN_BOOT_SECTOR_BLOCK_SIZE;
    }

    if (BlockSize + MIN_BOOT_SECTOR_BLOCK_SIZE > TMP_BUFFER_SIZE) {

        allocatedBuffer = IMirrorAllocMem( BlockSize + MIN_BOOT_SECTOR_BLOCK_SIZE );

        if (allocatedBuffer == NULL) {

            Status = STATUS_NO_MEMORY;
            goto exitSaveBootSector;
        }

        AlignedBuffer = ALIGN(allocatedBuffer, MIN_BOOT_SECTOR_BLOCK_SIZE);

    } else {

        AlignedBuffer = ALIGN(TmpBuffer, MIN_BOOT_SECTOR_BLOCK_SIZE);
    }

    Status = NtReadFile(bootSectorHandle,
                        NULL,
                        NULL,
                        NULL,
                        &IoStatusBlock,
                        AlignedBuffer,
                        BlockSize,
                        NULL,
                        NULL
                       );

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, CopyPartitions);
        goto exitSaveBootSector;
    }

    fileHandle = CreateFile(    DestFile,
                                GENERIC_WRITE,
                                0,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_ARCHIVE,
                                NULL );

    if (fileHandle == INVALID_HANDLE_VALUE) {

        Status = GetLastError();
        goto exitSaveBootSector;
    }

    if (!WriteFile( fileHandle,
                    AlignedBuffer,
                    BlockSize,
                    &bufferSize,
                    NULL)) {

        Status = GetLastError();

    } else {

        Status = STATUS_SUCCESS;
    }

exitSaveBootSector:

    if (bootSectorHandle != INVALID_HANDLE_VALUE) {
        NtClose(bootSectorHandle);
    }

    if (fileHandle != INVALID_HANDLE_VALUE) {
        NtClose(fileHandle);
    }

    if (allocatedBuffer) {
        IMirrorFreeMem( allocatedBuffer );
    }

    if (!NT_SUCCESS(Status)) {
        IMirrorHandleError(Status, CopyPartitions);
    }

    return STATUS_SUCCESS;
}

