• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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