1 //
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>
26 #include <fec/ecc.h>
27 extern "C" {
28 #include <fec.h>
29 }
30
31 #include "update_engine/common/utils.h"
32
33 namespace chromeos_update_engine {
34
35 namespace verity_writer {
CreateVerityWriter()36 std::unique_ptr<VerityWriterInterface> CreateVerityWriter() {
37 return std::make_unique<VerityWriterAndroid>();
38 }
39 } // namespace verity_writer
40
Init(const InstallPlan::Partition & partition)41 bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
42 partition_ = &partition;
43
44 if (partition_->hash_tree_size != 0 || partition_->fec_size != 0) {
45 utils::SetBlockDeviceReadOnly(partition_->target_path, false);
46 }
47 if (partition_->hash_tree_size != 0) {
48 auto hash_function =
49 HashTreeBuilder::HashFunction(partition_->hash_tree_algorithm);
50 if (hash_function == nullptr) {
51 LOG(ERROR) << "Verity hash algorithm not supported: "
52 << partition_->hash_tree_algorithm;
53 return false;
54 }
55 hash_tree_builder_ = std::make_unique<HashTreeBuilder>(
56 partition_->block_size, hash_function);
57 TEST_AND_RETURN_FALSE(hash_tree_builder_->Initialize(
58 partition_->hash_tree_data_size, partition_->hash_tree_salt));
59 if (hash_tree_builder_->CalculateSize(partition_->hash_tree_data_size) !=
60 partition_->hash_tree_size) {
61 LOG(ERROR) << "Verity hash tree size does not match, stored: "
62 << partition_->hash_tree_size << ", calculated: "
63 << hash_tree_builder_->CalculateSize(
64 partition_->hash_tree_data_size);
65 return false;
66 }
67 }
68 return true;
69 }
70
Update(uint64_t offset,const uint8_t * buffer,size_t size)71 bool VerityWriterAndroid::Update(uint64_t offset,
72 const uint8_t* buffer,
73 size_t size) {
74 if (partition_->hash_tree_size != 0) {
75 uint64_t hash_tree_data_end =
76 partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
77 uint64_t start_offset = std::max(offset, partition_->hash_tree_data_offset);
78 uint64_t end_offset = std::min(offset + size, hash_tree_data_end);
79 if (start_offset < end_offset) {
80 TEST_AND_RETURN_FALSE(hash_tree_builder_->Update(
81 buffer + start_offset - offset, end_offset - start_offset));
82
83 if (end_offset == hash_tree_data_end) {
84 // All hash tree data blocks has been hashed, write hash tree to disk.
85 int fd = HANDLE_EINTR(open(partition_->target_path.c_str(), O_WRONLY));
86 if (fd < 0) {
87 PLOG(ERROR) << "Failed to open " << partition_->target_path
88 << " to write hash tree.";
89 return false;
90 }
91 ScopedFdCloser fd_closer(&fd);
92
93 LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
94 TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
95 TEST_AND_RETURN_FALSE(hash_tree_builder_->WriteHashTreeToFd(
96 fd, partition_->hash_tree_offset));
97 hash_tree_builder_.reset();
98 }
99 }
100 }
101 if (partition_->fec_size != 0) {
102 uint64_t fec_data_end =
103 partition_->fec_data_offset + partition_->fec_data_size;
104 if (offset < fec_data_end && offset + size >= fec_data_end) {
105 LOG(INFO) << "Writing verity FEC to " << partition_->target_path;
106 TEST_AND_RETURN_FALSE(EncodeFEC(partition_->target_path,
107 partition_->fec_data_offset,
108 partition_->fec_data_size,
109 partition_->fec_offset,
110 partition_->fec_size,
111 partition_->fec_roots,
112 partition_->block_size,
113 false /* verify_mode */));
114 }
115 }
116 return true;
117 }
118
EncodeFEC(const std::string & path,uint64_t data_offset,uint64_t data_size,uint64_t fec_offset,uint64_t fec_size,uint32_t fec_roots,uint32_t block_size,bool verify_mode)119 bool VerityWriterAndroid::EncodeFEC(const std::string& path,
120 uint64_t data_offset,
121 uint64_t data_size,
122 uint64_t fec_offset,
123 uint64_t fec_size,
124 uint32_t fec_roots,
125 uint32_t block_size,
126 bool verify_mode) {
127 TEST_AND_RETURN_FALSE(data_size % block_size == 0);
128 TEST_AND_RETURN_FALSE(fec_roots >= 0 && fec_roots < FEC_RSM);
129 // This is the N in RS(M, N), which is the number of bytes for each rs block.
130 size_t rs_n = FEC_RSM - fec_roots;
131 uint64_t rounds = utils::DivRoundUp(data_size / block_size, rs_n);
132 TEST_AND_RETURN_FALSE(rounds * fec_roots * block_size == fec_size);
133
134 std::unique_ptr<void, decltype(&free_rs_char)> rs_char(
135 init_rs_char(FEC_PARAMS(fec_roots)), &free_rs_char);
136 TEST_AND_RETURN_FALSE(rs_char != nullptr);
137
138 int fd = HANDLE_EINTR(open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
139 if (fd < 0) {
140 PLOG(ERROR) << "Failed to open " << path << " to write FEC.";
141 return false;
142 }
143 ScopedFdCloser fd_closer(&fd);
144
145 for (size_t i = 0; i < rounds; i++) {
146 // Encodes |block_size| number of rs blocks each round so that we can read
147 // one block each time instead of 1 byte to increase random read
148 // performance. This uses about 1 MiB memory for 4K block size.
149 brillo::Blob rs_blocks(block_size * rs_n);
150 for (size_t j = 0; j < rs_n; j++) {
151 brillo::Blob buffer(block_size, 0);
152 uint64_t offset =
153 fec_ecc_interleave(i * rs_n * block_size + j, rs_n, rounds);
154 // Don't read past |data_size|, treat them as 0.
155 if (offset < data_size) {
156 ssize_t bytes_read = 0;
157 TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
158 buffer.data(),
159 buffer.size(),
160 data_offset + offset,
161 &bytes_read));
162 TEST_AND_RETURN_FALSE(bytes_read ==
163 static_cast<ssize_t>(buffer.size()));
164 }
165 for (size_t k = 0; k < buffer.size(); k++) {
166 rs_blocks[k * rs_n + j] = buffer[k];
167 }
168 }
169 brillo::Blob fec(block_size * fec_roots);
170 for (size_t j = 0; j < block_size; j++) {
171 // Encode [j * rs_n : (j + 1) * rs_n) in |rs_blocks| and write |fec_roots|
172 // number of parity bytes to |j * fec_roots| in |fec|.
173 encode_rs_char(rs_char.get(),
174 rs_blocks.data() + j * rs_n,
175 fec.data() + j * fec_roots);
176 }
177
178 if (verify_mode) {
179 brillo::Blob fec_read(fec.size());
180 ssize_t bytes_read = 0;
181 TEST_AND_RETURN_FALSE(utils::PReadAll(
182 fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
183 TEST_AND_RETURN_FALSE(bytes_read ==
184 static_cast<ssize_t>(fec_read.size()));
185 TEST_AND_RETURN_FALSE(fec == fec_read);
186 } else {
187 TEST_AND_RETURN_FALSE(
188 utils::PWriteAll(fd, fec.data(), fec.size(), fec_offset));
189 }
190 fec_offset += fec.size();
191 }
192
193 return true;
194 }
195 } // namespace chromeos_update_engine
196