diff options
-rw-r--r-- | etc/init.rc | 8 | ||||
-rw-r--r-- | recovery.cpp | 271 | ||||
-rw-r--r-- | uncrypt/uncrypt.c | 23 | ||||
-rw-r--r-- | updater/Android.mk | 10 | ||||
-rw-r--r-- | updater/blockimg.c | 254 | ||||
-rw-r--r-- | updater/install.c | 33 |
6 files changed, 380 insertions, 219 deletions
diff --git a/etc/init.rc b/etc/init.rc index d0d8ed91b..2cc446d0c 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -34,6 +34,7 @@ on init chmod 0775 /tmp write /proc/sys/kernel/panic_on_oops 1 + write /proc/sys/vm/max_map_count 1000000 on fs mount pstore pstore /sys/fs/pstore @@ -51,7 +52,6 @@ on fs write /sys/class/android_usb/android0/iProduct ${ro.product.model} write /sys/class/android_usb/android0/iSerial ${ro.serialno} - on boot ifup lo hostname localhost @@ -63,6 +63,9 @@ on boot on load_all_props_action load_all_props +on firmware_mounts_complete + rm /dev/.booting + # Mount filesystems and start core system services. on late-init trigger early-fs @@ -75,6 +78,9 @@ on late-init # issued fs triggers have completed. trigger load_all_props_action + # Remove a file to wake up anything waiting for firmware + trigger firmware_mounts_complete + trigger early-boot trigger boot diff --git a/recovery.cpp b/recovery.cpp index c6e12702d..1d22b248a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -25,6 +25,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/klog.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> @@ -33,11 +34,7 @@ #include "bootloader.h" #include "common.h" #include "cutils/properties.h" -#ifdef ANDROID_RB_RESTART #include "cutils/android_reboot.h" -#else -#include <sys/reboot.h> -#endif #include "install.h" #include "minui/minui.h" #include "minzip/DirUtil.h" @@ -52,20 +49,8 @@ extern "C" { #include "fuse_sdcard_provider.h" } -extern "C" { -#include "data.h" -#include "gui/gui.h" -} -#include "partitions.hpp" -#include "variables.h" -#include "openrecoveryscript.hpp" -#include "twrp-functions.hpp" - -TWPartitionManager PartitionManager; - struct selabel_handle *sehandle; - static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 's' }, { "update_package", required_argument, NULL, 'u' }, @@ -92,9 +77,14 @@ static const char *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; +static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; +#define KLOG_DEFAULT_LEN (64 * 1024) #define KEEP_LOG_COUNT 10 +// Number of lines per page when displaying a file on screen +#define LINES_PER_PAGE 30 + RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; @@ -275,8 +265,46 @@ set_sdcard_update_bootloader_message() { set_bootloader_message(&boot); } +// read from kernel log into buffer and write out to file +static void +save_kernel_log(const char *destination) { + int n; + char *buffer; + int klog_buf_len; + FILE *log; + + klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0); + if (klog_buf_len <= 0) { + LOGE("Error getting klog size (%s), using default\n", strerror(errno)); + klog_buf_len = KLOG_DEFAULT_LEN; + } + + buffer = (char *)malloc(klog_buf_len); + if (!buffer) { + LOGE("Can't alloc %d bytes for klog buffer\n", klog_buf_len); + return; + } + + n = klogctl(KLOG_READ_ALL, buffer, klog_buf_len); + if (n < 0) { + LOGE("Error in reading klog (%s)\n", strerror(errno)); + free(buffer); + return; + } + + log = fopen_path(destination, "w"); + if (log == NULL) { + LOGE("Can't open %s\n", destination); + free(buffer); + return; + } + fwrite(buffer, n, 1, log); + check_and_fclose(log, destination); + free(buffer); +} + // How much of the temp log we have copied to the copy in cache. -//static long tmplog_offset = 0; +static long tmplog_offset = 0; static void copy_log_file(const char* source, const char* destination, int append) { @@ -322,8 +350,11 @@ copy_logs() { copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false); copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false); + save_kernel_log(LAST_KMSG_FILE); chmod(LOG_FILE, 0600); chown(LOG_FILE, 1000, 1000); // system user + chmod(LAST_KMSG_FILE, 0600); + chown(LAST_KMSG_FILE, 1000, 1000); // system user chmod(LAST_LOG_FILE, 0640); chmod(LAST_INSTALL_FILE, 0644); sync(); @@ -697,29 +728,71 @@ static void file_to_ui(const char* fn) { } char line[1024]; int ct = 0; + int key = 0; redirect_stdio("/dev/null"); while(fgets(line, sizeof(line), fp) != NULL) { ui->Print("%s", line); ct++; - if (ct % 30 == 0) { + if (ct % LINES_PER_PAGE == 0) { // give the user time to glance at the entries - ui->WaitKey(); + key = ui->WaitKey(); + + if (key == KEY_POWER) { + break; + } + + if (key == KEY_VOLUMEUP) { + // Go back by seeking to the beginning and dumping ct - n + // lines. It's ugly, but this way we don't need to store + // the previous offsets. The files we're dumping here aren't + // expected to be very large. + int i; + + ct -= 2 * LINES_PER_PAGE; + if (ct < 0) { + ct = 0; + } + fseek(fp, 0, SEEK_SET); + for (i = 0; i < ct; i++) { + fgets(line, sizeof(line), fp); + } + ui->Print("^^^^^^^^^^\n"); + } } } + + // If the user didn't abort, then give the user time to glance at + // the end of the log, sorry, no rewind here + if (key != KEY_POWER) { + ui->Print("\n--END-- (press any key)\n"); + ui->WaitKey(); + } + redirect_stdio(TEMPORARY_LOG_FILE); fclose(fp); } static void choose_recovery_file(Device* device) { - int i; + unsigned int i; + unsigned int n; static const char** title_headers = NULL; char *filename; const char* headers[] = { "Select file to view", "", NULL }; - char* entries[KEEP_LOG_COUNT + 2]; + // "Go back" + LAST_KMSG_FILE + KEEP_LOG_COUNT + terminating NULL entry + char* entries[KEEP_LOG_COUNT + 3]; memset(entries, 0, sizeof(entries)); + n = 0; + entries[n++] = strdup("Go back"); + + // Add kernel kmsg file if available + if ((ensure_path_mounted(LAST_KMSG_FILE) == 0) && (access(LAST_KMSG_FILE, R_OK) == 0)) { + entries[n++] = strdup(LAST_KMSG_FILE); + } + + // Add LAST_LOG_FILE + LAST_LOG_FILE.x for (i = 0; i < KEEP_LOG_COUNT; i++) { char *filename; if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) { @@ -728,13 +801,12 @@ static void choose_recovery_file(Device* device) { } if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) { free(filename); - entries[i+1] = NULL; + entries[n++] = NULL; break; } - entries[i+1] = filename; + entries[n++] = filename; } - entries[0] = strdup("Go back"); title_headers = prepend_title((const char**)headers); while(1) { @@ -743,7 +815,7 @@ static void choose_recovery_file(Device* device) { file_to_ui(entries[chosen_item]); } - for (i = 0; i < KEEP_LOG_COUNT + 1; i++) { + for (i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { free(entries[i]); } } @@ -909,10 +981,6 @@ ui_print(const char* format, ...) { int main(int argc, char **argv) { - // Recovery needs to install world-readable files, so clear umask - // set by init - umask(0); - time_t start = time(NULL); redirect_stdio(TEMPORARY_LOG_FILE); @@ -924,41 +992,15 @@ main(int argc, char **argv) { // anything in the command file or bootloader control block; the // only way recovery should be run with this argument is when it // starts a copy of itself from the apply_from_adb() function. - if (argc == 3 && strcmp(argv[1], "--adbd") == 0) { - adb_main(argv[2]); + if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { + adb_main(); return 0; } -<<<<<<< HEAD - printf("Starting TWRP %s on %s", TW_VERSION_STR, ctime(&start)); - - Device* device = make_device(); - ui = device->GetUI(); - - //ui->Init(); - //ui->SetBackground(RecoveryUI::NONE); - //load_volume_table(); - - // Load default values to set DataManager constants and handle ifdefs - DataManager_LoadDefaults(); - printf("Starting the UI..."); - gui_init(); - printf("=> Linking mtab\n"); - symlink("/proc/mounts", "/etc/mtab"); - printf("=> Processing recovery.fstab\n"); - if (!PartitionManager.Process_Fstab("/etc/recovery.fstab", 1)) { - LOGE("Failing out of recovery due to problem with recovery.fstab.\n"); - //return -1; - } - PartitionManager.Output_Partition_Logging(); - // Load up all the resources - gui_loadResources(); - - PartitionManager.Mount_By_Path("/cache", true); + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); ensure_path_mounted(LAST_LOG_FILE); - rotate_last_logs(KEEP_LOG_COUNT); get_args(&argc, &argv); @@ -966,7 +1008,6 @@ main(int argc, char **argv) { const char *update_package = NULL; int wipe_data = 0, wipe_cache = 0, show_text = 0; bool just_exit = false; - bool perform_backup = false; bool shutdown_after = false; int arg; @@ -1027,7 +1068,7 @@ main(int argc, char **argv) { ui->Print("Warning: No file_contexts\n"); } - //device->StartRecovery(); + device->StartRecovery(); printf("Command:"); for (arg = 0; arg < argc; arg++) { @@ -1055,52 +1096,15 @@ main(int argc, char **argv) { property_get("ro.build.display.id", recovery_version, ""); printf("\n"); - // Check for and run startup script if script exists - TWFunc::check_and_run_script("/sbin/runatboot.sh", "boot"); - TWFunc::check_and_run_script("/sbin/postrecoveryboot.sh", "boot"); - -#ifdef TW_INCLUDE_INJECTTWRP - // Back up TWRP Ramdisk if needed: - TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot"); - LOGI("Backing up TWRP ramdisk...\n"); - if (Boot == NULL || Boot->Current_File_System != "emmc") - TWFunc::Exec_Cmd("injecttwrp --backup /tmp/backup_recovery_ramdisk.img"); - else { - string injectcmd = "injecttwrp --backup /tmp/backup_recovery_ramdisk.img bd=" + Boot->Actual_Block_Device; - TWFunc::Exec_Cmd(injectcmd); - } - LOGI("Backup of TWRP ramdisk done.\n"); -#endif - int status = INSTALL_SUCCESS; - string ORSCommand; - - if (perform_backup) { - char empt[50]; - gui_console_only(); - strcpy(empt, "(Current Date)"); - DataManager_SetStrValue(TW_BACKUP_NAME, empt); - if (!OpenRecoveryScript::Insert_ORS_Command("backup BSDCAE\n")) - status = INSTALL_ERROR; - } - if (status == INSTALL_SUCCESS) { // Prevent other actions if backup failed + if (update_package != NULL) { - ORSCommand = "install "; - ORSCommand += update_package; - ORSCommand += "\n"; - - if (OpenRecoveryScript::Insert_ORS_Command(ORSCommand)) - status = INSTALL_SUCCESS; - else - status = INSTALL_ERROR; - /* status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true); if (status == INSTALL_SUCCESS && wipe_cache) { if (erase_volume("/cache")) { LOGE("Cache wipe (requested by package) failed."); } } - if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); @@ -1114,66 +1118,18 @@ main(int argc, char **argv) { } } } else if (wipe_data) { - if (!OpenRecoveryScript::Insert_ORS_Command("wipe data\n")) - status = INSTALL_ERROR; - /* if (device->WipeData()) status = INSTALL_ERROR; if (erase_volume("/data")) status = INSTALL_ERROR; if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; if (erase_persistent_partition() == -1 ) status = INSTALL_ERROR; - */ if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n"); } else if (wipe_cache) { - if (!OpenRecoveryScript::Insert_ORS_Command("wipe cache\n")) - status = INSTALL_ERROR; + if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n"); } else if (!just_exit) { status = INSTALL_NONE; // No command specified ui->SetBackground(RecoveryUI::NO_COMMAND); } - } - - finish_recovery(NULL); - // Offer to decrypt if the device is encrypted - if (DataManager_GetIntValue(TW_IS_ENCRYPTED) != 0) { - LOGI("Is encrypted, do decrypt page first\n"); - if (gui_startPage("decrypt") != 0) { - LOGE("Failed to start decrypt GUI page.\n"); - } - } - - // Read the settings file - DataManager_ReadSettingsFile(); - // Run any outstanding OpenRecoveryScript - if (DataManager_GetIntValue(TW_IS_ENCRYPTED) == 0 && (TWFunc::Path_Exists(SCRIPT_FILE_TMP) || TWFunc::Path_Exists(SCRIPT_FILE_CACHE))) { - OpenRecoveryScript::Run_OpenRecoveryScript(); - } - // Launch the main GUI - gui_start(); - - // Check for su to see if the device is rooted or not - if (PartitionManager.Mount_By_Path("/system", false)) { - // Disable flashing of stock recovery - if (TWFunc::Path_Exists("/system/recovery-from-boot.p") && TWFunc::Path_Exists("/system/etc/install-recovery.sh")) { - rename("/system/recovery-from-boot.p", "/system/recovery-from-boot.bak"); - ui_print("Renamed stock recovery file in /system to prevent\nthe stock ROM from replacing TWRP.\n"); - } - if (TWFunc::Path_Exists("/supersu/su") && !TWFunc::Path_Exists("/system/bin/su") && !TWFunc::Path_Exists("/system/xbin/su") && !TWFunc::Path_Exists("/system/bin/.ext/.su")) { - // Device doesn't have su installed - DataManager_SetIntValue("tw_busy", 1); - if (gui_startPage("installsu") != 0) { - LOGE("Failed to start decrypt GUI page.\n"); - } - } else if (TWFunc::Check_su_Perms() > 0) { - // su perms are set incorrectly - DataManager_SetIntValue("tw_busy", 1); - if (gui_startPage("fixsu") != 0) { - LOGE("Failed to start decrypt GUI page.\n"); - } - } - sync(); - PartitionManager.UnMount_By_Path("/system", false); - } if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { copy_logs(); @@ -1187,27 +1143,6 @@ main(int argc, char **argv) { // Save logs and clean up before rebooting or shutting down. finish_recovery(send_intent); - ui->Print("Rebooting...\n"); - char backup_arg_char[50]; - strcpy(backup_arg_char, DataManager_GetStrValue("tw_reboot_arg")); - string backup_arg = backup_arg_char; - if (backup_arg == "recovery") - TWFunc::tw_reboot(rb_recovery); - else if (backup_arg == "poweroff") - TWFunc::tw_reboot(rb_poweroff); - else if (backup_arg == "bootloader") - TWFunc::tw_reboot(rb_bootloader); - else if (backup_arg == "download") - TWFunc::tw_reboot(rb_download); - else - TWFunc::tw_reboot(rb_system); - -#ifdef ANDROID_RB_RESTART - android_reboot(ANDROID_RB_RESTART, 0, 0); -#else - reboot(RB_AUTOBOOT); -#endif - property_set(ANDROID_RB_PROPERTY, "reboot,"); switch (after) { case Device::SHUTDOWN: diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 189fa57e1..7fb0989a7 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -164,7 +164,12 @@ char* parse_recovery_command_file() if (f == NULL) { return NULL; } - FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w"); + int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + ALOGE("failed to open %s\n", RECOVERY_COMMAND_FILE_TMP); + return NULL; + } + FILE* fo = fdopen(fd, "w"); while (fgets(temp, sizeof(temp), f)) { printf("read: %s", temp); @@ -175,6 +180,7 @@ char* parse_recovery_command_file() fputs(temp, fo); } fclose(f); + fsync(fd); fclose(fo); if (fn) { @@ -190,7 +196,12 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de struct stat sb; int ret; - FILE* mapf = fopen(map_file, "w"); + int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); + if (mapfd < 0) { + ALOGE("failed to open %s\n", map_file); + return -1; + } + FILE* mapf = fdopen(mapfd, "w"); ret = stat(path, &sb); if (ret != 0) { @@ -232,7 +243,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de int wfd = -1; if (encrypted) { - wfd = open(blk_dev, O_WRONLY); + wfd = open(blk_dev, O_WRONLY | O_SYNC); if (wfd < 0) { ALOGE("failed to open fd for writing: %s\n", strerror(errno)); return -1; @@ -302,9 +313,11 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); } + fsync(mapfd); fclose(mapf); close(fd); if (encrypted) { + fsync(wfd); close(wfd); } @@ -318,7 +331,7 @@ void wipe_misc() { struct fstab_rec* v = &fstab->recs[i]; if (!v->mount_point) continue; if (strcmp(v->mount_point, "/misc") == 0) { - int fd = open(v->blk_device, O_WRONLY); + int fd = open(v->blk_device, O_WRONLY | O_SYNC); uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery memset(zeroes, 0, sizeof(zeroes)); @@ -333,7 +346,7 @@ void wipe_misc() { written += w; } } - + fsync(fd); close(fd); } } diff --git a/updater/Android.mk b/updater/Android.mk index edc9f589d..cc2206134 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -46,6 +46,16 @@ LOCAL_STATIC_LIBRARIES += libflashutils libmmcutils libbmlutils LOCAL_STATIC_LIBRARIES += libmincrypttwrp libbz LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux +tune2fs_static_libraries := \ + libext2_com_err \ + libext2_blkid \ + libext2_quota \ + libext2_uuid_static \ + libext2_e2p \ + libext2fs +LOCAL_STATIC_LIBRARIES += libtune2fs $(tune2fs_static_libraries) + +LOCAL_C_INCLUDES += external/e2fsprogs/misc LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function diff --git a/updater/blockimg.c b/updater/blockimg.c index c3319c973..302689313 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -61,7 +61,7 @@ static RangeSet* parse_range(char* text) { RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); if (out == NULL) { - fprintf(stderr, "failed to allocate range of %lu bytes\n", + fprintf(stderr, "failed to allocate range of %zu bytes\n", sizeof(RangeSet) + num * sizeof(int)); exit(1); } @@ -245,6 +245,133 @@ static void* unzip_new_data(void* cookie) { return NULL; } +// Do a source/target load for move/bsdiff/imgdiff in version 1. +// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect +// to parse the remainder of the string as: +// +// <src_range> <tgt_range> +// +// The source range is loaded into the provided buffer, reallocating +// it to make it larger if necessary. The target ranges are returned +// in *tgt, if tgt is non-NULL. + +static void LoadSrcTgtVersion1(char* wordsave, RangeSet** tgt, int* src_blocks, + uint8_t** buffer, size_t* buffer_alloc, int fd) { + char* word; + + word = strtok_r(NULL, " ", &wordsave); + RangeSet* src = parse_range(word); + + if (tgt != NULL) { + word = strtok_r(NULL, " ", &wordsave); + *tgt = parse_range(word); + } + + allocate(src->size * BLOCKSIZE, buffer, buffer_alloc); + size_t p = 0; + int i; + 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; + } + + *src_blocks = src->size; + free(src); +} + +static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) { + // source contains packed data, which we want to move to the + // locations given in *locs in the dest buffer. source and dest + // may be the same buffer. + + int start = locs->size; + int i; + for (i = locs->count-1; i >= 0; --i) { + int blocks = locs->pos[i*2+1] - locs->pos[i*2]; + start -= blocks; + memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE), + blocks * BLOCKSIZE); + } +} + +// Do a source/target load for move/bsdiff/imgdiff in version 2. +// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect +// to parse the remainder of the string as one of: +// +// <tgt_range> <src_block_count> <src_range> +// (loads data from source image only) +// +// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...> +// (loads data from stashes only) +// +// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> +// (loads data from both source image and stashes) +// +// On return, buffer is filled with the loaded source data (rearranged +// and combined with stashed data as necessary). buffer may be +// reallocated if needed to accommodate the source data. *tgt is the +// target RangeSet. Any stashes required are taken from stash_table +// and free()'d after being used. + +static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks, + uint8_t** buffer, size_t* buffer_alloc, int fd, + uint8_t** stash_table) { + char* word; + + if (tgt != NULL) { + word = strtok_r(NULL, " ", &wordsave); + *tgt = parse_range(word); + } + + word = strtok_r(NULL, " ", &wordsave); + *src_blocks = strtol(word, NULL, 0); + + allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc); + + word = strtok_r(NULL, " ", &wordsave); + if (word[0] == '-' && word[1] == '\0') { + // no source ranges, only stashes + } else { + RangeSet* src = parse_range(word); + + size_t p = 0; + int i; + 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; + } + free(src); + + word = strtok_r(NULL, " ", &wordsave); + if (word == NULL) { + // no stashes, only source range + return; + } + + RangeSet* locs = parse_range(word); + MoveRange(*buffer, locs, *buffer); + } + + while ((word = strtok_r(NULL, " ", &wordsave)) != NULL) { + // Each word is a an index into the stash table, a colon, and + // then a rangeset describing where in the source block that + // stashed data should go. + char* colonsave = NULL; + char* colon = strtok_r(word, ":", &colonsave); + int stash_id = strtol(colon, NULL, 0); + colon = strtok_r(NULL, ":", &colonsave); + RangeSet* locs = parse_range(colon); + MoveRange(*buffer, locs, stash_table[stash_id]); + free(stash_table[stash_id]); + stash_table[stash_id] = NULL; + free(locs); + } +} + // args: // - block device (or file) to modify in-place // - transfer list (blob) @@ -311,23 +438,33 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] // 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 // + // move <...> + // bsdiff <patchstart> <patchlen> <...> + // imgdiff <patchstart> <patchlen> <...> + // - read the source blocks, apply a patch (or not in the + // case of move), write result to target blocks. bsdiff or + // imgdiff specifies the type of patch; move means no patch + // at all. + // + // The format of <...> differs between versions 1 and 2; + // see the LoadSrcTgtVersion{1,2}() functions for a + // description of what's expected. + // + // stash <stash_id> <src_range> + // - (version 2 only) load the given source range and stash + // the data in the given slot of the stash table. + // // 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. // + // In version 2, the creator will guarantee that a given stash is + // loaded (with a stash command) before it's used in a + // move/bsdiff/imgdiff command. + // // 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. @@ -379,12 +516,18 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] line = strtok_r(transfer_list, "\n", &linesave); + int version; // first line in transfer list is the version number; currently // there's only version 1. - if (strcmp(line, "1") != 0) { + if (strcmp(line, "1") == 0) { + version = 1; + } else if (strcmp(line, "2") == 0) { + version = 2; + } else { ErrorAbort(state, "unexpected transfer list version [%s]\n", line); goto done; } + printf("blockimg version is %d\n", version); // second line in transfer list is the total number of blocks we // expect to write. @@ -394,33 +537,49 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] if (total_blocks == 0) ++total_blocks; int blocks_so_far = 0; + uint8_t** stash_table = NULL; + if (version >= 2) { + // Next line is how many stash entries are needed simultaneously. + line = strtok_r(NULL, "\n", &linesave); + int stash_entries = strtol(line, NULL, 0); + + stash_table = (uint8_t**) calloc(stash_entries, sizeof(uint8_t*)); + if (stash_table == NULL) { + fprintf(stderr, "failed to allocate %d-entry stash table\n", stash_entries); + exit(1); + } + + // Next line is the maximum number of blocks that will be + // stashed simultaneously. This could be used to verify that + // enough memory or scratch disk space is available. + line = strtok_r(NULL, "\n", &linesave); + int stash_max_blocks = strtol(line, NULL, 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); + RangeSet* tgt; + int src_blocks; + if (version == 1) { + LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd); + } else if (version == 2) { + LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd, stash_table); + } - printf(" moving %d blocks\n", src->size); + printf(" moving %d blocks\n", src_blocks); - 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; @@ -432,9 +591,20 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); - free(src); free(tgt); + } else if (strcmp("stash", style) == 0) { + word = strtok_r(NULL, " ", &wordsave); + int stash_id = strtol(word, NULL, 0); + int src_blocks; + size_t stash_alloc = 0; + + // Even though the "stash" style only appears in version + // 2, the version 1 source loader happens to do exactly + // what we want to read data into the stash_table. + LoadSrcTgtVersion1(wordsave, NULL, &src_blocks, + stash_table + stash_id, &stash_alloc, fd); + } else if (strcmp("zero", style) == 0 || (DEBUG_ERASE && strcmp("erase", style) == 0)) { word = strtok_r(NULL, " ", &wordsave); @@ -493,23 +663,18 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] 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; + RangeSet* tgt; + int src_blocks; + if (version == 1) { + LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd); + } else if (version == 2) { + LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd, stash_table); } + printf(" patching %d blocks to %d\n", src_blocks, tgt->size); + Value patch_value; patch_value.type = VAL_BLOB; patch_value.size = patch_len; @@ -523,11 +688,11 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff - ApplyImagePatch(buffer, src->size * BLOCKSIZE, + ApplyImagePatch(buffer, src_blocks * BLOCKSIZE, &patch_value, &RangeSinkWrite, &rss, NULL, NULL); } else { - ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE, + ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE, &patch_value, 0, &RangeSinkWrite, &rss, NULL); } @@ -541,7 +706,6 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] 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; diff --git a/updater/install.c b/updater/install.c index 003064799..20ee431de 100644 --- a/updater/install.c +++ b/updater/install.c @@ -48,6 +48,7 @@ #include "applypatch/applypatch.h" #include "flashutils/flashutils.h" #include "install.h" +#include "tune2fs.h" #ifdef USE_EXT4 #include "make_ext4fs.h" @@ -1489,6 +1490,37 @@ Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup("t")); } +Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return ErrorAbort(state, "%s() expects args, got %d", name, argc); + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) { + return ErrorAbort(state, "%s() could not read args", name); + } + + int i; + char** args2 = malloc(sizeof(char*) * (argc+1)); + // Tune2fs expects the program name as its args[0] + args2[0] = strdup(name); + for (i = 0; i < argc; ++i) { + args2[i + 1] = args[i]; + } + int result = tune2fs_main(argc + 1, args2); + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + free(args2[0]); + free(args2); + if (result != 0) { + return ErrorAbort(state, "%s() returned error code %d", name, result); + } + return StringValue(strdup("t")); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1539,4 +1571,5 @@ void RegisterInstallFunctions() { RegisterFunction("set_stage", SetStageFn); RegisterFunction("enable_reboot", EnableRebootFn); + RegisterFunction("tune2fs", Tune2FsFn); } |