summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG18
-rw-r--r--gdisk.835
-rw-r--r--gdisk.cc17
-rw-r--r--gpt.cc107
-rw-r--r--gpt.h5
-rw-r--r--mbr.cc100
-rw-r--r--mbr.h9
7 files changed, 257 insertions, 34 deletions
diff --git a/CHANGELOG b/CHANGELOG
index da011e5..10458d7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,19 @@
- 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:
------
@@ -15,11 +28,6 @@
work OK on my Mac OS test system and on both 32- and 64-bit Linux
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 bug that caused display of options after a disk-write error.
diff --git a/gdisk.8 b/gdisk.8
index d1a7e9d..274a2c9 100644
--- a/gdisk.8
+++ b/gdisk.8
@@ -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
useful if the backup GPT header has been damaged or destroyed.
.TP
+
.B e
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
partition table (via 'c') but it's in worse shape then the main partition
table.
.TP
+
.B f
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
@@ -424,6 +426,16 @@ Change disk GUID. Each disk has a unique GUID code, which
.B gdisk
assigns randomly upon creation of the GPT data structures. You can generate
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
.B 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
Write table to disk and exit. This option is identical to the 'w' option in
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
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.
.SH BUGS
-As of August of 2009 (version 0.3.1),
+As of August of 2009 (version 0.3.2),
.B gdisk
should be considered early beta software. Known bugs and
limitations include:
@@ -508,12 +530,6 @@ 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
-.B *
The fields used to display the start and end sector numbers for partitions
in the 'p' command are 14 characters wide. This translates to a limitation
of about 45 PiB. On larger disks, the displayed columns will go out of
@@ -571,7 +587,10 @@ get appropriate GUID type codes at all.
Booting after converting an MBR disk may be disrupted. Sometimes
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
-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
.SH AUTHORS
diff --git a/gdisk.cc b/gdisk.cc
index 7cfb2bf..44f82a8 100644
--- a/gdisk.cc
+++ b/gdisk.cc
@@ -24,7 +24,7 @@ int main(int argc, char* argv[]) {
int doMore = 1;
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 (SizesOK()) {
@@ -88,7 +88,7 @@ int DoCommand(char* filename, struct GPTData* theGPT) {
break;
case 'o': case 'O':
theGPT->ClearGPTData();
-// theGPT->protectiveMBR.MakeProtectiveMBR();
+ theGPT->MakeProtectiveMBR();
// theGPT->BlankPartitions();
break;
case 'p': case 'P':
@@ -192,9 +192,9 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
printf("Enter the disk's unique GUID:\n");
theGPT->SetDiskGUID(GetGUID());
break;
-/* case 'h': case 'H':
+ case 'h': case 'H':
theGPT->MakeHybrid();
- break; */
+ break;
case 'i': case 'I':
theGPT->ShowDetails();
break;
@@ -238,6 +238,12 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
goOn = 0;
} // if
break;
+ case 'z': case 'Z':
+ if (theGPT->DestroyGPT() == 1) {
+ retval = 0;
+ goOn = 0;
+ }
+ break;
default:
ShowExpertCommands();
break;
@@ -254,7 +260,7 @@ void ShowExpertCommands(void) {
printf("e\tload main partition table from disk (rebuilding backup)\n");
printf("f\tchange partition 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("k\tsave partition data to 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("v\tverify disk\n");
printf("w\twrite table to disk and exit\n");
+ printf("z\tDestroy GPT data structures and exit\n");
} // ShowExpertCommands()
diff --git a/gpt.cc b/gpt.cc
index 83bf046..b2349db 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -1307,7 +1307,6 @@ uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
return totalFound;
} // GPTData::FindFreeBlocks()
-/*
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
// OSes that don't understand GPT.
void GPTData::MakeHybrid(void) {
@@ -1315,16 +1314,26 @@ void GPTData::MakeHybrid(void) {
char line[255];
int numParts, i, j, typeCode, bootable;
uint64_t length;
+ char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
- // First, rebuild the protective MBR...
- protectiveMBR.MakeProtectiveMBR();
+ printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
+ "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
// hybrid MBR....
- printf("Type from one to three partition numbers to be added to the hybrid MBR, in\n"
- "sequence: ");
+ printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
+ "added to the hybrid MBR, in sequence: ");
fgets(line, 255, stdin);
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++) {
j = partNums[i] - 1;
printf("Creating entry for partition #%d\n", j + 1);
@@ -1337,7 +1346,7 @@ void GPTData::MakeHybrid(void) {
printf("Set the bootable flag? ");
bootable = (GetYN() == 'Y');
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);
} else { // partition out of range
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);
} // if/else
} // 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()
-*/
+
+// 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
// write, 0 if there was a problem.
@@ -1609,6 +1656,52 @@ int GPTData::LoadGPTBackup(char* filename) {
return allOK;
} // 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
// 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
diff --git a/gpt.h b/gpt.h
index c56de20..4c2904e 100644
--- a/gpt.h
+++ b/gpt.h
@@ -126,11 +126,12 @@ public:
void RebuildSecondHeader(void);
void LoadSecondTableAsMain(void);
uint64_t FindFreeBlocks(int *numSegments, uint64_t *largestSegment);
-// void MakeHybrid(void);
- void MakeProtectiveMBR(void) {return protectiveMBR.MakeProtectiveMBR();}
+ void MakeHybrid(void);
+ void MakeProtectiveMBR(void);
int SaveGPTData(void);
int SaveGPTBackup(char* filename);
int LoadGPTBackup(char* filename);
+ int DestroyGPT(void); // Returns 1 if user proceeds
// Return data about the GPT structures....
uint32_t GetNumParts(void) {return mainHeader.numParts;}
diff --git a/mbr.cc b/mbr.cc
index e692adc..4909ebc 100644
--- a/mbr.cc
+++ b/mbr.cc
@@ -52,14 +52,22 @@ MBRData::MBRData(char *filename) {
MBRData::~MBRData(void) {
} // MBRData destructor
-// Empty all data. Meant mainly for calling by constructors
-void MBRData::EmptyMBR(void) {
+// Empty all data. Meant mainly for calling by constructors, but it's also
+// used by the hybrid MBR functions in the GPTData class.
+void MBRData::EmptyMBR(int clearBootloader) {
int i;
- for (i = 0; i < 440; i++)
- code[i] = 0;
- diskSignature = (uint32_t) rand();
- nulls = 0;
+ // Zero out the boot loader section, the disk signature, and the
+ // 2-byte nulls area only if requested to do so. (This is the
+ // default.)
+ 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++) {
partitions[i].status = UINT8_C(0);
partitions[i].firstSector[0] = UINT8_C(0);
@@ -343,6 +351,39 @@ void MBRData::MakeProtectiveMBR(void) {
state = gpt;
} // 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
// the partition is out of range....
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;
} // 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) {
MBRRecord* thePart;
uint8_t retval;
diff --git a/mbr.h b/mbr.h
index 86f792b..c178d19 100644
--- a/mbr.h
+++ b/mbr.h
@@ -71,7 +71,9 @@ public:
MBRData(void);
MBRData(char* deviceFilename);
~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);
void ReadMBRData(int fd);
int WriteMBRData(void);
@@ -84,6 +86,11 @@ public:
void ShowState(void);
void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
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....
uint8_t GetStatus(int i);