141 lines
5.1 KiB
C++
141 lines
5.1 KiB
C++
/*
|
|
* 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 <nvram/messages/io.h>
|
|
#include <nvram/messages/proto.hpp>
|
|
|
|
#include <nvram/core/logger.h>
|
|
|
|
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<Object>::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 <uint32_t magic, typename Object>
|
|
storage::Status EncodeObject(const Object& object, Blob* blob) {
|
|
BlobOutputStreamBuffer stream(blob);
|
|
ProtoWriter writer(&stream);
|
|
writer.set_field_number(magic);
|
|
if (!proto::detail::MessageEncoder<Object>::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<Object>::Decode()| takes care of reading the
|
|
// message payload from the proto field record.
|
|
template <uint32_t magic, typename Object>
|
|
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<Object>::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<NvramHeader> {
|
|
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<NvramSpace> {
|
|
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<kHeaderMagic>(blob, header);
|
|
}
|
|
|
|
storage::Status StoreHeader(const NvramHeader& header) {
|
|
Blob blob;
|
|
storage::Status status = EncodeObject<kHeaderMagic>(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<kSpaceMagic>(blob, space);
|
|
}
|
|
|
|
storage::Status StoreSpace(uint32_t index, const NvramSpace& space) {
|
|
Blob blob;
|
|
storage::Status status = EncodeObject<kSpaceMagic>(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
|