blob: 16dc562428f17b27644ddf1db66f5a2673147aed [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 <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <blkctl/blkctl.h>
#include <blkctl/command.h>
#include <fbl/auto_call.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <fs-management/fvm.h>
#include <fs-management/ramdisk.h>
#include <fvm/fvm.h>
#include <lib/zx/time.h>
#include <unittest/unittest.h>
#include <zircon/assert.h>
#include <zircon/device/block.h>
#include <zircon/device/device.h>
#include <zircon/status.h>
#include "utils.h"
namespace blkctl {
namespace testing {
namespace {
const char* kBinName = "blkctl";
} // namespace
bool BlkCtlTest::SetCanned(const char* fmt, ...) {
BEGIN_HELPER;
va_list ap;
va_start(ap, fmt);
ssize_t len = vsnprintf(canned_, sizeof(canned_), fmt, ap);
va_end(ap);
ASSERT_GE(len, 0);
size_t n = static_cast<size_t>(len);
ASSERT_LT(n, sizeof(canned_));
use_canned_ = true;
END_HELPER;
}
bool BlkCtlTest::Run(zx_status_t expected, const char* fmt, ...) {
BEGIN_HELPER;
// Consume canned responses.
const char* canned = use_canned_ ? canned_ : nullptr;
use_canned_ = false;
// Construct command line.
char buf[PAGE_SIZE / 2];
va_list ap;
va_start(ap, fmt);
ssize_t len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
// Make a copy for error reporting
char cmd[PAGE_SIZE / 2];
snprintf(cmd, sizeof(cmd), "When executing 'blkctl %s'", buf);
ASSERT_GE(len, 0, cmd);
size_t n = static_cast<size_t>(len);
ASSERT_LT(n, sizeof(buf), cmd);
fbl::Vector<char*> args;
fbl::AllocChecker ac;
// Push argv[0]
args.push_back(const_cast<char*>(kBinName), &ac);
ASSERT_TRUE(ac.check(), cmd);
// Split remaining args
bool token = true;
for (size_t i = 0; i < n; ++i) {
if (isspace(buf[i])) {
buf[i] = '\0';
token = true;
} else if (token && isprint(buf[i])) {
args.push_back(&buf[i], &ac);
ASSERT_TRUE(ac.check(), cmd);
token = false;
}
}
// |expected| may match either parsing or execution
zx_status_t rc;
if ((rc = obj_.Parse(static_cast<int>(args.size()), args.get(), canned)) != ZX_OK) {
EXPECT_EQ(rc, expected, cmd);
} else {
// Always skip confirmations
obj_.set_force(true);
EXPECT_EQ(obj_.cmd()->Run(), expected, cmd);
}
END_HELPER;
}
bool ScopedDevice::Set(int fd, fbl::unique_fd* out) {
BEGIN_HELPER;
fbl::unique_fd ufd(fd);
ssize_t res = ioctl_device_get_topo_path(ufd.get(), path_, sizeof(path_));
ASSERT_GE(res, 0, zx_status_get_string(static_cast<zx_status_t>(res)));
if (out) {
out->swap(ufd);
}
END_HELPER;
}
bool ScopedDevice::Open(const char* parent, const char* child, fbl::unique_fd* out) {
BEGIN_HELPER;
ssize_t n;
if (child) {
n = snprintf(path_, sizeof(path_), "%s/%s", parent, child);
} else {
n = snprintf(path_, sizeof(path_), "%s", parent);
}
ASSERT_GE(n, 0);
ASSERT_LT(static_cast<size_t>(n), sizeof(path_));
ASSERT_EQ(wait_for_device(path_, zx::sec(3).get()), 0);
ASSERT_TRUE(Set(open(path_, O_RDWR), out));
END_HELPER;
}
ScopedRamdisk::~ScopedRamdisk() {
destroy_ramdisk(path());
}
bool ScopedRamdisk::Init(fbl::unique_fd* out) {
BEGIN_HELPER;
if (size_ != 0) {
destroy_ramdisk(path());
}
char path[PATH_MAX];
ASSERT_EQ(create_ramdisk(kBlockSize, kBlockCount, path), 0);
ASSERT_TRUE(Open(path, nullptr, out));
size_ = kBlockSize * kBlockCount;
END_HELPER;
}
bool ScopedFvmVolume::Init(fbl::unique_fd* out) {
BEGIN_HELPER;
fbl::unique_fd fd;
ASSERT_TRUE(ramdisk_.Init(&fd));
ASSERT_TRUE(fd);
ASSERT_EQ(fvm_init(fd.get(), kSliceSize), ZX_OK);
const char* driver = "/boot/driver/fvm.so";
ASSERT_GE(ioctl_device_bind(fd.get(), driver, strlen(driver)), 0);
ASSERT_TRUE(Open(ramdisk_.path(), "fvm", out));
slices_ = fvm::UsableSlicesCount(ramdisk_.size(), kSliceSize);
END_HELPER;
}
bool ScopedFvmPartition::Init(fbl::unique_fd* out) {
BEGIN_HELPER;
fbl::unique_fd fd;
ASSERT_TRUE(volume_.Init(&fd));
ASSERT_TRUE(fd);
slices_ = volume_.slices() / 2;
alloc_req_t req;
memset(&req, 0, sizeof(alloc_req_t));
req.slice_count = slices_;
ASSERT_TRUE(Set(fvm_allocate_partition(fd.get(), &req), out));
END_HELPER;
}
} // namespace testing
} // namespace blkctl