diff --git a/gpt.cc b/gpt.cc index 76cd9ad..d61064f 100644 --- a/gpt.cc +++ b/gpt.cc @@ -1500,7 +1500,7 @@ int GPTData::DestroyGPT(void) { cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n"; allOK = 0; } // if write failed - } // if + } // if if (!myDisk.Seek(secondHeader.partitionEntriesLBA)) allOK = 0; if (allOK) { @@ -1911,6 +1911,23 @@ int GPTData::MoveMainTable(uint64_t pteSector) { return retval; } // GPTData::MoveMainTable() +// Change the start sector for the secondary partition table. +// Returns 1 on success, 0 on failure +int GPTData::MoveSecondTable(uint64_t pteSector) { + uint64_t pteSize = GetTableSizeInSectors(); + int retval = 1; + + if ((pteSector > FindLastUsedLBA()) && ((pteSector + pteSize) < diskSize)) { + secondHeader.partitionEntriesLBA = pteSector; // (RebuildSecondHeader actually replaces this with lastUsableLBA+1) + mainHeader.lastUsableLBA = secondHeader.partitionEntriesLBA - UINT64_C(1); + RebuildSecondHeader(); + } else { + cerr << "Unable to set the secondary partition table's location to " << pteSector << "!\n"; + retval = 0; + } // if/else + return retval; +} // GPTData::MoveSecondTable() + // Blank the partition array void GPTData::BlankPartitions(void) { uint32_t i; @@ -2285,7 +2302,7 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) { } // 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. +// sector number), or UINT64_MAX if there are no partitions defined. uint64_t GPTData::FindFirstUsedLBA(void) { uint32_t i; uint64_t firstFound = UINT64_MAX; @@ -2298,6 +2315,20 @@ uint64_t GPTData::FindFirstUsedLBA(void) { return firstFound; } // GPTData::FindFirstUsedLBA() +// Returns the LBA of the end of the last partition on the disk (by +// sector number), or 0 if there are no partitions defined. +uint64_t GPTData::FindLastUsedLBA(void) { + uint32_t i; + uint64_t lastFound = 0; + + for (i = 0; i < numParts; i++) { + if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() > lastFound)) { + lastFound = partitions[i].GetFirstLBA(); + } // if + } // for + return lastFound; +} // GPTData::FindLastUsedLBA() + // 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 5d19372..17b3380 100644 --- a/gpt.h +++ b/gpt.h @@ -142,6 +142,7 @@ public: // Adjust GPT structures WITHOUT user interaction... int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1); int MoveMainTable(uint64_t pteSector); + int MoveSecondTable(uint64_t pteSector); void BlankPartitions(void); int DeletePartition(uint32_t partNum); uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector); @@ -158,7 +159,7 @@ public: void RecomputeCHS(void); int Align(uint64_t* sector); void SetProtectiveMBR(BasicMBRData & newMBR) {protectiveMBR = newMBR;} - + // Return data about the GPT structures.... WhichToUse GetState(void) {return whichWasUsed;} int GetPartRange(uint32_t* low, uint32_t* high); @@ -181,6 +182,7 @@ public: // Find information about free space uint64_t FindFirstAvailable(uint64_t start = 0); uint64_t FindFirstUsedLBA(void); + uint64_t FindLastUsedLBA(void); uint64_t FindFirstInLargest(void); uint64_t FindLastAvailable(); uint64_t FindLastInFree(uint64_t start, bool align = false); diff --git a/gptcl.cc b/gptcl.cc index 34c9421..232285a 100644 --- a/gptcl.cc +++ b/gptcl.cc @@ -68,7 +68,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { int opt, numOptions = 0, saveData = 0, neverSaveData = 0; int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0; int byteSwapPartNum = 0; - uint64_t low, high, startSector, endSector, sSize, mainTableLBA; + uint64_t low, high, startSector, endSector, sSize, mainTableLBA, secondTableLBA; uint64_t temp; // temporary variable; free to use in any case char *device; string cmd, typeGUID, name; @@ -85,7 +85,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { {"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""}, {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"}, {"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""}, - {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""}, + {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second/backup header to end of disk", ""}, {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""}, {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""}, {"first-aligned-in-largest", 'F', POPT_ARG_NONE, NULL, 'F', "show start of the largest free block, aligned", ""}, @@ -94,7 +94,8 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...][:EE]"}, {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, {"align-end", 'I', POPT_ARG_NONE, NULL, 'I', "align partition end points", ""}, - {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"}, + {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "change the start sector of the main partition table", "sector"}, + {"move-backup-table", 'k', POPT_ARG_INT, &secondTableLBA, 'k', "change the start sector of the second/backup 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...]"}, @@ -117,6 +118,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""}, {"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""}, POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } + // TODO: Incorrect(ly documented) (long) arguments are silently swallowed and seem to take the next argument with them! }; // Create popt context... @@ -280,13 +282,21 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { alignEnd = true; break; case 'j': - if (MoveMainTable(mainTableLBA)) { - JustLooking(0); - saveData = 1; - } else { - neverSaveData = 1; - } // if/else - break; + if (MoveMainTable(mainTableLBA)) { + JustLooking(0); + saveData = 1; + } else { + neverSaveData = 1; + } // if/else + break; + case 'k': + if (MoveSecondTable(secondTableLBA)) { + 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 170a169..43be9e5 100644 --- a/gpttext.cc +++ b/gpttext.cc @@ -197,6 +197,24 @@ void GPTDataTextUI::MoveMainTable(void) { } // if } // GPTDataTextUI::MoveMainTable() +// Move the backup partition table. +void GPTDataTextUI::MoveSecondTable(void) { + uint64_t newStart, pteSize = GetTableSizeInSectors(); + uint64_t minValue = FindLastUsedLBA(); + uint64_t maxValue = diskSize - 1 - pteSize; + ostringstream prompt; + + cout << "Currently, backup partition table begins at sector " << secondHeader.partitionEntriesLBA + << " and ends at sector " << secondHeader.partitionEntriesLBA + pteSize - 1 << "\n"; + prompt << "Enter new starting location (" << minValue << " to " << maxValue << "; default is " << minValue << "; 1 to abort): "; + newStart = GetNumber(1, maxValue, minValue, prompt.str()); + if (newStart != 1) { + GPTData::MoveSecondTable(newStart); + } else { + cout << "Aborting change!\n"; + } // if +} // GPTDataTextUI::MoveSecondTable() + // Interactively create a partition void GPTDataTextUI::CreatePartition(void) { uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector, lastAligned; @@ -698,7 +716,7 @@ void GPTDataTextUI::ShowCommands(void) { void GPTDataTextUI::RecoveryMenu(string filename) { uint32_t numParts; int goOn = 1, temp1; - + do { cout << "\nRecovery/transformation command (? for help): "; switch (ReadString()[0]) { @@ -824,7 +842,7 @@ void GPTDataTextUI::ExpertsMenu(string filename) { string guidStr, device; GUIDData aGUID; ostringstream prompt; - + do { cout << "\nExpert command (? for help): "; switch (ReadString()[0]) { @@ -873,6 +891,9 @@ void GPTDataTextUI::ExpertsMenu(string filename) { case 'j': case 'J': MoveMainTable(); break; + case 'k': case 'K': + MoveSecondTable(); + break; case 'l': case 'L': prompt.seekp(0); prompt << "Enter the sector alignment value (1-" << MAX_ALIGNMENT << ", default = " @@ -946,6 +967,7 @@ void GPTDataTextUI::ShowExpertCommands(void) { 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 << "k\tmove the backup 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"; @@ -1007,4 +1029,4 @@ UnicodeString ReadUString(void) { return ReadString().c_str(); } // ReadUString() #endif - + diff --git a/gpttext.h b/gpttext.h index 32e2f88..8ed6274 100644 --- a/gpttext.h +++ b/gpttext.h @@ -41,6 +41,7 @@ class GPTDataTextUI : public GPTData { uint32_t GetPartNum(void); void ResizePartitionTable(void); void MoveMainTable(void); + void MoveSecondTable(void); void CreatePartition(void); void DeletePartition(void); void ChangePartType(void); diff --git a/sgdisk.8 b/sgdisk.8 index b966a13..6f8b375 100644 --- a/sgdisk.8 +++ b/sgdisk.8 @@ -304,13 +304,23 @@ with the current final partition being aligned, and if \fBsgdisk\fR is asked to create a partition in that space, then it will \fBnot\fR be end\-aligned. .TP -.B \-j, \-\-adjust\-main\-table=sector -Adjust the location of the main partition table. This value is normally 2, +.B \-j, \-\-move\-main\-table=sector +Sets the start sector 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 \-k, \-\-move\-backup\-table=sector +Sets the start sector of the second/backup partition table. The backup table +is usually placed just before the last sector, which holds the backup header. +The default value is thus the size of the disk, minus one, minus the total +size of the partition table (in sectors, usually 32). +There are probably very few reasons to ever change this, and while the EFI +standard does not mandate it, most tooling assumes the backup table to be at +the very end of the disk. + .TP .B \-l, \-\-load\-backup=file Load partition data from a backup file. This option is the reverse of the diff --git a/sgdisk.html b/sgdisk.html index 36a28bc..98c20be 100644 --- a/sgdisk.html +++ b/sgdisk.html @@ -195,7 +195,7 @@ when using this option. The others require a partition number. The nand, xor, =, set, clear, and toggle options enable you to change the attribute bit value. The set, clear, toggle, and get options work on a -bit number; the others work on a hexadecimal bit mask. For example, type +bit number; the others work on a hexadecimal bit mask. For example, type sgdisk -A 4:set:2 /dev/sdc to set the bit 2 attribute (legacy BIOS bootable) on partition 4 on /dev/sdc.
@@ -344,15 +344,26 @@ if the free space at the end of a disk is less than the alignment value, with the current final partition being aligned, and if sgdisk is asked to create a partition in that space, then it will not be end-aligned.
-
+