blob: be285a36541c59a266a9e8c240ff8d6b4552c099 [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.
#include "src/storage/fvm/host/sparse_paver.h"
#include <unistd.h>
#include <algorithm>
#include <memory>
zx_status_t SparsePaver::Create(std::unique_ptr<fvm::host::FileWrapper> wrapper, size_t slice_size,
size_t disk_offset, size_t disk_size,
std::unique_ptr<SparsePaver>* out) {
std::unique_ptr<SparsePaver> paver(new SparsePaver(disk_offset, disk_size));
zx_status_t status = paver->Init(std::move(wrapper), slice_size);
if (status != ZX_OK) {
return status;
}
*out = std::move(paver);
return status;
}
zx_status_t SparsePaver::AddPartition(const SparsePartitionInfo* partition,
fvm::SparseReader* reader) {
info_.CheckValid();
// Assign random guid.
uint8_t guid[fvm::kGuidSize];
static unsigned int seed = static_cast<unsigned int>(time(0));
for (size_t i = 0; i < fvm::kGuidSize; i++) {
guid[i] = static_cast<uint8_t>(rand_r(&seed));
}
uint32_t vpart_index;
zx_status_t status = info_.AllocatePartition(partition->descriptor, guid, &vpart_index);
if (status != ZX_OK) {
return status;
}
// Allocate all slices for this partition.
for (uint32_t i = 0; i < partition->descriptor.extent_count; i++) {
status = AddExtent(vpart_index, &partition->extents[i], reader);
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
zx_status_t SparsePaver::Commit() {
info_.CheckValid();
if (disk_ptr_ > disk_offset_ + disk_size_) {
fprintf(stderr, "FVM metadata size exceeds disk size\n");
return ZX_ERR_INTERNAL;
}
zx_status_t status = info_.Write(file_.get(), disk_offset_, disk_size_);
if (status != ZX_OK) {
return status;
}
// Move pointer to the end of the designated partition size to prevent any further edits.
disk_ptr_ = disk_offset_ + disk_size_ + 1;
file_->Sync();
return ZX_OK;
}
zx_status_t SparsePaver::Init(std::unique_ptr<fvm::host::FileWrapper> wrapper, size_t slice_size) {
file_ = std::move(wrapper);
zx_status_t status = info_.Reset(disk_size_, slice_size);
if (status != ZX_OK) {
return status;
}
disk_ptr_ = disk_offset_ + info_.SuperBlock().GetDataStartOffset();
if (disk_ptr_ >= disk_offset_ + disk_size_) {
fprintf(stderr, "FVM metadata size exceeds disk size\n");
return ZX_ERR_INTERNAL;
}
off_t result = file_->Seek(disk_ptr_, SEEK_SET);
if (result < 0 || static_cast<size_t>(result) != disk_ptr_) {
return ZX_ERR_IO;
}
data_.reset(new uint8_t[info_.SliceSize()]);
return ZX_OK;
}
zx_status_t SparsePaver::AddExtent(uint32_t vpart_index, fvm::ExtentDescriptor* extent,
fvm::SparseReader* reader) {
size_t bytes_left = extent->extent_length;
if (extent->slice_start > std::numeric_limits<uint32_t>::max()) {
return ZX_ERR_INTERNAL;
}
ExtentInfo extent_info{
.vslice_start = extent->slice_start,
.vslice_count = static_cast<uint32_t>(extent->slice_count),
};
auto pslice_or = info_.AllocateSlicesContiguous(vpart_index, extent_info);
if (pslice_or.is_error()) {
fprintf(stderr, "Failed to allocate slices: %d\n", pslice_or.status_value());
return pslice_or.status_value();
}
for (unsigned i = 0; i < extent->slice_count; i++) {
if (zx_status_t status = WriteSlice(&bytes_left, reader); status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
zx_status_t SparsePaver::WriteSlice(size_t* bytes_left, fvm::SparseReader* reader) {
info_.CheckValid();
const size_t slice_size = info_.SliceSize();
if (disk_ptr_ + slice_size > disk_offset_ + disk_size_) {
fprintf(stderr, "%lu + %lu exceeds the provided disk size (%lu)\n", disk_ptr_, slice_size,
disk_offset_ + disk_size_);
return ZX_ERR_INVALID_ARGS;
}
size_t read_length = std::min(slice_size, *bytes_left);
if (read_length > 0) {
size_t bytes_read = 0;
zx_status_t status = reader->ReadData(data_.get(), read_length, &bytes_read);
if (status != ZX_OK) {
return status;
}
if (bytes_read < read_length) {
fprintf(stderr, "Slice data is less than expected\n");
return ZX_ERR_IO;
}
if (read_length < slice_size) {
memset(&data_[read_length], 0, slice_size - read_length);
}
*bytes_left -= bytes_read;
} else {
memset(data_.get(), 0, slice_size);
}
ssize_t result = file_->Write(data_.get(), slice_size);
if (result < 0 || static_cast<size_t>(result) != slice_size) {
return ZX_ERR_IO;
}
disk_ptr_ += slice_size;
return ZX_OK;
}