| // 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 FS_VNODE_H_ |
| #define FS_VNODE_H_ |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include <fbl/function.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/intrusive_single_list.h> |
| #include <fbl/macros.h> |
| #include <fbl/ref_counted_internal.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/string_piece.h> |
| #include <fs/mount_channel.h> |
| #include <fs/ref_counted.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/vfs.h> |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #include <utility> |
| |
| #ifdef __Fuchsia__ |
| #include <fuchsia/io/c/fidl.h> |
| #include <lib/zx/channel.h> |
| #include <zircon/device/vfs.h> |
| #endif // __Fuchsia__ |
| |
| namespace fs { |
| |
| class Vfs; |
| typedef struct vdircookie vdircookie_t; |
| |
| inline bool vfs_valid_name(fbl::StringPiece name) { |
| return name.length() <= NAME_MAX && |
| memchr(name.data(), '/', name.length()) == nullptr && |
| name != "." && name != ".."; |
| } |
| |
| // The VFS interface declares a default abstract Vnode class with |
| // common operations that may be overwritten. |
| // |
| // The ops are used for dispatch and the lifecycle of Vnodes are owned |
| // by RefPtrs. |
| // |
| // All names passed to the Vnode class are valid according to "vfs_valid_name". |
| // |
| // The lower half of flags (VFS_FLAG_RESERVED_MASK) is reserved |
| // for usage by fs::Vnode, but the upper half of flags may |
| // be used by subclasses of Vnode. |
| class Vnode : public VnodeRefCounted<Vnode>, public fbl::Recyclable<Vnode> { |
| public: |
| virtual ~Vnode(); |
| virtual void fbl_recycle() { delete this; } |
| |
| // Ensures that it is valid to access the vnode with given flags. |
| virtual zx_status_t ValidateFlags(uint32_t flags); |
| |
| // Provides an opportunity to redirect subsequent I/O operations to a |
| // different vnode. |
| // |
| // Flags will have already been validated by "ValidateFlags". |
| // Open should never be invoked if flags includes "O_PATH". |
| // |
| // If the implementation of |Open()| sets |out_redirect| to a non-null value. |
| // all following I/O operations on the opened file will be redirected to the |
| // indicated vnode instead of being handled by this instance. |
| // |
| // |flags| are the open flags to be validated, such as |ZX_FS_RIGHT_READABLE| and |
| // |ZX_FS_FLAG_DIRECTORY|. |
| virtual zx_status_t Open(uint32_t flags, fbl::RefPtr<Vnode>* out_redirect); |
| |
| // METHODS FOR OPENED NODES |
| // |
| // The following operations will not be invoked unless the Vnode has |
| // been "Open()"-ed successfully. |
| // |
| // For files opened with O_PATH (as a file descriptor only) the base |
| // classes' implementation of some of these functions may be invoked anyway. |
| |
| #ifdef __Fuchsia__ |
| // Serves a connection to the Vnode over the specified channel. |
| // |
| // The default implementation creates and registers a FIDL |Connection| with the VFS. |
| // Subclasses may override this behavior to serve custom protocols over the channel. |
| // |
| // |vfs| is the VFS which manages the Vnode. |
| // |channel| is the channel over which the client will exchange messages with the Vnode. |
| // |flags| are the flags and rights which were previously provided to |Open()|. |
| virtual zx_status_t Serve(fs::Vfs* vfs, zx::channel channel, uint32_t flags); |
| |
| // Extract handle, type, and extra info from a vnode. |
| virtual zx_status_t GetNodeInfo(uint32_t flags, fuchsia_io_NodeInfo* info) = 0; |
| |
| virtual zx_status_t WatchDir(Vfs* vfs, uint32_t mask, uint32_t options, zx::channel watcher); |
| #endif |
| |
| // Closes vn. Will be called once for each successful Open(). |
| // |
| // Typically, most Vnodes simply return "ZX_OK". |
| virtual zx_status_t Close(); |
| |
| // Read data from vn at offset. |
| // |
| // If successful, returns the number of bytes read in |out_actual|. This must be |
| // less than or equal to |len|. |
| virtual zx_status_t Read(void* data, size_t len, size_t off, size_t* out_actual); |
| |
| // Write |len| bytes of |data| to the file, starting at |offset|. |
| // |
| // If successful, returns the number of bytes written in |out_actual|. This must be |
| // less than or equal to |len|. |
| virtual zx_status_t Write(const void* data, size_t len, size_t offset, |
| size_t* out_actual); |
| |
| // Write |len| bytes of |data| to the end of the file. |
| // |
| // If successful, returns the number of bytes written in |out_actual|, and |
| // returns the new end of file offset in |out_end|. |
| virtual zx_status_t Append(const void* data, size_t len, size_t* out_end, |
| size_t* out_actual); |
| |
| // Change the size of vn |
| virtual zx_status_t Truncate(size_t len); |
| |
| // Set attributes of vn. |
| virtual zx_status_t Setattr(const vnattr_t* a); |
| |
| // Acquire a vmo from a vnode. |
| // |
| // At the moment, mmap can only map files from read-only filesystems, |
| // since (without paging) there is no mechanism to update either |
| // 1) The file by writing to the mapping, or |
| // 2) The mapping by writing to the underlying file. |
| virtual zx_status_t GetVmo(int flags, zx_handle_t* out_vmo, size_t* out_size); |
| |
| // Syncs the vnode with its underlying storage. |
| // |
| // Returns the result status through a closure. |
| using SyncCallback = fbl::Function<void(zx_status_t status)>; |
| virtual void Sync(SyncCallback closure); |
| |
| // Read directory entries of vn, error if not a directory. |
| // FS-specific Cookie must be a buffer of vdircookie_t size or smaller. |
| // Cookie must be zero'd before first call and will be used by |
| // the readdir implementation to maintain state across calls. |
| // To "rewind" and start from the beginning, cookie may be zero'd. |
| virtual zx_status_t Readdir(vdircookie_t* cookie, void* dirents, size_t len, |
| size_t* out_actual); |
| |
| // METHODS FOR OPENED OR UNOPENED NODES |
| // |
| // The following operations may be invoked on a Vnode, even if it has |
| // not been "Open()"-ed. |
| |
| // Attempt to find child of vn, child returned on success. |
| // Name is len bytes long, and does not include a null terminator. |
| virtual zx_status_t Lookup(fbl::RefPtr<Vnode>* out, fbl::StringPiece name); |
| |
| // Read attributes of vn. |
| virtual zx_status_t Getattr(vnattr_t* a); |
| |
| // Create a new node under vn. |
| // Name is len bytes long, and does not include a null terminator. |
| // Mode specifies the type of entity to create. |
| virtual zx_status_t Create(fbl::RefPtr<Vnode>* out, fbl::StringPiece name, uint32_t mode); |
| |
| // Removes name from directory vn |
| virtual zx_status_t Unlink(fbl::StringPiece name, bool must_be_dir); |
| |
| // Renames the path at oldname in olddir to the path at newname in newdir. |
| // Called on the "olddir" vnode. |
| // Unlinks any prior newname if it already exists. |
| virtual zx_status_t Rename(fbl::RefPtr<Vnode> newdir, |
| fbl::StringPiece oldname, fbl::StringPiece newname, |
| bool src_must_be_dir, bool dst_must_be_dir); |
| |
| // Creates a hard link to the 'target' vnode with a provided name in vndir |
| virtual zx_status_t Link(fbl::StringPiece name, fbl::RefPtr<Vnode> target); |
| |
| // Invoked by the VFS layer whenever files are added or removed. |
| virtual void Notify(fbl::StringPiece name, unsigned event); |
| |
| #ifdef __Fuchsia__ |
| // Return information about the underlying filesystem, if desired. |
| virtual zx_status_t QueryFilesystem(fuchsia_io_FilesystemInfo* out); |
| |
| // Returns the name of the device backing the filesystem, if one exists. |
| virtual zx_status_t GetDevicePath(size_t buffer_len, char* out_name, size_t* out_len); |
| |
| // Attaches a handle to the vnode, if possible. Otherwise, returns an error. |
| virtual zx_status_t AttachRemote(MountChannel h); |
| |
| // The following methods are required to mount sub-filesystems. The logic |
| // (and storage) necessary to implement these functions exists within the |
| // "RemoteContainer" class, which may be composed inside Vnodes that wish |
| // to act as mount points. |
| |
| // The vnode is acting as a mount point for a remote filesystem or device. |
| virtual bool IsRemote() const; |
| virtual zx::channel DetachRemote(); |
| virtual zx_handle_t GetRemote() const; |
| virtual void SetRemote(zx::channel remote); |
| #endif |
| |
| // Return if the Vnode is a directory; as a guideline, a directory Vnode should |
| // support |Lookup| and opening a child Vnode. |
| virtual bool IsDirectory() const = 0; |
| |
| protected: |
| DISALLOW_COPY_ASSIGN_AND_MOVE(Vnode); |
| Vnode(); |
| }; |
| |
| // Opens a vnode by reference. |
| // The |vnode| reference is updated in-place if redirection occurs. |
| inline zx_status_t OpenVnode(uint32_t flags, fbl::RefPtr<Vnode>* vnode) { |
| fbl::RefPtr<Vnode> redirect; |
| zx_status_t status = (*vnode)->Open(flags, &redirect); |
| if (status == ZX_OK && redirect != nullptr) { |
| *vnode = std::move(redirect); |
| } |
| return status; |
| } |
| |
| // Helper class used to fill direntries during calls to Readdir. |
| class DirentFiller { |
| public: |
| DISALLOW_COPY_ASSIGN_AND_MOVE(DirentFiller); |
| |
| DirentFiller(void* ptr, size_t len); |
| |
| // Attempts to add the name to the end of the dirent buffer |
| // which is returned by readdir. |
| zx_status_t Next(fbl::StringPiece name, uint8_t type, uint64_t ino); |
| |
| zx_status_t BytesFilled() const { |
| return static_cast<zx_status_t>(pos_); |
| } |
| |
| private: |
| char* ptr_; |
| size_t pos_; |
| const size_t len_; |
| }; |
| |
| // Helper class to track outstanding operations associated to a |
| // particular Vnode. |
| class VnodeToken : public fbl::SinglyLinkedListable<std::unique_ptr<VnodeToken>> { |
| public: |
| VnodeToken(zx_koid_t koid, fbl::RefPtr<Vnode> vnode) |
| : koid_(koid), vnode_(std::move(vnode)) { |
| } |
| |
| zx_koid_t get_koid() const { return koid_; } |
| fbl::RefPtr<Vnode> get_vnode() const { return vnode_; } |
| |
| // Trait implementation for fbl::HashTable |
| zx_koid_t GetKey() const { return koid_; } |
| static size_t GetHash(zx_koid_t koid) { return koid; } |
| |
| private: |
| zx_koid_t koid_; |
| fbl::RefPtr<Vnode> vnode_; |
| }; |
| |
| } // namespace fs |
| |
| #endif // FS_VNODE_H_ |