| /* Copyright (c) 2012 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 <string.h> |
| |
| #include "cgpt.h" |
| #include "cgptlib_internal.h" |
| #include "vboot_host.h" |
| |
| static void AllocAndClear(uint8_t **buf, uint64_t size) { |
| if (*buf) { |
| memset(*buf, 0, size); |
| } else { |
| *buf = calloc(1, size); |
| if (!*buf) { |
| Error("Cannot allocate %" PRIu64 " bytes.\n", size); |
| abort(); |
| } |
| } |
| } |
| |
| static int GptCreate(struct drive *drive, CgptCreateParams *params) { |
| // Do not replace any existing IGNOREME GPT headers. |
| if (!memcmp(((GptHeader*)drive->gpt.primary_header)->signature, |
| GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) { |
| drive->gpt.ignored |= MASK_PRIMARY; |
| Warning("Primary GPT was marked ignored, will not overwrite.\n"); |
| } |
| |
| if (!memcmp(((GptHeader*)drive->gpt.secondary_header)->signature, |
| GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) { |
| drive->gpt.ignored |= MASK_SECONDARY; |
| Warning("Secondary GPT was marked ignored, will not overwrite.\n"); |
| } |
| |
| // Allocate and/or erase the data. |
| // We cannot assume the GPT headers or entry arrays have been allocated |
| // by GptLoad() because those fields might have failed validation checks. |
| AllocAndClear(&drive->gpt.primary_header, |
| drive->gpt.sector_bytes * GPT_HEADER_SECTORS); |
| AllocAndClear(&drive->gpt.secondary_header, |
| drive->gpt.sector_bytes * GPT_HEADER_SECTORS); |
| |
| drive->gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
| GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); |
| |
| // Initialize a blank set |
| if (!params->zap) { |
| GptHeader *h = (GptHeader *)drive->gpt.primary_header; |
| memcpy(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE); |
| h->revision = GPT_HEADER_REVISION; |
| h->size = sizeof(GptHeader); |
| h->my_lba = GPT_PMBR_SECTORS; /* The second sector on drive. */ |
| h->alternate_lba = drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS; |
| if (CGPT_OK != GenerateGuid(&h->disk_uuid)) { |
| Error("Unable to generate new GUID.\n"); |
| return -1; |
| } |
| |
| /* Calculate number of entries */ |
| h->size_of_entry = sizeof(GptEntry); |
| h->number_of_entries = MAX_NUMBER_OF_ENTRIES; |
| if (drive->gpt.flags & GPT_FLAG_EXTERNAL) { |
| // We might have smaller space for the GPT table. Scale accordingly. |
| // |
| // +------+------------+---------------+-----+--------------+-----------+ |
| // | PMBR | Prim. Head | Prim. Entries | ... | Sec. Entries | Sec. Head | |
| // +------+------------+---------------+-----+--------------+-----------+ |
| // |
| // Half the size of gpt_drive_sectors must be big enough to hold PMBR + |
| // GPT Header + Entries Table, though the secondary structures do not |
| // contain PMBR. |
| size_t required_headers_size = |
| (GPT_PMBR_SECTORS + GPT_HEADER_SECTORS) * drive->gpt.sector_bytes; |
| size_t min_entries_size = MIN_NUMBER_OF_ENTRIES * h->size_of_entry; |
| size_t required_min_size = required_headers_size + min_entries_size; |
| size_t half_size = |
| (drive->gpt.gpt_drive_sectors / 2) * drive->gpt.sector_bytes; |
| if (half_size < required_min_size) { |
| Error("Not enough space to store GPT structures. Required %zu bytes.\n", |
| required_min_size * 2); |
| return -1; |
| } |
| size_t max_entries = |
| (half_size - required_headers_size) / h->size_of_entry; |
| if (h->number_of_entries > max_entries) { |
| h->number_of_entries = max_entries; |
| } |
| } |
| |
| /* Then use number of entries to calculate entries_lba. */ |
| h->entries_lba = h->my_lba + GPT_HEADER_SECTORS; |
| if (!(drive->gpt.flags & GPT_FLAG_EXTERNAL)) { |
| h->entries_lba += params->padding; |
| h->first_usable_lba = h->entries_lba + CalculateEntriesSectors(h, |
| drive->gpt.sector_bytes); |
| h->last_usable_lba = |
| (drive->gpt.streaming_drive_sectors - GPT_HEADER_SECTORS - |
| CalculateEntriesSectors(h, drive->gpt.sector_bytes) - 1); |
| } else { |
| h->first_usable_lba = params->padding; |
| h->last_usable_lba = (drive->gpt.streaming_drive_sectors - 1); |
| } |
| |
| size_t entries_size = h->number_of_entries * h->size_of_entry; |
| AllocAndClear(&drive->gpt.primary_entries, entries_size); |
| AllocAndClear(&drive->gpt.secondary_entries, entries_size); |
| |
| // Copy to secondary |
| RepairHeader(&drive->gpt, MASK_PRIMARY); |
| |
| UpdateCrc(&drive->gpt); |
| } |
| |
| return 0; |
| } |
| |
| int CgptCreate(CgptCreateParams *params) { |
| struct drive drive; |
| |
| if (params == NULL) |
| return CGPT_FAILED; |
| |
| if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR, |
| params->drive_size)) |
| return CGPT_FAILED; |
| |
| if (GptCreate(&drive, params)) |
| goto bad; |
| |
| // Write it all out |
| return DriveClose(&drive, 1); |
| |
| bad: |
| |
| DriveClose(&drive, 0); |
| return CGPT_FAILED; |
| } |