blob: 971e189ddd863917dc598cd351abe3e4088bf538 [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 <fcntl.h>
#include <sys/stat.h>
#include <fbl/auto_lock.h>
namespace fs {
PseudoDir::PseudoDir() = default;
PseudoDir::~PseudoDir() = default;
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->nlink = 1;
return ZX_OK;
}
zx_status_t PseudoDir::Lookup(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name) {
fbl::AutoLock lock(&mutex_);
for (const auto& entry : entries_) {
if (entry.name().ToStringPiece() == name) {
*out = entry.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(Vfs* vfs, const vfs_watch_dir_t* cmd) {
return watcher_.WatchDir(vfs, this, cmd);
}
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) {
if ((r = df.Next(".", VTYPE_TO_DTYPE(V_TYPE_DIR))) != ZX_OK) {
*out_actual = df.BytesFilled();
return r;
}
cookie->n = kDotId;
}
fbl::AutoLock lock(&mutex_);
for (const auto& entry : entries_) {
if (cookie->n >= entry.id()) {
continue;
}
vnattr_t attr;
if ((r = entry.node()->Getattr(&attr)) != ZX_OK) {
continue;
}
if ((r = df.Next(entry.name().ToStringPiece(),
VTYPE_TO_DTYPE(attr.mode))) != ZX_OK) {
*out_actual = df.BytesFilled();
return r;
}
cookie->n = entry.id();
}
*out_actual = df.BytesFilled();
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_);
for (auto& entry : entries_) {
if (entry.name() == name) {
return ZX_ERR_ALREADY_EXISTS;
}
}
Notify(name.ToStringPiece(), VFS_WATCH_EVT_ADDED);
auto entry = fbl::unique_ptr<Entry>(new Entry(next_node_id_++,
fbl::move(name), fbl::move(vn)));
entries_.push_back(fbl::move(entry));
return ZX_OK;
}
zx_status_t PseudoDir::RemoveEntry(fbl::StringPiece name) {
fbl::AutoLock lock(&mutex_);
for (auto& entry : entries_) {
if (entry.name().ToStringPiece() == name) {
entries_.erase(entry);
Notify(name, VFS_WATCH_EVT_REMOVED);
return ZX_OK;
}
}
return ZX_ERR_NOT_FOUND;
}
void PseudoDir::RemoveAllEntries() {
fbl::AutoLock lock(&mutex_);
for (auto& entry : entries_) {
Notify(entry.name().ToStringPiece(), VFS_WATCH_EVT_REMOVED);
}
entries_.clear();
}
PseudoDir::Entry::Entry(uint64_t id, fbl::String name, fbl::RefPtr<fs::Vnode> node)
: id_(id), name_(fbl::move(name)), node_(fbl::move(node)) {
}
PseudoDir::Entry::~Entry() = default;
} // namespace fs