blob: 22f3ad462b1a22ededbb30eb6a287faf0904cfce [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 <getopt.h>
#include <gpt/cros.h>
#include <gpt/gpt.h>
#include <zircon/device/block.h>
#include <zircon/syscalls.h> // for zx_cprng_draw
#include <ctype.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
using gpt::GptDevice;
namespace {
constexpr uint64_t kFlagHidden = 0x2;
const char* bin_name;
bool confirm_writes = true;
zx_status_t ReadPartitionIndex(const char* arg, uint32_t* idx) {
char* end;
unsigned long lidx = strtoul(arg, &end, 10);
if (*end != 0 || lidx > UINT32_MAX || lidx >= gpt::kPartitionCount) {
return ZX_ERR_INVALID_ARGS;
}
*idx = static_cast<uint32_t>(lidx);
return ZX_OK;
}
int status_to_retcode(zx_status_t ret) {
return ret == ZX_OK ? 0 : 1;
}
int usage(zx_status_t ret) {
printf("usage:\n");
printf("Note that for all these commands, [<dev>] is the device containing the GPT.\n");
printf("Although using a GPT will split your device into small partitions, [<dev>] \n");
printf("should always refer to the containing device, NOT block devices representing\n");
printf("the partitions themselves.\n\n");
printf("> %s dump [<dev>]\n", bin_name);
printf(" View the properties of the selected device\n");
printf("> %s init [<dev>]\n", bin_name);
printf(" Initialize the block device with a GPT\n");
printf("> %s repartition <dev> [[<label> <type> <size>], ...]\n", bin_name);
printf(" Destructively repartition the device with the given layout\n");
printf(" e.g.\n");
printf(" %s repartition /dev/class/block-core/000", bin_name);
printf(" esp efi 100m sys system 5g blob blobfs 50%% data data 50%%\n");
printf("> %s add <start block> <end block> <name> [<dev>]\n", bin_name);
printf(" Add a partition to the device (and create a GPT if one does not exist)\n");
printf(" Range of blocks is INCLUSIVE (both start and end). Full device range\n");
printf(" may be queried using '%s dump'\n", bin_name);
printf("> %s edit <n> type|id BLOBFS|DATA|SYSTEM|EFI|<guid> [<dev>]\n", bin_name);
printf(" Edit the GUID of the nth partition on the device\n");
printf("> %s edit_cros <n> [-T <tries>] [-S <successful>] [-P <priority] <dev>\n", bin_name);
printf(" Edit the GUID of the nth partition on the device\n");
printf("> %s adjust <n> <start block> <end block> [<dev>]\n", bin_name);
printf(" Move or resize the nth partition on the device\n");
printf("> %s remove <n> [<dev>]\n", bin_name);
printf(" Remove the nth partition from the device\n");
printf("> %s visible <n> true|false [<dev>]\n", bin_name);
printf(" Set the visibility of the nth partition on the device\n");
printf("\n");
printf("The option --live-dangerously may be passed in front of any command\n");
printf("to skip the write confirmation prompt.\n");
return status_to_retcode(ret);
}
int cgetc(void) {
uint8_t ch;
for (;;) {
ssize_t r = read(0, &ch, 1);
if (r < 0)
return static_cast<int>(r);
if (r == 1)
return ch;
}
}
struct guid {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
};
char* guid_to_cstring(char* dst, const uint8_t* src) {
struct guid* guid = (struct guid*)src;
sprintf(dst, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid->data1, guid->data2, guid->data3, guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3], guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]);
return dst;
}
char* cros_flags_to_cstring(char* dst, size_t dst_len, uint64_t flags) {
uint32_t priority = gpt_cros_attr_get_priority(flags);
uint32_t tries = gpt_cros_attr_get_tries(flags);
bool successful = gpt_cros_attr_get_successful(flags);
snprintf(dst, dst_len, "priority=%u tries=%u successful=%u", priority, tries, successful);
dst[dst_len-1] = 0;
return dst;
}
char* flags_to_cstring(char* dst, size_t dst_len, const uint8_t* guid, uint64_t flags) {
if (gpt_cros_is_kernel_guid(guid, sizeof(struct guid))) {
return cros_flags_to_cstring(dst, dst_len, flags);
} else {
snprintf(dst, dst_len, "0x%016" PRIx64, flags);
}
dst[dst_len-1] = 0;
return dst;
}
fbl::unique_ptr<GptDevice> init(const char* dev) {
fbl::unique_fd fd(open(dev, O_RDWR));
if (!fd.is_valid()) {
printf("error opening %s\n", dev);
return nullptr;
}
block_info_t info;
ssize_t rc = ioctl_block_get_info(fd.get(), &info);
if (rc < 0) {
printf("error getting block info\n");
return nullptr;
}
printf("blocksize=0x%X blocks=%" PRIu64 "\n", info.block_size, info.block_count);
fbl::unique_ptr<GptDevice> gpt;
rc = GptDevice::Create(fd.get(), info.block_size, info.block_count, &gpt);
if (rc < 0) {
printf("error initializing GPT\n");
return fbl::unique_ptr<GptDevice>(nullptr);
}
return gpt;
}
constexpr void setxy(unsigned yes, const char** X, const char** Y) {
if (yes) {
*X = "\033[7m";
*Y = "\033[0m";
} else {
*X = "";
*Y = "";
}
}
void dump(const GptDevice* gpt, int* count) {
if (!gpt->Valid()) {
return;
}
const gpt_partition_t* p;
char name[gpt::kGuidStrLength];
char guid[gpt::kGuidStrLength];
char id[gpt::kGuidStrLength];
char flags_str[256];
const char* X;
const char* Y;
uint32_t i;
for (i = 0; i < gpt::kPartitionCount; i++) {
p = gpt->GetPartition(i);
if (!p) break;
memset(name, 0, gpt::kGuidStrLength);
unsigned diff;
ZX_ASSERT(gpt->GetDiffs(i, &diff) == ZX_OK);
setxy(diff & gpt::kGptDiffName, &X, &Y);
printf("Partition %d: %s%s%s\n",
i, X, utf16_to_cstring(name, (const uint16_t*)p->name, gpt::kGuidStrLength - 1), Y);
setxy(diff & (gpt::kGptDiffFirst | gpt::kGptDiffLast), &X, &Y);
printf(" Start: %s%" PRIu64 "%s, End: %s%" PRIu64 "%s (%" PRIu64 " blocks)\n",
X, p->first, Y, X, p->last, Y, p->last - p->first + 1);
setxy(diff & gpt::kGptDiffGuid, &X, &Y);
printf(" id: %s%s%s\n", X, guid_to_cstring(guid, (const uint8_t*)p->guid), Y);
setxy(diff & gpt::kGptDiffType, &X, &Y);
printf(" type: %s%s%s\n", X, guid_to_cstring(id, (const uint8_t*)p->type), Y);
setxy(diff & gpt::kGptDiffName, &X, &Y);
printf(" flags: %s%s%s\n", X, flags_to_cstring(flags_str, sizeof(flags_str), p->type, p->flags), Y);
}
if (count) {
*count = i;
}
}
void dump_partitions(const char* dev) {
fbl::unique_ptr<GptDevice> gpt = init(dev);
if (!gpt) return;
if (!gpt->Valid()) {
printf("No valid GPT found\n");
return;
}
printf("Partition table is valid\n");
uint64_t start, end;
if (gpt->Range(&start, &end) != ZX_OK) {
printf("Couldn't identify device range\n");
return;
}
printf("GPT contains usable blocks from %" PRIu64 " to %" PRIu64" (inclusive)\n", start, end);
int count;
dump(gpt.get(), &count);
printf("Total: %d partitions\n", count);
}
bool ConfirmCommit(const GptDevice* gpt, const char* dev) {
if (confirm_writes) {
dump(gpt, NULL);
printf("\n");
printf("WARNING: About to write partition table to: %s\n", dev);
printf("WARNING: Type 'y' to continue, 'n' or ESC to cancel\n");
for (;;) {
switch (cgetc()) {
case 'y':
case 'Y':
return true;
case 'n':
case 'N':
case 27:
return false;
}
}
}
return true;
}
zx_status_t commit(GptDevice* gpt, const char* dev) {
if (!ConfirmCommit(gpt, dev)) {
return ZX_OK;
}
zx_status_t rc = gpt->Sync();
if (rc != ZX_OK) {
printf("Error: GPT device sync failed.\n");
return rc;
}
if ((rc = gpt->BlockRrPart()) != ZX_OK) {
printf("Error: GPT updated but device could not be rebound. Please reboot.\n");
return rc;
}
printf("GPT changes complete.\n");
return ZX_OK;
}
void init_gpt(const char* dev) {
fbl::unique_ptr<GptDevice> gpt = init(dev);
if (!gpt) return;
// generate a default header
ZX_ASSERT(gpt->RemoveAllPartitions() == ZX_OK);
commit(gpt.get(), dev);
}
void add_partition(const char* dev, uint64_t start, uint64_t end, const char* name) {
uint8_t guid[GPT_GUID_LEN];
zx_cprng_draw(guid, GPT_GUID_LEN);
fbl::unique_ptr<GptDevice> gpt = init(dev);
if (!gpt) return;
if (!gpt->Valid()) {
// generate a default header
if (commit(gpt.get(), dev)) {
return;
}
}
uint8_t type[GPT_GUID_LEN];
memset(type, 0xff, GPT_GUID_LEN);
zx_status_t rc = gpt->AddPartition(name, type, guid, start, end - start + 1, 0);
if (rc == ZX_OK) {
printf("add partition: name=%s start=%" PRIu64 " end=%" PRIu64 "\n", name, start, end);
commit(gpt.get(), dev);
}
}
/*
* Converts a GUID of the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx to
* a properly arranged, 16 byte sequence. This takes care of flipping the byte
* order section-wise for the first three sections (8 bytes total) of the GUID.
* bytes_out should be a 16 byte array where the final output will be placed.
* A bool is returned representing success of parsing the GUID. false will be
* returned if the GUID string is the wrong length or contains invalid
* characters.
*/
bool parse_guid(const char* guid, uint8_t* bytes_out) {
if (strlen(guid) != gpt::kGuidStrLength - 1) {
printf("GUID length is wrong: %zd but expected %" PRIu64 "\n", strlen(guid),
(gpt::kGuidStrLength - 1));
return false;
}
// how many nibbles of the byte we've processed
uint8_t nibbles = 0;
// value to accumulate byte as we parse its two char nibbles
uint8_t val = 0;
// which byte we're parsing
uint8_t out_idx = 0;
uint8_t dashes = 0;
for (uint64_t idx = 0; idx < gpt::kGuidStrLength - 1; idx++) {
char c = guid[idx];
uint8_t char_val = 0;
if (c == '-') {
dashes++;
continue;
} else if (c >= '0' && c <= '9') {
char_val = static_cast<uint8_t>(c - '0');
} else if (c >= 'A' && c <= 'F') {
char_val = static_cast<uint8_t>(c - (uint8_t)'A' + (uint8_t)10);
} else if (c >= 'a' && c <= 'f') {
char_val = static_cast<uint8_t>(c - 'a' + 10);
} else {
fprintf(stderr, "'%c' is not a valid GUID character\n", c);
return false;
}
val = static_cast<uint8_t>(val + (char_val << (4 * (1 - nibbles))));
if (++nibbles == 2) {
bytes_out[out_idx++] = val;
nibbles = 0;
val = 0;
}
}
if (dashes != 4) {
printf("Error, incorrect number of hex characters.\n");
return false;
}
// Shuffle bytes because endianness is swapped for certain sections
uint8_t swap;
swap = bytes_out[0];
bytes_out[0] = bytes_out[3];
bytes_out[3] = swap;
swap = bytes_out[1];
bytes_out[1] = bytes_out[2];
bytes_out[2] = swap;
swap = bytes_out[4];
bytes_out[4] = bytes_out[5];
bytes_out[5] = swap;
swap = bytes_out[6];
bytes_out[6] = bytes_out[7];
bytes_out[7] = swap;
return true;
}
/*
* Give a path to a block device and a partition index into a GPT, load the GPT
* information into memory and find the requested partition. This does all the
* bounds and other error checking. If ZX_OK is returned, the out parameters
* will be set to valid values.
*/
zx_status_t get_gpt_and_part(const char* path_device, uint32_t idx_part,
fbl::unique_ptr<GptDevice>* gpt_out,
gpt_partition_t** part_out) {
if (idx_part >= gpt::kPartitionCount) {
return ZX_ERR_INVALID_ARGS;
}
fbl::unique_ptr<GptDevice> gpt = init(path_device);
if (gpt == NULL) {
return ZX_ERR_INTERNAL;
}
gpt_partition_t* part = gpt->GetPartition(idx_part);
if (part == NULL) {
return ZX_ERR_INTERNAL;
}
*gpt_out = std::move(gpt);
*part_out = part;
return ZX_OK;
}
constexpr struct {
const char* name;
const uint8_t guid[GPT_GUID_LEN];
} nametab[] = {
{ .name = "blobfs", .guid = GUID_BLOB_VALUE, },
{ .name = "data", .guid = GUID_DATA_VALUE, },
{ .name = "install", .guid = GUID_INSTALL_VALUE, },
{ .name = "system", .guid = GUID_SYSTEM_VALUE, },
{ .name = "efi", .guid = GUID_EFI_VALUE, },
{ .name = "zircon-a", .guid = GUID_ZIRCON_A_VALUE, },
{ .name = "zircon-b", .guid = GUID_ZIRCON_B_VALUE, },
{ .name = "zircon-r", .guid = GUID_ZIRCON_R_VALUE, },
};
/*
* Match keywords "BLOBFS", "DATA", "SYSTEM", or "EFI" and convert them to their
* corresponding byte sequences. 'out' should point to a GPT_GUID_LEN array.
*/
constexpr bool expand_special(const char* in, uint8_t* out) {
if (in == NULL) {
return false;
}
for (unsigned n = 0; n < countof(nametab); n++) {
if (!strcmp(in, nametab[n].name)) {
memcpy(out, nametab[n].guid, GPT_GUID_LEN);
return true;
}
}
return false;
}
void remove_partition(const char* dev, uint32_t n) {
fbl::unique_ptr<GptDevice> gpt = init(dev);
if (!gpt) return;
if (n >= gpt::kPartitionCount) {
return;
}
gpt_partition_t* p = gpt->GetPartition(n);
if (!p) {
return;
}
if (gpt->RemovePartition(p->guid) != ZX_OK) {
return;
}
char name[gpt::kGuidStrLength];
printf("remove partition: n=%u name=%s\n", n,
utf16_to_cstring(name, (const uint16_t*)p->name, gpt::kGuidStrLength - 1));
commit(gpt.get(), dev);
}
zx_status_t adjust_partition(const char* dev, uint32_t idx_part,
uint64_t start, uint64_t end) {
fbl::unique_ptr<GptDevice> gpt = NULL;
gpt_partition_t* part = NULL;
if (end < start) {
fprintf(stderr, "partition #%u would end before it started\n", idx_part);
}
zx_status_t rc = get_gpt_and_part(dev, idx_part, &gpt, &part);
if (rc != ZX_OK) {
return rc;
}
uint64_t block_start, block_end;
if ((rc = gpt->Range(&block_start, &block_end)) != ZX_OK) {
return rc;
}
if ((start < block_start) || (end > block_end)) {
fprintf(stderr, "partition #%u would be outside of valid block range\n", idx_part);
return ZX_ERR_OUT_OF_RANGE;
}
for (uint32_t idx = 0; idx < gpt::kPartitionCount; idx++) {
// skip this partition and non-existent partitions
if ((idx == idx_part) || (gpt->GetPartition(idx) == NULL)) {
continue;
}
// skip partitions we don't intersect
if ((start > gpt->GetPartition(idx)->last) ||
(end < gpt->GetPartition(idx)->first)) {
continue;
}
fprintf(stderr, "partition #%u would overlap partition #%u\n", idx_part, idx);
return ZX_ERR_UNAVAILABLE;
}
part->first = start;
part->last = end;
return commit(gpt.get(), dev);
}
/*
* Edit a partition, changing either its type or ID GUID. path_device should be
* the path to the device where the GPT can be read. idx_part should be the
* index of the partition in the GPT that you want to change. guid should be the
* string/human-readable form of the GUID and should be 36 characters plus a
* null terminator.
*/
zx_status_t edit_partition(const char* dev, uint32_t idx_part,
char* type_or_id, char* guid) {
fbl::unique_ptr<GptDevice> gpt = NULL;
gpt_partition_t* part = NULL;
// whether we're setting the type or id GUID
bool set_type;
if (!strcmp(type_or_id, "type")) {
set_type = true;
} else if (!strcmp(type_or_id, "id")) {
set_type = false;
} else {
return ZX_ERR_INVALID_ARGS;
}
uint8_t guid_bytes[GPT_GUID_LEN];
if (!expand_special(guid, guid_bytes) && !parse_guid(guid, guid_bytes)) {
printf("GUID could not be parsed.\n");
return ZX_ERR_INVALID_ARGS;
}
zx_status_t rc = get_gpt_and_part(dev, idx_part, &gpt, &part);
if (rc != ZX_OK) {
return rc;
}
if (set_type) {
memcpy(part->type, guid_bytes, GPT_GUID_LEN);
} else {
memcpy(part->guid, guid_bytes, GPT_GUID_LEN);
}
return commit(gpt.get(), dev);
}
/*
* Edit a Chrome OS kernel partition, changing its attributes.
*
* argv/argc should correspond only to the arguments after the command.
*/
int edit_cros_partition(char* const* argv, int argc) {
fbl::unique_ptr<GptDevice> gpt = NULL;
gpt_partition_t* part = NULL;
zx_status_t rc;
char* dev;
char* end;
unsigned long lidx_part = strtoul(argv[0], &end, 10);
if (*end != 0 || argv[0][0] == 0 || lidx_part > UINT32_MAX) {
return usage(ZX_ERR_INVALID_ARGS);
}
uint32_t idx_part = static_cast<uint32_t>(lidx_part);
// Use -1 as a sentinel for "not changing"
long tries = -1;
long priority = -1;
int successful = -1;
int c;
while ((c = getopt(argc, argv, "T:P:S:")) > 0) {
switch (c) {
case 'T': {
long val = strtol(optarg, &end, 10);
if (*end != 0 || optarg[0] == 0) {
return usage(ZX_ERR_INVALID_ARGS);
}
if (val < 0 || val > 15) {
printf("tries must be in the range [0, 16)\n");
return usage(ZX_ERR_INVALID_ARGS);
}
tries = val;
break;
}
case 'P': {
long val = strtol(optarg, &end, 10);
if (*end != 0 || optarg[0] == 0) {
return usage(ZX_ERR_INVALID_ARGS);
}
if (val < 0 || val > 15) {
printf("priority must be in the range [0, 16)\n");
return usage(ZX_ERR_INVALID_ARGS);
}
priority = val;
break;
}
case 'S': {
if (!strncmp(optarg, "0", 2)) {
successful = 0;
} else if (!strncmp(optarg, "1", 2)) {
successful = 1;
} else {
printf("successful must be 0 or 1\n");
return usage(ZX_ERR_INVALID_ARGS);
}
break;
}
default:
printf("Unknown option\n");
return usage(ZX_ERR_INVALID_ARGS);
}
}
if (optind != argc - 1) {
printf("Did not specify device arg\n");
return usage(ZX_ERR_INVALID_ARGS);
}
dev = argv[optind];
rc = get_gpt_and_part(dev, idx_part, &gpt, &part);
if (rc != ZX_OK) {
return status_to_retcode(rc);
}
if (!gpt_cros_is_kernel_guid(part->type, GPT_GUID_LEN)) {
printf("Partition is not a CrOS kernel partition\n");
return status_to_retcode(ZX_ERR_INVALID_ARGS);
}
if (tries >= 0) {
if (gpt_cros_attr_set_tries(&part->flags, static_cast<uint8_t>(tries)) < 0) {
printf("Failed to set tries\n");
return status_to_retcode(ZX_ERR_INVALID_ARGS);
}
}
if (priority >= 0) {
if (gpt_cros_attr_set_priority(&part->flags, static_cast<uint8_t>(priority)) < 0) {
printf("Failed to set priority\n");
return status_to_retcode(ZX_ERR_INVALID_ARGS);
}
}
if (successful >= 0) {
gpt_cros_attr_set_successful(&part->flags, successful);
}
return status_to_retcode(commit(gpt.get(), dev));
}
/*
* Set whether a partition is visible or not to the EFI firmware. If a
* partition is set as hidden, the firmware will not attempt to boot from the
* partition.
*/
zx_status_t set_visibility(char* dev, uint32_t idx_part, bool visible) {
fbl::unique_ptr<GptDevice> gpt = NULL;
gpt_partition_t* part = NULL;
zx_status_t rc = get_gpt_and_part(dev, idx_part, &gpt, &part);
if (rc != ZX_OK) {
return rc;
}
if (visible) {
part->flags &= ~kFlagHidden;
} else {
part->flags |= kFlagHidden;
}
return commit(gpt.get(), dev);
}
// parse_size parses long integers in base 10, expanding p, t, g, m, and k
// suffices as binary byte scales. If the suffix is %, the value is returned as
// negative, in order to indicate a proportion.
int64_t parse_size(char *s) {
char *end = s;
long long int v = strtoll(s, &end, 10);
switch(*end) {
case 0:
break;
case '%':
v = -v;
break;
case 'p':
case 'P':
v *= 1024;
__FALLTHROUGH;
case 't':
case 'T':
v *= 1024;
__FALLTHROUGH;
case 'g':
case 'G':
v *= 1024;
__FALLTHROUGH;
case 'm':
case 'M':
v *= 1024;
__FALLTHROUGH;
case 'k':
case 'K':
v *= 1024;
}
return v;
}
// TODO(raggi): this should eventually get moved into ulib/gpt.
// align finds the next block at or after base that is aligned to a physical
// block boundary. The gpt specification requires that all partitions are
// aligned to physical block boundaries.
uint64_t align(uint64_t base, uint64_t logical, uint64_t physical) {
uint64_t a = logical;
if (physical > a) a = physical;
uint64_t base_bytes = base * logical;
uint64_t d = base_bytes % a;
return (base_bytes + a - d) / logical;
}
// repartition expects argv to start with the disk path and be followed by
// triples of name, type and size.
zx_status_t repartition(int argc, char** argv) {
const char* dev = argv[0];
uint64_t logical, free_space;
fbl::unique_ptr<GptDevice> gpt = init(dev);
if (gpt == NULL) {
return ZX_ERR_INTERNAL;
}
argc--;
argv = &argv[1];
int num_partitions = argc/3;
gpt_partition_t* p = gpt->GetPartition(0);
while (p) {
ZX_ASSERT(gpt->RemovePartition(p->guid) == ZX_OK);
p = gpt->GetPartition(0);
}
logical = gpt->BlockSize();
free_space = gpt->TotalBlockCount() * logical;
{
// expand out any proportional sizes into absolute sizes
uint64_t sizes[num_partitions];
memset(sizes, 0, sizeof(sizes));
{
uint64_t percent = 100;
uint64_t portions[num_partitions];
memset(portions, 0, sizeof(portions));
for (int i = 0; i < num_partitions; i++) {
int64_t sz = parse_size(argv[(i*3)+2]);
if (sz > 0) {
sizes[i] = sz;
free_space -= sz;
} else {
if (percent == 0) {
printf("more than 100%% of free space requested\n");
return ZX_ERR_INVALID_ARGS;
}
// portions from parse_size are negative
portions[i] = -sz;
percent -= -sz;
}
}
for (int i = 0; i < num_partitions; i++) {
if (portions[i] != 0)
sizes[i] = (free_space * portions[i]) / 100;
}
}
// TODO(raggi): get physical block size...
uint64_t physical = 8192;
uint64_t first_usable = 0;
uint64_t last_usable = 0;
ZX_ASSERT(gpt->Range(&first_usable, &last_usable) == ZX_OK);
uint64_t start = align(first_usable, logical, physical);
for (int i = 0; i < num_partitions; i++) {
char *name = argv[i*3];
char *type_string = argv[i*3+1];
uint64_t byte_size = sizes[i];
uint8_t type[GPT_GUID_LEN];
if (!expand_special(type_string, type) && !parse_guid(type_string, type)) {
printf("GUID could not be parsed: %s\n", type_string);
return ZX_ERR_INVALID_ARGS;
}
uint8_t guid[GPT_GUID_LEN];
zx_cprng_draw(guid, GPT_GUID_LEN);
// end is clamped to the sector before the next aligned partition, in order
// to avoid wasting alignment space at the tail of partitions.
uint64_t nblocks = (byte_size/logical) + (byte_size%logical == 0 ? 0 : 1);
uint64_t end = align(start+nblocks+1, logical, physical) - 1;
if (end > last_usable) end = last_usable;
if (start > last_usable) {
printf("partition %s does not fit\n", name);
return ZX_ERR_OUT_OF_RANGE;
}
printf("%s: %" PRIu64 " bytes, %" PRIu64 " blocks, %" PRIu64 "-%" PRIu64 "\n",
name, byte_size, nblocks, start, end);
ZX_ASSERT(gpt->AddPartition(name, type, guid, start, end - start, 0) == ZX_OK);
start = end + 1;
}
}
return commit(gpt.get(), dev);
}
} // namespace
int main(int argc, char** argv) {
bin_name = argv[0];
const char* cmd;
uint32_t idx_part;
if (argc > 1) {
if (!strcmp(argv[1], "--live-dangerously")) {
confirm_writes = false;
argc--;
argv++;
}
}
if (argc == 1) {
return usage(ZX_OK);
}
cmd = argv[1];
if (!strcmp(cmd, "dump")) {
if (argc <= 2) {
return usage(ZX_OK);
}
dump_partitions(argv[2]);
} else if (!strcmp(cmd, "init")) {
if (argc <= 2) {
return usage(ZX_OK);
}
init_gpt(argv[2]);
} else if (!strcmp(cmd, "add")) {
if (argc <= 5) {
return usage(ZX_OK);
}
add_partition(argv[5], strtoull(argv[2], NULL, 0), strtoull(argv[3], NULL, 0), argv[4]);
} else if (!strcmp(cmd, "remove")) {
if (argc <= 3) {
return usage(ZX_OK);
}
if (ReadPartitionIndex(argv[2], &idx_part) != ZX_OK) {
return usage(ZX_OK);
}
remove_partition(argv[3], idx_part);
} else if (!strcmp(cmd, "edit")) {
if (argc <= 5) {
return usage(ZX_OK);
}
if (ReadPartitionIndex(argv[2], &idx_part) != ZX_OK) {
return usage(ZX_OK);
}
if (edit_partition(argv[5], idx_part, argv[3], argv[4])) {
printf("failed to edit partition.\n");
}
} else if (!strcmp(cmd, "edit_cros")) {
if (argc <= 4) {
return usage(ZX_OK);
}
if (edit_cros_partition(argv + 2, argc - 2)) {
printf("failed to edit partition.\n");
}
} else if (!strcmp(cmd, "adjust")) {
if (argc <= 5) {
return usage(ZX_OK);
}
if (ReadPartitionIndex(argv[2], &idx_part) != ZX_OK) {
return usage(ZX_OK);
}
if (adjust_partition(argv[5], idx_part,
strtoull(argv[3], NULL, 0), strtoull(argv[4], NULL, 0)) != ZX_OK) {
printf("failed to adjust partition.\n");
}
} else if (!strcmp(cmd, "visible")) {
if (argc < 5) {
return usage(ZX_OK);
}
bool visible;
if (!strcmp(argv[3], "true")) {
visible = true;
} else if (!strcmp(argv[3], "false")) {
visible = false;
} else {
return usage(ZX_OK);
}
if (ReadPartitionIndex(argv[2], &idx_part) != ZX_OK) {
return usage(ZX_OK);
}
if (set_visibility(argv[4], idx_part, visible) != ZX_OK) {
printf("Error changing visibility.\n");
}
} else if (!strcmp(cmd, "repartition")) {
if (argc < 6) {
return usage(ZX_OK);
}
if (argc % 3 != 0) {
return usage(ZX_OK);
}
return status_to_retcode(repartition(argc - 2, &argv[2]));
} else {
return usage(ZX_OK);
}
return 0;
}