blob: 6d4b5310bb674d7319cf81f6a06332ba052f1462 [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) {
57 // We expect the given tokens parameter in one of the following formats.
58 //
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
68 // At least it needs to provide three parameters: <tgt_ranges>, <src_block_count> and
69 // "-"/<src_ranges>.
70 if (tokens.size() < 3) {
71 *err = "invalid number of parameters";
72 return false;
73 }
74
75 size_t pos = 0;
76 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
77 if (!tgt_ranges) {
78 *err = "invalid target ranges";
79 return false;
80 }
81 *target = TargetInfo(tgt_hash, tgt_ranges);
82
83 // <src_block_count>
84 const std::string& token = tokens[pos++];
85 size_t src_blocks;
86 if (!android::base::ParseUint(token, &src_blocks)) {
87 *err = "invalid src_block_count \""s + token + "\"";
88 return false;
89 }
90
91 RangeSet src_ranges;
92 RangeSet src_ranges_location;
93 // "-" or <src_ranges> [<src_ranges_location>]
94 if (tokens[pos] == "-") {
95 // no source ranges, only stashes
96 pos++;
97 } else {
98 src_ranges = RangeSet::Parse(tokens[pos++]);
99 if (!src_ranges) {
100 *err = "invalid source ranges";
101 return false;
102 }
103
104 if (pos >= tokens.size()) {
105 // No stashes, only source ranges.
106 SourceInfo result(src_hash, src_ranges, {}, {});
107
108 // Sanity check the block count.
109 if (result.blocks() != src_blocks) {
110 *err =
111 android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
112 src_ranges.ToString().c_str(), src_blocks);
113 return false;
114 }
115
116 *source = result;
117 return true;
118 }
119
120 src_ranges_location = RangeSet::Parse(tokens[pos++]);
121 if (!src_ranges_location) {
122 *err = "invalid source ranges location";
123 return false;
124 }
125 }
126
127 // <[stash_id:stash_location]>
128 std::vector<StashInfo> stashes;
129 while (pos < tokens.size()) {
130 // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
131 // in the source block that stashed data should go.
132 std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
133 if (pairs.size() != 2) {
134 *err = "invalid stash info";
135 return false;
136 }
137 RangeSet stash_location = RangeSet::Parse(pairs[1]);
138 if (!stash_location) {
139 *err = "invalid stash location";
140 return false;
141 }
142 stashes.emplace_back(pairs[0], stash_location);
143 }
144
145 SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
146 if (src_blocks != result.blocks()) {
147 *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
148 src_ranges.ToString().c_str(), src_blocks);
149 return false;
150 }
151
152 *source = result;
153 return true;
154}
155
156Command Command::Parse(const std::string& line, size_t index, std::string* err) {
157 std::vector<std::string> tokens = android::base::Split(line, " ");
158 size_t pos = 0;
159 // tokens.size() will be 1 at least.
160 Type op = ParseType(tokens[pos++]);
161 if (op == Type::LAST) {
162 *err = "invalid type";
163 return {};
164 }
165
166 PatchInfo patch_info;
167 TargetInfo target_info;
168 SourceInfo source_info;
169 StashInfo stash_info;
170
171 if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
172 // zero/new/erase <rangeset>
173 RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
174 if (!tgt_ranges) {
175 return {};
176 }
177 static const std::string kUnknownHash{ "unknown-hash" };
178 target_info = TargetInfo(kUnknownHash, tgt_ranges);
179 } else if (op == Type::STASH) {
180 // stash <stash_id> <src_ranges>
181 if (pos + 2 > tokens.size()) {
182 *err = "missing stash id and/or source ranges";
183 return {};
184 }
185 const std::string& id = tokens[pos++];
186 RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
187 if (!src_ranges) {
188 *err = "invalid token";
189 return {};
190 }
191 stash_info = StashInfo(id, src_ranges);
192 } else if (op == Type::FREE) {
193 // free <stash_id>
194 if (pos + 1 > tokens.size()) {
195 *err = "missing stash id in free command";
196 return {};
197 }
198 stash_info = StashInfo(tokens[pos++], {});
199 } else if (op == Type::MOVE) {
200 // <hash>
201 if (pos + 1 > tokens.size()) {
202 *err = "missing hash";
203 return {};
204 }
205 std::string hash = tokens[pos++];
206 if (!ParseTargetInfoAndSourceInfo(
207 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
208 hash, &source_info, err)) {
209 return {};
210 }
211 } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
212 // <offset> <length> <srchash> <dsthash>
213 if (pos + 4 > tokens.size()) {
214 *err = "invalid number of tokens";
215 return {};
216 }
217 size_t offset;
218 size_t length;
219 if (!android::base::ParseUint(tokens[pos++], &offset) ||
220 !android::base::ParseUint(tokens[pos++], &length)) {
221 *err = "invalid patch offset/length";
222 return {};
223 }
224 patch_info = PatchInfo(offset, length);
225
226 std::string src_hash = tokens[pos++];
227 std::string dst_hash = tokens[pos++];
228 if (!ParseTargetInfoAndSourceInfo(
229 std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
230 src_hash, &source_info, err)) {
231 return {};
232 }
233 } else {
234 *err = "invalid op";
235 return {};
236 }
237
238 return Command(op, index, line, patch_info, target_info, source_info, stash_info);
239}
240
241std::ostream& operator<<(std::ostream& os, const Command& command) {
242 os << command.index() << ": " << command.cmdline();
243 return os;
244}
245
246std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
247 os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
248 return os;
249}
250
251std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
252 os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
253 return os;
254}
255
256std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
257 os << source.blocks_ << " blocks (" << source.hash_ << "): ";
258 if (source.ranges_) {
259 os << source.ranges_.ToString();
260 if (source.location_) {
261 os << " (location: " << source.location_.ToString() << ")";
262 }
263 }
264 if (!source.stashes_.empty()) {
265 os << " " << source.stashes_.size() << " stash(es)";
266 }
267 return os;
268}