Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 1 | // Copyright 2020 The Pigweed Authors |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 4 | // use this file except in compliance with the License. You may obtain a copy of |
| 5 | // the License at |
| 6 | // |
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 12 | // License for the specific language governing permissions and limitations under |
| 13 | // the License. |
| 14 | |
Armando Montanez | 28ecccb | 2020-05-04 15:35:54 -0700 | [diff] [blame] | 15 | #define PW_LOG_MODULE_NAME "KVS" |
Wyatt Hepler | ae222dc | 2020-10-14 10:46:27 -0700 | [diff] [blame] | 16 | #define PW_LOG_LEVEL PW_KVS_LOG_LEVEL |
Armando Montanez | 28ecccb | 2020-05-04 15:35:54 -0700 | [diff] [blame] | 17 | |
Wyatt Hepler | bdd8e5a | 2020-02-20 19:27:26 -0800 | [diff] [blame] | 18 | #include "pw_kvs/internal/entry.h" |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 19 | |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 20 | #include <cinttypes> |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 21 | #include <cstring> |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 22 | |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 23 | #include "pw_kvs_private/config.h" |
Wyatt Hepler | 1c83000 | 2020-02-05 08:41:06 -0800 | [diff] [blame] | 24 | #include "pw_log/log.h" |
David Rogers | b6b14b8 | 2020-09-11 16:27:27 -0700 | [diff] [blame] | 25 | #include "pw_status/try.h" |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 26 | |
Wyatt Hepler | 1fc1104 | 2020-02-19 17:17:51 -0800 | [diff] [blame] | 27 | namespace pw::kvs::internal { |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 28 | |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 29 | static_assert( |
| 30 | kMaxFlashAlignment >= Entry::kMinAlignmentBytes, |
| 31 | "Flash alignment is required to be at least Entry::kMinAlignmentBytes"); |
| 32 | |
| 33 | constexpr size_t kWriteBufferSize = |
| 34 | std::max(kMaxFlashAlignment, 4 * Entry::kMinAlignmentBytes); |
| 35 | |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 36 | using std::byte; |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 37 | |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 38 | Status Entry::Read(FlashPartition& partition, |
| 39 | Address address, |
| 40 | const internal::EntryFormats& formats, |
| 41 | Entry* entry) { |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 42 | EntryHeader header; |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 43 | PW_TRY(partition.Read(address, sizeof(header), &header)); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 44 | |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 45 | if (partition.AppearsErased(std::as_bytes(std::span(&header.magic, 1)))) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 46 | return Status::NotFound(); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 47 | } |
Wyatt Hepler | a00d1ef | 2020-02-14 14:31:26 -0800 | [diff] [blame] | 48 | if (header.key_length_bytes > kMaxKeyLength) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 49 | return Status::DataLoss(); |
Wyatt Hepler | a00d1ef | 2020-02-14 14:31:26 -0800 | [diff] [blame] | 50 | } |
| 51 | |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 52 | const EntryFormat* format = formats.Find(header.magic); |
| 53 | if (format == nullptr) { |
David Rogers | 39d2a03 | 2020-04-10 15:00:35 -0700 | [diff] [blame] | 54 | PW_LOG_ERROR("Found corrupt magic: %" PRIx32 " at address %u", |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 55 | header.magic, |
David Rogers | 39d2a03 | 2020-04-10 15:00:35 -0700 | [diff] [blame] | 56 | unsigned(address)); |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 57 | return Status::DataLoss(); |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | *entry = Entry(&partition, address, *format, header); |
Wyatt Hepler | 1b3da3a | 2021-01-07 13:26:57 -0800 | [diff] [blame] | 61 | return OkStatus(); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 62 | } |
| 63 | |
Wyatt Hepler | a00d1ef | 2020-02-14 14:31:26 -0800 | [diff] [blame] | 64 | Status Entry::ReadKey(FlashPartition& partition, |
| 65 | Address address, |
| 66 | size_t key_length, |
| 67 | char* key) { |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 68 | if (key_length == 0u || key_length > kMaxKeyLength) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 69 | return Status::DataLoss(); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 70 | } |
| 71 | |
Wyatt Hepler | a00d1ef | 2020-02-14 14:31:26 -0800 | [diff] [blame] | 72 | return partition.Read(address + sizeof(EntryHeader), key_length, key) |
| 73 | .status(); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 74 | } |
| 75 | |
| 76 | Entry::Entry(FlashPartition& partition, |
| 77 | Address address, |
Wyatt Hepler | 88adfe8 | 2020-02-20 19:33:27 -0800 | [diff] [blame] | 78 | const EntryFormat& format, |
Rob Oliver | e64daf4 | 2020-11-24 11:50:03 -0500 | [diff] [blame] | 79 | Key key, |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 80 | std::span<const byte> value, |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 81 | uint16_t value_size_bytes, |
Wyatt Hepler | 1fc1104 | 2020-02-19 17:17:51 -0800 | [diff] [blame] | 82 | uint32_t transaction_id) |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 83 | : Entry(&partition, |
| 84 | address, |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 85 | format, |
Wyatt Hepler | 88adfe8 | 2020-02-20 19:33:27 -0800 | [diff] [blame] | 86 | {.magic = format.magic, |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 87 | .checksum = 0, |
Wyatt Hepler | 7465be3 | 2020-02-21 15:30:53 -0800 | [diff] [blame] | 88 | .alignment_units = |
| 89 | alignment_bytes_to_units(partition.alignment_bytes()), |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 90 | .key_length_bytes = static_cast<uint8_t>(key.size()), |
| 91 | .value_size_bytes = value_size_bytes, |
Wyatt Hepler | 1fc1104 | 2020-02-19 17:17:51 -0800 | [diff] [blame] | 92 | .transaction_id = transaction_id}) { |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 93 | if (checksum_algo_ != nullptr) { |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 94 | std::span<const byte> checksum = CalculateChecksum(key, value); |
Wyatt Hepler | 30a5215 | 2020-02-12 11:26:05 -0800 | [diff] [blame] | 95 | std::memcpy(&header_.checksum, |
Wyatt Hepler | c656af2 | 2020-02-12 14:49:14 -0800 | [diff] [blame] | 96 | checksum.data(), |
Wyatt Hepler | 30a5215 | 2020-02-12 11:26:05 -0800 | [diff] [blame] | 97 | std::min(checksum.size(), sizeof(header_.checksum))); |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 98 | } |
| 99 | } |
| 100 | |
Rob Oliver | e64daf4 | 2020-11-24 11:50:03 -0500 | [diff] [blame] | 101 | StatusWithSize Entry::Write(Key key, std::span<const byte> value) const { |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 102 | FlashPartition::Output flash(partition(), address_); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 103 | return AlignedWrite<kWriteBufferSize>(flash, |
| 104 | alignment_bytes(), |
| 105 | {std::as_bytes(std::span(&header_, 1)), |
| 106 | std::as_bytes(std::span(key)), |
| 107 | value}); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 108 | } |
| 109 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 110 | Status Entry::Update(const EntryFormat& new_format, |
| 111 | uint32_t new_transaction_id) { |
| 112 | checksum_algo_ = new_format.checksum; |
| 113 | header_.magic = new_format.magic; |
| 114 | header_.alignment_units = |
| 115 | alignment_bytes_to_units(partition_->alignment_bytes()); |
| 116 | header_.transaction_id = new_transaction_id; |
| 117 | |
| 118 | // If we could write the header last, we could avoid reading the entry twice |
| 119 | // when moving an entry. However, to support alignments greater than the |
| 120 | // header size, we first read the entire value to calculate the new checksum, |
| 121 | // then write the full entry in WriteFrom. |
| 122 | return CalculateChecksumFromFlash(); |
| 123 | } |
| 124 | |
| 125 | StatusWithSize Entry::Copy(Address new_address) const { |
David Rogers | 31b358b | 2020-04-15 05:00:50 -0700 | [diff] [blame] | 126 | PW_LOG_DEBUG("Copying entry from %u to %u as ID %" PRIu32, |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 127 | unsigned(address()), |
| 128 | unsigned(new_address), |
| 129 | transaction_id()); |
| 130 | |
| 131 | FlashPartition::Output output(partition(), new_address); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 132 | AlignedWriterBuffer<kWriteBufferSize> writer(alignment_bytes(), output); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 133 | |
| 134 | // Use this object's header rather than the header in flash of flash, since |
| 135 | // this Entry may have been updated. |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 136 | PW_TRY_WITH_SIZE(writer.Write(&header_, sizeof(header_))); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 137 | |
| 138 | // Write only the key and value from the original entry. |
| 139 | FlashPartition::Input input(partition(), address() + sizeof(EntryHeader)); |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 140 | PW_TRY_WITH_SIZE(writer.Write(input, key_length() + value_size())); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 141 | return writer.Flush(); |
| 142 | } |
| 143 | |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 144 | StatusWithSize Entry::ReadValue(std::span<byte> buffer, |
| 145 | size_t offset_bytes) const { |
Wyatt Hepler | 5f6efc0 | 2020-02-18 16:54:31 -0800 | [diff] [blame] | 146 | if (offset_bytes > value_size()) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 147 | return StatusWithSize::OutOfRange(); |
Wyatt Hepler | 5f6efc0 | 2020-02-18 16:54:31 -0800 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | const size_t remaining_bytes = value_size() - offset_bytes; |
| 151 | const size_t read_size = std::min(buffer.size(), remaining_bytes); |
| 152 | |
| 153 | StatusWithSize result = partition().Read( |
| 154 | address_ + sizeof(EntryHeader) + key_length() + offset_bytes, |
| 155 | buffer.subspan(0, read_size)); |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 156 | PW_TRY_WITH_SIZE(result); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 157 | |
Wyatt Hepler | 5f6efc0 | 2020-02-18 16:54:31 -0800 | [diff] [blame] | 158 | if (read_size != remaining_bytes) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 159 | return StatusWithSize::ResourceExhausted(read_size); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 160 | } |
| 161 | return StatusWithSize(read_size); |
| 162 | } |
| 163 | |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 164 | Status Entry::ValueMatches(std::span<const std::byte> value) const { |
David Rogers | c010446 | 2020-05-08 15:50:23 -0700 | [diff] [blame] | 165 | if (value_size() != value.size_bytes()) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 166 | return Status::NotFound(); |
David Rogers | c010446 | 2020-05-08 15:50:23 -0700 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | Address address = address_ + sizeof(EntryHeader) + key_length(); |
| 170 | Address end = address + value_size(); |
| 171 | const std::byte* value_ptr = value.data(); |
| 172 | |
| 173 | std::array<std::byte, 2 * kMinAlignmentBytes> buffer; |
| 174 | while (address < end) { |
| 175 | const size_t read_size = std::min(size_t(end - address), buffer.size()); |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 176 | PW_TRY(partition_->Read(address, std::span(buffer).first(read_size))); |
David Rogers | c010446 | 2020-05-08 15:50:23 -0700 | [diff] [blame] | 177 | |
| 178 | if (std::memcmp(buffer.data(), value_ptr, read_size) != 0) { |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 179 | return Status::NotFound(); |
David Rogers | c010446 | 2020-05-08 15:50:23 -0700 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | address += read_size; |
| 183 | value_ptr += read_size; |
| 184 | } |
| 185 | |
Wyatt Hepler | 1b3da3a | 2021-01-07 13:26:57 -0800 | [diff] [blame] | 186 | return OkStatus(); |
David Rogers | c010446 | 2020-05-08 15:50:23 -0700 | [diff] [blame] | 187 | } |
| 188 | |
Rob Oliver | e64daf4 | 2020-11-24 11:50:03 -0500 | [diff] [blame] | 189 | Status Entry::VerifyChecksum(Key key, std::span<const byte> value) const { |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 190 | if (checksum_algo_ == nullptr) { |
Wyatt Hepler | 1b3da3a | 2021-01-07 13:26:57 -0800 | [diff] [blame] | 191 | return header_.checksum == 0 ? OkStatus() : Status::DataLoss(); |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 192 | } |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 193 | CalculateChecksum(key, value); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 194 | return checksum_algo_->Verify(checksum_bytes()); |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 195 | } |
| 196 | |
Wyatt Hepler | 22d0d9f | 2020-03-05 14:57:11 -0800 | [diff] [blame] | 197 | Status Entry::VerifyChecksumInFlash() const { |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 198 | // Read the entire entry piece-by-piece into a small buffer. If the entry is |
| 199 | // 32 B or less, only one read is required. |
| 200 | union { |
| 201 | EntryHeader header_to_verify; |
| 202 | byte buffer[sizeof(EntryHeader) * 2]; |
| 203 | }; |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 204 | |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 205 | size_t bytes_to_read = size(); |
| 206 | size_t read_size = std::min(sizeof(buffer), bytes_to_read); |
Wyatt Hepler | 1c83000 | 2020-02-05 08:41:06 -0800 | [diff] [blame] | 207 | |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 208 | Address read_address = address_; |
| 209 | |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 210 | // Read the first chunk, which includes the header, and compare the checksum. |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 211 | PW_TRY(partition().Read(read_address, read_size, buffer)); |
Wyatt Hepler | 1c83000 | 2020-02-05 08:41:06 -0800 | [diff] [blame] | 212 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 213 | if (header_to_verify.checksum != header_.checksum) { |
David Rogers | 39d2a03 | 2020-04-10 15:00:35 -0700 | [diff] [blame] | 214 | PW_LOG_ERROR("Expected checksum 0x%08" PRIx32 ", found 0x%08" PRIx32, |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 215 | header_.checksum, |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 216 | header_to_verify.checksum); |
Wyatt Hepler | d78f7c6 | 2020-09-28 14:27:32 -0700 | [diff] [blame] | 217 | return Status::DataLoss(); |
Wyatt Hepler | 1c83000 | 2020-02-05 08:41:06 -0800 | [diff] [blame] | 218 | } |
| 219 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 220 | if (checksum_algo_ == nullptr) { |
Wyatt Hepler | 1b3da3a | 2021-01-07 13:26:57 -0800 | [diff] [blame] | 221 | return header_.checksum == 0 ? OkStatus() : Status::DataLoss(); |
Wyatt Hepler | 6c24c06 | 2020-02-05 15:30:49 -0800 | [diff] [blame] | 222 | } |
| 223 | |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 224 | // The checksum is calculated as if the header's checksum field were 0. |
| 225 | header_to_verify.checksum = 0; |
| 226 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 227 | checksum_algo_->Reset(); |
Wyatt Hepler | 6c24c06 | 2020-02-05 15:30:49 -0800 | [diff] [blame] | 228 | |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 229 | while (true) { |
| 230 | // Add the chunk in the buffer to the checksum. |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 231 | checksum_algo_->Update(buffer, read_size); |
Wyatt Hepler | 0a22358 | 2020-02-04 17:47:40 -0800 | [diff] [blame] | 232 | |
Wyatt Hepler | 0a22358 | 2020-02-04 17:47:40 -0800 | [diff] [blame] | 233 | bytes_to_read -= read_size; |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 234 | if (bytes_to_read == 0u) { |
| 235 | break; |
| 236 | } |
| 237 | |
| 238 | // Read the next chunk into the buffer. |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 239 | read_address += read_size; |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 240 | read_size = std::min(sizeof(buffer), bytes_to_read); |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 241 | PW_TRY(partition().Read(read_address, read_size, buffer)); |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 242 | } |
| 243 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 244 | checksum_algo_->Finish(); |
| 245 | return checksum_algo_->Verify(checksum_bytes()); |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 246 | } |
| 247 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 248 | void Entry::DebugLog() const { |
| 249 | PW_LOG_DEBUG("Entry [%s]: ", deleted() ? "tombstone" : "present"); |
David Rogers | 9fc78b8 | 2020-06-12 13:56:12 -0700 | [diff] [blame] | 250 | PW_LOG_DEBUG(" Address = 0x%x", unsigned(address_)); |
| 251 | PW_LOG_DEBUG(" Transaction = %u", unsigned(transaction_id())); |
| 252 | PW_LOG_DEBUG(" Magic = 0x%x", unsigned(magic())); |
| 253 | PW_LOG_DEBUG(" Checksum = 0x%x", unsigned(header_.checksum)); |
| 254 | PW_LOG_DEBUG(" Key length = 0x%x", unsigned(key_length())); |
| 255 | PW_LOG_DEBUG(" Value length = 0x%x", unsigned(value_size())); |
| 256 | PW_LOG_DEBUG(" Entry size = 0x%x", unsigned(size())); |
| 257 | PW_LOG_DEBUG(" Alignment = 0x%x", unsigned(alignment_bytes())); |
Wyatt Hepler | e541e07 | 2020-02-14 09:10:53 -0800 | [diff] [blame] | 258 | } |
| 259 | |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 260 | std::span<const byte> Entry::CalculateChecksum( |
Rob Oliver | e64daf4 | 2020-11-24 11:50:03 -0500 | [diff] [blame] | 261 | const Key key, std::span<const byte> value) const { |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 262 | checksum_algo_->Reset(); |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 263 | |
| 264 | { |
| 265 | EntryHeader header_for_checksum = header_; |
| 266 | header_for_checksum.checksum = 0; |
| 267 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 268 | checksum_algo_->Update(&header_for_checksum, sizeof(header_for_checksum)); |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 269 | checksum_algo_->Update(std::as_bytes(std::span(key))); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 270 | checksum_algo_->Update(value); |
Wyatt Hepler | 38cfa98 | 2020-02-12 11:01:49 -0800 | [diff] [blame] | 271 | } |
| 272 | |
Wyatt Hepler | 5007049 | 2020-04-23 21:50:34 -0700 | [diff] [blame] | 273 | AddPaddingBytesToChecksum(); |
Wyatt Hepler | c656af2 | 2020-02-12 14:49:14 -0800 | [diff] [blame] | 274 | |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 275 | return checksum_algo_->Finish(); |
| 276 | } |
| 277 | |
| 278 | Status Entry::CalculateChecksumFromFlash() { |
| 279 | header_.checksum = 0; |
| 280 | |
| 281 | if (checksum_algo_ == nullptr) { |
Wyatt Hepler | 1b3da3a | 2021-01-07 13:26:57 -0800 | [diff] [blame] | 282 | return OkStatus(); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | checksum_algo_->Reset(); |
| 286 | checksum_algo_->Update(&header_, sizeof(header_)); |
| 287 | |
| 288 | Address address = address_ + sizeof(EntryHeader); |
Wyatt Hepler | 5007049 | 2020-04-23 21:50:34 -0700 | [diff] [blame] | 289 | // To handle alignment changes, do not read the padding. The padding is added |
| 290 | // after checksumming the key and value from flash. |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 291 | const Address end = address_ + content_size(); |
| 292 | |
| 293 | std::array<std::byte, 2 * kMinAlignmentBytes> buffer; |
| 294 | while (address < end) { |
| 295 | const size_t read_size = std::min(size_t(end - address), buffer.size()); |
David Rogers | c4dc864 | 2020-09-14 10:52:36 -0700 | [diff] [blame] | 296 | PW_TRY(partition_->Read(address, std::span(buffer).first(read_size))); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 297 | |
| 298 | checksum_algo_->Update(buffer.data(), read_size); |
| 299 | address += read_size; |
| 300 | } |
| 301 | |
Wyatt Hepler | 5007049 | 2020-04-23 21:50:34 -0700 | [diff] [blame] | 302 | AddPaddingBytesToChecksum(); |
| 303 | |
Wyatt Hepler | e2cbadf | 2020-06-22 11:21:45 -0700 | [diff] [blame] | 304 | std::span checksum = checksum_algo_->Finish(); |
Wyatt Hepler | 1d59456 | 2020-03-10 18:26:02 -0700 | [diff] [blame] | 305 | std::memcpy(&header_.checksum, |
| 306 | checksum.data(), |
| 307 | std::min(checksum.size(), sizeof(header_.checksum))); |
Wyatt Hepler | 1b3da3a | 2021-01-07 13:26:57 -0800 | [diff] [blame] | 308 | return OkStatus(); |
Wyatt Hepler | 6e3a83b | 2020-02-04 07:36:45 -0800 | [diff] [blame] | 309 | } |
| 310 | |
Wyatt Hepler | 5007049 | 2020-04-23 21:50:34 -0700 | [diff] [blame] | 311 | void Entry::AddPaddingBytesToChecksum() const { |
| 312 | constexpr byte padding[kMinAlignmentBytes - 1] = {}; |
| 313 | size_t padding_to_add = Padding(content_size(), alignment_bytes()); |
| 314 | |
| 315 | while (padding_to_add != 0u) { |
| 316 | const size_t chunk_size = std::min(padding_to_add, sizeof(padding)); |
| 317 | checksum_algo_->Update(padding, chunk_size); |
| 318 | padding_to_add -= chunk_size; |
| 319 | } |
| 320 | } |
| 321 | |
Wyatt Hepler | 1fc1104 | 2020-02-19 17:17:51 -0800 | [diff] [blame] | 322 | } // namespace pw::kvs::internal |