blob: e618b78bb1ae72c83e86b29b4ca9aef30e937e57 [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.
#include <inttypes.h>
#include <limits>
#include <safemath/checked_math.h>
#include <utility>
#include "fvm-host/format.h"
namespace {
template <class T> uint32_t ToU32(T in) {
if (in > std::numeric_limits<uint32_t>::max()) {
fprintf(stderr, "%s:%d out of range %" PRIuMAX "\n", __FILE__, __LINE__,
safemath::checked_cast<uintmax_t>(in));
exit(-1);
}
return safemath::checked_cast<uint32_t>(in);
}
} // namespace
BlobfsFormat::BlobfsFormat(fbl::unique_fd fd, const char* type)
: Format(), fd_(std::move(fd)) {
if (!strcmp(type, kBlobTypeName)) {
memcpy(type_, kBlobType, sizeof(kBlobType));
} else if (!strcmp(type, kDefaultTypeName)) {
memcpy(type_, kDefaultType, sizeof(kDefaultType));
} else {
fprintf(stderr, "Unrecognized type for blobfs: %s\n", type);
exit(-1);
}
if (blobfs::readblk(fd_.get(), 0, reinterpret_cast<void*>(blk_)) < 0) {
fprintf(stderr, "blobfs: could not read info block\n");
exit(-1);
}
if (blobfs::GetBlockCount(fd_.get(), &blocks_) != ZX_OK) {
fprintf(stderr, "blobfs: cannot find end of underlying device\n");
exit(-1);
} else if (blobfs::CheckSuperblock(&info_, blocks_) != ZX_OK) {
fprintf(stderr, "blobfs: Info check failed\n");
exit(-1);
}
}
BlobfsFormat::~BlobfsFormat() = default;
zx_status_t BlobfsFormat::ComputeSlices(uint64_t inode_count, uint64_t data_blocks,
uint64_t journal_block_count) {
auto abm_blocks = blobfs::BlocksRequiredForBits(data_blocks);
auto ino_blocks = blobfs::BlocksRequiredForInode(inode_count);
fvm_info_.abm_slices = BlocksToSlices(abm_blocks);
fvm_info_.ino_slices = BlocksToSlices(ino_blocks);
fvm_info_.journal_slices = BlocksToSlices(ToU32(journal_block_count));
fvm_info_.dat_slices = BlocksToSlices(safemath::checked_cast<uint32_t>(data_blocks));
fvm_info_.vslice_count = 1 + fvm_info_.abm_slices + fvm_info_.ino_slices +
fvm_info_.dat_slices + fvm_info_.journal_slices;
fvm_info_.inode_count = safemath::checked_cast<uint32_t>(
fvm_info_.ino_slices * fvm_info_.slice_size / blobfs::kBlobfsInodeSize);
fvm_info_.journal_block_count = SlicesToBlocks(fvm_info_.journal_slices);
fvm_info_.data_block_count = SlicesToBlocks(fvm_info_.dat_slices);
fvm_info_.flags |= blobfs::kBlobFlagFVM;
xprintf("Blobfs: slice_size is %" PRIu64 "\n", fvm_info_.slice_size);
xprintf("Blobfs: abm_blocks: %" PRIu64 ", abm_slices: %u\n", BlockMapBlocks(fvm_info_),
fvm_info_.abm_slices);
xprintf("Blobfs: ino_blocks: %" PRIu64 ", ino_slices: %u\n", NodeMapBlocks(fvm_info_),
fvm_info_.ino_slices);
xprintf("Blobfs: jnl_blocks: %" PRIu64 ", jnl_slices: %u\n", JournalBlocks(fvm_info_),
fvm_info_.journal_slices);
xprintf("Blobfs: dat_blocks: %" PRIu64 ", dat_slices: %u\n", DataBlocks(fvm_info_),
fvm_info_.dat_slices);
zx_status_t status;
if ((status = CheckSuperblock(&fvm_info_, blocks_)) != ZX_OK) {
fprintf(stderr, "Check info failed\n");
return status;
}
return ZX_OK;
}
zx_status_t BlobfsFormat::MakeFvmReady(size_t slice_size, uint32_t vpart_index,
FvmReservation* reserve) {
memcpy(&fvm_blk_, &blk_, BlockSize());
xprintf("fvm_info has data block count %" PRIu64 "\n", fvm_info_.data_block_count);
fvm_info_.slice_size = slice_size;
if (fvm_info_.slice_size % BlockSize()) {
fprintf(stderr, "MakeFvmReady: Slice size not multiple of minfs block\n");
return ZX_ERR_INVALID_ARGS;
}
uint64_t minimum_data_blocks =
fbl::round_up(reserve->data().request.value_or(0), BlockSize()) / BlockSize();
if (minimum_data_blocks < fvm_info_.data_block_count) {
minimum_data_blocks = fvm_info_.data_block_count;
}
uint64_t minimum_inode_count = reserve->inodes().request.value_or(0);
if (minimum_inode_count < fvm_info_.inode_count) {
minimum_inode_count = fvm_info_.inode_count;
}
zx_status_t status;
if ((status = ComputeSlices(minimum_inode_count, minimum_data_blocks, JournalBlocks(info_))) !=
ZX_OK) {
return status;
}
// Lets see if we can increase journal size now
uint64_t slice_limit = reserve->total_bytes().request.value_or(0) / slice_size;
if (slice_limit > fvm_info_.vslice_count) {
// TODO(auradkar): This should use TransactionLimits
uint64_t journal_block_count = blobfs::SuggestJournalBlocks(
ToU32(JournalBlocks(fvm_info_)),
ToU32((slice_limit - fvm_info_.vslice_count) * slice_size / BlockSize()));
// Above, we might have changed number of blocks allocated to the journal. This
// might affect the number of allocated/reserved slices. Call ComputeSlices
// again to adjust the count.
if ((status = ComputeSlices(minimum_inode_count, minimum_data_blocks,
journal_block_count)) != ZX_OK) {
return status;
}
}
reserve->set_data_reserved(fvm_info_.data_block_count * BlockSize());
reserve->set_inodes_reserved(fvm_info_.inode_count);
reserve->set_total_bytes_reserved(SlicesToBlocks(ToU32(fvm_info_.vslice_count)) * BlockSize());
if (!reserve->Approved()) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
fvm_ready_ = true;
vpart_index_ = vpart_index;
return ZX_OK;
}
zx_status_t BlobfsFormat::GetVsliceRange(unsigned extent_index, vslice_info_t* vslice_info) const {
CheckFvmReady();
switch (extent_index) {
case 0: {
vslice_info->vslice_start = 0;
vslice_info->slice_count = 1;
vslice_info->block_offset = 0;
vslice_info->block_count = ToU32(SuperblockBlocks(info_));
vslice_info->zero_fill = true;
return ZX_OK;
}
case 1: {
vslice_info->vslice_start = blobfs::kFVMBlockMapStart;
vslice_info->slice_count = fvm_info_.abm_slices;
vslice_info->block_offset = ToU32(BlockMapStartBlock(info_));
vslice_info->block_count = ToU32(BlockMapBlocks(info_));
vslice_info->zero_fill = true;
return ZX_OK;
}
case 2: {
vslice_info->vslice_start = blobfs::kFVMNodeMapStart;
vslice_info->slice_count = fvm_info_.ino_slices;
vslice_info->block_offset = ToU32(NodeMapStartBlock(info_));
vslice_info->block_count = ToU32(NodeMapBlocks(info_));
vslice_info->zero_fill = true;
return ZX_OK;
}
case 3: {
vslice_info->vslice_start = blobfs::kFVMJournalStart;
vslice_info->slice_count = fvm_info_.journal_slices;
vslice_info->block_offset = ToU32(JournalStartBlock(info_));
vslice_info->block_count = ToU32(JournalBlocks(info_));
vslice_info->zero_fill = false;
return ZX_OK;
}
case 4: {
vslice_info->vslice_start = blobfs::kFVMDataStart;
vslice_info->slice_count = fvm_info_.dat_slices;
vslice_info->block_offset = ToU32(DataStartBlock(info_));
vslice_info->block_count = ToU32(DataBlocks(info_));
vslice_info->zero_fill = false;
return ZX_OK;
}
}
return ZX_ERR_OUT_OF_RANGE;
}
zx_status_t BlobfsFormat::GetSliceCount(uint32_t* slices_out) const {
CheckFvmReady();
*slices_out = 1 + fvm_info_.abm_slices + fvm_info_.ino_slices + fvm_info_.journal_slices
+ fvm_info_.dat_slices;
return ZX_OK;
}
zx_status_t BlobfsFormat::FillBlock(size_t block_offset) {
CheckFvmReady();
// If we are reading the super block, make sure it is the fvm version and not the original
if (block_offset == 0) {
memcpy(datablk, fvm_blk_, BlockSize());
} else if (blobfs::readblk(fd_.get(), block_offset, datablk) != ZX_OK) {
fprintf(stderr, "blobfs: could not read block\n");
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
zx_status_t BlobfsFormat::EmptyBlock() {
CheckFvmReady();
memset(datablk, 0, BlockSize());
return ZX_OK;
}
void* BlobfsFormat::Data() {
return datablk;
}
const char* BlobfsFormat::Name() const {
return kBlobfsName;
}
uint32_t BlobfsFormat::BlockSize() const {
return blobfs::kBlobfsBlockSize;
}
uint32_t BlobfsFormat::BlocksPerSlice() const {
CheckFvmReady();
return ToU32(fvm_info_.slice_size / BlockSize());
}
uint32_t BlobfsFormat::BlocksToSlices(uint32_t block_count) const {
return ToU32(fvm::BlocksToSlices(fvm_info_.slice_size, BlockSize(), block_count));
}
uint32_t BlobfsFormat::SlicesToBlocks(uint32_t slice_count) const {
return ToU32(fvm::SlicesToBlocks(fvm_info_.slice_size, BlockSize(), slice_count));
}