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