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 #pragma once 6 7 #include <threads.h> 8 9 #include <zircon/compiler.h> 10 #include <zircon/types.h> 11 #include <lib/fdio/io.h> 12 #include <lib/fdio/vfs.h> 13 14 #ifdef __cplusplus 15 16 #include <fbl/intrusive_double_list.h> 17 #include <fbl/ref_ptr.h> 18 #include <fbl/unique_ptr.h> 19 #include <fs/managed-vfs.h> 20 #include <fs/remote.h> 21 #include <fs/vfs.h> 22 #include <fs/vnode.h> 23 #include <fs/watcher.h> 24 #include <lib/zx/vmo.h> 25 26 #include <atomic> 27 28 namespace memfs { 29 30 constexpr uint64_t kMemfsBlksize = PAGE_SIZE; 31 32 class Dnode; 33 class Vfs; 34 35 class VnodeMemfs : public fs::Vnode { 36 public: 37 virtual zx_status_t Setattr(const vnattr_t* a) final; 38 virtual void Sync(SyncCallback closure) final; 39 zx_status_t AttachRemote(fs::MountChannel h) final; 40 41 // To be more specific: Is this vnode connected into the directory hierarchy? 42 // VnodeDirs can be unlinked, and this method will subsequently return false. IsDirectory()43 bool IsDirectory() const { return dnode_ != nullptr; } UpdateModified()44 void UpdateModified() { modify_time_ = zx_clock_get(ZX_CLOCK_UTC); } 45 46 ~VnodeMemfs() override; 47 vfs()48 Vfs* vfs() const { return vfs_; } ino()49 uint64_t ino() const { return ino_; } 50 51 fbl::RefPtr<Dnode> dnode_; 52 uint32_t link_count_; 53 54 protected: 55 explicit VnodeMemfs(Vfs* vfs); 56 57 Vfs* vfs_; 58 uint64_t ino_; 59 uint64_t create_time_; 60 uint64_t modify_time_; 61 GetInoCounter()62 uint64_t GetInoCounter() const { 63 return ino_ctr_.load(std::memory_order_relaxed); 64 } 65 GetDeletedInoCounter()66 uint64_t GetDeletedInoCounter() const { 67 return deleted_ino_ctr_.load(std::memory_order_relaxed); 68 } 69 70 private: 71 static std::atomic<uint64_t> ino_ctr_; 72 static std::atomic<uint64_t> deleted_ino_ctr_; 73 }; 74 75 class VnodeFile final : public VnodeMemfs { 76 public: 77 explicit VnodeFile(Vfs* vfs); 78 ~VnodeFile() override; 79 80 virtual zx_status_t ValidateFlags(uint32_t flags) final; 81 82 private: 83 zx_status_t Read(void* data, size_t len, size_t off, size_t* out_actual) final; 84 zx_status_t Write(const void* data, size_t len, size_t offset, 85 size_t* out_actual) final; 86 zx_status_t Append(const void* data, size_t len, size_t* out_end, 87 size_t* out_actual) final; 88 zx_status_t Truncate(size_t len) final; 89 zx_status_t Getattr(vnattr_t* a) final; 90 zx_status_t GetHandles(uint32_t flags, fuchsia_io_NodeInfo* info) final; 91 zx_status_t GetVmo(int flags, zx_handle_t* out) final; 92 93 // Ensure the underlying vmo is filled with zero from: 94 // [start, round_up(end, PAGE_SIZE)). 95 void ZeroTail(size_t start, size_t end); 96 97 zx::vmo vmo_; 98 // Cached length of the vmo. 99 uint64_t vmo_size_; 100 // Logical length of the underlying file. 101 zx_off_t length_; 102 }; 103 104 class VnodeDir final : public VnodeMemfs { 105 public: 106 explicit VnodeDir(Vfs* vfs); 107 ~VnodeDir() override; 108 109 zx_status_t ValidateFlags(uint32_t flags) final; 110 zx_status_t Lookup(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name) final; 111 zx_status_t Create(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name, uint32_t mode) final; 112 113 // Create a vnode from a VMO. 114 // Fails if the vnode already exists. 115 // Passes the vmo to the Vnode; does not duplicate it. 116 zx_status_t CreateFromVmo(fbl::StringPiece name, zx_handle_t vmo, 117 zx_off_t off, zx_off_t len); 118 119 // Mount a subtree as a child of this directory. 120 void MountSubtree(fbl::RefPtr<VnodeDir> subtree); 121 122 // Use the watcher container to implement a directory watcher 123 void Notify(fbl::StringPiece name, unsigned event) final; 124 zx_status_t WatchDir(fs::Vfs* vfs, uint32_t mask, uint32_t options, zx::channel watcher) final; 125 zx_status_t QueryFilesystem(fuchsia_io_FilesystemInfo* out) final; 126 127 // The vnode is acting as a mount point for a remote filesystem or device. 128 bool IsRemote() const final; 129 zx::channel DetachRemote() final; 130 zx_handle_t GetRemote() const final; 131 void SetRemote(zx::channel remote) final; 132 133 private: 134 zx_status_t Readdir(fs::vdircookie_t* cookie, void* dirents, size_t len, 135 size_t* out_actual) final; 136 137 // Resolves the question, "Can this directory create a child node with the name?" 138 // Returns "ZX_OK" on success; otherwise explains failure with error message. 139 zx_status_t CanCreate(fbl::StringPiece name) const; 140 141 // Creates a dnode for the Vnode, attaches vnode to dnode, (if directory) attaches 142 // dnode to vnode, and adds dnode to parent directory. 143 zx_status_t AttachVnode(fbl::RefPtr<memfs::VnodeMemfs> vn, fbl::StringPiece name, 144 bool isdir); 145 146 zx_status_t Unlink(fbl::StringPiece name, bool must_be_dir) final; 147 zx_status_t Rename(fbl::RefPtr<fs::Vnode> newdir, fbl::StringPiece oldname, 148 fbl::StringPiece newname, bool src_must_be_dir, 149 bool dst_must_be_dir) final; 150 zx_status_t Link(fbl::StringPiece name, fbl::RefPtr<fs::Vnode> target) final; 151 zx_status_t Getattr(vnattr_t* a) final; 152 zx_status_t GetHandles(uint32_t flags, fuchsia_io_NodeInfo* info) final; 153 zx_status_t GetVmo(int flags, zx_handle_t* out) final; 154 155 fs::RemoteContainer remoter_; 156 fs::WatcherContainer watcher_; 157 }; 158 159 class VnodeVmo final : public VnodeMemfs { 160 public: 161 VnodeVmo(Vfs* vfs, zx_handle_t vmo, zx_off_t offset, zx_off_t length); 162 ~VnodeVmo() override; 163 164 virtual zx_status_t ValidateFlags(uint32_t flags) override; 165 166 private: 167 zx_status_t Read(void* data, size_t len, size_t off, size_t* out_actual) final; 168 zx_status_t Getattr(vnattr_t* a) final; 169 zx_status_t GetHandles(uint32_t flags, fuchsia_io_NodeInfo* info) final; 170 171 zx_handle_t vmo_; 172 zx_off_t offset_; 173 zx_off_t length_; 174 bool have_local_clone_; 175 }; 176 177 class Vfs : public fs::ManagedVfs { 178 public: 179 // Creates a Vfs with practically unlimited pages upper bound. Vfs()180 Vfs(): fs::ManagedVfs(), pages_limit_(UINT64_MAX), num_allocated_pages_(0) { } 181 182 // Creates a Vfs with the maximum |pages_limit| number of pages. Vfs(size_t pages_limit)183 explicit Vfs(size_t pages_limit): 184 fs::ManagedVfs(), pages_limit_(pages_limit), num_allocated_pages_(0) { } 185 186 // Creates a VnodeVmo under |parent| with |name| which is backed by |vmo|. 187 // N.B. The VMO will not be taken into account when calculating 188 // number of allocated pages in this Vfs. 189 zx_status_t CreateFromVmo(VnodeDir* parent, fbl::StringPiece name, 190 zx_handle_t vmo, zx_off_t off, 191 zx_off_t len); 192 193 void MountSubtree(VnodeDir* parent, fbl::RefPtr<VnodeDir> subtree); 194 PagesLimit()195 size_t PagesLimit() const { return pages_limit_; } 196 NumAllocatedPages()197 size_t NumAllocatedPages() const { return num_allocated_pages_; } 198 GetFsId()199 uint64_t GetFsId() const { return fs_id_; } 200 201 private: 202 // Initialize fs_id_ on the first call. 203 // Calling more than once is a no-op. 204 zx_status_t FillFsId(); 205 206 friend zx_status_t CreateFilesystem(const char* name, memfs::Vfs* vfs, 207 fbl::RefPtr<VnodeDir>* out); 208 209 // Allows VnodeFile (and no other class) to manipulate number of allocated pages 210 // using GrowVMO and WillFreeVMO. 211 friend VnodeFile; 212 213 // Increases the size of the |vmo| to at least |request_size| bytes. 214 // If the VMO is invalid, it will try to create it. 215 // |current_size| is the current size of the VMO in number of bytes. It should be 216 // a multiple of page size. The new size of the VMO is returned via |actual_size|. 217 // If the new size would cause us to exceed the limit on number of pages or if the system 218 // ran out of memory, an error is returned. 219 zx_status_t GrowVMO(zx::vmo& vmo, size_t current_size, 220 size_t request_size, size_t* actual_size); 221 222 // VnodeFile must call this function in the destructor to signal that its VMO will be freed. 223 // |vmo_size| is the size of the owned vmo in bytes. It should be a multiple of page size. 224 void WillFreeVMO(size_t vmo_size); 225 226 // Maximum number of pages available; fixed at Vfs creation time. 227 // Puts a bound on maximum memory usage. 228 const size_t pages_limit_; 229 230 // Number of pages currently in use by VnodeFiles. 231 size_t num_allocated_pages_; 232 233 uint64_t fs_id_ = 0; 234 }; 235 236 // Initializes the Vfs object and names the root directory |name|. The Vfs object is considered 237 // invalid prior to this call. Returns the root VnodeDir via |out|. 238 zx_status_t CreateFilesystem(const char* name, memfs::Vfs* vfs, fbl::RefPtr<VnodeDir>* out); 239 240 } // namespace memfs 241 242 #endif // ifdef __cplusplus 243