blob: 864d9a1deb121ee91238e337caf526ce33aaad4c [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"
Kelvin Zhangb138ab52020-11-06 15:56:41 -050032#include "update_engine/payload_consumer/file_descriptor.h"
Sen Jiang57f91802017-11-14 17:42:13 -080033
34namespace chromeos_update_engine {
35
36namespace verity_writer {
37std::unique_ptr<VerityWriterInterface> CreateVerityWriter() {
38 return std::make_unique<VerityWriterAndroid>();
39}
40} // namespace verity_writer
41
42bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
Kelvin Zhangb138ab52020-11-06 15:56:41 -050043 auto read_fd = FileDescriptorPtr(new EintrSafeFileDescriptor());
44 TEST_AND_RETURN_FALSE(read_fd->Open(partition.target_path.c_str(), O_RDWR));
45 return Init(partition, read_fd, read_fd);
46}
47bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition,
48 FileDescriptorPtr read_fd,
49 FileDescriptorPtr write_fd) {
Sen Jiang57f91802017-11-14 17:42:13 -080050 partition_ = &partition;
Kelvin Zhangb138ab52020-11-06 15:56:41 -050051 read_fd_ = read_fd;
52 write_fd_ = write_fd;
Sen Jiang57f91802017-11-14 17:42:13 -080053
xunchang32e82042019-04-25 10:37:36 -070054 if (partition_->hash_tree_size != 0 || partition_->fec_size != 0) {
55 utils::SetBlockDeviceReadOnly(partition_->target_path, false);
56 }
Sen Jiang57f91802017-11-14 17:42:13 -080057 if (partition_->hash_tree_size != 0) {
58 auto hash_function =
59 HashTreeBuilder::HashFunction(partition_->hash_tree_algorithm);
60 if (hash_function == nullptr) {
61 LOG(ERROR) << "Verity hash algorithm not supported: "
62 << partition_->hash_tree_algorithm;
63 return false;
64 }
65 hash_tree_builder_ = std::make_unique<HashTreeBuilder>(
66 partition_->block_size, hash_function);
67 TEST_AND_RETURN_FALSE(hash_tree_builder_->Initialize(
68 partition_->hash_tree_data_size, partition_->hash_tree_salt));
69 if (hash_tree_builder_->CalculateSize(partition_->hash_tree_data_size) !=
70 partition_->hash_tree_size) {
71 LOG(ERROR) << "Verity hash tree size does not match, stored: "
72 << partition_->hash_tree_size << ", calculated: "
73 << hash_tree_builder_->CalculateSize(
74 partition_->hash_tree_data_size);
75 return false;
76 }
77 }
78 return true;
79}
80
81bool VerityWriterAndroid::Update(uint64_t offset,
82 const uint8_t* buffer,
83 size_t size) {
84 if (partition_->hash_tree_size != 0) {
85 uint64_t hash_tree_data_end =
86 partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
87 uint64_t start_offset = std::max(offset, partition_->hash_tree_data_offset);
88 uint64_t end_offset = std::min(offset + size, hash_tree_data_end);
89 if (start_offset < end_offset) {
90 TEST_AND_RETURN_FALSE(hash_tree_builder_->Update(
91 buffer + start_offset - offset, end_offset - start_offset));
92
93 if (end_offset == hash_tree_data_end) {
94 // All hash tree data blocks has been hashed, write hash tree to disk.
Sen Jiang57f91802017-11-14 17:42:13 -080095 LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
96 TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
Kelvin Zhangb138ab52020-11-06 15:56:41 -050097 TEST_AND_RETURN_FALSE_ERRNO(
98 write_fd_->Seek(partition_->hash_tree_offset, SEEK_SET));
99 auto success = hash_tree_builder_->WriteHashTree(
100 [write_fd_(this->write_fd_)](auto data, auto size) {
101 return utils::WriteAll(write_fd_, data, size);
102 });
103 // hashtree builder already prints error messages.
104 if (!success) {
105 return false;
106 }
Sen Jiang57f91802017-11-14 17:42:13 -0800107 hash_tree_builder_.reset();
108 }
109 }
110 }
111 if (partition_->fec_size != 0) {
Sen Jianga778e5b2018-09-13 11:42:56 -0700112 uint64_t fec_data_end =
113 partition_->fec_data_offset + partition_->fec_data_size;
114 if (offset < fec_data_end && offset + size >= fec_data_end) {
115 LOG(INFO) << "Writing verity FEC to " << partition_->target_path;
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500116 TEST_AND_RETURN_FALSE(EncodeFEC(read_fd_,
117 write_fd_,
Sen Jianga778e5b2018-09-13 11:42:56 -0700118 partition_->fec_data_offset,
119 partition_->fec_data_size,
120 partition_->fec_offset,
121 partition_->fec_size,
122 partition_->fec_roots,
123 partition_->block_size,
124 false /* verify_mode */));
125 }
Sen Jiang57f91802017-11-14 17:42:13 -0800126 }
127 return true;
128}
129
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500130bool VerityWriterAndroid::EncodeFEC(FileDescriptorPtr read_fd,
131 FileDescriptorPtr write_fd,
Sen Jianga778e5b2018-09-13 11:42:56 -0700132 uint64_t data_offset,
133 uint64_t data_size,
134 uint64_t fec_offset,
135 uint64_t fec_size,
136 uint32_t fec_roots,
137 uint32_t block_size,
138 bool verify_mode) {
139 TEST_AND_RETURN_FALSE(data_size % block_size == 0);
140 TEST_AND_RETURN_FALSE(fec_roots >= 0 && fec_roots < FEC_RSM);
141 // This is the N in RS(M, N), which is the number of bytes for each rs block.
142 size_t rs_n = FEC_RSM - fec_roots;
143 uint64_t rounds = utils::DivRoundUp(data_size / block_size, rs_n);
144 TEST_AND_RETURN_FALSE(rounds * fec_roots * block_size == fec_size);
145
146 std::unique_ptr<void, decltype(&free_rs_char)> rs_char(
147 init_rs_char(FEC_PARAMS(fec_roots)), &free_rs_char);
148 TEST_AND_RETURN_FALSE(rs_char != nullptr);
149
Sen Jianga778e5b2018-09-13 11:42:56 -0700150 for (size_t i = 0; i < rounds; i++) {
151 // Encodes |block_size| number of rs blocks each round so that we can read
152 // one block each time instead of 1 byte to increase random read
153 // performance. This uses about 1 MiB memory for 4K block size.
154 brillo::Blob rs_blocks(block_size * rs_n);
155 for (size_t j = 0; j < rs_n; j++) {
156 brillo::Blob buffer(block_size, 0);
157 uint64_t offset =
158 fec_ecc_interleave(i * rs_n * block_size + j, rs_n, rounds);
159 // Don't read past |data_size|, treat them as 0.
160 if (offset < data_size) {
161 ssize_t bytes_read = 0;
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500162 TEST_AND_RETURN_FALSE(utils::PReadAll(read_fd,
Sen Jianga778e5b2018-09-13 11:42:56 -0700163 buffer.data(),
164 buffer.size(),
165 data_offset + offset,
166 &bytes_read));
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500167 TEST_AND_RETURN_FALSE(bytes_read >= 0);
168 TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == buffer.size());
Sen Jianga778e5b2018-09-13 11:42:56 -0700169 }
170 for (size_t k = 0; k < buffer.size(); k++) {
171 rs_blocks[k * rs_n + j] = buffer[k];
172 }
173 }
174 brillo::Blob fec(block_size * fec_roots);
175 for (size_t j = 0; j < block_size; j++) {
176 // Encode [j * rs_n : (j + 1) * rs_n) in |rs_blocks| and write |fec_roots|
177 // number of parity bytes to |j * fec_roots| in |fec|.
178 encode_rs_char(rs_char.get(),
179 rs_blocks.data() + j * rs_n,
180 fec.data() + j * fec_roots);
181 }
182
183 if (verify_mode) {
184 brillo::Blob fec_read(fec.size());
185 ssize_t bytes_read = 0;
186 TEST_AND_RETURN_FALSE(utils::PReadAll(
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500187 read_fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
188 TEST_AND_RETURN_FALSE(bytes_read >= 0);
189 TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == fec_read.size());
Sen Jianga778e5b2018-09-13 11:42:56 -0700190 TEST_AND_RETURN_FALSE(fec == fec_read);
191 } else {
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500192 CHECK(write_fd);
193 if (!utils::PWriteAll(write_fd, fec.data(), fec.size(), fec_offset)) {
194 PLOG(ERROR) << "EncodeFEC write() failed";
195 return false;
196 }
Sen Jianga778e5b2018-09-13 11:42:56 -0700197 }
198 fec_offset += fec.size();
199 }
200
201 return true;
202}
Kelvin Zhangb138ab52020-11-06 15:56:41 -0500203
204bool VerityWriterAndroid::EncodeFEC(const std::string& path,
205 uint64_t data_offset,
206 uint64_t data_size,
207 uint64_t fec_offset,
208 uint64_t fec_size,
209 uint32_t fec_roots,
210 uint32_t block_size,
211 bool verify_mode) {
212 FileDescriptorPtr fd(new EintrSafeFileDescriptor());
213 TEST_AND_RETURN_FALSE(
214 fd->Open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
215 return EncodeFEC(fd,
216 fd,
217 data_offset,
218 data_size,
219 fec_offset,
220 fec_size,
221 fec_roots,
222 block_size,
223 verify_mode);
224}
Sen Jiang57f91802017-11-14 17:42:13 -0800225} // namespace chromeos_update_engine