]> git.proxmox.com Git - mirror_edk2.git/blame - EdkCompatibilityPkg/Sample/Tools/Source/GenBootsector/genbootsector.c
Add in the 1st version of ECP.
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / GenBootsector / genbootsector.c
CommitLineData
3eb9473e 1/*++\r
2\r
3Copyright 2006 - 2007, Intel Corporation \r
4All rights reserved. This program and the accompanying materials \r
5are licensed and made available under the terms and conditions of the BSD License \r
6which accompanies this distribution. The full text of the license may be found at \r
7http://opensource.org/licenses/bsd-license.php \r
8 \r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
11\r
12Module Name:\r
13\r
14 genbootsector.c\r
15 \r
16Abstract:\r
17 Reading/writing MBR/DBR.\r
18 NOTE:\r
19 If we write MBR to disk, we just update the MBR code and the partition table wouldn't be over written.\r
20 If we process DBR, we will patch MBR to set first partition active if no active partition exists.\r
21\r
22--*/\r
23\r
24#include <windows.h>\r
25#include <stdio.h>\r
26#include <string.h>\r
27\r
28#define MAX_DRIVE 26\r
29#define PARTITION_TABLE_OFFSET 0x1BE\r
30\r
31#define SIZE_OF_PARTITION_ENTRY 0x10\r
32\r
33#define PARTITION_ENTRY_STARTLBA_OFFSET 8\r
34\r
35#define PARTITION_ENTRY_NUM 4\r
36\r
37INT\r
38GetDrvNumOffset (\r
39 IN VOID *BootSector\r
40 );\r
41\r
42typedef enum {\r
43 PatchTypeUnknown,\r
44 PatchTypeFloppy,\r
45 PatchTypeIde,\r
46 PatchTypeUsb,\r
47} PATCH_TYPE;\r
48\r
49typedef enum {\r
50 ErrorSuccess,\r
51 ErrorFileCreate,\r
52 ErrorFileReadWrite,\r
53 ErrorNoMbr,\r
54 ErrorFatType\r
55} ERROR_STATUS;\r
56\r
57CHAR *ErrorStatusDesc[] = {\r
58 "Success",\r
59 "Failed to create files",\r
60 "Failed to read/write files",\r
61 "No MBR exists",\r
62 "Failed to detect Fat type"\r
63};\r
64\r
65typedef struct _DRIVE_TYPE_DESC {\r
66 UINT Type;\r
67 CHAR *Description;\r
68} DRIVE_TYPE_DESC;\r
69\r
70#define DRIVE_TYPE_ITEM(x) {x, #x}\r
71DRIVE_TYPE_DESC DriveTypeDesc[] = {\r
72 DRIVE_TYPE_ITEM (DRIVE_UNKNOWN),\r
73 DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR),\r
74 DRIVE_TYPE_ITEM (DRIVE_REMOVABLE),\r
75 DRIVE_TYPE_ITEM (DRIVE_FIXED),\r
76 DRIVE_TYPE_ITEM (DRIVE_REMOTE),\r
77 DRIVE_TYPE_ITEM (DRIVE_CDROM),\r
78 DRIVE_TYPE_ITEM (DRIVE_RAMDISK),\r
79 (UINT) -1, NULL\r
80};\r
81\r
82typedef struct _DRIVE_INFO {\r
83 CHAR VolumeLetter;\r
84 DRIVE_TYPE_DESC *DriveType;\r
85 UINT DiskNumber;\r
86} DRIVE_INFO;\r
87\r
88#define BOOT_SECTOR_LBA_OFFSET 0x1FA\r
89\r
90#define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))\r
91\r
92BOOL\r
93GetDriveInfo (\r
94 CHAR VolumeLetter,\r
95 DRIVE_INFO *DriveInfo\r
96 )\r
97/*++\r
98Routine Description:\r
99 Get drive information including disk number and drive type,\r
100 where disknumber is useful for reading/writing disk raw data.\r
101 NOTE: Floppy disk doesn't have disk number but it doesn't matter because\r
102 we can reading/writing floppy disk without disk number.\r
103\r
104Arguments:\r
105 VolumeLetter : volume letter, e.g.: C for C:, A for A:\r
106 DriveInfo : pointer to DRIVE_INFO structure receiving drive information.\r
107\r
108Return:\r
109 TRUE : successful\r
110 FALSE : failed\r
111--*/\r
112{\r
113 HANDLE VolumeHandle;\r
114 STORAGE_DEVICE_NUMBER StorageDeviceNumber;\r
115 DWORD BytesReturned;\r
116 BOOL Success;\r
117 UINT DriveType;\r
118 UINT Index;\r
119\r
120 CHAR RootPath[] = "X:\\"; // "X:\" -> for GetDriveType\r
121 CHAR VolumeAccessPath[] = "\\\\.\\X:"; // "\\.\X:" -> to open the volume\r
122\r
123 RootPath[0] = VolumeAccessPath[4] = VolumeLetter;\r
124 DriveType = GetDriveType(RootPath);\r
125 if (DriveType != DRIVE_REMOVABLE && DriveType != DRIVE_FIXED) {\r
126 return FALSE;\r
127 }\r
128\r
129 DriveInfo->VolumeLetter = VolumeLetter;\r
130 VolumeHandle = CreateFile (\r
131 VolumeAccessPath,\r
132 0,\r
133 FILE_SHARE_READ | FILE_SHARE_WRITE,\r
134 NULL,\r
135 OPEN_EXISTING,\r
136 0,\r
137 NULL\r
138 );\r
139 if (VolumeHandle == INVALID_HANDLE_VALUE) {\r
140 fprintf (\r
141 stderr, \r
142 "ERROR: CreateFile failed: Volume = %s, LastError = 0x%x\n", \r
143 VolumeAccessPath, \r
144 GetLastError ()\r
145 );\r
146 return FALSE;\r
147 }\r
148\r
149 //\r
150 // Get Disk Number. It should fail when operating on floppy. That's ok \r
151 // because Disk Number is only needed when operating on Hard or USB disk.\r
152 //\r
153 // To direct write to disk:\r
154 // for USB and HD: use path = \\.\PHYSICALDRIVEx, where x is Disk Number\r
155 // for floppy: use path = \\.\X:, where X can be A or B\r
156 //\r
157 Success = DeviceIoControl(\r
158 VolumeHandle, \r
159 IOCTL_STORAGE_GET_DEVICE_NUMBER,\r
160 NULL, \r
161 0, \r
162 &StorageDeviceNumber, \r
163 sizeof(StorageDeviceNumber),\r
164 &BytesReturned, \r
165 NULL\r
166 );\r
167 //\r
168 // DeviceIoControl should fail if Volume is floppy or network drive.\r
169 //\r
170 if (!Success) {\r
171 DriveInfo->DiskNumber = (UINT) -1;\r
172 } else if (StorageDeviceNumber.DeviceType != FILE_DEVICE_DISK) {\r
173 //\r
174 // Only care about the disk.\r
175 //\r
176 return FALSE;\r
177 } else{\r
178 DriveInfo->DiskNumber = StorageDeviceNumber.DeviceNumber;\r
179 }\r
180 CloseHandle(VolumeHandle);\r
181 \r
182 //\r
183 // Fill in the type string\r
184 //\r
185 DriveInfo->DriveType = NULL;\r
186 for (Index = 0; DriveTypeDesc[Index].Description != NULL; Index ++) {\r
187 if (DriveType == DriveTypeDesc[Index].Type) {\r
188 DriveInfo->DriveType = &DriveTypeDesc[Index];\r
189 break;\r
190 }\r
191 }\r
192\r
193 if (DriveInfo->DriveType == NULL) {\r
194 //\r
195 // Should have a type.\r
196 //\r
197 fprintf (stderr, "ERROR: fetal error!!!\n");\r
198 return FALSE;\r
199 }\r
200 return TRUE;\r
201}\r
202\r
203VOID\r
204ListDrive (\r
205 VOID\r
206 )\r
207/*++\r
208Routine Description:\r
209 List every drive in current system and their information.\r
210\r
211--*/\r
212{\r
213 UINT Index;\r
214 DRIVE_INFO DriveInfo;\r
215 \r
216 UINT Mask = GetLogicalDrives();\r
217\r
218 for (Index = 0; Index < MAX_DRIVE; Index++) {\r
219 if (((Mask >> Index) & 0x1) == 1) {\r
220 if (GetDriveInfo ('A' + (CHAR) Index, &DriveInfo)) {\r
221 if (Index < 2) {\r
222 // Floppy will occupy 'A' and 'B'\r
223 fprintf (\r
224 stdout,\r
225 "%c: - Type: %s\n",\r
226 DriveInfo.VolumeLetter,\r
227 DriveInfo.DriveType->Description\r
228 );\r
229 }\r
230 else {\r
231 fprintf (\r
232 stdout,\r
233 "%c: - DiskNum: %d, Type: %s\n", \r
234 DriveInfo.VolumeLetter,\r
235 DriveInfo.DiskNumber, \r
236 DriveInfo.DriveType->Description\r
237 );\r
238 }\r
239 }\r
240 }\r
241 }\r
242\r
243}\r
244\r
245INT\r
246GetBootSectorOffset (\r
247 HANDLE DiskHandle,\r
248 BOOL WriteToDisk,\r
249 PATCH_TYPE PatchType\r
250 )\r
251/*++\r
252Description:\r
253 Get the offset of boot sector.\r
254 For non-MBR disk, offset is just 0\r
255 for disk with MBR, offset needs to be caculated by parsing MBR\r
256\r
257 NOTE: if no one is active, we will patch MBR to select first partition as active.\r
258\r
259Arguments:\r
260 DiskHandle : HANDLE of disk\r
261 WriteToDisk : TRUE indicates writing\r
262 PatchType : PatchTypeFloppy, PatchTypeIde, PatchTypeUsb\r
263\r
264Return:\r
265 -1 : failed\r
266 o.w. : Offset to boot sector\r
267--*/\r
268{\r
269 BYTE DiskPartition[0x200];\r
270 DWORD BytesReturn;\r
271 DWORD DbrOffset;\r
272 DWORD Index;\r
273 BOOL HasMbr;\r
274\r
275 DbrOffset = 0;\r
276 HasMbr = FALSE;\r
277 \r
278 SetFilePointer(DiskHandle, 0, NULL, FILE_BEGIN);\r
279 if (!ReadFile (DiskHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {\r
280 return -1;\r
281 }\r
282\r
283 //\r
284 // Check Signature, Jmp, and Boot Indicator.\r
285 // if all pass, we assume MBR found.\r
286 //\r
287\r
288 // Check Signature: 55AA\r
289 if ((DiskPartition[0x1FE] == 0x55) && (DiskPartition[0x1FF] == 0xAA)) {\r
290 // Check Jmp: (EB ?? 90) or (E9 ?? ??)\r
291 if (((DiskPartition[0] != 0xEB) || (DiskPartition[2] != 0x90)) &&\r
292 (DiskPartition[0] != 0xE9)) {\r
293 // Check Boot Indicator: 0x00 or 0x80\r
294 // Boot Indicator is the first byte of Partition Entry\r
295 HasMbr = TRUE;\r
296 for (Index = 0; Index < PARTITION_ENTRY_NUM; ++Index) {\r
297 if ((DiskPartition[PARTITION_TABLE_OFFSET + Index * SIZE_OF_PARTITION_ENTRY] & 0x7F) != 0) {\r
298 HasMbr = FALSE;\r
299 break;\r
300 }\r
301 }\r
302 }\r
303 }\r
304\r
305 if (HasMbr) {\r
306 //\r
307 // Skip MBR\r
308 //\r
309 for (Index = 0; Index < PARTITION_ENTRY_NUM; Index++) {\r
310 //\r
311 // Found Boot Indicator.\r
312 //\r
313 if (DiskPartition[PARTITION_TABLE_OFFSET + (Index * SIZE_OF_PARTITION_ENTRY)] == 0x80) {\r
314 DbrOffset = *(DWORD *)&DiskPartition[PARTITION_TABLE_OFFSET + (Index * SIZE_OF_PARTITION_ENTRY) + PARTITION_ENTRY_STARTLBA_OFFSET];\r
315 break;\r
316 }\r
317 }\r
318 //\r
319 // If no boot indicator, we manually select 1st partition, and patch MBR.\r
320 //\r
321 if (Index == PARTITION_ENTRY_NUM) {\r
322 DbrOffset = *(DWORD *)&DiskPartition[PARTITION_TABLE_OFFSET + PARTITION_ENTRY_STARTLBA_OFFSET];\r
323 if (WriteToDisk && (PatchType == PatchTypeUsb)) {\r
324 SetFilePointer(DiskHandle, 0, NULL, FILE_BEGIN);\r
325 DiskPartition[PARTITION_TABLE_OFFSET] = 0x80;\r
326 WriteFile (DiskHandle, DiskPartition, 0x200, &BytesReturn, NULL);\r
327 }\r
328 }\r
329 }\r
330\r
331 return DbrOffset;\r
332}\r
333\r
334ERROR_STATUS\r
335ProcessBsOrMbr (\r
336 CHAR *DiskName,\r
337 CHAR *FileName,\r
338 BOOL WriteToDisk,\r
339 PATCH_TYPE PatchType,\r
340 BOOL ProcessMbr\r
341 )\r
342/*++\r
343Routine Description:\r
344 Writing or reading boot sector or MBR according to the argument.\r
345\r
346Arguments:\r
347 DiskName : Win32 API recognized string name of disk\r
348 FileName : file name\r
349 WriteToDisk : TRUE is to write content of file to disk, otherwise, reading content of disk to file\r
350 PatchType : PatchTypeFloppy, PatchTypeIde, PatchTypeUsb\r
351 ProcessMbr : TRUE is to process MBR, otherwise, processing boot sector\r
352\r
353Return:\r
354 ErrorSuccess\r
355 ErrorFileCreate\r
356 ErrorFileReadWrite\r
357 ErrorNoMbr\r
358 ErrorFatType\r
359--*/\r
360{\r
361 BYTE DiskPartition[0x200];\r
362 BYTE DiskPartitionBackup[0x200];\r
363 HANDLE DiskHandle;\r
364 HANDLE FileHandle;\r
365 DWORD BytesReturn;\r
366 DWORD DbrOffset;\r
367 INT DrvNumOffset;\r
368\r
369 DiskHandle = CreateFile (\r
370 DiskName, \r
371 GENERIC_READ | GENERIC_WRITE, \r
372 FILE_SHARE_READ, \r
373 NULL, \r
374 OPEN_EXISTING, \r
375 FILE_ATTRIBUTE_NORMAL, \r
376 NULL\r
377 );\r
378 if (DiskHandle == INVALID_HANDLE_VALUE) {\r
379 return ErrorFileCreate;\r
380 }\r
381\r
382 FileHandle = CreateFile (\r
383 FileName,\r
384 GENERIC_READ | GENERIC_WRITE,\r
385 0,\r
386 NULL,\r
387 OPEN_ALWAYS,\r
388 FILE_ATTRIBUTE_NORMAL,\r
389 NULL\r
390 );\r
391 if (FileHandle == INVALID_HANDLE_VALUE) {\r
392 return ErrorFileCreate;\r
393 }\r
394\r
395 DbrOffset = 0;\r
396 //\r
397 // Skip potential MBR for Ide & USB disk\r
398 //\r
399 if ((PatchType == PatchTypeIde) || (PatchType == PatchTypeUsb)) {\r
400 //\r
401 // Even user just wants to process MBR, we get offset of boot sector here to validate the disk\r
402 // if disk have MBR, DbrOffset should be greater than 0\r
403 //\r
404 DbrOffset = GetBootSectorOffset (DiskHandle, WriteToDisk, PatchType);\r
405\r
406 if (!ProcessMbr) {\r
407 //\r
408 // 1. Process boot sector, set file pointer to the beginning of boot sector\r
409 //\r
410 SetFilePointer (DiskHandle, DbrOffset * 0x200, NULL, FILE_BEGIN);\r
411 } else if(DbrOffset == 0) {\r
412 //\r
413 // If user want to process Mbr, but no Mbr exists, simply return FALSE\r
414 //\r
415 return ErrorNoMbr;\r
416 } else {\r
417 //\r
418 // 2. Process MBR, set file pointer to 0\r
419 //\r
420 SetFilePointer (DiskHandle, 0, NULL, FILE_BEGIN);\r
421 }\r
422 }\r
423\r
424 //\r
425 // [File Pointer is pointed to beginning of Mbr or Dbr]\r
426 //\r
427 if (WriteToDisk) {\r
428 //\r
429 // Write\r
430 //\r
431 if (!ReadFile (FileHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {\r
432 return ErrorFileReadWrite;\r
433 }\r
434 if (ProcessMbr) {\r
435 //\r
436 // Use original partition table\r
437 //\r
438 if (!ReadFile (DiskHandle, DiskPartitionBackup, 0x200, &BytesReturn, NULL)) {\r
439 return ErrorFileReadWrite;\r
440 }\r
441 memcpy (DiskPartition + 0x1BE, DiskPartitionBackup + 0x1BE, 0x40);\r
442 SetFilePointer (DiskHandle, 0, NULL, FILE_BEGIN);\r
443 }\r
444\r
445 if (!WriteFile (DiskHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {\r
446 return ErrorFileReadWrite;\r
447 }\r
448\r
449 } else {\r
450 //\r
451 // Read\r
452 //\r
453 if (!ReadFile (DiskHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {\r
454 return ErrorFileReadWrite;\r
455 }\r
456\r
457 if (PatchType == PatchTypeUsb) {\r
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. \r
459 // offset of BS_DrvNum is 0x24 for FAT12/16\r
460 // 0x40 for FAT32\r
461 //\r
462 DrvNumOffset = GetDrvNumOffset (DiskPartition);\r
463 if (DrvNumOffset == -1) {\r
464 return ErrorFatType;\r
465 }\r
466 //\r
467 // Some legacy BIOS require 0x80 discarding MBR.\r
468 // Question left here: is it needed to check Mbr before set 0x80?\r
469 //\r
470 DiskPartition[DrvNumOffset] = ((DbrOffset > 0) ? 0x80 : 0);\r
471 }\r
472\r
473\r
474 if (PatchType == PatchTypeIde) {\r
475 //\r
476 // Patch LBAOffsetForBootSector\r
477 //\r
478 *(DWORD *)&DiskPartition [BOOT_SECTOR_LBA_OFFSET] = DbrOffset;\r
479 }\r
480 if (!WriteFile (FileHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {\r
481 return ErrorFileReadWrite;\r
482 }\r
483 }\r
484 CloseHandle (FileHandle);\r
485 CloseHandle (DiskHandle);\r
486 return ErrorSuccess;\r
487}\r
488\r
489VOID\r
490PrintUsage (\r
491 CHAR* AppName\r
492 )\r
493{\r
494 fprintf (\r
495 stdout,\r
496 "Usage: %s [OPTIONS]...\n"\r
497 "Copy file content from/to bootsector.\n"\r
498 "\n"\r
499 " -l list disks\n"\r
500 " -if=FILE specified an input, can be files or disks\n"\r
501 " -of=FILE specified an output, can be files or disks\n"\r
502 " -mbr process MBR also\n"\r
503 " -h print this message\n"\r
504 "\n"\r
505 "FILE providing a volume plus a colon (X:), indicates a disk\n"\r
506 "FILE providing other format, indicates a file\n",\r
507 AppName\r
508 );\r
509}\r
510 \r
511INT\r
512main (\r
513 INT argc,\r
514 CHAR *argv[]\r
515 )\r
516{\r
517 CHAR *AppName;\r
518 INT Index;\r
519 BOOL ProcessMbr;\r
520 CHAR VolumeLetter;\r
521 CHAR *FilePath;\r
522 BOOL WriteToDisk;\r
523 DRIVE_INFO DriveInfo;\r
524 PATCH_TYPE PatchType;\r
525 ERROR_STATUS Status;\r
526\r
527 CHAR FloppyPathTemplate[] = "\\\\.\\%c:";\r
528 CHAR DiskPathTemplate[] = "\\\\.\\PHYSICALDRIVE%u";\r
529 CHAR DiskPath[MAX_PATH];\r
530\r
531 AppName = *argv;\r
532 argv ++;\r
533 argc --;\r
534 \r
535 ProcessMbr = FALSE;\r
536 WriteToDisk = TRUE;\r
537 FilePath = NULL;\r
538 VolumeLetter = 0;\r
539\r
540 //\r
541 // Parse command line\r
542 //\r
543 for (Index = 0; Index < argc; Index ++) {\r
544 if (_stricmp (argv[Index], "-l") == 0) {\r
545 ListDrive ();\r
546 return 0;\r
547 }\r
548 else if (_stricmp (argv[Index], "-mbr") == 0) {\r
549 ProcessMbr = TRUE;\r
550 }\r
551 else if ((_strnicmp (argv[Index], "-if=", 4) == 0) ||\r
552 (_strnicmp (argv[Index], "-of=", 4) == 0)\r
553 ) {\r
554 if (argv[Index][6] == '\0' && argv[Index][5] == ':' && IsLetter (argv[Index][4])) {\r
555 VolumeLetter = argv[Index][4];\r
556 if (_strnicmp (argv[Index], "-if=", 4) == 0) {\r
557 WriteToDisk = FALSE;\r
558 }\r
559 }\r
560 else {\r
561 FilePath = &argv[Index][4];\r
562 }\r
563 }\r
564 else {\r
565 PrintUsage (AppName);\r
566 return 1;\r
567 }\r
568 }\r
569\r
570 //\r
571 // Check parameter\r
572 //\r
573 if (VolumeLetter == 0) {\r
574 fprintf (stderr, "ERROR: Volume isn't provided!\n");\r
575 PrintUsage (AppName);\r
576 return 1;\r
577 }\r
578 \r
579 if (FilePath == NULL) {\r
580 fprintf (stderr, "ERROR: File isn't pvovided!\n");\r
581 PrintUsage (AppName);\r
582 return 1;\r
583 }\r
584 \r
585 PatchType = PatchTypeUnknown;\r
586\r
587 if ((VolumeLetter == 'A') || (VolumeLetter == 'a') || \r
588 (VolumeLetter == 'B') || (VolumeLetter == 'b') \r
589 ) {\r
590 //\r
591 // Floppy\r
592 //\r
593 sprintf (DiskPath, FloppyPathTemplate, VolumeLetter);\r
594 PatchType = PatchTypeFloppy;\r
595 }\r
596 else {\r
597 //\r
598 // Hard/USB disk\r
599 //\r
600 if (!GetDriveInfo (VolumeLetter, &DriveInfo)) {\r
601 fprintf (stderr, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());\r
602 return 1;\r
603 }\r
604\r
605 //\r
606 // Shouldn't patch my own hard disk, but can read it.\r
607 // very safe then:)\r
608 //\r
609 if (DriveInfo.DriveType->Type == DRIVE_FIXED && WriteToDisk) {\r
610 fprintf (stderr, "ERROR: Write to local harddisk - permission denied!\n");\r
611 return 1;\r
612 }\r
613 \r
614 sprintf (DiskPath, DiskPathTemplate, DriveInfo.DiskNumber);\r
615 if (DriveInfo.DriveType->Type == DRIVE_REMOVABLE) {\r
616 PatchType = PatchTypeUsb;\r
617 }\r
618 else if (DriveInfo.DriveType->Type == DRIVE_FIXED) {\r
619 PatchType = PatchTypeIde;\r
620 }\r
621 }\r
622\r
623 if (PatchType == PatchTypeUnknown) {\r
624 fprintf (stderr, "ERROR: PatchType unknown!\n");\r
625 return 1;\r
626 }\r
627\r
628 //\r
629 // Process DBR (Patch or Read)\r
630 //\r
631 Status = ProcessBsOrMbr (DiskPath, FilePath, WriteToDisk, PatchType, ProcessMbr);\r
632 if (Status == ErrorSuccess) {\r
633 fprintf (\r
634 stdout, \r
635 "%s %s: successfully!\n", \r
636 WriteToDisk ? "Write" : "Read", \r
637 ProcessMbr ? "MBR" : "DBR"\r
638 );\r
639 return 0;\r
640 } else {\r
641 fprintf (\r
642 stderr, \r
643 "%s: %s %s: failed - %s (LastError: 0x%x)!\n",\r
644 (Status == ErrorNoMbr) ? "WARNING" : "ERROR",\r
645 WriteToDisk ? "Write" : "Read", \r
646 ProcessMbr ? "MBR" : "DBR", \r
647 ErrorStatusDesc[Status],\r
648 GetLastError ()\r
649 );\r
650 return 1;\r
651 }\r
652}\r