blob: 72557897b9e36ab14cdf1b22cdb8f33905279a33 [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 <vmofs/vmofs.h>
#include <fcntl.h>
#include <string.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <magenta/syscalls.h>
namespace vmofs {
struct dircookie_t {
uint64_t last_id;
};
static_assert(sizeof(dircookie_t) <= sizeof(vdircookie_t),
"vmofs dircookie too large to fit in IO state");
// Vnode -----------------------------------------------------------------------
Vnode::Vnode() = default;
Vnode::~Vnode() = default;
mx_status_t Vnode::Close() {
return MX_OK;
}
// VnodeFile --------------------------------------------------------------------
VnodeFile::VnodeFile(mx_handle_t vmo,
mx_off_t offset,
mx_off_t length)
: vmo_(vmo), offset_(offset), length_(length),
have_local_clone_(false) {}
VnodeFile::~VnodeFile() {
if (have_local_clone_) {
mx_handle_close(vmo_);
}
}
uint32_t VnodeFile::GetVType() {
return V_TYPE_FILE;
}
mx_status_t VnodeFile::Open(uint32_t flags) {
if (flags & O_DIRECTORY) {
return MX_ERR_NOT_DIR;
}
switch (flags & O_ACCMODE) {
case O_WRONLY:
case O_RDWR:
return MX_ERR_ACCESS_DENIED;
}
return MX_OK;
}
mx_status_t VnodeFile::Serve(fs::Vfs* vfs, mx::channel channel, uint32_t flags) {
return MX_OK;
}
ssize_t VnodeFile::Read(void* data, size_t length, size_t offset) {
if (offset > length_) {
return 0;
}
size_t remaining_length = length_ - offset;
if (length > remaining_length) {
length = remaining_length;
}
mx_status_t r = mx_vmo_read(vmo_, data, offset_ + offset, length, &length);
if (r < 0) {
return r;
}
return length;
}
constexpr uint64_t kVmofsBlksize = PAGE_SIZE;
mx_status_t VnodeFile::Getattr(vnattr_t* attr) {
memset(attr, 0, sizeof(vnattr_t));
attr->mode = V_TYPE_FILE | V_IRUSR;
attr->size = length_;
attr->blksize = kVmofsBlksize;
attr->blkcount = fbl::roundup(attr->size, kVmofsBlksize) / VNATTR_BLKSIZE;
attr->nlink = 1;
return MX_OK;
}
mx_status_t VnodeFile::GetHandles(uint32_t flags, mx_handle_t* hnds,
uint32_t* type, void* extra, uint32_t* esize) {
mx_off_t* offset = static_cast<mx_off_t*>(extra);
mx_off_t* length = offset + 1;
mx_handle_t vmo;
mx_status_t status;
if (!have_local_clone_) {
status = mx_vmo_clone(vmo_, MX_VMO_CLONE_COPY_ON_WRITE, offset_, length_, &vmo_);
if (status < 0)
return status;
offset_ = 0;
have_local_clone_ = true;
}
status = mx_handle_duplicate(
vmo_,
MX_RIGHT_READ | MX_RIGHT_EXECUTE | MX_RIGHT_MAP |
MX_RIGHT_DUPLICATE | MX_RIGHT_TRANSFER | MX_RIGHT_GET_PROPERTY,
&vmo);
if (status < 0) {
return status;
}
*offset = offset_;
*length = length_;
hnds[0] = vmo;
*type = MXIO_PROTOCOL_VMOFILE;
*esize = sizeof(mx_off_t) * 2;
return 1;
}
// VnodeDir --------------------------------------------------------------------
VnodeDir::VnodeDir(fbl::Array<fbl::StringPiece> names,
fbl::Array<fbl::RefPtr<Vnode>> children)
: names_(fbl::move(names)),
children_(fbl::move(children)) {
MX_DEBUG_ASSERT(names_.size() == children_.size());
}
VnodeDir::~VnodeDir() = default;
uint32_t VnodeDir::GetVType() {
return V_TYPE_DIR;
}
mx_status_t VnodeDir::Open(uint32_t flags) {
return MX_OK;
}
mx_status_t VnodeDir::Lookup(fbl::RefPtr<fs::Vnode>* out, const char* name, size_t len) {
fbl::StringPiece value(name, len);
auto* it = fbl::lower_bound(names_.begin(), names_.end(), value);
if (it == names_.end() || *it != value) {
return MX_ERR_NOT_FOUND;
}
*out = children_[it - names_.begin()];
return MX_OK;
}
mx_status_t VnodeDir::Getattr(vnattr_t* attr) {
memset(attr, 0, sizeof(vnattr_t));
attr->mode = V_TYPE_DIR | V_IRUSR;
attr->blksize = kVmofsBlksize;
attr->blkcount = fbl::roundup(attr->size, kVmofsBlksize) / VNATTR_BLKSIZE;
attr->nlink = 1;
return MX_OK;
}
mx_status_t VnodeDir::Readdir(void* cookie, void* data, size_t len) {
dircookie_t* c = static_cast<dircookie_t*>(cookie);
fs::DirentFiller df(data, len);
mx_status_t r = 0;
if (c->last_id < 1) {
if ((r = df.Next(".", 1, VTYPE_TO_DTYPE(V_TYPE_DIR))) != MX_OK) {
return df.BytesFilled();
}
c->last_id = 1;
}
for (size_t i = c->last_id - 2; i < children_.size(); ++i) {
fbl::StringPiece name = names_[i];
const auto& child = children_[i];
uint32_t vtype = child->GetVType();
if ((r = df.Next(name.data(), name.length(), VTYPE_TO_DTYPE(vtype))) != MX_OK) {
break;
}
c->last_id = i + 2;
}
return df.BytesFilled();
}
} // namespace vmofs