blob: cb158d8d38ec187c1d9e3bfd565a45ff11a79f30 [file] [log] [blame]
// 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.
// This file includes host-side functionality for accessing Blobfs.
#ifndef SRC_STORAGE_BLOBFS_HOST_H_
#define SRC_STORAGE_BLOBFS_HOST_H_
#ifdef __Fuchsia__
#error Host-only Header
#endif
#include <assert.h>
#include <lib/fit/function.h>
#include <lib/fpromise/result.h>
#include <lib/stdcompat/span.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <zircon/assert.h>
#include <zircon/types.h>
#include <cstdint>
#include <filesystem>
#include <memory>
#include <mutex>
#include <optional>
#include <variant>
#include <vector>
#include <bitmap/raw-bitmap.h>
#include <bitmap/storage.h>
#include <fbl/algorithm.h>
#include <fbl/macros.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include "src/lib/chunked-compression/multithreaded-chunked-compressor.h"
#include "src/lib/digest/digest.h"
#include "src/storage/blobfs/allocator/host_allocator.h"
#include "src/storage/blobfs/blob_layout.h"
#include "src/storage/blobfs/common.h"
#include "src/storage/blobfs/format.h"
#include "src/storage/blobfs/node_finder.h"
namespace blobfs {
// A mapping of a file. Does not own the file.
class FileMapping {
public:
FileMapping(const FileMapping&) = delete;
FileMapping(FileMapping&& other) noexcept;
FileMapping& operator=(const FileMapping&) = delete;
FileMapping& operator=(FileMapping&& other) noexcept;
~FileMapping();
static zx::result<FileMapping> Create(int fd);
cpp20::span<const uint8_t> data() const {
return cpp20::span(static_cast<const uint8_t*>(data_), length_);
}
private:
FileMapping(void* data, uint64_t length) : data_(data), length_(length) {}
void* data_ = nullptr;
uint64_t length_ = 0;
};
// Merkle tree, data, and compression information associated with a file.
class BlobInfo {
public:
BlobInfo(const BlobInfo&) = delete;
BlobInfo(BlobInfo&&) noexcept = default;
BlobInfo& operator=(const BlobInfo&) = delete;
BlobInfo& operator=(BlobInfo&&) noexcept = default;
// Creates a BlobInfo object for |fd| using the layout specified by |blob_layout_format|. If
// compressing the blob would save space then the blob will be compressed.
static zx::result<BlobInfo> CreateCompressed(
int fd, BlobLayoutFormat blob_layout_format, std::filesystem::path file_path,
chunked_compression::MultithreadedChunkedCompressor& compressor);
// Creates a BlobInfo object for |fd| using the layout specified by |blob_layout_format|. The blob
// will not be compressed.
static zx::result<BlobInfo> CreateUncompressed(int fd, BlobLayoutFormat blob_layout_format,
std::filesystem::path file_path);
// If the blob was compressed then this function will return the compressed data. Otherwise the
// uncompressed data is returned.
cpp20::span<const uint8_t> GetData() const { return std::visit(BlobDataVisitor{}, blob_data_); }
// Returns true if the data returned by |GetData| is compressed.
bool IsCompressed() const { return std::holds_alternative<CompressedBlobData>(blob_data_); }
const digest::Digest& GetDigest() const { return digest_; }
cpp20::span<const uint8_t> GetMerkleTree() const {
return cpp20::span<const uint8_t>(merkle_tree_);
}
const BlobLayout& GetBlobLayout() const { return *blob_layout_; }
const std::filesystem::path& GetSrcFilePath() const { return src_file_path_; }
private:
BlobInfo() = default;
digest::Digest digest_;
std::vector<uint8_t> merkle_tree_;
std::unique_ptr<BlobLayout> blob_layout_;
// The path to the file which this blob came from.
std::filesystem::path src_file_path_;
using CompressedBlobData = fbl::Array<uint8_t>;
using UncompressedBlobData = FileMapping;
struct BlobDataVisitor {
cpp20::span<const uint8_t> operator()(const std::monostate& /*unused*/) {
ZX_PANIC("Blob data was not set");
}
cpp20::span<const uint8_t> operator()(const CompressedBlobData& compressed_data) {
return cpp20::span<const uint8_t>(compressed_data);
}
cpp20::span<const uint8_t> operator()(const UncompressedBlobData& file_mapping) {
return file_mapping.data();
}
};
std::variant<std::monostate, CompressedBlobData, UncompressedBlobData> blob_data_;
};
union info_block_t {
uint8_t block[kBlobfsBlockSize];
Superblock info;
};
class Blobfs : public fbl::RefCounted<Blobfs> {
public:
DISALLOW_COPY_ASSIGN_AND_MOVE(Blobfs);
struct BlobView {
cpp20::span<const uint8_t> merkle_hash;
cpp20::span<const uint8_t> blob_contents;
};
using BlobVisitor = fit::function<fpromise::result<void, std::string>(BlobView)>;
// Creates an instance of Blobfs from the file at |blockfd|. The blobfs partition is expected to
// start at |offset| bytes into the file.
static zx::result<std::unique_ptr<Blobfs>> Create(fbl::unique_fd blockfd, off_t offset,
const info_block_t& info_block,
const fbl::Array<size_t>& extent_lengths);
// Adds a blob to the filesystem.
zx::result<> AddBlob(const BlobInfo& blob_info);
// Access the |node_index|-th inode.
zx::result<InodePtr> GetNode(uint32_t node_index);
NodeFinder* GetNodeFinder() { return allocator_.get(); }
bool CheckBlocksAllocated(uint64_t start_block, uint64_t end_block,
uint64_t* first_unset = nullptr) const;
const Superblock& Info() const { return info_; }
uint64_t GetBlockSize() const;
// Calls |visitor| on each of the existing blobs. Errors on |visitor| will be forwarded to the
// caller, and will stop the iteration.
fpromise::result<void, std::string> VisitBlobs(BlobVisitor visitor);
zx::result<std::unique_ptr<Superblock>> ReadBackupSuperblock();
private:
friend class BlobfsChecker;
Blobfs(fbl::unique_fd fd, off_t offset, const info_block_t& info_block,
const fbl::Array<size_t>& extent_lengths);
// Finds the index of the inode with the given |digest|. Returns ZX_ERR_NOT_FOUND if there is no
// inode with that digest.
zx::result<uint32_t> FindInodeByDigest(const Digest& digest);
zx::result<> WriteData(const BlobInfo& blob_info, const std::vector<ReservedExtent>& extents);
zx::result<> WriteBlockBitmap(const Extent& extent);
zx::result<> WriteNode(uint32_t node_index);
zx::result<> WriteInfo();
zx::result<RawBitmap> LoadBlockBitmap();
zx::result<std::vector<Inode>> LoadNodeMap();
// Read |block_count| blocks starting at |start_block| into |data|.
zx::result<> ReadBlocks(uint64_t start_block, uint64_t block_count, void* data);
// Read data from block |block_number| into |data|.
zx::result<> ReadBlock(uint64_t block_number, void* data);
// Read for inode |node_index| for |block_count| blocks from local |start_block| into |data|.
zx::result<> ReadBlocksForInode(uint32_t node_index, uint64_t start_block, uint64_t block_count,
uint8_t* data);
// Write |block_count| blocks of |data| at block number starting at |start_block|.
zx::result<> WriteBlocks(uint64_t start_block, uint64_t block_count, const void* data);
// Write |data| into block |block_number|.
zx::result<> WriteBlock(uint64_t block_number, const void* data);
zx_status_t LoadAndVerifyBlob(uint32_t node_index);
fpromise::result<std::vector<uint8_t>, std::string> LoadDataAndVerifyBlob(uint32_t inode_index);
std::vector<Inode> node_map_;
std::unique_ptr<HostAllocator> allocator_;
fbl::unique_fd blockfd_;
off_t offset_;
uint64_t block_map_start_block_;
uint64_t node_map_start_block_;
uint64_t journal_start_block_;
uint64_t data_start_block_;
uint64_t block_map_block_count_;
uint64_t node_map_block_count_;
uint64_t journal_block_count_;
uint64_t data_block_count_;
union {
Superblock info_;
uint8_t info_block_[kBlobfsBlockSize];
};
};
// Reads block |block_number| into |data| from |fd|.
zx_status_t ReadBlock(int fd, uint64_t block_number, void* data);
// Returns the number of blobfs blocks that fit in |fd|.
zx_status_t GetBlockCount(int fd, uint64_t* out);
// Formats a blobfs filesystem, meant to contain |block_count| blobfs blocks, to the device
// represented by |fd|.
//
// Returns -1 on error, 0 on success.
int Mkfs(int fd, uint64_t block_count, const FilesystemOptions& options);
// Copies into |out_size| the number of bytes used by data in a blobfs partition contained in |fd|.
// blobfs is assumed to start at offset 0 in the provided file handle.
zx_status_t UsedDataSize(const fbl::unique_fd& fd, uint64_t* out_size);
// Copies into |out_inodes| the number of allocated inodes in a blobfs partition contained in |fd|.
// blobfs is assumed to start at offset 0 in the provided file handle.
zx_status_t UsedInodes(const fbl::unique_fd& fd, uint64_t* out_inodes);
// Copies into |out_size| the number of bytes used by data and bytes reserved for superblock,
// bitmaps, inodes and journal for a blobfs partition contained in |fd|.
// blobfs is assumed to start at offset 0 in the provided file handle.
zx_status_t UsedSize(const fbl::unique_fd& fd, uint64_t* out_size);
zx_status_t blobfs_create(std::unique_ptr<Blobfs>* out, fbl::unique_fd blockfd);
zx_status_t blobfs_fsck(fbl::unique_fd fd, const std::vector<size_t>& extent_lengths);
// Create a blobfs from a sparse file.
// |start| indicates where the blobfs partition starts within the file (in bytes).
// |end| indicates the end of the blobfs partition (in bytes).
// |extent_lengths| contains the length (in bytes) of each blobfs extent: currently this includes
// the superblock, block bitmap, inode table, and data blocks.
zx_status_t blobfs_create_sparse(std::unique_ptr<Blobfs>* out, fbl::unique_fd fd,
const std::vector<size_t>& extent_vector);
// Write each blob contained in this image into |output_dir| as a standalone file, with the merkle
// root hash as the filename.
fpromise::result<void, std::string> ExportBlobs(int output_dir, Blobfs& fs);
} // namespace blobfs
#endif // SRC_STORAGE_BLOBFS_HOST_H_