xref: /aosp_15_r20/system/incremental_delivery/incfs/incfsdump/dump.cpp (revision 9190c2a8bd3622b7aa9bd7bfe4b3aec77820f478)
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