/*
* VasEBoot -- GRand Unified Bootloader
* Copyright (C) 2014 CoreOS, Inc.
*
* VasEBoot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VasEBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VasEBoot. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* from gnulib */
#include
/* Confirm that the GPT structures conform to the sizes in the spec:
* The header size "must be greater than or equal to 92 and must be less
* than or equal to the logical block size."
* The partition entry size must be "a value of 128*(2^n) where n is an
* integer greater than or equal to zero (e.g., 128, 256, 512, etc.)." */
verify (sizeof (struct VasEBoot_gpt_header) == 92);
verify (sizeof (struct VasEBoot_gpt_partentry) == 128);
/* GPT section sizes. */
#define HEADER_SIZE (sizeof (struct VasEBoot_gpt_header))
#define HEADER_PAD (VasEBoot_DISK_SECTOR_SIZE - HEADER_SIZE)
#define ENTRY_SIZE (sizeof (struct VasEBoot_gpt_partentry))
#define TABLE_ENTRIES 0x80
#define TABLE_SIZE (TABLE_ENTRIES * ENTRY_SIZE)
#define TABLE_SECTORS (TABLE_SIZE / VasEBoot_DISK_SECTOR_SIZE)
/* Double check that the table size calculation was valid. */
verify (TABLE_SECTORS * VasEBoot_DISK_SECTOR_SIZE == TABLE_SIZE);
/* GPT section locations for a 1MiB disk. */
#define DISK_SECTORS 0x800
#define DISK_SIZE (VasEBoot_DISK_SECTOR_SIZE * DISK_SECTORS)
#define PRIMARY_HEADER_SECTOR 0x1
#define PRIMARY_TABLE_SECTOR 0x2
#define BACKUP_HEADER_SECTOR (DISK_SECTORS - 0x1)
#define BACKUP_TABLE_SECTOR (BACKUP_HEADER_SECTOR - TABLE_SECTORS)
#define DATA_START_SECTOR (PRIMARY_TABLE_SECTOR + TABLE_SECTORS)
#define DATA_END_SECTOR (BACKUP_TABLE_SECTOR - 0x1)
#define DATA_SECTORS (BACKUP_TABLE_SECTOR - DATA_START_SECTOR)
#define DATA_SIZE (VasEBoot_DISK_SECTOR_SIZE * DATA_SECTORS)
struct test_disk
{
struct VasEBoot_msdos_partition_mbr mbr;
struct VasEBoot_gpt_header primary_header;
VasEBoot_uint8_t primary_header_pad[HEADER_PAD];
struct VasEBoot_gpt_partentry primary_entries[TABLE_ENTRIES];
VasEBoot_uint8_t data[DATA_SIZE];
struct VasEBoot_gpt_partentry backup_entries[TABLE_ENTRIES];
struct VasEBoot_gpt_header backup_header;
VasEBoot_uint8_t backup_header_pad[HEADER_PAD];
} VasEBoot_PACKED;
/* Sanity check that all the above ugly math was correct. */
verify (sizeof (struct test_disk) == DISK_SIZE);
struct test_data
{
int fd;
VasEBoot_device_t dev;
struct test_disk *raw;
};
/* Sample primary GPT header for a 1MB disk. */
static const struct VasEBoot_gpt_header example_primary = {
.magic = VasEBoot_GPT_HEADER_MAGIC,
.version = VasEBoot_GPT_HEADER_VERSION,
.headersize = sizeof (struct VasEBoot_gpt_header),
.crc32 = VasEBoot_cpu_to_le32_compile_time (0xb985abe0),
.header_lba = VasEBoot_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR),
.alternate_lba = VasEBoot_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR),
.start = VasEBoot_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = VasEBoot_cpu_to_le64_compile_time (DATA_END_SECTOR),
.guid = VasEBoot_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6,
0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac),
.partitions = VasEBoot_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR),
.maxpart = VasEBoot_cpu_to_le32_compile_time (TABLE_ENTRIES),
.partentry_size = VasEBoot_cpu_to_le32_compile_time (ENTRY_SIZE),
.partentry_crc32 = VasEBoot_cpu_to_le32_compile_time (0x074e052c),
};
static const struct VasEBoot_gpt_partentry example_entries[TABLE_ENTRIES] = {
{
.type = VasEBoot_GPT_PARTITION_TYPE_EFI_SYSTEM,
.guid = VasEBoot_GPT_GUID_INIT (0xa0f1792e, 0xb4ce, 0x4136, 0xbc, 0xf2,
0x1a, 0xfc, 0x13, 0x3c, 0x28, 0x28),
.start = VasEBoot_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = VasEBoot_cpu_to_le64_compile_time (0x3f),
.attrib = 0x0,
.name = {
VasEBoot_cpu_to_le16_compile_time ('E'),
VasEBoot_cpu_to_le16_compile_time ('F'),
VasEBoot_cpu_to_le16_compile_time ('I'),
VasEBoot_cpu_to_le16_compile_time (' '),
VasEBoot_cpu_to_le16_compile_time ('S'),
VasEBoot_cpu_to_le16_compile_time ('Y'),
VasEBoot_cpu_to_le16_compile_time ('S'),
VasEBoot_cpu_to_le16_compile_time ('T'),
VasEBoot_cpu_to_le16_compile_time ('E'),
VasEBoot_cpu_to_le16_compile_time ('M'),
0x0,
}
},
{
.type = VasEBoot_GPT_PARTITION_TYPE_BIOS_BOOT,
.guid = VasEBoot_GPT_GUID_INIT (0x876c898d, 0x1b40, 0x4727, 0xa1, 0x61,
0xed, 0xf9, 0xb5, 0x48, 0x66, 0x74),
.start = VasEBoot_cpu_to_le64_compile_time (0x40),
.end = VasEBoot_cpu_to_le64_compile_time (0x7f),
.attrib = VasEBoot_cpu_to_le64_compile_time (
1ULL << VasEBoot_GPT_PART_ATTR_OFFSET_LEGACY_BIOS_BOOTABLE),
.name = {
VasEBoot_cpu_to_le16_compile_time ('B'),
VasEBoot_cpu_to_le16_compile_time ('I'),
VasEBoot_cpu_to_le16_compile_time ('O'),
VasEBoot_cpu_to_le16_compile_time ('S'),
VasEBoot_cpu_to_le16_compile_time (' '),
VasEBoot_cpu_to_le16_compile_time ('B'),
VasEBoot_cpu_to_le16_compile_time ('O'),
VasEBoot_cpu_to_le16_compile_time ('O'),
VasEBoot_cpu_to_le16_compile_time ('T'),
0x0,
}
},
};
/* And the backup header. */
static const struct VasEBoot_gpt_header example_backup = {
.magic = VasEBoot_GPT_HEADER_MAGIC,
.version = VasEBoot_GPT_HEADER_VERSION,
.headersize = sizeof (struct VasEBoot_gpt_header),
.crc32 = VasEBoot_cpu_to_le32_compile_time (0x0af785eb),
.header_lba = VasEBoot_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR),
.alternate_lba = VasEBoot_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR),
.start = VasEBoot_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = VasEBoot_cpu_to_le64_compile_time (DATA_END_SECTOR),
.guid = VasEBoot_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6,
0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac),
.partitions = VasEBoot_cpu_to_le64_compile_time (BACKUP_TABLE_SECTOR),
.maxpart = VasEBoot_cpu_to_le32_compile_time (TABLE_ENTRIES),
.partentry_size = VasEBoot_cpu_to_le32_compile_time (ENTRY_SIZE),
.partentry_crc32 = VasEBoot_cpu_to_le32_compile_time (0x074e052c),
};
/* Sample protective MBR for the same 1MB disk. Note, this matches
* parted and fdisk behavior. The UEFI spec uses different values. */
static const struct VasEBoot_msdos_partition_mbr example_pmbr = {
.entries = {{.flag = 0x00,
.start_head = 0x00,
.start_sector = 0x01,
.start_cylinder = 0x00,
.type = 0xee,
.end_head = 0xfe,
.end_sector = 0xff,
.end_cylinder = 0xff,
.start = VasEBoot_cpu_to_le32_compile_time (0x1),
.length = VasEBoot_cpu_to_le32_compile_time (DISK_SECTORS - 0x1),
}},
.signature = VasEBoot_cpu_to_le16_compile_time (VasEBoot_PC_PARTITION_SIGNATURE),
};
/* If errors are left in VasEBoot's error stack things can get confused. */
static void
assert_error_stack_empty (void)
{
do
{
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"error on stack: %s", VasEBoot_errmsg);
}
while (VasEBoot_error_pop ());
}
static VasEBoot_err_t
execute_command2 (const char *name, const char *arg1, const char *arg2)
{
VasEBoot_command_t cmd;
VasEBoot_err_t err;
char *argv[2];
cmd = VasEBoot_command_find (name);
if (!cmd)
VasEBoot_fatal ("can't find command %s", name);
argv[0] = strdup (arg1);
argv[1] = strdup (arg2);
err = (cmd->func) (cmd, 2, argv);
free (argv[0]);
free (argv[1]);
return err;
}
static void
sync_disk (struct test_data *data)
{
if (msync (data->raw, DISK_SIZE, MS_SYNC | MS_INVALIDATE) < 0)
VasEBoot_fatal ("Syncing disk failed: %s", strerror (errno));
VasEBoot_disk_cache_invalidate_all ();
}
static void
reset_disk (struct test_data *data)
{
memset (data->raw, 0, DISK_SIZE);
/* Initialize image with valid example tables. */
memcpy (&data->raw->mbr, &example_pmbr, sizeof (data->raw->mbr));
memcpy (&data->raw->primary_header, &example_primary,
sizeof (data->raw->primary_header));
memcpy (&data->raw->primary_entries, &example_entries,
sizeof (data->raw->primary_entries));
memcpy (&data->raw->backup_entries, &example_entries,
sizeof (data->raw->backup_entries));
memcpy (&data->raw->backup_header, &example_backup,
sizeof (data->raw->backup_header));
sync_disk (data);
}
static void
open_disk (struct test_data *data)
{
const char *loop = "loop0";
char template[] = "/tmp/VasEBoot_gpt_test.XXXXXX";
char host[sizeof ("(host)") + sizeof (template)];
data->fd = mkstemp (template);
if (data->fd < 0)
VasEBoot_fatal ("Creating %s failed: %s", template, strerror (errno));
if (ftruncate (data->fd, DISK_SIZE) < 0)
{
int err = errno;
unlink (template);
VasEBoot_fatal ("Resizing %s failed: %s", template, strerror (err));
}
data->raw = mmap (NULL, DISK_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, data->fd, 0);
if (data->raw == MAP_FAILED)
{
int err = errno;
unlink (template);
VasEBoot_fatal ("Maping %s failed: %s", template, strerror (err));
}
snprintf (host, sizeof (host), "(host)%s", template);
if (execute_command2 ("loopback", loop, host) != VasEBoot_ERR_NONE)
{
unlink (template);
VasEBoot_fatal ("loopback %s %s failed: %s", loop, host, VasEBoot_errmsg);
}
if (unlink (template) < 0)
VasEBoot_fatal ("Unlinking %s failed: %s", template, strerror (errno));
reset_disk (data);
data->dev = VasEBoot_device_open (loop);
if (!data->dev)
VasEBoot_fatal ("Opening %s failed: %s", loop, VasEBoot_errmsg);
}
static void
close_disk (struct test_data *data)
{
char *loop;
assert_error_stack_empty ();
if (munmap (data->raw, DISK_SIZE) || close (data->fd))
VasEBoot_fatal ("Closing disk image failed: %s", strerror (errno));
loop = strdup (data->dev->disk->name);
VasEBoot_test_assert (VasEBoot_device_close (data->dev) == VasEBoot_ERR_NONE,
"Closing disk device failed: %s", VasEBoot_errmsg);
VasEBoot_test_assert (execute_command2 ("loopback", "-d", loop) ==
VasEBoot_ERR_NONE, "loopback -d %s failed: %s", loop,
VasEBoot_errmsg);
free (loop);
}
static VasEBoot_gpt_t
read_disk (struct test_data *data)
{
VasEBoot_gpt_t gpt;
gpt = VasEBoot_gpt_read (data->dev->disk);
if (gpt == NULL)
VasEBoot_fatal ("VasEBoot_gpt_read failed: %s", VasEBoot_errmsg);
return gpt;
}
static void
pmbr_test (void)
{
struct VasEBoot_msdos_partition_mbr mbr;
memset (&mbr, 0, sizeof (mbr));
/* Empty is invalid. */
VasEBoot_gpt_pmbr_check (&mbr);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
/* A table without a protective partition is invalid. */
mbr.signature = VasEBoot_cpu_to_le16_compile_time (VasEBoot_PC_PARTITION_SIGNATURE);
VasEBoot_gpt_pmbr_check (&mbr);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
/* A table with a protective type is ok. */
memcpy (&mbr, &example_pmbr, sizeof (mbr));
VasEBoot_gpt_pmbr_check (&mbr);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
}
static void
header_test (void)
{
struct VasEBoot_gpt_header primary, backup;
/* Example headers should be valid. */
memcpy (&primary, &example_primary, sizeof (primary));
VasEBoot_gpt_header_check (&primary, VasEBoot_DISK_SECTOR_BITS);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
memcpy (&backup, &example_backup, sizeof (backup));
VasEBoot_gpt_header_check (&backup, VasEBoot_DISK_SECTOR_BITS);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
/* Twiddle the GUID to invalidate the CRC. */
primary.guid.data1 = 0;
VasEBoot_gpt_header_check (&primary, VasEBoot_DISK_SECTOR_BITS);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
backup.guid.data1 = 0;
VasEBoot_gpt_header_check (&backup, VasEBoot_DISK_SECTOR_BITS);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
}
static void
read_valid_test (void)
{
struct test_data data;
VasEBoot_gpt_t gpt;
open_disk (&data);
gpt = read_disk (&data);
VasEBoot_test_assert (gpt->status == (VasEBoot_GPT_PROTECTIVE_MBR |
VasEBoot_GPT_PRIMARY_HEADER_VALID |
VasEBoot_GPT_PRIMARY_ENTRIES_VALID |
VasEBoot_GPT_BACKUP_HEADER_VALID |
VasEBoot_GPT_BACKUP_ENTRIES_VALID),
"unexpected status: 0x%02x", gpt->status);
VasEBoot_gpt_free (gpt);
close_disk (&data);
}
static void
read_invalid_entries_test (void)
{
struct test_data data;
VasEBoot_gpt_t gpt;
open_disk (&data);
/* Corrupt the first entry in both tables. */
memset (&data.raw->primary_entries[0], 0x55,
sizeof (data.raw->primary_entries[0]));
memset (&data.raw->backup_entries[0], 0x55,
sizeof (data.raw->backup_entries[0]));
sync_disk (&data);
gpt = VasEBoot_gpt_read (data.dev->disk);
VasEBoot_test_assert (gpt == NULL, "no error reported for corrupt entries");
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
close_disk (&data);
}
static void
read_fallback_test (void)
{
struct test_data data;
VasEBoot_gpt_t gpt;
open_disk (&data);
/* Corrupt the primary header. */
memset (&data.raw->primary_header.guid, 0x55,
sizeof (data.raw->primary_header.guid));
sync_disk (&data);
gpt = read_disk (&data);
VasEBoot_test_assert ((gpt->status & VasEBoot_GPT_PRIMARY_HEADER_VALID) == 0,
"unreported corrupt primary header");
VasEBoot_gpt_free (gpt);
reset_disk (&data);
/* Corrupt the backup header. */
memset (&data.raw->backup_header.guid, 0x55,
sizeof (data.raw->backup_header.guid));
sync_disk (&data);
gpt = read_disk (&data);
VasEBoot_test_assert ((gpt->status & VasEBoot_GPT_BACKUP_HEADER_VALID) == 0,
"unreported corrupt backup header");
VasEBoot_gpt_free (gpt);
reset_disk (&data);
/* Corrupt the primary entry table. */
memset (&data.raw->primary_entries[0], 0x55,
sizeof (data.raw->primary_entries[0]));
sync_disk (&data);
gpt = read_disk (&data);
VasEBoot_test_assert ((gpt->status & VasEBoot_GPT_PRIMARY_ENTRIES_VALID) == 0,
"unreported corrupt primary entries table");
VasEBoot_gpt_free (gpt);
reset_disk (&data);
/* Corrupt the backup entry table. */
memset (&data.raw->backup_entries[0], 0x55,
sizeof (data.raw->backup_entries[0]));
sync_disk (&data);
gpt = read_disk (&data);
VasEBoot_test_assert ((gpt->status & VasEBoot_GPT_BACKUP_ENTRIES_VALID) == 0,
"unreported corrupt backup entries table");
VasEBoot_gpt_free (gpt);
reset_disk (&data);
/* If primary is corrupt and disk size is unknown fallback fails. */
memset (&data.raw->primary_header.guid, 0x55,
sizeof (data.raw->primary_header.guid));
sync_disk (&data);
data.dev->disk->total_sectors = VasEBoot_DISK_SIZE_UNKNOWN;
gpt = VasEBoot_gpt_read (data.dev->disk);
VasEBoot_test_assert (gpt == NULL, "no error reported");
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
close_disk (&data);
}
static void
repair_test (void)
{
struct test_data data;
VasEBoot_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);
VasEBoot_gpt_repair (data.dev->disk, gpt);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"repair failed: %s", VasEBoot_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));
VasEBoot_test_assert (0, "repair did not restore primary header");
}
VasEBoot_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);
VasEBoot_gpt_repair (data.dev->disk, gpt);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"repair failed: %s", VasEBoot_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));
VasEBoot_test_assert (0, "repair did not restore backup header");
}
VasEBoot_gpt_free (gpt);
reset_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;
VasEBoot_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. */
VasEBoot_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. */
VasEBoot_test_assert ((gpt->status & VasEBoot_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. */
VasEBoot_gpt_repair (data.dev->disk, gpt);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_NONE,
"repair failed: %s", VasEBoot_errmsg);
VasEBoot_test_assert (memcmp (&gpt->primary, &example_primary,
sizeof (gpt->primary)) == 0,
"repair corrupted primary header");
VasEBoot_gpt_free (gpt);
close_disk (&data);
}
static void
iterate_partitions_test (void)
{
struct test_data data;
struct VasEBoot_gpt_partentry *p;
VasEBoot_gpt_t gpt;
VasEBoot_uint32_t n;
open_disk (&data);
gpt = read_disk (&data);
for (n = 0; (p = VasEBoot_gpt_get_partentry (gpt, n)) != NULL; n++)
VasEBoot_test_assert (memcmp (p, &example_entries[n], sizeof (*p)) == 0,
"unexpected partition %d data", n);
VasEBoot_test_assert (n == TABLE_ENTRIES, "unexpected partition limit: %d", n);
VasEBoot_gpt_free (gpt);
close_disk (&data);
}
static void
large_partitions_test (void)
{
struct test_data data;
struct VasEBoot_gpt_partentry *p;
VasEBoot_gpt_t gpt;
VasEBoot_uint32_t n;
open_disk (&data);
/* Double the entry size, cut the number of entries in half. */
data.raw->primary_header.maxpart =
data.raw->backup_header.maxpart =
VasEBoot_cpu_to_le32_compile_time (TABLE_ENTRIES/2);
data.raw->primary_header.partentry_size =
data.raw->backup_header.partentry_size =
VasEBoot_cpu_to_le32_compile_time (ENTRY_SIZE*2);
data.raw->primary_header.partentry_crc32 =
data.raw->backup_header.partentry_crc32 =
VasEBoot_cpu_to_le32_compile_time (0xf2c45af8);
data.raw->primary_header.crc32 = VasEBoot_cpu_to_le32_compile_time (0xde00cc8f);
data.raw->backup_header.crc32 = VasEBoot_cpu_to_le32_compile_time (0x6d72e284);
memset (&data.raw->primary_entries, 0,
sizeof (data.raw->primary_entries));
for (n = 0; n < TABLE_ENTRIES/2; n++)
memcpy (&data.raw->primary_entries[n*2], &example_entries[n],
sizeof (data.raw->primary_entries[0]));
memcpy (&data.raw->backup_entries, &data.raw->primary_entries,
sizeof (data.raw->backup_entries));
sync_disk(&data);
gpt = read_disk (&data);
for (n = 0; (p = VasEBoot_gpt_get_partentry (gpt, n)) != NULL; n++)
VasEBoot_test_assert (memcmp (p, &example_entries[n], sizeof (*p)) == 0,
"unexpected partition %d data", n);
VasEBoot_test_assert (n == TABLE_ENTRIES/2, "unexpected partition limit: %d", n);
VasEBoot_gpt_free (gpt);
/* Editing memory beyond the entry structure should still change the crc. */
data.raw->primary_entries[1].attrib = 0xff;
sync_disk(&data);
gpt = read_disk (&data);
VasEBoot_test_assert (gpt->status == (VasEBoot_GPT_PROTECTIVE_MBR |
VasEBoot_GPT_PRIMARY_HEADER_VALID |
VasEBoot_GPT_BACKUP_HEADER_VALID |
VasEBoot_GPT_BACKUP_ENTRIES_VALID),
"unexpected status: 0x%02x", gpt->status);
VasEBoot_gpt_free (gpt);
close_disk (&data);
}
static void
invalid_partsize_test (void)
{
struct VasEBoot_gpt_header header = {
.magic = VasEBoot_GPT_HEADER_MAGIC,
.version = VasEBoot_GPT_HEADER_VERSION,
.headersize = sizeof (struct VasEBoot_gpt_header),
.crc32 = VasEBoot_cpu_to_le32_compile_time (0x1ff2a054),
.header_lba = VasEBoot_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR),
.alternate_lba = VasEBoot_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR),
.start = VasEBoot_cpu_to_le64_compile_time (DATA_START_SECTOR),
.end = VasEBoot_cpu_to_le64_compile_time (DATA_END_SECTOR),
.guid = VasEBoot_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6,
0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac),
.partitions = VasEBoot_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR),
.maxpart = VasEBoot_cpu_to_le32_compile_time (TABLE_ENTRIES),
/* Triple the entry size, which is not valid. */
.partentry_size = VasEBoot_cpu_to_le32_compile_time (ENTRY_SIZE*3),
.partentry_crc32 = VasEBoot_cpu_to_le32_compile_time (0x074e052c),
};
VasEBoot_gpt_header_check(&header, VasEBoot_DISK_SECTOR_BITS);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_BAD_PART_TABLE,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_test_assert (strcmp(VasEBoot_errmsg, "invalid GPT entry size") == 0,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
}
static void
search_part_label_test (void)
{
struct test_data data;
const char *test_result;
char *expected_result;
open_disk (&data);
expected_result = VasEBoot_xasprintf ("%s,gpt1", data.dev->disk->name);
VasEBoot_env_unset ("test_result");
VasEBoot_search_part_label ("EFI SYSTEM", "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
VasEBoot_free (expected_result);
expected_result = VasEBoot_xasprintf ("%s,gpt2", data.dev->disk->name);
VasEBoot_env_unset ("test_result");
VasEBoot_search_part_label ("BIOS BOOT", "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
VasEBoot_free (expected_result);
VasEBoot_env_unset ("test_result");
VasEBoot_search_part_label ("bogus name", "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result == NULL,
"unexpected device: %s", test_result);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_FILE_NOT_FOUND,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
close_disk (&data);
}
static void
search_part_uuid_test (void)
{
struct test_data data;
const char gpt1_uuid[] = "A0F1792E-B4CE-4136-BCF2-1AFC133C2828";
const char gpt2_uuid[] = "876c898d-1b40-4727-a161-edf9b5486674";
const char bogus_uuid[] = "1534c928-c50e-4866-9daf-6a9fd7918a76";
const char *test_result;
char *expected_result;
open_disk (&data);
expected_result = VasEBoot_xasprintf ("%s,gpt1", data.dev->disk->name);
VasEBoot_env_unset ("test_result");
VasEBoot_search_part_uuid (gpt1_uuid, "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
VasEBoot_free (expected_result);
expected_result = VasEBoot_xasprintf ("%s,gpt2", data.dev->disk->name);
VasEBoot_env_unset ("test_result");
VasEBoot_search_part_uuid (gpt2_uuid, "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
VasEBoot_free (expected_result);
VasEBoot_env_unset ("test_result");
VasEBoot_search_part_uuid (bogus_uuid, "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result == NULL,
"unexpected device: %s", test_result);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_FILE_NOT_FOUND,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
close_disk (&data);
}
static void
search_disk_uuid_test (void)
{
struct test_data data;
const char disk_uuid[] = "69c131ad-67d6-46c6-93c4-124c755256ac";
const char bogus_uuid[] = "1534c928-c50e-4866-9daf-6a9fd7918a76";
const char *test_result;
char *expected_result;
open_disk (&data);
expected_result = VasEBoot_xasprintf ("%s", data.dev->disk->name);
VasEBoot_env_unset ("test_result");
VasEBoot_search_disk_uuid (disk_uuid, "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result && strcmp (test_result, expected_result) == 0,
"wrong device: %s (%s)", test_result, expected_result);
VasEBoot_free (expected_result);
VasEBoot_env_unset ("test_result");
VasEBoot_search_disk_uuid (bogus_uuid, "test_result", 0, NULL, 0);
test_result = VasEBoot_env_get ("test_result");
VasEBoot_test_assert (test_result == NULL,
"unexpected device: %s", test_result);
VasEBoot_test_assert (VasEBoot_errno == VasEBoot_ERR_FILE_NOT_FOUND,
"unexpected error: %s", VasEBoot_errmsg);
VasEBoot_errno = VasEBoot_ERR_NONE;
close_disk (&data);
}
void
VasEBoot_unit_test_init (void)
{
VasEBoot_init_all ();
VasEBoot_hostfs_init ();
VasEBoot_host_init ();
VasEBoot_test_register ("gpt_pmbr_test", pmbr_test);
VasEBoot_test_register ("gpt_header_test", header_test);
VasEBoot_test_register ("gpt_read_valid_test", read_valid_test);
VasEBoot_test_register ("gpt_read_invalid_test", read_invalid_entries_test);
VasEBoot_test_register ("gpt_read_fallback_test", read_fallback_test);
VasEBoot_test_register ("gpt_repair_test", repair_test);
VasEBoot_test_register ("gpt_iterate_partitions_test", iterate_partitions_test);
VasEBoot_test_register ("gpt_large_partitions_test", large_partitions_test);
VasEBoot_test_register ("gpt_invalid_partsize_test", invalid_partsize_test);
VasEBoot_test_register ("gpt_weird_disk_size_test", weird_disk_size_test);
VasEBoot_test_register ("gpt_search_part_label_test", search_part_label_test);
VasEBoot_test_register ("gpt_search_uuid_test", search_part_uuid_test);
VasEBoot_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test);
}
void
VasEBoot_unit_test_fini (void)
{
VasEBoot_test_unregister ("gpt_pmbr_test");
VasEBoot_test_unregister ("gpt_header_test");
VasEBoot_test_unregister ("gpt_read_valid_test");
VasEBoot_test_unregister ("gpt_read_invalid_test");
VasEBoot_test_unregister ("gpt_read_fallback_test");
VasEBoot_test_unregister ("gpt_repair_test");
VasEBoot_test_unregister ("gpt_iterate_partitions_test");
VasEBoot_test_unregister ("gpt_large_partitions_test");
VasEBoot_test_unregister ("gpt_invalid_partsize_test");
VasEBoot_test_unregister ("gpt_weird_disk_size_test");
VasEBoot_test_unregister ("gpt_search_part_label_test");
VasEBoot_test_unregister ("gpt_search_part_uuid_test");
VasEBoot_test_unregister ("gpt_search_disk_uuid_test");
VasEBoot_fini_all ();
}