2020-02-04 15:36:45 +00:00
|
|
|
// Copyright 2020 The Pigweed Authors
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
// https://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 "pw_kvs_private/format.h"
|
|
|
|
|
|
|
|
#include "pw_kvs_private/macros.h"
|
2020-02-05 16:41:06 +00:00
|
|
|
#include "pw_log/log.h"
|
2020-02-04 15:36:45 +00:00
|
|
|
|
|
|
|
namespace pw::kvs {
|
|
|
|
|
|
|
|
using std::byte;
|
|
|
|
using std::string_view;
|
|
|
|
|
2020-02-12 19:26:05 +00:00
|
|
|
Entry::Entry(uint32_t magic,
|
|
|
|
ChecksumAlgorithm* algorithm,
|
|
|
|
string_view key,
|
|
|
|
span<const byte> value,
|
|
|
|
uint16_t value_length_bytes,
|
|
|
|
size_t alignment_bytes,
|
|
|
|
uint32_t key_version)
|
|
|
|
: header_{.magic = magic,
|
|
|
|
.checksum = kNoChecksum,
|
|
|
|
.alignment_units = alignment_bytes_to_units(alignment_bytes),
|
|
|
|
.key_length_bytes = static_cast<uint8_t>(key.size()),
|
|
|
|
.value_length_bytes = value_length_bytes,
|
|
|
|
.key_version = key_version} {
|
2020-02-04 15:36:45 +00:00
|
|
|
if (algorithm != nullptr) {
|
2020-02-12 22:49:14 +00:00
|
|
|
span<const byte> checksum = CalculateChecksum(algorithm, key, value);
|
2020-02-12 19:26:05 +00:00
|
|
|
std::memcpy(&header_.checksum,
|
2020-02-12 22:49:14 +00:00
|
|
|
checksum.data(),
|
2020-02-12 19:26:05 +00:00
|
|
|
std::min(checksum.size(), sizeof(header_.checksum)));
|
2020-02-04 15:36:45 +00:00
|
|
|
}
|
2020-02-06 23:55:45 +00:00
|
|
|
|
|
|
|
// TODO: 0 is an invalid alignment value. There should be an assert for this.
|
|
|
|
// DCHECK_NE(alignment_bytes, 0);
|
2020-02-04 15:36:45 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 19:26:05 +00:00
|
|
|
Status Entry::VerifyChecksum(ChecksumAlgorithm* algorithm,
|
|
|
|
string_view key,
|
|
|
|
span<const byte> value) const {
|
2020-02-04 15:36:45 +00:00
|
|
|
if (algorithm == nullptr) {
|
|
|
|
return checksum() == kNoChecksum ? Status::OK : Status::DATA_LOSS;
|
|
|
|
}
|
|
|
|
CalculateChecksum(algorithm, key, value);
|
|
|
|
return algorithm->Verify(checksum_bytes());
|
|
|
|
}
|
|
|
|
|
2020-02-12 19:26:05 +00:00
|
|
|
Status Entry::VerifyChecksumInFlash(FlashPartition* partition,
|
|
|
|
FlashPartition::Address address,
|
|
|
|
ChecksumAlgorithm* algorithm) const {
|
2020-02-05 01:47:40 +00:00
|
|
|
// Read the entire entry piece-by-piece into a small buffer.
|
2020-02-04 15:36:45 +00:00
|
|
|
// TODO: This read may be unaligned. The partition can handle this, but
|
|
|
|
// consider creating a API that skips the intermediate buffering.
|
|
|
|
byte buffer[32];
|
|
|
|
|
2020-02-05 16:41:06 +00:00
|
|
|
// Read and compare the magic and checksum.
|
|
|
|
TRY(partition->Read(address, checked_data_offset(), buffer));
|
|
|
|
if (std::memcmp(this, buffer, checked_data_offset()) != 0) {
|
|
|
|
static_assert(sizeof(unsigned) >= sizeof(uint32_t));
|
|
|
|
unsigned actual_magic;
|
|
|
|
std::memcpy(&actual_magic, &buffer[0], sizeof(uint32_t));
|
|
|
|
unsigned actual_checksum;
|
|
|
|
std::memcpy(&actual_checksum, &buffer[4], sizeof(uint32_t));
|
|
|
|
|
|
|
|
PW_LOG_ERROR(
|
|
|
|
"Expected: magic=%08x, checksum=%08x; "
|
|
|
|
"Actual: magic=%08x, checksum=%08x",
|
|
|
|
unsigned(magic()),
|
|
|
|
unsigned(checksum()),
|
|
|
|
actual_magic,
|
|
|
|
actual_checksum);
|
|
|
|
|
|
|
|
return Status::DATA_LOSS;
|
|
|
|
}
|
|
|
|
|
2020-02-05 23:30:49 +00:00
|
|
|
if (algorithm == nullptr) {
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
algorithm->Reset();
|
|
|
|
|
2020-02-05 16:41:06 +00:00
|
|
|
// Read and calculate the checksum of the remaining header, key, and value.
|
2020-02-05 01:47:40 +00:00
|
|
|
address += checked_data_offset();
|
2020-02-06 23:55:45 +00:00
|
|
|
size_t bytes_to_read = content_size() - checked_data_offset();
|
2020-02-04 15:36:45 +00:00
|
|
|
|
|
|
|
while (bytes_to_read > 0u) {
|
|
|
|
const size_t read_size = std::min(sizeof(buffer), bytes_to_read);
|
2020-02-05 01:47:40 +00:00
|
|
|
|
2020-02-04 15:36:45 +00:00
|
|
|
TRY(partition->Read(address, read_size, buffer));
|
|
|
|
algorithm->Update(buffer, read_size);
|
2020-02-05 01:47:40 +00:00
|
|
|
|
|
|
|
address += read_size;
|
|
|
|
bytes_to_read -= read_size;
|
2020-02-04 15:36:45 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 22:49:14 +00:00
|
|
|
algorithm->Finish();
|
2020-02-04 15:36:45 +00:00
|
|
|
return algorithm->Verify(checksum_bytes());
|
|
|
|
}
|
|
|
|
|
2020-02-12 19:26:05 +00:00
|
|
|
span<const byte> Entry::CalculateChecksum(ChecksumAlgorithm* algorithm,
|
|
|
|
const string_view key,
|
|
|
|
span<const byte> value) const {
|
2020-02-04 15:36:45 +00:00
|
|
|
algorithm->Reset();
|
2020-02-05 01:47:40 +00:00
|
|
|
algorithm->Update(reinterpret_cast<const byte*>(this) + checked_data_offset(),
|
|
|
|
sizeof(*this) - checked_data_offset());
|
2020-02-04 15:36:45 +00:00
|
|
|
algorithm->Update(as_bytes(span(key)));
|
|
|
|
algorithm->Update(value);
|
2020-02-12 22:49:14 +00:00
|
|
|
|
|
|
|
return algorithm->Finish();
|
2020-02-04 15:36:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace pw::kvs
|