| // Copyright 2017 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/ledger/bin/storage/impl/object_impl.h" |
| |
| #include <lib/fsl/vmo/strings.h> |
| |
| #include <utility> |
| |
| #include "src/ledger/bin/storage/impl/btree/tree_node.h" |
| #include "src/ledger/bin/storage/impl/file_index.h" |
| #include "src/ledger/bin/storage/impl/file_index_generated.h" |
| #include "src/ledger/bin/storage/impl/object_digest.h" |
| #include "src/ledger/bin/storage/impl/object_identifier_encoding.h" |
| #include "src/ledger/bin/storage/public/data_source.h" |
| #include "src/ledger/bin/storage/public/types.h" |
| #include "src/lib/fxl/logging.h" |
| |
| namespace storage { |
| |
| namespace { |
| uint64_t ToFullPages(uint64_t value) { |
| return (value + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)); |
| } |
| } // namespace |
| |
| Status BasePiece::AppendReferences( |
| ObjectReferencesAndPriority* references) const { |
| // Chunks have no references. |
| const auto digest_info = GetObjectDigestInfo(GetIdentifier().object_digest()); |
| if (digest_info.is_chunk()) { |
| return Status::OK; |
| } |
| FXL_DCHECK(digest_info.piece_type == PieceType::INDEX); |
| // The piece is an index: parse it and append its children to references. |
| const FileIndex* file_index; |
| Status status = |
| FileIndexSerialization::ParseFileIndex(GetData(), &file_index); |
| if (status != Status::OK) { |
| return status; |
| } |
| for (const auto* child : *file_index->children()) { |
| ObjectDigest child_digest = |
| ToObjectIdentifier(child->object_identifier()).object_digest(); |
| // References must not contain inline pieces. |
| if (GetObjectDigestInfo(child_digest).is_inlined()) { |
| continue; |
| } |
| // Piece references are always eager. |
| references->emplace(child_digest, KeyPriority::EAGER); |
| } |
| return Status::OK; |
| } |
| |
| InlinePiece::InlinePiece(ObjectIdentifier identifier) |
| : identifier_(std::move(identifier)) {} |
| |
| fxl::StringView InlinePiece::GetData() const { |
| return ExtractObjectDigestData(identifier_.object_digest()); |
| } |
| |
| ObjectIdentifier InlinePiece::GetIdentifier() const { return identifier_; } |
| |
| DataChunkPiece::DataChunkPiece(ObjectIdentifier identifier, |
| std::unique_ptr<DataSource::DataChunk> chunk) |
| : identifier_(std::move(identifier)), chunk_(std::move(chunk)) {} |
| |
| fxl::StringView DataChunkPiece::GetData() const { return chunk_->Get(); } |
| |
| ObjectIdentifier DataChunkPiece::GetIdentifier() const { return identifier_; } |
| |
| LevelDBPiece::LevelDBPiece(ObjectIdentifier identifier, |
| std::unique_ptr<leveldb::Iterator> iterator) |
| : identifier_(std::move(identifier)), iterator_(std::move(iterator)) {} |
| |
| fxl::StringView LevelDBPiece::GetData() const { |
| return convert::ExtendedStringView(iterator_->value()); |
| } |
| |
| ObjectIdentifier LevelDBPiece::GetIdentifier() const { return identifier_; } |
| |
| Status BaseObject::AppendReferences( |
| ObjectReferencesAndPriority* references) const { |
| FXL_DCHECK(references); |
| // Blobs have no references. |
| const auto digest_info = GetObjectDigestInfo(GetIdentifier().object_digest()); |
| if (digest_info.object_type == ObjectType::BLOB) { |
| return Status::OK; |
| } |
| FXL_DCHECK(digest_info.object_type == ObjectType::TREE_NODE); |
| // Parse the object into a TreeNode. |
| std::unique_ptr<const btree::TreeNode> node; |
| Status status = btree::TreeNode::FromObject(*this, &node); |
| if (status != Status::OK) { |
| return status; |
| } |
| node->AppendReferences(references); |
| return Status::OK; |
| } |
| |
| ChunkObject::ChunkObject(std::unique_ptr<const Piece> piece) |
| : piece_(std::move(piece)) { |
| FXL_DCHECK( |
| GetObjectDigestInfo(piece_->GetIdentifier().object_digest()).is_chunk()) |
| << "INDEX piece " << piece_->GetIdentifier() |
| << " cannot be used as an object."; |
| } |
| |
| ObjectIdentifier ChunkObject::GetIdentifier() const { |
| return piece_->GetIdentifier(); |
| } |
| |
| Status ChunkObject::GetData(fxl::StringView* data) const { |
| *data = piece_->GetData(); |
| return Status::OK; |
| } |
| |
| VmoObject::VmoObject(ObjectIdentifier identifier, fsl::SizedVmo vmo) |
| : identifier_(std::move(identifier)), vmo_(std::move(vmo)) {} |
| |
| VmoObject::~VmoObject() { |
| if (vmar_) { |
| vmar_.destroy(); |
| } |
| } |
| |
| ObjectIdentifier VmoObject::GetIdentifier() const { return identifier_; } |
| |
| Status VmoObject::GetData(fxl::StringView* data) const { |
| Status status = Initialize(); |
| if (status != Status::OK) { |
| return status; |
| } |
| *data = data_; |
| return Status::OK; |
| } |
| |
| Status VmoObject::GetVmo(fsl::SizedVmo* vmo) const { |
| zx_status_t zx_status = |
| vmo_.Duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_MAP, vmo); |
| if (zx_status != ZX_OK) { |
| FXL_LOG(ERROR) << "Unable to duplicate a vmo. Status: " << zx_status; |
| return Status::INTERNAL_ERROR; |
| } |
| return Status::OK; |
| } |
| |
| Status VmoObject::Initialize() const { |
| if (initialized_) { |
| return Status::OK; |
| } |
| |
| uintptr_t allocate_address; |
| zx_status_t zx_status = zx::vmar::root_self()->allocate( |
| 0, ToFullPages(vmo_.size()), |
| ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, &vmar_, |
| &allocate_address); |
| if (zx_status != ZX_OK) { |
| FXL_LOG(ERROR) << "Unable to allocate VMAR. Error: " << zx_status; |
| return Status::INTERNAL_ERROR; |
| } |
| |
| char* mapped_address; |
| zx_status = vmar_.map(0, vmo_.vmo(), 0, vmo_.size(), |
| ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC, |
| reinterpret_cast<uintptr_t*>(&mapped_address)); |
| if (zx_status != ZX_OK) { |
| FXL_LOG(ERROR) << "Unable to map VMO. Error: " << zx_status; |
| vmar_.reset(); |
| return Status::INTERNAL_ERROR; |
| } |
| |
| data_ = fxl::StringView(mapped_address, vmo_.size()); |
| initialized_ = true; |
| |
| return Status::OK; |
| } |
| |
| } // namespace storage |