/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #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; }