From 247657a5acbb7eb21c336ba84a68b801b7c19be0 Mon Sep 17 00:00:00 2001
From: srs5694 <srs5694@users.sourceforge.net>
Date: Thu, 26 Nov 2009 18:36:12 -0500
Subject: [PATCH] 0.5.1-pre3; more bug fixes related to handling of mislocated
 secondary header and partition table.

---
 CHANGELOG  |   4 ++
 gdisk.cc   |   4 +-
 gpt.cc     | 111 ++++++++++++++++++++++++++++++-----------------------
 gpt.h      |   2 +-
 support.cc |   3 +-
 5 files changed, 73 insertions(+), 51 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index c853e5b..9667715 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,6 +13,10 @@
 - Added 'e' option (relocate backup GPT data structures) to the experts'
   menu.
 
+- Fixed bug that prevented recovery of partitions in case of partially
+  damaged GPT data (bad main and good backup or bad backup and good
+  main header, for instance).
+
 0.5.0:
 ------
 
diff --git a/gdisk.cc b/gdisk.cc
index ef42ea8..bfee217 100644
--- a/gdisk.cc
+++ b/gdisk.cc
@@ -29,7 +29,7 @@ int main(int argc, char* argv[]) {
    int doMore = 1;
    char* device = NULL;
 
-   printf("GPT fdisk (gdisk) version 0.5.1-pre2\n\n");
+   printf("GPT fdisk (gdisk) version 0.5.1-pre3\n\n");
 
     if (argc == 2) { // basic usage
       if (SizesOK()) {
@@ -311,7 +311,7 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
             break;
          case 'e': case 'E':
             printf("Relocating backup data structures to the end of the disk\n");
-            theGPT->FixSecondHeaderLocation();
+            theGPT->MoveSecondHeaderToEnd();
             break;
          case 'g': case 'G':
             printf("Enter the disk's unique GUID:\n");
diff --git a/gpt.cc b/gpt.cc
index f339a72..2e99714 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -124,8 +124,8 @@ int GPTData::Verify(void) {
       problems++;
       printf("\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
              "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
-             "option on the experts' menu to adjust the secondary header's and partition"
-             "table's locations.");
+             "option on the experts' menu to adjust the secondary header's and partition\n"
+             "table's locations.\n");
    } // if
 
    // Now check that critical main and backup GPT entries match each other
@@ -676,7 +676,7 @@ int GPTData::ForceLoadGPTData(int fd) {
    return allOK;
 } // GPTData::ForceLoadGPTData()
 
-// Loads the partition tables pointed to by the main GPT header. The
+// Loads the partition table 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) {
@@ -753,12 +753,24 @@ int GPTData::SaveGPTData(void) {
 
    // Check that disk is really big enough to handle this...
    if (mainHeader.backupLBA > diskSize) {
-      fprintf(stderr, "Error! Disk is too small -- either the original MBR is corrupt or you're\n");
-      fprintf(stderr, "working from an MBR copied to a file! Aborting!\n");
+      fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
+              "problem (or it might not). Aborting!\n");
       printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
              mainHeader.backupLBA);
       allOK = 0;
    } // if
+   // Check that second header is properly placed. Warn and ask if this should
+   // be corrected if the test fails....
+   if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
+      printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
+             "correct this problem? ");
+      if (GetYN() == 'Y') {
+         MoveSecondHeaderToEnd();
+         printf("Have moved second header and partition table to correct location.\n");
+      } else {
+         printf("Have not corrected the problem. Strange problems may occur in the future!\n");
+      } // if correction requested
+   } // if
 
    // Check for overlapping partitions....
    if (FindOverlaps() > 0) {
@@ -988,10 +1000,7 @@ int GPTData::LoadGPTBackup(char* filename) {
          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");
-            secondHeader.currentLBA = mainHeader.backupLBA = diskSize - UINT64_C(1);
-            mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
-            secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
-            secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+            MoveSecondHeaderToEnd();
          } // if
 
          // Load main partition table, and record whether its CRC
@@ -1398,6 +1407,7 @@ WhichToUse GPTData::UseWhichPartitions(void) {
                "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
                "verification and recovery are STRONGLY recommended.\n"
                "****************************************************************************\n");
+         which = use_gpt;
       } // if/else/else
    } // if (corrupt GPT)
 
@@ -1703,42 +1713,47 @@ int GPTData::SetGPTSize(uint32_t numEntries) {
       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;
+   // Do the work only if the # of partitions is changing. Along with being
+   // efficient, this prevents mucking the with location of the secondary
+   // partition table, which causes problems when loading data from a RAID
+   // array that's been expanded because this function is called when loading
+   // data.
+   if ((numEntries != mainHeader.numParts) || (partitions == NULL)) {
+      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;
-            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 existing partitions
+         mainHeader.numParts = numEntries;
+         secondHeader.numParts = numEntries;
+         mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + 2 ;
+         secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
+         MoveSecondHeaderToEnd();
+         if (diskSize > 0)
+            CheckGPTSize();
+      } else { // Bad memory allocation
+         fprintf(stderr, "Error allocating memory for partition table!\n");
+         allOK = 0;
+      } // if/else
    } // if/else
    return (allOK);
 } // GPTData::SetGPTSize()
@@ -1832,9 +1847,11 @@ int GPTData::ClearGPTData(void) {
    return (goOn);
 } // GPTData::ClearGPTData()
 
-// Set the location of the second GPT header data to the correct location.
-// Intended to help users of RAID arrays that have been resized.
-void GPTData::FixSecondHeaderLocation() {
+// Set the location of the second GPT header data to the end of the disk.
+// Used internally and called by the 'e' option on the recovery &
+// transformation menu, to help users of RAID arrays who add disk space
+// to their arrays.
+void GPTData::MoveSecondHeaderToEnd() {
    mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
    mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
    secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
diff --git a/gpt.h b/gpt.h
index c265169..f22bd61 100644
--- a/gpt.h
+++ b/gpt.h
@@ -127,7 +127,7 @@ public:
    void BlankPartitions(void);
    void SortGPT(void);
    int ClearGPTData(void);
-   void FixSecondHeaderLocation();
+   void MoveSecondHeaderToEnd();
    void SetName(uint32_t partNum, char* theName = NULL);
    void SetDiskGUID(GUIDData newGUID);
    int SetPartitionGUID(uint32_t pn, GUIDData theGUID);
diff --git a/support.cc b/support.cc
index 337dfce..bb419ed 100644
--- a/support.cc
+++ b/support.cc
@@ -480,6 +480,7 @@ uint64_t disksize(int fd, int *err) {
          sectors = bytes / UINT64_C(512);
       } // if
    } // if
-//   printf("In disksize(), sectors is %lld.\n", sectors);
+//   sectors = 25000000;
+//   printf("Returning bogus sector size: %d\n", sectors);
    return sectors;
 }
-- 
GitLab