static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
+static constexpr const char *kIdMapPath = "/system/bin/idmap";
static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
static constexpr const char* IDMAP_SUFFIX = "@idmap";
static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
{
- static const char *IDMAP_BIN = "/system/bin/idmap";
- static const size_t MAX_INT_LEN = 32;
- char idmap_str[MAX_INT_LEN];
+ execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk,
+ StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
+ PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
+}
+
+static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
+{
+ execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk,
+ StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
+ PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
+}
+
+static bool delete_stale_idmap(const char* target_apk, const char* overlay_apk,
+ const char* idmap_path, int32_t uid) {
+ int idmap_fd = open(idmap_path, O_RDWR);
+ if (idmap_fd < 0) {
+ PLOG(ERROR) << "idmap open failed: " << idmap_path;
+ unlink(idmap_path);
+ return true;
+ }
+
+ pid_t pid;
+ pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ if (setgid(uid) != 0) {
+ LOG(ERROR) << "setgid(" << uid << ") failed during idmap";
+ exit(1);
+ }
+ if (setuid(uid) != 0) {
+ LOG(ERROR) << "setuid(" << uid << ") failed during idmap";
+ exit(1);
+ }
+ if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
+ PLOG(ERROR) << "flock(" << idmap_path << ") failed during idmap";
+ exit(1);
+ }
- snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd);
+ run_verify_idmap(target_apk, overlay_apk, idmap_fd);
+ exit(1); /* only if exec call to deleting stale idmap failed */
+ } else {
+ int status = wait_child(pid);
+ close(idmap_fd);
- execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL);
- ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno));
+ if (status != 0) {
+ // Failed on verifying if idmap is made from target_apk and overlay_apk.
+ LOG(DEBUG) << "delete stale idmap: " << idmap_path;
+ unlink(idmap_path);
+ return true;
+ }
+ }
+ return false;
}
// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
int idmap_fd = -1;
char idmap_path[PATH_MAX];
+ struct stat idmap_stat;
+ bool outdated = false;
if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
idmap_path, sizeof(idmap_path)) == -1) {
goto fail;
}
- unlink(idmap_path);
- idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+ if (stat(idmap_path, &idmap_stat) < 0) {
+ outdated = true;
+ } else {
+ outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid);
+ }
+
+ if (outdated) {
+ idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+ } else {
+ idmap_fd = open(idmap_path, O_RDWR);
+ }
+
if (idmap_fd < 0) {
ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
goto fail;
goto fail;
}
+ if (!outdated) {
+ close(idmap_fd);
+ return ok();
+ }
+
pid_t pid;
pid = fork();
if (pid == 0) {