/* * 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 (); }