blob: 06d1489b6d8e21bc819ffe98d2aa9414fa9a7906 [file] [log] [blame]
Sen Jiang57f91802017-11-14 17:42:13 -08001//
2// Copyright (C) 2018 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/payload_consumer/verity_writer_android.h"
18
19#include <fcntl.h>
20
21#include <algorithm>
22#include <memory>
23
24#include <base/logging.h>
25#include <base/posix/eintr_wrapper.h>
Sen Jianga778e5b2018-09-13 11:42:56 -070026#include <fec/ecc.h>
27extern "C" {
28#include <fec.h>
29}
Sen Jiang57f91802017-11-14 17:42:13 -080030
31#include "update_engine/common/utils.h"
32
33namespace chromeos_update_engine {
34
35namespace verity_writer {
36std::unique_ptr<VerityWriterInterface> CreateVerityWriter() {
37 return std::make_unique<VerityWriterAndroid>();
38}
39} // namespace verity_writer
40
41bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
42 partition_ = &partition;
43
44 if (partition_->hash_tree_size != 0) {
45 auto hash_function =
46 HashTreeBuilder::HashFunction(partition_->hash_tree_algorithm);
47 if (hash_function == nullptr) {
48 LOG(ERROR) << "Verity hash algorithm not supported: "
49 << partition_->hash_tree_algorithm;
50 return false;
51 }
52 hash_tree_builder_ = std::make_unique<HashTreeBuilder>(
53 partition_->block_size, hash_function);
54 TEST_AND_RETURN_FALSE(hash_tree_builder_->Initialize(
55 partition_->hash_tree_data_size, partition_->hash_tree_salt));
56 if (hash_tree_builder_->CalculateSize(partition_->hash_tree_data_size) !=
57 partition_->hash_tree_size) {
58 LOG(ERROR) << "Verity hash tree size does not match, stored: "
59 << partition_->hash_tree_size << ", calculated: "
60 << hash_tree_builder_->CalculateSize(
61 partition_->hash_tree_data_size);
62 return false;
63 }
64 }
65 return true;
66}
67
68bool VerityWriterAndroid::Update(uint64_t offset,
69 const uint8_t* buffer,
70 size_t size) {
71 if (partition_->hash_tree_size != 0) {
72 uint64_t hash_tree_data_end =
73 partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
74 uint64_t start_offset = std::max(offset, partition_->hash_tree_data_offset);
75 uint64_t end_offset = std::min(offset + size, hash_tree_data_end);
76 if (start_offset < end_offset) {
77 TEST_AND_RETURN_FALSE(hash_tree_builder_->Update(
78 buffer + start_offset - offset, end_offset - start_offset));
79
80 if (end_offset == hash_tree_data_end) {
81 // All hash tree data blocks has been hashed, write hash tree to disk.
82 int fd = HANDLE_EINTR(open(partition_->target_path.c_str(), O_WRONLY));
83 if (fd < 0) {
84 PLOG(ERROR) << "Failed to open " << partition_->target_path
Sen Jianga778e5b2018-09-13 11:42:56 -070085 << " to write hash tree.";
Sen Jiang57f91802017-11-14 17:42:13 -080086 return false;
87 }
88 ScopedFdCloser fd_closer(&fd);
89
90 LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
91 TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
92 TEST_AND_RETURN_FALSE(hash_tree_builder_->WriteHashTreeToFd(
93 fd, partition_->hash_tree_offset));
94 hash_tree_builder_.reset();
95 }
96 }
97 }
98 if (partition_->fec_size != 0) {
Sen Jianga778e5b2018-09-13 11:42:56 -070099 uint64_t fec_data_end =
100 partition_->fec_data_offset + partition_->fec_data_size;
101 if (offset < fec_data_end && offset + size >= fec_data_end) {
102 LOG(INFO) << "Writing verity FEC to " << partition_->target_path;
103 TEST_AND_RETURN_FALSE(EncodeFEC(partition_->target_path,
104 partition_->fec_data_offset,
105 partition_->fec_data_size,
106 partition_->fec_offset,
107 partition_->fec_size,
108 partition_->fec_roots,
109 partition_->block_size,
110 false /* verify_mode */));
111 }
Sen Jiang57f91802017-11-14 17:42:13 -0800112 }
113 return true;
114}
115
Sen Jianga778e5b2018-09-13 11:42:56 -0700116bool VerityWriterAndroid::EncodeFEC(const std::string& path,
117 uint64_t data_offset,
118 uint64_t data_size,
119 uint64_t fec_offset,
120 uint64_t fec_size,
121 uint32_t fec_roots,
122 uint32_t block_size,
123 bool verify_mode) {
124 TEST_AND_RETURN_FALSE(data_size % block_size == 0);
125 TEST_AND_RETURN_FALSE(fec_roots >= 0 && fec_roots < FEC_RSM);
126 // This is the N in RS(M, N), which is the number of bytes for each rs block.
127 size_t rs_n = FEC_RSM - fec_roots;
128 uint64_t rounds = utils::DivRoundUp(data_size / block_size, rs_n);
129 TEST_AND_RETURN_FALSE(rounds * fec_roots * block_size == fec_size);
130
131 std::unique_ptr<void, decltype(&free_rs_char)> rs_char(
132 init_rs_char(FEC_PARAMS(fec_roots)), &free_rs_char);
133 TEST_AND_RETURN_FALSE(rs_char != nullptr);
134
135 int fd = HANDLE_EINTR(open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
136 if (fd < 0) {
137 PLOG(ERROR) << "Failed to open " << path << " to write FEC.";
138 return false;
139 }
140 ScopedFdCloser fd_closer(&fd);
141
142 for (size_t i = 0; i < rounds; i++) {
143 // Encodes |block_size| number of rs blocks each round so that we can read
144 // one block each time instead of 1 byte to increase random read
145 // performance. This uses about 1 MiB memory for 4K block size.
146 brillo::Blob rs_blocks(block_size * rs_n);
147 for (size_t j = 0; j < rs_n; j++) {
148 brillo::Blob buffer(block_size, 0);
149 uint64_t offset =
150 fec_ecc_interleave(i * rs_n * block_size + j, rs_n, rounds);
151 // Don't read past |data_size|, treat them as 0.
152 if (offset < data_size) {
153 ssize_t bytes_read = 0;
154 TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
155 buffer.data(),
156 buffer.size(),
157 data_offset + offset,
158 &bytes_read));
159 TEST_AND_RETURN_FALSE(bytes_read ==
160 static_cast<ssize_t>(buffer.size()));
161 }
162 for (size_t k = 0; k < buffer.size(); k++) {
163 rs_blocks[k * rs_n + j] = buffer[k];
164 }
165 }
166 brillo::Blob fec(block_size * fec_roots);
167 for (size_t j = 0; j < block_size; j++) {
168 // Encode [j * rs_n : (j + 1) * rs_n) in |rs_blocks| and write |fec_roots|
169 // number of parity bytes to |j * fec_roots| in |fec|.
170 encode_rs_char(rs_char.get(),
171 rs_blocks.data() + j * rs_n,
172 fec.data() + j * fec_roots);
173 }
174
175 if (verify_mode) {
176 brillo::Blob fec_read(fec.size());
177 ssize_t bytes_read = 0;
178 TEST_AND_RETURN_FALSE(utils::PReadAll(
179 fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
180 TEST_AND_RETURN_FALSE(bytes_read ==
181 static_cast<ssize_t>(fec_read.size()));
182 TEST_AND_RETURN_FALSE(fec == fec_read);
183 } else {
184 TEST_AND_RETURN_FALSE(
185 utils::PWriteAll(fd, fec.data(), fec.size(), fec_offset));
186 }
187 fec_offset += fec.size();
188 }
189
190 return true;
191}
Sen Jiang57f91802017-11-14 17:42:13 -0800192} // namespace chromeos_update_engine