blob: 5c051468019a796104fa63589303110034e244e7 [file] [log] [blame]
// Copyright 2017 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 <lib/async-loop/loop.h>
#include <lib/fdio/namespace.h>
#include <lib/fit/function.h>
#include <lib/memfs/memfs.h>
#include <lib/sync/completion.h>
#include <lib/zx/channel.h>
#include <lib/zx/status.h>
#include <zircon/assert.h>
#include <memory>
#include <string>
#include "src/storage/memfs/memfs.h"
#include "src/storage/memfs/vnode_dir.h"
struct memfs_filesystem {
public:
// Creates a memfs instance associated with the given dispatcher.
static zx::status<memfs_filesystem> Create(async_dispatcher_t* dispatcher) {
auto fs_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (fs_endpoints.is_error())
return fs_endpoints.take_error();
std::unique_ptr<memfs::Memfs> memfs;
fbl::RefPtr<memfs::VnodeDir> root;
if (zx_status_t status = memfs::Memfs::Create(dispatcher, "<tmp>", &memfs, &root);
status != ZX_OK) {
return zx::error(status);
}
if (zx_status_t status =
memfs->ServeDirectory(std::move(root), std::move(fs_endpoints->server));
status != ZX_OK) {
return zx::error(status);
}
return zx::ok(memfs_filesystem(std::move(memfs), fs_endpoints->client.TakeChannel()));
}
// Moveable but not copyable.
memfs_filesystem(const memfs_filesystem&) = delete;
memfs_filesystem(memfs_filesystem&&) = default;
// If AsyncTearDown() has not been called, does synchronous tear-down, blocking on cleanup. The
// message loop (dispatcher passed to Create()) must still be alive or this will deadlock.
~memfs_filesystem() {
if (memfs_) {
// Need to synchronize on teardown.
sync_completion_t unmounted;
AsyncTearDown([&unmounted](zx_status_t) { sync_completion_signal(&unmounted); });
sync_completion_wait(&unmounted, zx::duration::infinite().get());
}
}
// Takes ownership of the root() channel and installs it at the given path. The root() must be
// a valid handle before this call (ZX_ERR_BAD_STATE will be returned if not) and it will be
// cleared before the call completes.
//
// The mounted path will be automatically unmounted at tear-down.
zx_status_t MountAt(std::string path) {
if (!root_)
return ZX_ERR_BAD_STATE;
if (path.empty())
return ZX_ERR_NOT_SUPPORTED;
if (zx_status_t status = fdio_ns_get_installed(&namespace_); status != ZX_OK)
return status;
mounted_path_ = std::move(path);
return fdio_ns_bind(namespace_, mounted_path_.c_str(), root_.release());
}
// Deleting the setup via the destructor will trigger synchronous teardown and block on the
// filesystem cleanup (which might be on another thread or happen in the future on the current
// one).
//
// This function allows clients to trigger asynchronous cleanup. The callback will get called
// ON THE MEMFS THREAD (the dispatcher passed into Create()) class was created) after Memfs has
// been deleted with the status value from memfs teardown. After this call, the memfs_filesystem
// object can get deleted and memfs may outlive it.
void AsyncTearDown(fit::callback<void(zx_status_t)> cb) {
ZX_DEBUG_ASSERT(memfs_);
if (!mounted_path_.empty()) {
// If unmounting fails we continue with tear-down since there's not much else to do.
fdio_ns_unbind(namespace_, mounted_path_.c_str());
}
memfs::Memfs* memfs_ptr = memfs_.get(); // Need to both use & move in below line.
memfs_ptr->Shutdown(
[memfs = std::move(memfs_), cb = std::move(cb)](zx_status_t status) mutable {
memfs.reset(); // Release memfs class class before signaling.
cb(status);
});
}
// The channel to the root directory of the filesystem. Users can move this out, close it, or use
// in-place as they need.
//
// InstallRootAt() will take ownership of the root and clear this handle.
zx::channel& root() { return root_; }
const zx::channel& root() const { return root_; }
private:
memfs_filesystem(std::unique_ptr<memfs::Memfs> memfs, zx::channel root)
: memfs_(std::move(memfs)), root_(std::move(root)) {}
std::unique_ptr<memfs::Memfs> memfs_;
zx::channel root_;
fdio_ns_t* namespace_ = nullptr; // Set when mounted (for unmounting).
std::string mounted_path_; // Empty if not mounted.
};
zx_status_t memfs_create_filesystem(async_dispatcher_t* dispatcher, memfs_filesystem_t** out_fs,
zx_handle_t* out_root) {
ZX_DEBUG_ASSERT(dispatcher != nullptr);
ZX_DEBUG_ASSERT(out_fs != nullptr);
ZX_DEBUG_ASSERT(out_root != nullptr);
zx::status<memfs_filesystem> setup_or = memfs_filesystem::Create(dispatcher);
if (setup_or.is_error())
return setup_or.error_value();
*out_root = setup_or->root().release();
*out_fs = new memfs_filesystem_t(std::move(*setup_or));
return ZX_OK;
}
zx_status_t memfs_install_at(async_dispatcher_t* dispatcher, const char* path,
memfs_filesystem_t** out_fs) {
ZX_DEBUG_ASSERT(dispatcher);
ZX_DEBUG_ASSERT(path);
ZX_DEBUG_ASSERT(out_fs);
zx::status<memfs_filesystem> setup_or = memfs_filesystem::Create(dispatcher);
if (setup_or.is_error())
return setup_or.error_value();
if (zx_status_t status = setup_or->MountAt(path); status != ZX_OK)
return status;
*out_fs = new memfs_filesystem_t(std::move(*setup_or));
return ZX_OK;
}
void memfs_free_filesystem(memfs_filesystem_t* fs, sync_completion_t* unmounted) {
ZX_DEBUG_ASSERT(fs);
// Note: This deletes the memfs_filesystem_t pointer on the memfs thread which might be different
// than the current one.
fs->AsyncTearDown([fs, unmounted](zx_status_t status) {
delete fs;
if (unmounted) {
sync_completion_signal(unmounted);
}
});
}