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:
12
NEWS
12
NEWS
@@ -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
|
||||
sizes.
|
||||
|
||||
|
||||
8
gdisk.8
8
gdisk.8
@@ -466,6 +466,14 @@ boot.
|
||||
Show detailed partition information. This option is identical to the 'i'
|
||||
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
|
||||
.B l
|
||||
Change the sector alignment value. Disks with more logical sectors per
|
||||
|
||||
56
gpt.cc
56
gpt.cc
@@ -276,6 +276,23 @@ int GPTData::Verify(void) {
|
||||
<< "The 'e' option on the experts' menu may fix this problem.\n";
|
||||
} // 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)) {
|
||||
problems++;
|
||||
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.lastUsableLBA = secondHeader.lastUsableLBA;
|
||||
mainHeader.diskGUID = secondHeader.diskGUID;
|
||||
mainHeader.partitionEntriesLBA = UINT64_C(2);
|
||||
mainHeader.numParts = secondHeader.numParts;
|
||||
mainHeader.partitionEntriesLBA = secondHeader.firstUsableLBA - GetTableSizeInSectors();
|
||||
mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
|
||||
mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
|
||||
memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2));
|
||||
@@ -658,7 +675,7 @@ int GPTData::FindInsanePartitions(void) {
|
||||
} // if
|
||||
if (partitions[i].GetLastLBA() >= diskSize) {
|
||||
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
|
||||
} // for
|
||||
@@ -1441,6 +1458,8 @@ void GPTData::DisplayGPTData(void) {
|
||||
cout << "Logical sector size: " << blockSize << " bytes\n";
|
||||
cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\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
|
||||
<< ", last usable sector is " << mainHeader.lastUsableLBA << "\n";
|
||||
totalFree = FindFreeBlocks(&i, &temp);
|
||||
@@ -1732,7 +1751,7 @@ int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
|
||||
partitions = newParts;
|
||||
} // if/else existing partitions
|
||||
numParts = numEntries;
|
||||
mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + (((numEntries * GPT_SIZE) % blockSize) != 0) + 2 ;
|
||||
mainHeader.firstUsableLBA = GetTableSizeInSectors() + mainHeader.partitionEntriesLBA;
|
||||
secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
|
||||
MoveSecondHeaderToEnd();
|
||||
if (diskSize > 0)
|
||||
@@ -1747,6 +1766,23 @@ int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
|
||||
return (allOK);
|
||||
} // 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
|
||||
void GPTData::BlankPartitions(void) {
|
||||
uint32_t i;
|
||||
@@ -2121,6 +2157,20 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) {
|
||||
return (first);
|
||||
} // 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
|
||||
// space on the disk. Returns 0 if there are no available blocks left
|
||||
uint64_t GPTData::FindFirstInLargest(void) {
|
||||
|
||||
4
gpt.h
4
gpt.h
@@ -140,6 +140,7 @@ public:
|
||||
|
||||
// Adjust GPT structures WITHOUT user interaction...
|
||||
int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1);
|
||||
int MoveMainTable(uint64_t pteSector);
|
||||
void BlankPartitions(void);
|
||||
int DeletePartition(uint32_t partNum);
|
||||
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 FindFirstFreePart(void);
|
||||
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 GetSecondHeaderLBA(void) {return secondHeader.currentLBA;}
|
||||
uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;}
|
||||
@@ -176,6 +179,7 @@ public:
|
||||
|
||||
// Find information about free space
|
||||
uint64_t FindFirstAvailable(uint64_t start = 0);
|
||||
uint64_t FindFirstUsedLBA(void);
|
||||
uint64_t FindFirstInLargest(void);
|
||||
uint64_t FindLastAvailable();
|
||||
uint64_t FindLastInFree(uint64_t start);
|
||||
|
||||
11
gptcl.cc
11
gptcl.cc
@@ -64,7 +64,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||
GPTData secondDevice;
|
||||
int opt, numOptions = 0, saveData = 0, neverSaveData = 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
|
||||
char *device;
|
||||
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", ""},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"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...]"},
|
||||
@@ -262,6 +263,14 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||
case 'i':
|
||||
ShowPartDetails(infoPartNum - 1);
|
||||
break;
|
||||
case 'j':
|
||||
if (MoveMainTable(mainTableLBA)) {
|
||||
JustLooking(0);
|
||||
saveData = 1;
|
||||
} else {
|
||||
neverSaveData = 1;
|
||||
} // if/else
|
||||
break;
|
||||
case 'l':
|
||||
LoadBackupFile(backupFile, saveData, neverSaveData);
|
||||
free(backupFile);
|
||||
|
||||
25
gpttext.cc
25
gpttext.cc
@@ -132,7 +132,7 @@ int GPTDataTextUI::XFormDisklabel(void) {
|
||||
numDone = GPTData::XFormDisklabel(partNum);
|
||||
|
||||
return numDone;
|
||||
} // GPTData::XFormDisklabel(void)
|
||||
} // GPTDataTextUI::XFormDisklabel(void)
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
@@ -179,6 +179,24 @@ void GPTDataTextUI::ResizePartitionTable(void) {
|
||||
SetGPTSize(newSize);
|
||||
} // 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
|
||||
void GPTDataTextUI::CreatePartition(void) {
|
||||
uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector;
|
||||
@@ -811,6 +829,9 @@ void GPTDataTextUI::ExpertsMenu(string filename) {
|
||||
case 'i': case 'I':
|
||||
ShowDetails();
|
||||
break;
|
||||
case 'j': case 'J':
|
||||
MoveMainTable();
|
||||
break;
|
||||
case 'l': case 'L':
|
||||
prompt.seekp(0);
|
||||
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 << "d\tdisplay the sector alignment value\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 << "h\trecompute CHS values in protective/hybrid MBR\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 << "m\treturn to main menu\n";
|
||||
cout << "n\tcreate a new protective MBR\n";
|
||||
|
||||
@@ -42,6 +42,7 @@ class GPTDataTextUI : public GPTData {
|
||||
// Request information from the user (& possibly do something with it)
|
||||
uint32_t GetPartNum(void);
|
||||
void ResizePartitionTable(void);
|
||||
void MoveMainTable(void);
|
||||
void CreatePartition(void);
|
||||
void DeletePartition(void);
|
||||
void ChangePartType(void);
|
||||
|
||||
8
sgdisk.8
8
sgdisk.8
@@ -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
|
||||
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
|
||||
.B \-l, \-\-load\-backup=file
|
||||
Load partition data from a backup file. This option is the reverse of the
|
||||
|
||||
@@ -67,8 +67,8 @@ string ReadString(void) {
|
||||
// If user provides no input, def (default value) is returned.
|
||||
// (If def is outside of the low-high range, an explicit response
|
||||
// is required.)
|
||||
int GetNumber(int low, int high, int def, const string & prompt) {
|
||||
int response, num;
|
||||
uint64_t GetNumber(uint64_t low, uint64_t high, uint64_t def, const string & prompt) {
|
||||
uint64_t response, num;
|
||||
char line[255];
|
||||
|
||||
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);
|
||||
if (!cin.good())
|
||||
exit(5);
|
||||
num = sscanf(line, "%d", &response);
|
||||
num = sscanf(line, "%ld", &response);
|
||||
if (num == 1) { // user provided a response
|
||||
if ((response < low) || (response > high))
|
||||
cout << "Value out of range\n";
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
using namespace std;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user