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