| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_MEMFS_CPP_VNODE_H_ |
| #define LIB_MEMFS_CPP_VNODE_H_ |
| |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/vfs.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #include <atomic> |
| |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/ref_ptr.h> |
| #include <fs/managed_vfs.h> |
| #include <fs/remote.h> |
| #include <fs/vfs.h> |
| #include <fs/vfs_types.h> |
| #include <fs/vnode.h> |
| #include <fs/watcher.h> |
| |
| namespace memfs { |
| |
| constexpr uint64_t kMemfsBlksize = PAGE_SIZE; |
| |
| class Dnode; |
| class Vfs; |
| |
| class VnodeMemfs : public fs::Vnode { |
| public: |
| virtual zx_status_t SetAttributes(fs::VnodeAttributesUpdate a) final; |
| virtual void Sync(SyncCallback closure) final; |
| zx_status_t AttachRemote(fs::MountChannel h) final; |
| |
| // To be more specific: Is this vnode connected into the directory hierarchy? |
| // VnodeDirs can be unlinked, and this method will subsequently return false. |
| bool IsDirectory() const { return dnode_ != nullptr; } |
| void UpdateModified() { |
| zx_time_t now = 0; |
| zx_clock_get(ZX_CLOCK_UTC, &now); |
| modify_time_ = now; |
| } |
| |
| ~VnodeMemfs() override; |
| |
| Vfs* vfs() const { return vfs_; } |
| uint64_t ino() const { return ino_; } |
| |
| // TODO(smklein): Move member into the VnodeDir subclass. |
| // Directories contain a raw reference to their location in the filesystem hierarchy. |
| // Although this would have safer memory semantics with an actual weak pointer, it is |
| // currently raw to avoid circular dependencies from Vnode -> Dnode -> Vnode. |
| // |
| // Caution must be taken when detaching Dnodes from their parents to avoid leaving |
| // this reference dangling. |
| Dnode* dnode_; |
| uint32_t link_count_; |
| |
| protected: |
| explicit VnodeMemfs(Vfs* vfs); |
| |
| Vfs* vfs_; |
| uint64_t ino_; |
| uint64_t create_time_; |
| uint64_t modify_time_; |
| |
| uint64_t GetInoCounter() const { return ino_ctr_.load(std::memory_order_relaxed); } |
| |
| uint64_t GetDeletedInoCounter() const { return deleted_ino_ctr_.load(std::memory_order_relaxed); } |
| |
| private: |
| static std::atomic<uint64_t> ino_ctr_; |
| static std::atomic<uint64_t> deleted_ino_ctr_; |
| }; |
| |
| class VnodeFile final : public VnodeMemfs { |
| public: |
| explicit VnodeFile(Vfs* vfs); |
| ~VnodeFile() override; |
| |
| fs::VnodeProtocolSet GetProtocols() const final; |
| |
| private: |
| zx_status_t Read(void* data, size_t len, size_t off, size_t* out_actual) final; |
| zx_status_t Write(const void* data, size_t len, size_t offset, size_t* out_actual) final; |
| zx_status_t Append(const void* data, size_t len, size_t* out_end, size_t* out_actual) final; |
| zx_status_t Truncate(size_t len) final; |
| zx_status_t GetAttributes(fs::VnodeAttributes* a) final; |
| zx_status_t GetNodeInfoForProtocol(fs::VnodeProtocol protocol, fs::Rights rights, |
| fs::VnodeRepresentation* info) final; |
| zx_status_t GetVmo(int flags, zx::vmo* out_vmo, size_t* out_size) final; |
| |
| // Ensure the underlying vmo is filled with zero from: |
| // [start, round_up(end, PAGE_SIZE)). |
| void ZeroTail(size_t start, size_t end); |
| |
| zx::vmo vmo_; |
| // Cached length of the vmo. |
| uint64_t vmo_size_; |
| // Logical length of the underlying file. |
| zx_off_t length_; |
| }; |
| |
| class VnodeDir final : public VnodeMemfs { |
| public: |
| explicit VnodeDir(Vfs* vfs); |
| ~VnodeDir() override; |
| |
| fs::VnodeProtocolSet GetProtocols() const final; |
| |
| zx_status_t Lookup(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name) final; |
| zx_status_t Create(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name, uint32_t mode) final; |
| |
| // Create a vnode from a VMO. |
| // Fails if the vnode already exists. |
| // Passes the vmo to the Vnode; does not duplicate it. |
| zx_status_t CreateFromVmo(fbl::StringPiece name, zx_handle_t vmo, zx_off_t off, zx_off_t len); |
| |
| // Use the watcher container to implement a directory watcher |
| void Notify(fbl::StringPiece name, unsigned event) final; |
| zx_status_t WatchDir(fs::Vfs* vfs, uint32_t mask, uint32_t options, zx::channel watcher) final; |
| zx_status_t QueryFilesystem(::llcpp::fuchsia::io::FilesystemInfo* out) final; |
| |
| // The vnode is acting as a mount point for a remote filesystem or device. |
| bool IsRemote() const final; |
| zx::channel DetachRemote() final; |
| zx_handle_t GetRemote() const final; |
| void SetRemote(zx::channel remote) final; |
| |
| private: |
| zx_status_t Readdir(fs::vdircookie_t* cookie, void* dirents, size_t len, |
| size_t* out_actual) final; |
| |
| // Resolves the question, "Can this directory create a child node with the name?" |
| // Returns "ZX_OK" on success; otherwise explains failure with error message. |
| zx_status_t CanCreate(fbl::StringPiece name) const; |
| |
| // Creates a dnode for the Vnode, attaches vnode to dnode, (if directory) attaches |
| // dnode to vnode, and adds dnode to parent directory. |
| zx_status_t AttachVnode(fbl::RefPtr<memfs::VnodeMemfs> vn, fbl::StringPiece name, bool isdir); |
| |
| zx_status_t Unlink(fbl::StringPiece name, bool must_be_dir) final; |
| zx_status_t Rename(fbl::RefPtr<fs::Vnode> newdir, fbl::StringPiece oldname, |
| fbl::StringPiece newname, bool src_must_be_dir, bool dst_must_be_dir) final; |
| zx_status_t Link(fbl::StringPiece name, fbl::RefPtr<fs::Vnode> target) final; |
| zx_status_t GetAttributes(fs::VnodeAttributes* a) final; |
| zx_status_t GetNodeInfoForProtocol(fs::VnodeProtocol protocol, fs::Rights rights, |
| fs::VnodeRepresentation* info) final; |
| zx_status_t GetVmo(int flags, zx::vmo* out_vmo, size_t* out_size) final; |
| |
| fs::RemoteContainer remoter_; |
| fs::WatcherContainer watcher_; |
| }; |
| |
| class VnodeVmo final : public VnodeMemfs { |
| public: |
| VnodeVmo(Vfs* vfs, zx_handle_t vmo, zx_off_t offset, zx_off_t length); |
| ~VnodeVmo() override; |
| |
| fs::VnodeProtocolSet GetProtocols() const final; |
| bool ValidateRights(fs::Rights rights) final; |
| |
| private: |
| zx_status_t Read(void* data, size_t len, size_t off, size_t* out_actual) final; |
| zx_status_t GetAttributes(fs::VnodeAttributes* a) final; |
| zx_status_t GetNodeInfoForProtocol(fs::VnodeProtocol protocol, fs::Rights rights, |
| fs::VnodeRepresentation* info) final; |
| zx_status_t GetVmo(int flags, zx::vmo* out_vmo, size_t* out_size) final; |
| zx_status_t MakeLocalClone(bool executable); |
| |
| zx_handle_t vmo_; |
| zx_off_t offset_; |
| zx_off_t length_; |
| bool have_local_clone_; |
| }; |
| |
| class Vfs : public fs::ManagedVfs { |
| public: |
| static zx_status_t Create(const char* fs_name, size_t pages_limit, std::unique_ptr<Vfs>* out_vfs, |
| fbl::RefPtr<VnodeDir>* out_root); |
| |
| ~Vfs(); |
| |
| // Creates a VnodeVmo under |parent| with |name| which is backed by |vmo|. |
| // N.B. The VMO will not be taken into account when calculating |
| // number of allocated pages in this Vfs. |
| zx_status_t CreateFromVmo(VnodeDir* parent, fbl::StringPiece name, zx_handle_t vmo, zx_off_t off, |
| zx_off_t len); |
| |
| size_t PagesLimit() const { return pages_limit_; } |
| |
| size_t NumAllocatedPages() const { return num_allocated_pages_; } |
| |
| uint64_t GetFsId() const { return fs_id_; } |
| |
| // Increases the size of the |vmo| to at least |request_size| bytes. |
| // If the VMO is invalid, it will try to create it. |
| // |current_size| is the current size of the VMO in number of bytes. It should be |
| // a multiple of page size. The new size of the VMO is returned via |actual_size|. |
| // If the new size would cause us to exceed the limit on number of pages or if the system |
| // ran out of memory, an error is returned. |
| zx_status_t GrowVMO(zx::vmo& vmo, size_t current_size, size_t request_size, size_t* actual_size); |
| |
| // VnodeFile must call this function in the destructor to signal that its VMO will be freed. |
| // |vmo_size| is the size of the owned vmo in bytes. It should be a multiple of page size. |
| void WillFreeVMO(size_t vmo_size); |
| |
| private: |
| // Creates a Vfs with the maximum |pages_limit| number of pages. |
| explicit Vfs(uint64_t id, size_t pages_limit, const char* name); |
| |
| uint64_t fs_id_ = 0; |
| |
| // Maximum number of pages available; fixed at Vfs creation time. |
| // Puts a bound on maximum memory usage. |
| const size_t pages_limit_ = UINT64_MAX; |
| |
| // Number of pages currently in use by VnodeFiles. |
| size_t num_allocated_pages_ = 0; |
| |
| // Since no directory contains the root, it is owned by the VFS object. |
| std::unique_ptr<Dnode> root_; |
| }; |
| |
| } // namespace memfs |
| |
| #endif // LIB_MEMFS_CPP_VNODE_H_ |