3 Copyright 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 Reading/writing MBR/DBR.
19 If we write MBR to disk, we just update the MBR code and the partition table wouldn't be over written.
20 If we process DBR, we will patch MBR to set first partition active if no active partition exists.
29 #define PARTITION_TABLE_OFFSET 0x1BE
31 #define SIZE_OF_PARTITION_ENTRY 0x10
33 #define PARTITION_ENTRY_STARTLBA_OFFSET 8
35 #define PARTITION_ENTRY_NUM 4
57 CHAR
*ErrorStatusDesc
[] = {
59 "Failed to create files",
60 "Failed to read/write files",
62 "Failed to detect Fat type"
65 typedef struct _DRIVE_TYPE_DESC
{
70 #define DRIVE_TYPE_ITEM(x) {x, #x}
71 DRIVE_TYPE_DESC DriveTypeDesc
[] = {
72 DRIVE_TYPE_ITEM (DRIVE_UNKNOWN
),
73 DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR
),
74 DRIVE_TYPE_ITEM (DRIVE_REMOVABLE
),
75 DRIVE_TYPE_ITEM (DRIVE_FIXED
),
76 DRIVE_TYPE_ITEM (DRIVE_REMOTE
),
77 DRIVE_TYPE_ITEM (DRIVE_CDROM
),
78 DRIVE_TYPE_ITEM (DRIVE_RAMDISK
),
82 typedef struct _DRIVE_INFO
{
84 DRIVE_TYPE_DESC
*DriveType
;
88 #define BOOT_SECTOR_LBA_OFFSET 0x1FA
90 #define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
99 Get drive information including disk number and drive type,
100 where disknumber is useful for reading/writing disk raw data.
101 NOTE: Floppy disk doesn't have disk number but it doesn't matter because
102 we can reading/writing floppy disk without disk number.
105 VolumeLetter : volume letter, e.g.: C for C:, A for A:
106 DriveInfo : pointer to DRIVE_INFO structure receiving drive information.
114 STORAGE_DEVICE_NUMBER StorageDeviceNumber
;
120 CHAR RootPath
[] = "X:\\"; // "X:\" -> for GetDriveType
121 CHAR VolumeAccessPath
[] = "\\\\.\\X:"; // "\\.\X:" -> to open the volume
123 RootPath
[0] = VolumeAccessPath
[4] = VolumeLetter
;
124 DriveType
= GetDriveType(RootPath
);
125 if (DriveType
!= DRIVE_REMOVABLE
&& DriveType
!= DRIVE_FIXED
) {
129 DriveInfo
->VolumeLetter
= VolumeLetter
;
130 VolumeHandle
= CreateFile (
133 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
139 if (VolumeHandle
== INVALID_HANDLE_VALUE
) {
142 "ERROR: CreateFile failed: Volume = %s, LastError = 0x%x\n",
150 // Get Disk Number. It should fail when operating on floppy. That's ok
151 // because Disk Number is only needed when operating on Hard or USB disk.
153 // To direct write to disk:
154 // for USB and HD: use path = \\.\PHYSICALDRIVEx, where x is Disk Number
155 // for floppy: use path = \\.\X:, where X can be A or B
157 Success
= DeviceIoControl(
159 IOCTL_STORAGE_GET_DEVICE_NUMBER
,
162 &StorageDeviceNumber
,
163 sizeof(StorageDeviceNumber
),
168 // DeviceIoControl should fail if Volume is floppy or network drive.
171 DriveInfo
->DiskNumber
= (UINT
) -1;
172 } else if (StorageDeviceNumber
.DeviceType
!= FILE_DEVICE_DISK
) {
174 // Only care about the disk.
178 DriveInfo
->DiskNumber
= StorageDeviceNumber
.DeviceNumber
;
180 CloseHandle(VolumeHandle
);
183 // Fill in the type string
185 DriveInfo
->DriveType
= NULL
;
186 for (Index
= 0; DriveTypeDesc
[Index
].Description
!= NULL
; Index
++) {
187 if (DriveType
== DriveTypeDesc
[Index
].Type
) {
188 DriveInfo
->DriveType
= &DriveTypeDesc
[Index
];
193 if (DriveInfo
->DriveType
== NULL
) {
195 // Should have a type.
197 fprintf (stderr
, "ERROR: fetal error!!!\n");
209 List every drive in current system and their information.
214 DRIVE_INFO DriveInfo
;
216 UINT Mask
= GetLogicalDrives();
218 for (Index
= 0; Index
< MAX_DRIVE
; Index
++) {
219 if (((Mask
>> Index
) & 0x1) == 1) {
220 if (GetDriveInfo ('A' + (CHAR
) Index
, &DriveInfo
)) {
222 // Floppy will occupy 'A' and 'B'
226 DriveInfo
.VolumeLetter
,
227 DriveInfo
.DriveType
->Description
233 "%c: - DiskNum: %d, Type: %s\n",
234 DriveInfo
.VolumeLetter
,
235 DriveInfo
.DiskNumber
,
236 DriveInfo
.DriveType
->Description
246 GetBootSectorOffset (
253 Get the offset of boot sector.
254 For non-MBR disk, offset is just 0
255 for disk with MBR, offset needs to be caculated by parsing MBR
257 NOTE: if no one is active, we will patch MBR to select first partition as active.
260 DiskHandle : HANDLE of disk
261 WriteToDisk : TRUE indicates writing
262 PatchType : PatchTypeFloppy, PatchTypeIde, PatchTypeUsb
266 o.w. : Offset to boot sector
269 BYTE DiskPartition
[0x200];
278 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
279 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
284 // Check Signature, Jmp, and Boot Indicator.
285 // if all pass, we assume MBR found.
288 // Check Signature: 55AA
289 if ((DiskPartition
[0x1FE] == 0x55) && (DiskPartition
[0x1FF] == 0xAA)) {
290 // Check Jmp: (EB ?? 90) or (E9 ?? ??)
291 if (((DiskPartition
[0] != 0xEB) || (DiskPartition
[2] != 0x90)) &&
292 (DiskPartition
[0] != 0xE9)) {
293 // Check Boot Indicator: 0x00 or 0x80
294 // Boot Indicator is the first byte of Partition Entry
296 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; ++Index
) {
297 if ((DiskPartition
[PARTITION_TABLE_OFFSET
+ Index
* SIZE_OF_PARTITION_ENTRY
] & 0x7F) != 0) {
309 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; Index
++) {
311 // Found Boot Indicator.
313 if (DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
)] == 0x80) {
314 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
) + PARTITION_ENTRY_STARTLBA_OFFSET
];
319 // If no boot indicator, we manually select 1st partition, and patch MBR.
321 if (Index
== PARTITION_ENTRY_NUM
) {
322 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ PARTITION_ENTRY_STARTLBA_OFFSET
];
323 if (WriteToDisk
&& (PatchType
== PatchTypeUsb
)) {
324 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
325 DiskPartition
[PARTITION_TABLE_OFFSET
] = 0x80;
326 WriteFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
);
339 PATCH_TYPE PatchType
,
344 Writing or reading boot sector or MBR according to the argument.
347 DiskName : Win32 API recognized string name of disk
349 WriteToDisk : TRUE is to write content of file to disk, otherwise, reading content of disk to file
350 PatchType : PatchTypeFloppy, PatchTypeIde, PatchTypeUsb
351 ProcessMbr : TRUE is to process MBR, otherwise, processing boot sector
361 BYTE DiskPartition
[0x200];
362 BYTE DiskPartitionBackup
[0x200];
369 DiskHandle
= CreateFile (
371 GENERIC_READ
| GENERIC_WRITE
,
375 FILE_ATTRIBUTE_NORMAL
,
378 if (DiskHandle
== INVALID_HANDLE_VALUE
) {
379 return ErrorFileCreate
;
382 FileHandle
= CreateFile (
384 GENERIC_READ
| GENERIC_WRITE
,
388 FILE_ATTRIBUTE_NORMAL
,
391 if (FileHandle
== INVALID_HANDLE_VALUE
) {
392 return ErrorFileCreate
;
397 // Skip potential MBR for Ide & USB disk
399 if ((PatchType
== PatchTypeIde
) || (PatchType
== PatchTypeUsb
)) {
401 // Even user just wants to process MBR, we get offset of boot sector here to validate the disk
402 // if disk have MBR, DbrOffset should be greater than 0
404 DbrOffset
= GetBootSectorOffset (DiskHandle
, WriteToDisk
, PatchType
);
408 // 1. Process boot sector, set file pointer to the beginning of boot sector
410 SetFilePointer (DiskHandle
, DbrOffset
* 0x200, NULL
, FILE_BEGIN
);
411 } else if(DbrOffset
== 0) {
413 // If user want to process Mbr, but no Mbr exists, simply return FALSE
418 // 2. Process MBR, set file pointer to 0
420 SetFilePointer (DiskHandle
, 0, NULL
, FILE_BEGIN
);
425 // [File Pointer is pointed to beginning of Mbr or Dbr]
431 if (!ReadFile (FileHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
432 return ErrorFileReadWrite
;
436 // Use original partition table
438 if (!ReadFile (DiskHandle
, DiskPartitionBackup
, 0x200, &BytesReturn
, NULL
)) {
439 return ErrorFileReadWrite
;
441 memcpy (DiskPartition
+ 0x1BE, DiskPartitionBackup
+ 0x1BE, 0x40);
442 SetFilePointer (DiskHandle
, 0, NULL
, FILE_BEGIN
);
445 if (!WriteFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
446 return ErrorFileReadWrite
;
453 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
454 return ErrorFileReadWrite
;
457 if (PatchType
== PatchTypeUsb
) {
458 // Manually set BS_DrvNum to 0x80 as window's format.exe has a bug which will clear this field discarding USB disk's MBR.
459 // offset of BS_DrvNum is 0x24 for FAT12/16
462 DrvNumOffset
= GetDrvNumOffset (DiskPartition
);
463 if (DrvNumOffset
== -1) {
467 // Some legacy BIOS require 0x80 discarding MBR.
468 // Question left here: is it needed to check Mbr before set 0x80?
470 DiskPartition
[DrvNumOffset
] = ((DbrOffset
> 0) ? 0x80 : 0);
474 if (PatchType
== PatchTypeIde
) {
476 // Patch LBAOffsetForBootSector
478 *(DWORD
*)&DiskPartition
[BOOT_SECTOR_LBA_OFFSET
] = DbrOffset
;
480 if (!WriteFile (FileHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
481 return ErrorFileReadWrite
;
484 CloseHandle (FileHandle
);
485 CloseHandle (DiskHandle
);
496 "Usage: %s [OPTIONS]...\n"
497 "Copy file content from/to bootsector.\n"
500 " -if=FILE specified an input, can be files or disks\n"
501 " -of=FILE specified an output, can be files or disks\n"
502 " -mbr process MBR also\n"
503 " -h print this message\n"
505 "FILE providing a volume plus a colon (X:), indicates a disk\n"
506 "FILE providing other format, indicates a file\n",
523 DRIVE_INFO DriveInfo
;
524 PATCH_TYPE PatchType
;
527 CHAR FloppyPathTemplate
[] = "\\\\.\\%c:";
528 CHAR DiskPathTemplate
[] = "\\\\.\\PHYSICALDRIVE%u";
529 CHAR DiskPath
[MAX_PATH
];
541 // Parse command line
543 for (Index
= 0; Index
< argc
; Index
++) {
544 if (_stricmp (argv
[Index
], "-l") == 0) {
548 else if (_stricmp (argv
[Index
], "-mbr") == 0) {
551 else if ((_strnicmp (argv
[Index
], "-if=", 4) == 0) ||
552 (_strnicmp (argv
[Index
], "-of=", 4) == 0)
554 if (argv
[Index
][6] == '\0' && argv
[Index
][5] == ':' && IsLetter (argv
[Index
][4])) {
555 VolumeLetter
= argv
[Index
][4];
556 if (_strnicmp (argv
[Index
], "-if=", 4) == 0) {
561 FilePath
= &argv
[Index
][4];
565 PrintUsage (AppName
);
573 if (VolumeLetter
== 0) {
574 fprintf (stderr
, "ERROR: Volume isn't provided!\n");
575 PrintUsage (AppName
);
579 if (FilePath
== NULL
) {
580 fprintf (stderr
, "ERROR: File isn't pvovided!\n");
581 PrintUsage (AppName
);
585 PatchType
= PatchTypeUnknown
;
587 if ((VolumeLetter
== 'A') || (VolumeLetter
== 'a') ||
588 (VolumeLetter
== 'B') || (VolumeLetter
== 'b')
593 sprintf (DiskPath
, FloppyPathTemplate
, VolumeLetter
);
594 PatchType
= PatchTypeFloppy
;
600 if (!GetDriveInfo (VolumeLetter
, &DriveInfo
)) {
601 fprintf (stderr
, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());
606 // Shouldn't patch my own hard disk, but can read it.
609 if (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
&& WriteToDisk
) {
610 fprintf (stderr
, "ERROR: Write to local harddisk - permission denied!\n");
614 sprintf (DiskPath
, DiskPathTemplate
, DriveInfo
.DiskNumber
);
615 if (DriveInfo
.DriveType
->Type
== DRIVE_REMOVABLE
) {
616 PatchType
= PatchTypeUsb
;
618 else if (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
) {
619 PatchType
= PatchTypeIde
;
623 if (PatchType
== PatchTypeUnknown
) {
624 fprintf (stderr
, "ERROR: PatchType unknown!\n");
629 // Process DBR (Patch or Read)
631 Status
= ProcessBsOrMbr (DiskPath
, FilePath
, WriteToDisk
, PatchType
, ProcessMbr
);
632 if (Status
== ErrorSuccess
) {
635 "%s %s: successfully!\n",
636 WriteToDisk
? "Write" : "Read",
637 ProcessMbr
? "MBR" : "DBR"
643 "%s: %s %s: failed - %s (LastError: 0x%x)!\n",
644 (Status
== ErrorNoMbr
) ? "WARNING" : "ERROR",
645 WriteToDisk
? "Write" : "Read",
646 ProcessMbr
? "MBR" : "DBR",
647 ErrorStatusDesc
[Status
],