blob: 7a501a4ee497d5aa2fad4999c8aba89291b9db75 [file] [log] [blame]
Colin Cross13221c92014-02-11 18:04:44 -08001#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 }
Andreas Gampecfedceb2014-09-30 21:48:18 -070025 if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, reinterpret_cast<long*>(crc))) {
Colin Cross13221c92014-02-11 18:04:44 -080026 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 }
Andreas Gampecfedceb2014-09-30 21:48:18 -070069 bytesLeft -= static_cast<size_t>(w);
Colin Cross13221c92014-02-11 18:04:44 -080070 }
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 }
Andreas Gampeebee1372014-11-07 16:28:19 -080081 if (st.st_size < static_cast<off_t>(N)) {
Colin Cross13221c92014-02-11 18:04:44 -080082 // file is empty or corrupt
83 return true;
84 }
85
86 char buf[N];
Andreas Gampecfedceb2014-09-30 21:48:18 -070087 size_t bytesLeft = N;
Colin Cross13221c92014-02-11 18:04:44 -080088 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 }
Andreas Gampecfedceb2014-09-30 21:48:18 -070096 bytesLeft -= static_cast<size_t>(r);
Colin Cross13221c92014-02-11 18:04:44 -080097 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;
Adam Lesinskif90f2f8d2014-06-06 14:27:00 -0700108 if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc,
Colin Cross13221c92014-02-11 18:04:44 -0800109 &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}