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 <chrono>
41 #include <fstream>
42 #include <iomanip>
43 #include <iostream>
44 #include <iterator>
45 #include <optional>
46 #include <sstream>
47 #include <string_view>
48
49 #include "linux/incrementalfs.h"
50
51 using namespace std::literals;
52
53 namespace {
54
55 // stuff from the internal incfs implementation
56
57 #ifndef __packed
58 #define __packed __attribute__((packed))
59 #endif
60
61 struct mem_range {
62 char* data;
63 size_t len;
64 };
65
66 #define INCFS_MAX_NAME_LEN 255
67 #define INCFS_FORMAT_V1 1
68 #define INCFS_FORMAT_CURRENT_VER INCFS_FORMAT_V1
69
70 enum incfs_metadata_type {
71 INCFS_MD_NONE = 0,
72 INCFS_MD_BLOCK_MAP = 1,
73 INCFS_MD_FILE_ATTR = 2,
74 INCFS_MD_SIGNATURE = 3,
75 INCFS_MD_STATUS = 4,
76 INCFS_MD_VERITY_SIGNATURE = 5,
77 };
78
79 enum incfs_file_header_flags {
80 INCFS_FILE_MAPPED = 1 << 1,
81 };
82
83 /* Header included at the beginning of all metadata records on the disk. */
84 struct incfs_md_header {
85 uint8_t h_md_entry_type;
86
87 /*
88 * Size of the metadata record.
89 * (e.g. inode, dir entry etc) not just this struct.
90 */
91 int16_t h_record_size;
92
93 /*
94 * CRC32 of the metadata record.
95 * (e.g. inode, dir entry etc) not just this struct.
96 */
97 int32_t h_unused1;
98
99 /* Offset of the next metadata entry if any */
100 int64_t h_next_md_offset;
101
102 /* Offset of the previous metadata entry if any */
103 int64_t h_unused2;
104
105 } __packed;
106
107 /* Backing file header */
108 struct incfs_file_header {
109 /* Magic number: INCFS_MAGIC_NUMBER */
110 __le64 fh_magic;
111
112 /* Format version: INCFS_FORMAT_CURRENT_VER */
113 __le64 fh_version;
114
115 /* sizeof(incfs_file_header) */
116 __le16 fh_header_size;
117
118 /* INCFS_DATA_FILE_BLOCK_SIZE */
119 __le16 fh_data_block_size;
120
121 /* File flags, from incfs_file_header_flags */
122 __le32 fh_flags;
123
124 union {
125 /* Standard incfs file */
126 struct {
127 /* Offset of the first metadata record */
128 __le64 fh_first_md_offset;
129
130 /* Full size of the file's content */
131 __le64 fh_file_size;
132
133 /* File uuid */
134 incfs_uuid_t fh_uuid;
135 };
136
137 /* Mapped file - INCFS_FILE_MAPPED set in fh_flags */
138 struct {
139 /* Offset in original file */
140 __le64 fh_original_offset;
141
142 /* Full size of the file's content */
143 __le64 fh_mapped_file_size;
144
145 /* Original file's uuid */
146 incfs_uuid_t fh_original_uuid;
147 };
148 };
149 } __packed;
150
151 enum incfs_block_map_entry_flags {
152 INCFS_BLOCK_COMPRESSED_LZ4 = 1,
153 INCFS_BLOCK_COMPRESSED_ZSTD = 2,
154
155 /* Reserve 3 bits for compression alg */
156 INCFS_BLOCK_COMPRESSED_MASK = 7,
157 };
158
159 /* Block map entry pointing to an actual location of the data block. */
160 struct incfs_blockmap_entry {
161 /* Offset of the actual data block. Lower 32 bits */
162 int32_t me_data_offset_lo;
163
164 /* Offset of the actual data block. Higher 16 bits */
165 int16_t me_data_offset_hi;
166
167 /* How many bytes the data actually occupies in the backing file */
168 int16_t me_data_size;
169
170 /* Block flags from incfs_block_map_entry_flags */
171 int16_t me_flags;
172 } __packed;
173
174 /* Metadata record for locations of file blocks. Type = INCFS_MD_BLOCK_MAP */
175 struct incfs_blockmap {
176 struct incfs_md_header m_header;
177
178 /* Base offset of the array of incfs_blockmap_entry */
179 int64_t m_base_offset;
180
181 /* Size of the map entry array in blocks */
182 int32_t m_block_count;
183 } __packed;
184
185 /* Metadata record for file signature. Type = INCFS_MD_SIGNATURE */
186 struct incfs_file_signature {
187 struct incfs_md_header sg_header;
188
189 int32_t sg_sig_size; /* The size of the signature. */
190
191 int64_t sg_sig_offset; /* Signature's offset in the backing file */
192
193 int32_t sg_hash_tree_size; /* The size of the hash tree. */
194
195 int64_t sg_hash_tree_offset; /* Hash tree offset in the backing file */
196 } __packed;
197
198 struct incfs_status {
199 struct incfs_md_header is_header;
200
201 __le32 is_data_blocks_written; /* Number of data blocks written */
202
203 __le32 is_hash_blocks_written; /* Number of hash blocks written */
204
205 __le32 is_dummy[6]; /* Spare fields */
206 } __packed;
207
208 /*
209 * Metadata record for verity signature. Type = INCFS_MD_VERITY_SIGNATURE
210 *
211 * This record will only exist for verity-enabled files with signatures. Verity
212 * enabled files without signatures do not have this record. This signature is
213 * checked by fs-verity identically to any other fs-verity signature.
214 */
215 struct incfs_file_verity_signature {
216 struct incfs_md_header vs_header;
217
218 /* The size of the signature */
219 __le32 vs_size;
220
221 /* Signature's offset in the backing file */
222 __le64 vs_offset;
223 } __packed;
224
225 typedef union {
226 struct incfs_md_header md_header;
227 struct incfs_blockmap blockmap;
228 struct incfs_file_signature signature;
229 struct incfs_status status;
230 struct incfs_file_verity_signature verity_signature;
231 } md_buffer;
232
233 #define INCFS_MAX_METADATA_RECORD_SIZE sizeof(md_buffer)
234
235 class Dump {
236 public:
Dump(std::string_view backingFile)237 Dump(std::string_view backingFile)
238 : mBackingFile(android::base::Basename(backingFile)) {
239 std::string backingFileStr(backingFile);
240 mIn.open(backingFileStr);
241 }
242
run()243 void run() {
244 if (!mIn) {
245 err() << "bad input file name " << mBackingFile;
246 return;
247 }
248
249 auto header = read<incfs_file_header>();
250 out() << "header: " << hex(header.fh_magic) << ", " << header.fh_version << ", "
251 << hex(header.fh_data_block_size) << ", " << header.fh_header_size << ", "
252 << header.fh_file_size;
253 if (header.fh_magic != INCFS_MAGIC_NUMBER) {
254 err() << "bad magic, expected: " << hex(INCFS_MAGIC_NUMBER);
255 }
256 if (header.fh_version != INCFS_FORMAT_CURRENT_VER) {
257 err() << "bad version, expected: " << INCFS_FORMAT_CURRENT_VER;
258 }
259 if (header.fh_data_block_size != INCFS_DATA_FILE_BLOCK_SIZE) {
260 err() << "bad data block size, expected: " << hex(INCFS_DATA_FILE_BLOCK_SIZE);
261 }
262 if (header.fh_header_size != sizeof(header)) {
263 err() << "bad header size, expected: " << sizeof(header);
264 }
265 {
266 auto ostream = out() << "flags: " << hex(header.fh_flags);
267 if (header.fh_flags & INCFS_FILE_MAPPED) {
268 ostream << " (mapped file)";
269 }
270 }
271
272 if (header.fh_flags & INCFS_FILE_MAPPED) {
273 out() << "source " << toString(header.fh_original_uuid);
274 out() << "size " << header.fh_mapped_file_size << " @ "
275 << hex(header.fh_original_offset);
276 } else {
277 out() << "uuid " << toString(header.fh_uuid);
278 out() << "size " << header.fh_file_size;
279 out() << "first md offset " << hex(header.fh_first_md_offset);
280
281 int64_t metadataOffset = header.fh_first_md_offset;
282 if (metadataOffset >= mIn.tellg()) {
283 if (metadataOffset > mIn.tellg()) {
284 out() << "gap of " << metadataOffset - mIn.tellg()
285 << " bytes to the first metadata record";
286 }
287 incfs_md_header prevMd = {};
288 do {
289 dumpMd(metadataOffset, prevMd);
290 } while (metadataOffset != 0);
291 }
292 }
293 out() << "finished" << (mIn ? "" : " with read errors");
294 }
295
296 private:
scopedNesting()297 auto scopedNesting() {
298 ++mNesting;
299 auto undoNesting = [this](auto) { --mNesting; };
300 return std::unique_ptr<Dump, decltype(undoNesting)>(this, std::move(undoNesting));
301 }
302
mdType(int type)303 const char* mdType(int type) {
304 switch (type) {
305 case INCFS_MD_NONE:
306 return "none";
307 case INCFS_MD_BLOCK_MAP:
308 return "block map";
309 case INCFS_MD_STATUS:
310 return "status";
311 case INCFS_MD_SIGNATURE:
312 return "signature";
313 case INCFS_MD_VERITY_SIGNATURE:
314 return "verity signature";
315 default:
316 return "unknown";
317 }
318 }
319
blockFlags(int flags)320 std::string blockFlags(int flags) {
321 if (!flags) {
322 return {};
323 }
324 std::string res = "(";
325 auto compression = flags & INCFS_BLOCK_COMPRESSED_MASK;
326 if (compression == INCFS_BLOCK_COMPRESSED_LZ4) {
327 res += "|lz4-compressed|";
328 } else if (flags == INCFS_BLOCK_COMPRESSED_ZSTD) {
329 res += "|zstd-compressed|";
330 }
331 res += ")";
332 return res;
333 }
334
dumpBlockmap(int64_t offset,int64_t count)335 void dumpBlockmap(int64_t offset, int64_t count) {
336 auto nesting = scopedNesting();
337 mIn.seekg(offset);
338 for (int64_t i = 0; i != count; ++i) {
339 auto ostream = out() << i << " @ " << hex(mIn.tellg()) << ": [ ";
340
341 auto block = read<incfs_blockmap_entry>();
342 auto blockOffset =
343 uint64_t(block.me_data_offset_lo) | (uint64_t(block.me_data_offset_hi) << 32);
344 if (blockOffset) {
345 ostream << block.me_data_size << " @ " << hex(blockOffset);
346 } else {
347 ostream << "missing";
348 }
349 ostream << " ], flags = " << block.me_flags << blockFlags(block.me_flags);
350 }
351 }
352
dumpTree(int64_t offset,int64_t size)353 void dumpTree(int64_t offset, int64_t size) {
354 auto nesting = scopedNesting();
355 out() << "tree " << offset << " " << size;
356 }
357
dumpMd(int64_t & offset,incfs_md_header & prevMd)358 void dumpMd(int64_t& offset, incfs_md_header& prevMd) {
359 md_buffer mdBuf = {};
360 auto& md = mdBuf.md_header;
361 md = readAt<incfs_md_header>(offset);
362 out() << "metadata: " << mdType(md.h_md_entry_type) << "(" << int(md.h_md_entry_type)
363 << ")";
364
365 auto nesting = scopedNesting();
366 out() << "record size: " << md.h_record_size;
367 out() << "next md offset: " << hex(md.h_next_md_offset);
368
369 {
370 switch (md.h_md_entry_type) {
371 case INCFS_MD_NONE:
372 out() << "nothing here";
373 break;
374 case INCFS_MD_BLOCK_MAP: {
375 auto& bm = mdBuf.blockmap;
376 bm = readAt<decltype(bm)>(offset);
377 out() << "offset: " << hex(bm.m_base_offset);
378 out() << "block count: " << bm.m_block_count;
379 dumpBlockmap(bm.m_base_offset, bm.m_block_count);
380 break;
381 }
382 case INCFS_MD_SIGNATURE: {
383 auto& sig = mdBuf.signature;
384 sig = readAt<decltype(sig)>(offset);
385 out() << "signature size: " << sig.sg_sig_size;
386 out() << "signature offset: " << hex(sig.sg_sig_offset);
387 out() << "hash tree size: " << sig.sg_hash_tree_size;
388 out() << "hash tree offset: " << hex(sig.sg_hash_tree_offset);
389 dumpTree(sig.sg_hash_tree_offset, sig.sg_hash_tree_size);
390 break;
391 }
392 case INCFS_MD_STATUS: {
393 auto& st = mdBuf.status;
394 st = readAt<decltype(st)>(offset);
395 out() << "data blocks written: " << st.is_data_blocks_written;
396 out() << "hash blocks written: " << st.is_hash_blocks_written;
397 break;
398 }
399 case INCFS_MD_VERITY_SIGNATURE: {
400 auto& vs = mdBuf.verity_signature;
401 vs = readAt<decltype(vs)>(offset);
402 out() << "verity signature size: " << vs.vs_size;
403 out() << "verity signature offset: " << hex(vs.vs_offset);
404 break;
405 }
406 default:
407 out() << "don't know how to handle it";
408 break;
409 }
410 }
411
412 updateMaxPos();
413 prevMd = md;
414 offset = md.h_next_md_offset;
415 }
416
417 struct OstreamWrapper {
OstreamWrapper__anon5e30740e0111::Dump::OstreamWrapper418 explicit OstreamWrapper(std::ostream& wrapped) : mWrapped(&wrapped) {}
OstreamWrapper__anon5e30740e0111::Dump::OstreamWrapper419 OstreamWrapper(OstreamWrapper&& other) noexcept
420 : mWrapped(std::exchange(other.mWrapped, nullptr)) {}
421
~OstreamWrapper__anon5e30740e0111::Dump::OstreamWrapper422 ~OstreamWrapper() {
423 if (mWrapped) {
424 *mWrapped << '\n';
425 }
426 }
427
428 template <class T>
operator <<__anon5e30740e0111::Dump::OstreamWrapper429 OstreamWrapper& operator<<(const T& t) & {
430 *mWrapped << t;
431 return *this;
432 }
433 template <class T>
operator <<__anon5e30740e0111::Dump::OstreamWrapper434 OstreamWrapper&& operator<<(const T& t) && {
435 *this << t;
436 return std::move(*this);
437 }
438
439 private:
440 std::ostream* mWrapped;
441 };
442
hex(uint64_t t)443 static std::string hex(uint64_t t) {
444 char buf[32] = {};
445 snprintf(buf, std::size(buf) - 1, "0x%llx", (unsigned long long)t);
446 return buf;
447 }
448
toString(incfs_uuid_t uuid)449 static std::string toString(incfs_uuid_t uuid) {
450 std::stringstream res;
451 res << std::hex;
452 for (unsigned char b : uuid.bytes) {
453 res << std::setfill('0') << std::setw(2) << (unsigned int)b;
454 }
455 return res.str();
456 }
457
out() const458 OstreamWrapper out() const {
459 nesting(std::cout);
460 std::cout << "[" << mBackingFile << "] ";
461 return OstreamWrapper(std::cout);
462 }
463
err() const464 OstreamWrapper err() const {
465 nesting(std::cerr);
466 std::cerr << "[" << mBackingFile << "] ";
467 return OstreamWrapper(std::cerr);
468 }
469
nesting(std::ostream & out) const470 void nesting(std::ostream& out) const {
471 for (int i = 0; i < mNesting; ++i) {
472 out << " ";
473 }
474 }
475
476 template <class T>
read()477 std::remove_reference_t<T> read() {
478 std::remove_reference_t<T> res;
479 mIn.read((char*)&res, sizeof(res));
480 return res;
481 }
482
483 template <class T>
readAt(int64_t pos)484 std::remove_reference_t<T> readAt(int64_t pos) {
485 mIn.seekg(pos);
486 return read<T>();
487 }
488
skip(int64_t count)489 void skip(int64_t count) { mIn.seekg(count, std::ios_base::cur); }
490
updateMaxPos()491 void updateMaxPos() { mMaxDumpedPos = std::max<int64_t>(mMaxDumpedPos, mIn.tellg()); }
492
493 std::string mBackingFile;
494 std::ifstream mIn;
495 int mNesting = 0;
496 int64_t mMaxDumpedPos = 0;
497 };
498
499 } // namespace
500
501 namespace android::incfs {
502
dump(std::string_view backingFile)503 void dump(std::string_view backingFile) {
504 Dump(backingFile).run();
505 }
506
507 } // namespace android::incfs
508