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