1#include "idmap.h"
2
3#include <UniquePtr.h>
4#include <androidfw/AssetManager.h>
5#include <androidfw/ResourceTypes.h>
6#include <androidfw/ZipFileRO.h>
7#include <utils/String8.h>
8
9#include <fcntl.h>
10#include <sys/stat.h>
11
12using namespace android;
13
14namespace {
15    int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc)
16    {
17        UniquePtr<ZipFileRO> zip(ZipFileRO::open(zip_path));
18        if (zip.get() == NULL) {
19            return -1;
20        }
21        ZipEntryRO entry = zip->findEntryByName(entry_name);
22        if (entry == NULL) {
23            return -1;
24        }
25        if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)crc)) {
26            return -1;
27        }
28        zip->releaseEntry(entry);
29        return 0;
30    }
31
32    int open_idmap(const char *path)
33    {
34        int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644));
35        if (fd == -1) {
36            ALOGD("error: open %s: %s\n", path, strerror(errno));
37            goto fail;
38        }
39        if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
40            ALOGD("error: fchmod %s: %s\n", path, strerror(errno));
41            goto fail;
42        }
43        if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX | LOCK_NB)) != 0) {
44            ALOGD("error: flock %s: %s\n", path, strerror(errno));
45            goto fail;
46        }
47
48        return fd;
49fail:
50        if (fd != -1) {
51            close(fd);
52            unlink(path);
53        }
54        return -1;
55    }
56
57    int write_idmap(int fd, const uint32_t *data, size_t size)
58    {
59        if (lseek(fd, SEEK_SET, 0) < 0) {
60            return -1;
61        }
62        size_t bytesLeft = size;
63        while (bytesLeft > 0) {
64            ssize_t w = TEMP_FAILURE_RETRY(write(fd, data + size - bytesLeft, bytesLeft));
65            if (w < 0) {
66                fprintf(stderr, "error: write: %s\n", strerror(errno));
67                return -1;
68            }
69            bytesLeft -= w;
70        }
71        return 0;
72    }
73
74    bool is_idmap_stale_fd(const char *target_apk_path, const char *overlay_apk_path, int idmap_fd)
75    {
76        static const size_t N = ResTable::IDMAP_HEADER_SIZE_BYTES;
77        struct stat st;
78        if (fstat(idmap_fd, &st) == -1) {
79            return true;
80        }
81        if (st.st_size < N) {
82            // file is empty or corrupt
83            return true;
84        }
85
86        char buf[N];
87        ssize_t bytesLeft = N;
88        if (lseek(idmap_fd, SEEK_SET, 0) < 0) {
89            return true;
90        }
91        for (;;) {
92            ssize_t r = TEMP_FAILURE_RETRY(read(idmap_fd, buf + N - bytesLeft, bytesLeft));
93            if (r < 0) {
94                return true;
95            }
96            bytesLeft -= r;
97            if (bytesLeft == 0) {
98                break;
99            }
100            if (r == 0) {
101                // "shouldn't happen"
102                return true;
103            }
104        }
105
106        uint32_t cached_target_crc, cached_overlay_crc;
107        String8 cached_target_path, cached_overlay_path;
108        if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc,
109                    &cached_target_path, &cached_overlay_path)) {
110            return true;
111        }
112
113        if (cached_target_path != target_apk_path) {
114            return true;
115        }
116        if (cached_overlay_path != overlay_apk_path) {
117            return true;
118        }
119
120        uint32_t actual_target_crc, actual_overlay_crc;
121        if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
122				&actual_target_crc) == -1) {
123            return true;
124        }
125        if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
126				&actual_overlay_crc) == -1) {
127            return true;
128        }
129
130        return cached_target_crc != actual_target_crc || cached_overlay_crc != actual_overlay_crc;
131    }
132
133    bool is_idmap_stale_path(const char *target_apk_path, const char *overlay_apk_path,
134            const char *idmap_path)
135    {
136        struct stat st;
137        if (stat(idmap_path, &st) == -1) {
138            // non-existing idmap is always stale; on other errors, abort idmap generation
139            return errno == ENOENT;
140        }
141
142        int idmap_fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY));
143        if (idmap_fd == -1) {
144            return false;
145        }
146        bool is_stale = is_idmap_stale_fd(target_apk_path, overlay_apk_path, idmap_fd);
147        close(idmap_fd);
148        return is_stale;
149    }
150
151    int create_idmap(const char *target_apk_path, const char *overlay_apk_path,
152            uint32_t **data, size_t *size)
153    {
154        uint32_t target_crc, overlay_crc;
155        if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
156				&target_crc) == -1) {
157            return -1;
158        }
159        if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
160				&overlay_crc) == -1) {
161            return -1;
162        }
163
164        AssetManager am;
165        bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc,
166                data, size);
167        return b ? 0 : -1;
168    }
169
170    int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path,
171            int fd, bool check_if_stale)
172    {
173        if (check_if_stale) {
174            if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) {
175                // already up to date -- nothing to do
176                return 0;
177            }
178        }
179
180        uint32_t *data = NULL;
181        size_t size;
182
183        if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) {
184            return -1;
185        }
186
187        if (write_idmap(fd, data, size) == -1) {
188            free(data);
189            return -1;
190        }
191
192        free(data);
193        return 0;
194    }
195}
196
197int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
198        const char *idmap_path)
199{
200    if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) {
201        // already up to date -- nothing to do
202        return EXIT_SUCCESS;
203    }
204
205    int fd = open_idmap(idmap_path);
206    if (fd == -1) {
207        return EXIT_FAILURE;
208    }
209
210    int r = create_and_write_idmap(target_apk_path, overlay_apk_path, fd, false);
211    close(fd);
212    if (r != 0) {
213        unlink(idmap_path);
214    }
215    return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
216}
217
218int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
219{
220    return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
221        EXIT_SUCCESS : EXIT_FAILURE;
222}
223