cgptlib: Add support for IGNOREME GPT signature

This patch makes cgpt aware of a special "IGNOREME" GPT header signature
string that may appear in either the primary or the secondary GPT and
cause cgpt (and other cgptlib clients) to completely ignore that GPT. It
will continue to function correctly for all other purposes (using the
data from the non-ignored GPT), but never write any data back to the
ignored GPT.

BRANCH=None
BUG=chrome-os-partner:52595
TEST=unit tests

Change-Id: I7e53542385ae9d8d24dc25b75e91f4ff4917f66f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/340072
Reviewed-by: Nam Nguyen <namnguyen@google.com>
(cherry picked from commit 39910d062dffcd16683e0626dac1e7064991c7e5)
Reviewed-on: https://chromium-review.googlesource.com/354566
Reviewed-by: Bernie Thompson <bhthompson@chromium.org>
Commit-Queue: Bernie Thompson <bhthompson@chromium.org>
Tested-by: Bernie Thompson <bhthompson@chromium.org>
diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c
index 2c21cdc..29fe524 100644
--- a/cgpt/cgpt_common.c
+++ b/cgpt/cgpt_common.c
@@ -184,7 +184,11 @@
       return -1;
     }
   } else {
-    Warning("Primary GPT header is invalid\n");
+    Warning("Primary GPT header is %s\n",
+      memcmp(primary_header->signature, GPT_HEADER_SIGNATURE_IGNORED,
+             GPT_HEADER_SIGNATURE_SIZE) ? "invalid" : "being ignored");
+    drive->gpt.primary_entries = calloc(MAX_NUMBER_OF_ENTRIES,
+                                        sizeof(GptEntry));
   }
   GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
   if (CheckHeader(secondary_header, 1, drive->gpt.streaming_drive_sectors,
@@ -198,41 +202,48 @@
       return -1;
     }
   } else {
-    Warning("Secondary GPT header is invalid\n");
+    Warning("Secondary GPT header is %s\n",
+      memcmp(primary_header->signature, GPT_HEADER_SIGNATURE_IGNORED,
+             GPT_HEADER_SIGNATURE_SIZE) ? "invalid" : "being ignored");
+    drive->gpt.secondary_entries = calloc(MAX_NUMBER_OF_ENTRIES,
+                                          sizeof(GptEntry));
   }
   return 0;
 }
 
 static int GptSave(struct drive *drive) {
   int errors = 0;
-  if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
-    if (CGPT_OK != Save(drive, drive->gpt.primary_header,
-                        GPT_PMBR_SECTORS,
-                        drive->gpt.sector_bytes, GPT_HEADER_SECTORS)) {
-      errors++;
-      Error("Cannot write primary header: %s\n", strerror(errno));
-    }
-  }
-  GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
-  if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
-    if (CGPT_OK != Save(drive, drive->gpt.primary_entries,
-                        primary_header->entries_lba,
-                        drive->gpt.sector_bytes,
-                        CalculateEntriesSectors(primary_header))) {
-      errors++;
-      Error("Cannot write primary entries: %s\n", strerror(errno));
-    }
-  }
 
-  // Sync primary GPT before touching secondary so one is always valid.
-  if (drive->gpt.modified & (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1))
-    if (fsync(drive->fd) < 0 && errno == EIO) {
-      errors++;
-      Error("I/O error when trying to write primary GPT\n");
+  if (!(drive->gpt.ignored & MASK_PRIMARY)) {
+    if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
+      if (CGPT_OK != Save(drive, drive->gpt.primary_header,
+                          GPT_PMBR_SECTORS,
+                          drive->gpt.sector_bytes, GPT_HEADER_SECTORS)) {
+        errors++;
+        Error("Cannot write primary header: %s\n", strerror(errno));
+      }
     }
+    GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
+    if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
+      if (CGPT_OK != Save(drive, drive->gpt.primary_entries,
+                          primary_header->entries_lba,
+                          drive->gpt.sector_bytes,
+                          CalculateEntriesSectors(primary_header))) {
+        errors++;
+        Error("Cannot write primary entries: %s\n", strerror(errno));
+      }
+    }
+
+    // Sync primary GPT before touching secondary so one is always valid.
+    if (drive->gpt.modified & (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1))
+      if (fsync(drive->fd) < 0 && errno == EIO) {
+        errors++;
+        Error("I/O error when trying to write primary GPT\n");
+      }
+  }
 
   // Only start writing secondary GPT if primary was written correctly.
-  if (!errors) {
+  if (!errors && !(drive->gpt.ignored & MASK_SECONDARY)) {
     if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
       if(CGPT_OK != Save(drive, drive->gpt.secondary_header,
                          drive->gpt.gpt_drive_sectors - GPT_PMBR_SECTORS,
diff --git a/cgpt/cgpt_show.c b/cgpt/cgpt_show.c
index 143b5fa..638655b 100644
--- a/cgpt/cgpt_show.c
+++ b/cgpt/cgpt_show.c
@@ -250,79 +250,91 @@
     PMBRToStr(&drive->pmbr, buf, sizeof(buf)); // will exit if buf is too small
     printf(GPT_FMT, 0, GPT_PMBR_SECTORS, "", buf);
 
-    if (drive->gpt.valid_headers & MASK_PRIMARY) {
+    if (drive->gpt.ignored & MASK_PRIMARY) {
       printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
-             (int)GPT_HEADER_SECTORS, "", "Pri GPT header");
+             (int)GPT_HEADER_SECTORS, "IGNORED", "Pri GPT header");
     } else {
-      printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
-             (int)GPT_HEADER_SECTORS, "INVALID", "Pri GPT header");
+      if (drive->gpt.valid_headers & MASK_PRIMARY) {
+        printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
+               (int)GPT_HEADER_SECTORS, "", "Pri GPT header");
+      } else {
+        printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
+               (int)GPT_HEADER_SECTORS, "INVALID", "Pri GPT header");
+      }
+
+      if (params->debug ||
+          ((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
+        GptHeader *header;
+        char indent[64];
+
+        require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
+        header = (GptHeader*)drive->gpt.primary_header;
+        entries = (GptEntry*)drive->gpt.primary_entries;
+        HeaderDetails(header, entries, indent, params->numeric);
+      }
+
+      GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
+      printf(GPT_FMT, (int)primary_header->entries_lba,
+             (int)CalculateEntriesSectors(primary_header),
+             drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
+             "Pri GPT table");
+
+      if (params->debug ||
+          (drive->gpt.valid_entries & MASK_PRIMARY))
+        EntriesDetails(drive, PRIMARY, params->numeric);
     }
 
-    if (params->debug ||
-        ((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
-      GptHeader *header;
-      char indent[64];
-
-      require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
-      header = (GptHeader*)drive->gpt.primary_header;
-      entries = (GptEntry*)drive->gpt.primary_entries;
-      HeaderDetails(header, entries, indent, params->numeric);
-    }
-
-    GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
-    printf(GPT_FMT, (int)primary_header->entries_lba,
-           (int)CalculateEntriesSectors(primary_header),
-           drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
-           "Pri GPT table");
-
-    if (params->debug ||
-        (drive->gpt.valid_entries & MASK_PRIMARY))
-      EntriesDetails(drive, PRIMARY, params->numeric);
-
     /****************************** Secondary *************************/
-    GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
-    printf(GPT_FMT, (int)secondary_header->entries_lba,
-           (int)CalculateEntriesSectors(secondary_header),
-           drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
-           "Sec GPT table");
-    /* We show secondary table details if any of following is true.
-     *   1. in debug mode.
-     *   2. only secondary is valid.
-     *   3. secondary is not identical to promary.
-     */
-    if (params->debug ||
-        ((drive->gpt.valid_entries & MASK_SECONDARY) &&
-         (!(drive->gpt.valid_entries & MASK_PRIMARY) ||
-          memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries,
-                 secondary_header->number_of_entries *
-                 secondary_header->size_of_entry)))) {
-      EntriesDetails(drive, SECONDARY, params->numeric);
-    }
-
-    if (drive->gpt.valid_headers & MASK_SECONDARY)
+    if (drive->gpt.ignored & MASK_SECONDARY) {
       printf(GPT_FMT, (int)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
-             (int)GPT_HEADER_SECTORS, "", "Sec GPT header");
-    else
-      printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
-             (int)GPT_HEADER_SECTORS, "INVALID", "Sec GPT header");
-    /* We show secondary header if any of following is true:
-     *   1. in debug mode.
-     *   2. only secondary is valid.
-     *   3. secondary is not synonymous to primary.
-     */
-    if (params->debug ||
-        ((drive->gpt.valid_headers & MASK_SECONDARY) &&
-         (!(drive->gpt.valid_headers & MASK_PRIMARY) ||
-          !IsSynonymous((GptHeader*)drive->gpt.primary_header,
-                        (GptHeader*)drive->gpt.secondary_header)) &&
-         params->verbose)) {
-      GptHeader *header;
-      char indent[64];
+             (int)GPT_HEADER_SECTORS, "IGNORED", "Sec GPT header");
+    } else {
+      GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
+      printf(GPT_FMT, (int)secondary_header->entries_lba,
+             (int)CalculateEntriesSectors(secondary_header),
+             drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
+             "Sec GPT table");
+      /* We show secondary table details if any of following is true.
+       *   1. in debug mode.
+       *   2. primary table is being ignored
+       *   3. only secondary is valid.
+       *   4. secondary is not identical to promary.
+       */
+      if (params->debug || (drive->gpt.ignored & MASK_PRIMARY) ||
+          ((drive->gpt.valid_entries & MASK_SECONDARY) &&
+           (!(drive->gpt.valid_entries & MASK_PRIMARY) ||
+            memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries,
+                   secondary_header->number_of_entries *
+                   secondary_header->size_of_entry)))) {
+        EntriesDetails(drive, SECONDARY, params->numeric);
+      }
 
-      require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
-      header = (GptHeader*)drive->gpt.secondary_header;
-      entries = (GptEntry*)drive->gpt.secondary_entries;
-      HeaderDetails(header, entries, indent, params->numeric);
+      if (drive->gpt.valid_headers & MASK_SECONDARY) {
+        printf(GPT_FMT, (int)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
+               (int)GPT_HEADER_SECTORS, "", "Sec GPT header");
+      } else {
+        printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
+               (int)GPT_HEADER_SECTORS, "INVALID", "Sec GPT header");
+      }
+      /* We show secondary header if any of following is true:
+       *   1. in debug mode.
+       *   2. only secondary is valid.
+       *   3. secondary is not synonymous to primary and not ignored.
+       */
+      if (params->debug ||
+          ((drive->gpt.valid_headers & MASK_SECONDARY) &&
+           (!(drive->gpt.valid_headers & MASK_PRIMARY) ||
+            !IsSynonymous((GptHeader*)drive->gpt.primary_header,
+                          (GptHeader*)drive->gpt.secondary_header)) &&
+           params->verbose)) {
+        GptHeader *header;
+        char indent[64];
+
+        require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
+        header = (GptHeader*)drive->gpt.secondary_header;
+        entries = (GptEntry*)drive->gpt.secondary_entries;
+        HeaderDetails(header, entries, indent, params->numeric);
+      }
     }
   }
 
diff --git a/firmware/include/gpt.h b/firmware/include/gpt.h
index ed8bfce..275b3ae 100644
--- a/firmware/include/gpt.h
+++ b/firmware/include/gpt.h
@@ -19,6 +19,9 @@
 /* From https://chromium-review.googlesource.com/31264 */
 #define GPT_HEADER_SIGNATURE2 "CHROMEOS"
 
+/* From http://crosbug.com/p/52595 */
+#define GPT_HEADER_SIGNATURE_IGNORED "IGNOREME"
+
 /*
  * The first 3 numbers should be stored in network-endian format according to
  * the GUID RFC.  The UEFI spec appendix A claims they should be stored in
diff --git a/firmware/include/gpt_misc.h b/firmware/include/gpt_misc.h
index 9f442c4..0ecbba4 100644
--- a/firmware/include/gpt_misc.h
+++ b/firmware/include/gpt_misc.h
@@ -108,7 +108,7 @@
 	int current_kernel;
 
 	/* Internal variables */
-	uint32_t valid_headers, valid_entries;
+	uint8_t valid_headers, valid_entries, ignored;
 	int current_priority;
 } GptData;
 
diff --git a/firmware/lib/cgptlib/cgptlib_internal.c b/firmware/lib/cgptlib/cgptlib_internal.c
index 2975630..7d402df 100644
--- a/firmware/lib/cgptlib/cgptlib_internal.c
+++ b/firmware/lib/cgptlib/cgptlib_internal.c
@@ -245,6 +245,7 @@
 
 	gpt->valid_headers = 0;
 	gpt->valid_entries = 0;
+	gpt->ignored = 0;
 
 	retval = CheckParameters(gpt);
 	if (retval != GPT_SUCCESS)
@@ -255,12 +256,18 @@
 			     gpt->gpt_drive_sectors, gpt->flags)) {
 		gpt->valid_headers |= MASK_PRIMARY;
 		goodhdr = header1;
+	} else if (header1 && !Memcmp(header1->signature,
+		   GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) {
+		gpt->ignored |= MASK_PRIMARY;
 	}
 	if (0 == CheckHeader(header2, 1, gpt->streaming_drive_sectors,
 			     gpt->gpt_drive_sectors, gpt->flags)) {
 		gpt->valid_headers |= MASK_SECONDARY;
 		if (!goodhdr)
 			goodhdr = header2;
+	} else if (header2 && !Memcmp(header2->signature,
+		   GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) {
+		gpt->ignored |= MASK_SECONDARY;
 	}
 
 	if (!gpt->valid_headers)
@@ -309,6 +316,15 @@
 	    0 != HeaderFieldsSame(header1, header2))
 		gpt->valid_headers &= ~MASK_SECONDARY;
 
+	/*
+	 * When we're ignoring a GPT, make it look in memory like the other one
+	 * and pretend that everything is fine (until we try to save).
+	 */
+	if (MASK_NONE != gpt->ignored) {
+		GptRepair(gpt);
+		gpt->modified = 0;
+	}
+
 	return GPT_SUCCESS;
 }
 
diff --git a/firmware/lib/gpt_misc.c b/firmware/lib/gpt_misc.c
index cff9221..0bf0940 100644
--- a/firmware/lib/gpt_misc.c
+++ b/firmware/lib/gpt_misc.c
@@ -28,6 +28,8 @@
 
 	/* No data to be written yet */
 	gptdata->modified = 0;
+	/* This should get overwritten by GptInit() */
+	gptdata->ignored = 0;
 
 	/* Allocate all buffers */
 	gptdata->primary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes);
@@ -67,7 +69,11 @@
 			primary_valid = 0;
 		}
 	} else {
-		VBDEBUG(("Primary GPT header invalid!\n"));
+		VBDEBUG(("Primary GPT header is %s\n",
+			 Memcmp(primary_header->signature,
+				GPT_HEADER_SIGNATURE_IGNORED,
+				GPT_HEADER_SIGNATURE_SIZE)
+			 ? "invalid" : "being ignored"));
 	}
 
 	/* Read secondary header from the end of the drive */
@@ -96,7 +102,11 @@
 			secondary_valid = 0;
 		}
 	} else {
-		VBDEBUG(("Secondary GPT header invalid!\n"));
+		VBDEBUG(("Secondary GPT header is %s\n",
+			 Memcmp(secondary_header->signature,
+				GPT_HEADER_SIGNATURE_IGNORED,
+				GPT_HEADER_SIGNATURE_SIZE)
+			 ? "invalid" : "being ignored"));
 	}
 
 	/* Return 0 if least one GPT header was valid */
@@ -110,7 +120,7 @@
  */
 int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
 {
-	int legacy = 0;
+	int skip_primary = 0;
 	GptHeader *header = (GptHeader *)gptdata->primary_header;
 	uint64_t entries_bytes = header->number_of_entries
 				* header->size_of_entry;
@@ -126,19 +136,16 @@
 		GptHeader *h = (GptHeader *)(gptdata->primary_header);
 		entries_lba = h->entries_lba;
 
-		/*
-		 * Avoid even looking at this data if we don't need to. We
-		 * may in fact not have read it from disk if the read failed,
-		 * and this avoids a valgrind complaint.
-		 */
-		if (gptdata->modified) {
-			legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2,
-					GPT_HEADER_SIGNATURE_SIZE);
-		}
-		if (gptdata->modified & GPT_MODIFIED_HEADER1) {
-			if (legacy) {
-				VBDEBUG(("Not updating GPT header 1: "
+		if (gptdata->ignored & MASK_PRIMARY) {
+			VBDEBUG(("Not updating primary GPT: "
+				 "marked to be ignored.\n"));
+			skip_primary = 1;
+		} else if (gptdata->modified & GPT_MODIFIED_HEADER1) {
+			if (!Memcmp(h->signature, GPT_HEADER_SIGNATURE2,
+				    GPT_HEADER_SIGNATURE_SIZE)) {
+				VBDEBUG(("Not updating primary GPT: "
 					 "legacy mode is enabled.\n"));
+				skip_primary = 1;
 			} else {
 				VBDEBUG(("Updating GPT header 1\n"));
 				if (0 != VbExDiskWrite(disk_handle, 1, 1,
@@ -148,28 +155,23 @@
 		}
 	}
 
-	if (gptdata->primary_entries) {
+	if (gptdata->primary_entries && !skip_primary) {
 		if (gptdata->modified & GPT_MODIFIED_ENTRIES1) {
-			if (legacy) {
-				VBDEBUG(("Not updating GPT entries 1: "
-					 "legacy mode is enabled.\n"));
-			} else {
-				VBDEBUG(("Updating GPT entries 1\n"));
-				if (0 != VbExDiskWrite(disk_handle, entries_lba,
-						entries_sectors,
-						gptdata->primary_entries))
-					goto fail;
-			}
+			VBDEBUG(("Updating GPT entries 1\n"));
+			if (0 != VbExDiskWrite(disk_handle, entries_lba,
+					       entries_sectors,
+					       gptdata->primary_entries))
+				goto fail;
 		}
 	}
 
 	entries_lba = (gptdata->gpt_drive_sectors - entries_sectors -
 		GPT_HEADER_SECTORS);
-	if (gptdata->secondary_header) {
+	if (gptdata->secondary_header && !(gptdata->ignored & MASK_SECONDARY)) {
 		GptHeader *h = (GptHeader *)(gptdata->secondary_header);
 		entries_lba = h->entries_lba;
 		if (gptdata->modified & GPT_MODIFIED_HEADER2) {
-			VBDEBUG(("Updating GPT entries 2\n"));
+			VBDEBUG(("Updating GPT header 2\n"));
 			if (0 != VbExDiskWrite(disk_handle,
 					       gptdata->gpt_drive_sectors - 1, 1,
 					       gptdata->secondary_header))
@@ -177,12 +179,12 @@
 		}
 	}
 
-	if (gptdata->secondary_entries) {
+	if (gptdata->secondary_entries && !(gptdata->ignored & MASK_SECONDARY)){
 		if (gptdata->modified & GPT_MODIFIED_ENTRIES2) {
-			VBDEBUG(("Updating GPT header 2\n"));
+			VBDEBUG(("Updating GPT entries 2\n"));
 			if (0 != VbExDiskWrite(disk_handle,
-				entries_lba, entries_sectors,
-				gptdata->secondary_entries))
+					       entries_lba, entries_sectors,
+					       gptdata->secondary_entries))
 				goto fail;
 		}
 	}
diff --git a/tests/cgptlib_test.c b/tests/cgptlib_test.c
index 6174752..1e8df25 100644
--- a/tests/cgptlib_test.c
+++ b/tests/cgptlib_test.c
@@ -827,6 +827,7 @@
 {
 	GptData *gpt = GetEmptyGptData();
 	GptHeader *h1 = (GptHeader *)gpt->primary_header;
+	GptHeader *h2 = (GptHeader *)gpt->secondary_header;
 	GptEntry *e1 = (GptEntry *)gpt->primary_entries;
 	uint8_t *tempptr;
 
@@ -835,11 +836,13 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(MASK_NONE == gpt->ignored);
 	/* Repair doesn't damage it */
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(0 == gpt->modified);
 
 	/* Invalid sector size should fail */
@@ -854,11 +857,13 @@
 	EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt));
 	EXPECT(0 == gpt->valid_headers);
 	EXPECT(0 == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	/* Repair can't fix completely busted headers */
 	GptRepair(gpt);
 	EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt));
 	EXPECT(0 == gpt->valid_headers);
 	EXPECT(0 == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(0 == gpt->modified);
 
 	BuildTestGptData(gpt);
@@ -866,10 +871,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_SECONDARY == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(GPT_MODIFIED_HEADER1 == gpt->modified);
 
 	BuildTestGptData(gpt);
@@ -877,10 +884,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified);
 
 	/*
@@ -893,10 +902,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified);
 
 	/* Modify entries */
@@ -906,11 +917,13 @@
 	EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_NONE == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	/* Repair can't fix both copies of entries being bad, either. */
 	GptRepair(gpt);
 	EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_NONE == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(0 == gpt->modified);
 
 	BuildTestGptData(gpt);
@@ -918,10 +931,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_SECONDARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified);
 
 	BuildTestGptData(gpt);
@@ -929,10 +944,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_PRIMARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified);
 
 	/*
@@ -949,6 +966,7 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_SECONDARY == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	gpt->primary_entries = tempptr;
 
 	/* Modify both header and entries */
@@ -958,10 +976,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_SECONDARY == gpt->valid_headers);
 	EXPECT(MASK_SECONDARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1) == gpt->modified);
 
 	BuildTestGptData(gpt);
@@ -970,10 +990,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_headers);
 	EXPECT(MASK_PRIMARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified);
 
 	/* Test cross-correction (h1+e2, h2+e1) */
@@ -983,10 +1005,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_SECONDARY == gpt->valid_headers);
 	EXPECT(MASK_PRIMARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES2) == gpt->modified);
 
 	BuildTestGptData(gpt);
@@ -995,10 +1019,12 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_headers);
 	EXPECT(MASK_SECONDARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES1) == gpt->modified);
 
 	/*
@@ -1011,35 +1037,90 @@
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_headers);
 	EXPECT(MASK_PRIMARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	GptRepair(gpt);
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_BOTH == gpt->valid_headers);
 	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified);
 
 	/* Test unloaded entry array. */
-	gpt = GetEmptyGptData();
 	BuildTestGptData(gpt);
 	gpt->primary_entries = NULL;
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_SECONDARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
 	gpt = GetEmptyGptData();
 	BuildTestGptData(gpt);
 	gpt->secondary_entries = NULL;
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_entries);
+	EXPECT(0 == gpt->ignored);
+	gpt = GetEmptyGptData();
 
 	/* Test unloaded header. */
-	gpt = GetEmptyGptData();
 	BuildTestGptData(gpt);
 	gpt->primary_header = NULL;
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_SECONDARY == gpt->valid_headers);
+	EXPECT(0 == gpt->ignored);
 	gpt = GetEmptyGptData();
 	BuildTestGptData(gpt);
 	gpt->secondary_header = NULL;
 	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
 	EXPECT(MASK_PRIMARY == gpt->valid_headers);
+	EXPECT(0 == gpt->ignored);
+	gpt = GetEmptyGptData();
+
+	/* Test correct recognition of IGNOREME in primary GPT. */
+	BuildTestGptData(gpt);
+	Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE);
+	Memset(gpt->primary_header, 0, sizeof(GptHeader));
+	Memcpy(h1->signature, GPT_HEADER_SIGNATURE_IGNORED,
+	       GPT_HEADER_SIGNATURE_SIZE);
+	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
+	EXPECT(MASK_BOTH == gpt->valid_headers);
+	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(MASK_PRIMARY == gpt->ignored);
+	EXPECT(0 == gpt->modified);
+
+	/* Test correct recognition of IGNOREME in secondary GPT. */
+	BuildTestGptData(gpt);
+	Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE);
+	Memset(gpt->secondary_header, 0, sizeof(GptHeader));
+	Memcpy(h2->signature, GPT_HEADER_SIGNATURE_IGNORED,
+	       GPT_HEADER_SIGNATURE_SIZE);
+	EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
+	EXPECT(MASK_BOTH == gpt->valid_headers);
+	EXPECT(MASK_BOTH == gpt->valid_entries);
+	EXPECT(MASK_SECONDARY == gpt->ignored);
+	EXPECT(0 == gpt->modified);
+
+	/* Test that two IGNOREME GPTs are invalid. */
+	ZeroHeadersEntries(gpt);
+	Memcpy(h1->signature, GPT_HEADER_SIGNATURE_IGNORED,
+	       GPT_HEADER_SIGNATURE_SIZE);
+	Memcpy(h2->signature, GPT_HEADER_SIGNATURE_IGNORED,
+	       GPT_HEADER_SIGNATURE_SIZE);
+	EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt));
+	EXPECT(0 == gpt->valid_headers);
+	EXPECT(0 == gpt->valid_entries);
+	EXPECT(MASK_BOTH == gpt->ignored);
+	EXPECT(0 == gpt->modified);
+
+	/* Test that one IGNOREME GPT and one corrupted one are invalid. */
+	BuildTestGptData(gpt);
+	Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE);
+	Memset(gpt->primary_header, 0, sizeof(GptHeader));
+	Memcpy(h1->signature, GPT_HEADER_SIGNATURE_IGNORED,
+	       GPT_HEADER_SIGNATURE_SIZE);
+	gpt->secondary_entries[0]++;
+	EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt));
+	EXPECT(MASK_SECONDARY == gpt->valid_headers);
+	EXPECT(0 == gpt->valid_entries);
+	EXPECT(MASK_PRIMARY == gpt->ignored);
+	EXPECT(0 == gpt->modified);
 
 	return TEST_OK;
 }