blob: 1a7c272b54e97c43666b12ad3ee77354be24594d [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
Tao Bao6a7e4af2018-06-14 21:57:43 -0700131 if (result.blocks() != src_blocks) {
132 *err =
133 android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
134 src_ranges.ToString().c_str(), src_blocks);
135 return false;
136 }
137
138 *source = result;
139 return true;
140 }
141
142 src_ranges_location = RangeSet::Parse(tokens[pos++]);
143 if (!src_ranges_location) {
144 *err = "invalid source ranges location";
145 return false;
146 }
147 }
148
149 // <[stash_id:stash_location]>
150 std::vector<StashInfo> stashes;
151 while (pos < tokens.size()) {
152 // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
153 // in the source block that stashed data should go.
154 std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
155 if (pairs.size() != 2) {
156 *err = "invalid stash info";
157 return false;
158 }
159 RangeSet stash_location = RangeSet::Parse(pairs[1]);
160 if (!stash_location) {
161 *err = "invalid stash location";
162 return false;
163 }
164 stashes.emplace_back(pairs[0], stash_location);
165 }
166
167 SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
168 if (src_blocks != result.blocks()) {
169 *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
170 src_ranges.ToString().c_str(), src_blocks);
171 return false;
172 }
173
174 *source = result;
175 return true;
176}
177
178Command Command::Parse(const std::string& line, size_t index, std::string* err) {
179 std::vector<std::string> tokens = android::base::Split(line, " ");
180 size_t pos = 0;
181 // tokens.size() will be 1 at least.
182 Type op = ParseType(tokens[pos++]);
183 if (op == Type::LAST) {
184 *err = "invalid type";
185 return {};
186 }
187
188 PatchInfo patch_info;
189 TargetInfo target_info;
190 SourceInfo source_info;
191 StashInfo stash_info;
192
193 if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
194 // zero/new/erase <rangeset>
Tao Bao92f33932018-06-25 12:11:53 -0700195 if (pos + 1 != tokens.size()) {
196 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
197 tokens.size() - pos);
198 return {};
199 }
Tao Bao6a7e4af2018-06-14 21:57:43 -0700200 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
201 if (!tgt_ranges) {
202 return {};
203 }
204 static const std::string kUnknownHash{ "unknown-hash" };
205 target_info = TargetInfo(kUnknownHash, tgt_ranges);
206 } else if (op == Type::STASH) {
207 // stash <stash_id> <src_ranges>
Tao Bao92f33932018-06-25 12:11:53 -0700208 if (pos + 2 != tokens.size()) {
209 *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
210 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700211 return {};
212 }
213 const std::string& id = tokens[pos++];
214 RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
215 if (!src_ranges) {
216 *err = "invalid token";
217 return {};
218 }
219 stash_info = StashInfo(id, src_ranges);
220 } else if (op == Type::FREE) {
221 // free <stash_id>
Tao Bao92f33932018-06-25 12:11:53 -0700222 if (pos + 1 != tokens.size()) {
223 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
224 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700225 return {};
226 }
227 stash_info = StashInfo(tokens[pos++], {});
228 } else if (op == Type::MOVE) {
229 // <hash>
230 if (pos + 1 > tokens.size()) {
231 *err = "missing hash";
232 return {};
233 }
234 std::string hash = tokens[pos++];
235 if (!ParseTargetInfoAndSourceInfo(
236 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
237 hash, &source_info, err)) {
238 return {};
239 }
240 } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
241 // <offset> <length> <srchash> <dsthash>
242 if (pos + 4 > tokens.size()) {
Tao Bao92f33932018-06-25 12:11:53 -0700243 *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
244 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700245 return {};
246 }
247 size_t offset;
248 size_t length;
249 if (!android::base::ParseUint(tokens[pos++], &offset) ||
250 !android::base::ParseUint(tokens[pos++], &length)) {
251 *err = "invalid patch offset/length";
252 return {};
253 }
254 patch_info = PatchInfo(offset, length);
255
256 std::string src_hash = tokens[pos++];
257 std::string dst_hash = tokens[pos++];
258 if (!ParseTargetInfoAndSourceInfo(
259 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
260 src_hash, &source_info, err)) {
261 return {};
262 }
Tao Bao91a649a2018-05-21 16:05:56 -0700263 } else if (op == Type::ABORT) {
Tianjie1bc976a2020-07-22 17:25:11 -0700264 // Abort takes no arguments, so there's nothing else to check.
Tao Bao91a649a2018-05-21 16:05:56 -0700265 if (pos != tokens.size()) {
266 *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
267 tokens.size() - pos);
268 return {};
269 }
Tianjie Xu8f64bf62018-08-07 00:22:19 -0700270 } else if (op == Type::COMPUTE_HASH_TREE) {
271 // <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
272 if (pos + 5 != tokens.size()) {
273 *err = android::base::StringPrintf("invalid number of args: %zu (expected 5)",
274 tokens.size() - pos);
275 return {};
276 }
277
278 // Expects the hash_tree data to be contiguous.
279 RangeSet hash_tree_ranges = RangeSet::Parse(tokens[pos++]);
280 if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
281 *err = "invalid hash tree ranges in: " + line;
282 return {};
283 }
284
285 RangeSet source_ranges = RangeSet::Parse(tokens[pos++]);
286 if (!source_ranges) {
287 *err = "invalid source ranges in: " + line;
288 return {};
289 }
290
291 std::string hash_algorithm = tokens[pos++];
292 std::string salt_hex = tokens[pos++];
293 std::string root_hash = tokens[pos++];
294 if (hash_algorithm.empty() || salt_hex.empty() || root_hash.empty()) {
295 *err = "invalid hash tree arguments in " + line;
296 return {};
297 }
298
299 HashTreeInfo hash_tree_info(std::move(hash_tree_ranges), std::move(source_ranges),
300 std::move(hash_algorithm), std::move(salt_hex),
301 std::move(root_hash));
302 return Command(op, index, line, std::move(hash_tree_info));
Tao Bao6a7e4af2018-06-14 21:57:43 -0700303 } else {
304 *err = "invalid op";
305 return {};
306 }
307
308 return Command(op, index, line, patch_info, target_info, source_info, stash_info);
309}
310
Tao Bao3c892732018-06-18 09:44:33 -0700311bool SourceInfo::Overlaps(const TargetInfo& target) const {
312 return ranges_.Overlaps(target.ranges());
313}
314
315// Moves blocks in the 'source' vector to the specified locations (as in 'locs') in the 'dest'
316// vector. Note that source and dest may be the same buffer.
317static void MoveRange(std::vector<uint8_t>* dest, const RangeSet& locs,
318 const std::vector<uint8_t>& source, size_t block_size) {
319 const uint8_t* from = source.data();
320 uint8_t* to = dest->data();
321 size_t start = locs.blocks();
322 // Must do the movement backward.
323 for (auto it = locs.crbegin(); it != locs.crend(); it++) {
324 size_t blocks = it->second - it->first;
325 start -= blocks;
326 memmove(to + (it->first * block_size), from + (start * block_size), blocks * block_size);
327 }
328}
329
330bool SourceInfo::ReadAll(
331 std::vector<uint8_t>* buffer, size_t block_size,
332 const std::function<int(const RangeSet&, std::vector<uint8_t>*)>& block_reader,
333 const std::function<int(const std::string&, std::vector<uint8_t>*)>& stash_reader) const {
334 if (buffer->size() < blocks() * block_size) {
335 return false;
336 }
337
338 // Read in the source ranges.
339 if (ranges_) {
340 if (block_reader(ranges_, buffer) != 0) {
341 return false;
342 }
343 if (location_) {
344 MoveRange(buffer, location_, *buffer, block_size);
345 }
346 }
347
348 // Read in the stashes.
349 for (const StashInfo& stash : stashes_) {
350 std::vector<uint8_t> stash_buffer(stash.blocks() * block_size);
351 if (stash_reader(stash.id(), &stash_buffer) != 0) {
352 return false;
353 }
354 MoveRange(buffer, stash.ranges(), stash_buffer, block_size);
355 }
356 return true;
357}
358
359void SourceInfo::DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const {
360 LOG(INFO) << "Dumping hashes in hex for " << ranges_.blocks() << " source blocks";
361
362 const RangeSet& location = location_ ? location_ : RangeSet({ Range{ 0, ranges_.blocks() } });
363 for (size_t i = 0; i < ranges_.blocks(); i++) {
364 size_t block_num = ranges_.GetBlockNumber(i);
365 size_t buffer_index = location.GetBlockNumber(i);
366 CHECK_LE((buffer_index + 1) * block_size, buffer.size());
367
368 uint8_t digest[SHA_DIGEST_LENGTH];
369 SHA1(buffer.data() + buffer_index * block_size, block_size, digest);
370 std::string hexdigest = print_sha1(digest);
371 LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest;
372 }
373}
374
Tao Bao6a7e4af2018-06-14 21:57:43 -0700375std::ostream& operator<<(std::ostream& os, const Command& command) {
376 os << command.index() << ": " << command.cmdline();
377 return os;
378}
379
380std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
381 os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
382 return os;
383}
384
385std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
386 os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
387 return os;
388}
389
390std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
391 os << source.blocks_ << " blocks (" << source.hash_ << "): ";
392 if (source.ranges_) {
393 os << source.ranges_.ToString();
394 if (source.location_) {
395 os << " (location: " << source.location_.ToString() << ")";
396 }
397 }
398 if (!source.stashes_.empty()) {
399 os << " " << source.stashes_.size() << " stash(es)";
400 }
401 return os;
402}
Tao Baof8811bb2018-06-18 10:03:52 -0700403
404TransferList TransferList::Parse(const std::string& transfer_list_str, std::string* err) {
405 TransferList result{};
406
407 std::vector<std::string> lines = android::base::Split(transfer_list_str, "\n");
408 if (lines.size() < kTransferListHeaderLines) {
409 *err = android::base::StringPrintf("too few lines in the transfer list [%zu]", lines.size());
410 return TransferList{};
411 }
412
413 // First line in transfer list is the version number.
414 if (!android::base::ParseInt(lines[0], &result.version_, 3, 4)) {
415 *err = "unexpected transfer list version ["s + lines[0] + "]";
416 return TransferList{};
417 }
418
419 // Second line in transfer list is the total number of blocks we expect to write.
420 if (!android::base::ParseUint(lines[1], &result.total_blocks_)) {
421 *err = "unexpected block count ["s + lines[1] + "]";
422 return TransferList{};
423 }
424
425 // Third line is how many stash entries are needed simultaneously.
426 if (!android::base::ParseUint(lines[2], &result.stash_max_entries_)) {
427 return TransferList{};
428 }
429
430 // Fourth line is the maximum number of blocks that will be stashed simultaneously.
431 if (!android::base::ParseUint(lines[3], &result.stash_max_blocks_)) {
432 *err = "unexpected maximum stash blocks ["s + lines[3] + "]";
433 return TransferList{};
434 }
435
436 // Subsequent lines are all individual transfer commands.
437 for (size_t i = kTransferListHeaderLines; i < lines.size(); i++) {
438 const std::string& line = lines[i];
439 if (line.empty()) continue;
440
441 size_t cmdindex = i - kTransferListHeaderLines;
442 std::string parsing_error;
443 Command command = Command::Parse(line, cmdindex, &parsing_error);
444 if (!command) {
445 *err = android::base::StringPrintf("Failed to parse command %zu [%s]: %s", cmdindex,
446 line.c_str(), parsing_error.c_str());
447 return TransferList{};
448 }
449 result.commands_.push_back(command);
450 }
451
452 return result;
453}