blob: a4ec66097458474a2372e995f1e3721b742d6957 [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 <atomic>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/ref_ptr.h>
#include <fbl/unique_ptr.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/vfs.h>
#include <fs/vfs.h>
#include <lib/memfs/cpp/vnode.h>
#include <lib/memfs/memfs.h>
#include <lib/sync/completion.h>
#include <zircon/device/vfs.h>
#include "dnode.h"
namespace {
constexpr size_t kPageSize = static_cast<size_t>(PAGE_SIZE);
}
namespace memfs {
zx_status_t Vfs::CreateFromVmo(VnodeDir* parent, fbl::StringPiece name,
zx_handle_t vmo, zx_off_t off,
zx_off_t len) {
fbl::AutoLock lock(&vfs_lock_);
return parent->CreateFromVmo(name, vmo, off, len);
}
void Vfs::MountSubtree(VnodeDir* parent, fbl::RefPtr<VnodeDir> subtree) {
fbl::AutoLock lock(&vfs_lock_);
parent->MountSubtree(std::move(subtree));
}
zx_status_t Vfs::FillFsId() {
if (fs_id_) {
return ZX_OK;
}
zx::event event;
zx_status_t status = zx::event::create(0, &event);
if (status != ZX_OK) {
return status;
}
zx_info_handle_basic_t info;
status = event.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
fs_id_ = info.koid;
return ZX_OK;
}
zx_status_t Vfs::GrowVMO(zx::vmo& vmo, size_t current_size,
size_t request_size, size_t* actual_size) {
if (request_size <= current_size) {
*actual_size = current_size;
return ZX_OK;
}
size_t aligned_len = fbl::round_up(request_size, kPageSize);
ZX_DEBUG_ASSERT(current_size % kPageSize == 0);
size_t num_new_pages = (aligned_len - current_size) / kPageSize;
if (num_new_pages + num_allocated_pages_ > pages_limit_) {
*actual_size = current_size;
return ZX_ERR_NO_SPACE;
}
zx_status_t status;
if (!vmo.is_valid()) {
if ((status = zx::vmo::create(aligned_len, 0, &vmo)) != ZX_OK) {
return status;
}
} else {
if ((status = vmo.set_size(aligned_len)) != ZX_OK) {
return status;
}
}
// vmo operation succeeded
num_allocated_pages_ += num_new_pages;
*actual_size = aligned_len;
return ZX_OK;
}
void Vfs::WillFreeVMO(size_t vmo_size) {
ZX_DEBUG_ASSERT(vmo_size % kPageSize == 0);
size_t freed_pages = vmo_size / kPageSize;
ZX_DEBUG_ASSERT(freed_pages <= num_allocated_pages_);
num_allocated_pages_ -= freed_pages;
}
std::atomic<uint64_t> VnodeMemfs::ino_ctr_ = 0;
std::atomic<uint64_t> VnodeMemfs::deleted_ino_ctr_ = 0;
VnodeMemfs::VnodeMemfs(Vfs* vfs) : dnode_(nullptr), link_count_(0), vfs_(vfs),
ino_(ino_ctr_.fetch_add(1, std::memory_order_relaxed)) {
create_time_ = modify_time_ = zx_clock_get(ZX_CLOCK_UTC);
}
VnodeMemfs::~VnodeMemfs() {
deleted_ino_ctr_.fetch_add(1, std::memory_order_relaxed);
}
zx_status_t VnodeMemfs::Setattr(const vnattr_t* attr) {
if ((attr->valid & ~(ATTR_MTIME)) != 0) {
// only attr currently supported
return ZX_ERR_INVALID_ARGS;
}
if (attr->valid & ATTR_MTIME) {
modify_time_ = attr->modify_time;
}
return ZX_OK;
}
void VnodeMemfs::Sync(SyncCallback closure) {
// Since this filesystem is in-memory, all data is already up-to-date in
// the underlying storage
closure(ZX_OK);
}
zx_status_t VnodeMemfs::AttachRemote(fs::MountChannel h) {
if (!IsDirectory()) {
return ZX_ERR_NOT_DIR;
} else if (IsRemote()) {
return ZX_ERR_ALREADY_BOUND;
}
SetRemote(h.TakeChannel());
return ZX_OK;
}
zx_status_t CreateFilesystem(const char* name, memfs::Vfs* vfs, fbl::RefPtr<VnodeDir>* out) {
zx_status_t status;
if ((status = vfs->FillFsId()) != ZX_OK) {
return status;
}
fbl::AllocChecker ac;
fbl::RefPtr<VnodeDir> fs = fbl::AdoptRef(new (&ac) VnodeDir(vfs));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
fbl::RefPtr<Dnode> dn = Dnode::Create(name, fs);
if (dn == nullptr) {
return ZX_ERR_NO_MEMORY;
}
fs->dnode_ = dn; // FS root is directory
*out = fs;
return ZX_OK;
}
} // namespace memfs