blob: 847dda3df91f492d7eb8023e2d079de368bfdf8b [file] [log] [blame]
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +09001#include <dirent.h>
2#include <inttypes.h>
3#include <sys/file.h>
4#include <sys/stat.h>
5
6#include "idmap.h"
7
8#include <memory>
9#include <androidfw/ResourceTypes.h>
10#include <androidfw/StreamingZipInflater.h>
11#include <androidfw/ZipFileRO.h>
Todd Leed5566c62017-03-16 14:00:52 -070012#include <cutils/properties.h>
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +090013#include <private/android_filesystem_config.h> // for AID_SYSTEM
14#include <utils/SortedVector.h>
15#include <utils/String16.h>
16#include <utils/String8.h>
17
18#define NO_OVERLAY_TAG (-1000)
19
20using namespace android;
21
22namespace {
23 struct Overlay {
24 Overlay() {}
25 Overlay(const String8& a, const String8& i, int p) :
26 apk_path(a), idmap_path(i), priority(p) {}
27
28 bool operator<(Overlay const& rhs) const
29 {
30 return rhs.priority > priority;
31 }
32
33 String8 apk_path;
34 String8 idmap_path;
35 int priority;
36 };
37
38 bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
39 {
40 // the file is opened for appending so that it doesn't get truncated
41 // before we can guarantee mutual exclusion via the flock
42 FILE* fout = fopen(filename, "a");
43 if (fout == NULL) {
44 return false;
45 }
46
47 if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
48 fclose(fout);
49 return false;
50 }
51
52 if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
53 TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
54 fclose(fout);
55 return false;
56 }
57
58 for (size_t i = 0; i < overlayVector.size(); ++i) {
59 const Overlay& overlay = overlayVector[i];
60 fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
61 }
62
63 TEMP_FAILURE_RETRY(fflush(fout));
64 TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
65 fclose(fout);
66
67 // Make file world readable since Zygote (running as root) will read
68 // it when creating the initial AssetManger object
69 const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
70 if (chmod(filename, mode) == -1) {
71 unlink(filename);
72 return false;
73 }
74
75 return true;
76 }
77
78 String8 flatten_path(const char *path)
79 {
80 String16 tmp(path);
81 tmp.replaceAll('/', '@');
82 return String8(tmp);
83 }
84
Todd Leed5566c62017-03-16 14:00:52 -070085 bool check_property(String16 property, String16 value) {
Todd Leed5566c62017-03-16 14:00:52 -070086 char propBuf[PROPERTY_VALUE_MAX];
Elliott Hughes1f7b8542019-08-08 09:36:14 -070087 property_get(String8(property).c_str(), propBuf, NULL);
88 return String8(value) == propBuf;
Todd Leed5566c62017-03-16 14:00:52 -070089 }
90
Jaekyun Seok04342892017-03-02 15:24:19 +090091 int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
92 bool* is_static_overlay)
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +090093 {
94 const size_t N = parser.getAttributeCount();
95 String16 target;
96 int priority = -1;
Todd Leed5566c62017-03-16 14:00:52 -070097 String16 propName = String16();
98 String16 propValue = String16();
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +090099 for (size_t i = 0; i < N; ++i) {
100 size_t len;
101 String16 key(parser.getAttributeName(i, &len));
102 if (key == String16("targetPackage")) {
103 const char16_t *p = parser.getAttributeStringValue(i, &len);
104 if (p != NULL) {
105 target = String16(p, len);
106 }
107 } else if (key == String16("priority")) {
108 Res_value v;
109 if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
110 priority = v.data;
111 if (priority < 0 || priority > 9999) {
112 return -1;
113 }
114 }
Jaekyun Seok04342892017-03-02 15:24:19 +0900115 } else if (key == String16("isStatic")) {
116 Res_value v;
117 if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
118 *is_static_overlay = (v.data != 0);
119 }
Todd Leed5566c62017-03-16 14:00:52 -0700120 } else if (key == String16("requiredSystemPropertyName")) {
121 const char16_t *p = parser.getAttributeStringValue(i, &len);
122 if (p != NULL) {
123 propName = String16(p, len);
124 }
125 } else if (key == String16("requiredSystemPropertyValue")) {
126 const char16_t *p = parser.getAttributeStringValue(i, &len);
127 if (p != NULL) {
128 propValue = String16(p, len);
129 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900130 }
131 }
Todd Leed5566c62017-03-16 14:00:52 -0700132
133 // Note that conditional property enablement/exclusion only applies if
134 // the attribute is present. In its absence, all overlays are presumed enabled.
135 if (propName.size() > 0 && propValue.size() > 0) {
136 // if property set & equal to value, then include overlay - otherwise skip
137 if (!check_property(propName, propValue)) {
138 return NO_OVERLAY_TAG;
139 }
140 }
141
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900142 if (target == String16(target_package_name)) {
143 return priority;
144 }
145 return NO_OVERLAY_TAG;
146 }
147
148 int parse_manifest(const void *data, size_t size, const char *target_package_name)
149 {
150 ResXMLTree parser;
151 parser.setTo(data, size);
152 if (parser.getError() != NO_ERROR) {
153 ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
154 return -1;
155 }
156
157 ResXMLParser::event_code_t type;
Jaekyun Seok04342892017-03-02 15:24:19 +0900158 bool is_static_overlay = false;
159 int priority = NO_OVERLAY_TAG;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900160 do {
161 type = parser.next();
162 if (type == ResXMLParser::START_TAG) {
163 size_t len;
164 String16 tag(parser.getElementName(&len));
Todd Leed5566c62017-03-16 14:00:52 -0700165 if (tag == String16("overlay")) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900166 priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay);
167 break;
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900168 }
169 }
170 } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
171
Todd Leed5566c62017-03-16 14:00:52 -0700172 if (is_static_overlay) {
Jaekyun Seok04342892017-03-02 15:24:19 +0900173 return priority;
174 }
Jaekyun Seok7de2f9c2017-03-02 12:45:10 +0900175 return NO_OVERLAY_TAG;
176 }
177
178 int parse_apk(const char *path, const char *target_package_name)
179 {
180 std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
181 if (zip.get() == NULL) {
182 ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
183 return -1;
184 }
185 ZipEntryRO entry;
186 if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
187 ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
188 return -1;
189 }
190 uint32_t uncompLen = 0;
191 uint16_t method;
192 if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
193 ALOGW("%s: failed to read entry info\n", __FUNCTION__);
194 return -1;
195 }
196 if (method != ZipFileRO::kCompressDeflated) {
197 ALOGW("%s: cannot handle zip compression method %" PRIu16 "\n", __FUNCTION__, method);
198 return -1;
199 }
200 FileMap *dataMap = zip->createEntryFileMap(entry);
201 if (dataMap == NULL) {
202 ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
203 return -1;
204 }
205 char *buf = new char[uncompLen];
206 if (NULL == buf) {
207 ALOGW("%s: failed to allocate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
208 delete dataMap;
209 return -1;
210 }
211 StreamingZipInflater inflater(dataMap, uncompLen);
212 if (inflater.read(buf, uncompLen) < 0) {
213 ALOGW("%s: failed to inflate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
214 delete[] buf;
215 delete dataMap;
216 return -1;
217 }
218
219 int priority = parse_manifest(buf, static_cast<size_t>(uncompLen), target_package_name);
220 delete[] buf;
221 delete dataMap;
222 return priority;
223 }
224}
225
226int idmap_scan(const char *target_package_name, const char *target_apk_path,
227 const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
228{
229 String8 filename = String8(idmap_dir);
230 filename.appendPath("overlays.list");
231
232 SortedVector<Overlay> overlayVector;
233 const size_t N = overlay_dirs->size();
234 for (size_t i = 0; i < N; ++i) {
235 const char *overlay_dir = overlay_dirs->itemAt(i);
236 DIR *dir = opendir(overlay_dir);
237 if (dir == NULL) {
238 return EXIT_FAILURE;
239 }
240
241 struct dirent *dirent;
242 while ((dirent = readdir(dir)) != NULL) {
243 struct stat st;
244 char overlay_apk_path[PATH_MAX + 1];
245 snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
246 if (stat(overlay_apk_path, &st) < 0) {
247 continue;
248 }
249 if (!S_ISREG(st.st_mode)) {
250 continue;
251 }
252
253 int priority = parse_apk(overlay_apk_path, target_package_name);
254 if (priority < 0) {
255 continue;
256 }
257
258 String8 idmap_path(idmap_dir);
259 idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
260 idmap_path.append("@idmap");
261
262 if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
263 ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
264 target_apk_path, overlay_apk_path, idmap_path.string());
265 continue;
266 }
267
268 Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
269 overlayVector.add(overlay);
270 }
271
272 closedir(dir);
273 }
274
275 if (!writePackagesList(filename.string(), overlayVector)) {
276 return EXIT_FAILURE;
277 }
278
279 return EXIT_SUCCESS;
280}
281