blob: 4f9466be15edb32143c17a2909e9768368e9bbe3 [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 <fs/pseudo-dir.h>
#include <sys/stat.h>
#include <fbl/auto_lock.h>
#include <fuchsia/io/c/fidl.h>
#include <utility>
namespace fs {
PseudoDir::PseudoDir() = default;
PseudoDir::~PseudoDir() {
entries_by_name_.clear_unsafe();
entries_by_id_.clear();
}
zx_status_t PseudoDir::Open(uint32_t flags, fbl::RefPtr<Vnode>* out_redirect) {
return ZX_OK;
}
zx_status_t PseudoDir::Getattr(vnattr_t* attr) {
memset(attr, 0, sizeof(vnattr_t));
attr->mode = V_TYPE_DIR | V_IRUSR;
attr->inode = fuchsia_io_INO_UNKNOWN;
attr->nlink = 1;
return ZX_OK;
}
zx_status_t PseudoDir::Lookup(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name) {
fbl::AutoLock lock(&mutex_);
auto it = entries_by_name_.find(name);
if (it != entries_by_name_.end()) {
*out = it->node();
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
void PseudoDir::Notify(fbl::StringPiece name, unsigned event) {
watcher_.Notify(name, event);
}
zx_status_t PseudoDir::WatchDir(fs::Vfs* vfs, uint32_t mask, uint32_t options,
zx::channel watcher) {
return watcher_.WatchDir(vfs, this, mask, options, std::move(watcher));
}
zx_status_t PseudoDir::Readdir(vdircookie_t* cookie, void* data, size_t len, size_t* out_actual) {
fs::DirentFiller df(data, len);
zx_status_t r = 0;
if (cookie->n < kDotId) {
uint64_t ino = fuchsia_io_INO_UNKNOWN;
if ((r = df.Next(".", VTYPE_TO_DTYPE(V_TYPE_DIR), ino)) != ZX_OK) {
*out_actual = df.BytesFilled();
return r;
}
cookie->n = kDotId;
}
fbl::AutoLock lock(&mutex_);
for (auto it = entries_by_id_.lower_bound(cookie->n); it != entries_by_id_.end(); ++it) {
if (cookie->n >= it->id()) {
continue;
}
vnattr_t attr;
if ((r = it->node()->Getattr(&attr)) != ZX_OK) {
continue;
}
if (df.Next(it->name().ToStringPiece(),
VTYPE_TO_DTYPE(attr.mode), attr.inode) != ZX_OK) {
*out_actual = df.BytesFilled();
return ZX_OK;
}
cookie->n = it->id();
}
*out_actual = df.BytesFilled();
return ZX_OK;
}
zx_status_t PseudoDir::GetNodeInfo(uint32_t flags, fuchsia_io_NodeInfo* info) {
info->tag = fuchsia_io_NodeInfoTag_directory;
return ZX_OK;
}
zx_status_t PseudoDir::AddEntry(fbl::String name, fbl::RefPtr<fs::Vnode> vn) {
ZX_DEBUG_ASSERT(vn);
if (!vfs_valid_name(name.ToStringPiece())) {
return ZX_ERR_INVALID_ARGS;
}
fbl::AutoLock lock(&mutex_);
if (entries_by_name_.find(name) != entries_by_name_.end()) {
return ZX_ERR_ALREADY_EXISTS;
}
Notify(name.ToStringPiece(), fuchsia_io_WATCH_EVENT_ADDED);
auto entry = std::make_unique<Entry>(next_node_id_++,
std::move(name), std::move(vn));
entries_by_name_.insert(entry.get());
entries_by_id_.insert(std::move(entry));
return ZX_OK;
}
zx_status_t PseudoDir::RemoveEntry(fbl::StringPiece name) {
fbl::AutoLock lock(&mutex_);
auto it = entries_by_name_.find(name);
if (it != entries_by_name_.end()) {
entries_by_name_.erase(it);
entries_by_id_.erase(it->id());
Notify(name, fuchsia_io_WATCH_EVENT_REMOVED);
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
zx_status_t PseudoDir::RemoveEntry(fbl::StringPiece name, fs::Vnode* vn) {
fbl::AutoLock lock(&mutex_);
auto it = entries_by_name_.find(name);
if (it != entries_by_name_.end() && it->node().get() == vn) {
entries_by_name_.erase(it);
entries_by_id_.erase(it->id());
Notify(name, fuchsia_io_WATCH_EVENT_REMOVED);
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
void PseudoDir::RemoveAllEntries() {
fbl::AutoLock lock(&mutex_);
for (auto& entry : entries_by_name_) {
Notify(entry.name().ToStringPiece(), fuchsia_io_WATCH_EVENT_REMOVED);
}
entries_by_name_.clear();
entries_by_id_.clear();
}
bool PseudoDir::IsEmpty() const {
fbl::AutoLock lock(&mutex_);
return entries_by_name_.is_empty();
}
PseudoDir::Entry::Entry(uint64_t id, fbl::String name, fbl::RefPtr<fs::Vnode> node)
: id_(id), name_(std::move(name)), node_(std::move(node)) {
}
PseudoDir::Entry::~Entry() = default;
} // namespace fs