1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "nvram/core/persistence.h" 18 19 #include <nvram/messages/io.h> 20 #include <nvram/messages/proto.hpp> 21 22 #include <nvram/core/logger.h> 23 24 namespace nvram { 25 26 namespace { 27 28 // Magic constants that identify encoded |NvramHeader| vs. |NvramSpace| objects. 29 const uint32_t kHeaderMagic = 0x4e5648; // "NVH" in hex 30 const uint32_t kSpaceMagic = 0x4e5653; // "NVS" in hex 31 32 // Encodes an |object| as a protobuf message and writes it to |blob|. Note that 33 // standard protobuf encoding doesn't include information about the overall size 34 // of the encoded object. This is not good enough here, as encoding should 35 // gracefully handle trailing data on decode, e.g. to allow underlying storage 36 // systems that only provide block-granular I/O. 37 // 38 // Not that the code uses |proto::detail::MessageEncoder<Object>::Encode()| 39 // instead of the regular |proto::Encode()| to encode the message. This results 40 // in the message being wrapped in a length-delimited proto field record, so the 41 // length field can be used to determine the actual length of the message. Also, 42 // this gives us the opportunity to encode a magic constant in the field number 43 // bits of the wire tag, thus allowing us to detect situations where we're 44 // attempting to decode a message of wrong type. 45 template <uint32_t magic, typename Object> 46 storage::Status EncodeObject(const Object& object, Blob* blob) { 47 BlobOutputStreamBuffer stream(blob); 48 ProtoWriter writer(&stream); 49 writer.set_field_number(magic); 50 if (!proto::detail::MessageEncoder<Object>::Encode(object, &writer) || 51 !stream.Truncate()) { 52 NVRAM_LOG_ERR("Failed to encode object."); 53 return storage::Status::kStorageError; 54 } 55 return storage::Status::kSuccess; 56 } 57 58 // Decodes a protobuf-encoded |object| from |blob|. It is OK if the provided 59 // |blob| includes trailing data that doesn't belong to the encoded object. 60 // 61 // Note that the code below reads the wire tag to strip the wrapping proto field 62 // record produced by |EncodeObject|. It then checks the magic field number to 63 // make sure we're decoding a message of correct type. Finally, 64 // |proto::detail::MessageDecoder<Object>::Decode()| takes care of reading the 65 // message payload from the proto field record. 66 template <uint32_t magic, typename Object> 67 storage::Status DecodeObject(const Blob& blob, Object* object) { 68 InputStreamBuffer stream(blob.data(), blob.size()); 69 ProtoReader reader(&stream); 70 if (!reader.ReadWireTag() || reader.field_number() != magic || 71 reader.wire_type() != WireType::kLengthDelimited || 72 !proto::detail::MessageDecoder<Object>::Decode(*object, &reader)) { 73 NVRAM_LOG_ERR("Failed to decode object of size %zu.", blob.size()); 74 return storage::Status::kStorageError; 75 } 76 return storage::Status::kSuccess; 77 } 78 79 } // namespace 80 81 template <> struct DescriptorForType<NvramHeader> { 82 static constexpr auto kFields = 83 MakeFieldList(MakeField(1, &NvramHeader::version), 84 MakeField(2, &NvramHeader::flags), 85 MakeField(3, &NvramHeader::allocated_indices), 86 MakeField(4, &NvramHeader::provisional_index)); 87 }; 88 89 template <> struct DescriptorForType<NvramSpace> { 90 static constexpr auto kFields = 91 MakeFieldList(MakeField(1, &NvramSpace::flags), 92 MakeField(2, &NvramSpace::controls), 93 MakeField(3, &NvramSpace::authorization_value), 94 MakeField(4, &NvramSpace::contents)); 95 }; 96 97 namespace persistence { 98 99 storage::Status LoadHeader(NvramHeader* header) { 100 Blob blob; 101 storage::Status status = storage::LoadHeader(&blob); 102 if (status != storage::Status::kSuccess) { 103 return status; 104 } 105 return DecodeObject<kHeaderMagic>(blob, header); 106 } 107 108 storage::Status StoreHeader(const NvramHeader& header) { 109 Blob blob; 110 storage::Status status = EncodeObject<kHeaderMagic>(header, &blob); 111 if (status != storage::Status::kSuccess) { 112 return status; 113 } 114 return storage::StoreHeader(blob); 115 } 116 117 storage::Status LoadSpace(uint32_t index, NvramSpace* space) { 118 Blob blob; 119 storage::Status status = storage::LoadSpace(index, &blob); 120 if (status != storage::Status::kSuccess) { 121 return status; 122 } 123 return DecodeObject<kSpaceMagic>(blob, space); 124 } 125 126 storage::Status StoreSpace(uint32_t index, const NvramSpace& space) { 127 Blob blob; 128 storage::Status status = EncodeObject<kSpaceMagic>(space, &blob); 129 if (status != storage::Status::kSuccess) { 130 return status; 131 } 132 return storage::StoreSpace(index, blob); 133 } 134 135 storage::Status DeleteSpace(uint32_t index) { 136 return storage::DeleteSpace(index); 137 } 138 139 } // namespace persistence 140 } // namespace nvram 141