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 { 36 std::unique_ptr<VerityWriterInterface> CreateVerityWriter() { 37 return std::make_unique<VerityWriterAndroid>(); 38 } 39 } // namespace verity_writer 40 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 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 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