blob: c167f3b21b0bb36df9281eb327269914633ce8ee [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 <fcntl.h>
#include <inttypes.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/vfs.h>
#include <lib/memfs/cpp/vnode.h>
#include <lib/memfs/memfs.h>
#include <lib/sync/completion.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <zircon/device/vfs.h>
#include <zircon/time.h>
#include <atomic>
#include <ctime>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/ref_ptr.h>
#include <fs/vfs.h>
#include "dnode.h"
namespace memfs {
namespace {
constexpr size_t kPageSize = static_cast<size_t>(PAGE_SIZE);
zx_status_t CreateID(uint64_t* out_id) {
zx::event id;
zx_status_t status = zx::event::create(0, &id);
if (status != ZX_OK) {
return status;
}
zx_info_handle_basic_t info;
status = id.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
*out_id = info.koid;
return ZX_OK;
}
} // namespace
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);
zx_status_t status;
if (!vmo.is_valid()) {
if ((status = zx::vmo::create(aligned_len, ZX_VMO_RESIZABLE, &vmo)) != ZX_OK) {
return status;
}
} else {
if ((status = vmo.set_size(aligned_len)) != ZX_OK) {
return status;
}
}
// vmo operation succeeded
*actual_size = aligned_len;
return ZX_OK;
}
zx_status_t Vfs::Create(const char* name, std::unique_ptr<memfs::Vfs>* out_vfs,
fbl::RefPtr<VnodeDir>* out_root) {
uint64_t id;
zx_status_t status = CreateID(&id);
if (status != ZX_OK) {
return status;
}
auto fs = std::unique_ptr<memfs::Vfs>(new memfs::Vfs(id, name));
fbl::RefPtr<VnodeDir> root = fbl::AdoptRef(new VnodeDir(fs.get()));
std::unique_ptr<Dnode> dn = Dnode::Create(name, root);
root->dnode_ = dn.get();
fs->root_ = std::move(dn);
*out_root = std::move(root);
*out_vfs = std::move(fs);
return ZX_OK;
}
Vfs::Vfs(uint64_t id, const char* name) : fs::ManagedVfs(), fs_id_(id) {}
Vfs::~Vfs() = default;
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);
}
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_(0),
modify_time_(0) {
std::timespec ts;
if (std::timespec_get(&ts, TIME_UTC)) {
create_time_ = modify_time_ = zx_time_from_timespec(ts);
}
}
VnodeMemfs::~VnodeMemfs() { deleted_ino_ctr_.fetch_add(1, std::memory_order_relaxed); }
zx_status_t VnodeMemfs::SetAttributes(fs::VnodeAttributesUpdate attr) {
if (attr.has_modification_time()) {
modify_time_ = attr.take_modification_time();
}
if (attr.any()) {
// any unhandled field update is unsupported
return ZX_ERR_INVALID_ARGS;
}
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(std::move(h.client_end()));
return ZX_OK;
}
} // namespace memfs