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