| // 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> |
| |
| #define _STUB_IMPLEMENTATION_ |
| |
| #include "cgpt.h" |
| #include "cgpt_params.h" |
| #include "cgptlib_internal.h" |
| #include "utility.h" |
| #include "vboot_host.h" |
| |
| static const char* DumpCgptAddParams(const CgptAddParams *params) { |
| static char buf[256]; |
| char tmp[64]; |
| |
| buf[0] = 0; |
| snprintf(tmp, sizeof(tmp), "-i %d ", params->partition); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| if (params->label) { |
| snprintf(tmp, sizeof(tmp), "-l %s ", params->label); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| if (params->set_begin) { |
| snprintf(tmp, sizeof(tmp), "-b %llu ", (unsigned long long)params->begin); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| if (params->set_size) { |
| snprintf(tmp, sizeof(tmp), "-s %llu ", (unsigned long long)params->size); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| if (params->set_type) { |
| GuidToStr(¶ms->type_guid, tmp, sizeof(tmp)); |
| StrnAppend(buf, "-t ", sizeof(buf)); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| StrnAppend(buf, " ", sizeof(buf)); |
| } |
| if (params->set_unique) { |
| GuidToStr(¶ms->unique_guid, tmp, sizeof(tmp)); |
| StrnAppend(buf, "-u ", sizeof(buf)); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| StrnAppend(buf, " ", sizeof(buf)); |
| } |
| if (params->set_successful) { |
| snprintf(tmp, sizeof(tmp), "-S %d ", params->successful); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| if (params->set_tries) { |
| snprintf(tmp, sizeof(tmp), "-T %d ", params->tries); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| if (params->set_priority) { |
| snprintf(tmp, sizeof(tmp), "-P %d ", params->priority); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| if (params->set_raw) { |
| snprintf(tmp, sizeof(tmp), "-A 0x%x ", params->raw_value); |
| StrnAppend(buf, tmp, sizeof(buf)); |
| } |
| |
| StrnAppend(buf, "\n", sizeof(buf)); |
| return buf; |
| } |
| |
| // This is an internal helper function which assumes no NULL args are passed. |
| // It sets the given attribute values for a single entry at the given index. |
| static void set_entry_attributes(struct drive drive, |
| GptEntry *entry, |
| uint32_t index, |
| CgptAddParams *params) { |
| if (params->set_raw) { |
| entry->attrs.fields.gpt_att = params->raw_value; |
| } else { |
| if (params->set_successful) |
| SetSuccessful(&drive.gpt, PRIMARY, index, params->successful); |
| if (params->set_tries) |
| SetTries(&drive.gpt, PRIMARY, index, params->tries); |
| if (params->set_priority) |
| SetPriority(&drive.gpt, PRIMARY, index, params->priority); |
| } |
| } |
| |
| // Set the attributes such as is_successful, num_tries_left, priority, etc. |
| // from the given values in params. |
| int CgptSetAttributes(CgptAddParams *params) { |
| struct drive drive; |
| |
| int gpt_retval; |
| GptEntry *entry; |
| uint32_t index; |
| |
| if (params == NULL) |
| return CGPT_FAILED; |
| |
| if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) |
| return CGPT_FAILED; |
| |
| if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) { |
| Error("GptSanityCheck() returned %d: %s\n", |
| gpt_retval, GptError(gpt_retval)); |
| goto bad; |
| } |
| |
| if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) || |
| ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) { |
| Error("one of the GPT header/entries is invalid.\n" |
| "please run 'cgpt repair' before adding anything.\n"); |
| goto bad; |
| } |
| |
| if (params->partition == 0) { |
| Error("invalid partition number: %d\n", params->partition); |
| goto bad; |
| } |
| |
| uint32_t max_part = GetNumberOfEntries(&drive.gpt); |
| if (params->partition > max_part) { |
| Error("invalid partition number: %d\n", params->partition); |
| goto bad; |
| } |
| |
| index = params->partition - 1; |
| entry = GetEntry(&drive.gpt, PRIMARY, index); |
| |
| set_entry_attributes(drive, entry, index, params); |
| |
| RepairEntries(&drive.gpt, MASK_PRIMARY); |
| RepairHeader(&drive.gpt, MASK_PRIMARY); |
| |
| drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
| GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); |
| UpdateCrc(&drive.gpt); |
| |
| // Write it all out. |
| return DriveClose(&drive, 1); |
| |
| bad: |
| DriveClose(&drive, 0); |
| return CGPT_FAILED; |
| } |
| |
| // This method gets the partition details such as the attributes, the |
| // guids of the partitions, etc. Input is the partition number or the |
| // unique id of the partition. Output is populated in the respective |
| // fields of params. |
| int CgptGetPartitionDetails(CgptAddParams *params) { |
| struct drive drive; |
| |
| int gpt_retval; |
| GptEntry *entry; |
| uint32_t index; |
| int result = CGPT_FAILED; |
| |
| if (params == NULL) |
| return result; |
| |
| if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) { |
| Error("Unable to open drive: %s\n", params->drive_name); |
| return result; |
| } |
| |
| if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) { |
| Error("GptSanityCheck() returned %d: %s\n", |
| gpt_retval, GptError(gpt_retval)); |
| goto bad; |
| } |
| |
| if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) || |
| ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) { |
| Error("one of the GPT header/entries is invalid.\n" |
| "please run 'cgpt repair' before adding anything.\n"); |
| goto bad; |
| } |
| |
| uint32_t max_part = GetNumberOfEntries(&drive.gpt); |
| |
| if (params->partition) { |
| if (params->partition > max_part) { |
| Error("invalid partition number: %d\n", params->partition); |
| goto bad; |
| } |
| |
| // A valid partition number has been specified, so get the entry directly. |
| index = params->partition - 1; |
| entry = GetEntry(&drive.gpt, PRIMARY, index); |
| } else { |
| // Partition number is not specified, try looking up by the unique id. |
| if (!params->set_unique) { |
| Error("either partition or unique_id must be specified\n"); |
| goto bad; |
| } |
| |
| // A unique id is specified. find the entry that matches it. |
| for (index = 0; index < max_part; index++) { |
| entry = GetEntry(&drive.gpt, PRIMARY, index); |
| if (GuidEqual(&entry->unique, ¶ms->unique_guid)) { |
| params->partition = index + 1; |
| break; |
| } |
| } |
| |
| if (index >= max_part) { |
| Error("no partitions with the given unique id available\n"); |
| goto bad; |
| } |
| } |
| |
| // At this point, irrespective of whether a partition number is specified |
| // or a unique id is specified, we have valid non-null values for all these: |
| // index, entry, params->partition. |
| |
| params->begin = entry->starting_lba; |
| params->size = entry->ending_lba - entry->starting_lba + 1; |
| memcpy(¶ms->type_guid, &entry->type, sizeof(Guid)); |
| memcpy(¶ms->unique_guid, &entry->unique, sizeof(Guid)); |
| |
| params->raw_value = entry->attrs.fields.gpt_att; |
| params->successful = GetSuccessful(&drive.gpt, PRIMARY, index); |
| params->tries = GetTries(&drive.gpt, PRIMARY, index); |
| params->priority = GetPriority(&drive.gpt, PRIMARY, index); |
| result = CGPT_OK; |
| |
| bad: |
| DriveClose(&drive, 0); |
| return result; |
| } |
| |
| |
| int CgptAdd(CgptAddParams *params) { |
| struct drive drive; |
| |
| int gpt_retval; |
| GptEntry *entry, backup; |
| uint32_t index; |
| int rv; |
| |
| if (params == NULL) |
| return CGPT_FAILED; |
| |
| if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) |
| return CGPT_FAILED; |
| |
| if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) { |
| Error("GptSanityCheck() returned %d: %s\n", |
| gpt_retval, GptError(gpt_retval)); |
| goto bad; |
| } |
| |
| if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) || |
| ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) { |
| Error("one of the GPT header/entries is invalid.\n" |
| "please run 'cgpt repair' before adding anything.\n"); |
| goto bad; |
| } |
| |
| uint32_t max_part = GetNumberOfEntries(&drive.gpt); |
| if (params->partition) { |
| if (params->partition > max_part) { |
| Error("invalid partition number: %d\n", params->partition); |
| goto bad; |
| } |
| index = params->partition - 1; |
| entry = GetEntry(&drive.gpt, PRIMARY, index); |
| } else { |
| // Find next empty partition. |
| for (index = 0; index < max_part; index++) { |
| entry = GetEntry(&drive.gpt, PRIMARY, index); |
| if (GuidIsZero(&entry->type)) { |
| params->partition = index + 1; |
| break; |
| } |
| } |
| if (index >= max_part) { |
| Error("no unused partitions available\n"); |
| goto bad; |
| } |
| } |
| memcpy(&backup, entry, sizeof(backup)); |
| |
| // New partitions must specify type, begin, and size. |
| if (GuidIsZero(&entry->type)) { |
| if (!params->set_begin || !params->set_size || !params->set_type) { |
| Error("-t, -b, and -s options are required for new partitions\n"); |
| goto bad; |
| } |
| if (GuidIsZero(¶ms->type_guid)) { |
| Error("New partitions must have a type other than \"unused\"\n"); |
| goto bad; |
| } |
| if (!params->set_unique) |
| if (!uuid_generator) { |
| Error("Unable to generate new GUID. uuid_generator not set.\n"); |
| goto bad; |
| } |
| (*uuid_generator)((uint8_t *)&entry->unique); |
| } |
| |
| if (params->set_begin) |
| entry->starting_lba = params->begin; |
| if (params->set_size) |
| entry->ending_lba = entry->starting_lba + params->size - 1; |
| if (params->set_type) |
| memcpy(&entry->type, ¶ms->type_guid, sizeof(Guid)); |
| if (params->set_unique) |
| memcpy(&entry->unique, ¶ms->unique_guid, sizeof(Guid)); |
| if (params->label) { |
| if (CGPT_OK != UTF8ToUTF16((uint8_t *)params->label, entry->name, |
| sizeof(entry->name) / sizeof(entry->name[0]))) { |
| Error("The label cannot be converted to UTF16.\n"); |
| goto bad; |
| } |
| } |
| |
| set_entry_attributes(drive, entry, index, params); |
| |
| RepairEntries(&drive.gpt, MASK_PRIMARY); |
| RepairHeader(&drive.gpt, MASK_PRIMARY); |
| |
| drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
| GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); |
| UpdateCrc(&drive.gpt); |
| |
| rv = CheckEntries((GptEntry*)drive.gpt.primary_entries, |
| (GptHeader*)drive.gpt.primary_header); |
| |
| if (0 != rv) { |
| // If the modified entry is illegal, recover it and return error. |
| memcpy(entry, &backup, sizeof(*entry)); |
| Error("%s\n", GptErrorText(rv)); |
| Error(DumpCgptAddParams(params)); |
| goto bad; |
| } |
| |
| // Write it all out. |
| return DriveClose(&drive, 1); |
| |
| bad: |
| DriveClose(&drive, 0); |
| return CGPT_FAILED; |
| } |