diff --git a/NEWS b/NEWS index 3e2f1cf..31e9a15 100644 --- a/NEWS +++ b/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. diff --git a/gdisk.8 b/gdisk.8 index 272da46..6a945e1 100644 --- a/gdisk.8 +++ b/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 diff --git a/gpt.cc b/gpt.cc index d0a46c6..44f5fb4 100644 --- a/gpt.cc +++ b/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) { diff --git a/gpt.h b/gpt.h index e9afd06..2528b92 100644 --- a/gpt.h +++ b/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); diff --git a/gptcl.cc b/gptcl.cc index 920dd44..243b4d9 100644 --- a/gptcl.cc +++ b/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); diff --git a/gpttext.cc b/gpttext.cc index 718b99b..732d861 100644 --- a/gpttext.cc +++ b/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"; diff --git a/gpttext.h b/gpttext.h index 385fa27..98e59af 100644 --- a/gpttext.h +++ b/gpttext.h @@ -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); diff --git a/sgdisk.8 b/sgdisk.8 index d073f33..274ceb3 100644 --- a/sgdisk.8 +++ b/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 diff --git a/support.cc b/support.cc index 0ff3485..d96da2d 100644 --- a/support.cc +++ b/support.cc @@ -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"; diff --git a/support.h b/support.h index 078fef2..f607d4a 100644 --- a/support.h +++ b/support.h @@ -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);