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