1#include "idmap.h"
2
3#include <androidfw/AssetManager.h>
4#include <androidfw/ResourceTypes.h>
5#include <utils/ByteOrder.h>
6#include <utils/String8.h>
7
8#include <fcntl.h>
9#include <sys/mman.h>
10#include <sys/stat.h>
11
12using namespace android;
13
14namespace {
15    static const uint32_t IDMAP_MAGIC = 0x504D4449;
16    static const size_t PATH_LENGTH = 256;
17
18    void printe(const char *fmt, ...);
19
20    class IdmapBuffer {
21        private:
22            const char* buf_;
23            size_t len_;
24            size_t pos_;
25        public:
26            IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {}
27
28            ~IdmapBuffer() {
29                if (buf_ != MAP_FAILED) {
30                    munmap(const_cast<char*>(buf_), len_);
31                }
32            }
33
34            status_t init(const char *idmap_path) {
35                struct stat st;
36                int fd;
37
38                if (stat(idmap_path, &st) < 0) {
39                    printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno));
40                    return UNKNOWN_ERROR;
41                }
42                len_ = st.st_size;
43                if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) {
44                    printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno));
45                    return UNKNOWN_ERROR;
46                }
47                if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
48                    close(fd);
49                    printe("failed to mmap idmap: %s\n", strerror(errno));
50                    return UNKNOWN_ERROR;
51                }
52                close(fd);
53                return NO_ERROR;
54            }
55
56            status_t nextUint32(uint32_t* i) {
57                if (!buf_) {
58                    printe("failed to read next uint32_t: buffer not initialized\n");
59                    return UNKNOWN_ERROR;
60                }
61
62                if (pos_ + sizeof(uint32_t) > len_) {
63                    printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
64                            pos_);
65                    return UNKNOWN_ERROR;
66                }
67
68                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) {
69                    printe("failed to read next uint32_t: not aligned on 4-byte boundary\n");
70                    return UNKNOWN_ERROR;
71                }
72
73                *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_));
74                pos_ += sizeof(uint32_t);
75                return NO_ERROR;
76            }
77
78            status_t nextUint16(uint16_t* i) {
79                if (!buf_) {
80                    printe("failed to read next uint16_t: buffer not initialized\n");
81                    return UNKNOWN_ERROR;
82                }
83
84                if (pos_ + sizeof(uint16_t) > len_) {
85                    printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n",
86                            pos_);
87                    return UNKNOWN_ERROR;
88                }
89
90                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) {
91                    printe("failed to read next uint32_t: not aligned on 2-byte boundary\n");
92                    return UNKNOWN_ERROR;
93                }
94
95                *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_));
96                pos_ += sizeof(uint16_t);
97                return NO_ERROR;
98            }
99
100            status_t nextPath(char *b) {
101                if (!buf_) {
102                    printe("failed to read next path: buffer not initialized\n");
103                    return UNKNOWN_ERROR;
104                }
105                if (pos_ + PATH_LENGTH > len_) {
106                    printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
107                    return UNKNOWN_ERROR;
108                }
109                memcpy(b, buf_ + pos_, PATH_LENGTH);
110                pos_ += PATH_LENGTH;
111                return NO_ERROR;
112            }
113    };
114
115    void printe(const char *fmt, ...) {
116        va_list ap;
117
118        va_start(ap, fmt);
119        fprintf(stderr, "error: ");
120        vfprintf(stderr, fmt, ap);
121        va_end(ap);
122    }
123
124    void print_header() {
125        printf("SECTION      ENTRY        VALUE      COMMENT\n");
126    }
127
128    void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) {
129        va_list ap;
130
131        va_start(ap, fmt);
132        printf("%-12s %-12s 0x%08x ", section, subsection, value);
133        vprintf(fmt, ap);
134        printf("\n");
135        va_end(ap);
136    }
137
138    void print_path(const char *section, const char *subsection, const char *fmt, ...) {
139        va_list ap;
140
141        va_start(ap, fmt);
142        printf("%-12s %-12s .......... ", section, subsection);
143        vprintf(fmt, ap);
144        printf("\n");
145        va_end(ap);
146    }
147
148    status_t resource_metadata(const AssetManager& am, uint32_t res_id,
149            String8 *package, String8 *type, String8 *name) {
150        const ResTable& rt = am.getResources();
151        struct ResTable::resource_name data;
152        if (!rt.getResourceName(res_id, false, &data)) {
153            printe("failed to get resource name id=0x%08x\n", res_id);
154            return UNKNOWN_ERROR;
155        }
156        if (package != NULL) {
157            *package = String8(String16(data.package, data.packageLen));
158        }
159        if (type != NULL) {
160            *type = String8(String16(data.type, data.typeLen));
161        }
162        if (name != NULL) {
163            *name = String8(String16(data.name, data.nameLen));
164        }
165        return NO_ERROR;
166    }
167
168    status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) {
169        uint32_t i;
170        char path[PATH_LENGTH];
171
172        status_t err = buf.nextUint32(&i);
173        if (err != NO_ERROR) {
174            return err;
175        }
176
177        if (i != IDMAP_MAGIC) {
178            printe("not an idmap file: actual magic constant 0x%08x does not match expected magic "
179                    "constant 0x%08x\n", i, IDMAP_MAGIC);
180            return UNKNOWN_ERROR;
181        }
182
183        print_header();
184        print("IDMAP HEADER", "magic", i, "");
185
186        err = buf.nextUint32(&i);
187        if (err != NO_ERROR) {
188            return err;
189        }
190        print("", "version", i, "");
191
192        err = buf.nextUint32(&i);
193        if (err != NO_ERROR) {
194            return err;
195        }
196        print("", "base crc", i, "");
197
198        err = buf.nextUint32(&i);
199        if (err != NO_ERROR) {
200            return err;
201        }
202        print("", "overlay crc", i, "");
203
204        err = buf.nextPath(path);
205        if (err != NO_ERROR) {
206            // printe done from IdmapBuffer::nextPath
207            return err;
208        }
209        print_path("", "base path", "%s", path);
210
211        if (!am.addAssetPath(String8(path), NULL)) {
212            printe("failed to add '%s' as asset path\n", path);
213            return UNKNOWN_ERROR;
214        }
215
216        err = buf.nextPath(path);
217        if (err != NO_ERROR) {
218            // printe done from IdmapBuffer::nextPath
219            return err;
220        }
221        print_path("", "overlay path", "%s", path);
222
223        return NO_ERROR;
224    }
225
226    status_t parse_data(IdmapBuffer& buf, const AssetManager& am) {
227        const uint32_t packageId = am.getResources().getBasePackageId(0);
228
229        uint16_t data16;
230        status_t err = buf.nextUint16(&data16);
231        if (err != NO_ERROR) {
232            return err;
233        }
234        print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), "");
235
236        err = buf.nextUint16(&data16);
237        if (err != NO_ERROR) {
238            return err;
239        }
240        print("", "types count", static_cast<uint32_t>(data16), "");
241
242        uint32_t typeCount = static_cast<uint32_t>(data16);
243        while (typeCount > 0) {
244            typeCount--;
245
246            err = buf.nextUint16(&data16);
247            if (err != NO_ERROR) {
248                return err;
249            }
250            const uint32_t targetTypeId = static_cast<uint32_t>(data16);
251            print("DATA BLOCK", "target type", targetTypeId, "");
252
253            err = buf.nextUint16(&data16);
254            if (err != NO_ERROR) {
255                return err;
256            }
257            print("", "overlay type", static_cast<uint32_t>(data16), "");
258
259            err = buf.nextUint16(&data16);
260            if (err != NO_ERROR) {
261                return err;
262            }
263            const uint32_t entryCount = static_cast<uint32_t>(data16);
264            print("", "entry count", entryCount, "");
265
266            err = buf.nextUint16(&data16);
267            if (err != NO_ERROR) {
268                return err;
269            }
270            const uint32_t entryOffset = static_cast<uint32_t>(data16);
271            print("", "entry offset", entryOffset, "");
272
273            for (uint32_t i = 0; i < entryCount; i++) {
274                uint32_t data32;
275                err = buf.nextUint32(&data32);
276                if (err != NO_ERROR) {
277                    return err;
278                }
279
280                uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i);
281                String8 type;
282                String8 name;
283                err = resource_metadata(am, resID, NULL, &type, &name);
284                if (err != NO_ERROR) {
285                    return err;
286                }
287                print("", "entry", data32, "%s/%s", type.string(), name.string());
288            }
289        }
290
291        return NO_ERROR;
292    }
293}
294
295int idmap_inspect(const char *idmap_path) {
296    IdmapBuffer buf;
297    if (buf.init(idmap_path) < 0) {
298        // printe done from IdmapBuffer::init
299        return EXIT_FAILURE;
300    }
301    AssetManager am;
302    if (parse_idmap_header(buf, am) != NO_ERROR) {
303        // printe done from parse_idmap_header
304        return EXIT_FAILURE;
305    }
306    if (parse_data(buf, am) != NO_ERROR) {
307        // printe done from parse_data_header
308        return EXIT_FAILURE;
309    }
310    return EXIT_SUCCESS;
311}
312