blob: df12b2e2b50d932c9813cc973a8e4d86adfe8b70 [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 "src/storage/blobfs/directory.h"
#include <fuchsia/device/c/fidl.h>
#include <lib/fidl-utils/bind.h>
#include <lib/sync/completion.h>
#include <stdlib.h>
#include <string.h>
#include <zircon/device/vfs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <utility>
#include <digest/digest.h>
#include <fbl/ref_ptr.h>
#include <fbl/string_piece.h>
#include <fs/metrics/events.h>
#include <fs/vfs_types.h>
#include "src/storage/blobfs/blob.h"
#include "src/storage/blobfs/blobfs.h"
#include "src/storage/blobfs/metrics.h"
namespace blobfs {
Directory::Directory(Blobfs* bs) : blobfs_(bs) {}
BlobCache& Directory::Cache() { return blobfs_->Cache(); }
Directory::~Directory() = default;
zx_status_t Directory::GetNodeInfoForProtocol([[maybe_unused]] fs::VnodeProtocol protocol,
[[maybe_unused]] fs::Rights rights,
fs::VnodeRepresentation* info) {
*info = fs::VnodeRepresentation::Directory();
return ZX_OK;
}
fs::VnodeProtocolSet Directory::GetProtocols() const { return fs::VnodeProtocol::kDirectory; }
zx_status_t Directory::Readdir(fs::VdirCookie* cookie, void* dirents, size_t len,
size_t* out_actual) {
return blobfs_->Readdir(cookie, dirents, len, out_actual);
}
zx_status_t Directory::Read(void* data, size_t len, size_t off, size_t* out_actual) {
return ZX_ERR_NOT_FILE;
}
zx_status_t Directory::Write(const void* data, size_t len, size_t offset, size_t* out_actual) {
return ZX_ERR_NOT_FILE;
}
zx_status_t Directory::Append(const void* data, size_t len, size_t* out_end, size_t* out_actual) {
return ZX_ERR_NOT_FILE;
}
zx_status_t Directory::Lookup(fbl::StringPiece name, fbl::RefPtr<fs::Vnode>* out) {
TRACE_DURATION("blobfs", "Directory::Lookup", "name", name);
auto event = blobfs_->Metrics()->NewLatencyEvent(fs_metrics::Event::kLookUp);
assert(memchr(name.data(), '/', name.length()) == nullptr);
if (name == ".") {
// Special case: Accessing root directory via '.'
*out = fbl::RefPtr<Directory>(this);
return ZX_OK;
}
zx_status_t status;
Digest digest;
if ((status = digest.Parse(name.data(), name.length())) != ZX_OK) {
return status;
}
fbl::RefPtr<CacheNode> cache_node;
if ((status = Cache().Lookup(digest, &cache_node)) != ZX_OK) {
return status;
}
auto vnode = fbl::RefPtr<Blob>::Downcast(std::move(cache_node));
blobfs_->Metrics()->UpdateLookup(vnode->SizeData());
*out = std::move(vnode);
return ZX_OK;
}
zx_status_t Directory::GetAttributes(fs::VnodeAttributes* a) {
*a = fs::VnodeAttributes();
a->mode = V_TYPE_DIR | V_IRUSR;
a->inode = ::llcpp::fuchsia::io::INO_UNKNOWN;
a->content_size = 0;
a->storage_size = 0;
a->link_count = 1;
a->creation_time = 0;
a->modification_time = 0;
return ZX_OK;
}
zx_status_t Directory::Create(fbl::StringPiece name, uint32_t mode, fbl::RefPtr<fs::Vnode>* out) {
TRACE_DURATION("blobfs", "Directory::Create", "name", name, "mode", mode);
auto event = blobfs_->Metrics()->NewLatencyEvent(fs_metrics::Event::kCreate);
assert(memchr(name.data(), '/', name.length()) == nullptr);
Digest digest;
zx_status_t status;
if ((status = digest.Parse(name.data(), name.length())) != ZX_OK) {
return status;
}
fbl::RefPtr<Blob> vn = fbl::AdoptRef(new Blob(blobfs_, std::move(digest)));
if ((status = Cache().Add(vn)) != ZX_OK) {
return status;
}
if ((status = vn->OpenValidating(fs::VnodeConnectionOptions(), nullptr)) != ZX_OK) {
return status;
}
*out = std::move(vn);
return ZX_OK;
}
#ifdef __Fuchsia__
zx_status_t Directory::QueryFilesystem(::llcpp::fuchsia::io::FilesystemInfo* info) {
blobfs_->GetFilesystemInfo(info);
return ZX_OK;
}
zx_status_t Directory::GetDevicePath(size_t buffer_len, char* out_name, size_t* out_len) {
return blobfs_->Device()->GetDevicePath(buffer_len, out_name, out_len);
}
#endif // __Fuchsia__
zx_status_t Directory::Unlink(fbl::StringPiece name, bool must_be_dir) {
TRACE_DURATION("blobfs", "Directory::Unlink", "name", name, "must_be_dir", must_be_dir);
auto event = blobfs_->Metrics()->NewLatencyEvent(fs_metrics::Event::kUnlink);
assert(memchr(name.data(), '/', name.length()) == nullptr);
zx_status_t status;
Digest digest;
if ((status = digest.Parse(name.data(), name.length())) != ZX_OK) {
return status;
}
fbl::RefPtr<CacheNode> cache_node;
if ((status = Cache().Lookup(digest, &cache_node)) != ZX_OK) {
return status;
}
auto vnode = fbl::RefPtr<Blob>::Downcast(std::move(cache_node));
blobfs_->Metrics()->UpdateLookup(vnode->SizeData());
return vnode->QueueUnlink();
}
void Directory::Sync(SyncCallback closure) {
blobfs_->Sync([this, cb = std::move(closure)](zx_status_t status) mutable {
// This callback will be issued on the journal thread in the normal case. This is important
// because the WriteTxn must happen there or it will block the main thread which would block
// processing other requests.
//
// If called during shutdown this may get issued on the main thread but then the flush
// transaction should be a no-op.
if (status == ZX_OK) {
fs::WriteTxn sync_txn(blobfs_);
sync_txn.EnqueueFlush();
status = sync_txn.Transact();
}
cb(status);
});
}
#ifdef __Fuchsia__
void Directory::HandleFsSpecificMessage(fidl_incoming_msg_t* msg, fidl::Transaction* txn) {
llcpp::fuchsia::blobfs::Blobfs::Dispatch(this, msg, txn);
}
void Directory::GetAllocatedRegions(GetAllocatedRegionsCompleter::Sync& completer) {
static_assert(sizeof(llcpp::fuchsia::blobfs::BlockRegion) == sizeof(BlockRegion));
static_assert(offsetof(llcpp::fuchsia::blobfs::BlockRegion, offset) ==
offsetof(BlockRegion, offset));
static_assert(offsetof(llcpp::fuchsia::blobfs::BlockRegion, length) ==
offsetof(BlockRegion, length));
zx::vmo vmo;
zx_status_t status = ZX_OK;
fbl::Vector<BlockRegion> buffer = blobfs_->GetAllocator()->GetAllocatedRegions();
uint64_t allocations = buffer.size();
if (allocations != 0) {
status = zx::vmo::create(sizeof(BlockRegion) * allocations, 0, &vmo);
if (status == ZX_OK) {
status = vmo.write(buffer.data(), 0, sizeof(BlockRegion) * allocations);
}
}
if (status == ZX_OK) {
completer.Reply(ZX_OK, std::move(vmo), allocations);
} else {
completer.Reply(status, zx::vmo(), 0);
}
}
void Directory::SetCorruptBlobHandler(zx::channel handler,
SetCorruptBlobHandlerCompleter::Sync& completer) {
blobfs_->SetCorruptBlobHandler(std::move(handler));
completer.Reply(ZX_OK);
}
#endif // __Fuchsia__
} // namespace blobfs