blob: 2c70b89470d936b2f226d6ec6aec17c5359fd176 [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 "fvm-host/fvm-info.h"
#include "fvm-host/sparse-paver.h"
zx_status_t SparsePaver::Create(fbl::unique_ptr<fvm::host::FileWrapper> wrapper, size_t slice_size,
size_t disk_offset, size_t disk_size,
fbl::unique_ptr<SparsePaver>* out) {
fbl::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;
const fvm::partition_descriptor_t* descriptor = &partition->descriptor;
zx_status_t status = info_.AllocatePartition(descriptor, guid, &vpart_index);
if (status != ZX_OK) {
return status;
}
// Allocate all slices for this partition.
for (uint32_t i = 0; i < 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(fbl::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_.MetadataSize() * 2;
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::extent_descriptor_t* extent,
fvm::SparseReader* reader) {
uint32_t pslice_start = 0;
uint32_t pslice_total = 0;
size_t bytes_left = extent->extent_length;
if (extent->slice_start > std::numeric_limits<uint32_t>::max()) {
return ZX_ERR_INTERNAL;
}
uint32_t vslice = static_cast<uint32_t>(extent->slice_start);
for (unsigned i = 0; i < extent->slice_count; i++) {
uint32_t pslice;
zx_status_t status = info_.AllocateSlice(vpart_index, vslice + i, &pslice);
if (status != ZX_OK) {
return status;
}
if (!pslice_start) {
pslice_start = pslice;
}
// On a new FVM container, pslice allocation is expected to be contiguous.
if (pslice != pslice_start + pslice_total) {
fprintf(stderr,
"fvm: pslice allocation unexpectedly non-contiguous (internal error)\n");
return ZX_ERR_INTERNAL;
}
if ((status = WriteSlice(&bytes_left, reader)) != ZX_OK) {
return status;
}
pslice_total++;
}
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, "Partition data exceeds the provided disk size\n");
return ZX_ERR_INVALID_ARGS;
}
size_t read_length = fbl::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;
}