blob: e881496784d28ae6edea600e4a9abd6b3f943133 [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;
Tao Baoc3901232018-05-21 16:05:56 -070043 } else if (type_str == "erase") {
44 return Type::ERASE;
Tao Baoc3901232018-05-21 16:05:56 -070045 } else if (type_str == "free") {
46 return Type::FREE;
Tao Bao6a7e4af2018-06-14 21:57:43 -070047 } else if (type_str == "imgdiff") {
48 return Type::IMGDIFF;
49 } else if (type_str == "move") {
50 return Type::MOVE;
51 } else if (type_str == "new") {
52 return Type::NEW;
53 } else if (type_str == "stash") {
54 return Type::STASH;
55 } else if (type_str == "zero") {
56 return Type::ZERO;
Tao Baoc3901232018-05-21 16:05:56 -070057 }
Tao Baoc3901232018-05-21 16:05:56 -070058 return Type::LAST;
59};
Tao Bao6a7e4af2018-06-14 21:57:43 -070060
61bool Command::ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens,
62 const std::string& tgt_hash, TargetInfo* target,
63 const std::string& src_hash, SourceInfo* source,
64 std::string* err) {
Tao Bao92f33932018-06-25 12:11:53 -070065 // We expect the given args (in 'tokens' vector) in one of the following formats.
Tao Bao6a7e4af2018-06-14 21:57:43 -070066 //
67 // <tgt_ranges> <src_block_count> - <[stash_id:location] ...>
68 // (loads data from stashes only)
69 //
70 // <tgt_ranges> <src_block_count> <src_ranges>
71 // (loads data from source image only)
72 //
73 // <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location> <[stash_id:location] ...>
74 // (loads data from both of source image and stashes)
75
Tao Bao92f33932018-06-25 12:11:53 -070076 // At least it needs to provide three args: <tgt_ranges>, <src_block_count> and "-"/<src_ranges>.
Tao Bao6a7e4af2018-06-14 21:57:43 -070077 if (tokens.size() < 3) {
Tao Bao92f33932018-06-25 12:11:53 -070078 *err = "invalid number of args";
Tao Bao6a7e4af2018-06-14 21:57:43 -070079 return false;
80 }
81
82 size_t pos = 0;
83 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
84 if (!tgt_ranges) {
85 *err = "invalid target ranges";
86 return false;
87 }
88 *target = TargetInfo(tgt_hash, tgt_ranges);
89
90 // <src_block_count>
91 const std::string& token = tokens[pos++];
92 size_t src_blocks;
93 if (!android::base::ParseUint(token, &src_blocks)) {
94 *err = "invalid src_block_count \""s + token + "\"";
95 return false;
96 }
97
98 RangeSet src_ranges;
99 RangeSet src_ranges_location;
100 // "-" or <src_ranges> [<src_ranges_location>]
101 if (tokens[pos] == "-") {
102 // no source ranges, only stashes
103 pos++;
104 } else {
105 src_ranges = RangeSet::Parse(tokens[pos++]);
106 if (!src_ranges) {
107 *err = "invalid source ranges";
108 return false;
109 }
110
111 if (pos >= tokens.size()) {
112 // No stashes, only source ranges.
113 SourceInfo result(src_hash, src_ranges, {}, {});
114
115 // Sanity check the block count.
116 if (result.blocks() != src_blocks) {
117 *err =
118 android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
119 src_ranges.ToString().c_str(), src_blocks);
120 return false;
121 }
122
123 *source = result;
124 return true;
125 }
126
127 src_ranges_location = RangeSet::Parse(tokens[pos++]);
128 if (!src_ranges_location) {
129 *err = "invalid source ranges location";
130 return false;
131 }
132 }
133
134 // <[stash_id:stash_location]>
135 std::vector<StashInfo> stashes;
136 while (pos < tokens.size()) {
137 // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
138 // in the source block that stashed data should go.
139 std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
140 if (pairs.size() != 2) {
141 *err = "invalid stash info";
142 return false;
143 }
144 RangeSet stash_location = RangeSet::Parse(pairs[1]);
145 if (!stash_location) {
146 *err = "invalid stash location";
147 return false;
148 }
149 stashes.emplace_back(pairs[0], stash_location);
150 }
151
152 SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
153 if (src_blocks != result.blocks()) {
154 *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
155 src_ranges.ToString().c_str(), src_blocks);
156 return false;
157 }
158
159 *source = result;
160 return true;
161}
162
163Command Command::Parse(const std::string& line, size_t index, std::string* err) {
164 std::vector<std::string> tokens = android::base::Split(line, " ");
165 size_t pos = 0;
166 // tokens.size() will be 1 at least.
167 Type op = ParseType(tokens[pos++]);
168 if (op == Type::LAST) {
169 *err = "invalid type";
170 return {};
171 }
172
173 PatchInfo patch_info;
174 TargetInfo target_info;
175 SourceInfo source_info;
176 StashInfo stash_info;
177
178 if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
179 // zero/new/erase <rangeset>
Tao Bao92f33932018-06-25 12:11:53 -0700180 if (pos + 1 != tokens.size()) {
181 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
182 tokens.size() - pos);
183 return {};
184 }
Tao Bao6a7e4af2018-06-14 21:57:43 -0700185 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
186 if (!tgt_ranges) {
187 return {};
188 }
189 static const std::string kUnknownHash{ "unknown-hash" };
190 target_info = TargetInfo(kUnknownHash, tgt_ranges);
191 } else if (op == Type::STASH) {
192 // stash <stash_id> <src_ranges>
Tao Bao92f33932018-06-25 12:11:53 -0700193 if (pos + 2 != tokens.size()) {
194 *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
195 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700196 return {};
197 }
198 const std::string& id = tokens[pos++];
199 RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
200 if (!src_ranges) {
201 *err = "invalid token";
202 return {};
203 }
204 stash_info = StashInfo(id, src_ranges);
205 } else if (op == Type::FREE) {
206 // free <stash_id>
Tao Bao92f33932018-06-25 12:11:53 -0700207 if (pos + 1 != tokens.size()) {
208 *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
209 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700210 return {};
211 }
212 stash_info = StashInfo(tokens[pos++], {});
213 } else if (op == Type::MOVE) {
214 // <hash>
215 if (pos + 1 > tokens.size()) {
216 *err = "missing hash";
217 return {};
218 }
219 std::string hash = tokens[pos++];
220 if (!ParseTargetInfoAndSourceInfo(
221 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
222 hash, &source_info, err)) {
223 return {};
224 }
225 } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
226 // <offset> <length> <srchash> <dsthash>
227 if (pos + 4 > tokens.size()) {
Tao Bao92f33932018-06-25 12:11:53 -0700228 *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
229 tokens.size() - pos);
Tao Bao6a7e4af2018-06-14 21:57:43 -0700230 return {};
231 }
232 size_t offset;
233 size_t length;
234 if (!android::base::ParseUint(tokens[pos++], &offset) ||
235 !android::base::ParseUint(tokens[pos++], &length)) {
236 *err = "invalid patch offset/length";
237 return {};
238 }
239 patch_info = PatchInfo(offset, length);
240
241 std::string src_hash = tokens[pos++];
242 std::string dst_hash = tokens[pos++];
243 if (!ParseTargetInfoAndSourceInfo(
244 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
245 src_hash, &source_info, err)) {
246 return {};
247 }
Tao Bao91a649a2018-05-21 16:05:56 -0700248 } else if (op == Type::ABORT) {
249 // No-op, other than sanity checking the input args.
250 if (pos != tokens.size()) {
251 *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
252 tokens.size() - pos);
253 return {};
254 }
Tao Bao6a7e4af2018-06-14 21:57:43 -0700255 } else {
256 *err = "invalid op";
257 return {};
258 }
259
260 return Command(op, index, line, patch_info, target_info, source_info, stash_info);
261}
262
263std::ostream& operator<<(std::ostream& os, const Command& command) {
264 os << command.index() << ": " << command.cmdline();
265 return os;
266}
267
268std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
269 os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
270 return os;
271}
272
273std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
274 os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
275 return os;
276}
277
278std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
279 os << source.blocks_ << " blocks (" << source.hash_ << "): ";
280 if (source.ranges_) {
281 os << source.ranges_.ToString();
282 if (source.location_) {
283 os << " (location: " << source.location_.ToString() << ")";
284 }
285 }
286 if (!source.stashes_.empty()) {
287 os << " " << source.stashes_.size() << " stash(es)";
288 }
289 return os;
290}