Updated to version 0.4.1
Adds relative partition sizing and placement options for both start and
end sectors; improves hybrid MBR synchronization (deletes matching MBR
partition when a GPT partition is deleted, warns about inconsistencies
when verifying or writing a partition table).
diff --git a/gpt.cc b/gpt.cc
index af3da2a..9498a86 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -1,7 +1,7 @@
/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
data. */
-/* By Rod Smith, January to February, 2009 */
+/* By Rod Smith, initial coding January to February, 2009 */
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
@@ -33,6 +33,7 @@
* *
****************************************/
+// Default constructor
GPTData::GPTData(void) {
blockSize = SECTOR_SIZE; // set a default
diskSize = 0;
@@ -66,66 +67,139 @@
LoadPartitions(filename);
} // GPTData(char* filename) constructor
+// Destructor
GPTData::~GPTData(void) {
free(partitions);
} // GPTData destructor
-// Resizes GPT to specified number of entries. Creates a new table if
-// necessary, copies data if it already exists.
-int GPTData::SetGPTSize(uint32_t numEntries) {
- struct GPTPart* newParts;
- struct GPTPart* trash;
- uint32_t i, high, copyNum;
- int allOK = 1;
+/*********************************************************************
+ * *
+ * Begin functions that verify data, or that adjust the verification *
+ * information (compute CRCs, rebuild headers) *
+ * *
+ *********************************************************************/
- // First, adjust numEntries upward, if necessary, to get a number
- // that fills the allocated sectors
- i = blockSize / GPT_SIZE;
- if ((numEntries % i) != 0) {
- printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
- numEntries = ((numEntries / i) + 1) * i;
- printf("to %lu to fill the sector\n", (unsigned long) numEntries);
+// Perform detailed verification, reporting on any problems found, but
+// do *NOT* recover from these problems. Returns the total number of
+// problems identified.
+int GPTData::Verify(void) {
+ int problems = 0, numSegments, i, j;
+ uint64_t totalFree, largestSegment;
+ char tempStr[255], siTotal[255], siLargest[255];
+
+ // First, check for CRC errors in the GPT data....
+ if (!mainCrcOk) {
+ problems++;
+ printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
+ "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
+ "header\n");
+ } // if
+ if (!mainPartsCrcOk) {
+ problems++;
+ printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
+ "corrupt. Consider loading the backup partition table.\n");
+ } // if
+ if (!secondCrcOk) {
+ problems++;
+ printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
+ "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
+ "header.\n");
+ } // if
+ if (!secondPartsCrcOk) {
+ problems++;
+ printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
+ "be corrupt. This program will automatically create a new backup partition\n"
+ "table when you save your partitions.\n");
} // if
- newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
- if (newParts != NULL) {
- if (partitions != NULL) { // existing partitions; copy them over
- GetPartRange(&i, &high);
- if (numEntries < (high + 1)) { // Highest entry too high for new #
- printf("The highest-numbered partition is %lu, which is greater than the requested\n"
- "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
- (unsigned long) (high + 1), numEntries);
- allOK = 0;
- } else { // go ahead with copy
- if (numEntries < mainHeader.numParts)
- copyNum = numEntries;
- else
- copyNum = mainHeader.numParts;
- for (i = 0; i < copyNum; i++) {
- newParts[i] = partitions[i];
- } // for
- trash = partitions;
- partitions = newParts;
- free(trash);
- } // if
- } else { // No existing partition table; just create it
- partitions = newParts;
- } // if/else existing partitions
- mainHeader.numParts = numEntries;
- secondHeader.numParts = numEntries;
- mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
- secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
- mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
- secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
- secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
- if (diskSize > 0)
- CheckGPTSize();
- } else { // Bad memory allocation
- fprintf(stderr, "Error allocating memory for partition table!\n");
- allOK = 0;
+ // Now check that critical main and backup GPT entries match
+ if (mainHeader.currentLBA != secondHeader.backupLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's LBA pointer(%llu)\n",
+ (unsigned long long) mainHeader.currentLBA,
+ (unsigned long long) secondHeader.backupLBA);
+ } // if
+ if (mainHeader.backupLBA != secondHeader.currentLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's current LBA pointer (%llu)\n",
+ (unsigned long long) mainHeader.backupLBA,
+ (unsigned long long) secondHeader.currentLBA);
+ } // if
+ if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's first usable LBA pointer (%llu)\n",
+ (unsigned long long) mainHeader.firstUsableLBA,
+ (unsigned long long) secondHeader.firstUsableLBA);
+ } // if
+ if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
+ problems++;
+ printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
+ "match the backup GPT header's last usable LBA pointer (%llu)\n",
+ (unsigned long long) mainHeader.lastUsableLBA,
+ (unsigned long long) secondHeader.lastUsableLBA);
+ } // if
+ if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
+ (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
+ problems++;
+ printf("\nProblem: main header's disk GUID (%s) doesn't\n",
+ GUIDToStr(mainHeader.diskGUID, tempStr));
+ printf("match the backup GPT header's disk GUID (%s)\n",
+ GUIDToStr(secondHeader.diskGUID, tempStr));
+ } // if
+ if (mainHeader.numParts != secondHeader.numParts) {
+ problems++;
+ printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
+ "match the backup GPT header's number of partitions (%lu)\n",
+ (unsigned long) mainHeader.numParts,
+ (unsigned long) secondHeader.numParts);
+ } // if
+ if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
+ problems++;
+ printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
+ "match the backup GPT header's size of partition entries (%lu)\n",
+ (unsigned long) mainHeader.sizeOfPartitionEntries,
+ (unsigned long) secondHeader.sizeOfPartitionEntries);
+ } // if
+
+ // Now check for a few other miscellaneous problems...
+ // Check that the disk size will hold the data...
+ if (mainHeader.backupLBA > diskSize) {
+ problems++;
+ printf("\nProblem: Disk is too small to hold all the data!\n");
+ printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
+ (unsigned long long) diskSize,
+ (unsigned long long) mainHeader.backupLBA);
+ } // if
+
+ // Check for overlapping partitions....
+ problems += FindOverlaps();
+
+ // Check for mismatched MBR and GPT partitions...
+ problems += FindHybridMismatches();
+
+ // Verify that partitions don't run into GPT data areas....
+ problems += CheckGPTSize();
+
+ // Now compute available space, but only if no problems found, since
+ // problems could affect the results
+ if (problems == 0) {
+ totalFree = FindFreeBlocks(&numSegments, &largestSegment);
+ BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
+ BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
+ printf("No problems found. %llu free sectors (%s) available in %u\n"
+ "segments, the largest of which is %llu sectors (%s) in size\n",
+ (unsigned long long) totalFree,
+ siTotal, numSegments, (unsigned long long) largestSegment,
+ siLargest);
+ } else {
+ printf("\nIdentified %d problems!\n", problems);
} // if/else
- return (allOK);
-} // GPTData::SetGPTSize()
+
+ return (problems);
+} // GPTData::Verify()
// Checks to see if the GPT tables overrun existing partitions; if they
// do, issues a warning but takes no action. Returns number of problems
@@ -140,7 +214,7 @@
lastUsedBlock = 0;
for (i = 0; i < mainHeader.numParts; i++) {
if ((partitions[i].GetFirstLBA() < firstUsedBlock) &&
- (partitions[i].GetFirstLBA() != 0))
+ (partitions[i].GetFirstLBA() != 0))
firstUsedBlock = partitions[i].GetFirstLBA();
if (partitions[i].GetLastLBA() > lastUsedBlock)
lastUsedBlock = partitions[i].GetLastLBA();
@@ -180,845 +254,6 @@
return numProbs;
} // GPTData::CheckGPTSize()
-// Tell user whether Apple Partition Map (APM) was discovered....
-void GPTData::ShowAPMState(void) {
- if (apmFound)
- printf(" APM: present\n");
- else
- printf(" APM: not present\n");
-} // GPTData::ShowAPMState()
-
-// Tell user about the state of the GPT data....
-void GPTData::ShowGPTState(void) {
- switch (state) {
- case gpt_invalid:
- printf(" GPT: not present\n");
- break;
- case gpt_valid:
- printf(" GPT: present\n");
- break;
- case gpt_corrupt:
- printf(" GPT: damaged\n");
- break;
- default:
- printf("\a GPT: unknown -- bug!\n");
- break;
- } // switch
-} // GPTData::ShowGPTState()
-
-// Scan for partition data. This function loads the MBR data (regular MBR or
-// protective MBR) and loads BSD disklabel data (which is probably invalid).
-// It also looks for APM data, forces a load of GPT data, and summarizes
-// the results.
-void GPTData::PartitionScan(int fd) {
- BSDData bsdDisklabel;
-// int bsdFound;
-
- printf("Partition table scan:\n");
-
- // Read the MBR & check for BSD disklabel
- protectiveMBR.ReadMBRData(fd);
- protectiveMBR.ShowState();
- bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
- bsdFound = bsdDisklabel.ShowState();
-// bsdDisklabel.DisplayBSDData();
-
- // Load the GPT data, whether or not it's valid
- ForceLoadGPTData(fd);
- ShowAPMState(); // Show whether there's an Apple Partition Map present
- ShowGPTState(); // Show GPT status
- printf("\n");
-
- if (apmFound) {
- printf("\n*******************************************************************\n");
- printf("This disk appears to contain an Apple-format (APM) partition table!\n");
- printf("It will be destroyed if you continue!\n");
- printf("*******************************************************************\n\n\a");
- } // if
-/* if (bsdFound) {
- printf("\n*************************************************************************\n");
- printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
- "continue!\n");
- printf("*************************************************************************\n\n\a");
- } // if */
-} // GPTData::PartitionScan()
-
-// Read GPT data from a disk.
-int GPTData::LoadPartitions(char* deviceFilename) {
- int fd, err;
- int allOK = 1, i;
- uint64_t firstBlock, lastBlock;
- BSDData bsdDisklabel;
-
- if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
- // store disk information....
- diskSize = disksize(fd, &err);
- blockSize = (uint32_t) GetBlockSize(fd);
- strcpy(device, deviceFilename);
- PartitionScan(fd); // Check for partition types & print summary
-
- switch (UseWhichPartitions()) {
- case use_mbr:
- XFormPartitions();
- break;
- case use_bsd:
- bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
-// bsdDisklabel.DisplayBSDData();
- ClearGPTData();
- protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
- XFormDisklabel(&bsdDisklabel, 0);
- break;
- case use_gpt:
- break;
- case use_new:
- ClearGPTData();
- protectiveMBR.MakeProtectiveMBR();
- break;
- } // switch
-
- // Now find the first and last sectors used by partitions...
- if (allOK) {
- firstBlock = mainHeader.backupLBA; // start high
- lastBlock = 0; // start low
- for (i = 0; i < mainHeader.numParts; i++) {
- if ((partitions[i].GetFirstLBA() < firstBlock) &&
- (partitions[i].GetFirstLBA() > 0))
- firstBlock = partitions[i].GetFirstLBA();
- if (partitions[i].GetLastLBA() > lastBlock)
- lastBlock = partitions[i].GetLastLBA();
- } // for
- } // if
- CheckGPTSize();
- } else {
- allOK = 0;
- fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
- deviceFilename, errno);
- if (errno == EACCES) { // User is probably not running as root
- fprintf(stderr, "You must run this program as root or use sudo!\n");
- } // if
- } // if/else
- return (allOK);
-} // GPTData::LoadPartitions()
-
-// Loads the GPT, as much as possible. Returns 1 if this seems to have
-// succeeded, 0 if there are obvious problems....
-int GPTData::ForceLoadGPTData(int fd) {
- int allOK = 1, validHeaders;
- off_t seekTo;
- char* storage;
- uint32_t newCRC, sizeOfParts;
-
- // Seek to and read the main GPT header
- lseek64(fd, 512, SEEK_SET);
- read(fd, &mainHeader, 512); // read main GPT header
- mainCrcOk = CheckHeaderCRC(&mainHeader);
- if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
- ReverseHeaderBytes(&mainHeader);
-
- // Load backup header, check its CRC, and store the results of
- // the check for future reference
- seekTo = (diskSize * blockSize) - UINT64_C(512);
- if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
- read(fd, &secondHeader, 512); // read secondary GPT header
- secondCrcOk = CheckHeaderCRC(&secondHeader);
- if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
- ReverseHeaderBytes(&secondHeader);
- } else {
- allOK = 0;
- state = gpt_invalid;
- fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n",
- diskSize - (UINT64_C(1)));
- } // if/else lseek
-
- // Return valid headers code: 0 = both headers bad; 1 = main header
- // good, backup bad; 2 = backup header good, main header bad;
- // 3 = both headers good. Note these codes refer to valid GPT
- // signatures and version numbers; more subtle problems will elude
- // this check!
- validHeaders = CheckHeaderValidity();
-
- // Read partitions (from primary array)
- if (validHeaders > 0) { // if at least one header is OK....
- // GPT appears to be valid....
- state = gpt_valid;
-
- // We're calling the GPT valid, but there's a possibility that one
- // of the two headers is corrupt. If so, use the one that seems to
- // be in better shape to regenerate the bad one
- if (validHeaders == 2) { // valid backup header, invalid main header
- printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
- "from backup!\n");
- RebuildMainHeader();
- mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
- } else if (validHeaders == 1) { // valid main header, invalid backup
- printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
- "backup header from main header.\n");
- RebuildSecondHeader();
- secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
- } // if/else/if
-
- // Load the main partition table, including storing results of its
- // CRC check
- if (LoadMainTable() == 0)
- allOK = 0;
-
- // Load backup partition table into temporary storage to check
- // its CRC and store the results, then discard this temporary
- // storage, since we don't use it in any but recovery operations
- seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
- if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
- sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
- storage = (char*) malloc(sizeOfParts);
- read(fd, storage, sizeOfParts);
- newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
- free(storage);
- secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
- } // if
-
- // Check for valid CRCs and warn if there are problems
- if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
- (secondPartsCrcOk == 0)) {
- printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
- state = gpt_corrupt;
- } // if
- } else {
- state = gpt_invalid;
- } // if/else
- return allOK;
-} // GPTData::ForceLoadGPTData()
-
-// Loads the partition tables pointed to by the main GPT header. The
-// main GPT header in memory MUST be valid for this call to do anything
-// sensible!
-int GPTData::LoadMainTable(void) {
- int fd, retval = 0;
- uint32_t newCRC, sizeOfParts;
-
- if ((fd = open(device, O_RDONLY)) != -1) {
- // Set internal data structures for number of partitions on the disk
- SetGPTSize(mainHeader.numParts);
-
- // Load main partition table, and record whether its CRC
- // matches the stored value
- lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
- sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
- read(fd, partitions, sizeOfParts);
- newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
- mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
- if (IsLittleEndian() == 0)
- ReversePartitionBytes();
- retval = 1;
- } // if
- return retval;
-} // GPTData::LoadMainTable()
-
-// 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)
-WhichToUse GPTData::UseWhichPartitions(void) {
- WhichToUse which = use_new;
- MBRValidity mbrState;
- int answer;
-
- mbrState = protectiveMBR.GetValidity();
-
- if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
- printf("\n\a***************************************************************\n"
- "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
- "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
- "you don't want to convert your MBR partitions to GPT format!\n"
- "***************************************************************\n\n");
- which = use_mbr;
- } // if
-
- if ((state == gpt_invalid) && bsdFound) {
- printf("\n\a**********************************************************************\n"
- "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
- "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
- "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
- "want to convert your BSD partitions to GPT format!\n"
- "**********************************************************************\n\n");
- which = use_bsd;
- } // if
-
- if ((state == gpt_valid) && (mbrState == gpt)) {
- printf("Found valid GPT with protective MBR; using GPT.\n");
- which = use_gpt;
- } // if
- if ((state == gpt_valid) && (mbrState == hybrid)) {
- printf("Found valid GPT with hybrid MBR; using GPT.\n");
- printf("\aIf you change GPT partitions, you may need to re-create the hybrid MBR!\n");
- which = use_gpt;
- } // if
- if ((state == gpt_valid) && (mbrState == invalid)) {
- printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
- which = use_gpt;
- protectiveMBR.MakeProtectiveMBR();
- } // if
- if ((state == gpt_valid) && (mbrState == mbr)) {
- printf("Found valid MBR and GPT. Which do you want to use?\n");
- answer = GetNumber(1, 3, 2, (char*) " 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;
- protectiveMBR.MakeProtectiveMBR();
- printf("Using GPT and creating fresh protective MBR.\n");
- } else which = use_new;
- } // if
-
- // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
- // problems)
- if (state == gpt_corrupt) {
- if ((mbrState == mbr) || (mbrState == hybrid)) {
- printf("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, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
- if (answer == 1) {
- which = use_mbr;
-// protectiveMBR.MakeProtectiveMBR();
- } else if (answer == 2) {
- which = use_gpt;
- } else which = use_new;
- } else if (mbrState == invalid) {
- printf("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, (char*) " 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....
- printf("\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");
- } // if/else/else
- } // if (corrupt GPT)
-
- if (which == use_new)
- printf("Creating new GPT entries.\n");
-
- return which;
-} // UseWhichPartitions()
-
-void GPTData::ResizePartitionTable(void) {
- int newSize;
- char prompt[255];
- uint32_t curLow, curHigh;
-
- printf("Current partition table size is %lu.\n",
- (unsigned long) mainHeader.numParts);
- 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) {
- printf("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()
-
-// Find the low and high used partition numbers (numbered from 0).
-// Return value is the number of partitions found. Note that the
-// *low and *high values are both set to 0 when no partitions
-// are found, as well as when a single partition in the first
-// position exists. Thus, the return value is the only way to
-// tell when no partitions exist.
-int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
- uint32_t i;
- int numFound = 0;
-
- *low = mainHeader.numParts + 1; // code for "not found"
- *high = 0;
- if (mainHeader.numParts > 0) { // only try if partition table exists...
- 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...
- if (*low == (mainHeader.numParts + 1)) *low = i;
- numFound++;
- } // if
- } // for
- } // if
-
- // Above will leave *low pointing to its "not found" value if no partitions
- // are defined, so reset to 0 if this is the case....
- if (*low == (mainHeader.numParts + 1))
- *low = 0;
- return numFound;
-} // GPTData::GetPartRange()
-
-// Display the basic GPT data
-void GPTData::DisplayGPTData(void) {
- int i, j;
- char sizeInSI[255]; // String to hold size of disk in SI units
- char tempStr[255];
- uint64_t temp, totalFree;
-
- BytesToSI(diskSize * blockSize, sizeInSI);
- printf("Disk %s: %lu sectors, %s\n", device,
- (unsigned long) diskSize, sizeInSI);
- printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
- printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
- printf("First usable sector is %lu, last usable sector is %lu\n",
- (unsigned long) mainHeader.firstUsableLBA,
- (unsigned long) mainHeader.lastUsableLBA);
- totalFree = FindFreeBlocks(&i, &temp);
- printf("Total free space is %llu sectors (%s)\n", totalFree,
- BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
- printf("\nNumber Start (sector) End (sector) Size Code Name\n");
- for (i = 0; i < mainHeader.numParts; i++) {
- partitions[i].ShowSummary(i, blockSize, sizeInSI);
- } // 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 {
- printf("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) {
- partitions[partNum].ShowDetails(blockSize);
- } else {
- printf("Partition #%d does not exist.", (int) (partNum + 1));
- } // if
-} // GPTData::ShowPartDetails()
-
-// Interactively create a partition
-void GPTData::CreatePartition(void) {
- uint64_t firstBlock, lastBlock, sector;
- char prompt[255];
- int partNum, firstFreePart = 0;
-
- // Find first free partition...
- while (partitions[firstFreePart].GetFirstLBA() != 0) {
- firstFreePart++;
- } // while
-
- if (((firstBlock = FindFirstAvailable()) != 0) &&
- (firstFreePart < mainHeader.numParts)) {
- lastBlock = FindLastAvailable(firstBlock);
-
- // 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)
- printf("partition %d is in use.\n", partNum + 1);
- } while (partitions[partNum].GetFirstLBA() != 0);
-
- // Get first block for new partition...
- sprintf(prompt, "First sector (%llu-%llu, default = %llu): ", firstBlock,
- lastBlock, firstBlock);
- do {
- sector = GetNumber(firstBlock, lastBlock, firstBlock, prompt);
- } while (IsFree(sector) == 0);
- firstBlock = sector;
-
- // Get last block for new partitions...
- lastBlock = FindLastInFree(firstBlock);
- sprintf(prompt, "Last sector or +size or +sizeM or +sizeK (%llu-%llu, default = %d): ",
- firstBlock, lastBlock, lastBlock);
- do {
- sector = GetLastSector(firstBlock, lastBlock, prompt);
- } while (IsFree(sector) == 0);
- lastBlock = sector;
-
- partitions[partNum].SetFirstLBA(firstBlock);
- partitions[partNum].SetLastLBA(lastBlock);
-
- partitions[partNum].SetUniqueGUID(1);
- partitions[partNum].ChangeType();
- partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
- } else {
- printf("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);
- partitions[partNum - 1].BlankPartition();
- } else {
- printf("No partitions\n");
- } // if/else
-} // GPTData::DeletePartition()
-
-// Find the first available block after the starting point; returns 0 if
-// there are no available blocks left
-uint64_t GPTData::FindFirstAvailable(uint64_t start) {
- uint64_t first;
- uint32_t i;
- int firstMoved = 0;
-
- // Begin from the specified starting point or from the first usable
- // LBA, whichever is greater...
- if (start < mainHeader.firstUsableLBA)
- first = mainHeader.firstUsableLBA;
- else
- first = start;
-
- // ...now search through all partitions; if first is within an
- // existing partition, move it to the next sector after that
- // partition and repeat. If first was moved, set firstMoved
- // flag; repeat until firstMoved is not set, so as to catch
- // cases where partitions are out of sequential order....
- do {
- firstMoved = 0;
- for (i = 0; i < mainHeader.numParts; i++) {
- if ((first >= partitions[i].GetFirstLBA()) &&
- (first <= partitions[i].GetLastLBA())) { // in existing part.
- first = partitions[i].GetLastLBA() + 1;
- firstMoved = 1;
- } // if
- } // for
- } while (firstMoved == 1);
- if (first > mainHeader.lastUsableLBA)
- first = 0;
- return (first);
-} // GPTData::FindFirstAvailable()
-
-// Find the last available block on the disk at or after the start
-// block. Returns 0 if there are no available partitions after
-// (or including) start.
-uint64_t GPTData::FindLastAvailable(uint64_t start) {
- uint64_t last;
- uint32_t i;
- int lastMoved = 0;
-
- // Start by assuming the last usable LBA is available....
- last = mainHeader.lastUsableLBA;
-
- // ...now, similar to algorithm in FindFirstAvailable(), search
- // through all partitions, moving last when it's in an existing
- // partition. Set the lastMoved flag so we repeat to catch cases
- // where partitions are out of logical order.
- do {
- lastMoved = 0;
- for (i = 0; i < mainHeader.numParts; i++) {
- if ((last >= partitions[i].GetFirstLBA()) &&
- (last <= partitions[i].GetLastLBA())) { // in existing part.
- last = partitions[i].GetFirstLBA() - 1;
- lastMoved = 1;
- } // if
- } // for
- } while (lastMoved == 1);
- if (last < mainHeader.firstUsableLBA)
- last = 0;
- return (last);
-} // GPTData::FindLastAvailable()
-
-// Find the last available block in the free space pointed to by start.
-uint64_t GPTData::FindLastInFree(uint64_t start) {
- uint64_t nearestStart;
- uint32_t i;
-
- nearestStart = mainHeader.lastUsableLBA;
- for (i = 0; i < mainHeader.numParts; i++) {
- if ((nearestStart > partitions[i].GetFirstLBA()) &&
- (partitions[i].GetFirstLBA() > start)) {
- nearestStart = partitions[i].GetFirstLBA() - 1;
- } // if
- } // for
- return (nearestStart);
-} // GPTData::FindLastInFree()
-
-// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
-int GPTData::IsFree(uint64_t sector) {
- int isFree = 1;
- uint32_t i;
-
- for (i = 0; i < mainHeader.numParts; i++) {
- if ((sector >= partitions[i].GetFirstLBA()) &&
- (sector <= partitions[i].GetLastLBA())) {
- isFree = 0;
- } // if
- } // for
- if ((sector < mainHeader.firstUsableLBA) ||
- (sector > mainHeader.lastUsableLBA)) {
- isFree = 0;
- } // if
- return (isFree);
-} // GPTData::IsFree()
-
-int GPTData::XFormPartitions(void) {
- int i, numToConvert;
- uint8_t origType;
- struct newGUID;
- char name[NAME_SIZE];
-
- // Clear out old data & prepare basics....
- ClearGPTData();
-
- // Convert the smaller of the # of GPT or MBR partitions
- if (mainHeader.numParts > (NUM_LOGICALS + 4))
- numToConvert = NUM_LOGICALS + 4;
- else
- numToConvert = mainHeader.numParts;
-
- for (i = 0; i < numToConvert; i++) {
- origType = protectiveMBR.GetType(i);
- // don't waste CPU time trying to convert extended, hybrid protective, or
- // null (non-existent) partitions
- if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
- (origType != 0x00) && (origType != 0xEE))
- partitions[i] = protectiveMBR.AsGPT(i);
- } // for
-
- // Convert MBR into protective MBR
- protectiveMBR.MakeProtectiveMBR();
-
- // 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 disklable on the specified partition (numbered from 0).
-// If an invalid partition number is given, the program prompts for one.
-// Returns the number of new partitions created.
-int GPTData::XFormDisklabel(int i) {
- uint32_t low, high, partNum, startPart;
- uint16_t hexCode;
- int goOn = 1, numDone = 0;
- BSDData disklabel;
-
- if (GetPartRange(&low, &high) != 0) {
- if ((i < low) || (i > high))
- partNum = GetPartNum();
- else
- partNum = (uint32_t) i;
-
- // 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)) {
- printf("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(device, partitions[partNum].GetFirstLBA(),
- partitions[partNum].GetLastLBA());
- if ((goOn) && (disklabel.IsDisklabel())) {
- numDone = XFormDisklabel(&disklabel, startPart);
- if (numDone == 1)
- printf("Converted %d BSD partition.\n", numDone);
- else
- printf("Converted %d BSD partitions.\n", numDone);
- } else {
- printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
- } // if/else
- } // if
- if (numDone > 0) { // converted partitions; delete carrier
- partitions[partNum].BlankPartition();
- } // if
- } else {
- printf("No partitions\n");
- } // if/else
- return numDone;
-} // GPTData::XFormDisklable(int i)
-
-// Transform the partitions on an already-loaded BSD disklabel...
-int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
- int i, numDone = 0;
-
- if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
- (startPart < mainHeader.numParts)) {
- for (i = 0; i < disklabel->GetNumParts(); i++) {
- partitions[i + startPart] = disklabel->AsGPT(i);
- if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
- numDone++;
- } // for
- } // if
-
- // Record that all original CRCs were OK so as not to raise flags
- // when doing a disk verification
- mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
-
- return numDone;
-} // GPTData::XFormDisklabel(BSDData* disklabel)
-
-// 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) {
- int i, lastPart = 0;
- GPTPart temp;
-
- // First, find the last partition with data, so as not to
- // spend needless time sorting empty entries....
- for (i = 0; i < mainHeader.numParts; i++) {
- if (partitions[i].GetFirstLBA() > 0)
- lastPart = i;
- } // for
-
- // Now swap empties with the last partitions, to simplify the logic
- // in the Quicksort function....
- i = 0;
- while (i < lastPart) {
- if (partitions[i].GetFirstLBA() == 0) {
- temp = partitions[i];
- partitions[i] = partitions[lastPart];
- partitions[lastPart] = temp;
- lastPart--;
- } // if
- i++;
- } // while
-
- // Now call the recursive quick sort routine to do the real work....
- QuickSortGPT(partitions, 0, lastPart);
-} // GPTData::SortGPT()
-
-// Blank the partition array
-void GPTData::BlankPartitions(void) {
- uint32_t i;
-
- for (i = 0; i < mainHeader.numParts; i++) {
- partitions[i].BlankPartition();
- } // for
-} // GPTData::BlankPartitions()
-
-// Set up data structures for entirely new set of partitions on the
-// specified device. Returns 1 if OK, 0 if there were problems.
-int GPTData::ClearGPTData(void) {
- int goOn, i;
-
- // Set up the partition table....
- free(partitions);
- partitions = NULL;
- SetGPTSize(NUM_GPT_ENTRIES);
-
- // Now initialize a bunch of stuff that's static....
- mainHeader.signature = GPT_SIGNATURE;
- mainHeader.revision = 0x00010000;
- mainHeader.headerSize = (uint32_t) HEADER_SIZE;
- mainHeader.reserved = 0;
- mainHeader.currentLBA = UINT64_C(1);
- mainHeader.partitionEntriesLBA = (uint64_t) 2;
- mainHeader.sizeOfPartitionEntries = GPT_SIZE;
- for (i = 0; i < GPT_RESERVED; i++) {
- mainHeader.reserved2[i] = '\0';
- } // for
-
- // Now some semi-static items (computed based on end of disk)
- mainHeader.backupLBA = diskSize - UINT64_C(1);
- mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
-
- // Set a unique GUID for the disk, based on random numbers
- // rand() is only 32 bits, so multiply together to fill a 64-bit value
- mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
- mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
-
- // Copy main header to backup header
- RebuildSecondHeader();
-
- // Blank out the partitions array....
- BlankPartitions();
-
- // Flag all CRCs as being OK....
- mainCrcOk = 1;
- secondCrcOk = 1;
- mainPartsCrcOk = 1;
- secondPartsCrcOk = 1;
-
- return (goOn);
-} // GPTData::ClearGPTData()
-
-// 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 {
- printf("No partitions\n");
- } // if/else
-} // GPTData::ChangePartType()
-
-// 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()
-
-void GPTData::SetAttributes(uint32_t partNum) {
- Attributes theAttr;
-
- theAttr.SetAttributes(partitions[partNum].GetAttributes());
- theAttr.DisplayAttributes();
- theAttr.ChangeAttributes();
- partitions[partNum].SetAttributes(theAttr.GetAttributes());
-} // GPTData::SetAttributes()
-
-void GPTData::SetName(uint32_t partNum, char* theName) {
- if ((partNum >= 0) && (partNum < mainHeader.numParts))
- if (partitions[partNum].GetFirstLBA() > 0)
- partitions[partNum].SetName((unsigned char*) theName);
-} // GPTData::SetName
-
-// Set the disk GUID to the specified value. Note that the header CRCs must
-// be recomputed after calling this function.
-void GPTData::SetDiskGUID(GUIDData newGUID) {
- mainHeader.diskGUID = newGUID;
- secondHeader.diskGUID = newGUID;
-} // SetDiskGUID()
-
-// Set the unique GUID of the specified partition. Returns 1 on
-// successful completion, 0 if there were problems (invalid
-// partition number).
-int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
- int retval = 0;
-
- if (pn < mainHeader.numParts) {
- if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
- partitions[pn].SetUniqueGUID(theGUID);
- retval = 1;
- } // if
- } // if
- return retval;
-} // GPTData::SetPartitionGUID()
-
// Check the validity of the GPT header. Returns 1 if the main header
// is valid, 2 if the backup header is valid, 3 if both are valid, and
// 0 if neither is valid. Note that this function just checks the GPT
@@ -1048,12 +283,12 @@
// If MBR bad, check for an Apple disk signature
if ((protectiveMBR.GetValidity() == invalid) &&
- (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
+ (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
(mainHeader.signature << 32) == APM_SIGNATURE2)) {
apmFound = 1; // Will display warning message later
- } // if
+ } // if
- return valid;
+ return valid;
} // GPTData::CheckHeaderValidity()
// Check the header CRC to see if it's OK...
@@ -1117,138 +352,6 @@
secondHeader.headerCRC = crc;
} // GPTData::RecomputeCRCs()
-// Perform detailed verification, reporting on any problems found, but
-// do *NOT* recover from these problems. Returns the total number of
-// problems identified.
-int GPTData::Verify(void) {
- int problems = 0, numSegments, i, j;
- uint64_t totalFree, largestSegment;
- char tempStr[255], siTotal[255], siLargest[255];
-
- // First, check for CRC errors in the GPT data....
- if (!mainCrcOk) {
- problems++;
- printf("\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
- "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
- "header\n");
- } // if
- if (!mainPartsCrcOk) {
- problems++;
- printf("\nProblem: The CRC for the main partition table is invalid. This table may be\n"
- "corrupt. Consider loading the backup partition table.\n");
- } // if
- if (!secondCrcOk) {
- problems++;
- printf("\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
- "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
- "header.\n");
- } // if
- if (!secondPartsCrcOk) {
- problems++;
- printf("\nCaution: The CRC for the backup partition table is invalid. This table may\n"
- "be corrupt. This program will automatically create a new backup partition\n"
- "table when you save your partitions.\n");
- } // if
-
- // Now check that critical main and backup GPT entries match
- if (mainHeader.currentLBA != secondHeader.backupLBA) {
- problems++;
- printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
- "match the backup GPT header's LBA pointer(%llu)\n",
- (unsigned long long) mainHeader.currentLBA,
- (unsigned long long) secondHeader.backupLBA);
- } // if
- if (mainHeader.backupLBA != secondHeader.currentLBA) {
- problems++;
- printf("\nProblem: main GPT header's backup LBA pointer (%llu) doesn't\n"
- "match the backup GPT header's current LBA pointer (%llu)\n",
- (unsigned long long) mainHeader.backupLBA,
- (unsigned long long) secondHeader.currentLBA);
- } // if
- if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
- problems++;
- printf("\nProblem: main GPT header's first usable LBA pointer (%llu) doesn't\n"
- "match the backup GPT header's first usable LBA pointer (%llu)\n",
- (unsigned long long) mainHeader.firstUsableLBA,
- (unsigned long long) secondHeader.firstUsableLBA);
- } // if
- if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
- problems++;
- printf("\nProblem: main GPT header's last usable LBA pointer (%llu) doesn't\n"
- "match the backup GPT header's last usable LBA pointer (%llu)\n",
- (unsigned long long) mainHeader.lastUsableLBA,
- (unsigned long long) secondHeader.lastUsableLBA);
- } // if
- if ((mainHeader.diskGUID.data1 != secondHeader.diskGUID.data1) ||
- (mainHeader.diskGUID.data2 != secondHeader.diskGUID.data2)) {
- problems++;
- printf("\nProblem: main header's disk GUID (%s) doesn't\n",
- GUIDToStr(mainHeader.diskGUID, tempStr));
- printf("match the backup GPT header's disk GUID (%s)\n",
- GUIDToStr(secondHeader.diskGUID, tempStr));
- } // if
- if (mainHeader.numParts != secondHeader.numParts) {
- problems++;
- printf("\nProblem: main GPT header's number of partitions (%lu) doesn't\n"
- "match the backup GPT header's number of partitions (%lu)\n",
- (unsigned long) mainHeader.numParts,
- (unsigned long) secondHeader.numParts);
- } // if
- if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
- problems++;
- printf("\nProblem: main GPT header's size of partition entries (%lu) doesn't\n"
- "match the backup GPT header's size of partition entries (%lu)\n",
- (unsigned long) mainHeader.sizeOfPartitionEntries,
- (unsigned long) secondHeader.sizeOfPartitionEntries);
- } // if
-
- // Now check for a few other miscellaneous problems...
- // Check that the disk size will hold the data...
- if (mainHeader.backupLBA > diskSize) {
- problems++;
- printf("\nProblem: Disk is too small to hold all the data!\n");
- printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n",
- (unsigned long long) diskSize,
- (unsigned long long) mainHeader.backupLBA);
- } // if
-
- // Check for overlapping partitions....
- for (i = 1; i < mainHeader.numParts; i++) {
- for (j = 0; j < i; j++) {
- if (partitions[i].DoTheyOverlap(&partitions[j])) {
- problems++;
- printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
- printf(" Partition %d: %llu to %llu\n", i,
- (unsigned long long) partitions[i].GetFirstLBA(),
- (unsigned long long) partitions[i].GetLastLBA());
- printf(" Partition %d: %llu to %llu\n", j,
- (unsigned long long) partitions[j].GetFirstLBA(),
- (unsigned long long) partitions[j].GetLastLBA());
- } // if
- } // for j...
- } // for i...
-
- // Verify that partitions don't run into GPT data areas....
- problems += CheckGPTSize();
-
- // Now compute available space, but only if no problems found, since
- // problems could affect the results
- if (problems == 0) {
- totalFree = FindFreeBlocks(&numSegments, &largestSegment);
- BytesToSI(totalFree * (uint64_t) blockSize, siTotal);
- BytesToSI(largestSegment * (uint64_t) blockSize, siLargest);
- printf("No problems found. %llu free sectors (%s) available in %u\n"
- "segments, the largest of which is %llu sectors (%s) in size\n",
- (unsigned long long) totalFree,
- siTotal, numSegments, (unsigned long long) largestSegment,
- siLargest);
- } else {
- printf("\nIdentified %d problems!\n", problems);
- } // if/else
-
- return (problems);
-} // GPTData::Verify()
-
// Rebuild the main GPT header, using the secondary header as a model.
// Typically called when the main header has been found to be corrupt.
void GPTData::RebuildMainHeader(void) {
@@ -1294,7 +397,279 @@
secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
for (i = 0 ; i < GPT_RESERVED; i++)
secondHeader.reserved2[i] = mainHeader.reserved2[i];
-} // RebuildSecondHeader()
+} // GPTData::RebuildSecondHeader()
+
+// Search for hybrid MBR entries that have no corresponding GPT partition.
+// Returns number of such mismatches found
+int GPTData::FindHybridMismatches(void) {
+ int i, j, found, numFound = 0;
+ uint64_t mbrFirst, mbrLast;
+
+ for (i = 0; i < 4; i++) {
+ if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
+ j = 0;
+ found = 0;
+ do {
+ mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
+ mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
+ if ((partitions[j].GetFirstLBA() == mbrFirst) &&
+ (partitions[j].GetLastLBA() == mbrLast))
+ found = 1;
+ j++;
+ } while ((!found) && (j < mainHeader.numParts));
+ if (!found) {
+ numFound++;
+ printf("\nWarning! Mismatched GPT and MBR partitions! MBR partition "
+ "%d, of type 0x%02X,\nhas no corresponding GPT partition! "
+ "You may continue, but this condition\nmight cause data loss"
+ " in the future!\a\n", i + 1, protectiveMBR.GetType(i));
+ } // if
+ } // if
+ } // for
+ return numFound;
+} // GPTData::FindHybridMismatches
+
+// Find overlapping partitions and warn user about them. Returns number of
+// overlapping partitions.
+int GPTData::FindOverlaps(void) {
+ int i, j, problems = 0;
+
+ for (i = 1; i < mainHeader.numParts; i++) {
+ for (j = 0; j < i; j++) {
+ if (partitions[i].DoTheyOverlap(&partitions[j])) {
+ problems++;
+ printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1);
+ printf(" Partition %d: %llu to %llu\n", i,
+ (unsigned long long) partitions[i].GetFirstLBA(),
+ (unsigned long long) partitions[i].GetLastLBA());
+ printf(" Partition %d: %llu to %llu\n", j,
+ (unsigned long long) partitions[j].GetFirstLBA(),
+ (unsigned long long) partitions[j].GetLastLBA());
+ } // if
+ } // for j...
+ } // for i...
+ return problems;
+} // GPTData::FindOverlaps()
+
+/******************************************************************
+ * *
+ * Begin functions that load data from disk or save data to disk. *
+ * *
+ ******************************************************************/
+
+// Scan for partition data. This function loads the MBR data (regular MBR or
+// protective MBR) and loads BSD disklabel data (which is probably invalid).
+// It also looks for APM data, forces a load of GPT data, and summarizes
+// the results.
+void GPTData::PartitionScan(int fd) {
+ BSDData bsdDisklabel;
+// int bsdFound;
+
+ printf("Partition table scan:\n");
+
+ // Read the MBR & check for BSD disklabel
+ protectiveMBR.ReadMBRData(fd);
+ protectiveMBR.ShowState();
+ bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
+ bsdFound = bsdDisklabel.ShowState();
+// bsdDisklabel.DisplayBSDData();
+
+ // Load the GPT data, whether or not it's valid
+ ForceLoadGPTData(fd);
+ ShowAPMState(); // Show whether there's an Apple Partition Map present
+ ShowGPTState(); // Show GPT status
+ printf("\n");
+
+ if (apmFound) {
+ printf("\n*******************************************************************\n");
+ printf("This disk appears to contain an Apple-format (APM) partition table!\n");
+ printf("It will be destroyed if you continue!\n");
+ printf("*******************************************************************\n\n\a");
+ } // if
+/* if (bsdFound) {
+ printf("\n*************************************************************************\n");
+ printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
+ "continue!\n");
+ printf("*************************************************************************\n\n\a");
+} // if */
+} // GPTData::PartitionScan()
+
+// Read GPT data from a disk.
+int GPTData::LoadPartitions(char* deviceFilename) {
+ int fd, err;
+ int allOK = 1, i;
+ uint64_t firstBlock, lastBlock;
+ BSDData bsdDisklabel;
+
+ // First, do a test to see if writing will be possible later....
+ fd = OpenForWrite(deviceFilename);
+ if (fd == -1)
+ printf("\aNOTE: Write test failed with error number %d. It will be "
+ "impossible to save\nchanges to this disk's partition table!\n\n",
+ errno);
+ close(fd);
+
+ if ((fd = open(deviceFilename, O_RDONLY)) != -1) {
+ // store disk information....
+ diskSize = disksize(fd, &err);
+ blockSize = (uint32_t) GetBlockSize(fd);
+ strcpy(device, deviceFilename);
+ PartitionScan(fd); // Check for partition types & print summary
+
+ switch (UseWhichPartitions()) {
+ case use_mbr:
+ XFormPartitions();
+ break;
+ case use_bsd:
+ bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1);
+// bsdDisklabel.DisplayBSDData();
+ ClearGPTData();
+ protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
+ XFormDisklabel(&bsdDisklabel, 0);
+ break;
+ case use_gpt:
+ break;
+ case use_new:
+ ClearGPTData();
+ protectiveMBR.MakeProtectiveMBR();
+ break;
+ } // switch
+
+ // Now find the first and last sectors used by partitions...
+ if (allOK) {
+ firstBlock = mainHeader.backupLBA; // start high
+ lastBlock = 0; // start low
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((partitions[i].GetFirstLBA() < firstBlock) &&
+ (partitions[i].GetFirstLBA() > 0))
+ firstBlock = partitions[i].GetFirstLBA();
+ if (partitions[i].GetLastLBA() > lastBlock)
+ lastBlock = partitions[i].GetLastLBA();
+ } // for
+ } // if
+ CheckGPTSize();
+ } else {
+ allOK = 0;
+ fprintf(stderr, "Problem opening %s for reading! Error is %d\n",
+ deviceFilename, errno);
+ if (errno == EACCES) { // User is probably not running as root
+ fprintf(stderr, "You must run this program as root or use sudo!\n");
+ } // if
+ } // if/else
+ return (allOK);
+} // GPTData::LoadPartitions()
+
+// Loads the GPT, as much as possible. Returns 1 if this seems to have
+// succeeded, 0 if there are obvious problems....
+int GPTData::ForceLoadGPTData(int fd) {
+ int allOK = 1, validHeaders;
+ off_t seekTo;
+ char* storage;
+ uint32_t newCRC, sizeOfParts;
+
+ // Seek to and read the main GPT header
+ lseek64(fd, 512, SEEK_SET);
+ read(fd, &mainHeader, 512); // read main GPT header
+ mainCrcOk = CheckHeaderCRC(&mainHeader);
+ if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
+ ReverseHeaderBytes(&mainHeader);
+
+ // Load backup header, check its CRC, and store the results of
+ // the check for future reference
+ seekTo = (diskSize * blockSize) - UINT64_C(512);
+ if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
+ read(fd, &secondHeader, 512); // read secondary GPT header
+ secondCrcOk = CheckHeaderCRC(&secondHeader);
+ if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
+ ReverseHeaderBytes(&secondHeader);
+ } else {
+ allOK = 0;
+ state = gpt_invalid;
+ fprintf(stderr, "Unable to seek to secondary GPT at sector %llu!\n",
+ diskSize - (UINT64_C(1)));
+ } // if/else lseek
+
+ // Return valid headers code: 0 = both headers bad; 1 = main header
+ // good, backup bad; 2 = backup header good, main header bad;
+ // 3 = both headers good. Note these codes refer to valid GPT
+ // signatures and version numbers; more subtle problems will elude
+ // this check!
+ validHeaders = CheckHeaderValidity();
+
+ // Read partitions (from primary array)
+ if (validHeaders > 0) { // if at least one header is OK....
+ // GPT appears to be valid....
+ state = gpt_valid;
+
+ // We're calling the GPT valid, but there's a possibility that one
+ // of the two headers is corrupt. If so, use the one that seems to
+ // be in better shape to regenerate the bad one
+ if (validHeaders == 2) { // valid backup header, invalid main header
+ printf("Caution: invalid main GPT header, but valid backup; regenerating main header\n"
+ "from backup!\n");
+ RebuildMainHeader();
+ mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
+ } else if (validHeaders == 1) { // valid main header, invalid backup
+ printf("Caution: invalid backup GPT header, but valid main header; regenerating\n"
+ "backup header from main header.\n");
+ RebuildSecondHeader();
+ secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
+ } // if/else/if
+
+ // Load the main partition table, including storing results of its
+ // CRC check
+ if (LoadMainTable() == 0)
+ allOK = 0;
+
+ // Load backup partition table into temporary storage to check
+ // its CRC and store the results, then discard this temporary
+ // storage, since we don't use it in any but recovery operations
+ seekTo = secondHeader.partitionEntriesLBA * (off_t) blockSize;
+ if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) {
+ sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries;
+ storage = (char*) malloc(sizeOfParts);
+ read(fd, storage, sizeOfParts);
+ newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts);
+ free(storage);
+ secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
+ } // if
+
+ // Check for valid CRCs and warn if there are problems
+ if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
+ (secondPartsCrcOk == 0)) {
+ printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
+ state = gpt_corrupt;
+ } // if
+ } else {
+ state = gpt_invalid;
+ } // if/else
+ return allOK;
+} // GPTData::ForceLoadGPTData()
+
+// Loads the partition tables pointed to by the main GPT header. The
+// main GPT header in memory MUST be valid for this call to do anything
+// sensible!
+int GPTData::LoadMainTable(void) {
+ int fd, retval = 0;
+ uint32_t newCRC, sizeOfParts;
+
+ if ((fd = open(device, O_RDONLY)) != -1) {
+ // Set internal data structures for number of partitions on the disk
+ SetGPTSize(mainHeader.numParts);
+
+ // Load main partition table, and record whether its CRC
+ // matches the stored value
+ lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET);
+ sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries;
+ read(fd, partitions, sizeOfParts);
+ newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
+ mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
+ if (IsLittleEndian() == 0)
+ ReversePartitionBytes();
+ retval = 1;
+ } // if
+ return retval;
+} // GPTData::LoadMainTable()
// Load the second (backup) partition table as the primary partition
// table. Used in repair functions
@@ -1311,7 +686,7 @@
read(fd, partitions, sizeOfParts);
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
- mainPartsCrcOk = secondPartsCrcOk;
+ mainPartsCrcOk = secondPartsCrcOk;
if (IsLittleEndian() == 0)
ReversePartitionBytes();
if (!secondPartsCrcOk) {
@@ -1325,141 +700,6 @@
} // if/else
} // GPTData::LoadSecondTableAsMain()
-// Finds the total number of free blocks, the number of segments in which
-// they reside, and the size of the largest of those segments
-uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
- uint64_t start = UINT64_C(0); // starting point for each search
- uint64_t totalFound = UINT64_C(0); // running total
- uint64_t firstBlock; // first block in a segment
- uint64_t lastBlock; // last block in a segment
- uint64_t segmentSize; // size of segment in blocks
- int num = 0;
-
- *largestSegment = UINT64_C(0);
- do {
- firstBlock = FindFirstAvailable(start);
- if (firstBlock != UINT64_C(0)) { // something's free...
- lastBlock = FindLastInFree(firstBlock);
- segmentSize = lastBlock - firstBlock + UINT64_C(1);
- if (segmentSize > *largestSegment) {
- *largestSegment = segmentSize;
- } // if
- totalFound += segmentSize;
- num++;
- start = lastBlock + 1;
- } // if
- } while (firstBlock != 0);
- *numSegments = num;
- return totalFound;
-} // GPTData::FindFreeBlocks()
-
-// 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];
- int numParts, i, j, typeCode, bootable, mbrNum;
- uint64_t length;
- char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
- char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
-
- printf("\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....
- printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
- "added to the hybrid MBR, in sequence: ");
- 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);
- protectiveMBR.SetDiskSize(diskSize);
- printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
- eeFirst = GetYN();
- } // if
-
- for (i = 0; i < numParts; i++) {
- j = partNums[i] - 1;
- printf("\nCreating entry for partition #%d\n", j + 1);
- if ((j >= 0) && (j < mainHeader.numParts)) {
- if (partitions[j].GetLastLBA() < UINT32_MAX) {
- do {
- printf("Enter an MBR hex code (default %02X): ",
- typeHelper.GUIDToID(partitions[j].GetType()) / 256);
- fgets(line, 255, stdin);
- sscanf(line, "%x", &typeCode);
- if (line[0] == '\n')
- typeCode = partitions[j].GetHexType() / 256;
- } while ((typeCode <= 0) || (typeCode > 255));
- printf("Set the bootable flag? ");
- bootable = (GetYN() == 'Y');
- length = partitions[j].GetLengthLBA();
- if (eeFirst == 'Y')
- mbrNum = i + 1;
- else
- mbrNum = i;
- protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
- (uint32_t) length, typeCode, bootable);
- } else { // partition out of range
- printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
- j + 1);
- } // if/else
- } else {
- printf("Partition %d is out of range; omitting it.\n", j + 1);
- } // if/else
- } // for
-
- if (numParts > 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);
-
- // ... and for good measure, if there are any partition spaces left,
- // optionally create more protective EFI partitions to cover as much
- // space as possible....
- for (i = 0; i < 4; i++) {
- if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
- if (fillItUp == 'M') {
- printf("\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)) {
- printf("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.
- 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()
-
-// Create a fresh protective MBR.
-void GPTData::MakeProtectiveMBR(void) {
- protectiveMBR.MakeProtectiveMBR();
-} // GPTData::MakeProtectiveMBR(void)
-
// Writes GPT (and protective MBR) to disk. Returns 1 on successful
// write, 0 if there was a problem.
int GPTData::SaveGPTData(void) {
@@ -1491,21 +731,14 @@
} // if
// Check for overlapping partitions....
- for (i = 1; i < mainHeader.numParts; i++) {
- for (j = 0; j < i; j++) {
- if (partitions[i].DoTheyOverlap(&partitions[j])) {
- fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1);
- fprintf(stderr, " Partition %d: %llu to %llu\n", i,
- (unsigned long long) partitions[i].GetFirstLBA(),
- (unsigned long long) partitions[i].GetLastLBA());
- fprintf(stderr, " Partition %d: %llu to %llu\n", j,
- (unsigned long long) partitions[j].GetFirstLBA(),
- (unsigned long long) partitions[j].GetLastLBA());
- fprintf(stderr, "Aborting write operation!\n");
- allOK = 0;
- } // if
- } // for j...
- } // for i...
+ if (FindOverlaps() > 0) {
+ allOK = 0;
+ fprintf(stderr, "Aborting write operation!\n");
+ } // if
+
+ // Check for mismatched MBR and GPT data, but let it pass if found
+ // (function displays warning message)
+ FindHybridMismatches();
// Pull out some data that's needed before doing byte-order reversal on
// big-endian systems....
@@ -1536,30 +769,24 @@
// Do it!
if (allOK) {
- fd = open(device, O_WRONLY); // try to open the device; may fail....
-#ifdef __APPLE__
- // MacOS X requires a shared lock under some circumstances....
- if (fd < 0) {
- fd = open(device, O_WRONLY|O_SHLOCK);
- } // if
-#endif
+ fd = OpenForWrite(device);
if (fd != -1) {
// First, write the protective MBR...
- protectiveMBR.WriteMBRData(fd);
+ protectiveMBR.WriteMBRData(fd);
// Now write the main GPT header...
if (allOK)
if (write(fd, &mainHeader, 512) == -1)
- allOK = 0;
+ allOK = 0;
// Now write the main partition tables...
- if (allOK) {
- if (write(fd, partitions, GPT_SIZE * numParts) == -1)
+ if (allOK) {
+ if (write(fd, partitions, GPT_SIZE * numParts) == -1)
allOK = 0;
} // if
// Now seek to near the end to write the secondary GPT....
- if (allOK) {
+ if (allOK) {
offset = (off_t) secondTable * (off_t) (blockSize);
if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
allOK = 0;
@@ -1568,51 +795,52 @@
} // if
// Now write the secondary partition tables....
- if (allOK)
+ if (allOK)
if (write(fd, partitions, GPT_SIZE * numParts) == -1)
allOK = 0;
// Now write the secondary GPT header...
- if (allOK)
+ if (allOK)
if (write(fd, &secondHeader, 512) == -1)
- allOK = 0;
+ allOK = 0;
// re-read the partition table
if (allOK) {
sync();
#ifdef __APPLE__
- printf("Warning: The kernel may continue to use old or deleted partitions.\n"
- "You should reboot or remove the drive.\n");
+ printf("Warning: The kernel may continue to use old or deleted partitions.\n"
+ "You should reboot or remove the drive.\n");
/* don't know if this helps
* it definitely will get things on disk though:
- * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
- i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
+ * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
+ i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
#else
#ifdef __FreeBSD__
sleep(2);
i = ioctl(fd, DIOCGFLUSH);
- printf("Warning: The kernel is still using the old partition table.\n");
+ printf("Warning: The kernel may still provide disk access using old partition IDs.\n");
#else
- sleep(2);
+ sleep(2);
i = ioctl(fd, BLKRRPART);
if (i)
printf("Warning: The kernel is still using the old partition table.\n"
- "The new table will be used at the next reboot.\n");
+ "The new table will be used at the next reboot.\n");
#endif
#endif
} // if
if (allOK) { // writes completed OK
- printf("The operation has completed successfully.\n");
+ printf("The operation has completed successfully.\n");
} else {
- printf("Warning! An error was reported when writing the partition table! This error\n");
- printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
- printf("necessary, restore your original partition table.\n");
+ printf("Warning! An error was reported when writing the partition table! This error\n");
+ printf("MIGHT be harmless, but you may have trashed the disk! Use parted and, if\n");
+ printf("necessary, restore your original partition table.\n");
} // if/else
- close(fd);
+ close(fd);
} else {
- fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting!\n", device, errno);
- allOK = 0;
+ fprintf(stderr, "Unable to open device %s for writing! Errno is %d! Aborting write!\n",
+ device, errno);
+ allOK = 0;
} // if/else
} else {
printf("Aborting write of new partition table.\n");
@@ -1659,7 +887,7 @@
// Now write the secondary GPT header...
if (allOK)
if (write(fd, &secondHeader, 512) == -1)
- allOK = 0;
+ allOK = 0;
// Now write the main partition tables...
if (allOK) {
@@ -1732,7 +960,7 @@
if ((val = CheckHeaderValidity()) > 0) {
if (val == 2) { // only backup header seems to be good
numParts = secondHeader.numParts;
- sizeOfEntries = secondHeader.sizeOfPartitionEntries;
+ sizeOfEntries = secondHeader.sizeOfPartitionEntries;
} else { // main header is OK
numParts = mainHeader.numParts;
sizeOfEntries = mainHeader.sizeOfPartitionEntries;
@@ -1743,7 +971,7 @@
// If current disk size doesn't match that of backup....
if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
printf("Warning! Current disk size doesn't match that of the backup!\n"
- "Adjusting sizes to match, but subsequent problems are possible!\n");
+ "Adjusting sizes to match, but subsequent problems are possible!\n");
secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
@@ -1759,9 +987,9 @@
mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
// Reverse byte order, if necessary
- if (littleEndian == 0) {
- ReversePartitionBytes();
- } // if
+ if (littleEndian == 0) {
+ ReversePartitionBytes();
+ } // if
} else {
allOK = 0;
@@ -1779,6 +1007,229 @@
return allOK;
} // GPTData::LoadGPTBackup()
+// Tell user whether Apple Partition Map (APM) was discovered....
+void GPTData::ShowAPMState(void) {
+ if (apmFound)
+ printf(" APM: present\n");
+ else
+ printf(" APM: not present\n");
+} // GPTData::ShowAPMState()
+
+// Tell user about the state of the GPT data....
+void GPTData::ShowGPTState(void) {
+ switch (state) {
+ case gpt_invalid:
+ printf(" GPT: not present\n");
+ break;
+ case gpt_valid:
+ printf(" GPT: present\n");
+ break;
+ case gpt_corrupt:
+ printf(" GPT: damaged\n");
+ break;
+ default:
+ printf("\a GPT: unknown -- bug!\n");
+ break;
+ } // switch
+} // GPTData::ShowGPTState()
+
+// Display the basic GPT data
+void GPTData::DisplayGPTData(void) {
+ int i, j;
+ char sizeInSI[255]; // String to hold size of disk in SI units
+ char tempStr[255];
+ uint64_t temp, totalFree;
+
+ BytesToSI(diskSize * blockSize, sizeInSI);
+ printf("Disk %s: %lu sectors, %s\n", device,
+ (unsigned long) diskSize, sizeInSI);
+ printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
+ printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
+ printf("First usable sector is %lu, last usable sector is %lu\n",
+ (unsigned long) mainHeader.firstUsableLBA,
+ (unsigned long) mainHeader.lastUsableLBA);
+ totalFree = FindFreeBlocks(&i, &temp);
+ printf("Total free space is %llu sectors (%s)\n", totalFree,
+ BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
+ printf("\nNumber Start (sector) End (sector) Size Code Name\n");
+ for (i = 0; i < mainHeader.numParts; i++) {
+ partitions[i].ShowSummary(i, blockSize, sizeInSI);
+ } // 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 {
+ printf("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) {
+ partitions[partNum].ShowDetails(blockSize);
+ } else {
+ printf("Partition #%d does not exist.", (int) (partNum + 1));
+ } // 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;
+
+ printf("Current partition table size is %lu.\n",
+ (unsigned long) mainHeader.numParts);
+ 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) {
+ printf("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;
+ char prompt[255];
+ int partNum, firstFreePart = 0;
+
+ // Find first free partition...
+ while (partitions[firstFreePart].GetFirstLBA() != 0) {
+ firstFreePart++;
+ } // while
+
+ if (((firstBlock = FindFirstAvailable()) != 0) &&
+ (firstFreePart < mainHeader.numParts)) {
+ lastBlock = FindLastAvailable(firstBlock);
+ 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)
+ printf("partition %d is in use.\n", partNum + 1);
+ } while (partitions[partNum].GetFirstLBA() != 0);
+
+ // Get first block for new partition...
+ sprintf(prompt,
+ "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
+ firstBlock, lastBlock, firstInLargest);
+ do {
+ sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt);
+ } while (IsFree(sector) == 0);
+ firstBlock = sector;
+
+ // Get last block for new partitions...
+ lastBlock = FindLastInFree(firstBlock);
+ sprintf(prompt,
+ "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ",
+ firstBlock, lastBlock, lastBlock);
+ do {
+ sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt);
+ } while (IsFree(sector) == 0);
+ lastBlock = sector;
+
+ partitions[partNum].SetFirstLBA(firstBlock);
+ partitions[partNum].SetLastLBA(lastBlock);
+
+ partitions[partNum].SetUniqueGUID(1);
+ partitions[partNum].ChangeType();
+ partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
+ } else {
+ printf("No free sectors available\n");
+ } // if/else
+} // GPTData::CreatePartition()
+
+// Interactively delete a partition (duh!)
+void GPTData::DeletePartition(void) {
+ int partNum;
+ uint32_t low, high;
+ uint64_t startSector, length;
+ 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);
+
+ // In case there's a protective MBR, look for & delete matching
+ // MBR partition....
+ startSector = partitions[partNum - 1].GetFirstLBA();
+ length = partitions[partNum - 1].GetLengthLBA();
+ protectiveMBR.DeleteByLocation(startSector, length);
+
+ // Now delete the GPT partition
+ partitions[partNum - 1].BlankPartition();
+ } else {
+ printf("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 {
+ printf("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.
int GPTData::DestroyGPT(void) {
@@ -1810,14 +1261,14 @@
write(fd, blankSector, 512);
lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
write(fd, blankSector, 512); // blank it out
- printf("Blank out MBR? ");
- if (GetYN() == 'Y') {
+ printf("Blank out MBR? ");
+ if (GetYN() == 'Y') {
lseek64(fd, 0, SEEK_SET);
write(fd, blankSector, 512); // blank it out
} // if blank MBR
close(fd);
- printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
- "other utilities. Program will now terminate.\n");
+ printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
+ "other utilities. Program will now terminate.\n");
} else {
printf("Problem opening %s for writing! Program will now terminate.\n");
} // if/else (fd != -1)
@@ -1825,6 +1276,680 @@
return (goOn == 'Y');
} // GPTData::DestroyGPT()
+/**************************************************************************
+ * *
+ * Partition table transformation functions (MBR or BSD disklabel to GPT) *
+ * (some of these functions may require user interaction) *
+ * *
+ **************************************************************************/
+
+// 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)
+WhichToUse GPTData::UseWhichPartitions(void) {
+ WhichToUse which = use_new;
+ MBRValidity mbrState;
+ int answer;
+
+ mbrState = protectiveMBR.GetValidity();
+
+ if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
+ printf("\n\a***************************************************************\n"
+ "Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
+ "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
+ "you don't want to convert your MBR partitions to GPT format!\n"
+ "***************************************************************\n\n");
+ which = use_mbr;
+ } // if
+
+ if ((state == gpt_invalid) && bsdFound) {
+ printf("\n\a**********************************************************************\n"
+ "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
+ "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n"
+ "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
+ "want to convert your BSD partitions to GPT format!\n"
+ "**********************************************************************\n\n");
+ which = use_bsd;
+ } // if
+
+ if ((state == gpt_valid) && (mbrState == gpt)) {
+ printf("Found valid GPT with protective MBR; using GPT.\n");
+ which = use_gpt;
+ } // if
+ if ((state == gpt_valid) && (mbrState == hybrid)) {
+ printf("Found valid GPT with hybrid MBR; using GPT.\n");
+ which = use_gpt;
+ } // if
+ if ((state == gpt_valid) && (mbrState == invalid)) {
+ printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
+ which = use_gpt;
+ protectiveMBR.MakeProtectiveMBR();
+ } // if
+ if ((state == gpt_valid) && (mbrState == mbr)) {
+ printf("Found valid MBR and GPT. Which do you want to use?\n");
+ answer = GetNumber(1, 3, 2, (char*) " 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;
+ protectiveMBR.MakeProtectiveMBR();
+ printf("Using GPT and creating fresh protective MBR.\n");
+ } else which = use_new;
+ } // if
+
+ // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
+ // problems)
+ if (state == gpt_corrupt) {
+ if ((mbrState == mbr) || (mbrState == hybrid)) {
+ printf("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, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
+ if (answer == 1) {
+ which = use_mbr;
+// protectiveMBR.MakeProtectiveMBR();
+ } else if (answer == 2) {
+ which = use_gpt;
+ } else which = use_new;
+ } else if (mbrState == invalid) {
+ printf("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, (char*) " 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....
+ printf("\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");
+ } // if/else/else
+ } // if (corrupt GPT)
+
+ if (which == use_new)
+ printf("Creating new GPT entries.\n");
+
+ return which;
+} // UseWhichPartitions()
+
+// Convert MBR partition table into GPT form
+int GPTData::XFormPartitions(void) {
+ int i, numToConvert;
+ uint8_t origType;
+ struct newGUID;
+ char name[NAME_SIZE];
+
+ // Clear out old data & prepare basics....
+ ClearGPTData();
+
+ // Convert the smaller of the # of GPT or MBR partitions
+ if (mainHeader.numParts > (NUM_LOGICALS + 4))
+ numToConvert = NUM_LOGICALS + 4;
+ else
+ numToConvert = mainHeader.numParts;
+
+ for (i = 0; i < numToConvert; i++) {
+ origType = protectiveMBR.GetType(i);
+ // don't waste CPU time trying to convert extended, hybrid protective, or
+ // null (non-existent) partitions
+ if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
+ (origType != 0x00) && (origType != 0xEE))
+ partitions[i] = protectiveMBR.AsGPT(i);
+ } // for
+
+ // Convert MBR into protective MBR
+ protectiveMBR.MakeProtectiveMBR();
+
+ // 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.
+// Returns the number of new partitions created.
+int GPTData::XFormDisklabel(int i) {
+ uint32_t low, high, partNum, startPart;
+ uint16_t hexCode;
+ int goOn = 1, numDone = 0;
+ BSDData disklabel;
+
+ if (GetPartRange(&low, &high) != 0) {
+ if ((i < low) || (i > high))
+ partNum = GetPartNum();
+ else
+ partNum = (uint32_t) i;
+
+ // 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)) {
+ printf("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(device, partitions[partNum].GetFirstLBA(),
+ partitions[partNum].GetLastLBA());
+ if ((goOn) && (disklabel.IsDisklabel())) {
+ numDone = XFormDisklabel(&disklabel, startPart);
+ if (numDone == 1)
+ printf("Converted %d BSD partition.\n", numDone);
+ else
+ printf("Converted %d BSD partitions.\n", numDone);
+ } else {
+ printf("Unable to convert partitions! Unrecognized BSD disklabel.\n");
+ } // if/else
+ } // if
+ if (numDone > 0) { // converted partitions; delete carrier
+ partitions[partNum].BlankPartition();
+ } // if
+ } else {
+ printf("No partitions\n");
+ } // if/else
+ return numDone;
+} // GPTData::XFormDisklable(int i)
+
+// Transform the partitions on an already-loaded BSD disklabel...
+int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
+ int i, numDone = 0;
+
+ if ((disklabel->IsDisklabel()) && (startPart >= 0) &&
+ (startPart < mainHeader.numParts)) {
+ for (i = 0; i < disklabel->GetNumParts(); i++) {
+ partitions[i + startPart] = disklabel->AsGPT(i);
+ if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0))
+ numDone++;
+ } // for
+ } // if
+
+ // Record that all original CRCs were OK so as not to raise flags
+ // when doing a disk verification
+ mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
+
+ return numDone;
+} // GPTData::XFormDisklabel(BSDData* disklabel)
+
+// 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];
+ int numParts, i, j, typeCode, bootable, mbrNum;
+ uint64_t length;
+ char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
+ char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
+
+ printf("\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....
+ printf("Type from one to three GPT partition numbers, separated by spaces, to be\n"
+ "added to the hybrid MBR, in sequence: ");
+ 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);
+ protectiveMBR.SetDiskSize(diskSize);
+ printf("Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ");
+ eeFirst = GetYN();
+ } // if
+
+ for (i = 0; i < numParts; i++) {
+ j = partNums[i] - 1;
+ printf("\nCreating entry for partition #%d\n", j + 1);
+ if ((j >= 0) && (j < mainHeader.numParts)) {
+ if (partitions[j].GetLastLBA() < UINT32_MAX) {
+ do {
+ printf("Enter an MBR hex code (default %02X): ",
+ typeHelper.GUIDToID(partitions[j].GetType()) / 256);
+ fgets(line, 255, stdin);
+ sscanf(line, "%x", &typeCode);
+ if (line[0] == '\n')
+ typeCode = partitions[j].GetHexType() / 256;
+ } while ((typeCode <= 0) || (typeCode > 255));
+ printf("Set the bootable flag? ");
+ bootable = (GetYN() == 'Y');
+ length = partitions[j].GetLengthLBA();
+ if (eeFirst == 'Y')
+ mbrNum = i + 1;
+ else
+ mbrNum = i;
+ protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
+ (uint32_t) length, typeCode, bootable);
+ protectiveMBR.SetHybrid();
+ } else { // partition out of range
+ printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n",
+ j + 1);
+ } // if/else
+ } else {
+ printf("Partition %d is out of range; omitting it.\n", j + 1);
+ } // if/else
+ } // for
+
+ if (numParts > 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);
+
+ // ... and for good measure, if there are any partition spaces left,
+ // optionally create another protective EFI partition to cover as much
+ // space as possible....
+ for (i = 0; i < 4; i++) {
+ if (protectiveMBR.GetType(i) == 0x00) { // unused entry....
+ if (fillItUp == 'M') {
+ printf("\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)) {
+ printf("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.
+ 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()
+
+/**********************************************************************
+ * *
+ * Functions that adjust GPT data structures WITHOUT user interaction *
+ * (they may display information for the user's benefit, though) *
+ * *
+ **********************************************************************/
+
+// Resizes GPT to specified number of entries. Creates a new table if
+// necessary, copies data if it already exists.
+int GPTData::SetGPTSize(uint32_t numEntries) {
+ struct GPTPart* newParts;
+ struct GPTPart* trash;
+ uint32_t i, high, copyNum;
+ int allOK = 1;
+
+ // First, adjust numEntries upward, if necessary, to get a number
+ // that fills the allocated sectors
+ i = blockSize / GPT_SIZE;
+ if ((numEntries % i) != 0) {
+ printf("Adjusting GPT size from %lu ", (unsigned long) numEntries);
+ numEntries = ((numEntries / i) + 1) * i;
+ printf("to %lu to fill the sector\n", (unsigned long) numEntries);
+ } // if
+
+ newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart));
+ if (newParts != NULL) {
+ if (partitions != NULL) { // existing partitions; copy them over
+ GetPartRange(&i, &high);
+ if (numEntries < (high + 1)) { // Highest entry too high for new #
+ printf("The highest-numbered partition is %lu, which is greater than the requested\n"
+ "partition table size of %d; cannot resize. Perhaps sorting will help.\n",
+ (unsigned long) (high + 1), numEntries);
+ allOK = 0;
+ } else { // go ahead with copy
+ if (numEntries < mainHeader.numParts)
+ copyNum = numEntries;
+ else
+ copyNum = mainHeader.numParts;
+ for (i = 0; i < copyNum; i++) {
+ newParts[i] = partitions[i];
+ } // for
+ trash = partitions;
+ partitions = newParts;
+ free(trash);
+ } // if
+ } else { // No existing partition table; just create it
+ partitions = newParts;
+ } // if/else existing partitions
+ mainHeader.numParts = numEntries;
+ secondHeader.numParts = numEntries;
+ mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
+ secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
+ mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
+ secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
+ secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+ if (diskSize > 0)
+ CheckGPTSize();
+ } else { // Bad memory allocation
+ fprintf(stderr, "Error allocating memory for partition table!\n");
+ allOK = 0;
+ } // if/else
+ return (allOK);
+} // GPTData::SetGPTSize()
+
+// Blank the partition array
+void GPTData::BlankPartitions(void) {
+ uint32_t i;
+
+ for (i = 0; i < mainHeader.numParts; i++) {
+ partitions[i].BlankPartition();
+ } // for
+} // GPTData::BlankPartitions()
+
+// 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) {
+ int i, lastPart = 0;
+ GPTPart temp;
+
+ // First, find the last partition with data, so as not to
+ // spend needless time sorting empty entries....
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if (partitions[i].GetFirstLBA() > 0)
+ lastPart = i;
+ } // for
+
+ // Now swap empties with the last partitions, to simplify the logic
+ // in the Quicksort function....
+ i = 0;
+ while (i < lastPart) {
+ if (partitions[i].GetFirstLBA() == 0) {
+ temp = partitions[i];
+ partitions[i] = partitions[lastPart];
+ partitions[lastPart] = temp;
+ lastPart--;
+ } // if
+ i++;
+ } // while
+
+ // Now call the recursive quick sort routine to do the real work....
+ QuickSortGPT(partitions, 0, lastPart);
+} // GPTData::SortGPT()
+
+// Set up data structures for entirely new set of partitions on the
+// specified device. Returns 1 if OK, 0 if there were problems.
+int GPTData::ClearGPTData(void) {
+ int goOn, i;
+
+ // Set up the partition table....
+ free(partitions);
+ partitions = NULL;
+ SetGPTSize(NUM_GPT_ENTRIES);
+
+ // Now initialize a bunch of stuff that's static....
+ mainHeader.signature = GPT_SIGNATURE;
+ mainHeader.revision = 0x00010000;
+ mainHeader.headerSize = (uint32_t) HEADER_SIZE;
+ mainHeader.reserved = 0;
+ mainHeader.currentLBA = UINT64_C(1);
+ mainHeader.partitionEntriesLBA = (uint64_t) 2;
+ mainHeader.sizeOfPartitionEntries = GPT_SIZE;
+ for (i = 0; i < GPT_RESERVED; i++) {
+ mainHeader.reserved2[i] = '\0';
+ } // for
+
+ // Now some semi-static items (computed based on end of disk)
+ mainHeader.backupLBA = diskSize - UINT64_C(1);
+ mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
+
+ // Set a unique GUID for the disk, based on random numbers
+ // rand() is only 32 bits, so multiply together to fill a 64-bit value
+ mainHeader.diskGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
+ mainHeader.diskGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
+
+ // Copy main header to backup header
+ RebuildSecondHeader();
+
+ // Blank out the partitions array....
+ BlankPartitions();
+
+ // Flag all CRCs as being OK....
+ mainCrcOk = 1;
+ secondCrcOk = 1;
+ mainPartsCrcOk = 1;
+ secondPartsCrcOk = 1;
+
+ return (goOn);
+} // GPTData::ClearGPTData()
+
+void GPTData::SetName(uint32_t partNum, char* theName) {
+ if ((partNum >= 0) && (partNum < mainHeader.numParts))
+ if (partitions[partNum].GetFirstLBA() > 0)
+ partitions[partNum].SetName((unsigned char*) theName);
+} // GPTData::SetName
+
+// Set the disk GUID to the specified value. Note that the header CRCs must
+// be recomputed after calling this function.
+void GPTData::SetDiskGUID(GUIDData newGUID) {
+ mainHeader.diskGUID = newGUID;
+ secondHeader.diskGUID = newGUID;
+} // SetDiskGUID()
+
+// Set the unique GUID of the specified partition. Returns 1 on
+// successful completion, 0 if there were problems (invalid
+// partition number).
+int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
+ int retval = 0;
+
+ if (pn < mainHeader.numParts) {
+ if (partitions[pn].GetFirstLBA() != UINT64_C(0)) {
+ partitions[pn].SetUniqueGUID(theGUID);
+ retval = 1;
+ } // if
+ } // if
+ return retval;
+} // GPTData::SetPartitionGUID()
+
+/********************************************************
+ * *
+ * Functions that return data about GPT data structures *
+ * (most of these are inline in gpt.h) *
+ * *
+ ********************************************************/
+
+// Find the low and high used partition numbers (numbered from 0).
+// Return value is the number of partitions found. Note that the
+// *low and *high values are both set to 0 when no partitions
+// are found, as well as when a single partition in the first
+// position exists. Thus, the return value is the only way to
+// tell when no partitions exist.
+int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
+ uint32_t i;
+ int numFound = 0;
+
+ *low = mainHeader.numParts + 1; // code for "not found"
+ *high = 0;
+ if (mainHeader.numParts > 0) { // only try if partition table exists...
+ 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...
+ if (*low == (mainHeader.numParts + 1)) *low = i;
+ numFound++;
+ } // if
+ } // for
+ } // if
+
+ // Above will leave *low pointing to its "not found" value if no partitions
+ // are defined, so reset to 0 if this is the case....
+ if (*low == (mainHeader.numParts + 1))
+ *low = 0;
+ return numFound;
+} // GPTData::GetPartRange()
+
+/****************************************************
+ * *
+ * Functions that return data about disk free space *
+ * *
+ ****************************************************/
+
+// Find the first available block after the starting point; returns 0 if
+// there are no available blocks left
+uint64_t GPTData::FindFirstAvailable(uint64_t start) {
+ uint64_t first;
+ uint32_t i;
+ int firstMoved = 0;
+
+ // Begin from the specified starting point or from the first usable
+ // LBA, whichever is greater...
+ if (start < mainHeader.firstUsableLBA)
+ first = mainHeader.firstUsableLBA;
+ else
+ first = start;
+
+ // ...now search through all partitions; if first is within an
+ // existing partition, move it to the next sector after that
+ // partition and repeat. If first was moved, set firstMoved
+ // flag; repeat until firstMoved is not set, so as to catch
+ // cases where partitions are out of sequential order....
+ do {
+ firstMoved = 0;
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((first >= partitions[i].GetFirstLBA()) &&
+ (first <= partitions[i].GetLastLBA())) { // in existing part.
+ first = partitions[i].GetLastLBA() + 1;
+ firstMoved = 1;
+ } // if
+ } // for
+ } while (firstMoved == 1);
+ if (first > mainHeader.lastUsableLBA)
+ first = 0;
+ return (first);
+} // GPTData::FindFirstAvailable()
+
+// 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) {
+ uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment;
+
+ start = 0;
+ do {
+ firstBlock = FindFirstAvailable(start);
+ if (firstBlock != UINT32_C(0)) { // something's free...
+ lastBlock = FindLastInFree(firstBlock);
+ segmentSize = lastBlock - firstBlock + UINT32_C(1);
+ if (segmentSize > selectedSize) {
+ selectedSize = segmentSize;
+ selectedSegment = firstBlock;
+ } // if
+ start = lastBlock + 1;
+ } // if
+ } while (firstBlock != 0);
+ return selectedSegment;
+} // GPTData::FindFirstInLargest()
+
+// Find the last available block on the disk at or after the start
+// block. Returns 0 if there are no available partitions after
+// (or including) start.
+uint64_t GPTData::FindLastAvailable(uint64_t start) {
+ uint64_t last;
+ uint32_t i;
+ int lastMoved = 0;
+
+ // Start by assuming the last usable LBA is available....
+ last = mainHeader.lastUsableLBA;
+
+ // ...now, similar to algorithm in FindFirstAvailable(), search
+ // through all partitions, moving last when it's in an existing
+ // partition. Set the lastMoved flag so we repeat to catch cases
+ // where partitions are out of logical order.
+ do {
+ lastMoved = 0;
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((last >= partitions[i].GetFirstLBA()) &&
+ (last <= partitions[i].GetLastLBA())) { // in existing part.
+ last = partitions[i].GetFirstLBA() - 1;
+ lastMoved = 1;
+ } // if
+ } // for
+ } while (lastMoved == 1);
+ if (last < mainHeader.firstUsableLBA)
+ last = 0;
+ return (last);
+} // GPTData::FindLastAvailable()
+
+// Find the last available block in the free space pointed to by start.
+uint64_t GPTData::FindLastInFree(uint64_t start) {
+ uint64_t nearestStart;
+ uint32_t i;
+
+ nearestStart = mainHeader.lastUsableLBA;
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((nearestStart > partitions[i].GetFirstLBA()) &&
+ (partitions[i].GetFirstLBA() > start)) {
+ nearestStart = partitions[i].GetFirstLBA() - 1;
+ } // if
+ } // for
+ return (nearestStart);
+} // GPTData::FindLastInFree()
+
+// Finds the total number of free blocks, the number of segments in which
+// they reside, and the size of the largest of those segments
+uint64_t GPTData::FindFreeBlocks(int *numSegments, uint64_t *largestSegment) {
+ uint64_t start = UINT64_C(0); // starting point for each search
+ uint64_t totalFound = UINT64_C(0); // running total
+ uint64_t firstBlock; // first block in a segment
+ uint64_t lastBlock; // last block in a segment
+ uint64_t segmentSize; // size of segment in blocks
+ int num = 0;
+
+ *largestSegment = UINT64_C(0);
+ do {
+ firstBlock = FindFirstAvailable(start);
+ if (firstBlock != UINT64_C(0)) { // something's free...
+ lastBlock = FindLastInFree(firstBlock);
+ segmentSize = lastBlock - firstBlock + UINT64_C(1);
+ if (segmentSize > *largestSegment) {
+ *largestSegment = segmentSize;
+ } // if
+ totalFound += segmentSize;
+ num++;
+ start = lastBlock + 1;
+ } // if
+ } while (firstBlock != 0);
+ *numSegments = num;
+ return totalFound;
+} // GPTData::FindFreeBlocks()
+
+// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
+int GPTData::IsFree(uint64_t sector) {
+ int isFree = 1;
+ uint32_t i;
+
+ for (i = 0; i < mainHeader.numParts; i++) {
+ if ((sector >= partitions[i].GetFirstLBA()) &&
+ (sector <= partitions[i].GetLastLBA())) {
+ isFree = 0;
+ } // if
+ } // for
+ if ((sector < mainHeader.firstUsableLBA) ||
+ (sector > mainHeader.lastUsableLBA)) {
+ isFree = 0;
+ } // if
+ return (isFree);
+} // GPTData::IsFree()
+
+/********************************
+ * *
+ * Endianness support functions *
+ * *
+ ********************************/
+
void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
ReverseBytes(&header->signature, 8);
ReverseBytes(&header->revision, 4);
@@ -1854,7 +1979,7 @@
// mismatch if there's data corruption.
if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
- "data corruption or a misplaced call to this function.\n");
+ "data corruption or a misplaced call to this function.\n");
} // if signature mismatch....
for (i = 0; i < mainHeader.numParts; i++) {
partitions[i].ReversePartBytes();
@@ -1909,8 +2034,9 @@
// Determine endianness; set allOK = 0 if running on big-endian hardware
if (IsLittleEndian() == 0) {
fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
- " tested!\nBeware!\n");
+ " tested!\nBeware!\n");
// allOK = 0;
} // if
return (allOK);
} // SizesOK()
+