blob: fc01b0c58c1e6a215bf3033d605b532e4029b434 [file] [log] [blame]
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -08001// 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 Montanez28ecccb2020-05-04 15:35:54 -070015#define PW_LOG_MODULE_NAME "KVS"
Wyatt Heplerae222dc2020-10-14 10:46:27 -070016#define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
Armando Montanez28ecccb2020-05-04 15:35:54 -070017
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080018#include "pw_kvs/internal/entry.h"
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -080019
Wyatt Hepler38cfa982020-02-12 11:01:49 -080020#include <cinttypes>
Wyatt Heplere541e072020-02-14 09:10:53 -080021#include <cstring>
Wyatt Hepler38cfa982020-02-12 11:01:49 -080022
David Rogersca592962020-07-01 09:21:54 -070023#include "pw_kvs_private/config.h"
Wyatt Hepler1c830002020-02-05 08:41:06 -080024#include "pw_log/log.h"
David Rogersb6b14b82020-09-11 16:27:27 -070025#include "pw_status/try.h"
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -080026
Wyatt Hepler1fc11042020-02-19 17:17:51 -080027namespace pw::kvs::internal {
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -080028
David Rogersca592962020-07-01 09:21:54 -070029static_assert(
30 kMaxFlashAlignment >= Entry::kMinAlignmentBytes,
31 "Flash alignment is required to be at least Entry::kMinAlignmentBytes");
32
33constexpr size_t kWriteBufferSize =
34 std::max(kMaxFlashAlignment, 4 * Entry::kMinAlignmentBytes);
35
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -080036using std::byte;
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -080037
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080038Status Entry::Read(FlashPartition& partition,
39 Address address,
40 const internal::EntryFormats& formats,
41 Entry* entry) {
Wyatt Heplere541e072020-02-14 09:10:53 -080042 EntryHeader header;
David Rogersc4dc8642020-09-14 10:52:36 -070043 PW_TRY(partition.Read(address, sizeof(header), &header));
Wyatt Heplere541e072020-02-14 09:10:53 -080044
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070045 if (partition.AppearsErased(std::as_bytes(std::span(&header.magic, 1)))) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070046 return Status::NotFound();
Wyatt Heplere541e072020-02-14 09:10:53 -080047 }
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080048 if (header.key_length_bytes > kMaxKeyLength) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070049 return Status::DataLoss();
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080050 }
51
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080052 const EntryFormat* format = formats.Find(header.magic);
53 if (format == nullptr) {
David Rogers39d2a032020-04-10 15:00:35 -070054 PW_LOG_ERROR("Found corrupt magic: %" PRIx32 " at address %u",
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080055 header.magic,
David Rogers39d2a032020-04-10 15:00:35 -070056 unsigned(address));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070057 return Status::DataLoss();
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080058 }
59
60 *entry = Entry(&partition, address, *format, header);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080061 return OkStatus();
Wyatt Heplere541e072020-02-14 09:10:53 -080062}
63
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080064Status Entry::ReadKey(FlashPartition& partition,
65 Address address,
66 size_t key_length,
67 char* key) {
Wyatt Heplere541e072020-02-14 09:10:53 -080068 if (key_length == 0u || key_length > kMaxKeyLength) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070069 return Status::DataLoss();
Wyatt Heplere541e072020-02-14 09:10:53 -080070 }
71
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080072 return partition.Read(address + sizeof(EntryHeader), key_length, key)
73 .status();
Wyatt Heplere541e072020-02-14 09:10:53 -080074}
75
76Entry::Entry(FlashPartition& partition,
77 Address address,
Wyatt Hepler88adfe82020-02-20 19:33:27 -080078 const EntryFormat& format,
Rob Olivere64daf42020-11-24 11:50:03 -050079 Key key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070080 std::span<const byte> value,
Wyatt Heplere541e072020-02-14 09:10:53 -080081 uint16_t value_size_bytes,
Wyatt Hepler1fc11042020-02-19 17:17:51 -080082 uint32_t transaction_id)
Wyatt Heplere541e072020-02-14 09:10:53 -080083 : Entry(&partition,
84 address,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080085 format,
Wyatt Hepler88adfe82020-02-20 19:33:27 -080086 {.magic = format.magic,
Wyatt Heplere541e072020-02-14 09:10:53 -080087 .checksum = 0,
Wyatt Hepler7465be32020-02-21 15:30:53 -080088 .alignment_units =
89 alignment_bytes_to_units(partition.alignment_bytes()),
Wyatt Heplere541e072020-02-14 09:10:53 -080090 .key_length_bytes = static_cast<uint8_t>(key.size()),
91 .value_size_bytes = value_size_bytes,
Wyatt Hepler1fc11042020-02-19 17:17:51 -080092 .transaction_id = transaction_id}) {
Wyatt Hepler1d594562020-03-10 18:26:02 -070093 if (checksum_algo_ != nullptr) {
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070094 std::span<const byte> checksum = CalculateChecksum(key, value);
Wyatt Hepler30a52152020-02-12 11:26:05 -080095 std::memcpy(&header_.checksum,
Wyatt Heplerc656af22020-02-12 14:49:14 -080096 checksum.data(),
Wyatt Hepler30a52152020-02-12 11:26:05 -080097 std::min(checksum.size(), sizeof(header_.checksum)));
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -080098 }
99}
100
Rob Olivere64daf42020-11-24 11:50:03 -0500101StatusWithSize Entry::Write(Key key, std::span<const byte> value) const {
Wyatt Heplere541e072020-02-14 09:10:53 -0800102 FlashPartition::Output flash(partition(), address_);
David Rogersca592962020-07-01 09:21:54 -0700103 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 Heplere541e072020-02-14 09:10:53 -0800108}
109
Wyatt Hepler1d594562020-03-10 18:26:02 -0700110Status 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
125StatusWithSize Entry::Copy(Address new_address) const {
David Rogers31b358b2020-04-15 05:00:50 -0700126 PW_LOG_DEBUG("Copying entry from %u to %u as ID %" PRIu32,
Wyatt Hepler1d594562020-03-10 18:26:02 -0700127 unsigned(address()),
128 unsigned(new_address),
129 transaction_id());
130
131 FlashPartition::Output output(partition(), new_address);
David Rogersca592962020-07-01 09:21:54 -0700132 AlignedWriterBuffer<kWriteBufferSize> writer(alignment_bytes(), output);
Wyatt Hepler1d594562020-03-10 18:26:02 -0700133
134 // Use this object's header rather than the header in flash of flash, since
135 // this Entry may have been updated.
David Rogersc4dc8642020-09-14 10:52:36 -0700136 PW_TRY_WITH_SIZE(writer.Write(&header_, sizeof(header_)));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700137
138 // Write only the key and value from the original entry.
139 FlashPartition::Input input(partition(), address() + sizeof(EntryHeader));
David Rogersc4dc8642020-09-14 10:52:36 -0700140 PW_TRY_WITH_SIZE(writer.Write(input, key_length() + value_size()));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700141 return writer.Flush();
142}
143
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700144StatusWithSize Entry::ReadValue(std::span<byte> buffer,
145 size_t offset_bytes) const {
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800146 if (offset_bytes > value_size()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700147 return StatusWithSize::OutOfRange();
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800148 }
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 Rogersc4dc8642020-09-14 10:52:36 -0700156 PW_TRY_WITH_SIZE(result);
Wyatt Heplere541e072020-02-14 09:10:53 -0800157
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800158 if (read_size != remaining_bytes) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700159 return StatusWithSize::ResourceExhausted(read_size);
Wyatt Heplere541e072020-02-14 09:10:53 -0800160 }
161 return StatusWithSize(read_size);
162}
163
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700164Status Entry::ValueMatches(std::span<const std::byte> value) const {
David Rogersc0104462020-05-08 15:50:23 -0700165 if (value_size() != value.size_bytes()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700166 return Status::NotFound();
David Rogersc0104462020-05-08 15:50:23 -0700167 }
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 Rogersc4dc8642020-09-14 10:52:36 -0700176 PW_TRY(partition_->Read(address, std::span(buffer).first(read_size)));
David Rogersc0104462020-05-08 15:50:23 -0700177
178 if (std::memcmp(buffer.data(), value_ptr, read_size) != 0) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700179 return Status::NotFound();
David Rogersc0104462020-05-08 15:50:23 -0700180 }
181
182 address += read_size;
183 value_ptr += read_size;
184 }
185
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800186 return OkStatus();
David Rogersc0104462020-05-08 15:50:23 -0700187}
188
Rob Olivere64daf42020-11-24 11:50:03 -0500189Status Entry::VerifyChecksum(Key key, std::span<const byte> value) const {
Wyatt Hepler1d594562020-03-10 18:26:02 -0700190 if (checksum_algo_ == nullptr) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800191 return header_.checksum == 0 ? OkStatus() : Status::DataLoss();
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800192 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800193 CalculateChecksum(key, value);
Wyatt Hepler1d594562020-03-10 18:26:02 -0700194 return checksum_algo_->Verify(checksum_bytes());
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800195}
196
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800197Status Entry::VerifyChecksumInFlash() const {
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800198 // 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 Hepler6e3a83b2020-02-04 07:36:45 -0800204
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800205 size_t bytes_to_read = size();
206 size_t read_size = std::min(sizeof(buffer), bytes_to_read);
Wyatt Hepler1c830002020-02-05 08:41:06 -0800207
Wyatt Heplere541e072020-02-14 09:10:53 -0800208 Address read_address = address_;
209
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800210 // Read the first chunk, which includes the header, and compare the checksum.
David Rogersc4dc8642020-09-14 10:52:36 -0700211 PW_TRY(partition().Read(read_address, read_size, buffer));
Wyatt Hepler1c830002020-02-05 08:41:06 -0800212
Wyatt Hepler1d594562020-03-10 18:26:02 -0700213 if (header_to_verify.checksum != header_.checksum) {
David Rogers39d2a032020-04-10 15:00:35 -0700214 PW_LOG_ERROR("Expected checksum 0x%08" PRIx32 ", found 0x%08" PRIx32,
Wyatt Hepler1d594562020-03-10 18:26:02 -0700215 header_.checksum,
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800216 header_to_verify.checksum);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700217 return Status::DataLoss();
Wyatt Hepler1c830002020-02-05 08:41:06 -0800218 }
219
Wyatt Hepler1d594562020-03-10 18:26:02 -0700220 if (checksum_algo_ == nullptr) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800221 return header_.checksum == 0 ? OkStatus() : Status::DataLoss();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800222 }
223
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800224 // The checksum is calculated as if the header's checksum field were 0.
225 header_to_verify.checksum = 0;
226
Wyatt Hepler1d594562020-03-10 18:26:02 -0700227 checksum_algo_->Reset();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800228
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800229 while (true) {
230 // Add the chunk in the buffer to the checksum.
Wyatt Hepler1d594562020-03-10 18:26:02 -0700231 checksum_algo_->Update(buffer, read_size);
Wyatt Hepler0a223582020-02-04 17:47:40 -0800232
Wyatt Hepler0a223582020-02-04 17:47:40 -0800233 bytes_to_read -= read_size;
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800234 if (bytes_to_read == 0u) {
235 break;
236 }
237
238 // Read the next chunk into the buffer.
Wyatt Heplere541e072020-02-14 09:10:53 -0800239 read_address += read_size;
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800240 read_size = std::min(sizeof(buffer), bytes_to_read);
David Rogersc4dc8642020-09-14 10:52:36 -0700241 PW_TRY(partition().Read(read_address, read_size, buffer));
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800242 }
243
Wyatt Hepler1d594562020-03-10 18:26:02 -0700244 checksum_algo_->Finish();
245 return checksum_algo_->Verify(checksum_bytes());
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800246}
247
Wyatt Hepler1d594562020-03-10 18:26:02 -0700248void Entry::DebugLog() const {
249 PW_LOG_DEBUG("Entry [%s]: ", deleted() ? "tombstone" : "present");
David Rogers9fc78b82020-06-12 13:56:12 -0700250 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 Heplere541e072020-02-14 09:10:53 -0800258}
259
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700260std::span<const byte> Entry::CalculateChecksum(
Rob Olivere64daf42020-11-24 11:50:03 -0500261 const Key key, std::span<const byte> value) const {
Wyatt Hepler1d594562020-03-10 18:26:02 -0700262 checksum_algo_->Reset();
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800263
264 {
265 EntryHeader header_for_checksum = header_;
266 header_for_checksum.checksum = 0;
267
Wyatt Hepler1d594562020-03-10 18:26:02 -0700268 checksum_algo_->Update(&header_for_checksum, sizeof(header_for_checksum));
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700269 checksum_algo_->Update(std::as_bytes(std::span(key)));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700270 checksum_algo_->Update(value);
Wyatt Hepler38cfa982020-02-12 11:01:49 -0800271 }
272
Wyatt Hepler50070492020-04-23 21:50:34 -0700273 AddPaddingBytesToChecksum();
Wyatt Heplerc656af22020-02-12 14:49:14 -0800274
Wyatt Hepler1d594562020-03-10 18:26:02 -0700275 return checksum_algo_->Finish();
276}
277
278Status Entry::CalculateChecksumFromFlash() {
279 header_.checksum = 0;
280
281 if (checksum_algo_ == nullptr) {
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800282 return OkStatus();
Wyatt Hepler1d594562020-03-10 18:26:02 -0700283 }
284
285 checksum_algo_->Reset();
286 checksum_algo_->Update(&header_, sizeof(header_));
287
288 Address address = address_ + sizeof(EntryHeader);
Wyatt Hepler50070492020-04-23 21:50:34 -0700289 // To handle alignment changes, do not read the padding. The padding is added
290 // after checksumming the key and value from flash.
Wyatt Hepler1d594562020-03-10 18:26:02 -0700291 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 Rogersc4dc8642020-09-14 10:52:36 -0700296 PW_TRY(partition_->Read(address, std::span(buffer).first(read_size)));
Wyatt Hepler1d594562020-03-10 18:26:02 -0700297
298 checksum_algo_->Update(buffer.data(), read_size);
299 address += read_size;
300 }
301
Wyatt Hepler50070492020-04-23 21:50:34 -0700302 AddPaddingBytesToChecksum();
303
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700304 std::span checksum = checksum_algo_->Finish();
Wyatt Hepler1d594562020-03-10 18:26:02 -0700305 std::memcpy(&header_.checksum,
306 checksum.data(),
307 std::min(checksum.size(), sizeof(header_.checksum)));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800308 return OkStatus();
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800309}
310
Wyatt Hepler50070492020-04-23 21:50:34 -0700311void 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 Hepler1fc11042020-02-19 17:17:51 -0800322} // namespace pw::kvs::internal