1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <inttypes.h> 6#include <fcntl.h> 7#include <limits.h> 8#include <stdlib.h> 9#include <string.h> 10#include <sys/stat.h> 11 12#include <fbl/algorithm.h> 13#include <fbl/alloc_checker.h> 14#include <fbl/atomic.h> 15#include <fbl/auto_lock.h> 16#include <fbl/ref_ptr.h> 17#include <fbl/unique_ptr.h> 18#include <lib/fdio/namespace.h> 19#include <lib/fdio/vfs.h> 20#include <fs/vfs.h> 21#include <lib/memfs/cpp/vnode.h> 22#include <lib/memfs/memfs.h> 23#include <lib/sync/completion.h> 24#include <zircon/device/vfs.h> 25 26#include "dnode.h" 27 28namespace { 29 30constexpr size_t kPageSize = static_cast<size_t>(PAGE_SIZE); 31 32} 33 34namespace memfs { 35 36zx_status_t Vfs::CreateFromVmo(VnodeDir* parent, fbl::StringPiece name, 37 zx_handle_t vmo, zx_off_t off, 38 zx_off_t len) { 39 fbl::AutoLock lock(&vfs_lock_); 40 return parent->CreateFromVmo(name, vmo, off, len); 41} 42 43void Vfs::MountSubtree(VnodeDir* parent, fbl::RefPtr<VnodeDir> subtree) { 44 fbl::AutoLock lock(&vfs_lock_); 45 parent->MountSubtree(fbl::move(subtree)); 46} 47 48zx_status_t Vfs::FillFsId() { 49 if (fs_id_) { 50 return ZX_OK; 51 } 52 zx::event event; 53 zx_status_t status = zx::event::create(0, &event); 54 if (status != ZX_OK) { 55 return status; 56 } 57 zx_info_handle_basic_t info; 58 status = event.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr); 59 if (status != ZX_OK) { 60 return status; 61 } 62 63 fs_id_ = info.koid; 64 return ZX_OK; 65} 66 67zx_status_t Vfs::GrowVMO(zx::vmo& vmo, size_t current_size, 68 size_t request_size, size_t* actual_size) { 69 if (request_size <= current_size) { 70 *actual_size = current_size; 71 return ZX_OK; 72 } 73 size_t aligned_len = fbl::round_up(request_size, kPageSize); 74 ZX_DEBUG_ASSERT(current_size % kPageSize == 0); 75 size_t num_new_pages = (aligned_len - current_size) / kPageSize; 76 if (num_new_pages + num_allocated_pages_ > pages_limit_) { 77 *actual_size = current_size; 78 return ZX_ERR_NO_SPACE; 79 } 80 zx_status_t status; 81 if (!vmo.is_valid()) { 82 if ((status = zx::vmo::create(aligned_len, 0, &vmo)) != ZX_OK) { 83 return status; 84 } 85 } else { 86 if ((status = vmo.set_size(aligned_len)) != ZX_OK) { 87 return status; 88 } 89 } 90 // vmo operation succeeded 91 num_allocated_pages_ += num_new_pages; 92 *actual_size = aligned_len; 93 return ZX_OK; 94} 95 96void Vfs::WillFreeVMO(size_t vmo_size) { 97 ZX_DEBUG_ASSERT(vmo_size % kPageSize == 0); 98 size_t freed_pages = vmo_size / kPageSize; 99 ZX_DEBUG_ASSERT(freed_pages <= num_allocated_pages_); 100 num_allocated_pages_ -= freed_pages; 101} 102 103fbl::atomic<uint64_t> VnodeMemfs::ino_ctr_(0); 104fbl::atomic<uint64_t> VnodeMemfs::deleted_ino_ctr_(0); 105 106VnodeMemfs::VnodeMemfs(Vfs* vfs) : dnode_(nullptr), link_count_(0), vfs_(vfs), 107 ino_(ino_ctr_.fetch_add(1, fbl::memory_order_relaxed)) { 108 create_time_ = modify_time_ = zx_clock_get(ZX_CLOCK_UTC); 109} 110 111VnodeMemfs::~VnodeMemfs() { 112 deleted_ino_ctr_.fetch_add(1, fbl::memory_order_relaxed); 113} 114 115zx_status_t VnodeMemfs::Setattr(const vnattr_t* attr) { 116 if ((attr->valid & ~(ATTR_MTIME)) != 0) { 117 // only attr currently supported 118 return ZX_ERR_INVALID_ARGS; 119 } 120 if (attr->valid & ATTR_MTIME) { 121 modify_time_ = attr->modify_time; 122 } 123 return ZX_OK; 124} 125 126void VnodeMemfs::Sync(SyncCallback closure) { 127 // Since this filesystem is in-memory, all data is already up-to-date in 128 // the underlying storage 129 closure(ZX_OK); 130} 131 132zx_status_t VnodeMemfs::AttachRemote(fs::MountChannel h) { 133 if (!IsDirectory()) { 134 return ZX_ERR_NOT_DIR; 135 } else if (IsRemote()) { 136 return ZX_ERR_ALREADY_BOUND; 137 } 138 SetRemote(fbl::move(h.TakeChannel())); 139 return ZX_OK; 140} 141 142zx_status_t CreateFilesystem(const char* name, memfs::Vfs* vfs, fbl::RefPtr<VnodeDir>* out) { 143 zx_status_t status; 144 if ((status = vfs->FillFsId()) != ZX_OK) { 145 return status; 146 } 147 fbl::AllocChecker ac; 148 fbl::RefPtr<VnodeDir> fs = fbl::AdoptRef(new (&ac) VnodeDir(vfs)); 149 if (!ac.check()) { 150 return ZX_ERR_NO_MEMORY; 151 } 152 153 fbl::RefPtr<Dnode> dn = Dnode::Create(name, fs); 154 if (dn == nullptr) { 155 return ZX_ERR_NO_MEMORY; 156 } 157 158 fs->dnode_ = dn; // FS root is directory 159 *out = fs; 160 return ZX_OK; 161} 162 163} // namespace memfs 164