cgpt: Fully write out primary GPT before starting to write secondary
authorJulius Werner <jwerner@chromium.org>
Tue, 19 Apr 2016 20:20:52 +0000 (13:20 -0700)
committerchrome-bot <chrome-bot@chromium.org>
Mon, 25 Apr 2016 22:15:32 +0000 (15:15 -0700)
The point of having two GPTs is to always have a known good one if one
of them gets corrupted. One of the most obvious ways that could happen
is if the write stopped half-way through (e.g. due to a crash or random
power loss).

Unfortunately, the way we currently save modified GPTs can leave both
copies invalid if we stop writing at just the wrong time. Since a GPT
header contains a checksum over the GPT entries, we need to write both
the header and entries for one GPT (and make sure they're synced to
disk) before we start writing the other.

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

Change-Id: I2d4b56bcfba9a94395af5896f274ebade9e39081
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/340071
Reviewed-by: Nam Nguyen <namnguyen@google.com>
cgpt/cgpt_common.c

index 5bb44f7..2c21cdc 100644 (file)
@@ -213,15 +213,6 @@ static int GptSave(struct drive *drive) {
       Error("Cannot write primary header: %s\n", strerror(errno));
     }
   }
-
-  if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
-    if(CGPT_OK != Save(drive, drive->gpt.secondary_header,
-                       drive->gpt.gpt_drive_sectors - GPT_PMBR_SECTORS,
-                       drive->gpt.sector_bytes, GPT_HEADER_SECTORS)) {
-      errors++;
-      Error("Cannot write secondary 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,
@@ -232,14 +223,33 @@ static int GptSave(struct drive *drive) {
       Error("Cannot write primary entries: %s\n", strerror(errno));
     }
   }
-  GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
-  if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
-    if (CGPT_OK != Save(drive, drive->gpt.secondary_entries,
-                        secondary_header->entries_lba,
-                        drive->gpt.sector_bytes,
-                        CalculateEntriesSectors(secondary_header))) {
+
+  // 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("Cannot write secondary entries: %s\n", strerror(errno));
+      Error("I/O error when trying to write primary GPT\n");
+    }
+
+  // Only start writing secondary GPT if primary was written correctly.
+  if (!errors) {
+    if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
+      if(CGPT_OK != Save(drive, drive->gpt.secondary_header,
+                         drive->gpt.gpt_drive_sectors - GPT_PMBR_SECTORS,
+                         drive->gpt.sector_bytes, GPT_HEADER_SECTORS)) {
+        errors++;
+        Error("Cannot write secondary header: %s\n", strerror(errno));
+      }
+    }
+    GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
+    if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
+      if (CGPT_OK != Save(drive, drive->gpt.secondary_entries,
+                          secondary_header->entries_lba,
+                          drive->gpt.sector_bytes,
+                          CalculateEntriesSectors(secondary_header))) {
+        errors++;
+        Error("Cannot write secondary entries: %s\n", strerror(errno));
+      }
     }
   }