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