Merge pull request #39 from marineam/weird-disk-size
Tolerate systems that report different disk sizes in firmware and OS
This commit is contained in:
commit
5962f7d5e7
2 changed files with 79 additions and 9 deletions
|
@ -425,13 +425,21 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt)
|
||||||
grub_disk_addr_t addr;
|
grub_disk_addr_t addr;
|
||||||
|
|
||||||
/* Assumes gpt->log_sector_size == disk->log_sector_size */
|
/* Assumes gpt->log_sector_size == disk->log_sector_size */
|
||||||
if (grub_gpt_disk_size_valid(disk))
|
if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID)
|
||||||
sector = disk->total_sectors - 1;
|
{
|
||||||
else if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID)
|
|
||||||
sector = grub_le_to_cpu64 (gpt->primary.alternate_lba);
|
sector = grub_le_to_cpu64 (gpt->primary.alternate_lba);
|
||||||
|
if (grub_gpt_disk_size_valid (disk) && sector >= disk->total_sectors)
|
||||||
|
return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
||||||
|
"backup GPT located at 0x%llx, "
|
||||||
|
"beyond last disk sector at 0x%llx",
|
||||||
|
(unsigned long long) sector,
|
||||||
|
(unsigned long long) disk->total_sectors - 1);
|
||||||
|
}
|
||||||
|
else if (grub_gpt_disk_size_valid (disk))
|
||||||
|
sector = disk->total_sectors - 1;
|
||||||
else
|
else
|
||||||
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
||||||
"Unable to locate backup GPT");
|
"size of disk unknown, cannot locate backup GPT");
|
||||||
|
|
||||||
grub_dprintf ("gpt", "reading backup GPT from sector 0x%llx\n",
|
grub_dprintf ("gpt", "reading backup GPT from sector 0x%llx\n",
|
||||||
(unsigned long long) sector);
|
(unsigned long long) sector);
|
||||||
|
@ -722,18 +730,38 @@ grub_gpt_write_table (grub_disk_t disk, grub_gpt_t gpt,
|
||||||
grub_err_t
|
grub_err_t
|
||||||
grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt)
|
grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt)
|
||||||
{
|
{
|
||||||
|
grub_uint64_t backup_header;
|
||||||
|
|
||||||
/* TODO: update/repair protective MBRs too. */
|
/* TODO: update/repair protective MBRs too. */
|
||||||
|
|
||||||
if (!grub_gpt_both_valid (gpt))
|
if (!grub_gpt_both_valid (gpt))
|
||||||
return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data");
|
return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data");
|
||||||
|
|
||||||
grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name);
|
/* Write the backup GPT first so if writing fails the update is aborted
|
||||||
if (grub_gpt_write_table (disk, gpt, &gpt->primary))
|
* and the primary is left intact. However if the backup location is
|
||||||
return grub_errno;
|
* inaccessible we have to just skip and hope for the best, the backup
|
||||||
|
* will need to be repaired in the OS. */
|
||||||
|
backup_header = grub_le_to_cpu64 (gpt->backup.header_lba);
|
||||||
|
if (grub_gpt_disk_size_valid (disk) &&
|
||||||
|
backup_header >= disk->total_sectors)
|
||||||
|
{
|
||||||
|
grub_printf ("warning: backup GPT located at 0x%llx, "
|
||||||
|
"beyond last disk sector at 0x%llx\n",
|
||||||
|
(unsigned long long) backup_header,
|
||||||
|
(unsigned long long) disk->total_sectors - 1);
|
||||||
|
grub_printf ("warning: only writing primary GPT, "
|
||||||
|
"the backup GPT must be repaired from the OS\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
grub_dprintf ("gpt", "writing backup GPT to %s\n", disk->name);
|
grub_dprintf ("gpt", "writing backup GPT to %s\n", disk->name);
|
||||||
if (grub_gpt_write_table (disk, gpt, &gpt->backup))
|
if (grub_gpt_write_table (disk, gpt, &gpt->backup))
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name);
|
||||||
|
if (grub_gpt_write_table (disk, gpt, &gpt->primary))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -544,6 +544,46 @@ repair_test (void)
|
||||||
close_disk (&data);
|
close_disk (&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Finding/reading/writing the backup GPT may be difficult if the OS and
|
||||||
|
* BIOS report different sizes for the same disk. We need to gracefully
|
||||||
|
* recognize this and avoid causing trouble for the OS. */
|
||||||
|
static void
|
||||||
|
weird_disk_size_test (void)
|
||||||
|
{
|
||||||
|
struct test_data data;
|
||||||
|
grub_gpt_t gpt;
|
||||||
|
|
||||||
|
open_disk (&data);
|
||||||
|
|
||||||
|
/* Chop off 65536 bytes (128 512B sectors) which may happen when the
|
||||||
|
* BIOS thinks you are using a software RAID system that reserves that
|
||||||
|
* area for metadata when in fact you are not and using the bare disk. */
|
||||||
|
grub_test_assert(data.dev->disk->total_sectors == DISK_SECTORS,
|
||||||
|
"unexpected disk size: 0x%llx",
|
||||||
|
(unsigned long long) data.dev->disk->total_sectors);
|
||||||
|
data.dev->disk->total_sectors -= 128;
|
||||||
|
|
||||||
|
gpt = read_disk (&data);
|
||||||
|
assert_error_stack_empty ();
|
||||||
|
/* Reading the alternate_lba should have been blocked and reading
|
||||||
|
* the (new) end of disk should have found no useful data. */
|
||||||
|
grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) == 0,
|
||||||
|
"unreported missing backup header");
|
||||||
|
|
||||||
|
/* We should be able to reconstruct the backup header and the location
|
||||||
|
* of the backup should remain unchanged, trusting the GPT data over
|
||||||
|
* what the BIOS is telling us. Further changes are left to the OS. */
|
||||||
|
grub_gpt_repair (data.dev->disk, gpt);
|
||||||
|
grub_test_assert (grub_errno == GRUB_ERR_NONE,
|
||||||
|
"repair failed: %s", grub_errmsg);
|
||||||
|
grub_test_assert (memcmp (&gpt->primary, &example_primary,
|
||||||
|
sizeof (gpt->primary)) == 0,
|
||||||
|
"repair corrupted primary header");
|
||||||
|
|
||||||
|
grub_gpt_free (gpt);
|
||||||
|
close_disk (&data);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
iterate_partitions_test (void)
|
iterate_partitions_test (void)
|
||||||
{
|
{
|
||||||
|
@ -774,6 +814,7 @@ grub_unit_test_init (void)
|
||||||
grub_test_register ("gpt_iterate_partitions_test", iterate_partitions_test);
|
grub_test_register ("gpt_iterate_partitions_test", iterate_partitions_test);
|
||||||
grub_test_register ("gpt_large_partitions_test", large_partitions_test);
|
grub_test_register ("gpt_large_partitions_test", large_partitions_test);
|
||||||
grub_test_register ("gpt_invalid_partsize_test", invalid_partsize_test);
|
grub_test_register ("gpt_invalid_partsize_test", invalid_partsize_test);
|
||||||
|
grub_test_register ("gpt_weird_disk_size_test", weird_disk_size_test);
|
||||||
grub_test_register ("gpt_search_part_label_test", search_part_label_test);
|
grub_test_register ("gpt_search_part_label_test", search_part_label_test);
|
||||||
grub_test_register ("gpt_search_uuid_test", search_part_uuid_test);
|
grub_test_register ("gpt_search_uuid_test", search_part_uuid_test);
|
||||||
grub_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test);
|
grub_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test);
|
||||||
|
@ -791,6 +832,7 @@ grub_unit_test_fini (void)
|
||||||
grub_test_unregister ("gpt_iterate_partitions_test");
|
grub_test_unregister ("gpt_iterate_partitions_test");
|
||||||
grub_test_unregister ("gpt_large_partitions_test");
|
grub_test_unregister ("gpt_large_partitions_test");
|
||||||
grub_test_unregister ("gpt_invalid_partsize_test");
|
grub_test_unregister ("gpt_invalid_partsize_test");
|
||||||
|
grub_test_unregister ("gpt_weird_disk_size_test");
|
||||||
grub_test_unregister ("gpt_search_part_label_test");
|
grub_test_unregister ("gpt_search_part_label_test");
|
||||||
grub_test_unregister ("gpt_search_part_uuid_test");
|
grub_test_unregister ("gpt_search_part_uuid_test");
|
||||||
grub_test_unregister ("gpt_search_disk_uuid_test");
|
grub_test_unregister ("gpt_search_disk_uuid_test");
|
||||||
|
|
Loading…
Reference in a new issue