blob: 15a787c519073b2acf8a4e5c0435b42640c144e4 [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 Bao6a7e4af2018-06-14 21:57:43 -070019#include <ostream>
Tao Baoc3901232018-05-21 16:05:56 -070020#include <string>
Tao Bao6a7e4af2018-06-14 21:57:43 -070021#include <vector>
Tao Baoc3901232018-05-21 16:05:56 -070022
23#include <android-base/logging.h>
Tao Bao6a7e4af2018-06-14 21:57:43 -070024#include <android-base/parseint.h>
25#include <android-base/stringprintf.h>
26#include <android-base/strings.h>
27
28#include "otautil/rangeset.h"
29
30using namespace std::string_literals;
Tao Baoc3901232018-05-21 16:05:56 -070031
Tao Bao91a649a2018-05-21 16:05:56 -070032bool Command::abort_allowed_ = false;
33
Tao Baoc3901232018-05-21 16:05:56 -070034Command::Type Command::ParseType(const std::string& type_str) {
Tao Bao91a649a2018-05-21 16:05:56 -070035 if (type_str == "abort") {
36 if (!abort_allowed_) {
37 LOG(ERROR) << "ABORT disallowed";
38 return Type::LAST;
39 }
40 return Type::ABORT;
41 } else if (type_str == "bsdiff") {
Tao Bao6a7e4af2018-06-14 21:57:43 -070042 return Type::BSDIFF;
Tianjie Xu69ffa152018-08-01 16:40:00 -070043 } else if (type_str == "compute_hash_tree") {
44 return Type::COMPUTE_HASH_TREE;
Tao Baoc3901232018-05-21 16:05:56 -070045 } else if (type_str == "erase") {
46 return Type::ERASE;
Tao Baoc3901232018-05-21 16:05:56 -070047 } else if (type_str == "free") {
48 return Type::FREE;
Tao Bao6a7e4af2018-06-14 21:57:43 -070049 } else if (type_str == "imgdiff") {
50 return Type::IMGDIFF;
51 } else if (type_str == "move") {
52 return Type::MOVE;
53 } else if (type_str == "new") {
54 return Type::NEW;
55 } else if (type_str == "stash") {
56 return Type::STASH;
57 } else if (type_str == "zero") {
58 return Type::ZERO;
Tao Baoc3901232018-05-21 16:05:56 -070059 }
Tao Baoc3901232018-05-21 16:05:56 -070060 return Type::LAST;
61};
Tao Bao6a7e4af2018-06-14 21:57:43 -070062
63bool Command::ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens,
64 const std::string& tgt_hash, TargetInfo* target,
65 const std::string& src_hash, SourceInfo* source,
66 std::string* err) {
Tao Bao92f33932018-06-25 12:11:53 -070067 // We expect the given args (in 'tokens' vector) in one of the following formats.
Tao Bao6a7e4af2018-06-14 21:57:43 -070068 //
69 // <tgt_ranges> <src_block_count> - <[stash_id:location] ...>
70 // (loads data from stashes only)
71 //
72 // <tgt_ranges> <src_block_count> <src_ranges>
73 // (loads data from source image only)
74 //
75 // <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location> <[stash_id:location] ...>
76 // (loads data from both of source image and stashes)
77
Tao Bao92f33932018-06-25 12:11:53 -070078 // At least it needs to provide three args: <tgt_ranges>, <src_block_count> and "-"/<src_ranges>.
Tao Bao6a7e4af2018-06-14 21:57:43 -070079 if (tokens.size() < 3) {
Tao Bao92f33932018-06-25 12:11:53 -070080 *err = "invalid number of args";
Tao Bao6a7e4af2018-06-14 21:57:43 -070081 return false;
82 }
83
84 size_t pos = 0;
85 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
86 if (!tgt_ranges) {
87 *err = "invalid target ranges";
88 return false;
89 }
90 *target = TargetInfo(tgt_hash, tgt_ranges);
91
92 // <src_block_count>
93 const std::string& token = tokens[pos++];
94 size_t src_blocks;
95 if (!android::base::ParseUint(token, &src_blocks)) {
96 *err = "invalid src_block_count \""s + token + "\"";
97 return false;
98 }
99
100 RangeSet src_ranges;
101 RangeSet src_ranges_location;
102 // "-" or <src_ranges> [<src_ranges_location>]
103 if (tokens[pos] == "-") {
104 // no source ranges, only stashes
105 pos++;
106 } else {
107 src_ranges = RangeSet::Parse(tokens[pos++]);
108 if (!src_ranges) {
109 *err = "invalid source ranges";
110 return false;
111 }
112
113 if (pos >= tokens.size()) {
114 // No stashes, only source ranges.
115 SourceInfo result(src_hash, src_ranges, {}, {});
116
117 // Sanity check the block count.
118 if (result.blocks() != src_blocks) {
119 *err =
120 android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
121 src_ranges.ToString().c_str(), src_blocks);
122 return false;
123 }
124
125 *source = result;
126 return true;
127 }
128
129 src_ranges_location = RangeSet::Parse(tokens[pos++]);
130 if (!src_ranges_location) {
131 *err = "invalid source ranges location";
132 return false;
133 }
134 }
135
136 // <[stash_id:stash_location]>
137 std::vector<StashInfo> stashes;
138 while (pos < tokens.size()) {
139 // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
140 // in the source block that stashed data should go.
141 std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
142 if (pairs.size() != 2) {
143 *err = "invalid stash info";
144 return false;
145 }
146 RangeSet stash_location = RangeSet::Parse(pairs[1]);
147 if (!stash_location) {
148 *err = "invalid stash location";
149 return false;
150 }
151 stashes.emplace_back(pairs[0], stash_location);
152 }
153
154 SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
155 if (src_blocks != result.blocks()) {
156 *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
157 src_ranges.ToString().c_str(), src_blocks);
158 return false;
159 }
160
161 *source = result;
162 return true;
163}
164
165Command Command::Parse(const std::string& line, size_t index, std::string* err) {
166 std::vector<std::string> tokens = android::base::Split(line, " ");
167 size_t pos = 0;
168 // tokens.size() will be 1 at least.
169 Type op = ParseType(tokens[pos++]);
170 if (op == Type::LAST) {
171 *err = "invalid type";
172 return {};
173 }
174
175 PatchInfo patch_info;
176 TargetInfo target_info;
177 SourceInfo source_info;
178 StashInfo stash_info;
179
Tianjie Xu69ffa152018-08-01 16:40:00 -0700180 // TODO(xunchang) add the parse code of compute_hash_tree
Tao Bao6a7e4af2018-06-14 21:57:43 -0700181 if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
182 // zero/new/erase <rangeset>
Tao Bao92f33932018-06-25 12:11:53 -0700183 if (pos + 1 != tokens.size()) {
184 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
185 tokens.size() - pos);
186 return {};
187 }
Tao Bao6a7e4af2018-06-14 21:57:43 -0700188 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
189 if (!tgt_ranges) {
190 return {};
191 }
192 static const std::string kUnknownHash{ "unknown-hash" };
193 target_info = TargetInfo(kUnknownHash, tgt_ranges);
194 } else if (op == Type::STASH) {
195 // stash <stash_id> <src_ranges>
Tao Bao92f33932018-06-25 12:11:53 -0700196 if (pos + 2 != tokens.size()) {
197 *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
198 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700199 return {};
200 }
201 const std::string& id = tokens[pos++];
202 RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
203 if (!src_ranges) {
204 *err = "invalid token";
205 return {};
206 }
207 stash_info = StashInfo(id, src_ranges);
208 } else if (op == Type::FREE) {
209 // free <stash_id>
Tao Bao92f33932018-06-25 12:11:53 -0700210 if (pos + 1 != tokens.size()) {
211 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
212 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700213 return {};
214 }
215 stash_info = StashInfo(tokens[pos++], {});
216 } else if (op == Type::MOVE) {
217 // <hash>
218 if (pos + 1 > tokens.size()) {
219 *err = "missing hash";
220 return {};
221 }
222 std::string hash = tokens[pos++];
223 if (!ParseTargetInfoAndSourceInfo(
224 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
225 hash, &source_info, err)) {
226 return {};
227 }
228 } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
229 // <offset> <length> <srchash> <dsthash>
230 if (pos + 4 > tokens.size()) {
Tao Bao92f33932018-06-25 12:11:53 -0700231 *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
232 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700233 return {};
234 }
235 size_t offset;
236 size_t length;
237 if (!android::base::ParseUint(tokens[pos++], &offset) ||
238 !android::base::ParseUint(tokens[pos++], &length)) {
239 *err = "invalid patch offset/length";
240 return {};
241 }
242 patch_info = PatchInfo(offset, length);
243
244 std::string src_hash = tokens[pos++];
245 std::string dst_hash = tokens[pos++];
246 if (!ParseTargetInfoAndSourceInfo(
247 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
248 src_hash, &source_info, err)) {
249 return {};
250 }
Tao Bao91a649a2018-05-21 16:05:56 -0700251 } else if (op == Type::ABORT) {
252 // No-op, other than sanity checking the input args.
253 if (pos != tokens.size()) {
254 *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
255 tokens.size() - pos);
256 return {};
257 }
Tao Bao6a7e4af2018-06-14 21:57:43 -0700258 } else {
259 *err = "invalid op";
260 return {};
261 }
262
263 return Command(op, index, line, patch_info, target_info, source_info, stash_info);
264}
265
266std::ostream& operator<<(std::ostream& os, const Command& command) {
267 os << command.index() << ": " << command.cmdline();
268 return os;
269}
270
271std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
272 os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
273 return os;
274}
275
276std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
277 os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
278 return os;
279}
280
281std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
282 os << source.blocks_ << " blocks (" << source.hash_ << "): ";
283 if (source.ranges_) {
284 os << source.ranges_.ToString();
285 if (source.location_) {
286 os << " (location: " << source.location_.ToString() << ")";
287 }
288 }
289 if (!source.stashes_.empty()) {
290 os << " " << source.stashes_.size() << " stash(es)";
291 }
292 return os;
293}