Skip to content

Commit

Permalink
Write invalid GPT header before writing entries
Browse files Browse the repository at this point in the history
Once the entries are safely on disk, overwrite the invalid header (all
zeros) with a valid one.

This ensures that there is never a valid header with the new sector size
and incompletely written entries, even in the case of a CRC32 collision.
However, it provides no guarantees about the old header.  A future
commit will clobber that, too, either by replacing it and the protective
MBR with a correct protective MBR followed by zeroes or by replacing the
backup GPT header with zeroes.
  • Loading branch information
DemiMarie committed Nov 17, 2024
1 parent a2f5128 commit 9b5a62a
Showing 1 changed file with 32 additions and 16 deletions.
48 changes: 32 additions & 16 deletions gptfixer/gpt.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,27 @@ static void print_gpts(struct GPT *gpt) {
}
}

static void sync_pwrite(int fd, const void *data, uint64_t length,
uint64_t offset) {
CHECK(fd >= 0);
CHECK(data != NULL);
CHECK(length <= SSIZE_MAX);
CHECK((off_t)offset >= 0);
CHECK((uint64_t)(off_t)offset == offset);

ssize_t res = pwrite64(fd, data, (size_t)length, (off_t)offset);
CHECK(res >= -1);
if (res == -1)
err(1, "write error");
CHECK((size_t)res <= length);
if ((size_t)res != length)
err(1,
"short write: expected to write %" PRIu64 " bytes, but only wrote %zd",
length, res);
if (fsync(fd))
err(1, "fsync() failed");
}

static void create_new_gpt(int fd, bool used_primary, uint64_t sectors,
uint32_t sector_size, uint32_t new_sector_size,
struct GPT *gpt) {
Expand All @@ -753,22 +774,17 @@ static void create_new_gpt(int fd, bool used_primary, uint64_t sectors,
abort();
void *entries = new_gpt->entries;
CHECK(entries != NULL);
new_gpt->used_entries = 0;
new_gpt->entries = NULL;
ssize_t res = pwrite64(fd, new_gpt, sector_size,
(off_t)(new_gpt->header.MyLBA * sector_size));
if (res != sector_size)
err(1, "short write or write error");
if (fsync(fd))
err(1, "fsync() failed");
res = pwrite64(fd, entries, to_write,
(off_t)(new_gpt->header.PartitionEntryLBA * sector_size));
if (res < 0)
err(1, "write error");
if ((size_t)res != to_write)
err(1, "short write or write error");
if (fsync(fd))
err(1, "fsync() failed");
memset((char *)new_gpt + offsetof(struct GPT, used_entries), 0,
sector_size - offsetof(struct GPT, used_entries));
char *buf = aligned_alloc(sector_size, sector_size);
if (buf == NULL)
err(1, "aligned_alloc(%" PRIu32 ", %" PRIu32 ")", sector_size, sector_size);
memset(buf, 0, sector_size);
sync_pwrite(fd, buf, sector_size, new_gpt->header.MyLBA * sector_size);
sync_pwrite(fd, entries, to_write,
new_gpt->header.PartitionEntryLBA * sector_size);
sync_pwrite(fd, new_gpt, sector_size, new_gpt->header.MyLBA * sector_size);
free(buf);
free(new_gpt);
free(entries);
}
Expand Down

0 comments on commit 9b5a62a

Please sign in to comment.