blob: f6dda2eee6dba1dd0dab3140aff11f08093a0df4 [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/c/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/fit/promise.h>
#include <lib/fzl/owned-vmo-mapper.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 "allocator/allocator.h"
#include "allocator/extent-reserver.h"
#include "allocator/node-reserver.h"
#include "blob-cache.h"
#include "compression/blob-compressor.h"
#include "compression/compressor.h"
#include "format-assertions.h"
#include "metrics.h"
namespace blobfs {
class Blobfs;
using digest::Digest;
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 VerifyBlob(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
size_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(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_handle_t* 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.
void BlobCloseHandles();
// 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 CloneVmo(zx_rights_t rights, zx_handle_t* out_vmo, size_t* out_size);
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);
// Reads both VMOs into memory, if we haven't already.
//
// TODO(ZX-1481): When we have can register the Blob Store as a pager
// service, and it can properly handle pages faults on a vnode's contents,
// then we can avoid reading the entire blob up-front. Until then, read
// the contents of a VMO into memory when it is opened.
zx_status_t InitVmos();
// Initializes a compressed blob by reading it from disk and decompressing it.
// Does not verify the blob.
zx_status_t InitCompressed(CompressionAlgorithm algorithm);
// Initializes a decompressed blob by reading it from disk.
// Does not verify the blob.
zx_status_t InitUncompressed();
// Verifies the integrity of the in-memory Blob.
// InitVmos() 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();
// Acquires a pointer to the mapped data or merkle tree
void* GetData() const;
void* GetMerkle() const;
Blobfs* const blobfs_;
BlobFlags flags_ = {};
std::atomic_bool syncing_;
// The mapping here consists of:
// 1) The Merkle Tree
// 2) The Blob itself, aligned to the nearest kBlobfsBlockSize
fzl::OwnedVmoMapper mapping_;
vmoid_t vmoid_ = {};
// 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_ = {};
uint32_t map_index_ = {};
// 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_ = {};
};
} // namespace blobfs
#endif // ZIRCON_SYSTEM_ULIB_BLOBFS_BLOB_H_