blob: ed0d9e9e654b44b6bae63ff56be4f6b373c80a28 [file] [log] [blame]
// Copyright 2020 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.
#include "src/storage/blobfs/blobfs_inspector.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <algorithm>
#include "src/storage/blobfs/inspector/parser.h"
#include "src/storage/lib/vfs/cpp/journal/inspector_parser.h"
namespace blobfs {
BlobfsInspector::BlobfsInspector(std::unique_ptr<fs::TransactionHandler> handler,
std::unique_ptr<disk_inspector::BufferFactory> buffer_factory)
: handler_(std::move(handler)),
buffer_factory_(std::move(buffer_factory)),
loader_(handler_.get()) {}
zx::result<std::unique_ptr<BlobfsInspector>> BlobfsInspector::Create(
std::unique_ptr<fs::TransactionHandler> handler,
std::unique_ptr<disk_inspector::BufferFactory> factory) {
auto inspector =
std::unique_ptr<BlobfsInspector>(new BlobfsInspector(std::move(handler), std::move(factory)));
auto result = inspector->buffer_factory_->CreateBuffer(1);
if (result.is_error()) {
return zx::error(result.take_error());
}
inspector->buffer_ = result.take_value();
zx_status_t status = inspector->ReloadSuperblock();
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(inspector));
}
zx_status_t BlobfsInspector::ReloadSuperblock() {
zx_status_t status;
status = loader_.RunReadOperation(buffer_.get(), 0, kSuperblockOffset, kBlobfsSuperblockBlocks);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load superblock. Error: " << zx_status_get_string(status);
return status;
}
superblock_ = GetSuperblock(buffer_.get());
return ZX_OK;
}
Superblock BlobfsInspector::InspectSuperblock() { return superblock_; }
uint64_t BlobfsInspector::GetInodeCount() { return superblock_.inode_count; }
uint64_t BlobfsInspector::GetJournalEntryCount() {
uint64_t journal_block_count = JournalBlocks(superblock_);
// If there are no journal blocks, there cannot be any entries.
if (journal_block_count == 0) {
return 0;
}
return journal_block_count - fs::kJournalMetadataBlocks;
}
zx::result<std::vector<Inode>> BlobfsInspector::InspectInodeRange(uint64_t start_index,
uint64_t end_index) {
ZX_ASSERT(end_index > start_index);
// Since there are multiple inodes in a block, we first perform calculations to find the block
// range of only the desired inode range to load.
uint64_t start_block_offset = start_index / kBlobfsInodesPerBlock;
uint64_t start_block = NodeMapStartBlock(superblock_) + start_block_offset;
// Because the end index is exclusive, we calculate the length based on end index - 1 to get the
// last inclusive value, and add 1 to the length to prevent off-by-one.
uint64_t block_length = (end_index - 1) / kBlobfsInodesPerBlock - start_block_offset + 1;
auto result = buffer_factory_->CreateBuffer(block_length);
if (result.is_error()) {
return zx::error(result.take_error());
}
std::unique_ptr<storage::BlockBuffer> inode_buffer = result.take_value();
zx_status_t status = loader_.RunReadOperation(inode_buffer.get(), 0, start_block, block_length);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load inode. Error: " << zx_status_get_string(status);
return zx::error(status);
}
// Once loaded, we treat the buffer as the entire inode table and find the new start index
// relative to it being in the first block. The element count can be calculated normally.
uint64_t buffer_offset = start_index % kBlobfsInodesPerBlock;
uint64_t count = end_index - start_index;
std::vector<Inode> inodes;
for (uint64_t i = 0; i < count; ++i) {
inodes.emplace_back(GetInodeElement(inode_buffer.get(), buffer_offset + i));
}
return zx::ok(inodes);
}
// Since the scratch buffer is only a single block long, we check that the JournalSuperblock is
// small enough to load into the buffer.
static_assert(fs::kJournalMetadataBlocks == 1);
zx::result<fs::JournalInfo> BlobfsInspector::InspectJournalSuperblock() {
zx_status_t status = loader_.RunReadOperation(buffer_.get(), 0, JournalStartBlock(superblock_),
fs::kJournalMetadataBlocks);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load journal superblock. Error: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok(fs::GetJournalSuperblock(buffer_.get()));
}
template <>
zx::result<fs::JournalPrefix> BlobfsInspector::InspectJournalEntryAs(uint64_t index) {
zx_status_t status = LoadJournalEntry(buffer_.get(), index);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(*reinterpret_cast<fs::JournalPrefix*>(buffer_->Data(0)));
}
template <>
zx::result<fs::JournalHeaderBlock> BlobfsInspector::InspectJournalEntryAs(uint64_t index) {
zx_status_t status = LoadJournalEntry(buffer_.get(), index);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(*reinterpret_cast<fs::JournalHeaderBlock*>(buffer_->Data(0)));
}
template <>
zx::result<fs::JournalCommitBlock> BlobfsInspector::InspectJournalEntryAs(uint64_t index) {
zx_status_t status = LoadJournalEntry(buffer_.get(), index);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(*reinterpret_cast<fs::JournalCommitBlock*>(buffer_->Data(0)));
}
zx::result<std::vector<uint64_t>> BlobfsInspector::InspectDataBlockAllocatedInRange(
uint64_t start_index, uint64_t end_index) {
ZX_ASSERT(end_index > start_index);
// Since there are multiple bits in a block, we first perform calculations to find the block range
// of only the desired bit range to load.
uint64_t start_block_offset = start_index / kBlobfsBlockBits;
uint64_t start_block = BlockMapStartBlock(superblock_) + start_block_offset;
// Because the end index is exclusive, we calculate the length based on end index - 1 to get the
// last inclusive value, and add 1 to the length to prevent off-by-one.
uint64_t block_length = (end_index - 1) / kBlobfsBlockBits - start_block_offset + 1;
auto result = buffer_factory_->CreateBuffer(block_length);
if (result.is_error()) {
return zx::error(result.take_error());
}
std::unique_ptr<storage::BlockBuffer> bit_buffer = result.take_value();
zx_status_t status = loader_.RunReadOperation(bit_buffer.get(), 0, start_block, block_length);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load allocation bits. Error: " << zx_status_get_string(status);
return zx::error(status);
}
// Once loaded, we treat the buffer as the entire inode bitmap and find the new start index
// relative to it being in the first block. The element count can be calculated normally.
uint64_t buffer_offset = start_index % kBlobfsBlockBits;
uint64_t count = end_index - start_index;
std::vector<uint64_t> allocated_indices;
for (uint64_t i = 0; i < count; ++i) {
if (GetBitmapElement(bit_buffer.get(), buffer_offset + i)) {
allocated_indices.emplace_back(start_index + i);
}
}
return zx::ok(allocated_indices);
}
zx::result<> BlobfsInspector::WriteSuperblock(Superblock superblock) {
*static_cast<Superblock*>(buffer_->Data(0)) = superblock;
zx_status_t status = loader_.RunWriteOperation(buffer_.get(), 0, 0, 1);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot write superblock. Error: " << zx_status_get_string(status);
return zx::error(status);
}
superblock_ = superblock;
return zx::ok();
}
zx::result<> BlobfsInspector::WriteInodes(std::vector<Inode> inodes, uint64_t start_index) {
uint64_t end_index = start_index + inodes.size();
// Since there are multiple inodes in a block, we first perform calculations to find the block
// range of only the desired inode range to load.
uint64_t start_block_offset = start_index / kBlobfsInodesPerBlock;
uint64_t start_block = NodeMapStartBlock(superblock_) + start_block_offset;
// Because the end index is exclusive, we calculate the length based on end index - 1 to get the
// last inclusive value, and add 1 to the length to prevent off-by-one.
uint64_t block_length = (end_index - 1) / kBlobfsInodesPerBlock - start_block_offset + 1;
auto result = buffer_factory_->CreateBuffer(block_length);
if (result.is_error()) {
return zx::error(result.take_error());
}
std::unique_ptr<storage::BlockBuffer> inode_buffer = result.take_value();
// We still need to perform a read in case the inode range to write is not aligned on block
// boundaries.
zx_status_t status = loader_.RunReadOperation(inode_buffer.get(), 0, start_block, block_length);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load inodes. Error: " << zx_status_get_string(status);
return zx::error(status);
}
// Once loaded, we treat the buffer as the entire inode table and find the new start index
// relative to it being in the first block. The element count can be calculated normally.
uint64_t buffer_offset = start_index % kBlobfsInodesPerBlock;
uint64_t count = end_index - start_index;
for (uint64_t i = 0; i < count; ++i) {
WriteInodeElement(inode_buffer.get(), inodes[i], buffer_offset + i);
}
status = loader_.RunWriteOperation(inode_buffer.get(), 0, start_block, block_length);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot write inodes. Error: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok();
}
zx::result<> BlobfsInspector::WriteJournalSuperblock(fs::JournalInfo journal_info) {
*reinterpret_cast<fs::JournalInfo*>(buffer_->Data(0)) = journal_info;
zx_status_t status = loader_.RunWriteOperation(buffer_.get(), 0, JournalStartBlock(superblock_),
fs::kJournalMetadataBlocks);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot write journal superblock. Error: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok();
}
zx::result<> BlobfsInspector::WriteJournalEntryBlocks(storage::BlockBuffer* buffer,
uint64_t start_index) {
uint64_t start_block = JournalStartBlock(superblock_) + fs::kJournalMetadataBlocks + start_index;
zx_status_t status = loader_.RunWriteOperation(buffer, 0, start_block, buffer->capacity());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot write journal entries. Error: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok();
}
zx::result<> BlobfsInspector::WriteDataBlockAllocationBits(bool value, uint64_t start_index,
uint64_t end_index) {
ZX_ASSERT(end_index > start_index);
// Since there are multiple bits in a block, we first perform calculations to find the block range
// of only the desired bit range to load.
uint64_t start_block_offset = start_index / kBlobfsBlockBits;
uint64_t start_block = BlockMapBlocks(superblock_) + start_block_offset;
// Because the end index is exclusive, we calculate the length based on end index - 1 to get the
// last inclusive value, and add 1 to the length to prevent off-by-one.
uint64_t block_length = (end_index - 1) / kBlobfsBlockBits - start_block_offset + 1;
auto result = buffer_factory_->CreateBuffer(block_length);
if (result.is_error()) {
return zx::error(result.take_error());
}
std::unique_ptr<storage::BlockBuffer> bit_buffer = result.take_value();
// We still need to perform a read in case the bit range to write is not aligned on block
// boundaries.
zx_status_t status = loader_.RunReadOperation(bit_buffer.get(), 0, start_block, block_length);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load allocation bits. Error: " << zx_status_get_string(status);
return zx::error(status);
}
// Once loaded, we treat the buffer as the entire bit bitmap and find the new start index relative
// to it being in the first block. The element count can be calculated normally.
uint64_t buffer_offset = start_index % kBlobfsBlockBits;
uint64_t count = end_index - start_index;
std::vector<uint64_t> allocated_indices;
for (uint64_t i = 0; i < count; ++i) {
WriteBitmapElement(bit_buffer.get(), value, buffer_offset + i);
}
status = loader_.RunWriteOperation(bit_buffer.get(), 0, start_block, block_length);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load allocation bits. Error: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok();
}
zx::result<> BlobfsInspector::WriteDataBlocks(storage::BlockBuffer* buffer, uint64_t start_index) {
uint64_t start_block = DataStartBlock(superblock_) + start_index;
zx_status_t status = loader_.RunWriteOperation(buffer, 0, start_block, buffer->capacity());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot write data blocks. Error: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok();
}
zx_status_t BlobfsInspector::LoadNodeElement(storage::BlockBuffer* buffer, uint64_t index) {
uint64_t start_block_offset = index / kBlobfsInodesPerBlock;
uint64_t start_block = NodeMapStartBlock(superblock_) + start_block_offset;
zx_status_t status = loader_.RunReadOperation(buffer, 0, start_block, 1);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load node element. Error: " << zx_status_get_string(status);
}
return status;
}
zx_status_t BlobfsInspector::LoadJournalEntry(storage::BlockBuffer* buffer, uint64_t index) {
uint64_t start_block = JournalStartBlock(superblock_) + fs::kJournalMetadataBlocks + index;
zx_status_t status = loader_.RunReadOperation(buffer, 0, start_block, 1);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot load journal entry. Error: " << zx_status_get_string(status);
}
return status;
}
} // namespace blobfs