blob: 9cc8932ede7d2c7792eead9d67a5d366bffa961f [file] [log] [blame]
// Copyright 2018 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 <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <blkctl/blkctl.h>
#include <blkctl/command.h>
#include <fbl/auto_call.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include <zircon/assert.h>
#include <zircon/device/block.h>
#include <zircon/errors.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
namespace blkctl {
namespace {
// Byte offsets where dashes are placed in a printed GUID
constexpr size_t kGuidSegmentLens[] = {4, 2, 2, 2, 6};
} // namespace
// Public methods
Command::~Command() {}
// Protected methods
void Command::GenerateGuid(uint8_t* out, size_t out_len) {
ZX_DEBUG_ASSERT(out);
ZX_DEBUG_ASSERT(out_len == GUID_LEN);
zx_cprng_draw(out, out_len);
out[6] = (out[6] & 0x0F) | 0x40;
out[8] = (out[8] & 0x3F) | 0x80;
}
zx_status_t Command::ParseHex(const char* str, uint8_t* out, size_t out_len, char delim) {
ZX_DEBUG_ASSERT(out || out_len == 0);
auto printError =
fbl::MakeAutoCall([str] { fprintf(stderr, "failed to parse hex: '%s'\n", str); });
if (strlen(str) < out_len * 2) {
return ZX_ERR_INVALID_ARGS;
}
const char* p = str;
for (size_t i = 0; i < out_len; ++i) {
if (sscanf(p, "%02" SCNx8, &out[i]) != 1) {
return ZX_ERR_INVALID_ARGS;
}
p += 2;
}
if (*p != delim) {
return ZX_ERR_INVALID_ARGS;
}
printError.cancel();
return ZX_OK;
}
zx_status_t Command::ParseGuid(const char* guid, uint8_t* out, size_t out_len) {
ZX_DEBUG_ASSERT(out);
ZX_DEBUG_ASSERT(out_len == GUID_LEN);
zx_status_t rc;
for (size_t i = 0; i < sizeof(kGuidSegmentLens) / sizeof(kGuidSegmentLens[0]); ++i) {
size_t segment_len = kGuidSegmentLens[i];
if (out_len < segment_len) {
return ZX_ERR_INVALID_ARGS;
}
if (out_len > segment_len) {
rc = ParseHex(guid, out, segment_len, '-');
} else {
rc = ParseHex(guid, out, out_len);
}
if (rc != ZX_OK) {
return rc;
}
guid += (2 * segment_len) + 1;
out += segment_len;
out_len -= segment_len;
}
return ZX_OK;
}
void Command::PrintGuid(const uint8_t* guid, size_t guid_len) {
for (size_t i = 0; i < sizeof(kGuidSegmentLens) / sizeof(kGuidSegmentLens[0]); ++i) {
size_t segment_len = kGuidSegmentLens[i];
ZX_DEBUG_ASSERT(guid_len >= segment_len);
for (size_t j = 0; j < segment_len; ++j) {
printf("%02" PRIx8, *guid++);
}
guid_len -= segment_len;
if (guid_len != 0) {
printf("-");
}
}
}
zx_status_t Command::OpenReadable(const char* dev, int* out_fd) {
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s", dev);
devfd_.reset(open(dev, O_RDONLY));
if (!devfd_) {
snprintf(path, sizeof(path), "%s/%s", kDevClassBlock, dev);
devfd_.reset(open(path, O_RDONLY));
}
if (!devfd_) {
fprintf(stderr, "unable to open: %s\n", path);
return ZX_ERR_INVALID_ARGS;
}
fbl::AllocChecker ac;
devname_.Set(path, &ac);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
*out_fd = devfd_.get();
return ZX_OK;
}
zx_status_t Command::ReopenWritable(int* out) {
devfd_.reset(open(devname_.c_str(), O_RDWR));
if (!devfd_) {
fprintf(stderr, "failed to reopen %s as read-write\n", devname_.c_str());
return ZX_ERR_IO;
}
*out = devfd_.get();
return ZX_OK;
}
} // namespace blkctl