Added hybrid MBR and destroy-GPT options

This commit is contained in:
srs5694
2009-08-20 21:35:25 -04:00
parent 7f244babbe
commit c0ca8f877e
7 changed files with 257 additions and 34 deletions

View File

@@ -3,6 +3,19 @@
- Changed __DARWIN_UNIX03 to __APPLE__ as code to enable MacOS X support. - Changed __DARWIN_UNIX03 to __APPLE__ as code to enable MacOS X support.
- Added the ability to create a hybrid MBR ('h' on experts' menu). This was
motivated by my discovery that Windows 7 remains brain-dead when it comes
to the ability to boot from a GPT disk, at least on BIOS-based machines.
- Added 'z' option to experts' menu, to destroy GPT data structures and
exit. The intent is to use this feature to enable subsequent partitioning
of the disk using fdisk or other GPT-unaware tools. (GNU Parted will wipe
the GPT data structures itself when you create a new MBR ["msdos
disklabel," in Parted parlance], so using Parted is another option.)
- Slightly altered the effect of the 'o' command on the main menu. It now
blanks out the protective MBR, as well as the GPT data.
0.3.1: 0.3.1:
------ ------
@@ -15,11 +28,6 @@
work OK on my Mac OS test system and on both 32- and 64-bit Linux work OK on my Mac OS test system and on both 32- and 64-bit Linux
systems. systems.
- Added test for writability when opening a disk for reading. If the
test fails, a warning message is displayed. (The test simply opens
the disk for writing and then closes it before writing any data,
so the test shouldn't cause problems.)
- Fixed off-by-one bug in GPTData::FindLastAvailable(). - Fixed off-by-one bug in GPTData::FindLastAvailable().
- Fixed bug that caused display of options after a disk-write error. - Fixed bug that caused display of options after a disk-write error.

35
gdisk.8
View File

@@ -405,12 +405,14 @@ there will be no backup partition table on disk.
Use main GPT header and rebuild the backup. This option is likely to be Use main GPT header and rebuild the backup. This option is likely to be
useful if the backup GPT header has been damaged or destroyed. useful if the backup GPT header has been damaged or destroyed.
.TP .TP
.B e .B e
Load main partition table. This option reloads the main partition table Load main partition table. This option reloads the main partition table
from disk. It's only likely to be useful if you've tried to use the backup from disk. It's only likely to be useful if you've tried to use the backup
partition table (via 'c') but it's in worse shape then the main partition partition table (via 'c') but it's in worse shape then the main partition
table. table.
.TP .TP
.B f .B f
Change partition GUID. You can enter a custom unique GUID for a partition Change partition GUID. You can enter a custom unique GUID for a partition
using this option. (Note this refers to the GUID that uniquely identifies a using this option. (Note this refers to the GUID that uniquely identifies a
@@ -424,6 +426,16 @@ Change disk GUID. Each disk has a unique GUID code, which
.B gdisk .B gdisk
assigns randomly upon creation of the GPT data structures. You can generate assigns randomly upon creation of the GPT data structures. You can generate
a fresh random GUID or enter one manually with this option. a fresh random GUID or enter one manually with this option.
.TP
.B h
Create a hybrid MBR. This is an ugly workaround that enables GPT-unaware
OSes, or that that can't boot from a GPT disk, to access up to three of
the partitions on the disk by creating MBR entries for them. Note that
these hybrid MBR entries are not updated when you make subsequent changes
to the GPT entries, so you must re-run this option whenever you make
changes that would affect the hybridized partitions.
.TP .TP
.B i .B i
Show detailed partition information. This option is identical to the 'i' Show detailed partition information. This option is identical to the 'i'
@@ -480,6 +492,16 @@ Verify disk. This option is identical to the 'v' option in the main menu.
.B w .B w
Write table to disk and exit. This option is identical to the 'w' option in Write table to disk and exit. This option is identical to the 'w' option in
the main menu. the main menu.
.TP
.B z
Destroy the GPT data structures and exit. Use this option if you want to
repartition a GPT disk using
.B "fdisk"
or some other GPT-unaware program.
You'll be given the choice of preserving the existing MBR, in case it's a
hybrid MBR with salvageable partitions.
.PP .PP
In many cases, you can press the Enter key to select a default option when In many cases, you can press the Enter key to select a default option when
@@ -488,7 +510,7 @@ entering data. When only one option is possible,
usually bypasses the prompt entirely. usually bypasses the prompt entirely.
.SH BUGS .SH BUGS
As of August of 2009 (version 0.3.1), As of August of 2009 (version 0.3.2),
.B gdisk .B gdisk
should be considered early beta software. Known bugs and should be considered early beta software. Known bugs and
limitations include: limitations include:
@@ -506,12 +528,6 @@ The program compiles correctly only on Linux and Mac OS X. Both 64-bit
more thoroughly than the latter. The Mac OS X support was added with more thoroughly than the latter. The Mac OS X support was added with
version 0.3.1 and has not been thoroughly tested. version 0.3.1 and has not been thoroughly tested.
.TP
.B *
Under Mac OS X, the program will only save a partition table if no
partitions from the disk are currently mounted. (This limitation does not
exist in the Linux version of the program.)
.TP .TP
.B * .B *
The fields used to display the start and end sector numbers for partitions The fields used to display the start and end sector numbers for partitions
@@ -571,7 +587,10 @@ get appropriate GUID type codes at all.
Booting after converting an MBR disk may be disrupted. Sometimes Booting after converting an MBR disk may be disrupted. Sometimes
re-installing a boot loader will fix the problem, but other times you may re-installing a boot loader will fix the problem, but other times you may
need to switch boot loaders. Except on EFI-based platforms, Windows through need to switch boot loaders. Except on EFI-based platforms, Windows through
Vista doesn't support booting from GPT disks. at least Windows 7 RC doesn't support booting from GPT disks. Creating a
hybrid MBR (using the 'h' option on the experts' menu) or abandoning GPT in
favor of MBR may be your only options in this case.
.PP .PP
.SH AUTHORS .SH AUTHORS

View File

@@ -24,7 +24,7 @@ int main(int argc, char* argv[]) {
int doMore = 1; int doMore = 1;
char* device = NULL; char* device = NULL;
printf("GPT fdisk (gdisk) version 0.3.1\n\n"); printf("GPT fdisk (gdisk) version 0.3.2\n\n");
if (argc == 2) { // basic usage if (argc == 2) { // basic usage
if (SizesOK()) { if (SizesOK()) {
@@ -88,7 +88,7 @@ int DoCommand(char* filename, struct GPTData* theGPT) {
break; break;
case 'o': case 'O': case 'o': case 'O':
theGPT->ClearGPTData(); theGPT->ClearGPTData();
// theGPT->protectiveMBR.MakeProtectiveMBR(); theGPT->MakeProtectiveMBR();
// theGPT->BlankPartitions(); // theGPT->BlankPartitions();
break; break;
case 'p': case 'P': case 'p': case 'P':
@@ -192,9 +192,9 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
printf("Enter the disk's unique GUID:\n"); printf("Enter the disk's unique GUID:\n");
theGPT->SetDiskGUID(GetGUID()); theGPT->SetDiskGUID(GetGUID());
break; break;
/* case 'h': case 'H': case 'h': case 'H':
theGPT->MakeHybrid(); theGPT->MakeHybrid();
break; */ break;
case 'i': case 'I': case 'i': case 'I':
theGPT->ShowDetails(); theGPT->ShowDetails();
break; break;
@@ -238,6 +238,12 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
goOn = 0; goOn = 0;
} // if } // if
break; break;
case 'z': case 'Z':
if (theGPT->DestroyGPT() == 1) {
retval = 0;
goOn = 0;
}
break;
default: default:
ShowExpertCommands(); ShowExpertCommands();
break; break;
@@ -254,7 +260,7 @@ void ShowExpertCommands(void) {
printf("e\tload main partition table from disk (rebuilding backup)\n"); printf("e\tload main partition table from disk (rebuilding backup)\n");
printf("f\tchange partition GUID\n"); printf("f\tchange partition GUID\n");
printf("g\tchange disk GUID\n"); printf("g\tchange disk GUID\n");
// printf("h\tmake hybrid MBR\n"); printf("h\tmake hybrid MBR\n");
printf("i\tshow detailed information on a partition\n"); printf("i\tshow detailed information on a partition\n");
printf("k\tsave partition data to a backup file\n"); printf("k\tsave partition data to a backup file\n");
printf("l\tload partition data from a backup file\n"); printf("l\tload partition data from a backup file\n");
@@ -267,4 +273,5 @@ void ShowExpertCommands(void) {
printf("s\tresize partition table\n"); printf("s\tresize partition table\n");
printf("v\tverify disk\n"); printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n"); printf("w\twrite table to disk and exit\n");
printf("z\tDestroy GPT data structures and exit\n");
} // ShowExpertCommands() } // ShowExpertCommands()

107
gpt.cc
View File

@@ -1307,7 +1307,6 @@ uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
return totalFound; return totalFound;
} // GPTData::FindFreeBlocks() } // GPTData::FindFreeBlocks()
/*
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with // Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
// OSes that don't understand GPT. // OSes that don't understand GPT.
void GPTData::MakeHybrid(void) { void GPTData::MakeHybrid(void) {
@@ -1315,16 +1314,26 @@ void GPTData::MakeHybrid(void) {
char line[255]; char line[255];
int numParts, i, j, typeCode, bootable; int numParts, i, j, typeCode, bootable;
uint64_t length; uint64_t length;
char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
// First, rebuild the protective MBR... printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
protectiveMBR.MakeProtectiveMBR(); "to use one, just hit the Enter key at the below prompt and your MBR\n"
"partition table will be untouched.\n\n\a");
// Now get the numbers of up to three partitions to add to the // Now get the numbers of up to three partitions to add to the
// hybrid MBR.... // hybrid MBR....
printf("Type from one to three partition numbers to be added to the hybrid MBR, in\n" printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
"sequence: "); "added to the hybrid MBR, in sequence: ");
fgets(line, 255, stdin); fgets(line, 255, stdin);
numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]); numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
if (numParts > 0) {
// Blank out the protective MBR, but leave the boot loader code
// alone....
protectiveMBR.EmptyMBR(0);
protectiveMBR.SetDiskSize(diskSize);
} // if
for (i = 0; i < numParts; i++) { for (i = 0; i < numParts; i++) {
j = partNums[i] - 1; j = partNums[i] - 1;
printf("Creating entry for partition #%d\n", j + 1); printf("Creating entry for partition #%d\n", j + 1);
@@ -1337,7 +1346,7 @@ void GPTData::MakeHybrid(void) {
printf("Set the bootable flag? "); printf("Set the bootable flag? ");
bootable = (GetYN() == 'Y'); bootable = (GetYN() == 'Y');
length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1); length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1);
protectiveMBR.MakePart(i + 1, (uint32_t) partitions[j].firstLBA, protectiveMBR.MakePart(i, (uint32_t) partitions[j].firstLBA,
(uint32_t) length, typeCode, bootable); (uint32_t) length, typeCode, bootable);
} else { // partition out of range } else { // partition out of range
printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n", printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
@@ -1347,8 +1356,46 @@ void GPTData::MakeHybrid(void) {
printf("Partition %d is out of range; omitting it.\n", j + 1); printf("Partition %d is out of range; omitting it.\n", j + 1);
} // if/else } // if/else
} // for } // for
if (numParts > 0) { // User opted to create a hybrid MBR....
// Create EFI protective partition that covers the start of the disk.
// If this location (covering the main GPT data structures) is omitted,
// Linux won't find any partitions on the disk. Note that this is
// NUMBERED AFTER the hybrid partitions, contrary to what the
// gptsync utility does. This is because Windows seems to choke on
// disks with a 0xEE partition in the first slot and subsequent
// additional partitions, unless it boots from the disk.
protectiveMBR.MakePart(numParts, 1, protectiveMBR.FindLastInFree(1), 0xEE);
// ... and for good measure, if there are any partition spaces left,
// optionally create more protective EFI partitions to cover as much
// space as possible....
for (i = 0; i < 4; i++) {
if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
if (fillItUp == 'M') {
printf("Unused partition space(s) found. Use one to protect more partitions? ");
fillItUp = GetYN();
typeCode = 0x00; // use this to flag a need to get type code
} // if
if (fillItUp == 'Y') {
if (typeCode == 0x00) {
printf("Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ");
// Comment on above: Mac OS treats disks with more than one
// 0xEE MBR partition as MBR disks, not as GPT disks.
fgets(line, 255, stdin);
sscanf(line, "%x", &typeCode);
} // if (typeCode == 0x00)
protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
} // if (fillItUp == 'Y')
} // if unused entry
} // for (i = 0; i < 4; i++)
} // if (numParts > 0)
} // GPTData::MakeHybrid() } // GPTData::MakeHybrid()
*/
// Create a fresh protective MBR.
void GPTData::MakeProtectiveMBR(void) {
protectiveMBR.MakeProtectiveMBR();
} // GPTData::MakeProtectiveMBR(void)
// Writes GPT (and protective MBR) to disk. Returns 1 on successful // Writes GPT (and protective MBR) to disk. Returns 1 on successful
// write, 0 if there was a problem. // write, 0 if there was a problem.
@@ -1609,6 +1656,52 @@ int GPTData::LoadGPTBackup(char* filename) {
return allOK; return allOK;
} // GPTData::LoadGPTBackup() } // GPTData::LoadGPTBackup()
// This function destroys the on-disk GPT structures. Returns 1 if the
// user confirms destruction, 0 if the user aborts.
int GPTData::DestroyGPT(void) {
int fd, i, doMore;
char blankSector[512], goOn;
for (i = 0; i < 512; i++) {
blankSector[i] = '\0';
} // for
printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
goOn = GetYN();
if (goOn == 'Y') {
fd = open(device, O_WRONLY);
#ifdef __APPLE__
// MacOS X requires a shared lock under some circumstances....
if (fd < 0) {
fd = open(device, O_WRONLY|O_SHLOCK);
} // if
#endif
if (fd != -1) {
lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
write(fd, blankSector, 512); // blank it out
lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
for (i = 0; i < GetBlocksInPartTable(); i++)
write(fd, blankSector, 512);
lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table
for (i = 0; i < GetBlocksInPartTable(); i++)
write(fd, blankSector, 512);
lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
write(fd, blankSector, 512); // blank it out
printf("Blank out MBR? ");
if (GetYN() == 'Y') {
lseek64(fd, 0, SEEK_SET);
write(fd, blankSector, 512); // blank it out
} // if blank MBR
close(fd);
printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
"other utilities. Program will now terminate.\n");
} else {
printf("Problem opening %s for writing! Program will now terminate.\n");
} // if/else (fd != -1)
} // if (goOn == 'Y')
return (goOn == 'Y');
} // GPTData::DestroyGPT()
// Check to be sure that data type sizes are correct. The basic types (uint*_t) should // Check to be sure that data type sizes are correct. The basic types (uint*_t) should
// never fail these tests, but the struct types may fail depending on compile options. // never fail these tests, but the struct types may fail depending on compile options.
// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure // Specifically, the -fpack-struct option to gcc may be required to ensure proper structure

5
gpt.h
View File

@@ -126,11 +126,12 @@ public:
void RebuildSecondHeader(void); void RebuildSecondHeader(void);
void LoadSecondTableAsMain(void); void LoadSecondTableAsMain(void);
uint64_t FindFreeBlocks(int *numSegments, uint64_t *largestSegment); uint64_t FindFreeBlocks(int *numSegments, uint64_t *largestSegment);
// void MakeHybrid(void); void MakeHybrid(void);
void MakeProtectiveMBR(void) {return protectiveMBR.MakeProtectiveMBR();} void MakeProtectiveMBR(void);
int SaveGPTData(void); int SaveGPTData(void);
int SaveGPTBackup(char* filename); int SaveGPTBackup(char* filename);
int LoadGPTBackup(char* filename); int LoadGPTBackup(char* filename);
int DestroyGPT(void); // Returns 1 if user proceeds
// Return data about the GPT structures.... // Return data about the GPT structures....
uint32_t GetNumParts(void) {return mainHeader.numParts;} uint32_t GetNumParts(void) {return mainHeader.numParts;}

100
mbr.cc
View File

@@ -52,14 +52,22 @@ MBRData::MBRData(char *filename) {
MBRData::~MBRData(void) { MBRData::~MBRData(void) {
} // MBRData destructor } // MBRData destructor
// Empty all data. Meant mainly for calling by constructors // Empty all data. Meant mainly for calling by constructors, but it's also
void MBRData::EmptyMBR(void) { // used by the hybrid MBR functions in the GPTData class.
void MBRData::EmptyMBR(int clearBootloader) {
int i; int i;
for (i = 0; i < 440; i++) // Zero out the boot loader section, the disk signature, and the
code[i] = 0; // 2-byte nulls area only if requested to do so. (This is the
diskSignature = (uint32_t) rand(); // default.)
nulls = 0; if (clearBootloader == 1) {
for (i = 0; i < 440; i++)
code[i] = 0;
diskSignature = (uint32_t) rand();
nulls = 0;
} // if
// Blank out the partitions
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
partitions[i].status = UINT8_C(0); partitions[i].status = UINT8_C(0);
partitions[i].firstSector[0] = UINT8_C(0); partitions[i].firstSector[0] = UINT8_C(0);
@@ -343,6 +351,39 @@ void MBRData::MakeProtectiveMBR(void) {
state = gpt; state = gpt;
} // MBRData::MakeProtectiveMBR() } // MBRData::MakeProtectiveMBR()
// Create a partition that fills the most available space. Returns
// 1 if partition was created, 0 otherwise. Intended for use in
// creating hybrid MBRs.
int MBRData::MakeBiggestPart(int i, int type) {
uint32_t start = UINT32_C(1); // starting point for each search
uint32_t firstBlock; // first block in a segment
uint32_t lastBlock; // last block in a segment
uint32_t segmentSize; // size of segment in blocks
uint32_t selectedSegment = UINT32_C(0); // location of largest segment
uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks
int found = 0;
do {
firstBlock = FindFirstAvailable(start);
if (firstBlock != UINT32_C(0)) { // something's free...
lastBlock = FindLastInFree(firstBlock);
segmentSize = lastBlock - firstBlock + UINT32_C(1);
if (segmentSize > selectedSize) {
selectedSize = segmentSize;
selectedSegment = firstBlock;
} // if
start = lastBlock + 1;
} // if
} while (firstBlock != 0);
if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
found = 1;
MakePart(i, selectedSegment, selectedSize, type, 0);
} else {
found = 0;
} // if/else
return found;
} // MBRData::MakeBiggestPart(int i)
// Return a pointer to a primary or logical partition, or NULL if // Return a pointer to a primary or logical partition, or NULL if
// the partition is out of range.... // the partition is out of range....
struct MBRRecord* MBRData::GetPartition(int i) { struct MBRRecord* MBRData::GetPartition(int i) {
@@ -398,6 +439,53 @@ void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
partitions[num].lengthLBA = length; partitions[num].lengthLBA = length;
} // MakePart() } // MakePart()
// Finds the first free space on the disk from start onward; returns 0
// if none available....
uint32_t MBRData::FindFirstAvailable(uint32_t start) {
uint32_t first;
uint32_t i;
int firstMoved = 0;
first = start;
// ...now search through all partitions; if first is within an
// existing partition, move it to the next sector after that
// partition and repeat. If first was moved, set firstMoved
// flag; repeat until firstMoved is not set, so as to catch
// cases where partitions are out of sequential order....
do {
firstMoved = 0;
for (i = 0; i < 4; i++) {
// Check if it's in the existing partition
if ((first >= partitions[i].firstLBA) &&
(first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
first = partitions[i].firstLBA + partitions[i].lengthLBA;
firstMoved = 1;
} // if
} // for
} while (firstMoved == 1);
if (first >= diskSize)
first = 0;
return (first);
} // MBRData::FindFirstAvailable()
uint32_t MBRData::FindLastInFree(uint32_t start) {
uint32_t nearestStart;
uint32_t i;
if (diskSize <= UINT32_MAX)
nearestStart = diskSize - 1;
else
nearestStart = UINT32_MAX - 1;
for (i = 0; i < 4; i++) {
if ((nearestStart > partitions[i].firstLBA) &&
(partitions[i].firstLBA > start)) {
nearestStart = partitions[i].firstLBA - 1;
} // if
} // for
return (nearestStart);
} // MBRData::FindLastInFree
uint8_t MBRData::GetStatus(int i) { uint8_t MBRData::GetStatus(int i) {
MBRRecord* thePart; MBRRecord* thePart;
uint8_t retval; uint8_t retval;

9
mbr.h
View File

@@ -71,7 +71,9 @@ public:
MBRData(void); MBRData(void);
MBRData(char* deviceFilename); MBRData(char* deviceFilename);
~MBRData(void); ~MBRData(void);
void EmptyMBR(void); // Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
void EmptyMBR(int clearBootloader = 1);
void SetDiskSize(uint64_t ds) {diskSize = ds;}
int ReadMBRData(char* deviceFilename); int ReadMBRData(char* deviceFilename);
void ReadMBRData(int fd); void ReadMBRData(int fd);
int WriteMBRData(void); int WriteMBRData(void);
@@ -84,6 +86,11 @@ public:
void ShowState(void); void ShowState(void);
void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07, void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
int bootable = 0); int bootable = 0);
int MakeBiggestPart(int i, int type); // Make partition filling most space
// Functions to find information on free space....
uint32_t FindFirstAvailable(uint32_t start = 1);
uint32_t FindLastInFree(uint32_t start);
// Functions to extract data on specific partitions.... // Functions to extract data on specific partitions....
uint8_t GetStatus(int i); uint8_t GetStatus(int i);