blob: caa48dd126c74685e6008559714ec829df810b4c [file] [log] [blame]
// Copyright 2021 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 "src/devices/bin/driver_manager/builtin_devices.h"
#include "src/devices/lib/log/log.h"
#include "src/lib/storage/vfs/cpp/vfs_types.h"
namespace {
namespace fio = fuchsia_io;
BuiltinDevices* instance = nullptr;
} // namespace
zx_status_t BuiltinDevVnode::Read(void* data, size_t len, size_t off, size_t* out_actual) {
if (null_) {
// /dev/null implementation.
*out_actual = 0;
return ZX_OK;
} else {
// /dev/zero implementation.
memset(data, 0, len);
*out_actual = len;
return ZX_OK;
}
}
zx_status_t BuiltinDevVnode::Write(const void* data, size_t len, size_t off, size_t* out_actual) {
if (null_) {
*out_actual = len;
return ZX_OK;
} else {
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t BuiltinDevVnode::GetAttributes(fs::VnodeAttributes* a) {
a->mode = V_TYPE_CDEV | V_IRUSR | V_IWUSR;
a->content_size = 0;
a->link_count = 1;
return ZX_OK;
}
void BuiltinDevVnode::HandleFsSpecificMessage(fidl::IncomingMessage& msg, fidl::Transaction* txn) {
fidl::WireDispatch(this, std::move(msg), txn);
}
fs::VnodeProtocolSet BuiltinDevVnode::GetProtocols() const {
return fs::VnodeProtocol::kDevice | fs::VnodeProtocol::kDirectory;
}
fs::VnodeProtocol BuiltinDevVnode::Negotiate(fs::VnodeProtocolSet protocols) const {
if ((protocols & fs::VnodeProtocol::kDevice).any()) {
return fs::VnodeProtocol::kDevice;
}
return fs::VnodeProtocol::kDirectory;
}
zx_status_t BuiltinDevVnode::GetNodeInfoForProtocol(fs::VnodeProtocol protocol, fs::Rights rights,
fs::VnodeRepresentation* info) {
if (protocol == fs::VnodeProtocol::kDevice) {
*info = fs::VnodeRepresentation::Device{};
return ZX_OK;
}
if (protocol == fs::VnodeProtocol::kDirectory) {
*info = fs::VnodeRepresentation::Directory{};
return ZX_OK;
}
return ZX_ERR_NOT_SUPPORTED;
}
void BuiltinDevVnode::Open(OpenRequestView request, OpenCompleter::Sync& completer) {
if (request->path.get() == ".") {
// We need to support opening ourselves again, because this is the mechanism used by
// V1 components when they route /dev/null or /dev/zero to themselves.
// It's safe to access instance directly, because |BuiltinDevVnode| is only instantiated through
// it.
ZX_DEBUG_ASSERT(instance != nullptr);
instance->HandleOpen(request->flags, std::move(request->object),
null_ ? kNullDevName : kZeroDevName);
return;
}
LOGF(ERROR, "%s: not implemented for builtin device", __func__);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void BuiltinDevVnode::ReadDirents(ReadDirentsRequestView request,
ReadDirentsCompleter::Sync& completer) {
LOGF(ERROR, "%s: not implemented for builtin device", __func__);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void BuiltinDevVnode::Rewind(RewindRequestView request, RewindCompleter::Sync& completer) {
LOGF(ERROR, "%s: not implemented for builtin device", __func__);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void BuiltinDevVnode::GetToken(GetTokenRequestView request, GetTokenCompleter::Sync& completer) {
LOGF(ERROR, "%s: not implemented for builtin device", __func__);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void BuiltinDevVnode::Link(LinkRequestView request, LinkCompleter::Sync& completer) {
LOGF(ERROR, "%s: not implemented for builtin device", __func__);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void BuiltinDevVnode::Watch(WatchRequestView request, WatchCompleter::Sync& completer) {
LOGF(ERROR, "%s: not implemented for builtin device", __func__);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
BuiltinDevices* BuiltinDevices::Get(async_dispatcher_t* dispatcher) {
if (instance == nullptr) {
instance = new BuiltinDevices(dispatcher);
}
return instance;
}
void BuiltinDevices::Reset() {
delete instance;
instance = nullptr;
}
zx_status_t BuiltinDevices::HandleOpen(fio::OpenFlags flags, fidl::ServerEnd<fio::Node> request,
std::string_view name) {
auto options = fs::VnodeConnectionOptions::FromIoV1Flags(flags);
fbl::RefPtr<fs::Vnode> vnode;
if (name == kNullDevName) {
vnode = null_dev_;
} else if (name == kZeroDevName) {
vnode = zero_dev_;
} else {
return ZX_ERR_INVALID_ARGS;
}
fbl::RefPtr<fs::Vnode> target;
if (!options.flags.node_reference) {
zx_status_t status = vnode->OpenValidating(options, &target);
if (status != ZX_OK) {
return status;
}
}
if (target == nullptr) {
target = vnode;
}
return vfs_.Serve(std::move(target), request.TakeChannel(), options);
}