#include "idmap.h" #include #include #include #include #include #include #include #include using namespace android; namespace { int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc) { UniquePtr zip(ZipFileRO::open(zip_path)); if (zip.get() == NULL) { return -1; } ZipEntryRO entry = zip->findEntryByName(entry_name); if (entry == NULL) { return -1; } if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, crc)) { return -1; } zip->releaseEntry(entry); return 0; } int open_idmap(const char *path) { int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)); bool needUnlink = true; if (fd == -1) { ALOGD("error: open %s: %s\n", path, strerror(errno)); goto fail; } if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) { ALOGD("error: fchmod %s: %s\n", path, strerror(errno)); goto fail; } if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX)) != 0) { ALOGD("error: flock %s: %s\n", path, strerror(errno)); // If the file is locked by another process, then we needn't unlink the file. needUnlink = false; goto fail; } return fd; fail: if (fd != -1) { close(fd); if (needUnlink) unlink(path); } return -1; } int write_idmap(int fd, const uint32_t *data, size_t size) { if (lseek(fd, SEEK_SET, 0) < 0) { return -1; } size_t bytesLeft = size; while (bytesLeft > 0) { ssize_t w = TEMP_FAILURE_RETRY(write(fd, data + size - bytesLeft, bytesLeft)); if (w < 0) { fprintf(stderr, "error: write: %s\n", strerror(errno)); return -1; } bytesLeft -= static_cast(w); } return 0; } bool is_idmap_stale_fd(const char *target_apk_path, const char *overlay_apk_path, int idmap_fd) { static const size_t N = ResTable::IDMAP_HEADER_SIZE_BYTES; struct stat st; if (fstat(idmap_fd, &st) == -1) { return true; } if (st.st_size < static_cast(N)) { // file is empty or corrupt return true; } char buf[N]; size_t bytesLeft = N; if (lseek(idmap_fd, SEEK_SET, 0) < 0) { return true; } for (;;) { ssize_t r = TEMP_FAILURE_RETRY(read(idmap_fd, buf + N - bytesLeft, bytesLeft)); if (r < 0) { return true; } bytesLeft -= static_cast(r); if (bytesLeft == 0) { break; } if (r == 0) { // "shouldn't happen" return true; } } uint32_t cached_target_crc, cached_overlay_crc; String8 cached_target_path, cached_overlay_path; if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc, &cached_target_path, &cached_overlay_path)) { return true; } if (cached_target_path != target_apk_path) { return true; } if (cached_overlay_path != overlay_apk_path) { return true; } uint32_t actual_target_crc, actual_overlay_crc; if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME, &actual_target_crc) == -1) { return true; } if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME, &actual_overlay_crc) == -1) { return true; } return cached_target_crc != actual_target_crc || cached_overlay_crc != actual_overlay_crc; } bool is_idmap_stale_path(const char *target_apk_path, const char *overlay_apk_path, const char *idmap_path) { struct stat st; if (stat(idmap_path, &st) == -1) { // non-existing idmap is always stale; on other errors, abort idmap generation return errno == ENOENT; } int idmap_fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY)); if (idmap_fd == -1) { return false; } bool is_stale = is_idmap_stale_fd(target_apk_path, overlay_apk_path, idmap_fd); close(idmap_fd); return is_stale; } int create_idmap(const char *target_apk_path, const char *overlay_apk_path, const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, uint32_t **data, size_t *size) { uint32_t target_crc, overlay_crc; // In the original implementation, crc of the res tables are generated // theme apks however do not need a restable, everything is in assets/ // instead timestamps are used target_crc = 0; overlay_crc = 0; AssetManager am; bool b = am.createIdmap(target_apk_path, overlay_apk_path, cache_path, target_crc, overlay_crc, target_hash, overlay_hash, data, size); return b ? 0 : -1; } int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path, const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, int fd, bool check_if_stale) { if (check_if_stale) { if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) { // already up to date -- nothing to do return 0; } } uint32_t *data = NULL; size_t size; if (create_idmap(target_apk_path, overlay_apk_path, cache_path, target_hash, overlay_hash, &data, &size) == -1) { return -1; } if (write_idmap(fd, data, size) == -1) { free(data); return -1; } free(data); return 0; } } int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, const char *idmap_path) { if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) { // already up to date -- nothing to do return EXIT_SUCCESS; } int fd = open_idmap(idmap_path); if (fd == -1) { return EXIT_FAILURE; } int r = create_and_write_idmap(target_apk_path, overlay_apk_path, cache_path, target_hash, overlay_hash, fd, false); close(fd); if (r != 0) { unlink(idmap_path); } return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, int fd) { return create_and_write_idmap(target_apk_path, overlay_apk_path, cache_path, target_hash, overlay_hash, fd, true) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }