|  | // Copyright 2020 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 "fs_block_client.h" | 
|  |  | 
|  | #include <blobfs/format.h> | 
|  |  | 
|  | using block_client::BlockDevice; | 
|  |  | 
|  | zx_status_t FsBlockClient::Create(std::unique_ptr<BlockDevice> device, | 
|  | std::unique_ptr<FsBlockClient>* out) { | 
|  | fuchsia_hardware_block_BlockInfo block_info; | 
|  | zx_status_t status = device->BlockGetInfo(&block_info); | 
|  | if (status != ZX_OK) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | zx::vmo vmo; | 
|  | status = zx::vmo::create(blobfs::kBlobfsBlockSize, 0, &vmo); | 
|  | if (status != ZX_OK) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | storage::Vmoid vmoid; | 
|  | status = device->BlockAttachVmo(vmo, &vmoid); | 
|  | if (status != ZX_OK) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | out->reset(new FsBlockClient(std::move(device), block_info, std::move(vmo), std::move(vmoid))); | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | uint64_t FsBlockClient::BlockCount() const { | 
|  | return block_info_.block_count / device_blocks_per_blobfs_block(); | 
|  | } | 
|  |  | 
|  | zx_status_t FsBlockClient::ReadBlock(uint64_t block, void* data) { | 
|  | block_fifo_request_t request = {}; | 
|  | request.opcode = BLOCKIO_READ; | 
|  | request.vmoid = vmoid_.get(); | 
|  | request.length = static_cast<uint32_t>(fs_block_to_device_block(1)); | 
|  | request.vmo_offset = 0; | 
|  | request.dev_offset = fs_block_to_device_block(block); | 
|  |  | 
|  | zx_status_t status = device_->FifoTransaction(&request, 1); | 
|  | if (status != ZX_OK) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | return vmo_.read(data, 0, blobfs::kBlobfsBlockSize); | 
|  | } | 
|  |  | 
|  | zx_status_t FsBlockClient::WriteBlock(uint64_t block, const void* data) { | 
|  | zx_status_t status = vmo_.write(data, 0, blobfs::kBlobfsBlockSize); | 
|  | if (status != ZX_OK) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | block_fifo_request_t request = {}; | 
|  | request.opcode = BLOCKIO_WRITE; | 
|  | request.vmoid = vmoid_.get(); | 
|  | request.length = static_cast<uint32_t>(fs_block_to_device_block(1)); | 
|  | request.vmo_offset = 0; | 
|  | request.dev_offset = fs_block_to_device_block(block); | 
|  |  | 
|  | return device_->FifoTransaction(&request, 1); | 
|  | } | 
|  |  | 
|  | FsBlockClient::FsBlockClient(std::unique_ptr<BlockDevice> device, | 
|  | fuchsia_hardware_block_BlockInfo block_info, zx::vmo vmo, | 
|  | storage::Vmoid vmoid) | 
|  | : device_(std::move(device)), block_info_(block_info), vmo_(std::move(vmo)), | 
|  | vmoid_(std::move(vmoid)) {} | 
|  |  | 
|  | FsBlockClient::~FsBlockClient() { | 
|  | zx_status_t status = device_->BlockDetachVmo(std::move(vmoid_)); | 
|  | ZX_DEBUG_ASSERT(status == ZX_OK); | 
|  | } | 
|  |  | 
|  | uint64_t FsBlockClient::device_blocks_per_blobfs_block() const { | 
|  | return blobfs::kBlobfsBlockSize / block_info_.block_size; | 
|  | } | 
|  |  | 
|  | uint64_t FsBlockClient::fs_block_to_device_block(uint64_t block) const { | 
|  | return block * device_blocks_per_blobfs_block(); | 
|  | } |