blob: d77f5fcbaee3f869bbb81894796459877cea46a2 [file] [log] [blame]
// Copyright 2019 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 SRC_STORAGE_MINFS_VNODE_H_
#define SRC_STORAGE_MINFS_VNODE_H_
#include <inttypes.h>
#include <cstdint>
#include <memory>
#include <string_view>
#include <utility>
#ifdef __Fuchsia__
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fzl/resizeable-vmo-mapper.h>
#include <lib/zx/vmo.h>
#include <fbl/auto_lock.h>
#include "src/storage/lib/vfs/cpp/watcher.h"
#include "src/storage/minfs/vnode_allocation.h"
#endif
#include <lib/zircon-internal/fnv1hash.h>
#include <fbl/algorithm.h>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include "src/storage/lib/vfs/cpp/vfs.h"
#include "src/storage/lib/vfs/cpp/vnode.h"
#include "src/storage/minfs/format.h"
#include "src/storage/minfs/lazy_buffer.h"
#include "src/storage/minfs/minfs.h"
#include "src/storage/minfs/transaction_limits.h"
#include "src/storage/minfs/vnode_mapper.h"
#include "src/storage/minfs/writeback.h"
namespace minfs {
// Used by fsck
class Minfs;
// An abstract Vnode class contains the following:
//
// - A VMO, holding the in-memory representation of data stored persistently.
// - An inode, holding the root of this node's metadata.
//
// This class is capable of writing, reading, and truncating the node's data
// in a linear block-address space.
class VnodeMinfs : public fs::Vnode,
public fbl::SinglyLinkedListable<VnodeMinfs*>,
public fbl::Recyclable<VnodeMinfs> {
public:
explicit VnodeMinfs(Minfs* fs);
~VnodeMinfs() override;
// Allocates a new Vnode and initializes the in-memory inode structure given the type, where
// type is one of:
// - kMinfsTypeFile
// - kMinfsTypeDir
//
// Sets create / modify times of the new node.
// Does not allocate an inode number for the Vnode.
static void Allocate(Minfs* fs, uint32_t type, fbl::RefPtr<VnodeMinfs>* out);
// Allocates a Vnode, loading |ino| from storage.
//
// Doesn't update create / modify times of the node.
static void Recreate(Minfs* fs, ino_t ino, fbl::RefPtr<VnodeMinfs>* out);
bool IsUnlinked() const { return inode_.link_count == 0; }
const Inode* GetInode() const { return &inode_; }
Inode* GetMutableInode() { return &inode_; }
ino_t GetIno() const { return ino_; }
ino_t GetKey() const { return ino_; }
// Should only be called once for the VnodeMinfs lifecycle.
void SetIno(ino_t ino);
void SetNextInode(ino_t ino) { inode_.next_inode = ino; }
void SetLastInode(ino_t ino) { inode_.last_inode = ino; }
void AddLink();
void MarkPurged() { inode_.magic = kMinfsMagicPurged; }
static size_t GetHash(ino_t key) { return fnv1a_tiny(key, kMinfsHashBits); }
// fs::Vnode interface (invoked publicly).
// Required for memory management, see the class comment above Vnode for more.
void fbl_recycle() { RecycleNode(); }
// Queries the underlying vnode to ask if it may be unlinked.
//
// If the response is not ZX_OK, operations to unlink (or rename on top of) this
// vnode will fail.
virtual zx::result<> CanUnlink() const = 0;
// Removes from disk an unlinked and closed vnode. Asserts that inode IsUnlinked().
zx::result<> RemoveUnlinked();
// Issues a write on all dirty bytes within a vnode.
virtual zx::result<> FlushCachedWrites() = 0;
// Discards all the dirty bytes within a vnode.
// This also drops any inode or block reservation a vnode might have.
virtual void DropCachedWrites() = 0;
// Returns the current block count of the vnode.
virtual blk_t GetBlockCount() const = 0;
// Returns the total size of the vnode.
virtual uint64_t GetSize() const = 0;
// Returns if the node is a directory.
virtual bool IsDirectory() const = 0;
// Sets the new size of the vnode.
// Should update the in-memory representation of the Vnode, but not necessarily
// write it out to persistent storage.
//
// TODO(unknown): Upgrade internal size to 64-bit integer.
virtual void SetSize(uint32_t new_size) = 0;
// Accesses a block in the vnode at |vmo_offset| relative to the start of the file,
// which was previously at the device offset |dev_offset|.
//
// If the block was not previously allocated, |dev_offset| is zero.
// |*out_dev_offset| must contain the new value of the device offset to use when writing
// to this part of the Vnode. By default, it is set to |dev_offset|.
//
// |*out_dev_offset| may be passed to |IssueWriteback| as |dev_offset|.
virtual void AcquireWritableBlock(Transaction* transaction, blk_t vmo_offset, blk_t dev_offset,
blk_t* out_dev_offset) = 0;
// Deletes the block at |vmo_offset| within the file, corresponding to on-disk block
// |dev_offset| (zero if unallocated). |indirect| specifies whether the block is a direct or
// indirect block.
virtual void DeleteBlock(PendingWork* transaction, blk_t vmo_offset, blk_t dev_offset,
bool indirect) = 0;
#ifdef __Fuchsia__
// Instructs the Vnode to write out |count| blocks of the vnode, starting at local
// offset |vmo_offset|, corresponding to on-disk offset |dev_offset|.
virtual void IssueWriteback(Transaction* transaction, blk_t vmo_offset, blk_t dev_offset,
blk_t count) = 0;
// Queries the node, returning |true| if the node has an in-flight operation on |vmo_offset|
// that has not yet been enqueued to the writeback pipeline.
virtual bool HasPendingAllocation(blk_t vmo_offset) = 0;
// Instructs the node to cancel all pending writeback operations that have not yet been
// enqueued to the writeback pipeline.
//
// This method is used exclusively when deleting nodes.
virtual void CancelPendingWriteback() = 0;
// Returns a copy of unowned vmo.
zx::unowned_vmo vmo() const { return zx::unowned_vmo(vmo_.get()); }
#endif
// Returns true if dirty pages can be cached.
virtual bool DirtyCacheEnabled() const = 0;
// Returns true if the vnode needs to be flushed.
virtual bool IsDirty() const = 0;
Minfs* Vfs() const { return fs_; }
// Local implementations of read, write, and truncate functions which
// may operate on either files or directories.
zx::result<> ReadInternal(PendingWork* transaction, void* data, size_t len, size_t off,
size_t* actual);
zx::result<> ReadExactInternal(PendingWork* transaction, void* data, size_t len, size_t off);
zx::result<> WriteInternal(Transaction* transaction, const uint8_t* data, size_t len, size_t off,
size_t* actual);
zx::result<> WriteExactInternal(Transaction* transaction, const void* data, size_t len,
size_t off);
zx::result<> TruncateInternal(Transaction* transaction, size_t len);
// Update the vnode's inode and write it to disk.
void InodeSync(PendingWork* transaction, uint32_t flags);
// Decrements the inode link count to a vnode.
// Writes the inode back to |transaction|.
//
// If the link count becomes zero, the node either:
// 1) Calls |Purge()| (if no open fds exist), or
// 2) Adds itself to the "unlinked list", to be purged later.
[[nodiscard]] zx::result<> RemoveInodeLink(Transaction* transaction);
// Allocates an indirect block.
void AllocateIndirect(PendingWork* transaction, blk_t* block);
// Initializes (if necessary) and returns the indirect file.
[[nodiscard]] zx::result<LazyBuffer*> GetIndirectFile();
// Deletes all blocks (relative to a file) from "start" (inclusive) to the end
// of the file. Does not update mtime/atime.
// This can be extended to return indices of deleted bnos, or to delete a specific number of
// bnos
zx::result<> BlocksShrink(PendingWork* transaction, blk_t start);
protected:
// fs::Vnode protected interface.
void RecycleNode() final;
private:
// fs::Vnode private interface.
zx_status_t CloseNode() final;
zx_status_t GetAttributes(fs::VnodeAttributes* a) final;
zx_status_t SetAttributes(fs::VnodeAttributesUpdate a) final;
#ifdef __Fuchsia__
zx::result<std::string> GetDevicePath() const final;
#endif
// Get the disk block 'bno' corresponding to the 'n' block
//
// May or may not allocate |bno|; certain Vnodes (like File) delay allocation
// until writeback, and will return a sentinel value of zero.
zx::result<blk_t> BlockGetWritable(Transaction* transaction, blk_t n);
// Get the disk block 'bno' corresponding to relative block address |n| within the file.
// Does not allocate any blocks, direct or indirect, to acquire this block.
zx::result<blk_t> BlockGetReadable(blk_t n);
// Deletes this Vnode from disk, freeing the inode and blocks.
//
// Must only be called on Vnodes which
// - Have no open fds
// - Are fully unlinked (link count == 0)
[[nodiscard]] zx::result<> Purge(Transaction* transaction);
#ifdef __Fuchsia__
void Sync(SyncCallback closure) final;
// Initializes vmo that contains file's data by reading data from the disk.
// Since we cannot yet register the filesystem as a paging service (and
// cleanly fault on pages when they are actually needed), we currently read an
// entire file to a VMO when a file's data block are accessed.
zx::result<> InitVmo();
// Use the watcher container to implement a directory watcher
void Notify(std::string_view name, fuchsia_io::wire::WatchEvent event) final;
zx_status_t WatchDir(fs::FuchsiaVfs* vfs, fuchsia_io::wire::WatchMask mask, uint32_t options,
fidl::ServerEnd<fuchsia_io::DirectoryWatcher> watcher) final;
#endif
Minfs* const fs_;
#ifdef __Fuchsia__
zx::vmo vmo_{};
uint64_t vmo_size_ = 0;
storage::Vmoid vmoid_;
fs::WatcherContainer watcher_{};
#endif
// vnode_mapper.cc explains what this is and the code there is responsible for manipulating it.
// It is created on-demand.
std::unique_ptr<LazyBuffer> indirect_file_;
ino_t ino_{};
// DataBlockAssigner may modify this field asynchronously, so a valid Transaction object must
// be held before accessing it.
Inode inode_{};
};
} // namespace minfs
#endif // SRC_STORAGE_MINFS_VNODE_H_