#include "base/test/scoped_dev_zero_fuchsia.h"
#include <fuchsia/io/cpp/fidl.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/vfs/cpp/pseudo_dir.h>
#include <lib/vfs/cpp/vmo_file.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <stdint.h>
#include <zircon/types.h>
#include <functional>
#include <utility>
#include "base/check.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
namespace base {
class ScopedDevZero::Server {
public:
Server(fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
OnceCallback<void(zx_status_t status)> on_initialized);
Server(const Server&) = delete;
Server& operator=(const Server&) = delete;
~Server() = default;
private:
vfs::PseudoDir dev_dir_;
};
ScopedDevZero::Server::Server(
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
OnceCallback<void(zx_status_t status)> on_initialized) {
zx::vmo vmo;
auto status = zx::vmo::create(UINT32_MAX, 0, &vmo);
ZX_LOG_IF(ERROR, status != ZX_OK, status);
if (status == ZX_OK) {
status = dev_dir_.AddEntry(
"zero",
std::make_unique<vfs::VmoFile>(std::move(vmo), UINT32_MAX));
ZX_LOG_IF(ERROR, status != ZX_OK, status);
}
if (status == ZX_OK) {
status = dev_dir_.Serve(fuchsia::io::OpenFlags::RIGHT_READABLE,
directory_request.TakeChannel());
ZX_LOG_IF(ERROR, status != ZX_OK, status);
}
std::move(on_initialized).Run(status);
}
ScopedDevZero* ScopedDevZero::instance_ = nullptr;
scoped_refptr<ScopedDevZero> ScopedDevZero::Get() {
if (instance_)
return WrapRefCounted(instance_);
scoped_refptr<ScopedDevZero> result = AdoptRef(new ScopedDevZero);
return result->Initialize() ? std::move(result) : nullptr;
}
ScopedDevZero::ScopedDevZero() : io_thread_("/dev/zero") {
DCHECK_EQ(instance_, nullptr);
instance_ = this;
}
ScopedDevZero::~ScopedDevZero() {
DCHECK_EQ(instance_, this);
if (global_namespace_)
fdio_ns_unbind(std::exchange(global_namespace_, nullptr), "/dev");
instance_ = nullptr;
}
bool ScopedDevZero::Initialize() {
auto status = fdio_ns_get_installed(&global_namespace_);
if (status != ZX_OK) {
ZX_LOG(ERROR, status);
return false;
}
if (!io_thread_.StartWithOptions(Thread::Options(MessagePumpType::IO, 0)))
return false;
zx::channel client;
zx::channel request;
status = zx::channel::create(0, &client, &request);
ZX_CHECK(status == ZX_OK, status);
RunLoop run_loop;
server_ = SequenceBound<Server>(
io_thread_.task_runner(),
fidl::InterfaceRequest<fuchsia::io::Directory>(std::move(request)),
base::BindOnce(
[](base::OnceClosure quit_loop, zx_status_t& status,
zx_status_t init_status) {
status = init_status;
std::move(quit_loop).Run();
},
run_loop.QuitClosure(), std::ref(status)));
run_loop.Run();
if (status != ZX_OK)
return false;
status = fdio_ns_bind(global_namespace_, "/dev", client.release());
if (status != ZX_OK) {
ZX_LOG(ERROR, status);
global_namespace_ = nullptr;
server_.Reset();
return false;
}
return true;
}
}