// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dnode.h" namespace { constexpr size_t kPageSize = static_cast(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 subtree) { fbl::AutoLock lock(&vfs_lock_); parent->MountSubtree(fbl::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; } fbl::atomic VnodeMemfs::ino_ctr_(0); fbl::atomic VnodeMemfs::deleted_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() { deleted_ino_ctr_.fetch_add(1, fbl::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(fbl::move(h.TakeChannel())); return ZX_OK; } zx_status_t CreateFilesystem(const char* name, memfs::Vfs* vfs, fbl::RefPtr* out) { zx_status_t status; if ((status = vfs->FillFsId()) != ZX_OK) { return status; } fbl::AllocChecker ac; fbl::RefPtr fs = fbl::AdoptRef(new (&ac) VnodeDir(vfs)); if (!ac.check()) { return ZX_ERR_NO_MEMORY; } fbl::RefPtr 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