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