Version 0.6.4
diff --git a/gpt.cc b/gpt.cc
index a2e216f..5ce5b7f 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -10,7 +10,6 @@
#define __STDC_CONSTANT_MACROS
#include <stdio.h>
-#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
@@ -99,7 +98,6 @@
int problems = 0;
uint32_t i, numSegments;
uint64_t totalFree, largestSegment;
- char siTotal[255], siLargest[255];
// First, check for CRC errors in the GPT data....
if (!mainCrcOk) {
@@ -231,8 +229,6 @@
// problems could affect the results
if (problems == 0) {
totalFree = FindFreeBlocks(&numSegments, &largestSegment);
- strcpy(siTotal, BytesToSI(totalFree * (uint64_t) blockSize).c_str());
- strcpy(siLargest, BytesToSI(largestSegment * (uint64_t) blockSize).c_str());
cout << "No problems found. " << totalFree << " free sectors ("
<< BytesToSI(totalFree * (uint64_t) blockSize) << ") available in "
<< numSegments << "\nsegments, the largest of which is "
@@ -386,10 +382,6 @@
ReverseHeaderBytes(&mainHeader);
ReverseHeaderBytes(&secondHeader);
} // if
-/* if ((littleEndian = IsLittleEndian()) == 0) {
- ReverseBytes(&trueNumParts, 4);
- ReverseBytes(&hSize, 4);
- } // if */
// Compute CRC of partition tables & store in main and secondary headers
crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
@@ -566,10 +558,10 @@
// Read GPT data from a disk.
int GPTData::LoadPartitions(const string & deviceFilename) {
+ BSDData bsdDisklabel;
int err, allOK = 1;
uint32_t i;
uint64_t firstBlock, lastBlock;
- BSDData bsdDisklabel;
MBRValidity mbrState;
// First, do a test to see if writing will be possible later....
@@ -577,7 +569,7 @@
if ((err == 0) && (!justLooking)) {
cout << "\aNOTE: Write test failed with error number " << errno
<< ". It will be impossible to save\nchanges to this disk's partition table!\n";
-#ifdef __FreeBSD__
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
cout << "You may be able to enable writes by exiting this program, typing\n"
<< "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
<< "program.\n";
@@ -604,7 +596,7 @@
// bsdDisklabel.DisplayBSDData();
ClearGPTData();
protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
- XFormDisklabel(&bsdDisklabel, 0);
+ XFormDisklabel(&bsdDisklabel);
break;
case use_gpt:
mbrState = protectiveMBR.GetValidity();
@@ -655,12 +647,12 @@
if (mainCrcOk && (mainHeader.backupLBA < diskSize)) {
allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK;
} else {
- if (mainHeader.backupLBA >= diskSize)
+ allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK;
+ if (mainCrcOk && (mainHeader.backupLBA >= diskSize))
cout << "Warning! Disk size is smaller than the main header indicates! Loading\n"
<< "secondary header from the last sector of the disk! You should use 'v' to\n"
<< "verify disk integrity, and perhaps options on the experts' menu to repair\n"
<< "the disk.\n";
- allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK;
} // if/else
if (!allOK)
state = gpt_invalid;
@@ -850,7 +842,6 @@
int GPTData::SaveGPTData(int quiet) {
int allOK = 1, littleEndian;
char answer;
-// uint64_t secondTable;
uint32_t numParts;
littleEndian = IsLittleEndian();
@@ -1129,6 +1120,91 @@
return allOK;
} // GPTData::LoadGPTBackup()
+int GPTData::SaveMBR(void) {
+ return protectiveMBR.WriteMBRData();
+} // GPTData::SaveMBR()
+
+// This function destroys the on-disk GPT structures, but NOT the on-disk
+// MBR.
+// Returns 1 if the operation succeeds, 0 if not.
+int GPTData::DestroyGPT(void) {
+ int i, sum, tableSize, allOK = 1;
+ uint8_t blankSector[512];
+ uint8_t* emptyTable;
+
+ for (i = 0; i < 512; i++) {
+ blankSector[i] = 0;
+ } // for
+
+ if (myDisk.OpenForWrite()) {
+ if (!myDisk.Seek(mainHeader.currentLBA))
+ allOK = 0;
+ if (myDisk.Write(blankSector, 512) != 512) { // blank it out
+ cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n";
+ allOK = 0;
+ } // if
+ if (!myDisk.Seek(mainHeader.partitionEntriesLBA))
+ allOK = 0;
+ tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
+ emptyTable = new uint8_t[tableSize];
+ for (i = 0; i < tableSize; i++)
+ emptyTable[i] = 0;
+ if (allOK) {
+ sum = myDisk.Write(emptyTable, tableSize);
+ if (sum != tableSize) {
+ cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n";
+ allOK = 0;
+ } // if write failed
+ } // if
+ if (!myDisk.Seek(secondHeader.partitionEntriesLBA))
+ allOK = 0;
+ if (allOK) {
+ sum = myDisk.Write(emptyTable, tableSize);
+ if (sum != tableSize) {
+ cerr << "Warning! GPT backup partition table not overwritten! Error is "
+ << errno << "\n";
+ allOK = 0;
+ } // if wrong size written
+ } // if
+ if (!myDisk.Seek(secondHeader.currentLBA))
+ allOK = 0;
+ if (allOK) {
+ if (myDisk.Write(blankSector, 512) != 512) { // blank it out
+ cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n";
+ allOK = 0;
+ } // if
+ } // if
+ myDisk.DiskSync();
+ myDisk.Close();
+ cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n"
+ << "other utilities.\n";
+ delete[] emptyTable;
+ } else {
+ cerr << "Problem opening " << device << " for writing! Program will now terminate.\n";
+ } // if/else (fd != -1)
+ return (allOK);
+} // GPTDataTextUI::DestroyGPT()
+
+// Wipe MBR data from the disk (zero it out completely)
+// Returns 1 on success, 0 on failure.
+int GPTData::DestroyMBR(void) {
+ int allOK = 1, i;
+ uint8_t blankSector[512];
+
+ for (i = 0; i < 512; i++)
+ blankSector[i] = 0;
+
+ if (myDisk.OpenForWrite()) {
+ if (myDisk.Seek(0)) {
+ if (myDisk.Write(blankSector, 512) != 512)
+ allOK = 0;
+ } else allOK = 0;
+ } else allOK = 0;
+ if (!allOK)
+ cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n";
+ return allOK;
+} // GPTData::DestroyMBR(void)
+
// Tell user whether Apple Partition Map (APM) was discovered....
void GPTData::ShowAPMState(void) {
if (apmFound)
@@ -1176,20 +1252,6 @@
} // for
} // GPTData::DisplayGPTData()
-// Get partition number from user and then call ShowPartDetails(partNum)
-// to show its detailed information
-void GPTData::ShowDetails(void) {
- int partNum;
- uint32_t low, high;
-
- if (GetPartRange(&low, &high) > 0) {
- partNum = GetPartNum();
- ShowPartDetails(partNum);
- } else {
- cout << "No partitions\n";
- } // if/else
-} // GPTData::ShowDetails()
-
// Show detailed information on the specified partition
void GPTData::ShowPartDetails(uint32_t partNum) {
if (partitions[partNum].GetFirstLBA() != 0) {
@@ -1199,220 +1261,6 @@
} // if
} // GPTData::ShowPartDetails()
-/*********************************************************************
- * *
- * Begin functions that obtain information from the users, and often *
- * do something with that information (call other functions) *
- * *
- *********************************************************************/
-
-// Prompts user for partition number and returns the result.
-uint32_t GPTData::GetPartNum(void) {
- uint32_t partNum;
- uint32_t low, high;
- char prompt[255];
-
- if (GetPartRange(&low, &high) > 0) {
- sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
- partNum = GetNumber(low + 1, high + 1, low, prompt);
- } else partNum = 1;
- return (partNum - 1);
-} // GPTData::GetPartNum()
-
-// What it says: Resize the partition table. (Default is 128 entries.)
-void GPTData::ResizePartitionTable(void) {
- int newSize;
- char prompt[255];
- uint32_t curLow, curHigh;
-
- cout << "Current partition table size is " << mainHeader.numParts << ".\n";
- GetPartRange(&curLow, &curHigh);
- curHigh++; // since GetPartRange() returns numbers starting from 0...
- // There's no point in having fewer than four partitions....
- if (curHigh < 4)
- curHigh = 4;
- sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh,
- (int) NUM_GPT_ENTRIES);
- newSize = GetNumber(4, 65535, 128, prompt);
- if (newSize < 128) {
- cout << "Caution: The partition table size should officially be 16KB or larger,\n"
- << "which works out to 128 entries. In practice, smaller tables seem to\n"
- << "work with most OSes, but this practice is risky. I'm proceeding with\n"
- << "the resize, but you may want to reconsider this action and undo it.\n\n";
- } // if
- SetGPTSize(newSize);
-} // GPTData::ResizePartitionTable()
-
-// Interactively create a partition
-void GPTData::CreatePartition(void) {
- uint64_t firstBlock, firstInLargest, lastBlock, sector;
- uint32_t firstFreePart = 0;
- char prompt[255];
- int partNum;
-
- // Find first free partition...
- while (partitions[firstFreePart].GetFirstLBA() != 0) {
- firstFreePart++;
- } // while
-
- if (((firstBlock = FindFirstAvailable()) != 0) &&
- (firstFreePart < mainHeader.numParts)) {
- lastBlock = FindLastAvailable();
- firstInLargest = FindFirstInLargest();
-
- // Get partition number....
- do {
- sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1,
- mainHeader.numParts, firstFreePart + 1);
- partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
- firstFreePart + 1, prompt) - 1;
- if (partitions[partNum].GetFirstLBA() != 0)
- cout << "partition " << partNum + 1 << " is in use.\n";
- } while (partitions[partNum].GetFirstLBA() != 0);
-
- // Get first block for new partition...
- sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
- (unsigned long long) firstBlock, (unsigned long long) lastBlock,
- (unsigned long long) firstInLargest);
- do {
- sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
- } while (IsFree(sector) == 0);
- Align(§or); // Align sector to correct multiple
- firstBlock = sector;
-
- // Get last block for new partitions...
- lastBlock = FindLastInFree(firstBlock);
- sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
- (unsigned long long) firstBlock, (unsigned long long) lastBlock,
- (unsigned long long) lastBlock);
- do {
- sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
- } while (IsFree(sector) == 0);
- lastBlock = sector;
-
- firstFreePart = CreatePartition(partNum, firstBlock, lastBlock);
- partitions[partNum].ChangeType();
- partitions[partNum].SetDefaultDescription();
- } else {
- cout << "No free sectors available\n";
- } // if/else
-} // GPTData::CreatePartition()
-
-// Interactively delete a partition (duh!)
-void GPTData::DeletePartition(void) {
- int partNum;
- uint32_t low, high;
- char prompt[255];
-
- if (GetPartRange(&low, &high) > 0) {
- sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
- partNum = GetNumber(low + 1, high + 1, low, prompt);
- DeletePartition(partNum - 1);
- } else {
- cout << "No partitions\n";
- } // if/else
-} // GPTData::DeletePartition()
-
-// Prompt user for a partition number, then change its type code
-// using ChangeGPTType(struct GPTPartition*) function.
-void GPTData::ChangePartType(void) {
- int partNum;
- uint32_t low, high;
-
- if (GetPartRange(&low, &high) > 0) {
- partNum = GetPartNum();
- partitions[partNum].ChangeType();
- } else {
- cout << "No partitions\n";
- } // if/else
-} // GPTData::ChangePartType()
-
-// Partition attributes seem to be rarely used, but I want a way to
-// adjust them for completeness....
-void GPTData::SetAttributes(uint32_t partNum) {
- Attributes theAttr;
-
- theAttr.SetAttributes(partitions[partNum].GetAttributes());
- theAttr.DisplayAttributes();
- theAttr.ChangeAttributes();
- partitions[partNum].SetAttributes(theAttr.GetAttributes());
-} // GPTData::SetAttributes()
-
-// This function destroys the on-disk GPT structures. Returns 1 if the
-// user confirms destruction, 0 if the user aborts.
-// If prompt == 0, don't ask user about proceeding and do NOT wipe out
-// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
-// If prompt == -1, don't ask user about proceeding and DO wipe out
-// MBR.
-int GPTData::DestroyGPT(int prompt) {
- int i, sum, tableSize;
- uint8_t blankSector[512], goOn = 'Y', blank = 'N';
- uint8_t* emptyTable;
-
- for (i = 0; i < 512; i++) {
- blankSector[i] = 0;
- } // for
-
- if (((apmFound) || (bsdFound)) && (prompt > 0)) {
- cout << "WARNING: APM or BSD disklabel structures detected! This operation could\n"
- << "damage any APM or BSD partitions on this disk!\n";
- } // if APM or BSD
- if (prompt > 0) {
- cout << "\a\aAbout to wipe out GPT on " << device << ". Proceed? ";
- goOn = GetYN();
- } // if
- if (goOn == 'Y') {
- if (myDisk.OpenForWrite(device)) {
- myDisk.Seek(mainHeader.currentLBA); // seek to GPT header
- if (myDisk.Write(blankSector, 512) != 512) { // blank it out
- cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n";
- } // if
- myDisk.Seek(mainHeader.partitionEntriesLBA); // seek to partition table
- tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
- emptyTable = new uint8_t[tableSize];
- for (i = 0; i < tableSize; i++)
- emptyTable[i] = 0;
- sum = myDisk.Write(emptyTable, tableSize);
- if (sum != tableSize)
- cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n";
- myDisk.Seek(secondHeader.partitionEntriesLBA); // seek to partition table
- sum = myDisk.Write(emptyTable, tableSize);
- if (sum != tableSize)
- cerr << "Warning! GPT backup partition table not overwritten! Error is " << errno << "\n";
- myDisk.Seek(secondHeader.currentLBA); // seek to GPT header
- if (myDisk.Write(blankSector, 512) != 512) { // blank it out
- cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n";
- } // if
- if (prompt > 0) {
- cout << "Blank out MBR? ";
- blank = GetYN();
- } // if
- // Note on below: Touch the MBR only if the user wants it completely
- // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
- // the MBR, but this could wipe out a valid MBR that the program
- // had subsequently discarded (say, if it conflicted with older GPT
- // structures).
- if ((blank == 'Y') || (prompt < 0)) {
- myDisk.Seek(0);
- if (myDisk.Write(blankSector, 512) != 512) { // blank it out
- cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n";
- } // if
- } else {
- cout << "MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
- << "with fdisk or another tool.\n";
- } // if/else
- myDisk.DiskSync();
- myDisk.Close();
- cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n"
- << "other utilities. Program will now terminate.\n";
- delete[] emptyTable;
- } else {
- cerr << "Problem opening " << device << " for writing! Program will now terminate.\n";
- } // if/else (fd != -1)
- } // if (goOn == 'Y')
- return (goOn == 'Y');
-} // GPTData::DestroyGPT()
-
/**************************************************************************
* *
* Partition table transformation functions (MBR or BSD disklabel to GPT) *
@@ -1420,13 +1268,14 @@
* *
**************************************************************************/
-// Examines the MBR & GPT data, and perhaps asks the user questions, to
-// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt),
-// or create a new set of partitions (use_new)
+// Examines the MBR & GPT data to determine which set of data to use: the
+// MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create
+// a new set of partitions (use_new). A return value of use_abort indicates
+// that this function couldn't determine what to do. Overriding functions
+// in derived classes may ask users questions in such cases.
WhichToUse GPTData::UseWhichPartitions(void) {
WhichToUse which = use_new;
MBRValidity mbrState;
- int answer;
mbrState = protectiveMBR.GetValidity();
@@ -1470,49 +1319,20 @@
which = use_gpt;
} // if
if ((state == gpt_valid) && (mbrState == mbr)) {
- if (!beQuiet) {
- cout << "Found valid MBR and GPT. Which do you want to use?\n";
- answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
- if (answer == 1) {
- which = use_mbr;
- } else if (answer == 2) {
- which = use_gpt;
- cout << "Using GPT and creating fresh protective MBR.\n";
- } else which = use_new;
- } else which = use_abort;
+ which = use_abort;
} // if
- // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
- // problems)
if (state == gpt_corrupt) {
- if (beQuiet) {
- which = use_abort;
+ if (mbrState == gpt) {
+ cout << "\a\a****************************************************************************\n"
+ << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
+ << "verification and recovery are STRONGLY recommended.\n"
+ << "****************************************************************************\n";
+ which = use_gpt;
} else {
- if ((mbrState == mbr) || (mbrState == hybrid)) {
- cout << "Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
- << "GPT MAY permit recovery of GPT data.)\n";
- answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
- if (answer == 1) {
- which = use_mbr;
- } else if (answer == 2) {
- which = use_gpt;
- } else which = use_new;
- } else if (mbrState == invalid) {
- cout << "Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
- << "GPT MAY permit recovery of GPT data.)\n";
- answer = GetNumber(1, 2, 1, " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: ");
- if (answer == 1) {
- which = use_gpt;
- } else which = use_new;
- } else { // corrupt GPT, MBR indicates it's a GPT disk....
- cout << "\a\a****************************************************************************\n"
- << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
- << "verification and recovery are STRONGLY recommended.\n"
- << "****************************************************************************\n";
- which = use_gpt;
- } // if/else/else
- } // else (beQuiet)
- } // if (corrupt GPT)
+ which = use_abort;
+ } // if/else MBR says disk is GPT
+ } // if GPT corrupt
if (which == use_new)
cout << "Creating new GPT entries.\n";
@@ -1520,13 +1340,14 @@
return which;
} // UseWhichPartitions()
-// Convert MBR partition table into GPT form
-int GPTData::XFormPartitions(void) {
+// Convert MBR partition table into GPT form.
+void GPTData::XFormPartitions(void) {
int i, numToConvert;
uint8_t origType;
// Clear out old data & prepare basics....
ClearGPTData();
+ protectiveMBR.EmptyBootloader();
// Convert the smaller of the # of GPT or MBR partitions
if (mainHeader.numParts > (MAX_MBR_PARTS))
@@ -1549,71 +1370,60 @@
// Record that all original CRCs were OK so as not to raise flags
// when doing a disk verification
mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
-
- return (1);
} // GPTData::XFormPartitions()
// Transforms BSD disklabel on the specified partition (numbered from 0).
-// If an invalid partition number is given, the program prompts for one.
-// (Default for i is -1; called without an option, it therefore prompts.)
+// If an invalid partition number is given, the program does nothing.
// Returns the number of new partitions created.
-int GPTData::XFormDisklabel(int i) {
- uint32_t low, high, partNum, startPart;
- uint16_t hexCode;
+int GPTData::XFormDisklabel(uint32_t partNum) {
+ uint32_t low, high;
int goOn = 1, numDone = 0;
BSDData disklabel;
- if (GetPartRange(&low, &high) != 0) {
- if ((i < (int) low) || (i > (int) high))
- partNum = GetPartNum();
- else
- partNum = (uint32_t) i;
+ if (GetPartRange(&low, &high) == 0) {
+ goOn = 0;
+ cout << "No partitions!\n";
+ } // if
+ if (partNum > high) {
+ goOn = 0;
+ cout << "Specified partition is invalid!\n";
+ } // if
- // Find the partition after the last used one
- startPart = high + 1;
-
- // Now see if the specified partition has a BSD type code....
- hexCode = partitions[partNum].GetHexType();
- if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
- cout << "Specified partition doesn't have a disklabel partition type "
- << "code.\nContinue anyway? ";
- goOn = (GetYN() == 'Y');
- } // if
-
- // If all is OK, read the disklabel and convert it.
- if (goOn) {
- goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(),
- partitions[partNum].GetLastLBA());
- if ((goOn) && (disklabel.IsDisklabel())) {
- numDone = XFormDisklabel(&disklabel, startPart);
- if (numDone == 1)
- cout << "Converted " << numDone << " BSD partition.\n";
- else
- cout << "Converted " << numDone << " BSD partitions.\n";
- } else {
- cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n";
- } // if/else
- } // if
- if (numDone > 0) { // converted partitions; delete carrier
- partitions[partNum].BlankPartition();
- } // if
- } else {
- cout << "No partitions\n";
- } // if/else
+ // If all is OK, read the disklabel and convert it.
+ if (goOn) {
+ goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(),
+ partitions[partNum].GetLastLBA());
+ if ((goOn) && (disklabel.IsDisklabel())) {
+ numDone = XFormDisklabel(&disklabel);
+ if (numDone == 1)
+ cout << "Converted 1 BSD partition.\n";
+ else
+ cout << "Converted " << numDone << " BSD partitions.\n";
+ } else {
+ cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n";
+ } // if/else
+ } // if
+ if (numDone > 0) { // converted partitions; delete carrier
+ partitions[partNum].BlankPartition();
+ } // if
return numDone;
} // GPTData::XFormDisklable(int i)
// Transform the partitions on an already-loaded BSD disklabel...
-int GPTData::XFormDisklabel(BSDData* disklabel, uint32_t startPart) {
- int i, numDone = 0;
+int GPTData::XFormDisklabel(BSDData* disklabel) {
+ int i, partNum = 0, numDone = 0;
- if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
- (startPart < mainHeader.numParts)) {
+ if (disklabel->IsDisklabel()) {
for (i = 0; i < disklabel->GetNumParts(); i++) {
- partitions[i + startPart] = disklabel->AsGPT(i);
- if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
- numDone++;
+ partNum = FindFirstFreePart();
+ if (partNum >= 0) {
+ partitions[partNum] = disklabel->AsGPT(i);
+ if (partitions[partNum].IsUsed())
+ numDone++;
+ } // if
} // for
+ if (partNum == -1)
+ cerr << "Warning! Too many partitions to convert!\n";
} // if
// Record that all original CRCs were OK so as not to raise flags
@@ -1623,16 +1433,13 @@
return numDone;
} // GPTData::XFormDisklabel(BSDData* disklabel)
-// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
-// functions. Returns 1 if operation was successful.
+// Add one GPT partition to MBR. Used by PartsToMBR() functions. Created
+// partition has the active/bootable flag UNset and uses the GPT fdisk
+// type code divided by 0x0100 as the MBR type code.
+// Returns 1 if operation was 100% successful, 0 if there were ANY
+// problems.
int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
- int allOK = 1, typeCode, bootable;
- uint64_t length;
- char line[255];
- char* junk;
-
- cout.setf(ios::uppercase);
- cout.fill('0');
+ int allOK = 1;
if ((mbrPart < 0) || (mbrPart > 3)) {
cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n";
@@ -1651,156 +1458,60 @@
if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
cout << "Caution: Partition end point past 32-bit pointer boundary;"
<< " some OSes may\nreact strangely.\n";
- } // if partition ends past 32-bit (usually 2TiB) boundary
- do {
- cout << "Enter an MBR hex code (default " << hex;
- cout.width(2);
- cout << partitions[gptPart].GetHexType() / 0x0100 << "): ";
- junk = fgets(line, 255, stdin);
- if (line[0] == '\n')
- typeCode = partitions[gptPart].GetHexType() / 256;
- else
- sscanf(line, "%x", &typeCode);
- } while ((typeCode <= 0) || (typeCode > 255));
- cout << "Set the bootable flag? ";
- bootable = (GetYN() == 'Y');
- length = partitions[gptPart].GetLengthLBA();
+ } // if
protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
- (uint32_t) length, typeCode, bootable);
+ (uint32_t) partitions[gptPart].GetLengthLBA(),
+ partitions[gptPart].GetHexType() / 256, 0);
} else { // partition out of range
- cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR "
- << "partitions, or is\n too big; omitting it.\n";
+ if (allOK) // Display only if "else" triggered by out-of-bounds condition
+ cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR "
+ << "partitions, or is\n too big; omitting it.\n";
allOK = 0;
} // if/else
- cout.fill(' ');
return allOK;
} // GPTData::OnePartToMBR()
-// Convert the GPT to MBR form. This function is necessarily limited; it
-// handles at most four partitions and creates layouts that ignore CHS
-// geometries. Returns the number of converted partitions; if this value
-// is over 0, the calling function should call DestroyGPT() to destroy
-// the GPT data, and then exit.
-int GPTData::XFormToMBR(void) {
- char line[255];
- char* junk;
- int j, numParts, numConverted = 0;
- uint32_t i, partNums[4];
+// Convert up to four partitions to MBR form and return the number done.
+// Partitions are specified in an array of GPT partition numbers,
+// with an associated array of partition type codes. Both must be
+// at least four elements in size (longer is OK, but will be ignored).
+// A partition number of MBR_EFI_GPT means to place an EFI GPT
+// protective partition in that location in the table (the associated
+// mbrType[] should be 0xEE), and MBR_EMPTY means not to create a
+// partition in that table position. If the mbrType[] entry for a
+// partition is 0, a default entry is used, based on the GPT
+// partition type code.
+// Returns the number of partitions converted, NOT counting EFI GPT
+// protective partitions.
+int GPTData::PartsToMBR(const int *gptParts, const int *mbrTypes) {
+ int i, numConverted = 0;
- // Get the numbers of up to four partitions to add to the
- // hybrid MBR....
- numParts = CountParts();
- cout << "Counted " << numParts << " partitions.\n";
-
- // Prepare the MBR for conversion (empty it of existing partitions).
- protectiveMBR.EmptyMBR(0);
- protectiveMBR.SetDiskSize(diskSize);
-
- if (numParts > 4) { // Over four partitions; engage in triage
- cout << "Type from one to four GPT partition numbers, separated by spaces, to be\n"
- << "used in the MBR, in sequence: ";
- junk = fgets(line, 255, stdin);
- numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
- &partNums[2], &partNums[3]);
- } else { // Four or fewer partitions; convert them all
- i = j = 0;
- while ((j < numParts) && (i < mainHeader.numParts)) {
- if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
- partNums[j++] = ++i; // flag it for conversion
- } else i++;
- } // while
- } // if/else
-
- for (i = 0; i < (uint32_t) numParts; i++) {
- j = partNums[i] - 1;
- cout << "\nCreating entry for partition #" << j + 1 << "\n";
- numConverted += OnePartToMBR(j, i);
- } // for
- cout << "MBR writing returned " << protectiveMBR.WriteMBRData(&myDisk) << "\n";
- return numConverted;
-} // GPTData::XFormToMBR()
-
-// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
-// OSes that don't understand GPT.
-void GPTData::MakeHybrid(void) {
- uint32_t partNums[3];
- char line[255];
- char* junk;
- int numParts, numConverted = 0, i, j, typeCode, mbrNum;
- char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
- char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
-
- cout << "\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....
- cout << "Type from one to three GPT partition numbers, separated by spaces, to be\n"
- << "added to the hybrid MBR, in sequence: ";
- junk = 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);
+ if ((gptParts != NULL) && (mbrTypes != NULL)) {
+ protectiveMBR.EmptyMBR();
protectiveMBR.SetDiskSize(diskSize);
- cout << "Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ";
- eeFirst = GetYN();
- } // if
-
- for (i = 0; i < numParts; i++) {
- j = partNums[i] - 1;
- cout << "\nCreating entry for partition #" << j + 1 << "\n";
- if (eeFirst == 'Y')
- mbrNum = i + 1;
- else
- mbrNum = i;
- numConverted += OnePartToMBR(j, mbrNum);
- } // for
-
- if ((numParts > 0) && (numConverted > 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.
- if (eeFirst == 'Y')
- mbrNum = 0;
- else
- mbrNum = numParts;
- protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
- protectiveMBR.SetHybrid();
-
- // ... and for good measure, if there are any partition spaces left,
- // optionally create another protective EFI partition to cover as much
- // space as possible....
+ // Do two passes, one to get "real" partitions and
+ // the next to create EFI GPT protective partition(s)
for (i = 0; i < 4; i++) {
- if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
- if (fillItUp == 'M') {
- cout << "\nUnused 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') {
- while ((typeCode <= 0) || (typeCode > 255)) {
- cout << "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.
- junk = fgets(line, 255, stdin);
- sscanf(line, "%x", &typeCode);
- if (line[0] == '\n')
- typeCode = 0;
- } // while
- protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition
- } // if (fillItUp == 'Y')
- } // if unused entry
- } // for (i = 0; i < 4; i++)
- } // if (numParts > 0)
-} // GPTData::MakeHybrid()
+ if (gptParts[i] >= 0) {
+ numConverted += OnePartToMBR((uint32_t) gptParts[i], i);
+ if (mbrTypes[i] != 0)
+ protectiveMBR.SetPartType(i, mbrTypes[i]);
+ } // if
+ } // for (regular partition pass)
+ for (i = 0; i < 4; i++) {
+ if (gptParts[i] == MBR_EFI_GPT) {
+ if (protectiveMBR.FindFirstAvailable() == UINT32_C(1)) {
+ protectiveMBR.MakePart(i, 1, protectiveMBR.FindLastInFree(1), mbrTypes[i]);
+ protectiveMBR.SetHybrid();
+ } else {
+ protectiveMBR.MakeBiggestPart(i, mbrTypes[i]);
+ } // if/else
+ } // if EFI GPT partition specified
+ } // for (0xEE pass)
+ } // if arrays were passed
+ return numConverted;
+} // GPTData::PartsToMBR()
+
/**********************************************************************
* *
@@ -1813,8 +1524,8 @@
// necessary, copies data if it already exists. Returns 1 if all goes
// well, 0 if an error is encountered.
int GPTData::SetGPTSize(uint32_t numEntries) {
- struct GPTPart* newParts;
- struct GPTPart* trash;
+ GPTPart* newParts;
+ GPTPart* trash;
uint32_t i, high, copyNum;
int allOK = 1;
@@ -1907,10 +1618,8 @@
return retval;
} // GPTData::DeletePartition(uint32_t partNum)
-// Non-interactively create a partition. Note that this function is overloaded
-// with another of the same name but different parameters; that one prompts
-// the user for data. This one returns 1 if the operation was successful, 0
-// if a problem was discovered.
+// Non-interactively create a partition.
+// Returns 1 if the operation was successful, 0 if a problem was discovered.
uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
int retval = 1; // assume there'll be no problems
@@ -1931,7 +1640,6 @@
// Sort the GPT entries, eliminating gaps and making for a logical
// ordering. Relies on QuickSortGPT() for the bulk of the work
void GPTData::SortGPT(void) {
- GPTPart temp;
uint32_t i, numFound, firstPart, lastPart;
// First, find the last partition with data, so as not to
@@ -1943,9 +1651,7 @@
i = 0;
while (i < lastPart) {
if (partitions[i].GetFirstLBA() == 0) {
- temp = partitions[i];
- partitions[i] = partitions[lastPart];
- partitions[lastPart] = temp;
+ SwapPartitions(i, lastPart);
do {
lastPart--;
} while ((lastPart > 0) && (partitions[lastPart].GetFirstLBA() == 0));
@@ -1959,9 +1665,51 @@
GetPartRange(&firstPart, &lastPart);
// Now call the recursive quick sort routine to do the real work....
- QuickSortGPT(partitions, 0, lastPart);
+ QuickSortGPT(0, lastPart);
} // GPTData::SortGPT()
+// Recursive quick sort algorithm for GPT partitions. Note that if there
+// are any empties in the specified range, they'll be sorted to the
+// start, resulting in a sorted set of partitions that begins with
+// partition 2, 3, or higher.
+void GPTData::QuickSortGPT(int start, int finish) {
+ uint64_t starterValue; // starting location of median partition
+ int left, right;
+
+ left = start;
+ right = finish;
+ starterValue = partitions[(start + finish) / 2].GetFirstLBA();
+ do {
+ while (partitions[left].GetFirstLBA() < starterValue)
+ left++;
+ while (partitions[right].GetFirstLBA() > starterValue)
+ right--;
+ if (left <= right)
+ SwapPartitions(left++, right--);
+ } while (left <= right);
+ if (start < right) QuickSortGPT(start, right);
+ if (finish > left) QuickSortGPT(left, finish);
+} // GPTData::QuickSortGPT()
+
+// Swap the contents of two partitions.
+// Returns 1 if successful, 0 if either partition is out of range
+// (that is, not a legal number; either or both can be empty).
+// Note that if partNum1 = partNum2 and this number is in range,
+// it will be considered successful.
+int GPTData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) {
+ GPTPart temp;
+ int allOK = 1;
+
+ if ((partNum1 < mainHeader.numParts) && (partNum2 < mainHeader.numParts)) {
+ if (partNum1 != partNum2) {
+ temp = partitions[partNum1];
+ partitions[partNum1] = partitions[partNum2];
+ partitions[partNum2] = temp;
+ } // if
+ } else allOK = 0; // partition numbers are valid
+ return allOK;
+} // GPTData::SwapPartitions()
+
// Set up data structures for entirely new set of partitions on the
// specified device. Returns 1 if OK, 0 if there were problems.
// Note that this function does NOT clear the protectiveMBR data
@@ -2151,9 +1899,9 @@
for (i = 0; i < mainHeader.numParts; i++) {
if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists
*high = i; // since we're counting up, set the high value
- // Set the low value only if it's not yet found...
+ // Set the low value only if it's not yet found...
if (*low == (mainHeader.numParts + 1)) *low = i;
- numFound++;
+ numFound++;
} // if
} // for
} // if
@@ -2165,12 +1913,26 @@
return numFound;
} // GPTData::GetPartRange()
+// Returns the value of the first free partition, or -1 if none is
+// unused.
+int GPTData::FindFirstFreePart(void) {
+ int i = 0;
+
+ if (partitions != NULL) {
+ while ((partitions[i].IsUsed()) && (i < (int) mainHeader.numParts))
+ i++;
+ if (i >= (int) mainHeader.numParts)
+ i = -1;
+ } else i = -1;
+ return i;
+} // GPTData::FindFirstFreePart()
+
// Returns the number of defined partitions.
uint32_t GPTData::CountParts(void) {
uint32_t i, counted = 0;
for (i = 0; i < mainHeader.numParts; i++) {
- if (partitions[i].GetFirstLBA() > 0)
+ if (partitions[i].IsUsed())
counted++;
} // for
return counted;
@@ -2318,22 +2080,21 @@
if ((sector >= partitions[i].GetFirstLBA()) &&
(sector <= partitions[i].GetLastLBA())) {
isFree = 0;
- } // if
+ } // if
} // for
if ((sector < mainHeader.firstUsableLBA) ||
(sector > mainHeader.lastUsableLBA)) {
isFree = 0;
- } // if
- return (isFree);
+ } // if
+ return (isFree);
} // GPTData::IsFree()
// Returns 1 if partNum is unused.
int GPTData::IsFreePartNum(uint32_t partNum) {
int retval = 1;
- if ((partNum >= 0) && (partNum < mainHeader.numParts)) {
- if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) ||
- (partitions[partNum].GetLastLBA() != UINT64_C(0))) {
+ if ((partNum < mainHeader.numParts) && (partitions != NULL)) {
+ if (partitions[partNum].IsUsed()) {
retval = 0;
} // if partition is in use
} else retval = 0;
@@ -2361,8 +2122,7 @@
ReverseBytes(&header->numParts, 4);
ReverseBytes(&header->sizeOfPartitionEntries, 4);
ReverseBytes(&header->partitionEntriesCRC, 4);
- ReverseBytes(&header->reserved2, GPT_RESERVED);
-// header->diskGUID.ReverseGUIDBytes();
+ ReverseBytes(header->reserved2, GPT_RESERVED);
} // GPTData::ReverseHeaderBytes()
// IMPORTANT NOTE: This function requires non-reversed mainHeader