1 //
2 // Copyright (C) 2020 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 #include <stdio.h>
17 #include <unistd.h>
18
19 #include <chrono>
20 #include <iomanip>
21 #include <iostream>
22 #include <string>
23 #include <vector>
24
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/unique_fd.h>
28 #include <gflags/gflags.h>
29 #include <libsnapshot/cow_reader.h>
30 #include "parser_v2.h"
31
32 DEFINE_bool(silent, false, "Run silently");
33 DEFINE_bool(decompress, false, "Attempt to decompress data ops");
34 DEFINE_bool(show_bad_data, false, "If an op fails to decompress, show its daw data");
35 DEFINE_bool(show_ops, false, "Print all opcode information");
36 DEFINE_string(order, "", "If show_ops is true, change the order (either merge or reverse-merge)");
37 DEFINE_bool(show_merged, false,
38 "If show_ops is true, and order is merge or reverse-merge, include merged ops");
39 DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing");
40 DEFINE_bool(show_merge_sequence, false, "Show merge order sequence");
41 DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser");
42 DEFINE_string(extract_to, "", "Extract the COW contents to the given file");
43
44 namespace android {
45 namespace snapshot {
46
47 using android::base::borrowed_fd;
48 using android::base::unique_fd;
49
MyLogger(android::base::LogId,android::base::LogSeverity severity,const char *,const char *,unsigned int,const char * message)50 void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
51 unsigned int, const char* message) {
52 if (severity == android::base::ERROR) {
53 fprintf(stderr, "%s\n", message);
54 } else {
55 fprintf(stdout, "%s\n", message);
56 }
57 }
58
ShowBad(CowReader & reader,const CowOperation * op)59 static void ShowBad(CowReader& reader, const CowOperation* op) {
60 size_t count;
61 auto buffer = std::make_unique<uint8_t[]>(op->data_length);
62
63 if (!reader.GetRawBytes(op, buffer.get(), op->data_length, &count)) {
64 std::cerr << "Failed to read at all!\n";
65 } else {
66 std::cout << "The Block data is:\n";
67 for (int i = 0; i < op->data_length; i++) {
68 std::cout << std::hex << (int)buffer[i];
69 }
70 std::cout << std::dec << "\n\n";
71 if (op->data_length >= sizeof(CowOperation)) {
72 std::cout << "The start, as an op, would be " << *(CowOperation*)buffer.get() << "\n";
73 }
74 }
75 }
76
ShowRawOpStreamV2(borrowed_fd fd,const CowHeaderV3 & header)77 static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeaderV3& header) {
78 CowParserV2 parser;
79 if (!parser.Parse(fd, header)) {
80 LOG(ERROR) << "v2 parser failed";
81 return false;
82 }
83 for (const auto& op : *parser.get_v2ops()) {
84 std::cout << op << "\n";
85 if (auto iter = parser.xor_data_loc()->find(op.new_block);
86 iter != parser.xor_data_loc()->end()) {
87 std::cout << " data loc: " << iter->second << "\n";
88 }
89 }
90 return true;
91 }
92
ShowRawOpStream(borrowed_fd fd)93 static bool ShowRawOpStream(borrowed_fd fd) {
94 CowHeaderV3 header;
95 if (!ReadCowHeader(fd, &header)) {
96 LOG(ERROR) << "parse header failed";
97 return false;
98 }
99
100 switch (header.prefix.major_version) {
101 case 1:
102 case 2:
103 return ShowRawOpStreamV2(fd, header);
104 default:
105 LOG(ERROR) << "unknown COW version: " << header.prefix.major_version;
106 return false;
107 }
108 }
109
Inspect(const std::string & path)110 static bool Inspect(const std::string& path) {
111 unique_fd fd(open(path.c_str(), O_RDONLY));
112 if (fd < 0) {
113 PLOG(ERROR) << "open failed: " << path;
114 return false;
115 }
116
117 unique_fd extract_to;
118 if (!FLAGS_extract_to.empty()) {
119 extract_to.reset(open(FLAGS_extract_to.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));
120 if (extract_to < 0) {
121 PLOG(ERROR) << "could not open " << FLAGS_extract_to << " for writing";
122 return false;
123 }
124 }
125
126 CowReader reader;
127
128 auto start_time = std::chrono::steady_clock::now();
129 if (!reader.Parse(fd)) {
130 LOG(ERROR) << "parse failed: " << path;
131 return false;
132 }
133 std::chrono::duration<double> parse_time = std::chrono::steady_clock::now() - start_time;
134
135 const CowHeader& header = reader.GetHeader();
136 CowFooter footer;
137 bool has_footer = false;
138 if (reader.GetFooter(&footer)) has_footer = true;
139
140 if (!FLAGS_silent) {
141 std::cout << "Version: " << header.prefix.major_version << "."
142 << header.prefix.minor_version << "\n";
143 std::cout << "Header size: " << header.prefix.header_size << "\n";
144 std::cout << "Footer size: " << header.footer_size << "\n";
145 std::cout << "Block size: " << header.block_size << "\n";
146 std::cout << "Merge ops: " << header.num_merge_ops << "\n";
147 std::cout << "Readahead buffer: " << header.buffer_size << " bytes\n";
148 if (has_footer) {
149 std::cout << "Footer: ops usage: " << footer.op.ops_size << " bytes\n";
150 std::cout << "Footer: op count: " << footer.op.num_ops << "\n";
151 } else {
152 std::cout << "Footer: none\n";
153 }
154 }
155
156 if (!FLAGS_silent) {
157 std::cout << "Parse time: " << (parse_time.count() * 1000) << "ms\n";
158 }
159
160 if (FLAGS_verify_merge_sequence) {
161 std::cout << "\n";
162 if (reader.VerifyMergeOps()) {
163 std::cout << "\nMerge sequence is consistent.\n";
164 } else {
165 std::cout << "\nMerge sequence is inconsistent!\n";
166 }
167 }
168
169 std::unique_ptr<ICowOpIter> iter;
170 if (FLAGS_order.empty()) {
171 iter = reader.GetOpIter();
172 } else if (FLAGS_order == "reverse-merge") {
173 iter = reader.GetRevMergeOpIter(FLAGS_show_merged);
174 } else if (FLAGS_order == "merge") {
175 iter = reader.GetMergeOpIter(FLAGS_show_merged);
176 }
177
178 std::string buffer(header.block_size, '\0');
179
180 if (!FLAGS_silent && FLAGS_show_raw_ops) {
181 std::cout << "\n";
182 std::cout << "Listing raw op stream:\n";
183 std::cout << "----------------------\n";
184 if (!ShowRawOpStream(fd)) {
185 return false;
186 }
187 }
188
189 if (!FLAGS_silent && FLAGS_show_ops) {
190 std::cout << "\n";
191 std::cout << "Listing op stream:\n";
192 std::cout << "------------------\n";
193 }
194
195 bool success = true;
196 uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
197 while (!iter->AtEnd()) {
198 const CowOperation* op = iter->Get();
199
200 if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
201
202 if ((FLAGS_decompress || extract_to >= 0) && op->type() == kCowReplaceOp) {
203 if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
204 std::cerr << "Failed to decompress for :" << *op << "\n";
205 success = false;
206 if (FLAGS_show_bad_data) ShowBad(reader, op);
207 }
208 if (extract_to >= 0) {
209 off_t offset = uint64_t(op->new_block) * header.block_size;
210 if (!android::base::WriteFullyAtOffset(extract_to, buffer.data(), buffer.size(),
211 offset)) {
212 PLOG(ERROR) << "failed to write block " << op->new_block;
213 return false;
214 }
215 }
216 } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type() != kCowZeroOp) {
217 PLOG(ERROR) << "Cannot extract op yet: " << *op;
218 return false;
219 }
220
221 if (op->type() == kCowSequenceOp && FLAGS_show_merge_sequence) {
222 size_t read;
223 std::vector<uint32_t> merge_op_blocks;
224 size_t seq_len = op->data_length / sizeof(uint32_t);
225 merge_op_blocks.resize(seq_len);
226 if (!reader.GetRawBytes(op, merge_op_blocks.data(), op->data_length, &read)) {
227 PLOG(ERROR) << "Failed to read sequence op!";
228 return false;
229 }
230 if (!FLAGS_silent) {
231 std::cout << "Sequence for " << *op << " is :\n";
232 for (size_t i = 0; i < seq_len; i++) {
233 std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
234 if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << "\n";
235 }
236 }
237 }
238
239 if (op->type() == kCowCopyOp) {
240 copy_ops++;
241 } else if (op->type() == kCowReplaceOp) {
242 replace_ops++;
243 } else if (op->type() == kCowZeroOp) {
244 zero_ops++;
245 } else if (op->type() == kCowXorOp) {
246 xor_ops++;
247 }
248
249 iter->Next();
250 }
251
252 if (!FLAGS_silent) {
253 auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
254 std::cout << "Data ops: " << total_ops << "\n";
255 std::cout << "Replace ops: " << replace_ops << "\n";
256 std::cout << "Zero ops: " << zero_ops << "\n";
257 std::cout << "Copy ops: " << copy_ops << "\n";
258 std::cout << "Xor ops: " << xor_ops << "\n";
259 }
260
261 return success;
262 }
263
264 } // namespace snapshot
265 } // namespace android
266
main(int argc,char ** argv)267 int main(int argc, char** argv) {
268 gflags::ParseCommandLineFlags(&argc, &argv, true);
269
270 if (argc < 2) {
271 gflags::ShowUsageWithFlags(argv[0]);
272 return 1;
273 }
274 if (FLAGS_order != "" && FLAGS_order != "merge" && FLAGS_order != "reverse-merge") {
275 std::cerr << "Order must either be \"merge\" or \"reverse-merge\".\n";
276 return 1;
277 }
278
279 android::base::InitLogging(argv, android::snapshot::MyLogger);
280
281 if (!android::snapshot::Inspect(argv[1])) {
282 return 1;
283 }
284 return 0;
285 }
286