blob: 017086323ca1ff4f8db1fc54b75b00702f3a7b8b [file] [log] [blame]
Tao Baoc3901232018-05-21 16:05:56 -07001/*
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 "private/commands.h"
18
Tao Bao3c892732018-06-18 09:44:33 -070019#include <stdint.h>
20#include <string.h>
21
22#include <functional>
Tao Bao6a7e4af2018-06-14 21:57:43 -070023#include <ostream>
Tao Baoc3901232018-05-21 16:05:56 -070024#include <string>
Tao Bao6a7e4af2018-06-14 21:57:43 -070025#include <vector>
Tao Baoc3901232018-05-21 16:05:56 -070026
27#include <android-base/logging.h>
Tao Bao6a7e4af2018-06-14 21:57:43 -070028#include <android-base/parseint.h>
29#include <android-base/stringprintf.h>
30#include <android-base/strings.h>
Tao Bao3c892732018-06-18 09:44:33 -070031#include <openssl/sha.h>
Tao Bao6a7e4af2018-06-14 21:57:43 -070032
Tao Bao3c892732018-06-18 09:44:33 -070033#include "otautil/print_sha1.h"
Tao Bao6a7e4af2018-06-14 21:57:43 -070034#include "otautil/rangeset.h"
35
36using namespace std::string_literals;
Tao Baoc3901232018-05-21 16:05:56 -070037
Tao Bao91a649a2018-05-21 16:05:56 -070038bool Command::abort_allowed_ = false;
39
Tianjie Xu8f64bf62018-08-07 00:22:19 -070040Command::Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info)
41 : type_(type),
42 index_(index),
43 cmdline_(std::move(cmdline)),
44 hash_tree_info_(std::move(hash_tree_info)) {
45 CHECK(type == Type::COMPUTE_HASH_TREE);
46}
47
Tao Baoc3901232018-05-21 16:05:56 -070048Command::Type Command::ParseType(const std::string& type_str) {
Tao Bao91a649a2018-05-21 16:05:56 -070049 if (type_str == "abort") {
50 if (!abort_allowed_) {
51 LOG(ERROR) << "ABORT disallowed";
52 return Type::LAST;
53 }
54 return Type::ABORT;
55 } else if (type_str == "bsdiff") {
Tao Bao6a7e4af2018-06-14 21:57:43 -070056 return Type::BSDIFF;
Tianjie Xu69ffa152018-08-01 16:40:00 -070057 } else if (type_str == "compute_hash_tree") {
58 return Type::COMPUTE_HASH_TREE;
Tao Baoc3901232018-05-21 16:05:56 -070059 } else if (type_str == "erase") {
60 return Type::ERASE;
Tao Baoc3901232018-05-21 16:05:56 -070061 } else if (type_str == "free") {
62 return Type::FREE;
Tao Bao6a7e4af2018-06-14 21:57:43 -070063 } else if (type_str == "imgdiff") {
64 return Type::IMGDIFF;
65 } else if (type_str == "move") {
66 return Type::MOVE;
67 } else if (type_str == "new") {
68 return Type::NEW;
69 } else if (type_str == "stash") {
70 return Type::STASH;
71 } else if (type_str == "zero") {
72 return Type::ZERO;
Tao Baoc3901232018-05-21 16:05:56 -070073 }
Tao Baoc3901232018-05-21 16:05:56 -070074 return Type::LAST;
75};
Tao Bao6a7e4af2018-06-14 21:57:43 -070076
77bool Command::ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens,
78 const std::string& tgt_hash, TargetInfo* target,
79 const std::string& src_hash, SourceInfo* source,
80 std::string* err) {
Tao Bao92f33932018-06-25 12:11:53 -070081 // We expect the given args (in 'tokens' vector) in one of the following formats.
Tao Bao6a7e4af2018-06-14 21:57:43 -070082 //
83 // <tgt_ranges> <src_block_count> - <[stash_id:location] ...>
84 // (loads data from stashes only)
85 //
86 // <tgt_ranges> <src_block_count> <src_ranges>
87 // (loads data from source image only)
88 //
89 // <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location> <[stash_id:location] ...>
90 // (loads data from both of source image and stashes)
91
Tao Bao92f33932018-06-25 12:11:53 -070092 // At least it needs to provide three args: <tgt_ranges>, <src_block_count> and "-"/<src_ranges>.
Tao Bao6a7e4af2018-06-14 21:57:43 -070093 if (tokens.size() < 3) {
Tao Bao92f33932018-06-25 12:11:53 -070094 *err = "invalid number of args";
Tao Bao6a7e4af2018-06-14 21:57:43 -070095 return false;
96 }
97
98 size_t pos = 0;
99 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
100 if (!tgt_ranges) {
101 *err = "invalid target ranges";
102 return false;
103 }
104 *target = TargetInfo(tgt_hash, tgt_ranges);
105
106 // <src_block_count>
107 const std::string& token = tokens[pos++];
108 size_t src_blocks;
109 if (!android::base::ParseUint(token, &src_blocks)) {
110 *err = "invalid src_block_count \""s + token + "\"";
111 return false;
112 }
113
114 RangeSet src_ranges;
115 RangeSet src_ranges_location;
116 // "-" or <src_ranges> [<src_ranges_location>]
117 if (tokens[pos] == "-") {
118 // no source ranges, only stashes
119 pos++;
120 } else {
121 src_ranges = RangeSet::Parse(tokens[pos++]);
122 if (!src_ranges) {
123 *err = "invalid source ranges";
124 return false;
125 }
126
127 if (pos >= tokens.size()) {
128 // No stashes, only source ranges.
129 SourceInfo result(src_hash, src_ranges, {}, {});
130
131 // Sanity check the block count.
132 if (result.blocks() != src_blocks) {
133 *err =
134 android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
135 src_ranges.ToString().c_str(), src_blocks);
136 return false;
137 }
138
139 *source = result;
140 return true;
141 }
142
143 src_ranges_location = RangeSet::Parse(tokens[pos++]);
144 if (!src_ranges_location) {
145 *err = "invalid source ranges location";
146 return false;
147 }
148 }
149
150 // <[stash_id:stash_location]>
151 std::vector<StashInfo> stashes;
152 while (pos < tokens.size()) {
153 // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
154 // in the source block that stashed data should go.
155 std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
156 if (pairs.size() != 2) {
157 *err = "invalid stash info";
158 return false;
159 }
160 RangeSet stash_location = RangeSet::Parse(pairs[1]);
161 if (!stash_location) {
162 *err = "invalid stash location";
163 return false;
164 }
165 stashes.emplace_back(pairs[0], stash_location);
166 }
167
168 SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
169 if (src_blocks != result.blocks()) {
170 *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
171 src_ranges.ToString().c_str(), src_blocks);
172 return false;
173 }
174
175 *source = result;
176 return true;
177}
178
179Command Command::Parse(const std::string& line, size_t index, std::string* err) {
180 std::vector<std::string> tokens = android::base::Split(line, " ");
181 size_t pos = 0;
182 // tokens.size() will be 1 at least.
183 Type op = ParseType(tokens[pos++]);
184 if (op == Type::LAST) {
185 *err = "invalid type";
186 return {};
187 }
188
189 PatchInfo patch_info;
190 TargetInfo target_info;
191 SourceInfo source_info;
192 StashInfo stash_info;
193
194 if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
195 // zero/new/erase <rangeset>
Tao Bao92f33932018-06-25 12:11:53 -0700196 if (pos + 1 != tokens.size()) {
197 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
198 tokens.size() - pos);
199 return {};
200 }
Tao Bao6a7e4af2018-06-14 21:57:43 -0700201 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
202 if (!tgt_ranges) {
203 return {};
204 }
205 static const std::string kUnknownHash{ "unknown-hash" };
206 target_info = TargetInfo(kUnknownHash, tgt_ranges);
207 } else if (op == Type::STASH) {
208 // stash <stash_id> <src_ranges>
Tao Bao92f33932018-06-25 12:11:53 -0700209 if (pos + 2 != tokens.size()) {
210 *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
211 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700212 return {};
213 }
214 const std::string& id = tokens[pos++];
215 RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
216 if (!src_ranges) {
217 *err = "invalid token";
218 return {};
219 }
220 stash_info = StashInfo(id, src_ranges);
221 } else if (op == Type::FREE) {
222 // free <stash_id>
Tao Bao92f33932018-06-25 12:11:53 -0700223 if (pos + 1 != tokens.size()) {
224 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
225 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700226 return {};
227 }
228 stash_info = StashInfo(tokens[pos++], {});
229 } else if (op == Type::MOVE) {
230 // <hash>
231 if (pos + 1 > tokens.size()) {
232 *err = "missing hash";
233 return {};
234 }
235 std::string hash = tokens[pos++];
236 if (!ParseTargetInfoAndSourceInfo(
237 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
238 hash, &source_info, err)) {
239 return {};
240 }
241 } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
242 // <offset> <length> <srchash> <dsthash>
243 if (pos + 4 > tokens.size()) {
Tao Bao92f33932018-06-25 12:11:53 -0700244 *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
245 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700246 return {};
247 }
248 size_t offset;
249 size_t length;
250 if (!android::base::ParseUint(tokens[pos++], &offset) ||
251 !android::base::ParseUint(tokens[pos++], &length)) {
252 *err = "invalid patch offset/length";
253 return {};
254 }
255 patch_info = PatchInfo(offset, length);
256
257 std::string src_hash = tokens[pos++];
258 std::string dst_hash = tokens[pos++];
259 if (!ParseTargetInfoAndSourceInfo(
260 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
261 src_hash, &source_info, err)) {
262 return {};
263 }
Tao Bao91a649a2018-05-21 16:05:56 -0700264 } else if (op == Type::ABORT) {
265 // No-op, other than sanity checking the input args.
266 if (pos != tokens.size()) {
267 *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
268 tokens.size() - pos);
269 return {};
270 }
Tianjie Xu8f64bf62018-08-07 00:22:19 -0700271 } else if (op == Type::COMPUTE_HASH_TREE) {
272 // <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
273 if (pos + 5 != tokens.size()) {
274 *err = android::base::StringPrintf("invalid number of args: %zu (expected 5)",
275 tokens.size() - pos);
276 return {};
277 }
278
279 // Expects the hash_tree data to be contiguous.
280 RangeSet hash_tree_ranges = RangeSet::Parse(tokens[pos++]);
281 if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
282 *err = "invalid hash tree ranges in: " + line;
283 return {};
284 }
285
286 RangeSet source_ranges = RangeSet::Parse(tokens[pos++]);
287 if (!source_ranges) {
288 *err = "invalid source ranges in: " + line;
289 return {};
290 }
291
292 std::string hash_algorithm = tokens[pos++];
293 std::string salt_hex = tokens[pos++];
294 std::string root_hash = tokens[pos++];
295 if (hash_algorithm.empty() || salt_hex.empty() || root_hash.empty()) {
296 *err = "invalid hash tree arguments in " + line;
297 return {};
298 }
299
300 HashTreeInfo hash_tree_info(std::move(hash_tree_ranges), std::move(source_ranges),
301 std::move(hash_algorithm), std::move(salt_hex),
302 std::move(root_hash));
303 return Command(op, index, line, std::move(hash_tree_info));
Tao Bao6a7e4af2018-06-14 21:57:43 -0700304 } else {
305 *err = "invalid op";
306 return {};
307 }
308
309 return Command(op, index, line, patch_info, target_info, source_info, stash_info);
310}
311
Tao Bao3c892732018-06-18 09:44:33 -0700312bool SourceInfo::Overlaps(const TargetInfo& target) const {
313 return ranges_.Overlaps(target.ranges());
314}
315
316// Moves blocks in the 'source' vector to the specified locations (as in 'locs') in the 'dest'
317// vector. Note that source and dest may be the same buffer.
318static void MoveRange(std::vector<uint8_t>* dest, const RangeSet& locs,
319 const std::vector<uint8_t>& source, size_t block_size) {
320 const uint8_t* from = source.data();
321 uint8_t* to = dest->data();
322 size_t start = locs.blocks();
323 // Must do the movement backward.
324 for (auto it = locs.crbegin(); it != locs.crend(); it++) {
325 size_t blocks = it->second - it->first;
326 start -= blocks;
327 memmove(to + (it->first * block_size), from + (start * block_size), blocks * block_size);
328 }
329}
330
331bool SourceInfo::ReadAll(
332 std::vector<uint8_t>* buffer, size_t block_size,
333 const std::function<int(const RangeSet&, std::vector<uint8_t>*)>& block_reader,
334 const std::function<int(const std::string&, std::vector<uint8_t>*)>& stash_reader) const {
335 if (buffer->size() < blocks() * block_size) {
336 return false;
337 }
338
339 // Read in the source ranges.
340 if (ranges_) {
341 if (block_reader(ranges_, buffer) != 0) {
342 return false;
343 }
344 if (location_) {
345 MoveRange(buffer, location_, *buffer, block_size);
346 }
347 }
348
349 // Read in the stashes.
350 for (const StashInfo& stash : stashes_) {
351 std::vector<uint8_t> stash_buffer(stash.blocks() * block_size);
352 if (stash_reader(stash.id(), &stash_buffer) != 0) {
353 return false;
354 }
355 MoveRange(buffer, stash.ranges(), stash_buffer, block_size);
356 }
357 return true;
358}
359
360void SourceInfo::DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const {
361 LOG(INFO) << "Dumping hashes in hex for " << ranges_.blocks() << " source blocks";
362
363 const RangeSet& location = location_ ? location_ : RangeSet({ Range{ 0, ranges_.blocks() } });
364 for (size_t i = 0; i < ranges_.blocks(); i++) {
365 size_t block_num = ranges_.GetBlockNumber(i);
366 size_t buffer_index = location.GetBlockNumber(i);
367 CHECK_LE((buffer_index + 1) * block_size, buffer.size());
368
369 uint8_t digest[SHA_DIGEST_LENGTH];
370 SHA1(buffer.data() + buffer_index * block_size, block_size, digest);
371 std::string hexdigest = print_sha1(digest);
372 LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest;
373 }
374}
375
Tao Bao6a7e4af2018-06-14 21:57:43 -0700376std::ostream& operator<<(std::ostream& os, const Command& command) {
377 os << command.index() << ": " << command.cmdline();
378 return os;
379}
380
381std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
382 os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
383 return os;
384}
385
386std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
387 os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
388 return os;
389}
390
391std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
392 os << source.blocks_ << " blocks (" << source.hash_ << "): ";
393 if (source.ranges_) {
394 os << source.ranges_.ToString();
395 if (source.location_) {
396 os << " (location: " << source.location_.ToString() << ")";
397 }
398 }
399 if (!source.stashes_.empty()) {
400 os << " " << source.stashes_.size() << " stash(es)";
401 }
402 return os;
403}