Initial commit of support for moving main partition table to arbitrary

location on disk (within limits based on existing partitions).
This commit is contained in:
Rod Smith
2017-07-21 21:48:13 -04:00
parent e7452642ac
commit 503e9ada12
10 changed files with 123 additions and 10 deletions

12
NEWS
View File

@@ -1,6 +1,16 @@
1.0.2 (?/??/2016): 1.0.2 (?/??/2017):
------------------ ------------------
- Added new option: 'j' on the experts' menu in gdisk;
'-j/--move-main-table={sector}' in sgdisk. This option enables relocating
the main partition table from sector 2 (the default location) to somewhere
else on the disk. The main reason I know of to do this is if the disk is
to be used with a system-on-chip (SoC) computer, some of which require the
boot loader to be located at sector 2. If you pass this option the default
value of 2, it has the effect of reducing the padding placed between the
main partition table and the first usable sector value created by the
Linux fdisk tool.
- Updated man pages with new recommendations for ESP and BIOS Boot Partition - Updated man pages with new recommendations for ESP and BIOS Boot Partition
sizes. sizes.

View File

@@ -466,6 +466,14 @@ boot.
Show detailed partition information. This option is identical to the 'i' Show detailed partition information. This option is identical to the 'i'
option on the main menu. option on the main menu.
.TP
.B j
Adjust the location of the main partition table. This value is normally 2,
but it may need to be increased in some cases, such as when a
system\-on\-chip (SoC) is hard\-coded to read boot code from sector 2. I
recommend against adjusting this value unless doing so is absolutely
necessary.
.TP .TP
.B l .B l
Change the sector alignment value. Disks with more logical sectors per Change the sector alignment value. Disks with more logical sectors per

56
gpt.cc
View File

@@ -276,6 +276,23 @@ int GPTData::Verify(void) {
<< "The 'e' option on the experts' menu may fix this problem.\n"; << "The 'e' option on the experts' menu may fix this problem.\n";
} // if } // if
// Check the main and backup partition tables for overlap with things
if (mainHeader.partitionEntriesLBA + GetTableSizeInSectors() > mainHeader.firstUsableLBA) {
problems++;
cout << "\nProblem: Main partition table extends past the first usable LBA.\n"
<< "Using 'j' on the experts' menu may enable fixing this problem.\n";
} // if
if (mainHeader.partitionEntriesLBA < 2) {
problems++;
cout << "\nProblem: Main partition table appears impossibly early on the disk.\n"
<< "Using 'j' on the experts' menu may enable fixing this problem.\n";
} // if
if (secondHeader.partitionEntriesLBA + GetTableSizeInSectors() > secondHeader.currentLBA) {
problems++;
cout << "\nProblem: The backup partition table overlaps the backup header.\n"
<< "Using 'e' on the experts' menu may fix this problem.\n";
} // if
if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) { if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) {
problems++; problems++;
cout << "\nProblem: GPT claims the disk is larger than it is! (Claimed last usable\n" cout << "\nProblem: GPT claims the disk is larger than it is! (Claimed last usable\n"
@@ -552,8 +569,8 @@ void GPTData::RebuildMainHeader(void) {
mainHeader.firstUsableLBA = secondHeader.firstUsableLBA; mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
mainHeader.lastUsableLBA = secondHeader.lastUsableLBA; mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
mainHeader.diskGUID = secondHeader.diskGUID; mainHeader.diskGUID = secondHeader.diskGUID;
mainHeader.partitionEntriesLBA = UINT64_C(2);
mainHeader.numParts = secondHeader.numParts; mainHeader.numParts = secondHeader.numParts;
mainHeader.partitionEntriesLBA = secondHeader.firstUsableLBA - GetTableSizeInSectors();
mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries; mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC; mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2)); memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2));
@@ -658,7 +675,7 @@ int GPTData::FindInsanePartitions(void) {
} // if } // if
if (partitions[i].GetLastLBA() >= diskSize) { if (partitions[i].GetLastLBA() >= diskSize) {
problems++; problems++;
cout << "\nProblem: partition " << i + 1 << " is too big for the disk.\n"; cout << "\nProblem: partition " << i + 1 << " is too big for the disk.\n";
} // if } // if
} // if } // if
} // for } // for
@@ -1441,6 +1458,8 @@ void GPTData::DisplayGPTData(void) {
cout << "Logical sector size: " << blockSize << " bytes\n"; cout << "Logical sector size: " << blockSize << " bytes\n";
cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n"; cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n";
cout << "Partition table holds up to " << numParts << " entries\n"; cout << "Partition table holds up to " << numParts << " entries\n";
cout << "Main partition table begins at sector " << mainHeader.partitionEntriesLBA
<< " and ends at sector " << mainHeader.partitionEntriesLBA + GetTableSizeInSectors() - 1 << "\n";
cout << "First usable sector is " << mainHeader.firstUsableLBA cout << "First usable sector is " << mainHeader.firstUsableLBA
<< ", last usable sector is " << mainHeader.lastUsableLBA << "\n"; << ", last usable sector is " << mainHeader.lastUsableLBA << "\n";
totalFree = FindFreeBlocks(&i, &temp); totalFree = FindFreeBlocks(&i, &temp);
@@ -1732,7 +1751,7 @@ int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
partitions = newParts; partitions = newParts;
} // if/else existing partitions } // if/else existing partitions
numParts = numEntries; numParts = numEntries;
mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + (((numEntries * GPT_SIZE) % blockSize) != 0) + 2 ; mainHeader.firstUsableLBA = GetTableSizeInSectors() + mainHeader.partitionEntriesLBA;
secondHeader.firstUsableLBA = mainHeader.firstUsableLBA; secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
MoveSecondHeaderToEnd(); MoveSecondHeaderToEnd();
if (diskSize > 0) if (diskSize > 0)
@@ -1747,6 +1766,23 @@ int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
return (allOK); return (allOK);
} // GPTData::SetGPTSize() } // GPTData::SetGPTSize()
// Change the start sector for the main partition table.
// Returns 1 on success, 0 on failure
int GPTData::MoveMainTable(uint64_t pteSector) {
uint64_t pteSize = GetTableSizeInSectors();
int retval = 1;
if ((pteSector >= 2) && ((pteSector + pteSize) <= FindFirstUsedLBA())) {
mainHeader.partitionEntriesLBA = pteSector;
mainHeader.firstUsableLBA = pteSector + pteSize;
RebuildSecondHeader();
} else {
cerr << "Unable to set the main partition table's location to " << pteSector << "!\n";
retval = 0;
} // if/else
return retval;
} // GPTData::MoveMainTable()
// Blank the partition array // Blank the partition array
void GPTData::BlankPartitions(void) { void GPTData::BlankPartitions(void) {
uint32_t i; uint32_t i;
@@ -2121,6 +2157,20 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) {
return (first); return (first);
} // GPTData::FindFirstAvailable() } // GPTData::FindFirstAvailable()
// Returns the LBA of the start of the first partition on the disk (by
// sector number), or 0 if there are no partitions defined.
uint64_t GPTData::FindFirstUsedLBA(void) {
uint32_t i;
uint64_t firstFound = UINT64_MAX;
for (i = 0; i < numParts; i++) {
if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() < firstFound)) {
firstFound = partitions[i].GetFirstLBA();
} // if
} // for
return firstFound;
} // GPTData::FindFirstUsedLBA()
// Finds the first available sector in the largest block of unallocated // Finds the first available sector in the largest block of unallocated
// space on the disk. Returns 0 if there are no available blocks left // space on the disk. Returns 0 if there are no available blocks left
uint64_t GPTData::FindFirstInLargest(void) { uint64_t GPTData::FindFirstInLargest(void) {

4
gpt.h
View File

@@ -140,6 +140,7 @@ public:
// Adjust GPT structures WITHOUT user interaction... // Adjust GPT structures WITHOUT user interaction...
int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1); int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1);
int MoveMainTable(uint64_t pteSector);
void BlankPartitions(void); void BlankPartitions(void);
int DeletePartition(uint32_t partNum); int DeletePartition(uint32_t partNum);
uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector); uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
@@ -162,6 +163,8 @@ public:
int GetPartRange(uint32_t* low, uint32_t* high); int GetPartRange(uint32_t* low, uint32_t* high);
int FindFirstFreePart(void); int FindFirstFreePart(void);
uint32_t GetNumParts(void) {return mainHeader.numParts;} uint32_t GetNumParts(void) {return mainHeader.numParts;}
uint64_t GetTableSizeInSectors(void) {return (((numParts * GPT_SIZE) / blockSize) +
(((numParts * GPT_SIZE) % blockSize) != 0)); }
uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;} uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;}
uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;} uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;}
uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;} uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;}
@@ -176,6 +179,7 @@ public:
// Find information about free space // Find information about free space
uint64_t FindFirstAvailable(uint64_t start = 0); uint64_t FindFirstAvailable(uint64_t start = 0);
uint64_t FindFirstUsedLBA(void);
uint64_t FindFirstInLargest(void); uint64_t FindFirstInLargest(void);
uint64_t FindLastAvailable(); uint64_t FindLastAvailable();
uint64_t FindLastInFree(uint64_t start); uint64_t FindLastInFree(uint64_t start);

View File

@@ -64,7 +64,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
GPTData secondDevice; GPTData secondDevice;
int opt, numOptions = 0, saveData = 0, neverSaveData = 0; int opt, numOptions = 0, saveData = 0, neverSaveData = 0;
int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0; int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0;
uint64_t low, high, startSector, endSector, sSize; uint64_t low, high, startSector, endSector, sSize, mainTableLBA;
uint64_t temp; // temporary variable; free to use in any case uint64_t temp; // temporary variable; free to use in any case
char *device; char *device;
string cmd, typeGUID, name; string cmd, typeGUID, name;
@@ -88,6 +88,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
{"randomize-guids", 'G', POPT_ARG_NONE, NULL, 'G', "randomize disk and partition GUIDs", ""}, {"randomize-guids", 'G', POPT_ARG_NONE, NULL, 'G', "randomize disk and partition GUIDs", ""},
{"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...]"}, {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...]"},
{"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"},
{"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"},
{"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"},
{"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""},
{"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"}, {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"},
@@ -262,6 +263,14 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
case 'i': case 'i':
ShowPartDetails(infoPartNum - 1); ShowPartDetails(infoPartNum - 1);
break; break;
case 'j':
if (MoveMainTable(mainTableLBA)) {
JustLooking(0);
saveData = 1;
} else {
neverSaveData = 1;
} // if/else
break;
case 'l': case 'l':
LoadBackupFile(backupFile, saveData, neverSaveData); LoadBackupFile(backupFile, saveData, neverSaveData);
free(backupFile); free(backupFile);

View File

@@ -132,7 +132,7 @@ int GPTDataTextUI::XFormDisklabel(void) {
numDone = GPTData::XFormDisklabel(partNum); numDone = GPTData::XFormDisklabel(partNum);
return numDone; return numDone;
} // GPTData::XFormDisklabel(void) } // GPTDataTextUI::XFormDisklabel(void)
/********************************************************************* /*********************************************************************
@@ -179,6 +179,24 @@ void GPTDataTextUI::ResizePartitionTable(void) {
SetGPTSize(newSize); SetGPTSize(newSize);
} // GPTDataTextUI::ResizePartitionTable() } // GPTDataTextUI::ResizePartitionTable()
// Move the main partition table (to enable some SoC boot loaders to place
// code at sector 2, for instance).
void GPTDataTextUI::MoveMainTable(void) {
uint64_t newStart, pteSize = GetTableSizeInSectors();
uint64_t maxValue = FindFirstUsedLBA() - pteSize;
ostringstream prompt;
cout << "Currently, main partition table begins at sector " << mainHeader.partitionEntriesLBA
<< " and ends at sector " << mainHeader.partitionEntriesLBA + pteSize - 1 << "\n";
prompt << "Enter new starting location (2 to " << maxValue << "; default is 2; 1 to abort): ";
newStart = GetNumber(1, maxValue, 2, prompt.str());
if (newStart != 1) {
GPTData::MoveMainTable(newStart);
} else {
cout << "Aborting change!\n";
} // if
} // GPTDataTextUI::MoveMainTable()
// Interactively create a partition // Interactively create a partition
void GPTDataTextUI::CreatePartition(void) { void GPTDataTextUI::CreatePartition(void) {
uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector; uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector;
@@ -811,6 +829,9 @@ void GPTDataTextUI::ExpertsMenu(string filename) {
case 'i': case 'I': case 'i': case 'I':
ShowDetails(); ShowDetails();
break; break;
case 'j': case 'J':
MoveMainTable();
break;
case 'l': case 'L': case 'l': case 'L':
prompt.seekp(0); prompt.seekp(0);
prompt << "Enter the sector alignment value (1-" << MAX_ALIGNMENT << ", default = " prompt << "Enter the sector alignment value (1-" << MAX_ALIGNMENT << ", default = "
@@ -878,9 +899,11 @@ void GPTDataTextUI::ShowExpertCommands(void) {
cout << "c\tchange partition GUID\n"; cout << "c\tchange partition GUID\n";
cout << "d\tdisplay the sector alignment value\n"; cout << "d\tdisplay the sector alignment value\n";
cout << "e\trelocate backup data structures to the end of the disk\n"; cout << "e\trelocate backup data structures to the end of the disk\n";
cout << "f\trandomize disk and partition unique GUIDs\n";
cout << "g\tchange disk GUID\n"; cout << "g\tchange disk GUID\n";
cout << "h\trecompute CHS values in protective/hybrid MBR\n"; cout << "h\trecompute CHS values in protective/hybrid MBR\n";
cout << "i\tshow detailed information on a partition\n"; cout << "i\tshow detailed information on a partition\n";
cout << "j\tmove the main partition table\n";
cout << "l\tset the sector alignment value\n"; cout << "l\tset the sector alignment value\n";
cout << "m\treturn to main menu\n"; cout << "m\treturn to main menu\n";
cout << "n\tcreate a new protective MBR\n"; cout << "n\tcreate a new protective MBR\n";

View File

@@ -42,6 +42,7 @@ class GPTDataTextUI : public GPTData {
// Request information from the user (& possibly do something with it) // Request information from the user (& possibly do something with it)
uint32_t GetPartNum(void); uint32_t GetPartNum(void);
void ResizePartitionTable(void); void ResizePartitionTable(void);
void MoveMainTable(void);
void CreatePartition(void); void CreatePartition(void);
void DeletePartition(void); void DeletePartition(void);
void ChangePartType(void); void ChangePartType(void);

View File

@@ -280,6 +280,14 @@ unique GUID and the translation of \fBsgdisk\fR's
internal partition type code to a plain type name. The \fI\-i\fR option internal partition type code to a plain type name. The \fI\-i\fR option
displays this information for a single partition. displays this information for a single partition.
.TP
.B \-j, \-\-adjust\-main\-table=sector
Adjust the location of the main partition table. This value is normally 2,
but it may need to be increased in some cases, such as when a
system\-on\-chip (SoC) is hard\-coded to read boot code from sector 2. I
recommend against adjusting this value unless doing so is absolutely
necessary.
.TP .TP
.B \-l, \-\-load\-backup=file .B \-l, \-\-load\-backup=file
Load partition data from a backup file. This option is the reverse of the Load partition data from a backup file. This option is the reverse of the

View File

@@ -67,8 +67,8 @@ string ReadString(void) {
// If user provides no input, def (default value) is returned. // If user provides no input, def (default value) is returned.
// (If def is outside of the low-high range, an explicit response // (If def is outside of the low-high range, an explicit response
// is required.) // is required.)
int GetNumber(int low, int high, int def, const string & prompt) { uint64_t GetNumber(uint64_t low, uint64_t high, uint64_t def, const string & prompt) {
int response, num; uint64_t response, num;
char line[255]; char line[255];
if (low != high) { // bother only if low and high differ... if (low != high) { // bother only if low and high differ...
@@ -77,7 +77,7 @@ int GetNumber(int low, int high, int def, const string & prompt) {
cin.getline(line, 255); cin.getline(line, 255);
if (!cin.good()) if (!cin.good())
exit(5); exit(5);
num = sscanf(line, "%d", &response); num = sscanf(line, "%ld", &response);
if (num == 1) { // user provided a response if (num == 1) { // user provided a response
if ((response < low) || (response > high)) if ((response < low) || (response > high))
cout << "Value out of range\n"; cout << "Value out of range\n";

View File

@@ -72,7 +72,7 @@
using namespace std; using namespace std;
string ReadString(void); string ReadString(void);
int GetNumber(int low, int high, int def, const string & prompt); uint64_t GetNumber(uint64_t low, uint64_t high, uint64_t def, const string & prompt);
char GetYN(void); char GetYN(void);
uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, uint64_t sSize, const std::string& prompt); uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, uint64_t sSize, const std::string& prompt);
uint64_t IeeeToInt(string IeeeValue, uint64_t sSize, uint64_t low, uint64_t high, uint64_t def = 0); uint64_t IeeeToInt(string IeeeValue, uint64_t sSize, uint64_t low, uint64_t high, uint64_t def = 0);