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