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 "dump.h"
17
18 #include <android-base/file.h>
19 #include <android-base/logging.h>
20 #include <android-base/parsebool.h>
21 #include <android-base/properties.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <android-base/unique_fd.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <libgen.h>
28 #include <openssl/sha.h>
29 #include <selinux/android.h>
30 #include <selinux/selinux.h>
31 #include <sys/mount.h>
32 #include <sys/poll.h>
33 #include <sys/stat.h>
34 #include <sys/syscall.h>
35 #include <sys/types.h>
36 #include <sys/vfs.h>
37 #include <sys/xattr.h>
38 #include <unistd.h>
39
40 #include "linux/incrementalfs.h"
41
42 #include <chrono>
43 #include <fstream>
44 #include <iostream>
45 #include <iterator>
46 #include <optional>
47 #include <string_view>
48
49 using namespace std::literals;
50
51 namespace {
52
53 // stuff from the internal incfs implementation
54
55 #ifndef __packed
56 #define __packed __attribute__((packed))
57 #endif
58
59 struct mem_range {
60 char* data;
61 size_t len;
62 };
63
64 #define INCFS_MAX_NAME_LEN 255
65 #define INCFS_FORMAT_V1 1
66 #define INCFS_FORMAT_CURRENT_VER INCFS_FORMAT_V1
67
68 enum incfs_metadata_type {
69 INCFS_MD_NONE = 0,
70 INCFS_MD_BLOCK_MAP = 1,
71 INCFS_MD_FILE_ATTR = 2,
72 INCFS_MD_SIGNATURE = 3
73 };
74
75 enum incfs_file_header_flags {
76 INCFS_FILE_COMPLETE = 1 << 0,
77 };
78
79 /* Header included at the beginning of all metadata records on the disk. */
80 struct incfs_md_header {
81 uint8_t h_md_entry_type;
82
83 /*
84 * Size of the metadata record.
85 * (e.g. inode, dir entry etc) not just this struct.
86 */
87 int16_t h_record_size;
88
89 /*
90 * CRC32 of the metadata record.
91 * (e.g. inode, dir entry etc) not just this struct.
92 */
93 int32_t h_record_crc;
94
95 /* Offset of the next metadata entry if any */
96 int64_t h_next_md_offset;
97
98 /* Offset of the previous metadata entry if any */
99 int64_t h_prev_md_offset;
100
101 } __packed;
102
103 /* Backing file header */
104 struct incfs_file_header {
105 /* Magic number: INCFS_MAGIC_NUMBER */
106 int64_t fh_magic;
107
108 /* Format version: INCFS_FORMAT_CURRENT_VER */
109 int64_t fh_version;
110
111 /* sizeof(incfs_file_header) */
112 int16_t fh_header_size;
113
114 /* INCFS_DATA_FILE_BLOCK_SIZE */
115 int16_t fh_data_block_size;
116
117 /* File flags, from incfs_file_header_flags */
118 int32_t fh_file_header_flags;
119
120 /* Offset of the first metadata record */
121 int64_t fh_first_md_offset;
122
123 /*
124 * Put file specific information after this point
125 */
126
127 /* Full size of the file's content */
128 int64_t fh_file_size;
129
130 /* File uuid */
131 incfs_uuid_t fh_uuid;
132 } __packed;
133
134 enum incfs_block_map_entry_flags {
135 INCFS_BLOCK_COMPRESSED_LZ4 = (1 << 0),
136 INCFS_BLOCK_HASH = (1 << 1),
137 };
138
139 /* Block map entry pointing to an actual location of the data block. */
140 struct incfs_blockmap_entry {
141 /* Offset of the actual data block. Lower 32 bits */
142 int32_t me_data_offset_lo;
143
144 /* Offset of the actual data block. Higher 16 bits */
145 int16_t me_data_offset_hi;
146
147 /* How many bytes the data actually occupies in the backing file */
148 int16_t me_data_size;
149
150 /* Block flags from incfs_block_map_entry_flags */
151 int16_t me_flags;
152 } __packed;
153
154 /* Metadata record for locations of file blocks. Type = INCFS_MD_BLOCK_MAP */
155 struct incfs_blockmap {
156 struct incfs_md_header m_header;
157
158 /* Base offset of the array of incfs_blockmap_entry */
159 int64_t m_base_offset;
160
161 /* Size of the map entry array in blocks */
162 int32_t m_block_count;
163 } __packed;
164
165 /* Metadata record for file attribute. Type = INCFS_MD_FILE_ATTR */
166 struct incfs_file_attr {
167 struct incfs_md_header fa_header;
168
169 int64_t fa_offset;
170
171 int16_t fa_size;
172
173 int32_t fa_crc;
174 } __packed;
175
176 /* Metadata record for file signature. Type = INCFS_MD_SIGNATURE */
177 struct incfs_file_signature {
178 struct incfs_md_header sg_header;
179
180 int32_t sg_sig_size; /* The size of the signature. */
181
182 int64_t sg_sig_offset; /* Signature's offset in the backing file */
183
184 int32_t sg_hash_tree_size; /* The size of the hash tree. */
185
186 int64_t sg_hash_tree_offset; /* Hash tree offset in the backing file */
187 } __packed;
188
189 typedef union {
190 struct incfs_md_header md_header;
191 struct incfs_blockmap blockmap;
192 struct incfs_file_attr file_attr;
193 struct incfs_file_signature signature;
194 } md_buffer;
195
196 #define INCFS_MAX_METADATA_RECORD_SIZE sizeof(md_buffer)
197
198 class Dump {
199 public:
Dump(std::string_view backingFile)200 Dump(std::string_view backingFile)
201 : mBackingFile(android::base::Basename(std::string(backingFile))), mIn(backingFile) {}
202
run()203 void run() {
204 if (!mIn) {
205 err() << "bad input file name " << mBackingFile;
206 return;
207 }
208
209 auto header = read<incfs_file_header>();
210 out() << "header: " << hex(header.fh_magic) << ", " << header.fh_version << ", "
211 << hex(header.fh_data_block_size) << ", " << header.fh_header_size << ", "
212 << header.fh_file_size;
213 if (header.fh_magic != INCFS_MAGIC_NUMBER) {
214 err() << "bad magic, expected: " << hex(INCFS_MAGIC_NUMBER);
215 }
216 if (header.fh_version != INCFS_FORMAT_CURRENT_VER) {
217 err() << "bad version, expected: " << INCFS_FORMAT_CURRENT_VER;
218 }
219 if (header.fh_data_block_size != INCFS_DATA_FILE_BLOCK_SIZE) {
220 err() << "bad data block size, expected: " << hex(INCFS_DATA_FILE_BLOCK_SIZE);
221 }
222 if (header.fh_header_size != sizeof(header)) {
223 err() << "bad header size, expected: " << sizeof(header);
224 }
225 {
226 auto ostream = out() << "flags: " << hex(header.fh_file_header_flags);
227 if (header.fh_file_header_flags & INCFS_FILE_COMPLETE) {
228 out() << "(file_complete)";
229 }
230 }
231
232 out() << "first metadata block offset: " << hex(header.fh_first_md_offset);
233
234 auto metadataOffset = header.fh_first_md_offset;
235 if (mIn.tellg() != metadataOffset) {
236 out() << "gap of " << metadataOffset - mIn.tellg()
237 << " bytes to the first metadata record";
238 }
239 incfs_md_header prevMd = {};
240 do {
241 dumpMd(metadataOffset, prevMd);
242 } while (metadataOffset != 0);
243 out() << "finished" << (mIn ? "" : " with read errors");
244 }
245
246 private:
scopedNesting()247 auto scopedNesting() {
248 ++mNesting;
249 auto undoNesting = [this](auto) { --mNesting; };
250 return std::unique_ptr<Dump, decltype(undoNesting)>(this, std::move(undoNesting));
251 }
252
mdType(int type)253 const char* mdType(int type) {
254 switch (type) {
255 case INCFS_MD_NONE:
256 return "none";
257 case INCFS_MD_BLOCK_MAP:
258 return "block map";
259 case INCFS_MD_FILE_ATTR:
260 return "file attr";
261 case INCFS_MD_SIGNATURE:
262 return "signature";
263 default:
264 return "unknown";
265 }
266 }
267
blockFlags(int flags)268 std::string blockFlags(int flags) {
269 if (!flags) {
270 return {};
271 }
272 std::string res = "(";
273 if (flags & INCFS_BLOCK_COMPRESSED_LZ4) {
274 res += "|compressed|";
275 }
276 if (flags & INCFS_BLOCK_HASH) {
277 res += "|hash|";
278 }
279 res += ")";
280 return res;
281 }
282
dumpBlockmap(int64_t offset,int64_t count)283 void dumpBlockmap(int64_t offset, int64_t count) {
284 auto nesting = scopedNesting();
285 mIn.seekg(offset);
286 for (int64_t i = 0; i != count; ++i) {
287 auto ostream = out() << i << " @ " << hex(mIn.tellg()) << ": [ ";
288
289 auto block = read<incfs_blockmap_entry>();
290 auto blockOffset =
291 uint64_t(block.me_data_offset_lo) | (uint64_t(block.me_data_offset_hi) << 32);
292 if (blockOffset) {
293 ostream << block.me_data_size << " @ " << hex(blockOffset);
294 } else {
295 ostream << "missing";
296 }
297 ostream << " ], flags = " << block.me_flags << blockFlags(block.me_flags);
298 }
299 }
300
dumpAttr(int64_t offset,int64_t size)301 void dumpAttr(int64_t offset, int64_t size) {
302 auto nesting = scopedNesting();
303 out() << "attr " << offset << " " << size;
304 }
305
dumpTree(int64_t offset,int64_t size)306 void dumpTree(int64_t offset, int64_t size) {
307 auto nesting = scopedNesting();
308 out() << "tree " << offset << " " << size;
309 }
310
dumpMd(int64_t & offset,incfs_md_header & prevMd)311 void dumpMd(int64_t& offset, incfs_md_header& prevMd) {
312 md_buffer mdBuf = {};
313 auto& md = mdBuf.md_header;
314 md = readAt<incfs_md_header>(offset);
315 out() << "metadata: " << mdType(md.h_md_entry_type) << "(" << int(md.h_md_entry_type)
316 << ")";
317
318 auto nesting = scopedNesting();
319 out() << "record size: " << md.h_record_size;
320 out() << "record crc: " << hex(md.h_record_crc);
321 out() << "next md offset: " << hex(md.h_next_md_offset);
322 out() << "prev md offset: " << hex(md.h_prev_md_offset);
323
324 {
325 switch (md.h_md_entry_type) {
326 case INCFS_MD_NONE:
327 out() << "nothing here";
328 break;
329 case INCFS_MD_BLOCK_MAP: {
330 auto& bm = mdBuf.blockmap;
331 bm = readAt<decltype(bm)>(offset);
332 out() << "offset: " << hex(bm.m_base_offset);
333 out() << "block count: " << bm.m_block_count;
334 dumpBlockmap(bm.m_base_offset, bm.m_block_count);
335 break;
336 }
337 case INCFS_MD_FILE_ATTR: {
338 auto& attr = mdBuf.file_attr;
339 attr = readAt<decltype(attr)>(offset);
340 out() << "offset: " << hex(attr.fa_offset);
341 out() << "size: " << attr.fa_size;
342 out() << "crc: " << hex(attr.fa_crc);
343 dumpAttr(attr.fa_offset, attr.fa_size);
344 break;
345 }
346 case INCFS_MD_SIGNATURE: {
347 auto& sig = mdBuf.signature;
348 sig = readAt<decltype(sig)>(offset);
349 out() << "signature size: " << sig.sg_sig_size;
350 out() << "signature offset: " << hex(sig.sg_sig_offset);
351 out() << "hash tree size: " << sig.sg_hash_tree_size;
352 out() << "hash tree offset: " << hex(sig.sg_hash_tree_offset);
353 dumpTree(sig.sg_hash_tree_offset, sig.sg_hash_tree_size);
354 break;
355 }
356 default:
357 out() << "don't know how to handle it";
358 break;
359 }
360 }
361
362 updateMaxPos();
363 prevMd = md;
364 offset = md.h_next_md_offset;
365 }
366
367 struct OstreamWrapper {
OstreamWrapper__anon0107e9d80111::Dump::OstreamWrapper368 explicit OstreamWrapper(std::ostream& wrapped) : mWrapped(&wrapped) {}
OstreamWrapper__anon0107e9d80111::Dump::OstreamWrapper369 OstreamWrapper(OstreamWrapper&& other) : mWrapped(std::exchange(other.mWrapped, nullptr)) {}
~OstreamWrapper__anon0107e9d80111::Dump::OstreamWrapper370 ~OstreamWrapper() {
371 if (mWrapped) {
372 *mWrapped << '\n';
373 }
374 }
375
376 template <class T>
operator <<__anon0107e9d80111::Dump::OstreamWrapper377 OstreamWrapper& operator<<(const T& t) & {
378 *mWrapped << t;
379 return *this;
380 }
381 template <class T>
operator <<__anon0107e9d80111::Dump::OstreamWrapper382 OstreamWrapper&& operator<<(const T& t) && {
383 *this << t;
384 return std::move(*this);
385 }
386
387 private:
388 std::ostream* mWrapped;
389 };
390
hex(uint64_t t)391 std::string hex(uint64_t t) {
392 char buf[32] = {};
393 snprintf(buf, std::size(buf) - 1, "0x%llx", (unsigned long long)t);
394 return buf;
395 }
396
out() const397 OstreamWrapper out() const {
398 nesting(std::cout);
399 std::cout << "[" << mBackingFile << "] ";
400 return OstreamWrapper(std::cout);
401 }
402
err() const403 OstreamWrapper err() const {
404 nesting(std::cerr);
405 std::cerr << "[" << mBackingFile << "] ";
406 return OstreamWrapper(std::cerr);
407 }
408
nesting(std::ostream & out) const409 void nesting(std::ostream& out) const {
410 for (int i = 0; i < mNesting; ++i) {
411 out << " ";
412 }
413 }
414
415 template <class T>
read()416 std::remove_reference_t<T> read() {
417 std::remove_reference_t<T> res;
418 mIn.read((char*)&res, sizeof(res));
419 return res;
420 }
421
422 template <class T>
readAt(int64_t pos)423 std::remove_reference_t<T> readAt(int64_t pos) {
424 mIn.seekg(pos);
425 return read<T>();
426 }
427
skip(int64_t count)428 void skip(int64_t count) { mIn.seekg(count, std::ios_base::cur); }
429
updateMaxPos()430 void updateMaxPos() { mMaxDumpedPos = std::max<int64_t>(mMaxDumpedPos, mIn.tellg()); }
431
432 std::string mBackingFile;
433 std::ifstream mIn;
434 int mNesting = 0;
435 int64_t mMaxDumpedPos = 0;
436 };
437
438 } // namespace
439
440 namespace android::incfs {
441
dump(std::string_view backingFile)442 void dump(std::string_view backingFile) {
443 Dump(backingFile).run();
444 }
445
446 } // namespace android::incfs
447