Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1059 lines
25 KiB

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <ntddvol.h>
#include <ntdddisk.h>
#include <align.h>
#include <bootmbr.h>
#include "identify.h"
#define SECTOR_SIZE 512
#define SECTOR_MASK (SECTOR_SIZE - 1)
#define BOOTSECT_SECTORS 16
#define BOOTSECT_SIZE (BOOTSECT_SECTORS * SECTOR_SIZE)
#define MAX_NUM_CHS_ADDRESSABLE_SECTORS 16514064
#define NTLDR_FIND L"NTLDR"
#define NTLDR_REPLACE L"$LDR$"
#define NTLDR_LEN (sizeof(NTLDR_FIND)-sizeof(WCHAR))
//
// Perform the copy using 8 outstanding I/Os of 128k each
//
#define COPYBUF_SIZE (128 * 1024)
#define COPYBUF_COUNT 8
//
// A failed assert will abort the process
//
#define assert(x) if (!(x)) { printf("Assert failed: %s\n", #x); exit(-1); }
typedef struct _COPYBUF {
OVERLAPPED Overlapped;
ULONG State;
ULONG Bytes;
PVOID Buffer;
} COPYBUF, *PCOPYBUF;
//
// Three possible states for a copybuf
//
#define CB_FREE 0x0
#define CB_READ 0x1
#define CB_WRITE 0x2
//
// CUSTOM_IDENTIFY_DATA consists of an IDENTIFY_DATA structure,
// along with three fields in which to pass along the "BIOS" disk
// geometry to the SIMICS simulator.
//
#pragma pack(push,1)
typedef union _CUSTOM_IDENTIFY_DATA {
IDENTIFY_DATA IdentifyData;
struct {
USHORT Reserved[128];
ULONG Cylinders;
USHORT Heads;
USHORT SectorsPerTrack;
} BiosData;
} CUSTOM_IDENTIFY_DATA, *PCUSTOM_IDENTIFY_DATA;
#pragma pack(pop)
BOOLEAN
DisplayDiskGeometry(
IN HANDLE handle
);
VOID
DoWrite (
IN PVOID Buffer,
IN ULONG Length,
IN ULONG64 Offset,
IN PCOPYBUF CopyBuf
);
VOID
GetBootSectors (
IN LARGE_INTEGER Offset
);
BOOLEAN
GetIdentifyData(
IN HANDLE Handle,
OUT PIDENTIFY_DATA IdentifyData
);
BOOLEAN
GetVolumeInfo (
IN PCHAR DrivePath,
OUT PULONG DriveNumber,
OUT PULONG PartitionNumber,
OUT PULONG64 StartingOffset,
OUT PULONG64 ExtentLength
);
VOID
InitializeCopyBuffers (
VOID
);
VOID
MassageIdentifyData(
VOID
);
VOID
ProcessCompletedCopy (
PCOPYBUF CopyBuf
);
BOOL
ScanCopyBuffers (
VOID
);
VOID
StartRead (
IN OUT PCOPYBUF CopyBuf
);
VOID
StartWrite (
IN OUT PCOPYBUF CopyBuf
);
VOID
WriteMBRCode(
IN PUCHAR FilePath
);
//
// Global data declarations follow
//
COPYBUF CopyBufArray[COPYBUF_COUNT];
//
// Identifies the PhysicalDrive.
//
INT gDeviceNumber;
//
// Identifies the position of the drive on the controller
// ie. master == 0, slave == 1.
//
UCHAR gDriveNumber = 1;
HANDLE DriveHandle;
HANDLE FileHandle;
//
// CopyOffset is the byte offset between data on the source disk image and
// corresponding data in the output file. This is used to account for the
// sector-sized prefix in the output file.
//
ULONG CopyOffset;
ULONG64 CurrentOffset;
ULONG64 DriveSize;
ULONG64 MaxSize;
UCHAR PercentComplete;
ULONG OutstandingIo;
IDENTIFY_DATA IdentifyData;
DISK_GEOMETRY DiskGeometry;
//
// Array of event handles, one per copy buffer
//
HANDLE IoEvents[COPYBUF_COUNT];
int
_cdecl main (
int argc,
char *argv[]
)
{
char deviceBuffer[20];
PCHAR outputFileName;
PCHAR drive;
PCHAR options;
BOOLEAN result;
BOOLEAN writeBootSect;
ULONG64 volumeOffset;
ULONG64 volumeSize;
ULONG partitionNumber;
DWORD waitResult;
PCOPYBUF copyBuf;
ULONG i;
ULONG openAttributes;
writeBootSect = FALSE;
//
// Must be invoked with two arguments
//
if (argc != 3 && argc != 4) {
fprintf(stderr,
"Usage: %s <drive:> <OutputFile> [/b]\n",
argv[0]);
exit(1);
}
InitializeCopyBuffers();
//
// Extract arguments
//
drive = argv[1];
outputFileName = argv[2];
result = GetVolumeInfo(drive,
&gDeviceNumber,
&partitionNumber,
&volumeOffset,
&volumeSize);
if (result == FALSE) {
exit(1);
}
if (argc == 4) {
options = argv[3];
if (_stricmp(options, "/b") != 0) {
fprintf(stderr, "Invalid option %s specified\n",options);
exit(1);
}
writeBootSect = TRUE;
}
//
// Calculate how many sectors need to be in the image
//
MaxSize = (volumeOffset + volumeSize + SECTOR_MASK) / SECTOR_SIZE;
sprintf(deviceBuffer,"\\\\.\\PhysicalDrive%d",
gDeviceNumber);
openAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING;
if (writeBootSect == FALSE) {
openAttributes |= FILE_FLAG_OVERLAPPED;
}
//
// Open the physical source drive.
//
DriveHandle = CreateFile(deviceBuffer,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
openAttributes,
NULL);
if (INVALID_HANDLE_VALUE == DriveHandle){
printf("Couldn't open: %s. Drive may not exist. ",
deviceBuffer);
return -1;
}
//
// Retrieve and display the BIOS disk geometry
//
result = DisplayDiskGeometry( DriveHandle );
if (result == FALSE) {
printf("Could not retrieve disk geometry\n");
exit(1);
}
//
// Reteive the identify data, if possible. If the data could not be
// retrieved, MassageIdentifyData() will attempt to fabricate the relevant
// portions based on the BIOS disk geometry retrieved previously.
//
GetIdentifyData( DriveHandle,
&IdentifyData );
MassageIdentifyData();
DriveSize = IdentifyData.UserAddressableSectors * (ULONGLONG)512;
if (MaxSize == 0) {
MaxSize = DriveSize;
} else {
MaxSize *= 512;
}
printf("Drive size %dMB\n",(ULONG)(DriveSize / (1024 * 1024)));
printf("Image size %dMB\n",(ULONG)(MaxSize / (1024 * 1024)));
//
// Open the output file
//
FileHandle = CreateFile(outputFileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
openAttributes,
NULL);
if (FileHandle == INVALID_HANDLE_VALUE) {
printf("Could not create %s\n", outputFileName);
return -1;
}
if (writeBootSect != FALSE) {
//
// We're just creating bootsect.dat.
//
LARGE_INTEGER offset;
offset.QuadPart = volumeOffset;
GetBootSectors(offset);
goto closeHandles;
}
//
// Write the identify data
//
CopyOffset = 0;
CurrentOffset = 0;
DoWrite(&IdentifyData,
sizeof(IDENTIFY_DATA),
0,
&CopyBufArray[0]);
//
// Kick off reads on all of the remaining copy buffers
//
CopyOffset = sizeof(IDENTIFY_DATA);
for (i = 1; i < COPYBUF_COUNT; i++) {
StartRead(&CopyBufArray[i]);
}
//
// Loop, processing completed I/O as appropriate. When all
// outstanding io has completed, the copy is complete.
//
do {
waitResult = WaitForMultipleObjects( COPYBUF_COUNT,
IoEvents,
FALSE,
INFINITE );
waitResult -= WAIT_OBJECT_0;
assert(waitResult < COPYBUF_COUNT);
copyBuf = &CopyBufArray[waitResult];
ProcessCompletedCopy(copyBuf);
} while (OutstandingIo > 0);
closeHandles:
//
// The copy is finished.
//
printf("%s created\n", outputFileName);
CloseHandle(DriveHandle);
CloseHandle(FileHandle);
#if 0
if (writeBootSect == FALSE) {
//
// Write the MBR code into the output image
//
WriteMBRCode(outputFileName);
}
#endif
return 0;
}
VOID
InitializeCopyBuffers (
VOID
)
{
ULONG bytes;
PCOPYBUF copyBuf;
PCOPYBUF copyBufEnd;
ULONG i;
HANDLE event;
PCHAR copyBuffer;
//
// Make a single, sector-aligned allocation to contain all of the copy
// buffers
//
bytes = COPYBUF_SIZE * COPYBUF_COUNT + SECTOR_MASK;
copyBuffer = malloc(bytes);
if (copyBuffer == NULL) {
printf("Out of memory\n");
exit(-1);
}
copyBuffer =
(PCHAR)(((ULONG_PTR)copyBuffer + SECTOR_MASK) & ~SECTOR_MASK);
//
// Walk the copyBuf array, initializing each to point to it's portion of
// the copy buffer
//
copyBuf = CopyBufArray;
for (i = 0; i < COPYBUF_COUNT; i++) {
copyBuf->State = CB_FREE;
copyBuf->Buffer = copyBuffer;
event = CreateEvent( NULL,
FALSE,
FALSE,
NULL );
assert(event != NULL);
copyBuf->Overlapped.hEvent = event;
IoEvents[i] = event;
copyBuffer += COPYBUF_SIZE;
copyBuf++;
}
}
BOOLEAN
GetVolumeInfo (
IN PCHAR DrivePath,
OUT PULONG DriveNumber,
OUT PULONG PartitionNumber,
OUT PULONG64 StartingOffset,
OUT PULONG64 ExtentLength
)
{
char deviceBuffer[20];
HANDLE volumeHandle;
BOOL result;
STORAGE_DEVICE_NUMBER deviceNumber;
PARTITION_INFORMATION partitionInformation;
ULONG bytesReturned;
//
// Determine which physical drive contains the specified partition by
//
// - Opening the volume
//
// - Sending IOCTL_STORAGE_GET_DEVICE_NUMBER to retrieve the device and
// partition number
//
// - Sending IOCTL_DISK_GET_PARTITION_INFO to retrieve the starting
// offset and length of the volume
//
// - Closing the volume
//
sprintf(deviceBuffer, "\\\\.\\%s", DrivePath);
volumeHandle = CreateFile(deviceBuffer,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_NO_BUFFERING |
FILE_FLAG_OVERLAPPED,
NULL);
if (volumeHandle == INVALID_HANDLE_VALUE) {
printf("Error %d opening %s\n", GetLastError(), deviceBuffer);
return FALSE;
}
result = DeviceIoControl(volumeHandle,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL,
0,
&deviceNumber,
sizeof(deviceNumber),
&bytesReturned,
NULL);
if (result == FALSE) {
printf("Could not get device number for %s\n", deviceBuffer);
CloseHandle(volumeHandle);
return FALSE;
}
if (deviceNumber.DeviceType != FILE_DEVICE_DISK) {
printf("%s is not a disk\n",deviceBuffer);
CloseHandle(volumeHandle);
return FALSE;
}
bytesReturned = 0;
result = DeviceIoControl(volumeHandle,
IOCTL_DISK_GET_PARTITION_INFO,
NULL,
0,
&partitionInformation,
sizeof(partitionInformation),
&bytesReturned,
NULL);
CloseHandle(volumeHandle);
if (result == FALSE) {
printf("Error %d retrieving partition information for %s\n",
GetLastError(),
deviceBuffer);
return FALSE;
}
//
// All of the information was successfully retrieved. Fill in the
// output parameters and return.
//
*DriveNumber = deviceNumber.DeviceNumber;
*PartitionNumber = deviceNumber.PartitionNumber;
*StartingOffset = partitionInformation.StartingOffset.QuadPart;
*ExtentLength = partitionInformation.PartitionLength.QuadPart;
return TRUE;
}
BOOLEAN
GetIdentifyData(
IN HANDLE Handle,
OUT PIDENTIFY_DATA IdentifyData
)
{
SENDCMDINPARAMS inputParams;
PSENDCMDOUTPARAMS outputParams;
PIDENTIFY_DATA identifyData;
ULONG bytesReturned;
ULONG bufSize;
ZeroMemory(&inputParams, sizeof(SENDCMDINPARAMS));
bufSize = sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE;
bufSize *= 2;
outputParams = (PSENDCMDOUTPARAMS) malloc(bufSize);
if (!outputParams) {
printf("Out of memory\n");
return FALSE;
}
ZeroMemory(outputParams, bufSize);
//
// Build register structure to send SMART command.
//
inputParams.irDriveRegs.bFeaturesReg = 0;
inputParams.irDriveRegs.bSectorCountReg = 1;
inputParams.irDriveRegs.bSectorNumberReg = 1;
inputParams.irDriveRegs.bCylLowReg = 0;
inputParams.irDriveRegs.bCylHighReg = 0;
inputParams.irDriveRegs.bDriveHeadReg = 0xA0 | ((gDriveNumber & 1) << 4);
inputParams.irDriveRegs.bCommandReg = ID_CMD;
bytesReturned = 0;
if (!DeviceIoControl (Handle,
SMART_RCV_DRIVE_DATA,
&inputParams,
sizeof(SENDCMDINPARAMS) - 1,
outputParams,
bufSize,
&bytesReturned,
NULL)) {
printf("IDE_IDENTIFY failed with 0x%x, %d bytes returned\n",
GetLastError(),
bytesReturned);
printf("WARNING: This image file will work with the SIMICS simulator\n"
" but not simnow.\n");
memset(IdentifyData, 0, sizeof(IDENTIFY_DATA));
free(outputParams);
return FALSE;
}
identifyData = (PIDENTIFY_DATA)outputParams->bBuffer;
*IdentifyData = *identifyData;
free(outputParams);
return TRUE;
}
VOID
MassageIdentifyData(
VOID
)
/*++
Routine Description:
This routine sets the bios CHS geometry in the IdentifyData structure
in a place previously agreed upon with Simics.
Arguments:
None.
Return Value:
None.
--*/
{
PCUSTOM_IDENTIFY_DATA custom;
ULONG sectorCount;
USHORT ideCylinders;
USHORT ideHeads;
USHORT ideSectorsPerTrack;
C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,NumCylinders)/2 == 1);
C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,NumHeads)/2 == 3);
C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,NumSectorsPerTrack)/2 == 6);
C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,CurrentSectorCapacity)/2 == 57);
//
// Set the BIOS disk geometry in the new fields that are passed
// along to the SIMICS simulator.
//
custom = (PCUSTOM_IDENTIFY_DATA)&IdentifyData;
custom->BiosData.Cylinders = DiskGeometry.Cylinders.LowPart;
custom->BiosData.Heads = (USHORT)DiskGeometry.TracksPerCylinder;
custom->BiosData.SectorsPerTrack = (USHORT)DiskGeometry.SectorsPerTrack;
if (IdentifyData.NumCylinders == 0) {
//
// The IDENTIFY_DATA ioctl failed (SMART isn't supported), so parts
// of the IDE geometry must be fabricated, including:
//
// - NumCylinders
// - NumHeads
// - NumSectorsPerTrack
// - CurrentSectorCapacity
// - UserAddressableSectors
//
sectorCount = DiskGeometry.Cylinders.LowPart *
DiskGeometry.TracksPerCylinder *
DiskGeometry.SectorsPerTrack;
if (sectorCount > MAX_NUM_CHS_ADDRESSABLE_SECTORS) {
IdentifyData.NumCylinders = 16383;
IdentifyData.NumHeads = 16;
IdentifyData.NumSectorsPerTrack = 63;
} else {
IdentifyData.NumSectorsPerTrack =
(USHORT)DiskGeometry.SectorsPerTrack;
IdentifyData.NumHeads = 16;
IdentifyData.NumCylinders = (USHORT)
(sectorCount / (IdentifyData.NumSectorsPerTrack *
IdentifyData.NumHeads));
}
IdentifyData.CurrentSectorCapacity = sectorCount;
IdentifyData.UserAddressableSectors = sectorCount;
}
printf("IDE disk geometry:\n"
" Cyls %d\n"
" Heads %d\n"
" Sct/Trk %d\n\n"
"BIOS disk geometry:\n"
" Cyls %d\n"
" Heads %d\n"
" Sct/Trk %d\n",
IdentifyData.NumCylinders,
IdentifyData.NumHeads,
IdentifyData.NumSectorsPerTrack,
custom->BiosData.Cylinders,
custom->BiosData.Heads,
custom->BiosData.SectorsPerTrack);
}
VOID
DoWrite (
IN PVOID Buffer,
IN ULONG Length,
IN ULONG64 Offset,
IN PCOPYBUF CopyBuf
)
{
LARGE_INTEGER offset;
BOOL result;
offset.QuadPart = Offset;
CopyBuf->Overlapped.Offset = offset.HighPart;
CopyBuf->Overlapped.OffsetHigh = offset.LowPart;
CopyBuf->State = CB_READ;
memcpy(CopyBuf->Buffer,Buffer,Length);
CopyBuf->Bytes = Length;
StartWrite(CopyBuf);
}
VOID
StartWrite (
IN OUT PCOPYBUF CopyBuf
)
{
LARGE_INTEGER offset;
BOOL result;
ULONG error;
CopyBuf->State = CB_WRITE;
//
// Adjust the offset
//
offset.LowPart = CopyBuf->Overlapped.Offset;
offset.HighPart = CopyBuf->Overlapped.OffsetHigh;
offset.QuadPart += CopyOffset;
CopyBuf->Overlapped.Offset = offset.LowPart;
CopyBuf->Overlapped.OffsetHigh = offset.HighPart;
result = WriteFile( FileHandle,
CopyBuf->Buffer,
CopyBuf->Bytes,
NULL,
&CopyBuf->Overlapped );
if (result == FALSE) {
error = GetLastError();
if (error != ERROR_IO_PENDING &&
error != ERROR_IO_INCOMPLETE) {
printf("Error %d returned from write\n",error);
exit(-1);
}
}
OutstandingIo += 1;
}
VOID
StartRead (
IN OUT PCOPYBUF CopyBuf
)
{
LARGE_INTEGER offset;
BOOL result;
ULONG64 length;
ULONG error;
if (CurrentOffset == MaxSize) {
return;
}
length = MaxSize - CurrentOffset;
if (length > COPYBUF_SIZE) {
length = COPYBUF_SIZE;
}
CopyBuf->State = CB_READ;
offset.QuadPart = CurrentOffset;
CurrentOffset += length;
CopyBuf->Overlapped.Offset = offset.LowPart;
CopyBuf->Overlapped.OffsetHigh = offset.HighPart;
result = ReadFile( DriveHandle,
CopyBuf->Buffer,
(ULONG)length,
NULL,
&CopyBuf->Overlapped );
if (result == FALSE) {
error = GetLastError();
if (error != ERROR_IO_PENDING &&
error != ERROR_IO_INCOMPLETE) {
printf("Error %d returned from read\n",error);
exit(-1);
}
}
OutstandingIo += 1;
}
BOOLEAN
DisplayDiskGeometry(
IN HANDLE handle
)
{
BOOL result;
ULONG bytesReturned;
result = DeviceIoControl(handle,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
&DiskGeometry,
sizeof(DiskGeometry),
&bytesReturned,
NULL);
if (result == FALSE) {
return FALSE;
}
printf("%I64d Cylinders %d Heads %d Sectors/Track\n",
DiskGeometry.Cylinders.QuadPart,
DiskGeometry.TracksPerCylinder,
DiskGeometry.SectorsPerTrack);
return TRUE;
}
VOID
ProcessCompletedCopy (
PCOPYBUF CopyBuf
)
{
UCHAR percent;
HANDLE handle;
BOOL result;
//
// Decrement the outstanding Io count. Successfully starting another
// read or write will increment it again.
//
OutstandingIo -= 1;
//
// We have found a buffer with either a read or a write in progress.
// Retrieve the number of bytes transferred.
//
if (CopyBuf->State == CB_READ) {
handle = DriveHandle;
} else {
handle = FileHandle;
}
result = GetOverlappedResult( handle,
&CopyBuf->Overlapped,
&CopyBuf->Bytes,
FALSE );
assert(result != FALSE);
if (CopyBuf->State == CB_READ) {
//
// This buffer contains data read from the drive, kick off a write
// to the output file.
//
StartWrite(CopyBuf);
} else {
//
// This buffer represents data that has been written to the drive.
// Use it to start another read.
//
percent = (UCHAR)((CurrentOffset * 100) / MaxSize);
if (percent != PercentComplete) {
printf("%d%%\r",percent);
PercentComplete = percent;
}
StartRead(CopyBuf);
}
}
VOID
GetBootSectors (
IN LARGE_INTEGER VolumeOffset
)
/*++
Routine Description:
Reads the first BOOT_SECT sectors from the cmd-line-supplied volume,
searches for NTLDR and replaces with $LDR$, and writes the sectors
to the output file.
Arguments:
VolumeOffset - Physical disk offset of the start of the boot sectors
Return Value:
None. Program is aborted on failure.
--*/
{
UCHAR buffer[ BOOTSECT_SIZE + SECTOR_SIZE - 1 ];
OVERLAPPED overlapped;
PUCHAR sectorData;
ULONG bytesTransferred;
PCHAR search;
BOOL result;
//
// Read the boot sectors into sectorData
//
sectorData = ROUND_UP_POINTER(buffer,SECTOR_SIZE);
RtlZeroMemory(&overlapped,sizeof(overlapped));
overlapped.Offset = VolumeOffset.LowPart;
overlapped.OffsetHigh = VolumeOffset.HighPart;
result = ReadFile(DriveHandle,
sectorData,
BOOTSECT_SIZE,
&bytesTransferred,
&overlapped);
if (result == FALSE || bytesTransferred != BOOTSECT_SIZE) {
fprintf(stderr,
"Error %d reading boot sectors\n",
GetLastError());
exit(1);
}
//
// Find "NTLDR" and replace it with "$LDR$". The magic numbers here
// came directly from code in setup... start the search at offset 1024
// and work backwards, giving up if not found by offset 62.
//
search = sectorData + 1024 - NTLDR_LEN;
while (TRUE) {
if (memcmp(search,NTLDR_FIND,NTLDR_LEN) == 0) {
//
// Found it. Copy $LDR$ in instead.
//
memcpy(search,NTLDR_REPLACE,NTLDR_LEN);
break;
}
search -= sizeof(WCHAR);
if (search == (sectorData + 62)) {
//
// Couldn't find the string, give up
//
fprintf(stderr,"Couldn't find NTLDR string\n");
exit(1);
}
}
RtlZeroMemory(&overlapped,sizeof(overlapped));
overlapped.Offset = 0;
result = WriteFile(FileHandle,
sectorData,
BOOTSECT_SECTORS * SECTOR_SIZE,
&bytesTransferred,
&overlapped);
if (result == FALSE) {
fprintf(stderr,
"Error %d reading boot sectors\n",
GetLastError());
exit(1);
}
}
VOID
WriteMBRCode(
IN PUCHAR FilePath
)
{
HANDLE handle;
BOOL result;
DWORD bytesWritten;
handle = CreateFile(FilePath,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
fprintf(stderr,"Error %d opening %s\n", GetLastError(),FilePath);
exit(1);
}
result = WriteFile(handle,
x86BootCode,
440,
&bytesWritten,
NULL);
if (result == FALSE) {
fprintf(stderr,"Error %d writing MBR code\n", GetLastError());
exit(1);
}
CloseHandle(handle);
}