blob: a4d77ffabae4365c1062f2cf6962b28d3b83355a [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 <stddef.h>
#include <stdio.h>
#include <blkctl/blkctl.h>
#include <blkctl/command.h>
#include <fs-management/fvm.h>
#include <fs-management/ramdisk.h>
#include <fvm/fvm.h>
#include <lib/zx/time.h>
#include <zircon/device/block.h>
#include <zircon/device/device.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include "fvm.h"
#include "generic.h"
namespace blkctl {
namespace fvm {
namespace {
bool SupportsFvmQuery(int fd) {
if (fd < 0) {
return false;
}
fvm_info_t info;
memset(&info, 0, sizeof(info));
return ioctl_block_fvm_query(fd, &info) != ZX_ERR_NOT_SUPPORTED;
}
bool SupportsFvmVSliceQuery(int fd) {
if (fd < 0) {
return false;
}
query_request_t request;
query_response_t response;
memset(&request, 0, sizeof(request));
memset(&response, 0, sizeof(response));
return ioctl_block_fvm_vslice_query(fd, &request, &response) != ZX_ERR_NOT_SUPPORTED;
}
zx_status_t CheckFvm(int fd) {
if (!SupportsFvmQuery(fd) && !SupportsFvmVSliceQuery(fd)) {
fprintf(stderr, "device does not appear to be an FVM volume or partition\n");
return ZX_ERR_INVALID_ARGS;
}
return ZX_OK;
}
zx_status_t CheckFvmVolume(int fd) {
if (!SupportsFvmQuery(fd)) {
fprintf(stderr, "device does not appear to be an FVM volume\n");
return ZX_ERR_INVALID_ARGS;
}
return ZX_OK;
}
zx_status_t CheckFvmPartition(int fd) {
if (!SupportsFvmVSliceQuery(fd)) {
fprintf(stderr, "device does not appear to be an FVM partition\n");
return ZX_ERR_INVALID_ARGS;
}
return ZX_OK;
}
} // namespace
zx_status_t Init::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
size_t slice_size;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
(rc = cmdline->GetNumArg("slice_size", &slice_size)) != ZX_OK ||
(rc = cmdline->ArgsDone()) != ZX_OK || (rc = OpenReadable(dev, &fd)) != ZX_OK) {
return rc;
}
char path[PATH_MAX];
if ((res = ioctl_device_get_topo_path(fd, path, sizeof(path))) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "failed to get topological path: %s\n", zx_status_get_string(rc));
return rc;
}
if ((rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
if ((rc = fvm_init(fd, slice_size)) != ZX_OK) {
fprintf(stderr, "fvm_init failed: %s\n", zx_status_get_string(rc));
return rc;
}
if ((res = ioctl_device_bind(fd, kDriver, strlen(kDriver))) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "could not bind fvm driver: %s\n", zx_status_get_string(rc));
return rc;
}
char name[PATH_MAX];
snprintf(name, sizeof(name), "%s/fvm", path);
if (wait_for_device(name, zx::sec(3).get()) != 0) {
fprintf(stderr, "timed out waiting for fvm driver to bind\n");
return ZX_ERR_TIMED_OUT;
}
printf("%s created\n", name);
return ZX_OK;
}
zx_status_t Dump::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
fvm_info_t info;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
(rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = CheckFvm(fd)) != ZX_OK) {
return rc;
}
if ((res = ioctl_block_fvm_query(fd, &info)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "ioctl_block_fvm_query failed: %s\n", zx_status_get_string(rc));
return rc;
}
generic::Dump baseCmd(cmdline);
if ((rc = cmdline->UngetArgs(1)) != ZX_OK || (rc = baseCmd.Run()) != ZX_OK) {
return rc;
}
printf("%16s: %zu\n", "FVM slice size", info.slice_size);
printf("%16s: %zu\n", "FVM slice count", info.vslice_count);
return ZX_OK;
}
zx_status_t Add::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
alloc_req_t request;
memset(&request, 0, sizeof(request));
const char* name;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
(rc = cmdline->GetStrArg("name", &name)) != ZX_OK ||
(rc = cmdline->GetNumArg("slices", &request.slice_count)) != ZX_OK) {
return rc;
}
const char* guid;
rc = cmdline->GetStrArg("guid", &guid, true /* optional */);
if (rc == ZX_ERR_NOT_FOUND) {
GenerateGuid(request.type, sizeof(request.type));
} else if (rc != ZX_OK || (rc = ParseGuid(guid, request.type, sizeof(request.type))) != ZX_OK) {
return rc;
}
if ((rc = cmdline->ArgsDone()) != ZX_OK ||
(rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = CheckFvmVolume(fd)) != ZX_OK ||
(rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
GenerateGuid(request.guid, sizeof(request.guid));
if ((res = ioctl_block_fvm_alloc_partition(fd, &request)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "ioctl_block_fvm_alloc_partition failed: %s\n", zx_status_get_string(rc));
return rc;
}
printf("added partition '%s' as ", name);
PrintGuid(request.guid, sizeof(request.guid));
printf("\n");
return ZX_OK;
}
zx_status_t Query::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
(rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = CheckFvmPartition(fd)) != ZX_OK) {
return rc;
}
fvm_info_t info;
if ((res = ioctl_block_fvm_query(fd, &info)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "ioctl_block_fvm_query failed: %s\n", zx_status_get_string(rc));
return rc;
}
query_request_t request;
query_response_t response;
request.count = 1;
printf("Allocated ranges:\n");
size_t end = 0;
for (size_t off = 0; off < info.vslice_count; off = end) {
request.vslice_start[0] = off;
if ((res = ioctl_block_fvm_vslice_query(fd, &request, &response)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "ioctl_block_fvm_vslice_query failed: %s\n", zx_status_get_string(rc));
return rc;
}
ZX_DEBUG_ASSERT(response.count == 1);
ZX_DEBUG_ASSERT(response.vslice_range[0].count != 0);
end = off + response.vslice_range[0].count;
if (response.vslice_range[0].allocated) {
printf(" <0x%016" PRIx64 ", 0x%016" PRIx64 ">: slices %zu through %zu\n",
static_cast<uint64_t>(off * info.slice_size),
static_cast<uint64_t>((end * info.slice_size) - 1), off, end);
}
}
return ZX_OK;
}
zx_status_t Extend::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
extend_request_t request;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
(rc = cmdline->GetNumArg("start", &request.offset)) != ZX_OK ||
(rc = cmdline->GetNumArg("length", &request.length)) != ZX_OK ||
(rc = cmdline->ArgsDone()) != ZX_OK || (rc = OpenReadable(dev, &fd)) != ZX_OK ||
(rc = CheckFvmPartition(fd)) != ZX_OK) {
return rc;
}
if ((rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
if ((res = ioctl_block_fvm_extend(fd, &request)) < 0) {
fprintf(stderr, "ioctl_block_fvm_extend failed: %s\n", zx_status_get_string(rc));
rc = static_cast<zx_status_t>(res);
return rc;
}
printf("partition extended\n");
return ZX_OK;
}
zx_status_t Shrink::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
extend_request_t request;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK ||
(rc = cmdline->GetNumArg("start", &request.offset)) != ZX_OK ||
(rc = cmdline->GetNumArg("length", &request.length)) != ZX_OK ||
(rc = cmdline->ArgsDone()) != ZX_OK || (rc = OpenReadable(dev, &fd)) != ZX_OK ||
(rc = CheckFvmPartition(fd)) != ZX_OK) {
return rc;
}
if ((rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
if ((res = ioctl_block_fvm_shrink(fd, &request)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "ioctl_block_fvm_shrink failed: %s\n", zx_status_get_string(rc));
return rc;
}
printf("partition shrunk\n");
return ZX_OK;
}
zx_status_t Remove::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
(rc = OpenReadable(dev, &fd)) != ZX_OK || (rc = CheckFvmPartition(fd)) != ZX_OK) {
return rc;
}
if ((rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
if ((res = ioctl_block_fvm_destroy_partition(fd)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "ioctl_block_fvm_destroy_partition failed: %s\n", zx_status_get_string(rc));
return rc;
}
printf("partition removed\n");
return ZX_OK;
}
zx_status_t Destroy::Run() {
zx_status_t rc;
ssize_t res;
BlkCtl* cmdline = this->cmdline();
const char* dev;
int fd;
if ((rc = cmdline->GetStrArg("device", &dev)) != ZX_OK || (rc = cmdline->ArgsDone()) != ZX_OK ||
(rc = OpenReadable(dev, &fd)) != ZX_OK) {
return rc;
}
char path[PATH_MAX];
constexpr const char* suffix = "/fvm";
ZX_DEBUG_ASSERT(strlen(suffix) < sizeof(path));
size_t path_max = sizeof(path) - (strlen(suffix) + 1);
if ((res = ioctl_device_get_topo_path(fd, path, path_max)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "failed to get topological path: %s\n", zx_status_get_string(rc));
return rc;
}
strcat(path, suffix); // Safe to due to size limit above
fbl::unique_fd fvm_fd(open(path, O_RDONLY));
if ((rc = CheckFvmVolume(fvm_fd.get())) != ZX_OK) {
return rc;
}
fvm_fd.reset();
if ((rc = cmdline->Confirm()) != ZX_OK || (rc = ReopenWritable(&fd)) != ZX_OK) {
return rc;
}
if ((rc = fvm_destroy(devname())) != ZX_OK) {
fprintf(stderr, "fvm_destroy failed\n");
return rc;
}
if ((res = ioctl_block_rr_part(fd)) < 0) {
rc = static_cast<zx_status_t>(res);
fprintf(stderr, "failed to unbind FVM driver: %s\n", zx_status_get_string(rc));
return rc;
}
printf("FVM volume metadata destroyed\n");
return ZX_OK;
}
} // namespace fvm
} // namespace blkctl