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