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>
EncodeObject(const Object & object,Blob * blob)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>
DecodeObject(const Blob & blob,Object * 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 
LoadHeader(NvramHeader * header)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 
StoreHeader(const NvramHeader & header)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 
LoadSpace(uint32_t index,NvramSpace * space)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 
StoreSpace(uint32_t index,const NvramSpace & space)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 
DeleteSpace(uint32_t index)135 storage::Status DeleteSpace(uint32_t index) {
136   return storage::DeleteSpace(index);
137 }
138 
139 }  // namespace persistence
140 }  // namespace nvram
141