/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nvram/core/persistence.h" #include #include #include namespace nvram { namespace { // Magic constants that identify encoded |NvramHeader| vs. |NvramSpace| objects. const uint32_t kHeaderMagic = 0x4e5648; // "NVH" in hex const uint32_t kSpaceMagic = 0x4e5653; // "NVS" in hex // Encodes an |object| as a protobuf message and writes it to |blob|. Note that // standard protobuf encoding doesn't include information about the overall size // of the encoded object. This is not good enough here, as encoding should // gracefully handle trailing data on decode, e.g. to allow underlying storage // systems that only provide block-granular I/O. // // Not that the code uses |proto::detail::MessageEncoder::Encode()| // instead of the regular |proto::Encode()| to encode the message. This results // in the message being wrapped in a length-delimited proto field record, so the // length field can be used to determine the actual length of the message. Also, // this gives us the opportunity to encode a magic constant in the field number // bits of the wire tag, thus allowing us to detect situations where we're // attempting to decode a message of wrong type. template storage::Status EncodeObject(const Object& object, Blob* blob) { BlobOutputStreamBuffer stream(blob); ProtoWriter writer(&stream); writer.set_field_number(magic); if (!proto::detail::MessageEncoder::Encode(object, &writer) || !stream.Truncate()) { NVRAM_LOG_ERR("Failed to encode object."); return storage::Status::kStorageError; } return storage::Status::kSuccess; } // Decodes a protobuf-encoded |object| from |blob|. It is OK if the provided // |blob| includes trailing data that doesn't belong to the encoded object. // // Note that the code below reads the wire tag to strip the wrapping proto field // record produced by |EncodeObject|. It then checks the magic field number to // make sure we're decoding a message of correct type. Finally, // |proto::detail::MessageDecoder::Decode()| takes care of reading the // message payload from the proto field record. template storage::Status DecodeObject(const Blob& blob, Object* object) { InputStreamBuffer stream(blob.data(), blob.size()); ProtoReader reader(&stream); if (!reader.ReadWireTag() || reader.field_number() != magic || reader.wire_type() != WireType::kLengthDelimited || !proto::detail::MessageDecoder::Decode(*object, &reader)) { NVRAM_LOG_ERR("Failed to decode object of size %zu.", blob.size()); return storage::Status::kStorageError; } return storage::Status::kSuccess; } } // namespace template <> struct DescriptorForType { static constexpr auto kFields = MakeFieldList(MakeField(1, &NvramHeader::version), MakeField(2, &NvramHeader::flags), MakeField(3, &NvramHeader::allocated_indices), MakeField(4, &NvramHeader::provisional_index)); }; template <> struct DescriptorForType { static constexpr auto kFields = MakeFieldList(MakeField(1, &NvramSpace::flags), MakeField(2, &NvramSpace::controls), MakeField(3, &NvramSpace::authorization_value), MakeField(4, &NvramSpace::contents)); }; namespace persistence { storage::Status LoadHeader(NvramHeader* header) { Blob blob; storage::Status status = storage::LoadHeader(&blob); if (status != storage::Status::kSuccess) { return status; } return DecodeObject(blob, header); } storage::Status StoreHeader(const NvramHeader& header) { Blob blob; storage::Status status = EncodeObject(header, &blob); if (status != storage::Status::kSuccess) { return status; } return storage::StoreHeader(blob); } storage::Status LoadSpace(uint32_t index, NvramSpace* space) { Blob blob; storage::Status status = storage::LoadSpace(index, &blob); if (status != storage::Status::kSuccess) { return status; } return DecodeObject(blob, space); } storage::Status StoreSpace(uint32_t index, const NvramSpace& space) { Blob blob; storage::Status status = EncodeObject(space, &blob); if (status != storage::Status::kSuccess) { return status; } return storage::StoreSpace(index, blob); } storage::Status DeleteSpace(uint32_t index) { return storage::DeleteSpace(index); } } // namespace persistence } // namespace nvram