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