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