From 5de0000ece70bf419130db9bdbaf444ffc98bf30 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Tue, 19 Apr 2016 13:20:52 -0700 Subject: cgpt: Fully write out primary GPT before starting to write secondary 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 Reviewed-on: https://chromium-review.googlesource.com/340071 Reviewed-by: Nam Nguyen --- cgpt/cgpt_common.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c index 5bb44f7a..2c21cdc2 100644 --- a/cgpt/cgpt_common.c +++ b/cgpt/cgpt_common.c @@ -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)); + } } } -- cgit v1.2.1