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 
13 using namespace android;
14 
15 namespace {
get_zip_entry_crc(const char * zip_path,const char * entry_name,uint32_t * crc)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 
open_idmap(const char * path)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;
50 fail:
51         if (fd != -1) {
52             close(fd);
53             unlink(path);
54         }
55         return -1;
56     }
57 
write_idmap(int fd,const uint32_t * data,size_t size)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 
is_idmap_stale_fd(const char * target_apk_path,const char * overlay_apk_path,int idmap_fd)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 version, cached_target_crc, cached_overlay_crc;
108         String8 cached_target_path, cached_overlay_path;
109         if (!ResTable::getIdmapInfo(buf, N, &version, &cached_target_crc, &cached_overlay_crc,
110                     &cached_target_path, &cached_overlay_path)) {
111             return true;
112         }
113 
114         if (version != ResTable::IDMAP_CURRENT_VERSION) {
115             return true;
116         }
117 
118         if (cached_target_path != target_apk_path) {
119             return true;
120         }
121         if (cached_overlay_path != overlay_apk_path) {
122             return true;
123         }
124 
125         uint32_t actual_target_crc, actual_overlay_crc;
126         if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
127 				&actual_target_crc) == -1) {
128             return true;
129         }
130         if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
131 				&actual_overlay_crc) == -1) {
132             return true;
133         }
134 
135         return cached_target_crc != actual_target_crc || cached_overlay_crc != actual_overlay_crc;
136     }
137 
is_idmap_stale_path(const char * target_apk_path,const char * overlay_apk_path,const char * idmap_path)138     bool is_idmap_stale_path(const char *target_apk_path, const char *overlay_apk_path,
139             const char *idmap_path)
140     {
141         struct stat st;
142         if (stat(idmap_path, &st) == -1) {
143             // non-existing idmap is always stale; on other errors, abort idmap generation
144             return errno == ENOENT;
145         }
146 
147         int idmap_fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY));
148         if (idmap_fd == -1) {
149             return false;
150         }
151         bool is_stale = is_idmap_stale_fd(target_apk_path, overlay_apk_path, idmap_fd);
152         close(idmap_fd);
153         return is_stale;
154     }
155 
create_idmap(const char * target_apk_path,const char * overlay_apk_path,uint32_t ** data,size_t * size)156     int create_idmap(const char *target_apk_path, const char *overlay_apk_path,
157             uint32_t **data, size_t *size)
158     {
159         uint32_t target_crc, overlay_crc;
160         if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
161 				&target_crc) == -1) {
162             return -1;
163         }
164         if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
165 				&overlay_crc) == -1) {
166             return -1;
167         }
168 
169         AssetManager am;
170         bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc,
171                 data, size);
172         return b ? 0 : -1;
173     }
174 
create_and_write_idmap(const char * target_apk_path,const char * overlay_apk_path,int fd,bool check_if_stale)175     int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path,
176             int fd, bool check_if_stale)
177     {
178         if (check_if_stale) {
179             if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) {
180                 // already up to date -- nothing to do
181                 return 0;
182             }
183         }
184 
185         uint32_t *data = NULL;
186         size_t size;
187 
188         if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) {
189             return -1;
190         }
191 
192         if (write_idmap(fd, data, size) == -1) {
193             free(data);
194             return -1;
195         }
196 
197         free(data);
198         return 0;
199     }
200 }
201 
idmap_create_path(const char * target_apk_path,const char * overlay_apk_path,const char * idmap_path)202 int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
203         const char *idmap_path)
204 {
205     if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) {
206         // already up to date -- nothing to do
207         return EXIT_SUCCESS;
208     }
209 
210     int fd = open_idmap(idmap_path);
211     if (fd == -1) {
212         return EXIT_FAILURE;
213     }
214 
215     int r = create_and_write_idmap(target_apk_path, overlay_apk_path, fd, false);
216     close(fd);
217     if (r != 0) {
218         unlink(idmap_path);
219     }
220     return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
221 }
222 
idmap_create_fd(const char * target_apk_path,const char * overlay_apk_path,int fd)223 int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
224 {
225     return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
226         EXIT_SUCCESS : EXIT_FAILURE;
227 }
228 
idmap_verify_fd(const char * target_apk_path,const char * overlay_apk_path,int fd)229 int idmap_verify_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
230 {
231     return !is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd) ?
232             EXIT_SUCCESS : EXIT_FAILURE;
233 }
234