blob: 971a41e5a6eb2767dacbd7dc7015eb4cd9fbd148 [file] [log] [blame]
// Copyright 2018 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.
// This file contains Vnodes which back a Blobfs filesystem.
#ifndef ZIRCON_SYSTEM_ULIB_BLOBFS_BLOB_H_
#define ZIRCON_SYSTEM_ULIB_BLOBFS_BLOB_H_
#ifndef __Fuchsia__
#error Fuchsia-only Header
#endif
#include <fuchsia/io/llcpp/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/fit/promise.h>
#include <lib/zx/event.h>
#include <string.h>
#include <atomic>
#include <memory>
#include <blobfs/common.h>
#include <blobfs/format.h>
#include <digest/digest.h>
#include <fbl/algorithm.h>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include <fbl/vector.h>
#include <fs/vfs.h>
#include <fs/vfs_types.h>
#include <fs/vnode.h>
#include <storage/buffer/owned_vmoid.h>
#include "allocator/allocator.h"
#include "allocator/extent-reserver.h"
#include "allocator/node-reserver.h"
#include "blob-cache.h"
#include "blob-loader.h"
#include "compression/blob-compressor.h"
#include "compression/compressor.h"
#include "format-assertions.h"
#include "metrics.h"
#include "pager/page-watcher.h"
namespace blobfs {
class Blobfs;
using digest::Digest;
using digest::MerkleTreeVerifier;
typedef uint32_t BlobFlags;
// clang-format off
// After Open:
constexpr BlobFlags kBlobStateEmpty = 0x00000001; // Not yet allocated
// After Space Reserved (but allocation not yet persisted).
constexpr BlobFlags kBlobStateDataWrite = 0x00000002; // Data is being written
// After Writing:
constexpr BlobFlags kBlobStateReadable = 0x00000004; // Readable
// After Unlink:
constexpr BlobFlags kBlobStatePurged = 0x00000008; // Blob should be released during recycle
// Unrecoverable error state:
constexpr BlobFlags kBlobStateError = 0x00000010; // Unrecoverable error state
constexpr BlobFlags kBlobStateMask = 0x000000FF;
// Informational non-state flags:
constexpr BlobFlags kBlobFlagDeletable = 0x00000100; // This node should be unlinked when closed
constexpr BlobFlags kBlobOtherMask = 0x0000FF00;
// clang-format on
class Blob final : public CacheNode, fbl::Recyclable<Blob> {
public:
// Constructs a blob, reads in data, verifies the contents, then destroys the in-memory copy.
static zx_status_t LoadAndVerifyBlob(Blobfs* bs, uint32_t node_index);
// Constructs actual blobs
Blob(Blobfs* bs, const Digest& digest);
virtual ~Blob();
////////////////
// fs::Vnode interface.
using fs::Vnode::Open;
zx_status_t Open(ValidatedOptions options, fbl::RefPtr<Vnode>* out_redirect) final;
zx_status_t Close() final;
////////////////
// fbl::Recyclable interface.
void fbl_recycle() final { CacheNode::fbl_recycle(); }
////////////////
// Other methods.
BlobFlags GetState() const { return flags_ & kBlobStateMask; }
// Identifies if we can safely remove all on-disk and in-memory storage used
// by this blob.
bool Purgeable() const {
return fd_count_ == 0 && (DeletionQueued() || !(GetState() & kBlobStateReadable));
}
bool DeletionQueued() const { return flags_ & kBlobFlagDeletable; }
void SetState(BlobFlags new_state) { flags_ = (flags_ & ~kBlobStateMask) | new_state; }
uint32_t GetMapIndex() const { return map_index_; }
// Returns a unique identifier for this blob
uint32_t Ino() const { return map_index_; }
void PopulateInode(uint32_t node_index);
uint64_t SizeData() const;
const Inode& GetNode() const { return inode_; }
void CompleteSync();
// When blob VMOs are cloned and returned to clients, blobfs watches
// the original VMO handle for the signal |ZX_VMO_ZERO_CHILDREN|.
// While this signal is not set, the blob's Vnode keeps an extra
// reference to itself to prevent teardown while clients are using
// this Vmo. This reference is internally called the "clone watcher".
//
// This function may be called on a blob to tell it to forcefully release
// the "reference to itself" that is kept when the blob is mapped.
//
// Returns this reference, if it exists, to provide control over
// when the Vnode destructor is executed.
fbl::RefPtr<Blob> CloneWatcherTeardown();
// Marks the blob as deletable, and attempt to purge it.
zx_status_t QueueUnlink();
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(Blob);
////////////////
// fs::Vnode interface.
zx_status_t GetNodeInfoForProtocol(fs::VnodeProtocol protocol, fs::Rights rights,
fs::VnodeRepresentation* info) final;
fs::VnodeProtocolSet GetProtocols() const final;
bool ValidateRights(fs::Rights rights) final;
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 GetAttributes(fs::VnodeAttributes* a) final;
zx_status_t Truncate(size_t len) final;
zx_status_t QueryFilesystem(llcpp::fuchsia::io::FilesystemInfo* out) final;
zx_status_t GetDevicePath(size_t buffer_len, char* out_name, size_t* out_len) final;
zx_status_t GetVmo(int flags, zx::vmo* out_vmo, size_t* out_size) final;
void Sync(SyncCallback closure) final;
////////////////
// blobfs::CacheNode interface.
BlobCache& Cache() final;
bool ShouldCache() const final;
void ActivateLowMemory() final;
////////////////
// Other methods.
// Returns a handle to an event which will be signalled when
// the blob is readable.
//
// Returns "ZX_OK" if successful, otherwise the error code
// will indicate the failure status.
zx_status_t GetReadableEvent(zx::event* out);
// Returns a clone of the blobfs VMO.
//
// Monitors the current VMO, keeping a reference to the Vnode
// alive while the |out| VMO (and any clones it may have) are open.
zx_status_t CloneDataVmo(zx_rights_t rights, zx::vmo* out_vmo, size_t* out_size);
// Receives notifications when all clones vended by CloneDataVmo() are released.
void HandleNoClones(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal);
// Invokes |Purge()| if the vnode is purgeable.
zx_status_t TryPurge();
// Removes all traces of the vnode from blobfs.
// The blob is not expected to be accessed again after this is called.
zx_status_t Purge();
// If successful, allocates Blob Node and Blocks (in-memory)
// kBlobStateEmpty --> kBlobStateDataWrite
zx_status_t SpaceAllocate(uint64_t size_data);
// Writes to either the Merkle Tree or the Data section,
// depending on the state.
zx_status_t WriteInternal(const void* data, size_t len, size_t* actual);
// For a blob being written, consider stopping the compressor,
// the blob to eventually be written uncompressed to disk.
//
// For blobs which don't compress very well, this provides an escape
// hatch to avoid wasting work.
void ConsiderCompressionAbort();
// Reads from a blob.
// Requires: kBlobStateReadable
zx_status_t ReadInternal(void* data, size_t len, size_t off, size_t* actual);
// Loads the blob's data and merkle from disk, and initializes the data/merkle VMOs.
// If paging is enabled, the data VMO will be pager-backed and lazily loaded and verified as the
// client accesses the pages.
// If paging is disabled, the entire data VMO is loaded in and verified.
//
// Idempotent (and has no effect if PrepareVmosForWriting() has already been called.)
zx_status_t LoadVmosFromDisk();
// Initializes the data/merkle VMOs for writing into by:
// - Creating and mapping the VMOs, and
// - Attaching the VMOs to the block device FIFO.
//
// Idempotent (and has no effect if LoadVmosFromDisk() has already been called.)
zx_status_t PrepareVmosForWriting(uint32_t node_index, size_t data_size);
// Verifies the integrity of the in-memory Blob - operates on the entire blob at once.
// LoadVmosFromDisk() must have already been called for this blob.
zx_status_t Verify() const;
// Called by the Vnode once the last write has completed, updating the
// on-disk metadata.
fit::promise<void, zx_status_t> WriteMetadata();
// Returns whether the data or merkle tree bytes are mapped and resident in memory.
bool IsDataLoaded() const;
bool IsMerkleTreeLoaded() const;
// Acquires a pointer to the mapped data or merkle tree.
// May return nullptr if the mappings have not been initialized.
void* GetDataBuffer() const;
void* GetMerkleTreeBuffer() const;
// Returns whether the blob's contents are pager-backed or not.
bool IsPagerBacked() const;
// Returns a digest::Digest containing the blob's merkle root.
// Equivalent to digest::Digest(GetKey()).
digest::Digest MerkleRoot() const;
Blobfs* const blobfs_;
BlobFlags flags_ = {};
std::atomic_bool syncing_;
BlobLoader blob_loader_;
// VMO mappings for the blob's merkle tree and data.
// Data is stored in a separate VMO from the merkle tree for several reasons:
// - While data may be paged, the merkle tree (i.e. verification metadata) should always be
// retained.
// - VMO cloning when handing out a copy to read clients is simpler and requires no arithmetic.
// - Makes memory accounting more granular.
// For small blobs, merkle_mapping_ may be absent, since small blobs may not have any stored
// merkle tree.
fzl::OwnedVmoMapper merkle_mapping_;
fzl::OwnedVmoMapper data_mapping_;
// Watches any clones of "vmo_" provided to clients.
// Observes the ZX_VMO_ZERO_CHILDREN signal.
async::WaitMethod<Blob, &Blob::HandleNoClones> clone_watcher_;
// Keeps a reference to the blob alive (from within itself)
// until there are no cloned VMOs in used.
//
// This RefPtr is only non-null when a client is using a cloned VMO,
// or there would be a clear leak of Blob.
fbl::RefPtr<Blob> clone_ref_ = {};
zx::event readable_event_ = {};
uint32_t fd_count_ = 0;
uint32_t map_index_ = 0;
bool tearing_down_ = false;
// TODO(smklein): We are only using a few of these fields, such as:
// - blob_size
// - block_count
// To save space, we could avoid holding onto the entire inode.
Inode inode_ = {};
// Data used exclusively during writeback.
struct WritebackInfo {
uint64_t bytes_written = {};
fbl::Vector<ReservedExtent> extents;
fbl::Vector<ReservedNode> node_indices;
std::optional<BlobCompressor> compressor;
};
std::unique_ptr<WritebackInfo> write_info_ = {};
// Reads in the blob's pages on demand.
std::unique_ptr<PageWatcher> page_watcher_ = nullptr;
};
} // namespace blobfs
#endif // ZIRCON_SYSTEM_ULIB_BLOBFS_BLOB_H_