| // 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; |
| } |