// package_extract_file(package_path, destination_path) // or // package_extract_file(package_path) // to return the entire contents of the file as the result of this // function (the char* returned is actually a FileContents*). Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1 && argc != 2) { return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", name, argc); } bool success = false; if (argc == 2) { // The two-argument version extracts to a file. char* zip_path; char* dest_path; if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { fprintf(stderr, "%s: no %s in package\n", name, zip_path); goto done2; } FILE* f = fopen(dest_path, "wb"); if (f == NULL) { fprintf(stderr, "%s: can't open %s for write: %s\n", name, dest_path, strerror(errno)); goto done2; } success = mzExtractZipEntryToFile(za, entry, fileno(f)); fclose(f); done2: free(zip_path); free(dest_path); return StringValue(strdup(success ? "t" : "")); } else { // The one-argument version returns the contents of the file // as the result. char* zip_path; Value* v = malloc(sizeof(Value)); v->type = VAL_BLOB; v->size = -1; v->data = NULL; if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { fprintf(stderr, "%s: no %s in package\n", name, zip_path); goto done1; } v->size = mzGetZipEntryUncompLen(entry); v->data = malloc(v->size); if (v->data == NULL) { fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n", name, (long)v->size, zip_path); goto done1; } success = mzExtractZipEntryToBuffer(za, entry, (unsigned char *)v->data); done1: free(zip_path); if (!success) { free(v->data); v->data = NULL; v->size = -1; } return v; } }
// If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { const ZipEntry* update_script_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME); if (update_script_entry != NULL) { ui_print("Amend scripting (update-script) is no longer supported.\n"); ui_print("Amend scripting was deprecated by Google in Android 1.5.\n"); ui_print("It was necessary to remove it when upgrading to the ClockworkMod 3.0 Gingerbread based recovery.\n"); ui_print("Please switch to Edify scripting (updater-script and update-binary) to create working update zip packages.\n"); return INSTALL_UPDATE_BINARY_MISSING; } mzCloseZipArchive(zip); return INSTALL_UPDATE_BINARY_MISSING; } char* binary = "/tmp/update_binary"; unlink(binary); int fd = creat(binary, 0755); if (fd < 0) { mzCloseZipArchive(zip); LOGE("Can't make %s\n", binary); return 1; } bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); close(fd); if (!ok) { LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); mzCloseZipArchive(zip); return 1; } int pipefd[2]; pipe(pipefd); // When executing the update binary contained in the package, the // arguments passed are: // // - the version number for this interface // // - an fd to which the program can write in order to update the // progress bar. The program can write single-line commands: // // progress <frac> <secs> // fill up the next <frac> part of of the progress bar // over <secs> seconds. If <secs> is zero, use // set_progress commands to manually control the // progress of this segment of the bar // // set_progress <frac> // <frac> should be between 0.0 and 1.0; sets the // progress bar within the segment defined by the most // recent progress command. // // firmware <"hboot"|"radio"> <filename> // arrange to install the contents of <filename> in the // given partition on reboot. // // (API v2: <filename> may start with "PACKAGE:" to // indicate taking a file from the OTA package.) // // (API v3: this command no longer exists.) // // ui_print <string> // display <string> on the screen. // // - the name of the package zip file. // char** args = malloc(sizeof(char*) * 5); args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk args[2] = malloc(10); sprintf(args[2], "%d", pipefd[1]); args[3] = (char*)path; args[4] = NULL; pid_t pid = fork(); if (pid == 0) { setenv("UPDATE_PACKAGE", path, 1); close(pipefd[0]); execv(binary, args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } close(pipefd[1]); char* firmware_type = NULL; char* firmware_filename = NULL; char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); while (fgets(buffer, sizeof(buffer), from_child) != NULL) { char* command = strtok(buffer, " \n"); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_s = strtok(NULL, " \n"); char* seconds_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); int seconds = strtol(seconds_s, NULL, 10); ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); } else if (strcmp(command, "set_progress") == 0) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui_set_progress(fraction); } else if (strcmp(command, "firmware") == 0) { char* type = strtok(NULL, " \n"); char* filename = strtok(NULL, " \n"); if (type != NULL && filename != NULL) { if (firmware_type != NULL) { LOGE("ignoring attempt to do multiple firmware updates"); } else { firmware_type = strdup(type); firmware_filename = strdup(filename); } } } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { ui_print("%s", str); } else { ui_print("\n"); } } else { LOGE("unknown command [%s]\n", command); } } fclose(from_child); int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); mzCloseZipArchive(zip); return INSTALL_ERROR; } if (firmware_type != NULL) { int ret = handle_firmware_update(firmware_type, firmware_filename, zip); mzCloseZipArchive(zip); return ret; } return INSTALL_SUCCESS; }
// If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { mzCloseZipArchive(zip); return INSTALL_CORRUPT; } const char* binary = "/tmp/update_binary"; unlink(binary); int fd = creat(binary, 0755); if (fd < 0) { mzCloseZipArchive(zip); LOGE("Can't make %s\n", binary); return INSTALL_ERROR; } bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); close(fd); mzCloseZipArchive(zip); if (!ok) { LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); return INSTALL_ERROR; } int pipefd[2]; pipe(pipefd); // When executing the update binary contained in the package, the // arguments passed are: // // - the version number for this interface // // - an fd to which the program can write in order to update the // progress bar. The program can write single-line commands: // // progress <frac> <secs> // fill up the next <frac> part of of the progress bar // over <secs> seconds. If <secs> is zero, use // set_progress commands to manually control the // progress of this segment of the bar // // set_progress <frac> // <frac> should be between 0.0 and 1.0; sets the // progress bar within the segment defined by the most // recent progress command. // // firmware <"hboot"|"radio"> <filename> // arrange to install the contents of <filename> in the // given partition on reboot. // // (API v2: <filename> may start with "PACKAGE:" to // indicate taking a file from the OTA package.) // // (API v3: this command no longer exists.) // // ui_print <string> // display <string> on the screen. // // - the name of the package zip file. // const char** args = (const char**)malloc(sizeof(char*) * 5); args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk char* temp = (char*)malloc(10); sprintf(temp, "%d", pipefd[1]); args[2] = temp; args[3] = (char*)path; args[4] = NULL; pid_t pid = fork(); if (pid == 0) { umask(022); close(pipefd[0]); execv(binary, (char* const*)args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } close(pipefd[1]); *wipe_cache = 0; char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); while (fgets(buffer, sizeof(buffer), from_child) != NULL) { char* command = strtok(buffer, " \n"); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_s = strtok(NULL, " \n"); char* seconds_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); int seconds = strtol(seconds_s, NULL, 10); ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); } else if (strcmp(command, "set_progress") == 0) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui->SetProgress(fraction); } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { ui->Print("%s", str); } else { ui->Print("\n"); } fflush(stdout); } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = 1; } else if (strcmp(command, "clear_display") == 0) { ui->SetBackground(RecoveryUI::NONE); } else if (strcmp(command, "enable_reboot") == 0) { // packages can explicitly request that they want the user // to be able to reboot during installation (useful for // debugging packages that don't exit). ui->SetEnableReboot(true); } else { LOGE("unknown command [%s]\n", command); } } fclose(from_child); int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; } return INSTALL_SUCCESS; }
// If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip) { struct statfs st; char* binary = (char*)malloc(20); if (statfs(INCLUDED_BINARY_NAME, &st) != 0) { // No update-binary included in recovery, extract it from the zip strcpy(binary, "/tmp/update_binary"); const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { mzCloseZipArchive(zip); return INSTALL_CORRUPT; } unlink(binary); int fd = creat(binary, 0755); if (fd < 0) { mzCloseZipArchive(zip); LOGE("Can't make %s\n", binary); return 1; } bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); close(fd); mzCloseZipArchive(zip); if (!ok) { LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); return 1; } } else { // Use the update-binary that is included in the recovery strcpy(binary, INCLUDED_BINARY_NAME); LOGI("Using update-binary included in recovery: '%s'.\n", binary); } int pipefd[2]; pipe(pipefd); // When executing the update binary contained in the package, the // arguments passed are: // // - the version number for this interface // // - an fd to which the program can write in order to update the // progress bar. The program can write single-line commands: // // progress <frac> <secs> // fill up the next <frac> part of of the progress bar // over <secs> seconds. If <secs> is zero, use // set_progress commands to manually control the // progress of this segment of the bar // // set_progress <frac> // <frac> should be between 0.0 and 1.0; sets the // progress bar within the segment defined by the most // recent progress command. // // firmware <"hboot"|"radio"> <filename> // arrange to install the contents of <filename> in the // given partition on reboot. // // (API v2: <filename> may start with "PACKAGE:" to // indicate taking a file from the OTA package.) // // (API v3: this command no longer exists.) // // ui_print <string> // display <string> on the screen. // // - the name of the package zip file. // char** args = malloc(sizeof(char*) * 5); args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk args[2] = malloc(10); sprintf(args[2], "%d", pipefd[1]); args[3] = (char*)path; args[4] = NULL; pid_t pid = fork(); if (pid == 0) { close(pipefd[0]); execv(binary, args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } close(pipefd[1]); char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); while (fgets(buffer, sizeof(buffer), from_child) != NULL) { char* command = strtok(buffer, " \n"); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_s = strtok(NULL, " \n"); char* seconds_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); int seconds = strtol(seconds_s, NULL, 10); ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); } else if (strcmp(command, "set_progress") == 0) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui_set_progress(fraction); } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { ui_print("%s", str); } else { ui_print("\n"); } } else { LOGE("unknown command [%s]\n", command); } } fclose(from_child); int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; } return INSTALL_SUCCESS; }
Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { Value* blockdev_filename; Value* transfer_list_value; char* transfer_list = NULL; Value* new_data_fn; Value* patch_data_fn; bool success = false; if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, &new_data_fn, &patch_data_fn) < 0) { return NULL; } if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, "blockdev_filename argument to %s must be string", name); goto done; } if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, "transfer_list argument to %s must be blob", name); goto done; } if (new_data_fn->type != VAL_STRING) { ErrorAbort(state, "new_data_fn argument to %s must be string", name); goto done; } if (patch_data_fn->type != VAL_STRING) { ErrorAbort(state, "patch_data_fn argument to %s must be string", name); goto done; } UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); FILE* cmd_pipe = ui->cmd_pipe; ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data); if (patch_entry == NULL) { ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data); goto done; } uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr + mzGetZipEntryOffset(patch_entry); const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data); if (new_entry == NULL) { ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data); goto done; } // The transfer list is a text file containing commands to // transfer data from one place to another on the target // partition. We parse it and execute the commands in order: // // zero [rangeset] // - fill the indicated blocks with zeros // // new [rangeset] // - fill the blocks with data read from the new_data file // // bsdiff patchstart patchlen [src rangeset] [tgt rangeset] // imgdiff patchstart patchlen [src rangeset] [tgt rangeset] // - read the source blocks, apply a patch, write result to // target blocks. bsdiff or imgdiff specifies the type of // patch. // // move [src rangeset] [tgt rangeset] // - copy data from source blocks to target blocks (no patch // needed; rangesets are the same size) // // erase [rangeset] // - mark the given blocks as empty // // The creator of the transfer list will guarantee that no block // is read (ie, used as the source for a patch or move) after it // has been written. // // Within one command the source and target ranges may overlap so // in general we need to read the entire source into memory before // writing anything to the target blocks. // // All the patch data is concatenated into one patch_data file in // the update package. It must be stored uncompressed because we // memory-map it in directly from the archive. (Since patches are // already compressed, we lose very little by not compressing // their concatenation.) pthread_t new_data_thread; NewThreadInfo nti; nti.za = za; nti.entry = new_entry; nti.rss = NULL; pthread_mutex_init(&nti.mu, NULL); pthread_cond_init(&nti.cv, NULL); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&new_data_thread, &attr, unzip_new_data, &nti); int i, j; char* linesave; char* wordsave; int fd = open(blockdev_filename->data, O_RDWR); if (fd < 0) { ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); goto done; } char* line; char* word; // The data in transfer_list_value is not necessarily // null-terminated, so we need to copy it to a new buffer and add // the null that strtok_r will need. transfer_list = malloc(transfer_list_value->size+1); if (transfer_list == NULL) { fprintf(stderr, "failed to allocate %zd bytes for transfer list\n", transfer_list_value->size+1); exit(1); } memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); transfer_list[transfer_list_value->size] = '\0'; line = strtok_r(transfer_list, "\n", &linesave); // first line in transfer list is the version number; currently // there's only version 1. if (strcmp(line, "1") != 0) { ErrorAbort(state, "unexpected transfer list version [%s]\n", line); goto done; } // second line in transfer list is the total number of blocks we // expect to write. line = strtok_r(NULL, "\n", &linesave); int total_blocks = strtol(line, NULL, 0); // shouldn't happen, but avoid divide by zero. if (total_blocks == 0) ++total_blocks; int blocks_so_far = 0; uint8_t* buffer = NULL; size_t buffer_alloc = 0; // third and subsequent lines are all individual transfer commands. for (line = strtok_r(NULL, "\n", &linesave); line; line = strtok_r(NULL, "\n", &linesave)) { char* style; style = strtok_r(line, " ", &wordsave); if (strcmp("move", style) == 0) { word = strtok_r(NULL, " ", &wordsave); RangeSet* src = parse_range(word); word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" moving %d blocks\n", src->size); allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; } p = 0; for (i = 0; i < tgt->count; ++i) { check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; } blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); free(src); free(tgt); } else if (strcmp("zero", style) == 0 || (DEBUG_ERASE && strcmp("erase", style) == 0)) { word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" zeroing %d blocks\n", tgt->size); allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } } if (style[0] == 'z') { // "zero" but not "erase" blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); } free(tgt); } else if (strcmp("new", style) == 0) { word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" writing %d blocks of new data\n", tgt->size); RangeSinkState rss; rss.fd = fd; rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; pthread_cond_broadcast(&nti.cv); while (nti.rss) { pthread_cond_wait(&nti.cv, &nti.mu); } pthread_mutex_unlock(&nti.mu); blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); free(tgt); } else if (strcmp("bsdiff", style) == 0 || strcmp("imgdiff", style) == 0) { word = strtok_r(NULL, " ", &wordsave); size_t patch_offset = strtoul(word, NULL, 0); word = strtok_r(NULL, " ", &wordsave); size_t patch_len = strtoul(word, NULL, 0); word = strtok_r(NULL, " ", &wordsave); RangeSet* src = parse_range(word); word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" patching %d blocks to %d\n", src->size, tgt->size); // Read the source into memory. allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; } Value patch_value; patch_value.type = VAL_BLOB; patch_value.size = patch_len; patch_value.data = (char*)(patch_start + patch_offset); RangeSinkState rss; rss.fd = fd; rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, &patch_value, &RangeSinkWrite, &rss, NULL, NULL); } else { ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE, &patch_value, 0, &RangeSinkWrite, &rss, NULL); } // We expect the output of the patcher to fill the tgt ranges exactly. if (rss.p_block != tgt->count || rss.p_remain != 0) { fprintf(stderr, "range sink underrun?\n"); } blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); free(src); free(tgt); } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) { struct stat st; if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" erasing %d blocks\n", tgt->size); for (i = 0; i < tgt->count; ++i) { uint64_t range[2]; // offset in bytes range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE; // len in bytes range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE; if (ioctl(fd, BLKDISCARD, &range) < 0) { printf(" blkdiscard failed: %s\n", strerror(errno)); } } free(tgt); } else { printf(" ignoring erase (not block device)\n"); } } else { fprintf(stderr, "unknown transfer style \"%s\"\n", style); exit(1); } } pthread_join(new_data_thread, NULL); success = true; free(buffer); printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks); printf("max alloc needed was %zu\n", buffer_alloc); done: free(transfer_list); FreeValue(blockdev_filename); FreeValue(transfer_list_value); FreeValue(new_data_fn); FreeValue(patch_data_fn); return StringValue(success ? strdup("t") : strdup("")); }
std::string MROMInstaller::open(const std::string& file) { char* manifest = NULL; const ZipEntry *script_entry; ZipArchive zip; MemMapping map; if (sysMapFile(file.c_str(), &map) != 0) { LOGERR("Failed to sysMapFile '%s'\n", file.c_str()); return false; } if (mzOpenZipArchive(map.addr, map.length, &zip) != 0) return "Failed to open installer file!"; script_entry = mzFindZipEntry(&zip, "manifest.txt"); if(!script_entry) { mzCloseZipArchive(&zip); sysReleaseMap(&map); return "Failed to find manifest.txt"; } int res = read_data(&zip, script_entry, &manifest, NULL); mzCloseZipArchive(&zip); sysReleaseMap(&map); if(res < 0) return "Failed to read manifest.txt!"; int line_cnt = 1; for(char *line = strtok(manifest, "\r\n"); line; line = strtok(NULL, "\r\n"), ++line_cnt) { if(line[0] == '#') continue; char *val = strchr(line, '='); if(!val) continue; std::string key = std::string(line, val-line); ++val; // skip '=' char char *start = strchr(val, '"'); char *end = strrchr(val, '"'); if(!start || start == end || start+1 == end) gui_print("Line %d: failed to parse string\n", line_cnt); else { ++start; m_vals[key] = std::string(start, end-start); LOGI("MROMInstaller: got tag %s=%s\n", key.c_str(), m_vals[key].c_str()); } } free(manifest); static const char* needed[] = { "manifest_ver", "devices", "base_folders" }; for(uint32_t i = 0; i < sizeof(needed)/sizeof(needed[0]); ++i) { std::map<std::string, std::string>::const_iterator itr = m_vals.find(needed[i]); if(itr == m_vals.end()) return std::string("Required key not found in manifest: ") + needed[i]; } m_file = file; return std::string(); }
int GUIAction::flash_zip(std::string filename, std::string pageName, const int simulate) { int ret_val = 0; DataManager::SetValue("ui_progress", 0); if (filename.empty()) { LOGE("No file specified.\n"); return -1; } // We're going to jump to this page first, like a loading page gui_changePage(pageName); int fd = -1; ZipArchive zip; if (mzOpenZipArchive(filename.c_str(), &zip)) { LOGE("Unable to open zip file.\n"); return -1; } // Check the zip to see if it has a custom installer theme const ZipEntry* twrp = mzFindZipEntry(&zip, "META-INF/teamwin/twrp.zip"); if (twrp != NULL) { unlink("/tmp/twrp.zip"); fd = creat("/tmp/twrp.zip", 0666); } if (fd >= 0 && twrp != NULL && mzExtractZipEntryToFile(&zip, twrp, fd) && !PageManager::LoadPackage("install", "/tmp/twrp.zip")) { mzCloseZipArchive(&zip); PageManager::SelectPackage("install"); gui_changePage("main"); } else { // In this case, we just use the default page mzCloseZipArchive(&zip); gui_changePage(pageName); } if (fd >= 0) close(fd); if (simulate) { simulate_progress_bar(); } else { ret_val = install_zip_package(filename.c_str()); // Now, check if we need to ensure TWRP remains installed... struct stat st; if (stat("/sbin/installTwrp", &st) == 0) { DataManager::SetValue("tw_operation", "Configuring TWRP"); DataManager::SetValue("tw_partition", ""); ui_print("Configuring TWRP...\n"); if (__system("/sbin/installTwrp reinstall") < 0) { ui_print("Unable to configure TWRP with this kernel.\n"); } } } // Done DataManager::SetValue("ui_progress", 100); DataManager::SetValue("ui_progress", 0); return ret_val; }
int main(int argc, char** argv) { // Various things log information to stdout or stderr more or less // at random (though we've tried to standardize on stdout). The // log file makes more sense if buffering is turned off so things // appear in the right order. setbuf(stdout, NULL); setbuf(stderr, NULL); if (argc != 4) { fprintf(stderr, "unexpected number of arguments (%d)\n", argc); return 1; } char* version = argv[1]; if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { // We support version 1, 2, or 3. fprintf(stderr, "wrong updater binary API; expected 1, 2, or 3; " "got %s\n", argv[1]); return 2; } // Set up the pipe for sending commands back to the parent process. int fd = atoi(argv[2]); FILE* cmd_pipe = fdopen(fd, "wb"); setlinebuf(cmd_pipe); // Extract the script from the package. char* package_data = argv[3]; ZipArchive za; int err; err = mzOpenZipArchive(package_data, &za); if (err != 0) { fprintf(stderr, "failed to open package %s: %s\n", package_data, strerror(err)); return 3; } const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); if (script_entry == NULL) { fprintf(stderr, "failed to find %s in %s\n", SCRIPT_NAME, package_data); return 4; } char* script = malloc(script_entry->uncompLen+1); if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) { fprintf(stderr, "failed to read script from package\n"); return 5; } script[script_entry->uncompLen] = '\0'; #if 1 //wschen 2012-06-01 fprintf(stderr, "====== Updater-Script:\n"); fprintf(stderr, "%s\n\n", script); #endif // Configure edify's functions. RegisterBuiltins(); RegisterInstallFunctions(); RegisterDeviceExtensions(); FinishRegistration(); // Parse the script. Expr* root; int error_count = 0; yy_scan_string(script); int error = yyparse(&root, &error_count); if (error != 0 || error_count > 0) { fprintf(stderr, "%d parse errors\n", error_count); return 6; } struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (!sehandle) { fprintf(stderr, "Warning: No file_contexts\n"); fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); } // Evaluate the parsed script. UpdaterInfo updater_info; updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = &za; updater_info.version = atoi(version); State state; state.cookie = &updater_info; state.script = script; state.errmsg = NULL; char* result = Evaluate(&state, root); if (result == NULL) { if (state.errmsg == NULL) { fprintf(stderr, "script aborted (no error message)\n"); fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); } else { fprintf(stderr, "script aborted: %s\n", state.errmsg); char* line = strtok(state.errmsg, "\n"); while (line) { fprintf(cmd_pipe, "ui_print %s\n", line); line = strtok(NULL, "\n"); } fprintf(cmd_pipe, "ui_print\n"); } free(state.errmsg); return 7; } else { fprintf(stderr, "script result was [%s]\n", result); free(result); } if (updater_info.package_zip) { mzCloseZipArchive(updater_info.package_zip); } free(script); return 0; }
static const ZipEntry * find_update_script(ZipArchive *zip) { //TODO: Get the location of this script from the MANIFEST.MF file return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME); }
static int Run_Update_Binary(const char *path, ZipArchive *Zip, int* wipe_cache) { const ZipEntry* binary_location = mzFindZipEntry(Zip, ASSUMED_UPDATE_BINARY_NAME); string Temp_Binary = "/tmp/updater"; int binary_fd, ret_val, pipe_fd[2], status, zip_verify; char buffer[1024]; const char** args = (const char**)malloc(sizeof(char*) * 5); FILE* child_data; if (binary_location == NULL) { mzCloseZipArchive(Zip); return INSTALL_CORRUPT; } // Delete any existing updater if (TWFunc::Path_Exists(Temp_Binary) && unlink(Temp_Binary.c_str()) != 0) { LOGINFO("Unable to unlink '%s'\n", Temp_Binary.c_str()); } binary_fd = creat(Temp_Binary.c_str(), 0755); if (binary_fd < 0) { mzCloseZipArchive(Zip); LOGERR("Could not create file for updater extract in '%s'\n", Temp_Binary.c_str()); return INSTALL_ERROR; } ret_val = mzExtractZipEntryToFile(Zip, binary_location, binary_fd); close(binary_fd); if (!ret_val) { mzCloseZipArchive(Zip); LOGERR("Could not extract '%s'\n", ASSUMED_UPDATE_BINARY_NAME); return INSTALL_ERROR; } // If exists, extract file_contexts from the zip file const ZipEntry* selinx_contexts = mzFindZipEntry(Zip, "file_contexts"); if (selinx_contexts == NULL) { mzCloseZipArchive(Zip); LOGINFO("Zip does not contain SELinux file_contexts file in its root.\n"); } else { string output_filename = "/file_contexts"; LOGINFO("Zip contains SELinux file_contexts file in its root. Extracting to %s\n", output_filename.c_str()); // Delete any file_contexts if (TWFunc::Path_Exists(output_filename) && unlink(output_filename.c_str()) != 0) { LOGINFO("Unable to unlink '%s'\n", output_filename.c_str()); } int file_contexts_fd = creat(output_filename.c_str(), 0644); if (file_contexts_fd < 0) { mzCloseZipArchive(Zip); LOGERR("Could not extract file_contexts to '%s'\n", output_filename.c_str()); return INSTALL_ERROR; } ret_val = mzExtractZipEntryToFile(Zip, selinx_contexts, file_contexts_fd); close(file_contexts_fd); if (!ret_val) { mzCloseZipArchive(Zip); LOGERR("Could not extract '%s'\n", ASSUMED_UPDATE_BINARY_NAME); return INSTALL_ERROR; } } mzCloseZipArchive(Zip); #ifndef TW_NO_LEGACY_PROPS /* Set legacy properties */ if (switch_to_legacy_properties() != 0) { LOGERR("Legacy property environment did not initialize successfully. Properties may not be detected.\n"); } else { LOGINFO("Legacy property environment initialized.\n"); } #endif pipe(pipe_fd); args[0] = Temp_Binary.c_str(); args[1] = EXPAND(RECOVERY_API_VERSION); char* temp = (char*)malloc(10); sprintf(temp, "%d", pipe_fd[1]); args[2] = temp; args[3] = (char*)path; args[4] = NULL; pid_t pid = fork(); if (pid == 0) { close(pipe_fd[0]); execve(Temp_Binary.c_str(), (char* const*)args, environ); printf("E:Can't execute '%s'\n", Temp_Binary.c_str()); _exit(-1); } close(pipe_fd[1]); *wipe_cache = 0; DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify); child_data = fdopen(pipe_fd[0], "r"); while (fgets(buffer, sizeof(buffer), child_data) != NULL) { char* command = strtok(buffer, " \n"); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_char = strtok(NULL, " \n"); char* seconds_char = strtok(NULL, " \n"); float fraction_float = strtof(fraction_char, NULL); int seconds_float = strtol(seconds_char, NULL, 10); if (zip_verify) DataManager::ShowProgress(fraction_float * (1 - VERIFICATION_PROGRESS_FRACTION), seconds_float); else DataManager::ShowProgress(fraction_float, seconds_float); } else if (strcmp(command, "set_progress") == 0) { char* fraction_char = strtok(NULL, " \n"); float fraction_float = strtof(fraction_char, NULL); DataManager::SetProgress(fraction_float); } else if (strcmp(command, "ui_print") == 0) { char* display_value = strtok(NULL, "\n"); if (display_value) { gui_print("%s", display_value); } else { gui_print("\n"); } } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = 1; } else if (strcmp(command, "clear_display") == 0) { // Do nothing, not supported by TWRP } else { LOGERR("unknown command [%s]\n", command); } } fclose(child_data); waitpid(pid, &status, 0); #ifndef TW_NO_LEGACY_PROPS /* Unset legacy properties */ if (legacy_props_path_modified) { if (switch_to_new_properties() != 0) { LOGERR("Legacy property environment did not disable successfully. Legacy properties may still be in use.\n"); } else { LOGINFO("Legacy property environment disabled.\n"); } } #endif if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGERR("Error executing updater binary in zip '%s'\n", path); return INSTALL_ERROR; } return INSTALL_SUCCESS; }