blob: a9cfe4fb05eef1b1215d132c91f2cc48662a46b0 [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 <inttypes.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <async/cpp/loop.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/atomic.h>
#include <fbl/auto_lock.h>
#include <fbl/ref_ptr.h>
#include <fbl/unique_ptr.h>
#include <fdio/vfs.h>
#include <fs/vfs.h>
#include <memfs/memfs.h>
#include <memfs/vnode.h>
#include <zircon/device/vfs.h>
#include "dnode.h"
namespace memfs {
zx_status_t Vfs::CreateFromVmo(VnodeDir* parent, bool vmofile, fbl::StringPiece name,
zx_handle_t vmo, zx_off_t off,
zx_off_t len) {
fbl::AutoLock lock(&vfs_lock_);
return parent->CreateFromVmo(vmofile, name, vmo, off, len);
}
void Vfs::MountSubtree(VnodeDir* parent, fbl::RefPtr<VnodeDir> subtree) {
fbl::AutoLock lock(&vfs_lock_);
parent->MountSubtree(fbl::move(subtree));
}
fbl::atomic<uint64_t> VnodeMemfs::ino_ctr_(0);
VnodeMemfs::VnodeMemfs(Vfs* vfs) : dnode_(nullptr), link_count_(0), vfs_(vfs),
ino_(ino_ctr_.fetch_add(1, fbl::memory_order_relaxed)) {
create_time_ = modify_time_ = zx_clock_get(ZX_CLOCK_UTC);
}
VnodeMemfs::~VnodeMemfs() {}
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);
}
constexpr const char kFsName[] = "memfs";
zx_status_t VnodeMemfs::Ioctl(uint32_t op, const void* in_buf, size_t in_len,
void* out_buf, size_t out_len, size_t* out_actual) {
switch (op) {
case IOCTL_VFS_QUERY_FS: {
if (out_len < sizeof(vfs_query_info_t) + strlen(kFsName)) {
return ZX_ERR_INVALID_ARGS;
}
vfs_query_info_t* info = static_cast<vfs_query_info_t*>(out_buf);
memset(info, 0, sizeof(*info));
//TODO(planders): eventually report something besides 0.
memcpy(info->name, kFsName, strlen(kFsName));
*out_actual = sizeof(vfs_query_info_t) + strlen(kFsName);
return ZX_OK;
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t VnodeMemfs::AttachRemote(fs::MountChannel h) {
if (!IsDirectory()) {
return ZX_ERR_NOT_DIR;
} else if (IsRemote()) {
return ZX_ERR_ALREADY_BOUND;
}
SetRemote(fbl::move(h.TakeChannel()));
return ZX_OK;
}
zx_status_t createFilesystem(const char* name, memfs::Vfs* vfs, fbl::RefPtr<VnodeDir>* out) {
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
struct memfs_filesystem {
memfs::Vfs vfs;
};
zx_status_t memfs_create_filesystem(async_t* async, memfs_filesystem_t** fs_out,
zx_handle_t* root_out) {
ZX_DEBUG_ASSERT(async != nullptr);
ZX_DEBUG_ASSERT(fs_out != nullptr);
ZX_DEBUG_ASSERT(root_out != nullptr);
zx::channel client, server;
zx_status_t status = zx::channel::create(0, &client, &server);
if (status != ZX_OK) {
return status;
}
fbl::unique_ptr<memfs_filesystem_t> fs = fbl::make_unique<memfs_filesystem_t>();
fs->vfs.set_async(async);
fbl::RefPtr<memfs::VnodeDir> root;
if ((status = memfs::createFilesystem("<tmp>", &fs->vfs, &root)) != ZX_OK) {
return status;
}
if ((status = fs->vfs.ServeDirectory(fbl::move(root), fbl::move(server))) != ZX_OK) {
return status;
}
*fs_out = fs.release();
*root_out = client.release();
return ZX_OK;
}
zx_status_t memfs_free_filesystem(memfs_filesystem_t* fs, zx_duration_t timeout) {
ZX_DEBUG_ASSERT(fs != nullptr);
zx_status_t status = fs->vfs.UninstallAll(zx_deadline_after(timeout));
delete fs;
return status;
}