mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-30 21:23:40 +00:00
In several places the existing code assumes LBA, but was improperly converted to use byte offsets, so multiply by the sector size to correct it and maintain the same interface between MTD & GPT. Also, since we will need to cgpt create on /dev/fts, which isn't a stat()able device, allow providing the disk size on the commandline. BRANCH=none BUG=chromium:221745 TEST=make runtests; cgpt create -s 12345 on MTD image Change-Id: Icc89a4505aba9a3dfc39b176a372f6e12d106aed Reviewed-on: https://gerrit.chromium.org/gerrit/62675 Reviewed-by: Bill Richardson <wfrichar@chromium.org> Tested-by: Albert Chaulk <achaulk@chromium.org> Commit-Queue: Albert Chaulk <achaulk@chromium.org>
389 lines
11 KiB
C
389 lines
11 KiB
C
// 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 the implementation-specific helper function.
|
|
static int GptSetEntryAttributes(struct drive *drive,
|
|
uint32_t index,
|
|
CgptAddParams *params) {
|
|
GptEntry *entry;
|
|
|
|
entry = GetEntry(&drive->gpt, PRIMARY, index);
|
|
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_unique) {
|
|
memcpy(&entry->unique, ¶ms->unique_guid, sizeof(Guid));
|
|
} else if (GuidIsZero(&entry->type)) {
|
|
if (!uuid_generator) {
|
|
Error("Unable to generate new GUID. uuid_generator not set.\n");
|
|
return -1;
|
|
}
|
|
(*uuid_generator)((uint8_t *)&entry->unique);
|
|
}
|
|
if (params->set_type)
|
|
memcpy(&entry->type, ¶ms->type_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");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int MtdSetEntryAttributes(struct drive *drive,
|
|
uint32_t index,
|
|
CgptAddParams *params) {
|
|
MtdDiskPartition *entry;
|
|
|
|
entry = MtdGetEntry(&drive->mtd, PRIMARY, index);
|
|
if (params->set_begin) {
|
|
uint64_t start = params->begin * drive->mtd.sector_bytes;
|
|
memcpy(&entry->starting_offset, &start, sizeof(params->begin));
|
|
}
|
|
if (params->set_size) {
|
|
uint64_t start;
|
|
uint64_t end;
|
|
MtdGetPartitionSize(entry, &start, NULL, NULL);
|
|
end = start + params->size * drive->mtd.sector_bytes - 1;
|
|
memcpy(&entry->ending_offset, &end, sizeof(end));
|
|
}
|
|
if (params->set_type)
|
|
MtdSetEntryType(entry, LookupMtdTypeForGuid(¶ms->type_guid));
|
|
if (params->label) {
|
|
strncpy(entry->label, params->label, sizeof(entry->label));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// 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 int SetEntryAttributes(struct drive *drive,
|
|
uint32_t index,
|
|
CgptAddParams *params) {
|
|
if (params->set_raw) {
|
|
SetRaw(drive, PRIMARY, index, params->raw_value);
|
|
} else {
|
|
if (params->set_successful)
|
|
SetSuccessful(drive, PRIMARY, index, params->successful);
|
|
if (params->set_tries)
|
|
SetTries(drive, PRIMARY, index, params->tries);
|
|
if (params->set_priority)
|
|
SetPriority(drive, PRIMARY, index, params->priority);
|
|
}
|
|
|
|
// New partitions must specify type, begin, and size.
|
|
if (IsUnused(drive, PRIMARY, index)) {
|
|
if (!params->set_begin || !params->set_size || !params->set_type) {
|
|
Error("-t, -b, and -s options are required for new partitions\n");
|
|
return -1;
|
|
}
|
|
if (GuidIsZero(¶ms->type_guid)) {
|
|
Error("New partitions must have a type other than \"unused\"\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int CgptCheckAddValidity(struct drive *drive) {
|
|
if (drive->is_mtd) {
|
|
if (drive->mtd.primary.crc32 != MtdHeaderCrc(&drive->mtd.primary)) {
|
|
Error("MTD header CRC is invalid\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
int gpt_retval;
|
|
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
|
|
Error("GptSanityCheck() returned %d: %s\n",
|
|
gpt_retval, GptError(gpt_retval));
|
|
return -1;
|
|
}
|
|
|
|
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");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int CgptGetUnusedPartition(struct drive *drive, uint32_t *index,
|
|
CgptAddParams *params) {
|
|
uint32_t i;
|
|
uint32_t max_part = GetNumberOfEntries(drive);
|
|
if (params->partition) {
|
|
if (params->partition > max_part) {
|
|
Error("invalid partition number: %d\n", params->partition);
|
|
return -1;
|
|
}
|
|
*index = params->partition - 1;
|
|
return 0;
|
|
} else {
|
|
// Find next empty partition.
|
|
for (i = 0; i < max_part; i++) {
|
|
if (IsUnused(drive, PRIMARY, i)) {
|
|
params->partition = i + 1;
|
|
*index = i;
|
|
return 0;
|
|
}
|
|
}
|
|
Error("no unused partitions available\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int CgptSetAttributes(CgptAddParams *params) {
|
|
struct drive drive;
|
|
|
|
if (params == NULL)
|
|
return CGPT_FAILED;
|
|
|
|
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
|
|
return CGPT_FAILED;
|
|
|
|
if (CgptCheckAddValidity(&drive)) {
|
|
goto bad;
|
|
}
|
|
|
|
if (params->partition == 0 ||
|
|
params->partition >= GetNumberOfEntries(&drive)) {
|
|
Error("invalid partition number: %d\n", params->partition);
|
|
goto bad;
|
|
}
|
|
|
|
SetEntryAttributes(&drive, params->partition - 1, params);
|
|
|
|
UpdateAllEntries(&drive);
|
|
|
|
// 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 result = CGPT_FAILED;
|
|
int index;
|
|
|
|
if (params == NULL)
|
|
return CGPT_FAILED;
|
|
|
|
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
|
|
return CGPT_FAILED;
|
|
|
|
if (CgptCheckAddValidity(&drive)) {
|
|
goto bad;
|
|
}
|
|
|
|
int max_part = GetNumberOfEntries(&drive);
|
|
if (params->partition > 0) {
|
|
if (params->partition >= max_part) {
|
|
Error("invalid partition number: %d\n", params->partition);
|
|
goto bad;
|
|
}
|
|
} else {
|
|
if (!params->set_unique) {
|
|
Error("either partition or unique_id must be specified\n");
|
|
goto bad;
|
|
}
|
|
if (drive.is_mtd) {
|
|
Error("MTD partitions cannot be specified by unique_id\n");
|
|
goto bad;
|
|
}
|
|
for (index = 0; index < max_part; index++) {
|
|
GptEntry *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;
|
|
}
|
|
}
|
|
index = params->partition - 1;
|
|
|
|
if(drive.is_mtd) {
|
|
MtdDiskPartition *entry = MtdGetEntry(&drive.mtd, PRIMARY, index);
|
|
const Guid *guid = LookupGuidForMtdType(MtdGetEntryType(entry));
|
|
memcpy(¶ms->type_guid, guid, sizeof(params->type_guid));
|
|
memset(¶ms->unique_guid, 0, sizeof(params->unique_guid));
|
|
MtdGetPartitionSizeInSectors(entry, ¶ms->begin, NULL, ¶ms->size);
|
|
params->raw_value = entry->flags;
|
|
} else {
|
|
// GPT-specific code
|
|
GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
|
|
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, PRIMARY, index);
|
|
params->tries = GetTries(&drive, PRIMARY, index);
|
|
params->priority = GetPriority(&drive, PRIMARY, index);
|
|
result = CGPT_OK;
|
|
|
|
bad:
|
|
DriveClose(&drive, 0);
|
|
return result;
|
|
}
|
|
|
|
int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
|
|
GptEntry *entry, backup;
|
|
int rv;
|
|
|
|
entry = GetEntry(&drive->gpt, PRIMARY, index);
|
|
memcpy(&backup, entry, sizeof(backup));
|
|
|
|
if (SetEntryAttributes(drive, index, params) ||
|
|
GptSetEntryAttributes(drive, index, params)) {
|
|
memcpy(entry, &backup, sizeof(*entry));
|
|
return -1;
|
|
}
|
|
|
|
UpdateAllEntries(drive);
|
|
|
|
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));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int MtdAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
|
|
MtdDiskPartition *entry, backup;
|
|
entry = MtdGetEntry(&drive->mtd, PRIMARY, index);
|
|
memcpy(&backup, entry, sizeof(backup));
|
|
|
|
if (SetEntryAttributes(drive, index, params) ||
|
|
MtdSetEntryAttributes(drive, index, params)) {
|
|
memcpy(entry, &backup, sizeof(*entry));
|
|
return -1;
|
|
}
|
|
|
|
UpdateAllEntries(drive);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CgptAdd(CgptAddParams *params) {
|
|
struct drive drive;
|
|
uint32_t index;
|
|
|
|
if (params == NULL)
|
|
return CGPT_FAILED;
|
|
|
|
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
|
|
return CGPT_FAILED;
|
|
|
|
if (CgptCheckAddValidity(&drive)) {
|
|
goto bad;
|
|
}
|
|
|
|
if (CgptGetUnusedPartition(&drive, &index, params)) {
|
|
goto bad;
|
|
}
|
|
|
|
if (drive.is_mtd) {
|
|
if (MtdAdd(&drive, params, index))
|
|
goto bad;
|
|
} else {
|
|
if (GptAdd(&drive, params, index))
|
|
goto bad;
|
|
}
|
|
|
|
// Write it all out.
|
|
return DriveClose(&drive, 1);
|
|
|
|
bad:
|
|
DriveClose(&drive, 0);
|
|
return CGPT_FAILED;
|
|
}
|