gpt: add new repair function to sync up primary and backup tables.

This commit is contained in:
Michael Marineau 2014-10-18 18:21:07 -07:00
parent dc6076187e
commit 478458d404
3 changed files with 142 additions and 0 deletions

View file

@ -31,6 +31,20 @@ GRUB_MOD_LICENSE ("GPLv3+");
static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC; static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC;
static grub_uint64_t
grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size)
{
unsigned int sector_size;
grub_uint64_t sectors;
sector_size = 1U << gpt->log_sector_size;
sectors = size / sector_size;
if (size % sector_size)
sectors++;
return sectors;
}
static grub_err_t static grub_err_t
grub_gpt_lecrc32 (void *data, grub_size_t len, grub_uint32_t *crc) grub_gpt_lecrc32 (void *data, grub_size_t len, grub_uint32_t *crc)
{ {
@ -275,6 +289,82 @@ fail:
return NULL; return NULL;
} }
grub_err_t
grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt)
{
grub_uint64_t backup_header, backup_entries;
grub_uint32_t crc;
if (disk->log_sector_size != gpt->log_sector_size)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"GPT sector size must match disk sector size");
if (!(gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID ||
gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID))
return grub_error (GRUB_ERR_BUG, "No valid GPT entries");
if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID)
{
backup_header = grub_le_to_cpu64 (gpt->primary.alternate_lba);
grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup));
}
else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID)
{
backup_header = grub_le_to_cpu64 (gpt->backup.header_lba);
grub_memcpy (&gpt->primary, &gpt->backup, sizeof (gpt->primary));
}
else
return grub_error (GRUB_ERR_BUG, "No valid GPT header");
/* Relocate backup to end if disk whenever possible. */
if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
backup_header = disk->total_sectors - 1;
backup_entries = backup_header -
grub_gpt_size_to_sectors (gpt, gpt->entries_size);
/* Update/fixup header and partition table locations. */
gpt->primary.header_lba = grub_cpu_to_le64_compile_time (1);
gpt->primary.alternate_lba = grub_cpu_to_le64 (backup_header);
gpt->primary.partitions = grub_cpu_to_le64_compile_time (2);
gpt->backup.header_lba = gpt->primary.alternate_lba;
gpt->backup.alternate_lba = gpt->primary.header_lba;
gpt->backup.partitions = grub_cpu_to_le64 (backup_entries);
/* Writing headers larger than our header structure are unsupported. */
gpt->primary.headersize =
grub_cpu_to_le32_compile_time (sizeof (gpt->primary));
gpt->backup.headersize =
grub_cpu_to_le32_compile_time (sizeof (gpt->backup));
/* Recompute checksums. */
if (grub_gpt_lecrc32 (gpt->entries, gpt->entries_size, &crc))
return grub_errno;
gpt->primary.partentry_crc32 = crc;
gpt->backup.partentry_crc32 = crc;
if (grub_gpt_header_lecrc32 (&gpt->primary, &gpt->primary.crc32))
return grub_errno;
if (grub_gpt_header_lecrc32 (&gpt->backup, &gpt->backup.crc32))
return grub_errno;
/* Sanity check. */
if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size))
return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header");
if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size))
return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header");
gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID |
GRUB_GPT_PRIMARY_ENTRIES_VALID |
GRUB_GPT_BACKUP_HEADER_VALID |
GRUB_GPT_BACKUP_ENTRIES_VALID);
return GRUB_ERR_NONE;
}
void void
grub_gpt_free (grub_gpt_t gpt) grub_gpt_free (grub_gpt_t gpt)
{ {

View file

@ -141,6 +141,9 @@ grub_gpt_sector_to_addr (grub_gpt_t gpt, grub_uint64_t sector)
/* Allocates and fills new grub_gpt structure, free with grub_gpt_free. */ /* Allocates and fills new grub_gpt structure, free with grub_gpt_free. */
grub_gpt_t grub_gpt_read (grub_disk_t disk); grub_gpt_t grub_gpt_read (grub_disk_t disk);
/* Sync up primary and backup headers, recompute checksums. */
grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt);
void grub_gpt_free (grub_gpt_t gpt); void grub_gpt_free (grub_gpt_t gpt);
grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr); grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr);

View file

@ -24,6 +24,7 @@
#include <grub/err.h> #include <grub/err.h>
#include <grub/gpt_partition.h> #include <grub/gpt_partition.h>
#include <grub/msdos_partition.h> #include <grub/msdos_partition.h>
#include <grub/lib/hexdump.h>
#include <grub/test.h> #include <grub/test.h>
#include <errno.h> #include <errno.h>
@ -442,6 +443,52 @@ read_fallback_test (void)
close_disk (&data); close_disk (&data);
} }
static void
repair_test (void)
{
struct test_data data;
grub_gpt_t gpt;
open_disk (&data);
/* Erase/Repair primary. */
memset (&data.raw->primary_header, 0, sizeof (data.raw->primary_header));
sync_disk (&data);
gpt = read_disk (&data);
grub_gpt_repair (data.dev->disk, gpt);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"repair failed: %s", grub_errmsg);
if (memcmp (&gpt->primary, &example_primary, sizeof (gpt->primary)))
{
printf ("Invalid restored primary header:\n");
hexdump (16, (char*)&gpt->primary, sizeof (gpt->primary));
printf ("Expected primary header:\n");
hexdump (16, (char*)&example_primary, sizeof (example_primary));
grub_test_assert (0, "repair did not restore primary header");
}
grub_gpt_free (gpt);
reset_disk (&data);
/* Erase/Repair backup. */
memset (&data.raw->backup_header, 0, sizeof (data.raw->backup_header));
sync_disk (&data);
gpt = read_disk (&data);
grub_gpt_repair (data.dev->disk, gpt);
grub_test_assert (grub_errno == GRUB_ERR_NONE,
"repair failed: %s", grub_errmsg);
if (memcmp (&gpt->backup, &example_backup, sizeof (gpt->backup)))
{
printf ("Invalid restored backup header:\n");
hexdump (16, (char*)&gpt->backup, sizeof (gpt->backup));
printf ("Expected backup header:\n");
hexdump (16, (char*)&example_backup, sizeof (example_backup));
grub_test_assert (0, "repair did not restore backup header");
}
grub_gpt_free (gpt);
reset_disk (&data);
close_disk (&data);
}
void void
grub_unit_test_init (void) grub_unit_test_init (void)
{ {
@ -453,6 +500,7 @@ grub_unit_test_init (void)
grub_test_register ("gpt_read_valid_test", read_valid_test); grub_test_register ("gpt_read_valid_test", read_valid_test);
grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test); grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test);
grub_test_register ("gpt_read_fallback_test", read_fallback_test); grub_test_register ("gpt_read_fallback_test", read_fallback_test);
grub_test_register ("gpt_repair_test", repair_test);
} }
void void
@ -463,5 +511,6 @@ grub_unit_test_fini (void)
grub_test_unregister ("gpt_read_valid_test"); grub_test_unregister ("gpt_read_valid_test");
grub_test_unregister ("gpt_read_invalid_test"); grub_test_unregister ("gpt_read_invalid_test");
grub_test_unregister ("gpt_read_fallback_test"); grub_test_unregister ("gpt_read_fallback_test");
grub_test_unregister ("gpt_repair_test");
grub_fini_all (); grub_fini_all ();
} }