/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "sysincludes.h" #include "cgptlib.h" #include "cgptlib_internal.h" #include "crc32.h" #include "gpt.h" #include "utility.h" #include "vboot_api.h" /** * Allocate and read GPT data from the drive. * * The sector_bytes and gpt_drive_sectors fields should be filled on input. The * primary and secondary header and entries are filled on output. * * Returns 0 if successful, 1 if error. */ int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) { uint64_t max_entries_bytes = MAX_NUMBER_OF_ENTRIES * sizeof(GptEntry); int primary_valid = 0, secondary_valid = 0; /* No data to be written yet */ gptdata->modified = 0; /* Allocate all buffers */ gptdata->primary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes); gptdata->secondary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes); gptdata->primary_entries = (uint8_t *)VbExMalloc(max_entries_bytes); gptdata->secondary_entries = (uint8_t *)VbExMalloc(max_entries_bytes); if (gptdata->primary_header == NULL || gptdata->secondary_header == NULL || gptdata->primary_entries == NULL || gptdata->secondary_entries == NULL) return 1; /* Read primary header from the drive, skipping the protective MBR */ if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) { VBDEBUG(("Read error in primary GPT header\n")); Memset(gptdata->primary_header, 0, gptdata->sector_bytes); } /* Only read primary GPT if the primary header is valid */ GptHeader* primary_header = (GptHeader*)gptdata->primary_header; if (0 == CheckHeader(primary_header, 0, gptdata->streaming_drive_sectors, gptdata->gpt_drive_sectors, gptdata->flags)) { primary_valid = 1; uint64_t entries_bytes = primary_header->number_of_entries * primary_header->size_of_entry; uint64_t entries_sectors = entries_bytes / gptdata->sector_bytes; if (0 != VbExDiskRead(disk_handle, primary_header->entries_lba, entries_sectors, gptdata->primary_entries)) { VBDEBUG(("Read error in primary GPT entries\n")); primary_valid = 0; } } else { VBDEBUG(("Primary GPT header invalid!\n")); } /* Read secondary header from the end of the drive */ if (0 != VbExDiskRead(disk_handle, gptdata->gpt_drive_sectors - 1, 1, gptdata->secondary_header)) { VBDEBUG(("Read error in secondary GPT header\n")); Memset(gptdata->secondary_header, 0, gptdata->sector_bytes); } /* Only read secondary GPT if the secondary header is valid */ GptHeader* secondary_header = (GptHeader*)gptdata->secondary_header; if (0 == CheckHeader(secondary_header, 1, gptdata->streaming_drive_sectors, gptdata->gpt_drive_sectors, gptdata->flags)) { secondary_valid = 1; uint64_t entries_bytes = secondary_header->number_of_entries * secondary_header->size_of_entry; uint64_t entries_sectors = entries_bytes / gptdata->sector_bytes; if (0 != VbExDiskRead(disk_handle, secondary_header->entries_lba, entries_sectors, gptdata->secondary_entries)) { VBDEBUG(("Read error in secondary GPT entries\n")); secondary_valid = 0; } } else { VBDEBUG(("Secondary GPT header invalid!\n")); } /* Return 0 if least one GPT header was valid */ return (primary_valid || secondary_valid) ? 0 : 1; } /** * Write any changes for the GPT data back to the drive, then free the buffers. * * Returns 0 if successful, 1 if error. */ int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) { int legacy = 0; GptHeader *header = (GptHeader *)gptdata->primary_header; uint64_t entries_bytes = header->number_of_entries * header->size_of_entry; uint64_t entries_sectors = entries_bytes / gptdata->sector_bytes; int ret = 1; /* * TODO(namnguyen): Preserve padding between primary GPT header and * its entries. */ uint64_t entries_lba = GPT_PMBR_SECTORS + GPT_HEADER_SECTORS; if (gptdata->primary_header) { GptHeader *h = (GptHeader *)(gptdata->primary_header); entries_lba = h->entries_lba; /* * Avoid even looking at this data if we don't need to. We * may in fact not have read it from disk if the read failed, * and this avoids a valgrind complaint. */ if (gptdata->modified) { legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE); } if (gptdata->modified & GPT_MODIFIED_HEADER1) { if (legacy) { VBDEBUG(("Not updating GPT header 1: " "legacy mode is enabled.\n")); } else { VBDEBUG(("Updating GPT header 1\n")); if (0 != VbExDiskWrite(disk_handle, 1, 1, gptdata->primary_header)) goto fail; } } } if (gptdata->primary_entries) { if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { if (legacy) { VBDEBUG(("Not updating GPT entries 1: " "legacy mode is enabled.\n")); } else { VBDEBUG(("Updating GPT entries 1\n")); if (0 != VbExDiskWrite(disk_handle, entries_lba, entries_sectors, gptdata->primary_entries)) goto fail; } } } entries_lba = (gptdata->gpt_drive_sectors - entries_sectors - GPT_HEADER_SECTORS); if (gptdata->secondary_header) { GptHeader *h = (GptHeader *)(gptdata->secondary_header); entries_lba = h->entries_lba; if (gptdata->modified & GPT_MODIFIED_HEADER2) { VBDEBUG(("Updating GPT entries 2\n")); if (0 != VbExDiskWrite(disk_handle, gptdata->gpt_drive_sectors - 1, 1, gptdata->secondary_header)) goto fail; } } if (gptdata->secondary_entries) { if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { VBDEBUG(("Updating GPT header 2\n")); if (0 != VbExDiskWrite(disk_handle, entries_lba, entries_sectors, gptdata->secondary_entries)) goto fail; } } ret = 0; fail: /* Avoid leaking memory on disk write failure */ if (gptdata->primary_header) VbExFree(gptdata->primary_header); if (gptdata->primary_entries) VbExFree(gptdata->primary_entries); if (gptdata->secondary_entries) VbExFree(gptdata->secondary_entries); if (gptdata->secondary_header) VbExFree(gptdata->secondary_header); /* Success */ return ret; } int IsUnusedEntry(const GptEntry *e) { static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero)); } /* * Func: GptGetEntrySize * Desc: This function returns size(in lba) of a partition represented by * given GPT entry. */ size_t GptGetEntrySizeLba(const GptEntry *e) { return (e->ending_lba - e->starting_lba + 1); } /* * Func: GptGetEntrySize * Desc: This function returns size(in bytes) of a partition represented by * given GPT entry. */ size_t GptGetEntrySizeBytes(const GptData *gpt, const GptEntry *e) { return GptGetEntrySizeLba(e) * gpt->sector_bytes; }