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