diff options
author | Ethan Yonker <dees_troy@teamw.in> | 2015-10-09 18:15:26 +0200 |
---|---|---|
committer | Ethan Yonker <dees_troy@teamw.in> | 2015-10-09 18:15:29 +0200 |
commit | c798c9cd2486e0ff83776002c74f113677b10a84 (patch) | |
tree | d128a80cbc58e63a622fda2774e727611f9d2cd4 | |
parent | Add TW_IGNORE_ABS_MT_TRACKING_ID (diff) | |
parent | merge in mnc-release history after reset to mnc-dev (diff) | |
download | android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.tar android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.tar.gz android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.tar.bz2 android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.tar.lz android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.tar.xz android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.tar.zst android_bootable_recovery-c798c9cd2486e0ff83776002c74f113677b10a84.zip |
-rw-r--r-- | Android.mk | 50 | ||||
-rw-r--r-- | README.md | 9 | ||||
-rw-r--r-- | adb_install.cpp | 12 | ||||
-rw-r--r-- | applypatch/applypatch.c | 53 | ||||
-rw-r--r-- | applypatch/bspatch.c | 1 | ||||
-rw-r--r-- | applypatch/imgdiff.c | 9 | ||||
-rw-r--r-- | applypatch/imgpatch.c | 1 | ||||
-rw-r--r-- | asn1_decoder.cpp | 1 | ||||
-rw-r--r-- | bootloader.cpp | 1 | ||||
-rw-r--r-- | common.h | 14 | ||||
-rw-r--r-- | crypto/lollipop/Android.mk | 40 | ||||
-rw-r--r-- | crypto/lollipop/cryptfs.c | 355 | ||||
-rw-r--r-- | crypto/lollipop/main.c | 9 | ||||
-rw-r--r-- | crypto/scrypt/Scrypt.mk | 2 | ||||
-rw-r--r-- | data.cpp | 5 | ||||
-rw-r--r-- | data.hpp | 1 | ||||
-rw-r--r-- | default_device.cpp | 67 | ||||
-rw-r--r-- | device.cpp | 74 | ||||
-rw-r--r-- | device.h | 70 | ||||
-rw-r--r-- | digest/md5.h | 2 | ||||
-rw-r--r-- | edify/Android.mk | 7 | ||||
-rw-r--r-- | edify/main.c | 3 | ||||
-rw-r--r-- | etc/init.rc | 9 | ||||
-rw-r--r-- | find_file.cpp | 1 | ||||
-rw-r--r-- | flashutils/flashutils.c | 1 | ||||
-rw-r--r-- | fuse_sdcard_provider.c | 17 | ||||
-rw-r--r-- | fuse_sdcard_provider.h | 6 | ||||
-rw-r--r-- | fuse_sideload.c | 52 | ||||
-rw-r--r-- | fuse_sideload.h | 6 | ||||
-rw-r--r-- | gui/Android.mk | 2 | ||||
-rw-r--r-- | gui/action.cpp | 1 | ||||
-rw-r--r-- | install.cpp | 27 | ||||
-rw-r--r-- | install.h | 2 | ||||
-rw-r--r-- | libblkid/include/all-io.h | 2 | ||||
-rw-r--r-- | libblkid/lib/exec_shell.c | 1 | ||||
-rw-r--r-- | libblkid/lib/fileutils.c | 4 | ||||
-rw-r--r-- | libblkid/lib/sysfs.c | 7 | ||||
-rw-r--r-- | libpixelflinger/Android.mk | 102 | ||||
-rw-r--r-- | libtar/extract.c | 1 | ||||
-rw-r--r-- | minadbd.old/Android.mk | 30 | ||||
-rw-r--r-- | minadbd.old/README.txt | 39 | ||||
-rw-r--r-- | minadbd.old/adb.c (renamed from minadbd/adb.c) | 0 | ||||
-rw-r--r-- | minadbd.old/adb.h (renamed from minadbd/adb.h) | 0 | ||||
-rw-r--r-- | minadbd.old/fdevent.c (renamed from minadbd/fdevent.c) | 0 | ||||
-rw-r--r-- | minadbd.old/fdevent.h (renamed from minadbd/fdevent.h) | 0 | ||||
-rw-r--r-- | minadbd.old/fuse_adb_provider.c (renamed from minadbd/fuse_adb_provider.c) | 0 | ||||
-rw-r--r-- | minadbd.old/fuse_adb_provider.h | 22 | ||||
-rw-r--r-- | minadbd.old/mutex_list.h (renamed from minadbd/mutex_list.h) | 0 | ||||
-rw-r--r-- | minadbd.old/services.c (renamed from minadbd/services.c) | 0 | ||||
-rw-r--r-- | minadbd.old/sockets.c (renamed from minadbd/sockets.c) | 0 | ||||
-rw-r--r-- | minadbd.old/sysdeps.h (renamed from minadbd/sysdeps.h) | 0 | ||||
-rw-r--r-- | minadbd.old/transport.c (renamed from minadbd/transport.c) | 0 | ||||
-rw-r--r-- | minadbd.old/transport.h (renamed from minadbd/transport.h) | 0 | ||||
-rw-r--r-- | minadbd.old/transport_usb.c (renamed from minadbd/transport_usb.c) | 0 | ||||
-rw-r--r-- | minadbd.old/usb_linux_client.c (renamed from minadbd/usb_linux_client.c) | 0 | ||||
-rw-r--r-- | minadbd.old/utils.c (renamed from minadbd/utils.c) | 0 | ||||
-rw-r--r-- | minadbd.old/utils.h (renamed from minadbd/utils.h) | 0 | ||||
-rw-r--r-- | minadbd/Android.mk | 48 | ||||
-rw-r--r-- | minadbd/README.txt | 37 | ||||
-rw-r--r-- | minadbd/adb_main.cpp | 49 | ||||
-rw-r--r-- | minadbd/fuse_adb_provider.cpp | 61 | ||||
-rw-r--r-- | minadbd/fuse_adb_provider.h | 10 | ||||
-rw-r--r-- | minadbd/fuse_adb_provider_test.cpp | 88 | ||||
-rw-r--r-- | minadbd/services.cpp | 106 | ||||
-rw-r--r-- | minui.old/Android.mk | 140 | ||||
-rw-r--r-- | minui.old/events.c (renamed from minui/events.c) | 0 | ||||
-rw-r--r-- | minui.old/font_10x18.h | 214 | ||||
-rw-r--r-- | minui.old/font_7x16.h (renamed from minui/font_7x16.h) | 0 | ||||
-rw-r--r-- | minui.old/graphics.c (renamed from minui/graphics.c) | 0 | ||||
-rw-r--r-- | minui.old/graphics_overlay.c (renamed from minui/graphics_overlay.c) | 0 | ||||
-rw-r--r-- | minui.old/include/linux/msm_ion.h (renamed from minui/include/linux/msm_ion.h) | 0 | ||||
-rw-r--r-- | minui.old/include/linux/msm_mdp.h (renamed from minui/include/linux/msm_mdp.h) | 0 | ||||
-rw-r--r-- | minui.old/minui.h | 105 | ||||
-rw-r--r-- | minui.old/mkfont.c | 54 | ||||
-rw-r--r-- | minui.old/resources.c (renamed from minui/resources.c) | 0 | ||||
-rw-r--r-- | minui.old/roboto_10x18.h (renamed from minui/roboto_10x18.h) | 0 | ||||
-rw-r--r-- | minui.old/roboto_15x24.h (renamed from minui/roboto_15x24.h) | 0 | ||||
-rw-r--r-- | minui.old/roboto_23x41.h (renamed from minui/roboto_23x41.h) | 0 | ||||
-rw-r--r-- | minui/Android.mk | 101 | ||||
-rw-r--r-- | minui/events.cpp | 232 | ||||
-rw-r--r-- | minui/font_10x18.h | 2 | ||||
-rw-r--r-- | minui/graphics.cpp | 411 | ||||
-rw-r--r-- | minui/graphics.h | 43 | ||||
-rw-r--r-- | minui/graphics_adf.cpp | 249 | ||||
-rw-r--r-- | minui/graphics_drm.cpp | 476 | ||||
-rw-r--r-- | minui/graphics_fbdev.cpp | 227 | ||||
-rw-r--r-- | minui/minui.h | 113 | ||||
-rw-r--r-- | minui/resources.cpp | 459 | ||||
-rw-r--r-- | minuitwrp/Android.mk | 2 | ||||
-rw-r--r-- | minuitwrp/events.c | 2 | ||||
-rw-r--r-- | minuitwrp/graphics.c | 1 | ||||
-rw-r--r-- | minuitwrp/resources.c | 1 | ||||
-rw-r--r-- | minzip/DirUtil.c | 4 | ||||
-rw-r--r-- | minzip/Hash.c | 11 | ||||
-rw-r--r-- | minzip/SysUtil.c | 10 | ||||
-rw-r--r-- | minzip/Zip.c | 223 | ||||
-rw-r--r-- | minzip/Zip.h | 74 | ||||
-rw-r--r-- | mtdutils/flash_image.c | 8 | ||||
-rw-r--r-- | mtdutils/mounts.c | 89 | ||||
-rw-r--r-- | mtdutils/mtdutils.c | 47 | ||||
-rw-r--r-- | mtdutils/mtdutils.h | 2 | ||||
-rwxr-xr-x | mtp/Android.mk | 12 | ||||
-rw-r--r-- | openaes/src/ftime.h | 2 | ||||
-rw-r--r-- | openaes/src/oaes_lib.c | 1 | ||||
-rw-r--r-- | partitionmanager.cpp | 2 | ||||
-rw-r--r-- | pigz/yarn.c | 16 | ||||
-rw-r--r-- | prebuilt/Android.mk | 33 | ||||
-rwxr-xr-x | prebuilt/permissive.sh | 8 | ||||
-rwxr-xr-x | prebuilt/relink.sh | 2 | ||||
-rw-r--r-- | recovery.cpp | 562 | ||||
-rw-r--r-- | roots.cpp | 40 | ||||
-rw-r--r-- | roots.h | 5 | ||||
-rw-r--r-- | screen_ui.cpp | 399 | ||||
-rw-r--r-- | screen_ui.h | 73 | ||||
-rw-r--r-- | tarWrite.c | 5 | ||||
-rw-r--r-- | tests/Android.mk | 48 | ||||
-rw-r--r-- | toolbox/Android.mk | 220 | ||||
-rw-r--r-- | tools/ota/check-lost+found.c | 3 | ||||
-rw-r--r-- | toybox/Android.mk | 371 | ||||
-rw-r--r-- | twinstall.cpp | 1 | ||||
-rw-r--r-- | twrp.cpp | 12 | ||||
-rw-r--r-- | twrpTar.cpp | 3 | ||||
-rw-r--r-- | twrpTar.hpp | 2 | ||||
-rw-r--r-- | ui.cpp | 210 | ||||
-rw-r--r-- | ui.h | 55 | ||||
-rw-r--r-- | uncrypt/Android.mk | 7 | ||||
-rw-r--r-- | uncrypt/uncrypt.cpp (renamed from uncrypt/uncrypt.c) | 285 | ||||
-rw-r--r-- | updater/Android.mk | 2 | ||||
-rw-r--r-- | updater/MODULE_LICENSE_GPL | 0 | ||||
-rw-r--r-- | updater/NOTICE | 339 | ||||
-rw-r--r-- | updater/blockimg.c | 1872 | ||||
-rw-r--r-- | updater/install.c | 2 | ||||
-rw-r--r-- | updater/updater.c | 1 | ||||
-rw-r--r-- | verifier.cpp | 5 | ||||
-rw-r--r-- | verifier_test.cpp | 9 |
135 files changed, 7085 insertions, 2339 deletions
diff --git a/Android.mk b/Android.mk index c1365e70a..ed32528d5 100644 --- a/Android.mk +++ b/Android.mk @@ -88,14 +88,25 @@ LOCAL_CFLAGS += -Wno-unused-parameter # libm \ # libc +LOCAL_C_INCLUDES += \ + system/vold \ + system/extras/ext4_utils \ + system/core/adb \ + LOCAL_C_INCLUDES += bionic external/stlport/stlport external/openssl/include $(LOCAL_PATH)/libmincrypt/includes LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := LOCAL_STATIC_LIBRARIES += libguitwrp -LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery -LOCAL_SHARED_LIBRARIES += libgccdemangle libcrecovery +LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery +LOCAL_SHARED_LIBRARIES += libcrecovery + +ifneq ($(wildcard external/stlport/Android.mk),) + LOCAL_SHARED_LIBRARIES += libstlport +else + LOCAL_SHARED_LIBRARIES += libc++ +endif ifneq ($(wildcard system/core/libsparse/Android.mk),) LOCAL_SHARED_LIBRARIES += libsparse @@ -113,7 +124,7 @@ ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_SHARED_LIBRARIES += libext4_utils ifneq ($(wildcard external/lz4/Android.mk),) - LOCAL_STATIC_LIBRARIES += liblz4-static + #LOCAL_STATIC_LIBRARIES += liblz4-static endif endif ifneq ($(wildcard external/libselinux/Android.mk),) @@ -138,14 +149,10 @@ ifeq ($(TWHAVE_SELINUX), true) endif endif -# This binary is in the recovery ramdisk, which is otherwise a copy of root. -# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses -# a (redundant) copy of the binary in /system/bin for user builds. -# TODO: Build the ramdisk image in a more principled way. -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin #ifeq ($(TARGET_RECOVERY_UI_LIB),) - LOCAL_SRC_FILES += default_device.cpp +# LOCAL_SRC_FILES += default_device.cpp #else # LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) #endif @@ -271,6 +278,7 @@ endif ifeq ($(TW_INCLUDE_CRYPTO), true) LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO LOCAL_SHARED_LIBRARIES += libcryptfslollipop + LOCAL_C_INCLUDES += external/boringssl/src/include endif ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true) LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID @@ -311,6 +319,9 @@ endif ifneq ($(TARGET_RECOVERY_INITRC),) TW_EXCLUDE_DEFAULT_USB_INIT := true endif +ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) + LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD +endif LOCAL_ADDITIONAL_DEPENDENCIES := \ dump_image \ @@ -339,6 +350,10 @@ else endif ifneq ($(TW_USE_TOOLBOX), true) LOCAL_ADDITIONAL_DEPENDENCIES += busybox_symlinks +else + ifneq ($(wildcard external/toybox/Android.mk),) + LOCAL_ADDITIONAL_DEPENDENCIES += toybox_symlinks + endif endif ifneq ($(TW_NO_EXFAT), true) LOCAL_ADDITIONAL_DEPENDENCIES += mkexfatfs @@ -483,9 +498,7 @@ endif include $(BUILD_SHARED_LIBRARY) commands_recovery_local_path := $(LOCAL_PATH) -include $(LOCAL_PATH)/minui/Android.mk \ - $(LOCAL_PATH)/minadbd/Android.mk \ - $(LOCAL_PATH)/tests/Android.mk \ +include $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/updater/Android.mk \ @@ -495,6 +508,15 @@ ifeq ($(wildcard system/core/uncrypt/Android.mk),) include $(commands_recovery_local_path)/uncrypt/Android.mk endif +ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) + include $(commands_recovery_local_path)/minadbd/Android.mk \ + $(commands_recovery_local_path)/minui/Android.mk +else + TARGET_GLOBAL_CFLAGS += -DTW_USE_OLD_MINUI_H + include $(commands_recovery_local_path)/minadbd.old/Android.mk \ + $(commands_recovery_local_path)/minui.old/Android.mk +endif + #includes for TWRP include $(commands_recovery_local_path)/injecttwrp/Android.mk \ $(commands_recovery_local_path)/htcdumlock/Android.mk \ @@ -516,7 +538,9 @@ include $(commands_recovery_local_path)/injecttwrp/Android.mk \ $(commands_recovery_local_path)/mtp/Android.mk \ $(commands_recovery_local_path)/minzip/Android.mk \ $(commands_recovery_local_path)/dosfstools/Android.mk \ - $(commands_recovery_local_path)/etc/Android.mk + $(commands_recovery_local_path)/etc/Android.mk \ + $(commands_recovery_local_path)/toybox/Android.mk \ + $(commands_recovery_local_path)/libpixelflinger/Android.mk ifeq ($(TW_INCLUDE_CRYPTO), true) include $(commands_recovery_local_path)/crypto/lollipop/Android.mk @@ -1,13 +1,4 @@ **Team Win Recovery Project (TWRP)** -The goal of this branch is to rebase TWRP onto AOSP while maintaining as much of the original AOSP code as possible. This goal should allow us to apply updates to the AOSP code going forward with little to no extra work. With this goal in mind, we will carefully consider any changes needed to the AOSP code before allowing them. In most cases, instead of changing the AOSP code, we'll create our own functions instead. The only changes that should be made to AOSP code should be those affecting startup of the recovery and some of the make files. - -If there are changes that need to be merged from AOSP, we will pull the change directly from AOSP instead of creating a new patch in order to prevent merge conflicts with AOSP. - -This branch is under final testing and will be used shortly for public builds, but has not officially been released. - You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide"). -[More information about the project.](http://www.teamw.in/project/twrp2 "More Information") - -If you have code changes to submit those should be pushed to our gerrit instance. A guide can be found [here](http://teamw.in/twrp2-gerrit "Gerrit Guide"). diff --git a/adb_install.cpp b/adb_install.cpp index 3e53066f5..5c54468d4 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -29,10 +29,8 @@ #include "ui.h" #include "cutils/properties.h" #include "adb_install.h" -extern "C" { #include "minadbd/fuse_adb_provider.h" #include "fuse_sideload.h" -} static RecoveryUI* ui = NULL; @@ -47,7 +45,8 @@ set_usb_driver(bool enabled) { printf("failed to open driver control: %s\n", strerror(errno)); return; } - if (write(fd, enabled ? "1" : "0", 1) < 0) { + + if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) { /* ui->Print("failed to set driver control: %s\n", strerror(errno)); */ @@ -67,12 +66,15 @@ stop_adbd() { set_usb_driver(false); } +bool is_ro_debuggable() { + char value[PROPERTY_VALUE_MAX+1]; + return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1'); +} void maybe_restart_adbd() { char value[PROPERTY_VALUE_MAX+1]; - int len = property_get("ro.debuggable", value, NULL); - if (len == 1 && value[0] == '1') { + if (is_ro_debuggable()) { printf("Restarting adbd...\n"); set_usb_driver(true); property_set("ctl.start", "adbd"); diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 73195d96a..bc45e3c45 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -453,20 +453,19 @@ int WriteToPartition(unsigned char* data, size_t len, int attempt; for (attempt = 0; attempt < 2; ++attempt) { - lseek(fd, start, SEEK_SET); + if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { + printf("failed seek on %s: %s\n", + partition, strerror(errno)); + return -1; + } while (start < len) { size_t to_write = len - start; if (to_write > 1<<20) to_write = 1<<20; - ssize_t written = write(fd, data+start, to_write); - if (written < 0) { - if (errno == EINTR) { - written = 0; - } else { - printf("failed write writing to %s (%s)\n", - partition, strerror(errno)); - return -1; - } + ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write)); + if (written == -1) { + printf("failed write writing to %s: %s\n", partition, strerror(errno)); + return -1; } start += written; } @@ -491,13 +490,20 @@ int WriteToPartition(unsigned char* data, size_t len, // won't just be reading the cache. sync(); int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); - write(dc, "3\n", 2); + if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) { + printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); + } else { + printf(" caches dropped\n"); + } close(dc); sleep(1); - printf(" caches dropped\n"); // verify - lseek(fd, 0, SEEK_SET); + if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { + printf("failed to seek back to beginning of %s: %s\n", + partition, strerror(errno)); + return -1; + } unsigned char buffer[4096]; start = len; size_t p; @@ -507,15 +513,12 @@ int WriteToPartition(unsigned char* data, size_t len, size_t so_far = 0; while (so_far < to_read) { - ssize_t read_count = read(fd, buffer+so_far, to_read-so_far); - if (read_count < 0) { - if (errno == EINTR) { - read_count = 0; - } else { - printf("verify read error %s at %zu: %s\n", - partition, p, strerror(errno)); - return -1; - } + ssize_t read_count = + TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far)); + if (read_count == -1) { + printf("verify read error %s at %zu: %s\n", + partition, p, strerror(errno)); + return -1; } if ((size_t)read_count < to_read) { printf("short verify read %s at %zu: %zd %zu %s\n", @@ -656,8 +659,8 @@ ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { ssize_t done = 0; ssize_t wrote; while (done < (ssize_t) len) { - wrote = write(fd, data+done, len-done); - if (wrote <= 0) { + wrote = TEMP_FAILURE_RETRY(write(fd, data+done, len-done)); + if (wrote == -1) { printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno)); return done; } @@ -690,7 +693,7 @@ size_t FreeSpaceForFile(const char* filename) { printf("failed to statfs %s: %s\n", filename, strerror(errno)); return -1; } - return sf.f_bsize * sf.f_bfree; + return sf.f_bsize * sf.f_bavail; } int CacheSizeCheck(size_t bytes) { diff --git a/applypatch/bspatch.c b/applypatch/bspatch.c index b34ec2a88..b57760eda 100644 --- a/applypatch/bspatch.c +++ b/applypatch/bspatch.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <sys/stat.h> #include <errno.h> +#include <malloc.h> #include <unistd.h> #include <string.h> diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.c index 05c4f250f..3bac8be91 100644 --- a/applypatch/imgdiff.c +++ b/applypatch/imgdiff.c @@ -408,6 +408,7 @@ unsigned char* ReadImage(const char* filename, p[2] == 0x08 && // deflate compression p[3] == 0x00) { // no header flags // 'pos' is the offset of the start of a gzip chunk. + size_t chunk_offset = pos; *num_chunks += 3; *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk)); @@ -453,6 +454,14 @@ unsigned char* ReadImage(const char* filename, strm.avail_out = allocated - curr->len; strm.next_out = curr->data + curr->len; ret = inflate(&strm, Z_NO_FLUSH); + if (ret < 0) { + printf("Error: inflate failed [%s] at file offset [%zu]\n" + "imgdiff only supports gzip kernel compression," + " did you try CONFIG_KERNEL_LZO?\n", + strm.msg, chunk_offset); + free(img); + return NULL; + } curr->len = allocated - strm.avail_out; if (strm.avail_out == 0) { allocated *= 2; diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c index 33c448762..09b0a7397 100644 --- a/applypatch/imgpatch.c +++ b/applypatch/imgpatch.c @@ -21,6 +21,7 @@ #include <sys/cdefs.h> #include <sys/stat.h> #include <errno.h> +#include <malloc.h> #include <unistd.h> #include <string.h> diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp index 7280f7480..e7aef781c 100644 --- a/asn1_decoder.cpp +++ b/asn1_decoder.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <malloc.h> #include <stdint.h> #include <string.h> diff --git a/bootloader.cpp b/bootloader.cpp index d23d1e9cd..b5b20e9c9 100644 --- a/bootloader.cpp +++ b/bootloader.cpp @@ -30,6 +30,7 @@ extern "C" { #include <string.h> #include <sys/stat.h> #include <unistd.h> +#include <stdlib.h> // fake Volume struct that allows us to use the AOSP code easily struct Volume @@ -17,6 +17,7 @@ #ifndef RECOVERY_COMMON_H #define RECOVERY_COMMON_H +#include <stdbool.h> #include <stdio.h> #include <stdarg.h> @@ -24,13 +25,7 @@ extern "C" { #endif -static long tmplog_offset = 0; - -#define ui_print(...) printf(__VA_ARGS__) -#define ui_print_overwrite(...) printf(__VA_ARGS__) - -// TODO: restore ui_print for LOGE -#define LOGE(...) printf("E:" __VA_ARGS__) +#define LOGE(...) fprintf(stdout, "E:" __VA_ARGS__) #define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__) #define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__) @@ -45,12 +40,15 @@ static long tmplog_offset = 0; #define STRINGIFY(x) #x #define EXPAND(x) STRINGIFY(x) +extern bool modified_flash; //typedef struct fstab_rec Volume; // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const char *path, const char *mode); -//void ui_print(const char* format, ...); +void ui_print(const char* format, ...); + +bool is_ro_debuggable(); #ifdef __cplusplus } diff --git a/crypto/lollipop/Android.mk b/crypto/lollipop/Android.mk index 938e0d184..16dfd28fd 100644 --- a/crypto/lollipop/Android.mk +++ b/crypto/lollipop/Android.mk @@ -15,28 +15,38 @@ ifeq ($(TARGET_HW_DISK_ENCRYPTION),true) LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION endif +ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) + LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX + LOCAL_C_INCLUDES += external/boringssl/src/include +endif + LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static include $(BUILD_SHARED_LIBRARY) -#include $(CLEAR_VARS) -#LOCAL_MODULE := twrpdec -#LOCAL_MODULE_TAGS := eng optional -#LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -#LOCAL_SRC_FILES := main.c cryptfs.c -#LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc -#LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto +include $(CLEAR_VARS) +LOCAL_MODULE := twrpdec +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := main.c cryptfs.c +LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc +LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto + +ifeq ($(TARGET_HW_DISK_ENCRYPTION),true) + LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw + LOCAL_SHARED_LIBRARIES += libcryptfs_hw + LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION +endif -#ifeq ($(TARGET_HW_DISK_ENCRYPTION),true) -# LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw -# LOCAL_SHARED_LIBRARIES += libcryptfs_hw -# LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION -#endif +ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) + LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX + LOCAL_C_INCLUDES += external/boringssl/src/include +endif -#LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static -#include $(BUILD_EXECUTABLE) +LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static +include $(BUILD_EXECUTABLE) endif diff --git a/crypto/lollipop/cryptfs.c b/crypto/lollipop/cryptfs.c index 697593fd7..87e4c989c 100644 --- a/crypto/lollipop/cryptfs.c +++ b/crypto/lollipop/cryptfs.c @@ -43,7 +43,16 @@ #include "cryptfs.h" #include "cutils/properties.h" #include "crypto_scrypt.h" + +#ifndef TW_CRYPTO_HAVE_KEYMASTERX #include <hardware/keymaster.h> +#else +#include <stdbool.h> +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <hardware/keymaster0.h> +#include <hardware/keymaster1.h> +#endif #ifndef min /* already defined by windows.h */ #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -76,6 +85,7 @@ #define RSA_KEY_SIZE 2048 #define RSA_KEY_SIZE_BYTES (RSA_KEY_SIZE / 8) #define RSA_EXPONENT 0x10001 +#define KEYMASTER_CRYPTFS_RATE_LIMIT 1 // Maximum one try per second #define RETRY_MOUNT_ATTEMPTS 10 #define RETRY_MOUNT_DELAY_SECONDS 1 @@ -97,6 +107,7 @@ void set_partition_data(const char* block_device, const char* key_location, cons strcpy(file_system, fs); } +#ifndef TW_CRYPTO_HAVE_KEYMASTERX static int keymaster_init(keymaster_device_t **keymaster_dev) { int rc; @@ -279,6 +290,308 @@ static int keymaster_sign_object(struct crypt_mnt_ftr *ftr, keymaster_close(keymaster_dev); return rc; } +#else //#ifndef TW_CRYPTO_HAVE_KEYMASTERX +static int keymaster_init(keymaster0_device_t **keymaster0_dev, + keymaster1_device_t **keymaster1_dev) +{ + int rc; + + const hw_module_t* mod; + rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod); + if (rc) { + printf("could not find any keystore module\n"); + goto err; + } + + printf("keymaster module name is %s\n", mod->name); + printf("keymaster version is %d\n", mod->module_api_version); + + *keymaster0_dev = NULL; + *keymaster1_dev = NULL; + if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) { + printf("Found keymaster1 module, using keymaster1 API.\n"); + rc = keymaster1_open(mod, keymaster1_dev); + } else { + printf("Found keymaster0 module, using keymaster0 API.\n"); + rc = keymaster0_open(mod, keymaster0_dev); + } + + if (rc) { + printf("could not open keymaster device in %s (%s)\n", + KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc)); + goto err; + } + + return 0; + +err: + *keymaster0_dev = NULL; + *keymaster1_dev = NULL; + return rc; +} + +/* Should we use keymaster? */ +static int keymaster_check_compatibility() +{ + keymaster0_device_t *keymaster0_dev = 0; + keymaster1_device_t *keymaster1_dev = 0; + int rc = 0; + + if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) { + printf("Failed to init keymaster\n"); + rc = -1; + goto out; + } + + if (keymaster1_dev) { + rc = 1; + goto out; + } + + // TODO(swillden): Check to see if there's any reason to require v0.3. I think v0.1 and v0.2 + // should work. + if (keymaster0_dev->common.module->module_api_version + < KEYMASTER_MODULE_API_VERSION_0_3) { + rc = 0; + goto out; + } + + if (!(keymaster0_dev->flags & KEYMASTER_SOFTWARE_ONLY) && + (keymaster0_dev->flags & KEYMASTER_BLOBS_ARE_STANDALONE)) { + rc = 1; + } + +out: + if (keymaster1_dev) { + keymaster1_close(keymaster1_dev); + } + if (keymaster0_dev) { + keymaster0_close(keymaster0_dev); + } + return rc; +} + +/* Create a new keymaster key and store it in this footer */ +static int keymaster_create_key(struct crypt_mnt_ftr *ftr) +{ + uint8_t* key = 0; + keymaster0_device_t *keymaster0_dev = 0; + keymaster1_device_t *keymaster1_dev = 0; + + if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) { + printf("Failed to init keymaster\n"); + return -1; + } + + int rc = 0; + size_t key_size = 0; + if (keymaster1_dev) { + keymaster_key_param_t params[] = { + /* Algorithm & size specifications. Stick with RSA for now. Switch to AES later. */ + keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA), + keymaster_param_int(KM_TAG_KEY_SIZE, RSA_KEY_SIZE), + keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, RSA_EXPONENT), + + /* The only allowed purpose for this key is signing. */ + keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN), + + /* Padding & digest specifications. */ + keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE), + keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE), + + /* Require that the key be usable in standalone mode. File system isn't available. */ + keymaster_param_enum(KM_TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE), + + /* No auth requirements, because cryptfs is not yet integrated with gatekeeper. */ + keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED), + + /* Rate-limit key usage attempts, to rate-limit brute force */ + keymaster_param_int(KM_TAG_MIN_SECONDS_BETWEEN_OPS, KEYMASTER_CRYPTFS_RATE_LIMIT), + }; + keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) }; + keymaster_key_blob_t key_blob; + keymaster_error_t error = keymaster1_dev->generate_key(keymaster1_dev, ¶m_set, + &key_blob, + NULL /* characteristics */); + if (error != KM_ERROR_OK) { + printf("Failed to generate keymaster1 key, error %d\n", error); + rc = -1; + goto out; + } + + key = (uint8_t*)key_blob.key_material; + key_size = key_blob.key_material_size; + } + else if (keymaster0_dev) { + keymaster_rsa_keygen_params_t params; + memset(¶ms, '\0', sizeof(params)); + params.public_exponent = RSA_EXPONENT; + params.modulus_size = RSA_KEY_SIZE; + + if (keymaster0_dev->generate_keypair(keymaster0_dev, TYPE_RSA, ¶ms, + &key, &key_size)) { + printf("Failed to generate keypair\n"); + rc = -1; + goto out; + } + } else { + printf("Cryptfs bug: keymaster_init succeeded but didn't initialize a device\n"); + rc = -1; + goto out; + } + + if (key_size > KEYMASTER_BLOB_SIZE) { + printf("Keymaster key too large for crypto footer\n"); + rc = -1; + goto out; + } + + memcpy(ftr->keymaster_blob, key, key_size); + ftr->keymaster_blob_size = key_size; + +out: + if (keymaster0_dev) + keymaster0_close(keymaster0_dev); + if (keymaster1_dev) + keymaster1_close(keymaster1_dev); + free(key); + return rc; +} + +/* This signs the given object using the keymaster key. */ +static int keymaster_sign_object(struct crypt_mnt_ftr *ftr, + const unsigned char *object, + const size_t object_size, + unsigned char **signature, + size_t *signature_size) +{ + int rc = 0; + keymaster0_device_t *keymaster0_dev = 0; + keymaster1_device_t *keymaster1_dev = 0; + if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) { + printf("Failed to init keymaster\n"); + rc = -1; + goto out; + } + + unsigned char to_sign[RSA_KEY_SIZE_BYTES]; + size_t to_sign_size = sizeof(to_sign); + memset(to_sign, 0, RSA_KEY_SIZE_BYTES); + + // To sign a message with RSA, the message must satisfy two + // constraints: + // + // 1. The message, when interpreted as a big-endian numeric value, must + // be strictly less than the public modulus of the RSA key. Note + // that because the most significant bit of the public modulus is + // guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit + // key), an n-bit message with most significant bit 0 always + // satisfies this requirement. + // + // 2. The message must have the same length in bits as the public + // modulus of the RSA key. This requirement isn't mathematically + // necessary, but is necessary to ensure consistency in + // implementations. + switch (ftr->kdf_type) { + case KDF_SCRYPT_KEYMASTER: + // This ensures the most significant byte of the signed message + // is zero. We could have zero-padded to the left instead, but + // this approach is slightly more robust against changes in + // object size. However, it's still broken (but not unusably + // so) because we really should be using a proper deterministic + // RSA padding function, such as PKCS1. + memcpy(to_sign + 1, object, min(RSA_KEY_SIZE_BYTES - 1, object_size)); + printf("Signing safely-padded object\n"); + break; + default: + printf("Unknown KDF type %d\n", ftr->kdf_type); + rc = -1; + goto out; + } + + if (keymaster0_dev) { + keymaster_rsa_sign_params_t params; + params.digest_type = DIGEST_NONE; + params.padding_type = PADDING_NONE; + + rc = keymaster0_dev->sign_data(keymaster0_dev, + ¶ms, + ftr->keymaster_blob, + ftr->keymaster_blob_size, + to_sign, + to_sign_size, + signature, + signature_size); + goto out; + } else if (keymaster1_dev) { + keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size }; + keymaster_key_param_t params[] = { + keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE), + keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE), + }; + keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) }; + keymaster_operation_handle_t op_handle; + keymaster_error_t error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key, + ¶m_set, NULL /* out_params */, + &op_handle); + if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) { + // Key usage has been rate-limited. Wait a bit and try again. + sleep(KEYMASTER_CRYPTFS_RATE_LIMIT); + error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key, + ¶m_set, NULL /* out_params */, + &op_handle); + } + if (error != KM_ERROR_OK) { + printf("Error starting keymaster signature transaction: %d\n", error); + rc = -1; + goto out; + } + + keymaster_blob_t input = { to_sign, to_sign_size }; + size_t input_consumed; + error = keymaster1_dev->update(keymaster1_dev, op_handle, NULL /* in_params */, + &input, &input_consumed, NULL /* out_params */, + NULL /* output */); + if (error != KM_ERROR_OK) { + printf("Error sending data to keymaster signature transaction: %d\n", error); + rc = -1; + goto out; + } + if (input_consumed != to_sign_size) { + // This should never happen. If it does, it's a bug in the keymaster implementation. + printf("Keymaster update() did not consume all data.\n"); + keymaster1_dev->abort(keymaster1_dev, op_handle); + rc = -1; + goto out; + } + + keymaster_blob_t tmp_sig; + error = keymaster1_dev->finish(keymaster1_dev, op_handle, NULL /* in_params */, + NULL /* verify signature */, NULL /* out_params */, + &tmp_sig); + if (error != KM_ERROR_OK) { + printf("Error finishing keymaster signature transaction: %d\n", error); + rc = -1; + goto out; + } + + *signature = (uint8_t*)tmp_sig.data; + *signature_size = tmp_sig.data_length; + } else { + printf("Cryptfs bug: keymaster_init succeded but didn't initialize a device.\n"); + rc = -1; + goto out; + } + + out: + if (keymaster1_dev) + keymaster1_close(keymaster1_dev); + if (keymaster0_dev) + keymaster0_close(keymaster0_dev); + + return rc; +} +#endif //#ifndef TW_CRYPTO_HAVE_KEYMASTERX /* Store password when userdata is successfully decrypted and mounted. * Cleared by cryptfs_clear_password @@ -352,7 +665,7 @@ static void get_device_scrypt_params(struct crypt_mnt_ftr *ftr) { * taken. */ if ((i != 3) || (token != NULL)) { - printf("bad scrypt parameters '%s' should be like '12:8:1'; using defaults", paramstr); + printf("bad scrypt parameters '%s' should be like '12:8:1'; using defaults\n", paramstr); memcpy(params, default_params, sizeof(params)); } } @@ -526,13 +839,13 @@ static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii, size_t size = strlen (master_key_ascii); if (size % 2) { - printf("Trying to convert ascii string of odd length"); + printf("Trying to convert ascii string of odd length\n"); return NULL; } unsigned char* master_key = (unsigned char*) malloc(size / 2); if (master_key == 0) { - printf("Cannot allocate"); + printf("Cannot allocate\n"); return NULL; } @@ -541,7 +854,7 @@ static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii, int low_nibble = hexdigit (master_key_ascii[i + 1]); if(high_nibble < 0 || low_nibble < 0) { - printf("Invalid hex string"); + printf("Invalid hex string\n"); free (master_key); return NULL; } @@ -818,7 +1131,7 @@ errout: static int pbkdf2(const char *passwd, const unsigned char *salt, unsigned char *ikey, void *params UNUSED) { - printf("Using pbkdf2 for cryptfs KDF"); + printf("Using pbkdf2 for cryptfs KDF\n"); /* Turn the password into a key and IV that can decrypt the master key */ unsigned int keysize; @@ -922,25 +1235,25 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt, case KDF_SCRYPT_KEYMASTER_BADLY_PADDED: case KDF_SCRYPT_KEYMASTER: if (keymaster_create_key(crypt_ftr)) { - printf("keymaster_create_key failed"); + printf("keymaster_create_key failed\n"); return -1; } if (scrypt_keymaster(passwd, salt, ikey, crypt_ftr)) { - printf("scrypt failed"); + printf("scrypt failed\n"); return -1; } break; case KDF_SCRYPT: if (scrypt(passwd, salt, ikey, crypt_ftr)) { - printf("scrypt failed"); + printf("scrypt failed\n"); return -1; } break; default: - printf("Invalid kdf_type"); + printf("Invalid kdf_type\n"); return -1; } @@ -957,7 +1270,11 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt, printf("EVP_EncryptUpdate failed\n"); return -1; } +#ifndef TW_CRYPTO_HAVE_KEYMASTERX if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) { +#else + if (! EVP_EncryptFinal_ex(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) { +#endif printf("EVP_EncryptFinal failed\n"); return -1; } @@ -982,7 +1299,7 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt, sizeof(crypt_ftr->scrypted_intermediate_key)); if (rc) { - printf("encrypt_master_key: crypto_scrypt failed"); + printf("encrypt_master_key: crypto_scrypt failed\n"); } return 0; @@ -1002,7 +1319,7 @@ static int decrypt_master_key_aux(char *passwd, unsigned char *salt, /* Turn the password into an intermediate key and IV that can decrypt the master key */ if (kdf(passwd, salt, ikey, kdf_params)) { - printf("kdf failed"); + printf("kdf failed\n"); return -1; } @@ -1016,7 +1333,11 @@ static int decrypt_master_key_aux(char *passwd, unsigned char *salt, encrypted_master_key, KEY_LEN_BYTES)) { return -1; } +#ifndef TW_CRYPTO_HAVE_KEYMASTERX if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) { +#else + if (! EVP_DecryptFinal_ex(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) { +#endif return -1; } @@ -1066,7 +1387,7 @@ static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key, decrypted_master_key, kdf, kdf_params, intermediate_key, intermediate_key_size); if (ret != 0) { - printf("failure decrypting master key"); + printf("failure decrypting master key\n"); } return ret; @@ -1396,7 +1717,7 @@ int cryptfs_check_passwd(char *passwd) // cryptfs_changepw also adjusts so pass original // Note that adjust_passwd only recognises patterns // so we can safely use CRYPT_TYPE_PATTERN - printf("TWRP NOT Updating pattern to new format"); + printf("TWRP NOT Updating pattern to new format\n"); //cryptfs_changepw(CRYPT_TYPE_PATTERN, passwd); } else if (hex_passwd) { //printf("trying hex_passwd '%s'\n", hex_passwd); @@ -1445,17 +1766,17 @@ int cryptfs_verify_passwd(char *passwd) property_get("ro.crypto.state", encrypted_state, ""); if (strcmp(encrypted_state, "encrypted") ) { - printf("device not encrypted, aborting"); + printf("device not encrypted, aborting\n"); return -2; } if (!master_key_saved) { - printf("encrypted fs not yet mounted, aborting"); + printf("encrypted fs not yet mounted, aborting\n"); return -1; } if (!saved_mount_point) { - printf("encrypted fs failed to save mount point, aborting"); + printf("encrypted fs failed to save mount point, aborting\n"); return -1; } @@ -1515,7 +1836,7 @@ static int cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr *ftr) break; default: - printf("keymaster_check_compatibility failed"); + printf("keymaster_check_compatibility failed\n"); return -1; } diff --git a/crypto/lollipop/main.c b/crypto/lollipop/main.c index 4edec861b..232afb959 100644 --- a/crypto/lollipop/main.c +++ b/crypto/lollipop/main.c @@ -23,13 +23,10 @@ #include "cryptfs.h" #include "cutils/properties.h" #include "crypto_scrypt.h" -#include <hardware/keymaster.h> int main() { - printf("blah\n"); - set_partition_data("/dev/block/mmcblk0p28", "/dev/block/mmcblk0p27", "ext4"); - printf("blah2\n"); - int ret = cryptfs_check_passwd("30303030"); - //int ret = cryptfs_check_passwd("0000"); + set_partition_data("/dev/block/platform/sdhci-tegra.3/by-name/UDA", "/dev/block/platform/sdhci-tegra.3/by-name/MD1", "f2fs"); + //int ret = cryptfs_check_passwd("30303030"); + int ret = cryptfs_check_passwd("0000"); return 0; } diff --git a/crypto/scrypt/Scrypt.mk b/crypto/scrypt/Scrypt.mk index d2a2fa413..baa41eca6 100644 --- a/crypto/scrypt/Scrypt.mk +++ b/crypto/scrypt/Scrypt.mk @@ -1,6 +1,6 @@ local_c_flags := -DUSE_OPENSSL_PBKDF2 -local_c_includes := $(log_c_includes) external/openssl/include +local_c_includes := $(log_c_includes) external/openssl/include external/boringssl/src/include local_additional_dependencies := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Scrypt.mk @@ -38,6 +38,7 @@ #include <map> #include <fstream> #include <sstream> +#include <pthread.h> #include "variables.h" #include "data.hpp" @@ -76,7 +77,11 @@ int DataManager::mInitialized = 0; extern bool datamedia; +#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP pthread_mutex_t DataManager::m_valuesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; +#else +pthread_mutex_t DataManager::m_valuesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +#endif // Device ID functions void DataManager::sanitize_device_id(char* device_id) { @@ -22,6 +22,7 @@ #include <string> #include <utility> #include <map> +#include <pthread.h> using namespace std; diff --git a/default_device.cpp b/default_device.cpp index 97806ac58..a9718668d 100644 --- a/default_device.cpp +++ b/default_device.cpp @@ -14,74 +14,9 @@ * limitations under the License. */ -#include <linux/input.h> - -#include "common.h" #include "device.h" #include "screen_ui.h" -static const char* HEADERS[] = { "Volume up/down to move highlight;", - "enter button to select.", - "", - NULL }; - -static const char* ITEMS[] = {"reboot system now", - "apply update from ADB", - "wipe data/factory reset", - "wipe cache partition", - "reboot to bootloader", - "power down", - "view recovery logs", - NULL }; - -class DefaultDevice : public Device { - public: - DefaultDevice() : - ui(new ScreenRecoveryUI) { - } - - RecoveryUI* GetUI() { return ui; } - - int HandleMenuKey(int key, int visible) { - if (visible) { - switch (key) { - case KEY_DOWN: - case KEY_VOLUMEDOWN: - return kHighlightDown; - - case KEY_UP: - case KEY_VOLUMEUP: - return kHighlightUp; - - case KEY_ENTER: - case KEY_POWER: - return kInvokeItem; - } - } - - return kNoAction; - } - - BuiltinAction InvokeMenuItem(int menu_position) { - switch (menu_position) { - case 0: return REBOOT; - case 1: return APPLY_ADB_SIDELOAD; - case 2: return WIPE_DATA; - case 3: return WIPE_CACHE; - case 4: return REBOOT_BOOTLOADER; - case 5: return SHUTDOWN; - case 6: return READ_RECOVERY_LASTLOG; - default: return NO_ACTION; - } - } - - const char* const* GetMenuHeaders() { return HEADERS; } - const char* const* GetMenuItems() { return ITEMS; } - - private: - RecoveryUI* ui; -}; - Device* make_device() { - return new DefaultDevice(); + return new Device(new ScreenRecoveryUI); } diff --git a/device.cpp b/device.cpp new file mode 100644 index 000000000..fd1a9875b --- /dev/null +++ b/device.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device.h" + +static const char* MENU_ITEMS[] = { + "Reboot system now", + "Reboot to bootloader", + "Apply update from ADB", + "Apply update from SD card", + "Wipe data/factory reset", + "Wipe cache partition", + "Mount /system", + "View recovery logs", + "Power off", + NULL +}; + +const char* const* Device::GetMenuItems() { + return MENU_ITEMS; +} + +Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { + switch (menu_position) { + case 0: return REBOOT; + case 1: return REBOOT_BOOTLOADER; + case 2: return APPLY_ADB_SIDELOAD; + case 3: return APPLY_SDCARD; + case 4: return WIPE_DATA; + case 5: return WIPE_CACHE; + case 6: return MOUNT_SYSTEM; + case 7: return VIEW_RECOVERY_LOGS; + case 8: return SHUTDOWN; + default: return NO_ACTION; + } +} + +int Device::HandleMenuKey(int key, int visible) { + if (!visible) { + return kNoAction; + } + + switch (key) { + case KEY_DOWN: + case KEY_VOLUMEDOWN: + return kHighlightDown; + + case KEY_UP: + case KEY_VOLUMEUP: + return kHighlightUp; + + case KEY_ENTER: + case KEY_POWER: + return kInvokeItem; + + default: + // If you have all of the above buttons, any other buttons + // are ignored. Otherwise, any button cycles the highlight. + return ui_->HasThreeButtons() ? kNoAction : kHighlightDown; + } +} @@ -21,29 +21,20 @@ class Device { public: + Device(RecoveryUI* ui) : ui_(ui) { } virtual ~Device() { } // Called to obtain the UI object that should be used to display // the recovery user interface for this device. You should not // have called Init() on the UI object already, the caller will do // that after this method returns. - virtual RecoveryUI* GetUI() = 0; + virtual RecoveryUI* GetUI() { return ui_; } // Called when recovery starts up (after the UI has been obtained // and initialized and after the arguments have been parsed, but // before anything else). virtual void StartRecovery() { }; - // enum KeyAction { NONE, TOGGLE, REBOOT }; - - // // Called in the input thread when a new key (key_code) is - // // pressed. *key_pressed is an array of KEY_MAX+1 bytes - // // indicating which other keys are already pressed. Return a - // // KeyAction to indicate action should be taken immediately. - // // These actions happen when recovery is not waiting for input - // // (eg, in the midst of installing a package). - // virtual KeyAction CheckImmediateKeyAction(volatile char* key_pressed, int key_code) = 0; - // Called from the main thread when recovery is at the main menu // and waiting for input, and a key is pressed. (Note that "at" // the main menu does not necessarily mean the menu is visible; @@ -63,12 +54,26 @@ class Device { // - invoke the highlighted item (kInvokeItem) // - do nothing (kNoAction) // - invoke a specific action (a menu position: any non-negative number) - virtual int HandleMenuKey(int key, int visible) = 0; + virtual int HandleMenuKey(int key, int visible); + + enum BuiltinAction { + NO_ACTION = 0, + REBOOT = 1, + APPLY_SDCARD = 2, + // APPLY_CACHE was 3. + APPLY_ADB_SIDELOAD = 4, + WIPE_DATA = 5, + WIPE_CACHE = 6, + REBOOT_BOOTLOADER = 7, + SHUTDOWN = 8, + VIEW_RECOVERY_LOGS = 9, + MOUNT_SYSTEM = 10, + }; - enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, - APPLY_CACHE, // APPLY_CACHE is deprecated; has no effect - APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE, - REBOOT_BOOTLOADER, SHUTDOWN, READ_RECOVERY_LASTLOG }; + // Return the list of menu items (an array of strings, + // NULL-terminated). The menu_position passed to InvokeMenuItem + // will correspond to the indexes into this array. + virtual const char* const* GetMenuItems(); // Perform a recovery action selected from the menu. // 'menu_position' will be the item number of the selected menu @@ -79,31 +84,26 @@ class Device { // builtin actions, you can just return the corresponding enum // value. If it is an action specific to your device, you // actually perform it here and return NO_ACTION. - virtual BuiltinAction InvokeMenuItem(int menu_position) = 0; + virtual BuiltinAction InvokeMenuItem(int menu_position); static const int kNoAction = -1; static const int kHighlightUp = -2; static const int kHighlightDown = -3; static const int kInvokeItem = -4; - // Called when we do a wipe data/factory reset operation (either via a - // reboot from the main system with the --wipe_data flag, or when the - // user boots into recovery manually and selects the option from the - // menu.) Can perform whatever device-specific wiping actions are - // needed. Return 0 on success. The userdata and cache partitions - // are erased AFTER this returns (whether it returns success or not). - virtual int WipeData() { return 0; } - - // Return the headers (an array of strings, one per line, - // NULL-terminated) for the main menu. Typically these tell users - // what to push to move the selection and invoke the selected - // item. - virtual const char* const* GetMenuHeaders() = 0; - - // Return the list of menu items (an array of strings, - // NULL-terminated). The menu_position passed to InvokeMenuItem - // will correspond to the indexes into this array. - virtual const char* const* GetMenuItems() = 0; + // Called before and after we do a wipe data/factory reset operation, + // either via a reboot from the main system with the --wipe_data flag, + // or when the user boots into recovery image manually and selects the + // option from the menu, to perform whatever device-specific wiping + // actions are needed. + // Return true on success; returning false from PreWipeData will prevent + // the regular wipe, and returning false from PostWipeData will cause + // the wipe to be considered a failure. + virtual bool PreWipeData() { return true; } + virtual bool PostWipeData() { return true; } + + private: + RecoveryUI* ui_; }; // The device-specific library must define this function (or the diff --git a/digest/md5.h b/digest/md5.h index d997e379d..cf306881f 100644 --- a/digest/md5.h +++ b/digest/md5.h @@ -24,6 +24,6 @@ void MD5Transform(uint32_t buf[4], uint32_t const in[16]); /* * This is needed to make RSAREF happy on some MS-DOS compilers. */ -typedef struct MD5Context MD5_CTX; +//typedef struct MD5Context MD5_CTX; #endif /* !MD5_H */ diff --git a/edify/Android.mk b/edify/Android.mk index 61ed6fa17..03c04e432 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -7,9 +7,10 @@ edify_src_files := \ parser.y \ expr.c -# "-x c" forces the lex/yacc files to be compiled as c; -# the build system otherwise forces them to be c++. -edify_cflags := -x c +# "-x c" forces the lex/yacc files to be compiled as c the build system +# otherwise forces them to be c++. Need to also add an explicit -std because the +# build system will soon default C++ to -std=c++11. +edify_cflags := -x c -std=gnu89 # # Build the host-side command line tool diff --git a/edify/main.c b/edify/main.c index b3fad53b8..b1baa0b13 100644 --- a/edify/main.c +++ b/edify/main.c @@ -25,13 +25,12 @@ extern int yyparse(Expr** root, int* error_count); int expect(const char* expr_str, const char* expected, int* errors) { Expr* e; - int error; char* result; printf("."); int error_count = parse_string(expr_str, &e, &error_count); - if (error > 0 || error_count > 0) { + if (error_count > 0) { printf("error parsing \"%s\" (%d errors)\n", expr_str, error_count); ++*errors; diff --git a/etc/init.rc b/etc/init.rc index 4e7b84506..3cf5d4c1d 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -2,17 +2,10 @@ import /init.recovery.usb.rc import /init.recovery.${ro.hardware}.rc on early-init - # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. - write /sys/fs/selinux/checkreqprot 0 - - # Set the security context for the init process. - # This should occur before anything else (e.g. ueventd) is started. - setcon u:r:init:s0 - start ueventd start healthd -service set_permissive /sbin/toolbox setenforce 0 +service set_permissive /sbin/permissive.sh oneshot seclabel u:r:recovery:s0 diff --git a/find_file.cpp b/find_file.cpp index 8ae0a51b4..78db5343d 100644 --- a/find_file.cpp +++ b/find_file.cpp @@ -19,6 +19,7 @@ #include <string> #include <vector> #include <dirent.h> +#include <stdlib.h> #include "find_file.hpp" #include "twrp-functions.hpp" #include "twcommon.h" diff --git a/flashutils/flashutils.c b/flashutils/flashutils.c index de45e87b8..fe1181fcd 100644 --- a/flashutils/flashutils.c +++ b/flashutils/flashutils.c @@ -3,6 +3,7 @@ #include <unistd.h> #include <sys/wait.h> #include <stdio.h> +#include <string.h> #include "flashutils/flashutils.h" diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.c index 19fb52df0..4565c7b5b 100644 --- a/fuse_sdcard_provider.c +++ b/fuse_sdcard_provider.c @@ -16,6 +16,7 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <errno.h> #include <pthread.h> #include <sys/mount.h> @@ -35,19 +36,17 @@ struct file_data { static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { struct file_data* fd = (struct file_data*)cookie; - if (lseek(fd->fd, block * fd->block_size, SEEK_SET) < 0) { - printf("seek on sdcard failed: %s\n", strerror(errno)); + off64_t offset = ((off64_t) block) * fd->block_size; + if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) { + fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno)); return -EIO; } while (fetch_size > 0) { - ssize_t r = read(fd->fd, buffer, fetch_size); - if (r < 0) { - if (r != -EINTR) { - printf("read on sdcard failed: %s\n", strerror(errno)); - return -EIO; - } - r = 0; + ssize_t r = TEMP_FAILURE_RETRY(read(fd->fd, buffer, fetch_size)); + if (r == -1) { + fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno)); + return -EIO; } fetch_size -= r; buffer += r; diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h index dc2982ca0..dbfbcd521 100644 --- a/fuse_sdcard_provider.h +++ b/fuse_sdcard_provider.h @@ -17,7 +17,13 @@ #ifndef __FUSE_SDCARD_PROVIDER_H #define __FUSE_SDCARD_PROVIDER_H +#include <sys/cdefs.h> + +__BEGIN_DECLS + void* start_sdcard_fuse(const char* path); void finish_sdcard_fuse(void* token); +__END_DECLS + #endif diff --git a/fuse_sideload.c b/fuse_sideload.c index f5f20ea47..3259c5fb4 100644 --- a/fuse_sideload.c +++ b/fuse_sideload.c @@ -53,6 +53,7 @@ #include <string.h> #include <sys/inotify.h> #include <sys/mount.h> +#include <sys/param.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/statfs.h> @@ -105,27 +106,52 @@ static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, siz vec[0].iov_base = &hdr; vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = data; + vec[1].iov_base = /* const_cast */(void*)(data); vec[1].iov_len = len; res = writev(fd->ffd, vec, 2); if (res < 0) { - printf("*** REPLY FAILED *** %d\n", errno); + printf("*** REPLY FAILED *** %s\n", strerror(errno)); } } static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { const struct fuse_init_in* req = data; struct fuse_init_out out; + size_t fuse_struct_size; + + + /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out + * defined (fuse version 7.6). The structure is the same from 7.6 through + * 7.22. Beginning with 7.23, the structure increased in size and added + * new parameters. + */ + if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) { + printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", + req->major, req->minor, FUSE_KERNEL_VERSION); + return -1; + } + + out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); + fuse_struct_size = sizeof(out); +#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) + /* FUSE_KERNEL_VERSION >= 23. */ + + /* If the kernel only works on minor revs older than or equal to 22, + * then use the older structure size since this code only uses the 7.22 + * version of the structure. */ + if (req->minor <= 22) { + fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE; + } +#endif out.major = FUSE_KERNEL_VERSION; - out.minor = FUSE_KERNEL_MINOR_VERSION; out.max_readahead = req->max_readahead; out.flags = 0; out.max_background = 32; out.congestion_threshold = 32; out.max_write = 4096; - fuse_reply(fd, hdr->unique, &out, sizeof(out)); + fuse_reply(fd, hdr->unique, &out, fuse_struct_size); return NO_STATUS; } @@ -404,7 +430,7 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, char opts[256]; snprintf(opts, sizeof(opts), - ("fd=%d,user_id=%d,group_id=%d,max_read=%zu," + ("fd=%d,user_id=%d,group_id=%d,max_read=%u," "allow_other,rootmode=040000"), fd.ffd, fd.uid, fd.gid, block_size); @@ -416,14 +442,12 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, } uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; for (;;) { - ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer)); - if (len < 0) { - if (errno != EINTR) { - perror("read request"); - if (errno == ENODEV) { - result = -1; - break; - } + ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer))); + if (len == -1) { + perror("read request"); + if (errno == ENODEV) { + result = -1; + break; } continue; } @@ -482,7 +506,7 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, outhdr.len = sizeof(outhdr); outhdr.error = result; outhdr.unique = hdr->unique; - write(fd.ffd, &outhdr, sizeof(outhdr)); + TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr))); } } diff --git a/fuse_sideload.h b/fuse_sideload.h index c0b16efbe..f9e3bf0d3 100644 --- a/fuse_sideload.h +++ b/fuse_sideload.h @@ -17,6 +17,10 @@ #ifndef __FUSE_SIDELOAD_H #define __FUSE_SIDELOAD_H +#include <sys/cdefs.h> + +__BEGIN_DECLS + // define the filenames created by the sideload FUSE filesystem #define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload" #define FUSE_SIDELOAD_HOST_FILENAME "package.zip" @@ -35,4 +39,6 @@ struct provider_vtab { int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size, uint32_t block_size); +__END_DECLS + #endif diff --git a/gui/Android.mk b/gui/Android.mk index 17718124d..1fef03d41 100644 --- a/gui/Android.mk +++ b/gui/Android.mk @@ -71,7 +71,7 @@ ifeq ($(TW_ROUND_SCREEN), true) LOCAL_CFLAGS += -DTW_ROUND_SCREEN endif -LOCAL_C_INCLUDES += bionic external/stlport/stlport +LOCAL_C_INCLUDES += bionic external/stlport/stlport system/core/libpixelflinger/include LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\" include $(BUILD_STATIC_LIBRARY) diff --git a/gui/action.cpp b/gui/action.cpp index b74584598..0f8c7eea6 100644 --- a/gui/action.cpp +++ b/gui/action.cpp @@ -49,7 +49,6 @@ extern "C" { #include "../variables.h" #include "../twinstall.h" #include "cutils/properties.h" -#include "../minadbd/adb.h" #include "../adb_install.h" #include "../set_metadata.h" }; diff --git a/install.cpp b/install.cpp index 8e3d6d6d9..b46bbdf9d 100644 --- a/install.cpp +++ b/install.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <fcntl.h> #include <limits.h> +#include <string.h> #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> @@ -47,7 +48,7 @@ static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; // 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) { +try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { @@ -87,7 +88,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { // 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 + // progress of this segment of the bar. // // set_progress <frac> // <frac> should be between 0.0 and 1.0; sets the @@ -106,6 +107,18 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { // ui_print <string> // display <string> on the screen. // + // wipe_cache + // a wipe of cache will be performed following a successful + // installation. + // + // clear_display + // turn off the text display. + // + // enable_reboot + // packages can explicitly request that they want the user + // to be able to reboot during installation (useful for + // debugging packages that don't exit). + // // - the name of the package zip file. // @@ -128,7 +141,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { } close(pipefd[1]); - *wipe_cache = 0; + *wipe_cache = false; char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); @@ -157,7 +170,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { } fflush(stdout); } else if (strcmp(command, "wipe_cache") == 0) { - *wipe_cache = 1; + *wipe_cache = true; } else if (strcmp(command, "clear_display") == 0) { ui->SetBackground(RecoveryUI::NONE); } else if (strcmp(command, "enable_reboot") == 0) { @@ -182,7 +195,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { } static int -really_install_package(const char *path, int* wipe_cache, bool needs_mount) +really_install_package(const char *path, bool* wipe_cache, bool needs_mount) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); @@ -252,9 +265,11 @@ really_install_package(const char *path, int* wipe_cache, bool needs_mount) } int -install_package(const char* path, int* wipe_cache, const char* install_file, +install_package(const char* path, bool* wipe_cache, const char* install_file, bool needs_mount) { + modified_flash = true; + FILE* install_log = fopen_path(install_file, "w"); if (install_log) { fputs(path, install_log); @@ -28,7 +28,7 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE }; // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. -int install_package(const char *root_path, int* wipe_cache, +int install_package(const char* root_path, bool* wipe_cache, const char* install_file, bool needs_mount); RSAPublicKey* load_keys(const char* filename, int* numKeys); diff --git a/libblkid/include/all-io.h b/libblkid/include/all-io.h index 1fad66e53..9a4aeba3b 100644 --- a/libblkid/include/all-io.h +++ b/libblkid/include/all-io.h @@ -49,7 +49,7 @@ static inline int fwrite_all(const void *ptr, size_t size, } else if (errno != EINTR && errno != EAGAIN) return -1; if (errno == EAGAIN) /* Try later, *sigh* */ - xusleep(250000); + usleep(250000); } return 0; } diff --git a/libblkid/lib/exec_shell.c b/libblkid/lib/exec_shell.c index 2b263644d..2b723acb4 100644 --- a/libblkid/lib/exec_shell.c +++ b/libblkid/lib/exec_shell.c @@ -20,6 +20,7 @@ #include <string.h> #include <unistd.h> #include <sys/types.h> +#include <libgen.h> #include "nls.h" #include "c.h" diff --git a/libblkid/lib/fileutils.c b/libblkid/lib/fileutils.c index 4e884d39a..c6eb0d6c9 100644 --- a/libblkid/lib/fileutils.c +++ b/libblkid/lib/fileutils.c @@ -14,6 +14,10 @@ #include "fileutils.h" #include "pathnames.h" +#ifndef _PATH_TMP +#define _PATH_TMP "/tmp/" +#endif + /* Create open temporary file in safe way. Please notice that the * file permissions are -rw------- by default. */ int xmkstemp(char **tmpname, char *dir) diff --git a/libblkid/lib/sysfs.c b/libblkid/lib/sysfs.c index 63a90dcbc..8070750fa 100644 --- a/libblkid/lib/sysfs.c +++ b/libblkid/lib/sysfs.c @@ -5,6 +5,8 @@ * Written by Karel Zak <kzak@redhat.com> */ #include <ctype.h> +#include <string.h> +#include <libgen.h> #include "c.h" #include "at.h" @@ -13,6 +15,9 @@ #include "fileutils.h" #include "all-io.h" +#define STRINGIFY(x) #x +#define EXPAND(x) STRINGIFY(x) + char *sysfs_devno_attribute_path(dev_t devno, char *buf, size_t bufsiz, const char *attr) { @@ -436,7 +441,7 @@ int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str) int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num) { - char buf[sizeof(stringify_value(ULLONG_MAX))]; + char buf[sizeof(STRINGIFY(ULLONG_MAX))]; int fd, rc = 0, len, errsv; fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC); diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk new file mode 100644 index 000000000..d464c7707 --- /dev/null +++ b/libpixelflinger/Android.mk @@ -0,0 +1,102 @@ +LOCAL_PATH:= system/core/libpixelflinger +include $(CLEAR_VARS) + +# +# C/C++ and ARMv5 objects +# + +include $(CLEAR_VARS) + +ifneq ($(wildcard system/core/libpixelflinger/codeflinger/x86/X86Assembler.cpp),) + ifeq ($(TARGET_ARCH),x86) + TW_HAVE_X86_ACCELERATED_PIXELFLINGER := true + endif +endif + +PIXELFLINGER_SRC_FILES += \ + codeflinger/CodeCache.cpp \ + codeflinger/tinyutils/SharedBuffer.cpp \ + codeflinger/tinyutils/VectorImpl.cpp \ + format.cpp \ + clear.cpp \ + raster.cpp \ + buffer.cpp + +ifneq ($(TW_HAVE_X86_ACCELERATED_PIXELFLINGER),true) +PIXELFLINGER_SRC_FILES += \ + codeflinger/ARMAssemblerInterface.cpp \ + codeflinger/ARMAssemblerProxy.cpp \ + codeflinger/GGLAssembler.cpp \ + codeflinger/load_store.cpp \ + codeflinger/blending.cpp \ + codeflinger/texturing.cpp \ + fixed.cpp.arm \ + picker.cpp.arm \ + pixelflinger.cpp.arm \ + trap.cpp.arm \ + scanline.cpp.arm +else +PIXELFLINGER_SRC_FILES_x86 := \ + codeflinger/x86/X86Assembler.cpp \ + codeflinger/x86/GGLX86Assembler.cpp \ + codeflinger/x86/load_store.cpp \ + codeflinger/x86/blending.cpp \ + codeflinger/x86/texturing.cpp \ + fixed.cpp \ + picker.cpp \ + pixelflinger.cpp \ + trap.cpp \ + scanline.cpp + +PIXELFLINGER_C_INCLUDES_x86 := \ + external/libenc + +endif + +PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer + +PIXELFLINGER_SRC_FILES_arm := \ + codeflinger/ARMAssembler.cpp \ + codeflinger/disassem.c \ + col32cb16blend.S \ + t32cb16blend.S \ + +ifeq ($(ARCH_ARM_HAVE_NEON),true) +PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S +PIXELFLINGER_CFLAGS_arm += -D__ARM_HAVE_NEON +endif + +PIXELFLINGER_SRC_FILES_arm64 := \ + codeflinger/Arm64Assembler.cpp \ + codeflinger/Arm64Disassembler.cpp \ + arch-arm64/col32cb16blend.S \ + arch-arm64/t32cb16blend.S \ + +ifndef ARCH_MIPS_REV6 +PIXELFLINGER_SRC_FILES_mips := \ + codeflinger/MIPSAssembler.cpp \ + codeflinger/mips_disassem.c \ + arch-mips/t32cb16blend.S \ + +endif + +# +# Static library version +# + +include $(CLEAR_VARS) +LOCAL_MODULE:= libpixelflinger_twrp +LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) +LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm) +LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64) +LOCAL_SRC_FILES_x86 := $(PIXELFLINGER_SRC_FILES_x86) +LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) +LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) +LOCAL_C_INCLUDES_x86 := $(PIXELFLINGER_C_INCLUDES_x86) +ifeq ($(TW_HAVE_X86_ACCELERATED_PIXELFLINGER),true) +LOCAL_WHOLE_STATIC_LIBRARIES += libenc +LOCAL_C_INCLUDES += external/libenc +endif +include $(BUILD_STATIC_LIBRARY) diff --git a/libtar/extract.c b/libtar/extract.c index 4532b7640..69e08bd54 100644 --- a/libtar/extract.c +++ b/libtar/extract.c @@ -18,6 +18,7 @@ #include <fcntl.h> #include <errno.h> #include <utime.h> +#include <string.h> #define DEBUG #ifdef STDC_HEADERS diff --git a/minadbd.old/Android.mk b/minadbd.old/Android.mk new file mode 100644 index 000000000..19c8b6047 --- /dev/null +++ b/minadbd.old/Android.mk @@ -0,0 +1,30 @@ +# Copyright 2005 The Android Open Source Project +# +# Android.mk for adb +# + +LOCAL_PATH:= $(call my-dir) + +# minadbd library +# ========================================================= + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + adb.c \ + fdevent.c \ + fuse_adb_provider.c \ + transport.c \ + transport_usb.c \ + sockets.c \ + services.c \ + usb_linux_client.c \ + utils.c + +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := libminadbd +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../ +LOCAL_SHARED_LIBRARIES := libfusesideload libcutils libc +include $(BUILD_SHARED_LIBRARY) diff --git a/minadbd.old/README.txt b/minadbd.old/README.txt new file mode 100644 index 000000000..c9df484c3 --- /dev/null +++ b/minadbd.old/README.txt @@ -0,0 +1,39 @@ +The contents of this directory are copied from system/core/adb, with +the following changes: + +adb.c + - much support for host mode and non-linux OS's stripped out; this + version only runs as adbd on the device. + - always setuid/setgid's itself to the shell user + - only uses USB transport + - references to JDWP removed + - main() removed + - all ADB_HOST and win32 code removed + - removed listeners, logging code, background server (for host) + +adb.h + - minor changes to match adb.c changes + +sockets.c + - references to JDWP removed + - ADB_HOST code removed + +services.c + - all services except echo_service (which is commented out) removed + - all host mode support removed + - sideload_service() added; this is the only service supported. It + receives a single blob of data, writes it to a fixed filename, and + makes the process exit. + +Android.mk + - only builds in adbd mode; builds as static library instead of a + standalone executable. + +sysdeps.h + - changes adb_creat() to use O_NOFOLLOW + +transport.c + - removed ADB_HOST code + +transport_usb.c + - removed ADB_HOST code diff --git a/minadbd/adb.c b/minadbd.old/adb.c index c35e8300b..c35e8300b 100644 --- a/minadbd/adb.c +++ b/minadbd.old/adb.c diff --git a/minadbd/adb.h b/minadbd.old/adb.h index 08ee989d6..08ee989d6 100644 --- a/minadbd/adb.h +++ b/minadbd.old/adb.h diff --git a/minadbd/fdevent.c b/minadbd.old/fdevent.c index 5c374a71b..5c374a71b 100644 --- a/minadbd/fdevent.c +++ b/minadbd.old/fdevent.c diff --git a/minadbd/fdevent.h b/minadbd.old/fdevent.h index a0ebe2a7e..a0ebe2a7e 100644 --- a/minadbd/fdevent.h +++ b/minadbd.old/fdevent.h diff --git a/minadbd/fuse_adb_provider.c b/minadbd.old/fuse_adb_provider.c index f80533a8c..f80533a8c 100644 --- a/minadbd/fuse_adb_provider.c +++ b/minadbd.old/fuse_adb_provider.c diff --git a/minadbd.old/fuse_adb_provider.h b/minadbd.old/fuse_adb_provider.h new file mode 100644 index 000000000..0eb1f79d1 --- /dev/null +++ b/minadbd.old/fuse_adb_provider.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FUSE_ADB_PROVIDER_H +#define __FUSE_ADB_PROVIDER_H + +int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size); + +#endif diff --git a/minadbd/mutex_list.h b/minadbd.old/mutex_list.h index 652dd7341..652dd7341 100644 --- a/minadbd/mutex_list.h +++ b/minadbd.old/mutex_list.h diff --git a/minadbd/services.c b/minadbd.old/services.c index 218b84a38..218b84a38 100644 --- a/minadbd/services.c +++ b/minadbd.old/services.c diff --git a/minadbd/sockets.c b/minadbd.old/sockets.c index 817410d13..817410d13 100644 --- a/minadbd/sockets.c +++ b/minadbd.old/sockets.c diff --git a/minadbd/sysdeps.h b/minadbd.old/sysdeps.h index 800ddb753..800ddb753 100644 --- a/minadbd/sysdeps.h +++ b/minadbd.old/sysdeps.h diff --git a/minadbd/transport.c b/minadbd.old/transport.c index 92679f518..92679f518 100644 --- a/minadbd/transport.c +++ b/minadbd.old/transport.c diff --git a/minadbd/transport.h b/minadbd.old/transport.h index 992e05285..992e05285 100644 --- a/minadbd/transport.h +++ b/minadbd.old/transport.h diff --git a/minadbd/transport_usb.c b/minadbd.old/transport_usb.c index 91cbf6151..91cbf6151 100644 --- a/minadbd/transport_usb.c +++ b/minadbd.old/transport_usb.c diff --git a/minadbd/usb_linux_client.c b/minadbd.old/usb_linux_client.c index ec32bcf91..ec32bcf91 100644 --- a/minadbd/usb_linux_client.c +++ b/minadbd.old/usb_linux_client.c diff --git a/minadbd/utils.c b/minadbd.old/utils.c index 91518bab6..91518bab6 100644 --- a/minadbd/utils.c +++ b/minadbd.old/utils.c diff --git a/minadbd/utils.h b/minadbd.old/utils.h index f70ecd24d..f70ecd24d 100644 --- a/minadbd/utils.h +++ b/minadbd.old/utils.h diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 19c8b6047..36e14a51e 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -1,30 +1,38 @@ # Copyright 2005 The Android Open Source Project -# -# Android.mk for adb -# LOCAL_PATH:= $(call my-dir) -# minadbd library -# ========================================================= +minadbd_cflags := \ + -Wall -Werror \ + -Wno-unused-parameter \ + -Wno-missing-field-initializers \ + -DADB_HOST=0 \ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - adb.c \ - fdevent.c \ - fuse_adb_provider.c \ - transport.c \ - transport_usb.c \ - sockets.c \ - services.c \ - usb_linux_client.c \ - utils.c - -LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter -LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -LOCAL_MODULE_TAGS := eng + adb_main.cpp \ + fuse_adb_provider.cpp \ + services.cpp \ + ../fuse_sideload.c + LOCAL_MODULE := libminadbd -LOCAL_C_INCLUDES += $(LOCAL_PATH)/../ -LOCAL_SHARED_LIBRARIES := libfusesideload libcutils libc +LOCAL_CFLAGS := $(minadbd_cflags) +LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. system/core/adb +LOCAL_WHOLE_STATIC_LIBRARIES := libadbd +LOCAL_SHARED_LIBRARIES := libbase liblog libmincrypttwrp libcutils libc + include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_CLANG := true +LOCAL_MODULE := minadbd_test +LOCAL_SRC_FILES := fuse_adb_provider_test.cpp +LOCAL_CFLAGS := $(minadbd_cflags) +LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb +LOCAL_STATIC_LIBRARIES := libminadbd +LOCAL_SHARED_LIBRARIES := liblog libbase libcutils + +include $(BUILD_NATIVE_TEST) diff --git a/minadbd/README.txt b/minadbd/README.txt index c9df484c3..e69dc87c6 100644 --- a/minadbd/README.txt +++ b/minadbd/README.txt @@ -1,39 +1,8 @@ -The contents of this directory are copied from system/core/adb, with -the following changes: +minadbd is now mostly built from libadbd. The fuse features are unique to +minadbd, and services.c has been modified as follows: -adb.c - - much support for host mode and non-linux OS's stripped out; this - version only runs as adbd on the device. - - always setuid/setgid's itself to the shell user - - only uses USB transport - - references to JDWP removed - - main() removed - - all ADB_HOST and win32 code removed - - removed listeners, logging code, background server (for host) - -adb.h - - minor changes to match adb.c changes - -sockets.c - - references to JDWP removed - - ADB_HOST code removed - -services.c - - all services except echo_service (which is commented out) removed + - all services removed - all host mode support removed - sideload_service() added; this is the only service supported. It receives a single blob of data, writes it to a fixed filename, and makes the process exit. - -Android.mk - - only builds in adbd mode; builds as static library instead of a - standalone executable. - -sysdeps.h - - changes adb_creat() to use O_NOFOLLOW - -transport.c - - removed ADB_HOST code - -transport_usb.c - - removed ADB_HOST code diff --git a/minadbd/adb_main.cpp b/minadbd/adb_main.cpp new file mode 100644 index 000000000..7fae99a9a --- /dev/null +++ b/minadbd/adb_main.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#define TRACE_TAG TRACE_ADB + +#include "sysdeps.h" + +#include "adb.h" +#include "adb_auth.h" +#include "transport.h" + +int adb_main(int is_daemon, int server_port) +{ + atexit(usb_cleanup); + + adb_device_banner = "sideload"; + + // No SIGCHLD. Let the service subproc handle its children. + signal(SIGPIPE, SIG_IGN); + + // We can't require authentication for sideloading. http://b/22025550. + auth_required = false; + + init_transport_registration(); + usb_init(); + + D("Event loop starting\n"); + fdevent_loop(); + + return 0; +} diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp new file mode 100644 index 000000000..d71807dfb --- /dev/null +++ b/minadbd/fuse_adb_provider.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "sysdeps.h" + +#include "adb.h" +#include "adb_io.h" +#include "fuse_adb_provider.h" +#include "fuse_sideload.h" + +int read_block_adb(void* data, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + adb_data* ad = reinterpret_cast<adb_data*>(data); + + if (!WriteFdFmt(ad->sfd, "%08u", block)) { + fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno)); + return -EIO; + } + + if (!ReadFdExactly(ad->sfd, buffer, fetch_size)) { + fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno)); + return -EIO; + } + + return 0; +} + +static void close_adb(void* data) { + adb_data* ad = reinterpret_cast<adb_data*>(data); + WriteFdExactly(ad->sfd, "DONEDONE"); +} + +int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) { + adb_data ad; + ad.sfd = sfd; + ad.file_size = file_size; + ad.block_size = block_size; + + provider_vtab vtab; + vtab.read_block = read_block_adb; + vtab.close = close_adb; + + return run_fuse_sideload(&vtab, &ad, file_size, block_size); +} diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index 0eb1f79d1..9941709b9 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -17,6 +17,16 @@ #ifndef __FUSE_ADB_PROVIDER_H #define __FUSE_ADB_PROVIDER_H +#include <stdint.h> + +struct adb_data { + int sfd; // file descriptor for the adb channel + + uint64_t file_size; + uint32_t block_size; +}; + +int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size); int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size); #endif diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp new file mode 100644 index 000000000..0f2e881c7 --- /dev/null +++ b/minadbd/fuse_adb_provider_test.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fuse_adb_provider.h" + +#include <gtest/gtest.h> + +#include <errno.h> +#include <fcntl.h> +#include <sys/socket.h> + +#include <string> + +#include "adb_io.h" + +TEST(fuse_adb_provider, read_block_adb) { + adb_data data = {}; + int sockets[2]; + + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)); + data.sfd = sockets[0]; + + int host_socket = sockets[1]; + fcntl(host_socket, F_SETFL, O_NONBLOCK); + + const char expected_data[] = "foobar"; + char block_data[sizeof(expected_data)] = {}; + + // If we write the result of read_block_adb's request before the request is + // actually made we can avoid needing an extra thread for this test. + ASSERT_TRUE(WriteFdExactly(host_socket, expected_data, + strlen(expected_data))); + + uint32_t block = 1234U; + const char expected_block[] = "00001234"; + ASSERT_EQ(0, read_block_adb(reinterpret_cast<void*>(&data), block, + reinterpret_cast<uint8_t*>(block_data), + sizeof(expected_data) - 1)); + + // Check that read_block_adb requested the right block. + char block_req[sizeof(expected_block)] = {}; + ASSERT_TRUE(ReadFdExactly(host_socket, block_req, 8)); + ASSERT_EQ(0, block_req[8]); + ASSERT_EQ(8U, strlen(block_req)); + ASSERT_STREQ(expected_block, block_req); + + // Check that read_block_adb returned the right data. + ASSERT_EQ(0, block_req[8]); + ASSERT_STREQ(expected_data, block_data); + + // Check that nothing else was written to the socket. + char tmp; + errno = 0; + ASSERT_EQ(-1, read(host_socket, &tmp, 1)); + ASSERT_EQ(EWOULDBLOCK, errno); + + close(sockets[0]); + close(sockets[1]); +} + +TEST(fuse_adb_provider, read_block_adb_fail_write) { + adb_data data = {}; + int sockets[2]; + + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)); + data.sfd = sockets[0]; + + ASSERT_EQ(0, close(sockets[1])); + + char buf[1]; + ASSERT_EQ(-EIO, read_block_adb(reinterpret_cast<void*>(&data), 0, + reinterpret_cast<uint8_t*>(buf), 1)); + + close(sockets[0]); +} diff --git a/minadbd/services.cpp b/minadbd/services.cpp new file mode 100644 index 000000000..dd1fd7c4b --- /dev/null +++ b/minadbd/services.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SERVICES +#include "adb.h" +#include "fdevent.h" +#include "fuse_adb_provider.h" + +typedef struct stinfo stinfo; + +struct stinfo { + void (*func)(int fd, void *cookie); + int fd; + void *cookie; +}; + +void* service_bootstrap_func(void* x) { + stinfo* sti = reinterpret_cast<stinfo*>(x); + sti->func(sti->fd, sti->cookie); + free(sti); + return 0; +} + +static void sideload_host_service(int sfd, void* data) { + const char* args = reinterpret_cast<const char*>(data); + int file_size; + int block_size; + if (sscanf(args, "%d:%d", &file_size, &block_size) != 2) { + printf("bad sideload-host arguments: %s\n", args); + exit(1); + } + + printf("sideload-host file size %d block size %d\n", file_size, block_size); + + int result = run_adb_fuse(sfd, file_size, block_size); + + printf("sideload_host finished\n"); + sleep(1); + exit(result == 0 ? 0 : 1); +} + +static int create_service_thread(void (*func)(int, void *), void *cookie) +{ + int s[2]; + if(adb_socketpair(s)) { + printf("cannot create service socket pair\n"); + return -1; + } + + stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo))); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = func; + sti->cookie = cookie; + sti->fd = s[1]; + + adb_thread_t t; + if (adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(s[0]); + adb_close(s[1]); + printf("cannot create service thread\n"); + return -1; + } + + D("service thread started, %d:%d\n",s[0], s[1]); + return s[0]; +} + +int service_to_fd(const char* name) { + int ret = -1; + + if (!strncmp(name, "sideload:", 9)) { + // this exit status causes recovery to print a special error + // message saying to use a newer adb (that supports + // sideload-host). + exit(3); + } else if (!strncmp(name, "sideload-host:", 14)) { + ret = create_service_thread(sideload_host_service, (void*)(name + 14)); + } + if (ret >= 0) { + close_on_exec(ret); + } + return ret; +} diff --git a/minui.old/Android.mk b/minui.old/Android.mk new file mode 100644 index 000000000..7d8e3a7f3 --- /dev/null +++ b/minui.old/Android.mk @@ -0,0 +1,140 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := graphics_overlay.c events.c resources.c +ifneq ($(BOARD_CUSTOM_GRAPHICS),) + LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS) +else + LOCAL_SRC_FILES += graphics.c +endif + +LOCAL_C_INCLUDES +=\ + external/libpng \ + external/zlib \ + system/core/include/pixelflinger + +ifeq ($(TW_TARGET_USES_QCOM_BSP), true) + LOCAL_CFLAGS += -DMSM_BSP + ifeq ($(TARGET_PREBUILT_KERNEL),) + LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + else + ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),) + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include + else + LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS) + endif + endif +else + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include +endif + +ifeq ($(TW_NEW_ION_HEAP), true) + LOCAL_CFLAGS += -DNEW_ION_HEAP +endif + +LOCAL_STATIC_LIBRARY := libpng +LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_static +LOCAL_MODULE := libminui + +# This used to compare against values in double-quotes (which are just +# ordinary characters in this context). Strip double-quotes from the +# value so that either will work. + +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888) + LOCAL_CFLAGS += -DRECOVERY_RGBX +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888) + LOCAL_CFLAGS += -DRECOVERY_BGRA +endif + +ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),) + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT) +else + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 +endif + +ifneq ($(TW_BRIGHTNESS_PATH),) + LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=\"$(TW_BRIGHTNESS_PATH)\" +endif +ifneq ($(TW_MAX_BRIGHTNESS),) + LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS) +else + LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=255 +endif +ifneq ($(TW_NO_SCREEN_BLANK),) + LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK +endif +ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),) + LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT) +endif +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := graphics_overlay.c events.c resources.c +ifneq ($(BOARD_CUSTOM_GRAPHICS),) + LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS) +else + LOCAL_SRC_FILES += graphics.c +endif + +ifeq ($(TW_TARGET_USES_QCOM_BSP), true) + LOCAL_CFLAGS += -DMSM_BSP + ifeq ($(TARGET_PREBUILT_KERNEL),) + LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + else + ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),) + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include + else + LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS) + endif + endif +else + LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include +endif + +LOCAL_C_INCLUDES +=\ + external/libpng\ + external/zlib + +LOCAL_MODULE := libminui + +LOCAL_ARM_MODE:= arm +LOCAL_SHARED_LIBRARIES := libpng libpixelflinger +# This used to compare against values in double-quotes (which are just +# ordinary characters in this context). Strip double-quotes from the +# value so that either will work. + +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888) + LOCAL_CFLAGS += -DRECOVERY_RGBX +endif +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888) + LOCAL_CFLAGS += -DRECOVERY_BGRA +endif + +ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),) + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT) +else + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 +endif + +ifneq ($(TW_BRIGHTNESS_PATH),) + LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=\"$(TW_BRIGHTNESS_PATH)\" +endif +ifneq ($(TW_MAX_BRIGHTNESS),) + LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS) +else + LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=255 +endif +ifneq ($(TW_NO_SCREEN_BLANK),) + LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK +endif +ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),) + LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT) +endif + +LOCAL_CFLAGS += -DFASTMMI_FEATURE + +include $(BUILD_SHARED_LIBRARY) diff --git a/minui/events.c b/minui.old/events.c index b8cf15ab4..b8cf15ab4 100644 --- a/minui/events.c +++ b/minui.old/events.c diff --git a/minui.old/font_10x18.h b/minui.old/font_10x18.h new file mode 100644 index 000000000..7f96465cc --- /dev/null +++ b/minui.old/font_10x18.h @@ -0,0 +1,214 @@ +struct { + unsigned width; + unsigned height; + unsigned cwidth; + unsigned cheight; + unsigned char rundata[]; +} font = { + .width = 960, + .height = 18, + .cwidth = 10, + .cheight = 18, + .rundata = { +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82, +0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06, +0x84,0x0a,0x81,0x03,0x88,0x04,0x84,0x04,0x88,0x04,0x84,0x06,0x84,0x1e,0x81, +0x0e,0x81,0x0a,0x84,0x06,0x84,0x07,0x82,0x05,0x85,0x07,0x84,0x04,0x86,0x04, +0x88,0x02,0x88,0x04,0x84,0x04,0x82,0x04,0x82,0x02,0x88,0x05,0x86,0x01,0x82, +0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04, +0x86,0x06,0x84,0x04,0x86,0x06,0x84,0x04,0x88,0x02,0x82,0x04,0x82,0x02,0x82, +0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02, +0x88,0x03,0x86,0x0e,0x86,0x06,0x82,0x11,0x82,0x10,0x82,0x18,0x82,0x0f,0x84, +0x0d,0x82,0x1c,0x82,0x09,0x84,0x7f,0x16,0x84,0x05,0x82,0x05,0x84,0x07,0x83, +0x02,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x03,0x86,0x04, +0x83,0x02,0x82,0x03,0x82,0x01,0x82,0x07,0x82,0x09,0x82,0x06,0x82,0x3e,0x82, +0x04,0x84,0x06,0x83,0x06,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x03, +0x82,0x09,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82, +0x1c,0x82,0x0e,0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x05,0x84,0x04, +0x82,0x02,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82, +0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x04, +0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x03,0x82,0x02,0x82, +0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02, +0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82, +0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x03,0x82,0x08,0x82,0x0c, +0x82,0x05,0x84,0x11,0x82,0x0f,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82, +0x1c,0x82,0x0b,0x82,0x7f,0x15,0x82,0x08,0x82,0x08,0x82,0x05,0x82,0x01,0x82, +0x01,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x02,0x82,0x01, +0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x07,0x82, +0x08,0x82,0x08,0x82,0x3d,0x82,0x03,0x82,0x02,0x82,0x04,0x84,0x05,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x06,0x83,0x03,0x82,0x08,0x82,0x04,0x81,0x09,0x82, +0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x1a,0x82,0x10,0x82,0x06,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82, +0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02, +0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83, +0x02,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02, +0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82, +0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04, +0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x08,0x82,0x0c,0x82,0x04,0x82,0x02,0x82, +0x11,0x82,0x0e,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,0x0b,0x82,0x0b, +0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x05,0x82, +0x02,0x83,0x1a,0x82,0x07,0x81,0x02,0x81,0x07,0x82,0x01,0x82,0x02,0x82,0x01, +0x82,0x05,0x82,0x01,0x84,0x04,0x82,0x01,0x82,0x07,0x82,0x08,0x82,0x08,0x82, +0x06,0x82,0x02,0x82,0x06,0x82,0x28,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01, +0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x84,0x03,0x82,0x08,0x82, +0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x19,0x82,0x12,0x82,0x05, +0x82,0x04,0x82,0x02,0x82,0x02,0x84,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82, +0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04, +0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,0x02,0x83, +0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,0x05,0x82,0x04,0x82,0x02,0x82, +0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08, +0x82,0x04,0x82,0x09,0x82,0x0b,0x82,0x03,0x82,0x04,0x82,0x20,0x82,0x18,0x82, +0x0e,0x82,0x10,0x82,0x0b,0x82,0x0b,0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45, +0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x88,0x01,0x82,0x01,0x82,0x06,0x83, +0x01,0x82,0x04,0x84,0x08,0x81,0x08,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06, +0x82,0x28,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x08,0x82,0x04,0x82, +0x01,0x82,0x03,0x82,0x08,0x82,0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04, +0x82,0x18,0x82,0x06,0x88,0x06,0x82,0x04,0x82,0x04,0x82,0x02,0x82,0x01,0x85, +0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02, +0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82, +0x02,0x82,0x04,0x82,0x08,0x88,0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02, +0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82, +0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x84,0x06, +0x84,0x08,0x82,0x05,0x82,0x09,0x82,0x0b,0x82,0x2b,0x82,0x18,0x82,0x0e,0x82, +0x10,0x82,0x1c,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x26, +0x82,0x11,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x09,0x82,0x06,0x82,0x12,0x82, +0x0a,0x82,0x06,0x84,0x07,0x82,0x27,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0b, +0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,0x83,0x04,0x82,0x01,0x83, +0x08,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x83,0x07,0x83,0x05, +0x82,0x16,0x82,0x08,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82, +0x02,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08, +0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82, +0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82, +0x0a,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01, +0x82,0x04,0x84,0x06,0x84,0x08,0x82,0x05,0x82,0x0a,0x82,0x0a,0x82,0x23,0x85, +0x03,0x82,0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x84,0x04,0x86,0x05, +0x85,0x01,0x81,0x02,0x82,0x01,0x83,0x05,0x84,0x09,0x84,0x02,0x82,0x03,0x82, +0x06,0x82,0x05,0x81,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x83,0x06,0x84,0x04, +0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x02,0x82,0x01,0x84,0x04,0x86,0x03,0x86, +0x04,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x03,0x87,0x05,0x82,0x08,0x82,0x08,0x82,0x26,0x82, +0x11,0x82,0x01,0x82,0x04,0x86,0x07,0x82,0x05,0x83,0x12,0x82,0x0a,0x82,0x04, +0x88,0x02,0x88,0x0c,0x88,0x10,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0a,0x82, +0x06,0x83,0x04,0x82,0x03,0x82,0x03,0x83,0x02,0x82,0x03,0x83,0x02,0x82,0x07, +0x82,0x06,0x84,0x05,0x82,0x02,0x83,0x05,0x83,0x07,0x83,0x04,0x82,0x18,0x82, +0x06,0x82,0x04,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x86,0x04, +0x82,0x08,0x82,0x04,0x82,0x02,0x86,0x04,0x86,0x04,0x82,0x02,0x84,0x02,0x88, +0x05,0x82,0x0a,0x82,0x03,0x85,0x05,0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02, +0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82, +0x04,0x82,0x02,0x82,0x03,0x82,0x05,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x03, +0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x08,0x82,0x08,0x82, +0x06,0x82,0x0a,0x82,0x0a,0x82,0x22,0x82,0x03,0x82,0x02,0x83,0x02,0x82,0x04, +0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x02,0x82,0x05,0x82,0x06,0x82, +0x03,0x83,0x02,0x83,0x02,0x82,0x06,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,0x07, +0x82,0x05,0x88,0x02,0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82, +0x04,0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06, +0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82, +0x03,0x82,0x04,0x82,0x08,0x82,0x02,0x84,0x09,0x82,0x09,0x84,0x23,0x82,0x11, +0x82,0x01,0x82,0x06,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x01,0x82,0x11,0x82, +0x0a,0x82,0x06,0x84,0x07,0x82,0x26,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x08, +0x83,0x09,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x02,0x82,0x04,0x82,0x05,0x82, +0x06,0x82,0x02,0x82,0x05,0x83,0x01,0x82,0x17,0x82,0x16,0x82,0x06,0x82,0x05, +0x82,0x01,0x82,0x01,0x82,0x02,0x88,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82, +0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05, +0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82, +0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x86,0x04,0x82,0x04,0x82,0x02, +0x86,0x09,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82, +0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,0x82,0x27, +0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82, +0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02, +0x82,0x01,0x82,0x08,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82, +0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x07, +0x82,0x0a,0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82, +0x04,0x84,0x04,0x82,0x04,0x82,0x07,0x82,0x06,0x82,0x08,0x82,0x08,0x82,0x26, +0x82,0x0f,0x88,0x05,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x02,0x82,0x01,0x82, +0x0d,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,0x82,0x26,0x82,0x05,0x82,0x04, +0x82,0x05,0x82,0x07,0x82,0x0c,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82, +0x05,0x82,0x05,0x82,0x04,0x82,0x08,0x82,0x18,0x82,0x14,0x82,0x07,0x82,0x05, +0x82,0x01,0x84,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82, +0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05, +0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82, +0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02, +0x82,0x02,0x82,0x0a,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82, +0x01,0x82,0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09, +0x82,0x22,0x87,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x88, +0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02, +0x84,0x09,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82, +0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x86,0x05, +0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82, +0x05,0x82,0x05,0x82,0x04,0x82,0x06,0x82,0x07,0x82,0x08,0x82,0x08,0x82,0x26, +0x82,0x10,0x82,0x01,0x82,0x07,0x82,0x01,0x82,0x04,0x82,0x01,0x83,0x02,0x82, +0x03,0x83,0x0f,0x82,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x25,0x82,0x07, +0x82,0x02,0x82,0x06,0x82,0x06,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x09,0x82, +0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,0x82,0x08,0x82,0x19,0x82,0x05, +0x88,0x05,0x82,0x08,0x82,0x05,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x02,0x82, +0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,0x03,0x82,0x03,0x82, +0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02, +0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x03,0x82,0x09,0x82,0x05,0x82, +0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x06, +0x82,0x06,0x82,0x08,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,0x04,0x82,0x02,0x82, +0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,0x82,0x06,0x82,0x03, +0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x85,0x08,0x82,0x05,0x82, +0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,0x06,0x82,0x04,0x82, +0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05, +0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x38,0x82,0x01,0x82,0x04,0x82,0x01,0x82, +0x01,0x82,0x04,0x84,0x01,0x82,0x01,0x82,0x03,0x82,0x10,0x82,0x08,0x82,0x30, +0x83,0x06,0x82,0x07,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x08,0x82,0x04,0x82, +0x07,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04, +0x82,0x03,0x81,0x04,0x82,0x1a,0x82,0x10,0x82,0x10,0x82,0x08,0x82,0x04,0x82, +0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08, +0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82, +0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02, +0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x02,0x84,0x02,0x82,0x03,0x82,0x03,0x82, +0x04,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x05,0x83,0x02,0x83,0x03, +0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0c,0x82,0x08,0x82,0x21,0x82, +0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a, +0x82,0x07,0x85,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x02,0x82, +0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, +0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82, +0x06,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x04,0x84,0x04, +0x82,0x04,0x82,0x04,0x82,0x09,0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x82, +0x01,0x82,0x05,0x86,0x04,0x82,0x01,0x82,0x01,0x82,0x01,0x83,0x01,0x84,0x10, +0x82,0x06,0x82,0x1d,0x83,0x11,0x83,0x05,0x82,0x09,0x84,0x07,0x82,0x05,0x82, +0x09,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, +0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x06,0x83,0x07,0x83,0x09,0x82, +0x0e,0x82,0x0a,0x82,0x06,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03, +0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x09,0x82, +0x02,0x83,0x02,0x82,0x04,0x82,0x05,0x82,0x06,0x82,0x01,0x82,0x04,0x82,0x04, +0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82, +0x03,0x82,0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x06, +0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82, +0x05,0x82,0x05,0x82,0x09,0x82,0x0d,0x82,0x07,0x82,0x21,0x82,0x04,0x82,0x02, +0x83,0x02,0x82,0x04,0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x03,0x82, +0x04,0x82,0x06,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x03, +0x82,0x06,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x03,0x82, +0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x07,0x82,0x04, +0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x02,0x83,0x05,0x82,0x05,0x88,0x03,0x82, +0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x0a,0x82,0x08,0x82,0x08,0x82,0x26, +0x82,0x1c,0x82,0x06,0x82,0x02,0x83,0x03,0x84,0x02,0x82,0x10,0x82,0x04,0x82, +0x1e,0x83,0x11,0x83,0x05,0x82,0x0a,0x82,0x05,0x88,0x02,0x88,0x04,0x84,0x09, +0x82,0x05,0x84,0x06,0x84,0x05,0x82,0x09,0x84,0x06,0x84,0x07,0x83,0x07,0x83, +0x0a,0x81,0x0e,0x81,0x0b,0x82,0x07,0x85,0x03,0x82,0x04,0x82,0x02,0x86,0x06, +0x84,0x04,0x86,0x04,0x88,0x02,0x82,0x0a,0x84,0x01,0x81,0x02,0x82,0x04,0x82, +0x02,0x88,0x04,0x83,0x05,0x82,0x04,0x82,0x02,0x88,0x02,0x82,0x04,0x82,0x02, +0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x0a,0x85,0x03,0x82,0x04,0x82,0x04,0x84, +0x07,0x82,0x07,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05, +0x82,0x05,0x88,0x03,0x86,0x09,0x82,0x03,0x86,0x22,0x85,0x01,0x81,0x02,0x82, +0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x85,0x05,0x82,0x07,0x86,0x03, +0x82,0x04,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x88,0x02,0x82, +0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x83,0x06, +0x83,0x01,0x82,0x03,0x82,0x08,0x86,0x06,0x84,0x05,0x83,0x01,0x82,0x05,0x82, +0x06,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x83,0x01,0x82,0x03,0x87,0x06, +0x84,0x05,0x82,0x05,0x84,0x7f,0x15,0x83,0x7f,0x14,0x83,0x7f,0x5e,0x82,0x7f, +0x05,0x89,0x47,0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x4e, +0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,0x82,0x04,0x82,0x17,0x82,0x03,0x82, +0x34,0x82,0x0e,0x82,0x48,0x82,0x04,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a, +0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x49,0x82,0x02,0x82, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0c,0x86,0x19,0x85,0x35,0x82,0x0e,0x82,0x4a, +0x84,0x3f, +0x00, + } +}; diff --git a/minui/font_7x16.h b/minui.old/font_7x16.h index 0f72b5386..0f72b5386 100644 --- a/minui/font_7x16.h +++ b/minui.old/font_7x16.h diff --git a/minui/graphics.c b/minui.old/graphics.c index cce02a963..cce02a963 100644 --- a/minui/graphics.c +++ b/minui.old/graphics.c diff --git a/minui/graphics_overlay.c b/minui.old/graphics_overlay.c index d793b57e6..d793b57e6 100644 --- a/minui/graphics_overlay.c +++ b/minui.old/graphics_overlay.c diff --git a/minui/include/linux/msm_ion.h b/minui.old/include/linux/msm_ion.h index 121a5a7a7..121a5a7a7 100644 --- a/minui/include/linux/msm_ion.h +++ b/minui.old/include/linux/msm_ion.h diff --git a/minui/include/linux/msm_mdp.h b/minui.old/include/linux/msm_mdp.h index abdcd29e9..abdcd29e9 100644 --- a/minui/include/linux/msm_mdp.h +++ b/minui.old/include/linux/msm_mdp.h diff --git a/minui.old/minui.h b/minui.old/minui.h new file mode 100644 index 000000000..4c629c1b5 --- /dev/null +++ b/minui.old/minui.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MINUI_H_ +#define _MINUI_H_ + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* gr_surface; +typedef unsigned short gr_pixel; + +int gr_init(void); +void gr_exit(void); + +int gr_fb_width(void); +int gr_fb_height(void); +gr_pixel *gr_fb_data(void); +void gr_flip(void); +void gr_fb_blank(bool blank); + +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void gr_fill(int x1, int y1, int x2, int y2); + +// system/core/charger uses different gr_print signatures in diferent +// Android versions, either with or without int bold. +int gr_text(int x, int y, const char *s, ...); +int gr_text_impl(int x, int y, const char *s, int bold); + + void gr_texticon(int x, int y, gr_surface icon); +int gr_measure(const char *s); +void gr_font_size(int *x, int *y); +void gr_get_memory_surface(gr_surface); + +void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy); +unsigned int gr_get_width(gr_surface surface); +unsigned int gr_get_height(gr_surface surface); + +// input event structure, include <linux/input.h> for the definition. +// see http://www.mjmwired.net/kernel/Documentation/input/ for info. +struct input_event; + +typedef int (*ev_callback)(int fd, uint32_t epevents, void *data); +typedef int (*ev_set_key_callback)(int code, int value, void *data); + +int ev_init(ev_callback input_cb, void *data); +void ev_exit(void); +int ev_add_fd(int fd, ev_callback cb, void *data); +int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data); + +/* timeout has the same semantics as for poll + * 0 : don't block + * < 0 : block forever + * > 0 : block for 'timeout' milliseconds + */ +int ev_wait(int timeout); + +int ev_get_input(int fd, uint32_t epevents, struct input_event *ev); +void ev_dispatch(void); +int ev_get_epollfd(void); + +// Resources + +// Returns 0 if no error, else negative. +int res_create_surface(const char* name, gr_surface* pSurface); + +// Load an array of display surfaces from a single PNG image. The PNG +// should have a 'Frames' text chunk whose value is the number of +// frames this image represents. The pixel data itself is interlaced +// by row. +int res_create_multi_display_surface(const char* name, + int* frames, gr_surface** pSurface); + +int res_create_localized_surface(const char* name, gr_surface* pSurface); +void res_free_surface(gr_surface surface); +static inline int res_create_display_surface(const char* name, gr_surface* pSurface) { + return res_create_surface(name, pSurface); +} + +// These are new graphics functions from 5.0 that were not available in +// 4.4 that are required by charger and healthd +void gr_clear(); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/minui.old/mkfont.c b/minui.old/mkfont.c new file mode 100644 index 000000000..61a5edeb2 --- /dev/null +++ b/minui.old/mkfont.c @@ -0,0 +1,54 @@ +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv) +{ + unsigned n; + unsigned char *x; + unsigned m; + unsigned run_val; + unsigned run_count; + + n = gimp_image.width * gimp_image.height; + m = 0; + x = gimp_image.pixel_data; + + printf("struct {\n"); + printf(" unsigned width;\n"); + printf(" unsigned height;\n"); + printf(" unsigned cwidth;\n"); + printf(" unsigned cheight;\n"); + printf(" unsigned char rundata[];\n"); + printf("} font = {\n"); + printf(" .width = %d,\n .height = %d,\n .cwidth = %d,\n .cheight = %d,\n", gimp_image.width, gimp_image.height, + gimp_image.width / 96, gimp_image.height); + printf(" .rundata = {\n"); + + run_val = (*x ? 0 : 255); + run_count = 1; + n--; + x+=3; + + while(n-- > 0) { + unsigned val = (*x ? 0 : 255); + x+=3; + if((val == run_val) && (run_count < 127)) { + run_count++; + } else { +eject: + printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00)); + run_val = val; + run_count = 1; + m += 5; + if(m >= 75) { + printf("\n"); + m = 0; + } + } + } + printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00)); + printf("\n0x00,"); + printf("\n"); + printf(" }\n};\n"); + return 0; +} diff --git a/minui/resources.c b/minui.old/resources.c index ed25e45a2..ed25e45a2 100644 --- a/minui/resources.c +++ b/minui.old/resources.c diff --git a/minui/roboto_10x18.h b/minui.old/roboto_10x18.h index 3119c81c2..3119c81c2 100644 --- a/minui/roboto_10x18.h +++ b/minui.old/roboto_10x18.h diff --git a/minui/roboto_15x24.h b/minui.old/roboto_15x24.h index 7271d74bb..7271d74bb 100644 --- a/minui/roboto_15x24.h +++ b/minui.old/roboto_15x24.h diff --git a/minui/roboto_23x41.h b/minui.old/roboto_23x41.h index 6e7566efe..6e7566efe 100644 --- a/minui/roboto_23x41.h +++ b/minui.old/roboto_23x41.h diff --git a/minui/Android.mk b/minui/Android.mk index 7d8e3a7f3..d50bf1048 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -1,17 +1,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := graphics_overlay.c events.c resources.c -ifneq ($(BOARD_CUSTOM_GRAPHICS),) - LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS) -else - LOCAL_SRC_FILES += graphics.c -endif - -LOCAL_C_INCLUDES +=\ - external/libpng \ - external/zlib \ - system/core/include/pixelflinger +LOCAL_SRC_FILES := \ + events.cpp \ + graphics.cpp \ + graphics_adf.cpp \ + graphics_drm.cpp \ + graphics_fbdev.cpp \ + resources.cpp \ ifeq ($(TW_TARGET_USES_QCOM_BSP), true) LOCAL_CFLAGS += -DMSM_BSP @@ -33,14 +29,21 @@ ifeq ($(TW_NEW_ION_HEAP), true) LOCAL_CFLAGS += -DNEW_ION_HEAP endif -LOCAL_STATIC_LIBRARY := libpng -LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_static +LOCAL_WHOLE_STATIC_LIBRARIES += libadf +LOCAL_WHOLE_STATIC_LIBRARIES += libdrm +LOCAL_STATIC_LIBRARIES += libpng + LOCAL_MODULE := libminui +LOCAL_CLANG := true + # This used to compare against values in double-quotes (which are just # ordinary characters in this context). Strip double-quotes from the # value so that either will work. +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),ABGR_8888) + LOCAL_CFLAGS += -DRECOVERY_ABGR +endif ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888) LOCAL_CFLAGS += -DRECOVERY_RGBX endif @@ -65,76 +68,12 @@ endif ifneq ($(TW_NO_SCREEN_BLANK),) LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK endif -ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),) - LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT) -endif + include $(BUILD_STATIC_LIBRARY) +# Used by OEMs for factory test images. include $(CLEAR_VARS) - -LOCAL_SRC_FILES := graphics_overlay.c events.c resources.c -ifneq ($(BOARD_CUSTOM_GRAPHICS),) - LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS) -else - LOCAL_SRC_FILES += graphics.c -endif - -ifeq ($(TW_TARGET_USES_QCOM_BSP), true) - LOCAL_CFLAGS += -DMSM_BSP - ifeq ($(TARGET_PREBUILT_KERNEL),) - LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr - LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include - else - ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),) - LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include - else - LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS) - endif - endif -else - LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include -endif - -LOCAL_C_INCLUDES +=\ - external/libpng\ - external/zlib - LOCAL_MODULE := libminui - -LOCAL_ARM_MODE:= arm -LOCAL_SHARED_LIBRARIES := libpng libpixelflinger -# This used to compare against values in double-quotes (which are just -# ordinary characters in this context). Strip double-quotes from the -# value so that either will work. - -ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888) - LOCAL_CFLAGS += -DRECOVERY_RGBX -endif -ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888) - LOCAL_CFLAGS += -DRECOVERY_BGRA -endif - -ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),) - LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT) -else - LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 -endif - -ifneq ($(TW_BRIGHTNESS_PATH),) - LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=\"$(TW_BRIGHTNESS_PATH)\" -endif -ifneq ($(TW_MAX_BRIGHTNESS),) - LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS) -else - LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=255 -endif -ifneq ($(TW_NO_SCREEN_BLANK),) - LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK -endif -ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),) - LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT) -endif - -LOCAL_CFLAGS += -DFASTMMI_FEATURE - +LOCAL_WHOLE_STATIC_LIBRARIES += libminui +LOCAL_SHARED_LIBRARIES := libpng include $(BUILD_SHARED_LIBRARY) diff --git a/minui/events.cpp b/minui/events.cpp new file mode 100644 index 000000000..3b2262a4b --- /dev/null +++ b/minui/events.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/epoll.h> +#include <unistd.h> + +#include <linux/input.h> + +#include "minui.h" + +#define MAX_DEVICES 16 +#define MAX_MISC_FDS 16 + +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG) + +struct fd_info { + int fd; + ev_callback cb; + void* data; +}; + +static int g_epoll_fd; +static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS]; +static int npolledevents; + +static fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS]; + +static unsigned ev_count = 0; +static unsigned ev_dev_count = 0; +static unsigned ev_misc_count = 0; + +static bool test_bit(size_t bit, unsigned long* array) { + return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; +} + +int ev_init(ev_callback input_cb, void* data) { + bool epollctlfail = false; + + g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); + if (g_epoll_fd == -1) { + return -1; + } + + DIR* dir = opendir("/dev/input"); + if (dir != NULL) { + dirent* de; + while ((de = readdir(dir))) { + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; + +// fprintf(stderr,"/dev/input/%s\n", de->d_name); + if (strncmp(de->d_name, "event", 5)) continue; + int fd = openat(dirfd(dir), de->d_name, O_RDONLY); + if (fd == -1) continue; + + // Read the evbits of the input device. + if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + close(fd); + continue; + } + + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. + if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { + close(fd); + continue; + } + + epoll_event ev; + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = &ev_fdinfo[ev_count]; + if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { + close(fd); + epollctlfail = true; + continue; + } + + ev_fdinfo[ev_count].fd = fd; + ev_fdinfo[ev_count].cb = input_cb; + ev_fdinfo[ev_count].data = data; + ev_count++; + ev_dev_count++; + if (ev_dev_count == MAX_DEVICES) break; + } + + closedir(dir); + } + + if (epollctlfail && !ev_count) { + close(g_epoll_fd); + g_epoll_fd = -1; + return -1; + } + + return 0; +} + +int ev_get_epollfd(void) { + return g_epoll_fd; +} + +int ev_add_fd(int fd, ev_callback cb, void* data) { + if (ev_misc_count == MAX_MISC_FDS || cb == NULL) { + return -1; + } + + epoll_event ev; + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)&ev_fdinfo[ev_count]; + int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev); + if (!ret) { + ev_fdinfo[ev_count].fd = fd; + ev_fdinfo[ev_count].cb = cb; + ev_fdinfo[ev_count].data = data; + ev_count++; + ev_misc_count++; + } + + return ret; +} + +void ev_exit(void) { + while (ev_count > 0) { + close(ev_fdinfo[--ev_count].fd); + } + ev_misc_count = 0; + ev_dev_count = 0; + close(g_epoll_fd); +} + +int ev_wait(int timeout) { + npolledevents = epoll_wait(g_epoll_fd, polledevents, ev_count, timeout); + if (npolledevents <= 0) { + return -1; + } + return 0; +} + +void ev_dispatch(void) { + for (int n = 0; n < npolledevents; n++) { + fd_info* fdi = reinterpret_cast<fd_info*>(polledevents[n].data.ptr); + ev_callback cb = fdi->cb; + if (cb) { + cb(fdi->fd, polledevents[n].events, fdi->data); + } + } +} + +int ev_get_input(int fd, uint32_t epevents, input_event* ev) { + if (epevents & EPOLLIN) { + ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev))); + if (r == sizeof(*ev)) { + return 0; + } + } + return -1; +} + +int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data) { + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; + unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; + + for (size_t i = 0; i < ev_dev_count; ++i) { + memset(ev_bits, 0, sizeof(ev_bits)); + memset(key_bits, 0, sizeof(key_bits)); + + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + if (!test_bit(EV_KEY, ev_bits)) { + continue; + } + if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) { + continue; + } + + for (int code = 0; code <= KEY_MAX; code++) { + if (test_bit(code, key_bits)) { + set_key_cb(code, 1, data); + } + } + } + + return 0; +} + +void ev_iterate_available_keys(std::function<void(int)> f) { + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; + unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; + + for (size_t i = 0; i < ev_dev_count; ++i) { + memset(ev_bits, 0, sizeof(ev_bits)); + memset(key_bits, 0, sizeof(key_bits)); + + // Does this device even have keys? + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + if (!test_bit(EV_KEY, ev_bits)) { + continue; + } + + int rc = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits); + if (rc == -1) { + continue; + } + + for (int key_code = 0; key_code <= KEY_MAX; ++key_code) { + if (test_bit(key_code, key_bits)) { + f(key_code); + } + } + } +} diff --git a/minui/font_10x18.h b/minui/font_10x18.h index 7f96465cc..29d705344 100644 --- a/minui/font_10x18.h +++ b/minui/font_10x18.h @@ -3,7 +3,7 @@ struct { unsigned height; unsigned cwidth; unsigned cheight; - unsigned char rundata[]; + unsigned char rundata[2973]; } font = { .width = 960, .height = 18, diff --git a/minui/graphics.cpp b/minui/graphics.cpp new file mode 100644 index 000000000..c0eea9e38 --- /dev/null +++ b/minui/graphics.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <linux/fb.h> +#include <linux/kd.h> + +#include <time.h> + +#include "font_10x18.h" +#include "minui.h" +#include "graphics.h" + +struct GRFont { + GRSurface* texture; + int cwidth; + int cheight; +}; + +static GRFont* gr_font = NULL; +static minui_backend* gr_backend = NULL; + +static int overscan_percent = OVERSCAN_PERCENT; +static int overscan_offset_x = 0; +static int overscan_offset_y = 0; + +static unsigned char gr_current_r = 255; +static unsigned char gr_current_g = 255; +static unsigned char gr_current_b = 255; +static unsigned char gr_current_a = 255; + +static GRSurface* gr_draw = NULL; + +static bool outside(int x, int y) +{ + return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; +} + +int gr_measure(const char *s) +{ + return gr_font->cwidth * strlen(s); +} + +void gr_font_size(int *x, int *y) +{ + *x = gr_font->cwidth; + *y = gr_font->cheight; +} + +static void text_blend(unsigned char* src_p, int src_row_bytes, + unsigned char* dst_p, int dst_row_bytes, + int width, int height) +{ + for (int j = 0; j < height; ++j) { + unsigned char* sx = src_p; + unsigned char* px = dst_p; + for (int i = 0; i < width; ++i) { + unsigned char a = *sx++; + if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255; + if (a == 255) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } else if (a > 0) { + *px = (*px * (255-a) + gr_current_r * a) / 255; + ++px; + *px = (*px * (255-a) + gr_current_g * a) / 255; + ++px; + *px = (*px * (255-a) + gr_current_b * a) / 255; + ++px; + ++px; + } else { + px += 4; + } + } + src_p += src_row_bytes; + dst_p += dst_row_bytes; + } +} + +void gr_text(int x, int y, const char *s, bool bold) +{ + GRFont* font = gr_font; + + if (!font->texture || gr_current_a == 0) return; + + bold = bold && (font->texture->height != font->cheight); + + x += overscan_offset_x; + y += overscan_offset_y; + + unsigned char ch; + while ((ch = *s++)) { + if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break; + + if (ch < ' ' || ch > '~') { + ch = '?'; + } + + unsigned char* src_p = font->texture->data + ((ch - ' ') * font->cwidth) + + (bold ? font->cheight * font->texture->row_bytes : 0); + unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; + + text_blend(src_p, font->texture->row_bytes, + dst_p, gr_draw->row_bytes, + font->cwidth, font->cheight); + + x += font->cwidth; + } +} + +void gr_texticon(int x, int y, GRSurface* icon) { + if (icon == NULL) return; + + if (icon->pixel_bytes != 1) { + printf("gr_texticon: source has wrong format\n"); + return; + } + + x += overscan_offset_x; + y += overscan_offset_y; + + if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return; + + unsigned char* src_p = icon->data; + unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; + + text_blend(src_p, icon->row_bytes, + dst_p, gr_draw->row_bytes, + icon->width, icon->height); +} + +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + gr_current_r = b; + gr_current_g = g; + gr_current_b = r; + gr_current_a = a; +#else + gr_current_r = r; + gr_current_g = g; + gr_current_b = b; + gr_current_a = a; +#endif +} + +void gr_clear() +{ + if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) { + memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes); + } else { + unsigned char* px = gr_draw->data; + for (int y = 0; y < gr_draw->height; ++y) { + for (int x = 0; x < gr_draw->width; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes); + } + } +} + +void gr_fill(int x1, int y1, int x2, int y2) +{ + x1 += overscan_offset_x; + y1 += overscan_offset_y; + + x2 += overscan_offset_x; + y2 += overscan_offset_y; + + if (outside(x1, y1) || outside(x2-1, y2-1)) return; + + unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes; + if (gr_current_a == 255) { + int x, y; + for (y = y1; y < y2; ++y) { + unsigned char* px = p; + for (x = x1; x < x2; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + p += gr_draw->row_bytes; + } + } else if (gr_current_a > 0) { + int x, y; + for (y = y1; y < y2; ++y) { + unsigned char* px = p; + for (x = x1; x < x2; ++x) { + *px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255; + ++px; + *px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255; + ++px; + *px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255; + ++px; + ++px; + } + p += gr_draw->row_bytes; + } + } +} + +void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { + if (source == NULL) return; + + if (gr_draw->pixel_bytes != source->pixel_bytes) { + printf("gr_blit: source has wrong format\n"); + return; + } + + dx += overscan_offset_x; + dy += overscan_offset_y; + + if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return; + + unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes; + unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes; + + int i; + for (i = 0; i < h; ++i) { + memcpy(dst_p, src_p, w * source->pixel_bytes); + src_p += source->row_bytes; + dst_p += gr_draw->row_bytes; + } +} + +unsigned int gr_get_width(GRSurface* surface) { + if (surface == NULL) { + return 0; + } + return surface->width; +} + +unsigned int gr_get_height(GRSurface* surface) { + if (surface == NULL) { + return 0; + } + return surface->height; +} + +static void gr_init_font(void) +{ + gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1)); + + int res = res_create_alpha_surface("font", &(gr_font->texture)); + if (res == 0) { + // The font image should be a 96x2 array of character images. The + // columns are the printable ASCII characters 0x20 - 0x7f. The + // top row is regular text; the bottom row is bold. + gr_font->cwidth = gr_font->texture->width / 96; + gr_font->cheight = gr_font->texture->height / 2; + } else { + printf("failed to read font: res=%d\n", res); + + // fall back to the compiled-in font. + gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture))); + gr_font->texture->width = font.width; + gr_font->texture->height = font.height; + gr_font->texture->row_bytes = font.width; + gr_font->texture->pixel_bytes = 1; + + unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height)); + gr_font->texture->data = reinterpret_cast<unsigned char*>(bits); + + unsigned char data; + unsigned char* in = font.rundata; + while((data = *in++)) { + memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f); + bits += (data & 0x7f); + } + + gr_font->cwidth = font.cwidth; + gr_font->cheight = font.cheight; + } +} + +#if 0 +// Exercises many of the gr_*() functions; useful for testing. +static void gr_test() { + GRSurface** images; + int frames; + int result = res_create_multi_surface("icon_installing", &frames, &images); + if (result < 0) { + printf("create surface %d\n", result); + gr_exit(); + return; + } + + time_t start = time(NULL); + int x; + for (x = 0; x <= 1200; ++x) { + if (x < 400) { + gr_color(0, 0, 0, 255); + } else { + gr_color(0, (x-400)%128, 0, 255); + } + gr_clear(); + + gr_color(255, 0, 0, 255); + GRSurface* frame = images[x%frames]; + gr_blit(frame, 0, 0, frame->width, frame->height, x, 0); + + gr_color(255, 0, 0, 128); + gr_fill(400, 150, 600, 350); + + gr_color(255, 255, 255, 255); + gr_text(500, 225, "hello, world!", 0); + gr_color(255, 255, 0, 128); + gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1); + + gr_color(0, 0, 255, 128); + gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500); + + gr_draw = gr_backend->flip(gr_backend); + } + printf("getting end time\n"); + time_t end = time(NULL); + printf("got end time\n"); + printf("start %ld end %ld\n", (long)start, (long)end); + if (end > start) { + printf("%.2f fps\n", ((double)x) / (end-start)); + } +} +#endif + +void gr_flip() { + gr_draw = gr_backend->flip(gr_backend); +} + +int gr_init(void) +{ + gr_init_font(); + + gr_backend = open_adf(); + if (gr_backend) { + gr_draw = gr_backend->init(gr_backend); + if (!gr_draw) { + gr_backend->exit(gr_backend); + } + } + + if (!gr_draw) { + gr_backend = open_drm(); + gr_draw = gr_backend->init(gr_backend); + } + + if (!gr_draw) { + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { + return -1; + } + } + + overscan_offset_x = gr_draw->width * overscan_percent / 100; + overscan_offset_y = gr_draw->height * overscan_percent / 100; + + gr_flip(); + gr_flip(); + + return 0; +} + +void gr_exit(void) +{ + gr_backend->exit(gr_backend); +} + +int gr_fb_width(void) +{ + return gr_draw->width - 2*overscan_offset_x; +} + +int gr_fb_height(void) +{ + return gr_draw->height - 2*overscan_offset_y; +} + +void gr_fb_blank(bool blank) +{ + gr_backend->blank(gr_backend, blank); +} diff --git a/minui/graphics.h b/minui/graphics.h new file mode 100644 index 000000000..52968eb10 --- /dev/null +++ b/minui/graphics.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GRAPHICS_H_ +#define _GRAPHICS_H_ + +#include "minui.h" + +// TODO: lose the function pointers. +struct minui_backend { + // Initializes the backend and returns a GRSurface* to draw into. + GRSurface* (*init)(minui_backend*); + + // Causes the current drawing surface (returned by the most recent + // call to flip() or init()) to be displayed, and returns a new + // drawing surface. + GRSurface* (*flip)(minui_backend*); + + // Blank (or unblank) the screen. + void (*blank)(minui_backend*, bool); + + // Device cleanup when drawing is done. + void (*exit)(minui_backend*); +}; + +minui_backend* open_fbdev(); +minui_backend* open_adf(); +minui_backend* open_drm(); + +#endif diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp new file mode 100644 index 000000000..5d0867f58 --- /dev/null +++ b/minui/graphics_adf.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/cdefs.h> +#include <sys/mman.h> + +#include <adf/adf.h> + +#include "graphics.h" + +struct adf_surface_pdata { + GRSurface base; + int fd; + __u32 offset; + __u32 pitch; +}; + +struct adf_pdata { + minui_backend base; + int intf_fd; + adf_id_t eng_id; + __u32 format; + + unsigned int current_surface; + unsigned int n_surfaces; + adf_surface_pdata surfaces[2]; +}; + +static GRSurface* adf_flip(minui_backend *backend); +static void adf_blank(minui_backend *backend, bool blank); + +static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) { + memset(surf, 0, sizeof(*surf)); + + surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay, + mode->vdisplay, pdata->format, &surf->offset, &surf->pitch); + if (surf->fd < 0) + return surf->fd; + + surf->base.width = mode->hdisplay; + surf->base.height = mode->vdisplay; + surf->base.row_bytes = surf->pitch; + surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4; + + surf->base.data = reinterpret_cast<uint8_t*>(mmap(NULL, + surf->pitch * surf->base.height, PROT_WRITE, + MAP_SHARED, surf->fd, surf->offset)); + if (surf->base.data == MAP_FAILED) { + close(surf->fd); + return -errno; + } + + return 0; +} + +static int adf_interface_init(adf_pdata *pdata) +{ + adf_interface_data intf_data; + int ret = 0; + int err; + + err = adf_get_interface_data(pdata->intf_fd, &intf_data); + if (err < 0) + return err; + + err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]); + if (err < 0) { + fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err)); + ret = err; + goto done; + } + + err = adf_surface_init(pdata, &intf_data.current_mode, + &pdata->surfaces[1]); + if (err < 0) { + fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err)); + memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1])); + pdata->n_surfaces = 1; + } else { + pdata->n_surfaces = 2; + } + +done: + adf_free_interface_data(&intf_data); + return ret; +} + +static int adf_device_init(adf_pdata *pdata, adf_device *dev) +{ + adf_id_t intf_id; + int intf_fd; + int err; + + err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, + &pdata->eng_id); + if (err < 0) + return err; + + err = adf_device_attach(dev, pdata->eng_id, intf_id); + if (err < 0 && err != -EALREADY) + return err; + + pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR); + if (pdata->intf_fd < 0) + return pdata->intf_fd; + + err = adf_interface_init(pdata); + if (err < 0) { + close(pdata->intf_fd); + pdata->intf_fd = -1; + } + + return err; +} + +static GRSurface* adf_init(minui_backend *backend) +{ + adf_pdata *pdata = (adf_pdata *)backend; + adf_id_t *dev_ids = NULL; + ssize_t n_dev_ids, i; + GRSurface* ret; + +#if defined(RECOVERY_ABGR) + pdata->format = DRM_FORMAT_ABGR8888; +#elif defined(RECOVERY_BGRA) + pdata->format = DRM_FORMAT_BGRA8888; +#elif defined(RECOVERY_RGBX) + pdata->format = DRM_FORMAT_RGBX8888; +#else + pdata->format = DRM_FORMAT_RGB565; +#endif + + n_dev_ids = adf_devices(&dev_ids); + if (n_dev_ids == 0) { + return NULL; + } else if (n_dev_ids < 0) { + fprintf(stderr, "enumerating adf devices failed: %s\n", + strerror(-n_dev_ids)); + return NULL; + } + + pdata->intf_fd = -1; + + for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) { + adf_device dev; + + int err = adf_device_open(dev_ids[i], O_RDWR, &dev); + if (err < 0) { + fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], + strerror(-err)); + continue; + } + + err = adf_device_init(pdata, &dev); + if (err < 0) + fprintf(stderr, "initializing adf device %u failed: %s\n", + dev_ids[i], strerror(-err)); + + adf_device_close(&dev); + } + + free(dev_ids); + + if (pdata->intf_fd < 0) + return NULL; + + ret = adf_flip(backend); + + adf_blank(backend, true); + adf_blank(backend, false); + + return ret; +} + +static GRSurface* adf_flip(minui_backend *backend) +{ + adf_pdata *pdata = (adf_pdata *)backend; + adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface]; + + int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id, + surf->base.width, surf->base.height, pdata->format, surf->fd, + surf->offset, surf->pitch, -1); + if (fence_fd >= 0) + close(fence_fd); + + pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces; + return &pdata->surfaces[pdata->current_surface].base; +} + +static void adf_blank(minui_backend *backend, bool blank) +{ + adf_pdata *pdata = (adf_pdata *)backend; + adf_interface_blank(pdata->intf_fd, + blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); +} + +static void adf_surface_destroy(adf_surface_pdata *surf) +{ + munmap(surf->base.data, surf->pitch * surf->base.height); + close(surf->fd); +} + +static void adf_exit(minui_backend *backend) +{ + adf_pdata *pdata = (adf_pdata *)backend; + unsigned int i; + + for (i = 0; i < pdata->n_surfaces; i++) + adf_surface_destroy(&pdata->surfaces[i]); + if (pdata->intf_fd >= 0) + close(pdata->intf_fd); + free(pdata); +} + +minui_backend *open_adf() +{ + adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata))); + if (!pdata) { + perror("allocating adf backend failed"); + return NULL; + } + + pdata->base.init = adf_init; + pdata->base.flip = adf_flip; + pdata->base.blank = adf_blank; + pdata->base.exit = adf_exit; + return &pdata->base; +} diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp new file mode 100644 index 000000000..03e33b775 --- /dev/null +++ b/minui/graphics_drm.cpp @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <drm_fourcc.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/cdefs.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "minui.h" +#include "graphics.h" + +#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A))) + +struct drm_surface { + GRSurface base; + uint32_t fb_id; + uint32_t handle; +}; + +static drm_surface *drm_surfaces[2]; +static int current_buffer; + +static drmModeCrtc *main_monitor_crtc; +static drmModeConnector *main_monitor_connector; + +static int drm_fd = -1; + +static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) { + if (crtc) { + drmModeSetCrtc(drm_fd, crtc->crtc_id, + 0, // fb_id + 0, 0, // x,y + NULL, // connectors + 0, // connector_count + NULL); // mode + } +} + +static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc, + struct drm_surface *surface) { + int32_t ret; + + ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, + surface->fb_id, + 0, 0, // x,y + &main_monitor_connector->connector_id, + 1, // connector_count + &main_monitor_crtc->mode); + + if (ret) + printf("drmModeSetCrtc failed ret=%d\n", ret); +} + +static void drm_blank(minui_backend* backend __unused, bool blank) { + if (blank) + drm_disable_crtc(drm_fd, main_monitor_crtc); + else + drm_enable_crtc(drm_fd, main_monitor_crtc, + drm_surfaces[current_buffer]); +} + +static void drm_destroy_surface(struct drm_surface *surface) { + struct drm_gem_close gem_close; + int ret; + + if(!surface) + return; + + if (surface->base.data) + munmap(surface->base.data, + surface->base.row_bytes * surface->base.height); + + if (surface->fb_id) { + ret = drmModeRmFB(drm_fd, surface->fb_id); + if (ret) + printf("drmModeRmFB failed ret=%d\n", ret); + } + + if (surface->handle) { + memset(&gem_close, 0, sizeof(gem_close)); + gem_close.handle = surface->handle; + + ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close); + if (ret) + printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret); + } + + free(surface); +} + +static int drm_format_to_bpp(uint32_t format) { + switch(format) { + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_XRGB8888: + return 32; + case DRM_FORMAT_RGB565: + return 16; + default: + printf("Unknown format %d\n", format); + return 32; + } +} + +static drm_surface *drm_create_surface(int width, int height) { + struct drm_surface *surface; + struct drm_mode_create_dumb create_dumb; + uint32_t format; + int ret; + + surface = (struct drm_surface*)calloc(1, sizeof(*surface)); + if (!surface) { + printf("Can't allocate memory\n"); + return NULL; + } + +#if defined(RECOVERY_ABGR) + format = DRM_FORMAT_RGBA8888; +#elif defined(RECOVERY_BGRA) + format = DRM_FORMAT_ARGB8888; +#elif defined(RECOVERY_RGBX) + format = DRM_FORMAT_XBGR8888; +#else + format = DRM_FORMAT_RGB565; +#endif + + memset(&create_dumb, 0, sizeof(create_dumb)); + create_dumb.height = height; + create_dumb.width = width; + create_dumb.bpp = drm_format_to_bpp(format); + create_dumb.flags = 0; + + ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); + if (ret) { + printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret); + drm_destroy_surface(surface); + return NULL; + } + surface->handle = create_dumb.handle; + + uint32_t handles[4], pitches[4], offsets[4]; + + handles[0] = surface->handle; + pitches[0] = create_dumb.pitch; + offsets[0] = 0; + + ret = drmModeAddFB2(drm_fd, width, height, + format, handles, pitches, offsets, + &(surface->fb_id), 0); + if (ret) { + printf("drmModeAddFB2 failed ret=%d\n", ret); + drm_destroy_surface(surface); + return NULL; + } + + struct drm_mode_map_dumb map_dumb; + memset(&map_dumb, 0, sizeof(map_dumb)); + map_dumb.handle = create_dumb.handle; + ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); + if (ret) { + printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret); + drm_destroy_surface(surface); + return NULL;; + } + + surface->base.height = height; + surface->base.width = width; + surface->base.row_bytes = create_dumb.pitch; + surface->base.pixel_bytes = create_dumb.bpp / 8; + surface->base.data = (unsigned char*) + mmap(NULL, + surface->base.height * surface->base.row_bytes, + PROT_READ | PROT_WRITE, MAP_SHARED, + drm_fd, map_dumb.offset); + if (surface->base.data == MAP_FAILED) { + perror("mmap() failed"); + drm_destroy_surface(surface); + return NULL; + } + + return surface; +} + +static drmModeCrtc *find_crtc_for_connector(int fd, + drmModeRes *resources, + drmModeConnector *connector) { + int i, j; + drmModeEncoder *encoder; + int32_t crtc; + + /* + * Find the encoder. If we already have one, just use it. + */ + if (connector->encoder_id) + encoder = drmModeGetEncoder(fd, connector->encoder_id); + else + encoder = NULL; + + if (encoder && encoder->crtc_id) { + crtc = encoder->crtc_id; + drmModeFreeEncoder(encoder); + return drmModeGetCrtc(fd, crtc); + } + + /* + * Didn't find anything, try to find a crtc and encoder combo. + */ + crtc = -1; + for (i = 0; i < connector->count_encoders; i++) { + encoder = drmModeGetEncoder(fd, connector->encoders[i]); + + if (encoder) { + for (j = 0; j < resources->count_crtcs; j++) { + if (!(encoder->possible_crtcs & (1 << j))) + continue; + crtc = resources->crtcs[j]; + break; + } + if (crtc >= 0) { + drmModeFreeEncoder(encoder); + return drmModeGetCrtc(fd, crtc); + } + } + } + + return NULL; +} + +static drmModeConnector *find_used_connector_by_type(int fd, + drmModeRes *resources, + unsigned type) { + int i; + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnector(fd, resources->connectors[i]); + if (connector) { + if ((connector->connector_type == type) && + (connector->connection == DRM_MODE_CONNECTED) && + (connector->count_modes > 0)) + return connector; + + drmModeFreeConnector(connector); + } + } + return NULL; +} + +static drmModeConnector *find_first_connected_connector(int fd, + drmModeRes *resources) { + int i; + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnector(fd, resources->connectors[i]); + if (connector) { + if ((connector->count_modes > 0) && + (connector->connection == DRM_MODE_CONNECTED)) + return connector; + + drmModeFreeConnector(connector); + } + } + return NULL; +} + +static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources, + uint32_t *mode_index) { + unsigned i = 0; + int modes; + /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */ + unsigned kConnectorPriority[] = { + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_eDP, + DRM_MODE_CONNECTOR_DSI, + }; + + drmModeConnector *main_monitor_connector = NULL; + do { + main_monitor_connector = find_used_connector_by_type(fd, + resources, + kConnectorPriority[i]); + i++; + } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority)); + + /* If we didn't find a connector, grab the first one that is connected. */ + if (!main_monitor_connector) + main_monitor_connector = + find_first_connected_connector(fd, resources); + + /* If we still didn't find a connector, give up and return. */ + if (!main_monitor_connector) + return NULL; + + *mode_index = 0; + for (modes = 0; modes < main_monitor_connector->count_modes; modes++) { + if (main_monitor_connector->modes[modes].type & + DRM_MODE_TYPE_PREFERRED) { + *mode_index = modes; + break; + } + } + + return main_monitor_connector; +} + +static void disable_non_main_crtcs(int fd, + drmModeRes *resources, + drmModeCrtc* main_crtc) { + int i; + drmModeCrtc* crtc; + + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnector(fd, resources->connectors[i]); + crtc = find_crtc_for_connector(fd, resources, connector); + if (crtc->crtc_id != main_crtc->crtc_id) + drm_disable_crtc(fd, crtc); + drmModeFreeCrtc(crtc); + } +} + +static GRSurface* drm_init(minui_backend* backend __unused) { + drmModeRes *res = NULL; + uint32_t selected_mode; + char *dev_name; + int width, height; + int ret, i; + + /* Consider DRM devices in order. */ + for (i = 0; i < DRM_MAX_MINOR; i++) { + uint64_t cap = 0; + + ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret < 0) + continue; + + drm_fd = open(dev_name, O_RDWR, 0); + free(dev_name); + if (drm_fd < 0) + continue; + + /* We need dumb buffers. */ + ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap); + if (ret || cap == 0) { + close(drm_fd); + continue; + } + + res = drmModeGetResources(drm_fd); + if (!res) { + close(drm_fd); + continue; + } + + /* Use this device if it has at least one connected monitor. */ + if (res->count_crtcs > 0 && res->count_connectors > 0) + if (find_first_connected_connector(drm_fd, res)) + break; + + drmModeFreeResources(res); + close(drm_fd); + res = NULL; + } + + if (drm_fd < 0 || res == NULL) { + perror("cannot find/open a drm device"); + return NULL; + } + + main_monitor_connector = find_main_monitor(drm_fd, + res, &selected_mode); + + if (!main_monitor_connector) { + printf("main_monitor_connector not found\n"); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } + + main_monitor_crtc = find_crtc_for_connector(drm_fd, res, + main_monitor_connector); + + if (!main_monitor_crtc) { + printf("main_monitor_crtc not found\n"); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } + + disable_non_main_crtcs(drm_fd, + res, main_monitor_crtc); + + main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode]; + + width = main_monitor_crtc->mode.hdisplay; + height = main_monitor_crtc->mode.vdisplay; + + drmModeFreeResources(res); + + drm_surfaces[0] = drm_create_surface(width, height); + drm_surfaces[1] = drm_create_surface(width, height); + if (!drm_surfaces[0] || !drm_surfaces[1]) { + drm_destroy_surface(drm_surfaces[0]); + drm_destroy_surface(drm_surfaces[1]); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } + + current_buffer = 0; + + drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]); + + return &(drm_surfaces[0]->base); +} + +static GRSurface* drm_flip(minui_backend* backend __unused) { + int ret; + + ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, + drm_surfaces[current_buffer]->fb_id, 0, NULL); + if (ret < 0) { + printf("drmModePageFlip failed ret=%d\n", ret); + return NULL; + } + current_buffer = 1 - current_buffer; + return &(drm_surfaces[current_buffer]->base); +} + +static void drm_exit(minui_backend* backend __unused) { + drm_disable_crtc(drm_fd, main_monitor_crtc); + drm_destroy_surface(drm_surfaces[0]); + drm_destroy_surface(drm_surfaces[1]); + drmModeFreeCrtc(main_monitor_crtc); + drmModeFreeConnector(main_monitor_connector); + close(drm_fd); + drm_fd = -1; +} + +static minui_backend drm_backend = { + .init = drm_init, + .flip = drm_flip, + .blank = drm_blank, + .exit = drm_exit, +}; + +minui_backend* open_drm() { + return &drm_backend; +} diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp new file mode 100644 index 000000000..512a7d0e7 --- /dev/null +++ b/minui/graphics_fbdev.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/cdefs.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <linux/fb.h> +#include <linux/kd.h> + +#include "minui.h" +#include "graphics.h" + +static GRSurface* fbdev_init(minui_backend*); +static GRSurface* fbdev_flip(minui_backend*); +static void fbdev_blank(minui_backend*, bool); +static void fbdev_exit(minui_backend*); + +static GRSurface gr_framebuffer[2]; +static bool double_buffered; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static fb_var_screeninfo vi; +static int fb_fd = -1; + +static minui_backend my_backend = { + .init = fbdev_init, + .flip = fbdev_flip, + .blank = fbdev_blank, + .exit = fbdev_exit, +}; + +minui_backend* open_fbdev() { + return &my_backend; +} + +static void fbdev_blank(minui_backend* backend __unused, bool blank) +{ +#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS) + int fd; + char brightness[4]; + snprintf(brightness, 4, "%03d", TW_MAX_BRIGHTNESS/2); + + fd = open(TW_BRIGHTNESS_PATH, O_RDWR); + if (fd < 0) { + perror("cannot open LCD backlight"); + return; + } + write(fd, blank ? "000" : brightness, 3); + close(fd); +#else + int ret; + + ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) + perror("ioctl(): blank"); +#endif +} + +static void set_displayed_framebuffer(unsigned n) +{ + if (n > 1 || !double_buffered) return; + + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; +} + +static GRSurface* fbdev_init(minui_backend* backend) { + int fd = open("/dev/graphics/fb0", O_RDWR); + if (fd == -1) { + perror("cannot open fb0"); + return NULL; + } + + fb_fix_screeninfo fi; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf("fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, + vi.red.offset, vi.red.length, + vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + + void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (bits == MAP_FAILED) { + perror("failed to mmap framebuffer"); + close(fd); + return NULL; + } + + memset(bits, 0, fi.smem_len); + + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits); + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + + /* check if we can use double buffering */ + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; + + memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = gr_framebuffer[0].data + + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; + + gr_draw = gr_framebuffer+1; + + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return NULL; + } + } + + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + fb_fd = fd; + set_displayed_framebuffer(0); + + printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); + + fbdev_blank(backend, true); + fbdev_blank(backend, false); + + return gr_draw; +} + +static GRSurface* fbdev_flip(minui_backend* backend __unused) { + if (double_buffered) { +#if defined(RECOVERY_BGRA) + // In case of BGRA, do some byte swapping + unsigned int idx; + unsigned char tmp; + unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data; + for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); + idx += 4) { + tmp = ucfb_vaddr[idx]; + ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2]; + ucfb_vaddr[idx + 2] = tmp; + } +#endif + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1-displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + } + return gr_draw; +} + +static void fbdev_exit(minui_backend* backend __unused) { + close(fb_fd); + fb_fd = -1; + + if (!double_buffered && gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; +} diff --git a/minui/minui.h b/minui/minui.h index 4c629c1b5..18173b198 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -17,6 +17,116 @@ #ifndef _MINUI_H_ #define _MINUI_H_ +#ifndef TW_USE_OLD_MINUI_H + +#include <sys/types.h> + +#include <functional> + +// +// Graphics. +// + +struct GRSurface { + int width; + int height; + int row_bytes; + int pixel_bytes; + unsigned char* data; +}; + +int gr_init(); +void gr_exit(); + +int gr_fb_width(); +int gr_fb_height(); + +void gr_flip(); +void gr_fb_blank(bool blank); + +void gr_clear(); // clear entire surface to current color +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void gr_fill(int x1, int y1, int x2, int y2); +void gr_text(int x, int y, const char *s, bool bold); +void gr_texticon(int x, int y, GRSurface* icon); +int gr_measure(const char *s); +void gr_font_size(int *x, int *y); + +void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy); +unsigned int gr_get_width(GRSurface* surface); +unsigned int gr_get_height(GRSurface* surface); + +// +// Input events. +// + +struct input_event; + +// TODO: move these over to std::function. +typedef int (*ev_callback)(int fd, uint32_t epevents, void* data); +typedef int (*ev_set_key_callback)(int code, int value, void* data); + +int ev_init(ev_callback input_cb, void* data); +void ev_exit(); +int ev_add_fd(int fd, ev_callback cb, void* data); +void ev_iterate_available_keys(std::function<void(int)> f); +int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data); + +// 'timeout' has the same semantics as poll(2). +// 0 : don't block +// < 0 : block forever +// > 0 : block for 'timeout' milliseconds +int ev_wait(int timeout); + +int ev_get_input(int fd, uint32_t epevents, input_event* ev); +void ev_dispatch(); +int ev_get_epollfd(); + +// +// Resources +// + +// res_create_*_surface() functions return 0 if no error, else +// negative. +// +// A "display" surface is one that is intended to be drawn to the +// screen with gr_blit(). An "alpha" surface is a grayscale image +// interpreted as an alpha mask used to render text in the current +// color (with gr_text() or gr_texticon()). +// +// All these functions load PNG images from "/res/images/${name}.png". + +// Load a single display surface from a PNG image. +int res_create_display_surface(const char* name, GRSurface** pSurface); + +// Load an array of display surfaces from a single PNG image. The PNG +// should have a 'Frames' text chunk whose value is the number of +// frames this image represents. The pixel data itself is interlaced +// by row. +int res_create_multi_display_surface(const char* name, + int* frames, GRSurface*** pSurface); + +// Load a single alpha surface from a grayscale PNG image. +int res_create_alpha_surface(const char* name, GRSurface** pSurface); + +// Load part of a grayscale PNG image that is the first match for the +// given locale. The image is expected to be a composite of multiple +// translations of the same text, with special added rows that encode +// the subimages' size and intended locale in the pixel data. See +// development/tools/recovery_l10n for an app that will generate these +// specialized images from Android resources. +int res_create_localized_alpha_surface(const char* name, const char* locale, + GRSurface** pSurface); + +// Free a surface allocated by any of the res_create_*_surface() +// functions. +void res_free_surface(GRSurface* surface); + +#else //ifndef TW_USE_OLD_MINUI_H + +// This the old minui.old/minui.h for compatibility with building TWRP +// in pre 6.0 trees. + #include <stdbool.h> #ifdef __cplusplus @@ -102,4 +212,5 @@ void gr_clear(); } #endif -#endif +#endif // ifndef TW_USE_OLD_MINUI_H +#endif // ifndef _MINUI_H_ diff --git a/minui/resources.cpp b/minui/resources.cpp new file mode 100644 index 000000000..5e4789277 --- /dev/null +++ b/minui/resources.cpp @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <linux/fb.h> +#include <linux/kd.h> + +#include <png.h> + +#include "minui.h" + +extern char* locale; + +#define SURFACE_DATA_ALIGNMENT 8 + +static GRSurface* malloc_surface(size_t data_size) { + size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT; + unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size)); + if (temp == NULL) return NULL; + GRSurface* surface = reinterpret_cast<GRSurface*>(temp); + surface->data = temp + sizeof(GRSurface) + + (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT)); + return surface; +} + +static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, + png_uint_32* width, png_uint_32* height, png_byte* channels) { + char resPath[256]; + unsigned char header[8]; + int result = 0; + int color_type, bit_depth; + size_t bytesRead; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); + resPath[sizeof(resPath)-1] = '\0'; + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + + bytesRead = fread(header, 1, sizeof(header), fp); + if (bytesRead != sizeof(header)) { + result = -2; + goto exit; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; + goto exit; + } + + *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!*png_ptr) { + result = -4; + goto exit; + } + + *info_ptr = png_create_info_struct(*png_ptr); + if (!*info_ptr) { + result = -5; + goto exit; + } + + if (setjmp(png_jmpbuf(*png_ptr))) { + result = -6; + goto exit; + } + + png_init_io(*png_ptr, fp); + png_set_sig_bytes(*png_ptr, sizeof(header)); + png_read_info(*png_ptr, *info_ptr); + + png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth, + &color_type, NULL, NULL, NULL); + + *channels = png_get_channels(*png_ptr, *info_ptr); + + if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) { + // 8-bit RGB images: great, nothing to do. + } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) { + // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray. + png_set_expand_gray_1_2_4_to_8(*png_ptr); + } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { + // paletted images: expand to 8-bit RGB. Note that we DON'T + // currently expand the tRNS chunk (if any) to an alpha + // channel, because minui doesn't support alpha channels in + // general. + png_set_palette_to_rgb(*png_ptr); + *channels = 3; + } else { + fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", + bit_depth, *channels, color_type); + result = -7; + goto exit; + } + + return result; + + exit: + if (result < 0) { + png_destroy_read_struct(png_ptr, info_ptr, NULL); + } + if (fp != NULL) { + fclose(fp); + } + + return result; +} + +// "display" surfaces are transformed into the framebuffer's required +// pixel format (currently only RGBX is supported) at load time, so +// gr_blit() can be nothing more than a memcpy() for each row. The +// next two functions are the only ones that know anything about the +// framebuffer pixel format; they need to be modified if the +// framebuffer format changes (but nothing else should). + +// Allocate and return a GRSurface* sufficient for storing an image of +// the indicated size in the framebuffer pixel format. +static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) { + GRSurface* surface = malloc_surface(width * height * 4); + if (surface == NULL) return NULL; + + surface->width = width; + surface->height = height; + surface->row_bytes = width * 4; + surface->pixel_bytes = 4; + + return surface; +} + +// Copy 'input_row' to 'output_row', transforming it to the +// framebuffer pixel format. The input format depends on the value of +// 'channels': +// +// 1 - input is 8-bit grayscale +// 3 - input is 24-bit RGB +// 4 - input is 32-bit RGBA/RGBX +// +// 'width' is the number of pixels in the row. +static void transform_rgb_to_draw(unsigned char* input_row, + unsigned char* output_row, + int channels, int width) { + int x; + unsigned char* ip = input_row; + unsigned char* op = output_row; + + switch (channels) { + case 1: + // expand gray level to RGBX + for (x = 0; x < width; ++x) { + *op++ = *ip; + *op++ = *ip; + *op++ = *ip; + *op++ = 0xff; + ip++; + } + break; + + case 3: + // expand RGBA to RGBX + for (x = 0; x < width; ++x) { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + *op++ = 0xff; + } + break; + + case 4: + // copy RGBA to RGBX + memcpy(output_row, input_row, width*4); + break; + } +} + +int res_create_display_surface(const char* name, GRSurface** pSurface) { + GRSurface* surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; + unsigned char* p_row; + unsigned int y; + + *pSurface = NULL; + + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; + + surface = init_display_surface(width, height); + if (surface == NULL) { + result = -8; + goto exit; + } + +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + png_set_bgr(png_ptr); +#endif + + p_row = reinterpret_cast<unsigned char*>(malloc(width * 4)); + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, p_row, NULL); + transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width); + } + free(p_row); + + *pSurface = surface; + + exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (result < 0 && surface != NULL) free(surface); + return result; +} + +int res_create_multi_display_surface(const char* name, int* frames, GRSurface*** pSurface) { + GRSurface** surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; + int i; + png_textp text; + int num_text; + unsigned char* p_row; + unsigned int y; + + *pSurface = NULL; + *frames = -1; + + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; + + *frames = 1; + if (png_get_text(png_ptr, info_ptr, &text, &num_text)) { + for (i = 0; i < num_text; ++i) { + if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { + *frames = atoi(text[i].text); + break; + } + } + printf(" found frames = %d\n", *frames); + } + + if (height % *frames != 0) { + printf("bad height (%d) for frame count (%d)\n", height, *frames); + result = -9; + goto exit; + } + + surface = reinterpret_cast<GRSurface**>(malloc(*frames * sizeof(GRSurface*))); + if (surface == NULL) { + result = -8; + goto exit; + } + for (i = 0; i < *frames; ++i) { + surface[i] = init_display_surface(width, height / *frames); + if (surface[i] == NULL) { + result = -8; + goto exit; + } + } + +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + png_set_bgr(png_ptr); +#endif + + p_row = reinterpret_cast<unsigned char*>(malloc(width * 4)); + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, p_row, NULL); + int frame = y % *frames; + unsigned char* out_row = surface[frame]->data + + (y / *frames) * surface[frame]->row_bytes; + transform_rgb_to_draw(p_row, out_row, channels, width); + } + free(p_row); + + *pSurface = reinterpret_cast<GRSurface**>(surface); + +exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (result < 0) { + if (surface) { + for (i = 0; i < *frames; ++i) { + if (surface[i]) free(surface[i]); + } + free(surface); + } + } + return result; +} + +int res_create_alpha_surface(const char* name, GRSurface** pSurface) { + GRSurface* surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; + + *pSurface = NULL; + + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; + + if (channels != 1) { + result = -7; + goto exit; + } + + surface = malloc_surface(width * height); + if (surface == NULL) { + result = -8; + goto exit; + } + surface->width = width; + surface->height = height; + surface->row_bytes = width; + surface->pixel_bytes = 1; + +#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) + png_set_bgr(png_ptr); +#endif + + unsigned char* p_row; + unsigned int y; + for (y = 0; y < height; ++y) { + p_row = surface->data + y * surface->row_bytes; + png_read_row(png_ptr, p_row, NULL); + } + + *pSurface = surface; + + exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (result < 0 && surface != NULL) free(surface); + return result; +} + +static int matches_locale(const char* loc, const char* locale) { + if (locale == NULL) return 0; + + if (strcmp(loc, locale) == 0) return 1; + + // if loc does *not* have an underscore, and it matches the start + // of locale, and the next character in locale *is* an underscore, + // that's a match. For instance, loc == "en" matches locale == + // "en_US". + + int i; + for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i); + if (loc[i] == '_') return 0; + + return (strncmp(locale, loc, i) == 0 && locale[i] == '_'); +} + +int res_create_localized_alpha_surface(const char* name, + const char* locale, + GRSurface** pSurface) { + GRSurface* surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; + unsigned char* row; + png_uint_32 y; + + *pSurface = NULL; + + if (locale == NULL) { + surface = malloc_surface(0); + surface->width = 0; + surface->height = 0; + surface->row_bytes = 0; + surface->pixel_bytes = 1; + goto exit; + } + + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; + + if (channels != 1) { + result = -7; + goto exit; + } + + row = reinterpret_cast<unsigned char*>(malloc(width)); + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, row, NULL); + int w = (row[1] << 8) | row[0]; + int h = (row[3] << 8) | row[2]; + int len = row[4]; + char* loc = (char*)row+5; + + if (y+1+h >= height || matches_locale(loc, locale)) { + printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); + + surface = malloc_surface(w*h); + if (surface == NULL) { + result = -8; + goto exit; + } + surface->width = w; + surface->height = h; + surface->row_bytes = w; + surface->pixel_bytes = 1; + + int i; + for (i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row, NULL); + memcpy(surface->data + i*w, row, w); + } + + *pSurface = reinterpret_cast<GRSurface*>(surface); + break; + } else { + int i; + for (i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row, NULL); + } + } + } + +exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (result < 0 && surface != NULL) free(surface); + return result; +} + +void res_free_surface(GRSurface* surface) { + free(surface); +} diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk index 76b4024cc..2f30343d0 100644 --- a/minuitwrp/Android.mk +++ b/minuitwrp/Android.mk @@ -154,7 +154,7 @@ endif LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\" LOCAL_SHARED_LIBRARIES += libz libc libcutils libjpeg libpng -LOCAL_STATIC_LIBRARIES += libpixelflinger_static +LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libminuitwrp diff --git a/minuitwrp/events.c b/minuitwrp/events.c index 1d2af6b7c..4e10e72fd 100644 --- a/minuitwrp/events.c +++ b/minuitwrp/events.c @@ -24,6 +24,8 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include <stdio.h> +#include <string.h> #include "../common.h" diff --git a/minuitwrp/graphics.c b/minuitwrp/graphics.c index 04c41fb54..6dfbc23b5 100644 --- a/minuitwrp/graphics.c +++ b/minuitwrp/graphics.c @@ -21,6 +21,7 @@ #include <errno.h> #include <fcntl.h> #include <stdio.h> +#include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> diff --git a/minuitwrp/resources.c b/minuitwrp/resources.c index 3e3375d11..0e124608e 100644 --- a/minuitwrp/resources.c +++ b/minuitwrp/resources.c @@ -23,6 +23,7 @@ #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> +#include <string.h> #include <linux/fb.h> #include <linux/kd.h> diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c index fe2c880ac..97cb2e0ee 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.c @@ -85,7 +85,7 @@ dirCreateHierarchy(const char *path, int mode, c--; } if (c == cpath) { -//xxx test this path + //xxx test this path /* No directory component. Act like the path was empty. */ errno = ENOENT; @@ -206,7 +206,7 @@ dirUnlinkHierarchy(const char *path) /* recurse over components */ errno = 0; while ((de = readdir(dir)) != NULL) { -//TODO: don't blow the stack + //TODO: don't blow the stack char dn[PATH_MAX]; if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { continue; diff --git a/minzip/Hash.c b/minzip/Hash.c index 8c6ca9bc2..8f8ed68e5 100644 --- a/minzip/Hash.c +++ b/minzip/Hash.c @@ -140,7 +140,6 @@ static bool resizeHash(HashTable* pHashTable, int newSize) int i; assert(countTombStones(pHashTable) == pHashTable->numDeadEntries); - //LOGI("before: dead=%d\n", pHashTable->numDeadEntries); pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable)); if (pNewEntries == NULL) @@ -196,7 +195,6 @@ void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item (*cmpFunc)(pEntry->data, item) == 0) { /* match */ - //LOGD("+++ match on entry %d\n", pEntry - pHashTable->pEntries); break; } @@ -206,8 +204,6 @@ void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item break; /* edge case - single-entry table */ pEntry = pHashTable->pEntries; } - - //LOGI("+++ look probing %d...\n", pEntry - pHashTable->pEntries); } if (pEntry->data == NULL) { @@ -228,10 +224,6 @@ void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item abort(); } /* note "pEntry" is now invalid */ - } else { - //LOGW("okay %d/%d/%d\n", - // pHashTable->numEntries, pHashTable->tableSize, - // (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM); } /* full table is bad -- search for nonexistent never halts */ @@ -264,7 +256,6 @@ bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item) pEnd = &pHashTable->pEntries[pHashTable->tableSize]; while (pEntry->data != NULL) { if (pEntry->data == item) { - //LOGI("+++ stepping on entry %d\n", pEntry - pHashTable->pEntries); pEntry->data = HASH_TOMBSTONE; pHashTable->numEntries--; pHashTable->numDeadEntries++; @@ -277,8 +268,6 @@ bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item) break; /* edge case - single-entry table */ pEntry = pHashTable->pEntries; } - - //LOGI("+++ del probing %d...\n", pEntry - pHashTable->pEntries); } return false; diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index aac162d3a..1858cd515 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -27,11 +27,13 @@ static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) assert(start_ != NULL); assert(length_ != NULL); - start = lseek(fd, 0L, SEEK_CUR); - end = lseek(fd, 0L, SEEK_END); - (void) lseek(fd, start, SEEK_SET); + // TODO: isn't start always 0 for the single call site? just use fstat instead? - if (start == (off_t) -1 || end == (off_t) -1) { + start = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_CUR)); + end = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_END)); + + if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1 || + start == (off_t) -1 || end == (off_t) -1) { LOGE("could not determine length of file\n"); return -1; } diff --git a/minzip/Zip.c b/minzip/Zip.c index 5070104d3..40712e03a 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -327,10 +327,6 @@ static bool parseZipArchive(ZipArchive* pArchive) #else pEntry = &pArchive->pEntries[i]; #endif - - //LOGI("%d: localHdr=%d fnl=%d el=%d cl=%d\n", - // i, localHdrOffset, fileNameLen, extraLen, commentLen); - pEntry->fileNameLen = fileNameLen; pEntry->fileName = fileName; @@ -488,7 +484,7 @@ const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive, /* * Return true if the entry is a symbolic link. */ -bool mzIsZipEntrySymlink(const ZipEntry* pEntry) +static bool mzIsZipEntrySymlink(const ZipEntry* pEntry) { if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) { return S_ISLNK(pEntry->externalFileAttributes >> 16); @@ -632,30 +628,6 @@ static bool crcProcessFunction(const unsigned char *data, int dataLen, return true; } -/* - * Check the CRC on this entry; return true if it is correct. - * May do other internal checks as well. - */ -bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry) -{ - unsigned long crc; - bool ret; - - crc = crc32(0L, Z_NULL, 0); - ret = mzProcessZipEntryContents(pArchive, pEntry, crcProcessFunction, - (void *)&crc); - if (!ret) { - LOGE("Can't calculate CRC for entry\n"); - return false; - } - if (crc != (unsigned long)pEntry->crc32) { - LOGW("CRC for entry %.*s (0x%08lx) != expected (0x%08lx)\n", - pEntry->fileNameLen, pEntry->fileName, crc, pEntry->crc32); - return false; - } - return true; -} - typedef struct { char *buf; int bufLen; @@ -703,13 +675,11 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen, } ssize_t soFar = 0; while (true) { - ssize_t n = write(fd, data+soFar, dataLen-soFar); + ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar)); if (n <= 0) { LOGE("Error writing %zd bytes from zip file from %p: %s\n", dataLen-soFar, data+soFar, strerror(errno)); - if (errno != EINTR) { - return false; - } + return false; } else if (n > 0) { soFar += n; if (soFar == dataLen) return true; @@ -737,23 +707,6 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, return true; } -/* - * Obtain a pointer to the in-memory representation of a stored entry. - */ -bool mzGetStoredEntry(const ZipArchive *pArchive, - const ZipEntry *pEntry, unsigned char **addr, size_t *length) -{ - if (pEntry->compression != STORED) { - LOGE("Can't getStoredEntry for '%s'; not stored\n", - pEntry->fileName); - return false; - } - - *addr = pArchive->addr + pEntry->offset; - *length = pEntry->uncompLen; - return true; -} - typedef struct { unsigned char* buffer; long len; @@ -873,7 +826,7 @@ static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry) */ bool mzExtractRecursive(const ZipArchive *pArchive, const char *zipDir, const char *targetDir, - int flags, const struct utimbuf *timestamp, + const struct utimbuf *timestamp, void (*callback)(const char *fn, void *), void *cookie, struct selabel_handle *sehnd) { @@ -923,8 +876,8 @@ bool mzExtractRecursive(const ZipArchive *pArchive, /* Walk through the entries and extract anything whose path begins * with zpath. -//TODO: since the entries are sorted, binary search for the first match -// and stop after the first non-match. + //TODO: since the entries are sorted, binary search for the first match + // and stop after the first non-match. */ unsigned int i; bool seenMatch = false; @@ -933,10 +886,10 @@ bool mzExtractRecursive(const ZipArchive *pArchive, for (i = 0; i < pArchive->numEntries; i++) { ZipEntry *pEntry = pArchive->pEntries + i; if (pEntry->fileNameLen < zipDirLen) { -//TODO: look out for a single empty directory entry that matches zpath, but -// missing the trailing slash. Most zip files seem to include -// the trailing slash, but I think it's legal to leave it off. -// e.g., zpath "a/b/", entry "a/b", with no children of the entry. + //TODO: look out for a single empty directory entry that matches zpath, but + // missing the trailing slash. Most zip files seem to include + // the trailing slash, but I think it's legal to leave it off. + // e.g., zpath "a/b/", entry "a/b", with no children of the entry. /* No chance of matching. */ #if SORT_ENTRIES @@ -977,30 +930,19 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } - /* With DRY_RUN set, invoke the callback but don't do anything else. - */ - if (flags & MZ_EXTRACT_DRY_RUN) { - if (callback != NULL) callback(targetFile, cookie); - continue; - } - - /* Create the file or directory. - */ #define UNZIP_DIRMODE 0755 #define UNZIP_FILEMODE 0644 - if (pEntry->fileName[pEntry->fileNameLen-1] == '/') { - if (!(flags & MZ_EXTRACT_FILES_ONLY)) { - int ret = dirCreateHierarchy( - targetFile, UNZIP_DIRMODE, timestamp, false, sehnd); - if (ret != 0) { - LOGE("Can't create containing directory for \"%s\": %s\n", - targetFile, strerror(errno)); - ok = false; - break; - } - LOGD("Extracted dir \"%s\"\n", targetFile); - } - } else { + /* + * Create the file or directory. We ignore directory entries + * because we recursively create paths to each file entry we encounter + * in the zip archive anyway. + * + * NOTE: A "directory entry" in a zip archive is just a zero length + * entry that ends in a "/". They're not mandatory and many tools get + * rid of them. We need to process them only if we want to preserve + * empty directories from the archive. + */ + if (pEntry->fileName[pEntry->fileNameLen-1] != '/') { /* This is not a directory. First, make sure that * the containing directory exists. */ @@ -1013,91 +955,62 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } - /* With FILES_ONLY set, we need to ignore metadata entirely, - * so treat symlinks as regular files. + /* + * The entry is a regular file or a symlink. Open the target for writing. + * + * TODO: This behavior for symlinks seems rather bizarre. For a + * symlink foo/bar/baz -> foo/tar/taz, we will create a file called + * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We + * warn about this for now and preserve older behavior. */ - if (!(flags & MZ_EXTRACT_FILES_ONLY) && mzIsZipEntrySymlink(pEntry)) { - /* The entry is a symbolic link. - * The relative target of the symlink is in the - * data section of this entry. - */ - if (pEntry->uncompLen == 0) { - LOGE("Symlink entry \"%s\" has no target\n", - targetFile); - ok = false; - break; - } - char *linkTarget = malloc(pEntry->uncompLen + 1); - if (linkTarget == NULL) { - ok = false; - break; - } - ok = mzReadZipEntry(pArchive, pEntry, linkTarget, - pEntry->uncompLen); - if (!ok) { - LOGE("Can't read symlink target for \"%s\"\n", - targetFile); - free(linkTarget); - break; - } - linkTarget[pEntry->uncompLen] = '\0'; - - /* Make the link. - */ - ret = symlink(linkTarget, targetFile); - if (ret != 0) { - LOGE("Can't symlink \"%s\" to \"%s\": %s\n", - targetFile, linkTarget, strerror(errno)); - free(linkTarget); - ok = false; - break; - } - LOGD("Extracted symlink \"%s\" -> \"%s\"\n", - targetFile, linkTarget); - free(linkTarget); - } else { - /* The entry is a regular file. - * Open the target for writing. - */ - - char *secontext = NULL; + if (mzIsZipEntrySymlink(pEntry)) { + LOGE("Symlink entry \"%.*s\" will be output as a regular file.", + pEntry->fileNameLen, pEntry->fileName); + } - if (sehnd) { - selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); - setfscreatecon(secontext); - } + char *secontext = NULL; - int fd = creat(targetFile, UNZIP_FILEMODE); + if (sehnd) { + selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); + setfscreatecon(secontext); + } - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } + int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC, + UNZIP_FILEMODE); - if (fd < 0) { - LOGE("Can't create target file \"%s\": %s\n", - targetFile, strerror(errno)); - ok = false; - break; - } + if (secontext) { + freecon(secontext); + setfscreatecon(NULL); + } - bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); - close(fd); - if (!ok) { - LOGE("Error extracting \"%s\"\n", targetFile); - ok = false; - break; - } + if (fd < 0) { + LOGE("Can't create target file \"%s\": %s\n", + targetFile, strerror(errno)); + ok = false; + break; + } - if (timestamp != NULL && utime(targetFile, timestamp)) { - LOGE("Error touching \"%s\"\n", targetFile); - ok = false; - break; - } + bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); + if (ok) { + ok = (fsync(fd) == 0); + } + if (close(fd) != 0) { + ok = false; + } + if (!ok) { + LOGE("Error extracting \"%s\"\n", targetFile); + ok = false; + break; + } - LOGV("Extracted file \"%s\"\n", targetFile); - ++extractCount; + if (timestamp != NULL && utime(targetFile, timestamp)) { + LOGE("Error touching \"%s\"\n", targetFile); + ok = false; + break; } + + LOGV("Extracted file \"%s\"\n", targetFile); + ++extractCount; } if (callback != NULL) callback(targetFile, cookie); diff --git a/minzip/Zip.h b/minzip/Zip.h index 2054b38a4..86d8db597 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -85,56 +85,12 @@ void mzCloseZipArchive(ZipArchive* pArchive); const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive, const char* entryName); -/* - * Get the number of entries in the Zip archive. - */ -INLINE unsigned int mzZipEntryCount(const ZipArchive* pArchive) { - return pArchive->numEntries; -} - -/* - * Get an entry by index. Returns NULL if the index is out-of-bounds. - */ -INLINE const ZipEntry* -mzGetZipEntryAt(const ZipArchive* pArchive, unsigned int index) -{ - if (index < pArchive->numEntries) { - return pArchive->pEntries + index; - } - return NULL; -} - -/* - * Get the index number of an entry in the archive. - */ -INLINE unsigned int -mzGetZipEntryIndex(const ZipArchive *pArchive, const ZipEntry *pEntry) { - return pEntry - pArchive->pEntries; -} - -/* - * Simple accessors. - */ -INLINE UnterminatedString mzGetZipEntryFileName(const ZipEntry* pEntry) { - UnterminatedString ret; - ret.str = pEntry->fileName; - ret.len = pEntry->fileNameLen; - return ret; -} INLINE long mzGetZipEntryOffset(const ZipEntry* pEntry) { return pEntry->offset; } INLINE long mzGetZipEntryUncompLen(const ZipEntry* pEntry) { return pEntry->uncompLen; } -INLINE long mzGetZipEntryModTime(const ZipEntry* pEntry) { - return pEntry->modTime; -} -INLINE long mzGetZipEntryCrc32(const ZipEntry* pEntry) { - return pEntry->crc32; -} -bool mzIsZipEntrySymlink(const ZipEntry* pEntry); - /* * Type definition for the callback function used by @@ -164,12 +120,6 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, char* buf, int bufLen); /* - * Check the CRC on this entry; return true if it is correct. - * May do other internal checks as well. - */ -bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry); - -/* * Inflate and write an entry to a file. */ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, @@ -183,20 +133,12 @@ bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, const ZipEntry *pEntry, unsigned char* buffer); /* - * Return a pointer and length for a given entry. The returned region - * should be valid until pArchive is closed, and should be treated as - * read-only. - * - * Only makes sense for entries which are stored (ie, not compressed). - * No guarantees are made regarding alignment of the returned pointer. - */ -bool mzGetStoredEntry(const ZipArchive *pArchive, - const ZipEntry* pEntry, unsigned char **addr, size_t *length); - -/* - * Inflate all entries under zipDir to the directory specified by + * Inflate all files under zipDir to the directory specified by * targetDir, which must exist and be a writable directory. * + * Directory entries and symlinks are not extracted. + * + * * The immediate children of zipDir will become the immediate * children of targetDir; e.g., if the archive contains the entries * @@ -211,21 +153,15 @@ bool mzGetStoredEntry(const ZipArchive *pArchive, * /tmp/two * /tmp/d/three * - * flags is zero or more of the following: - * - * MZ_EXTRACT_FILES_ONLY - only unpack files, not directories or symlinks - * MZ_EXTRACT_DRY_RUN - don't do anything, but do invoke the callback - * * If timestamp is non-NULL, file timestamps will be set accordingly. * * If callback is non-NULL, it will be invoked with each unpacked file. * * Returns true on success, false on failure. */ -enum { MZ_EXTRACT_FILES_ONLY = 1, MZ_EXTRACT_DRY_RUN = 2 }; bool mzExtractRecursive(const ZipArchive *pArchive, const char *zipDir, const char *targetDir, - int flags, const struct utimbuf *timestamp, + const struct utimbuf *timestamp, void (*callback)(const char *fn, void*), void *cookie, struct selabel_handle *sehnd); diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c index 5657dfc82..36ffa1314 100644 --- a/mtdutils/flash_image.c +++ b/mtdutils/flash_image.c @@ -72,7 +72,7 @@ int main(int argc, char **argv) { if (fd < 0) die("error opening %s", argv[2]); char header[HEADER_SIZE]; - int headerlen = read(fd, header, sizeof(header)); + int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header))); if (headerlen <= 0) die("error reading %s header", argv[2]); MtdReadContext *in = mtd_read_partition(partition); @@ -104,7 +104,7 @@ int main(int argc, char **argv) { if (wrote != headerlen) die("error writing %s", argv[1]); int len; - while ((len = read(fd, buf, sizeof(buf))) > 0) { + while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) { wrote = mtd_write_data(out, buf, len); if (wrote != len) die("error writing %s", argv[1]); } @@ -125,13 +125,13 @@ int main(int argc, char **argv) { if (mtd_partition_info(partition, NULL, &block_size, NULL)) die("error getting %s block size", argv[1]); - if (lseek(fd, headerlen, SEEK_SET) != headerlen) + if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen) die("error rewinding %s", argv[2]); int left = block_size - headerlen; while (left < 0) left += block_size; while (left > 0) { - len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left); + len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left)); if (len <= 0) die("error reading %s", argv[2]); if (mtd_write_data(out, buf, len) != len) die("error writing %s", argv[1]); diff --git a/mtdutils/mounts.c b/mtdutils/mounts.c index c90fc8acf..6a9b03d30 100644 --- a/mtdutils/mounts.c +++ b/mtdutils/mounts.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <mntent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -59,10 +60,8 @@ free_volume_internals(const MountedVolume *volume, int zero) int scan_mounted_volumes() { - char buf[2048]; - const char *bufp; - int fd; - ssize_t nbytes; + FILE* fp; + struct mntent* mentry; if (g_mounts_state.volumes == NULL) { const int numv = 32; @@ -84,80 +83,20 @@ scan_mounted_volumes() } g_mounts_state.volume_count = 0; - /* Open and read the file contents. - */ - fd = open(PROC_MOUNTS_FILENAME, O_RDONLY); - if (fd < 0) { - goto bail; - } - nbytes = read(fd, buf, sizeof(buf) - 1); - close(fd); - if (nbytes < 0) { - goto bail; + /* Open and read mount table entries. */ + fp = setmntent(PROC_MOUNTS_FILENAME, "r"); + if (fp == NULL) { + return -1; } - buf[nbytes] = '\0'; - - /* Parse the contents of the file, which looks like: - * - * # cat /proc/mounts - * rootfs / rootfs rw 0 0 - * /dev/pts /dev/pts devpts rw 0 0 - * /proc /proc proc rw 0 0 - * /sys /sys sysfs rw 0 0 - * /dev/block/mtdblock4 /system yaffs2 rw,nodev,noatime,nodiratime 0 0 - * /dev/block/mtdblock5 /data yaffs2 rw,nodev,noatime,nodiratime 0 0 - * /dev/block/mmcblk0p1 /sdcard vfat rw,sync,dirsync,fmask=0000,dmask=0000,codepage=cp437,iocharset=iso8859-1,utf8 0 0 - * - * The zeroes at the end are dummy placeholder fields to make the - * output match Linux's /etc/mtab, but don't represent anything here. - */ - bufp = buf; - while (nbytes > 0) { - char device[64]; - char mount_point[64]; - char filesystem[64]; - char flags[128]; - int matches; - - /* %as is a gnu extension that malloc()s a string for each field. - */ - matches = sscanf(bufp, "%63s %63s %63s %127s", - device, mount_point, filesystem, flags); - - if (matches == 4) { - device[sizeof(device)-1] = '\0'; - mount_point[sizeof(mount_point)-1] = '\0'; - filesystem[sizeof(filesystem)-1] = '\0'; - flags[sizeof(flags)-1] = '\0'; - - MountedVolume *v = - &g_mounts_state.volumes[g_mounts_state.volume_count++]; - v->device = strdup(device); - v->mount_point = strdup(mount_point); - v->filesystem = strdup(filesystem); - v->flags = strdup(flags); - } else { -printf("matches was %d on <<%.40s>>\n", matches, bufp); - } - - /* Eat the line. - */ - while (nbytes > 0 && *bufp != '\n') { - bufp++; - nbytes--; - } - if (nbytes > 0) { - bufp++; - nbytes--; - } + while ((mentry = getmntent(fp)) != NULL) { + MountedVolume* v = &g_mounts_state.volumes[g_mounts_state.volume_count++]; + v->device = strdup(mentry->mnt_fsname); + v->mount_point = strdup(mentry->mnt_dir); + v->filesystem = strdup(mentry->mnt_type); + v->flags = strdup(mentry->mnt_opts); } - + endmntent(fp); return 0; - -bail: -//TODO: free the strings we've allocated. - g_mounts_state.volume_count = 0; - return -1; } const MountedVolume * diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 158c88058..14be57f1e 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -105,7 +105,7 @@ mtd_scan_partitions() if (fd < 0) { goto bail; } - nbytes = read(fd, buf, sizeof(buf) - 1); + nbytes = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf) - 1)); close(fd); if (nbytes < 0) { goto bail; @@ -276,12 +276,6 @@ MtdReadContext *mtd_read_partition(const MtdPartition *partition) return ctx; } -// Seeks to a location in the partition. Don't mix with reads of -// anything other than whole blocks; unpredictable things will result. -void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset) { - lseek64(ctx->fd, offset, SEEK_SET); -} - static int read_block(const MtdPartition *partition, int fd, char *data) { struct mtd_ecc_stats before, after; @@ -290,13 +284,18 @@ static int read_block(const MtdPartition *partition, int fd, char *data) return -1; } - loff_t pos = lseek64(fd, 0, SEEK_CUR); + loff_t pos = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR)); + if (pos == -1) { + printf("mtd: read_block: couldn't SEEK_CUR: %s\n", strerror(errno)); + return -1; + } ssize_t size = partition->erase_size; int mgbb; while (pos + size <= (int) partition->size) { - if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) { + if (TEMP_FAILURE_RETRY(lseek64(fd, pos, SEEK_SET)) != pos || + TEMP_FAILURE_RETRY(read(fd, data, size)) != size) { printf("mtd: read error at 0x%08llx (%s)\n", pos, strerror(errno)); } else if (ioctl(fd, ECCGETSTATS, &after)) { @@ -310,8 +309,8 @@ static int read_block(const MtdPartition *partition, int fd, char *data) memcpy(&before, &after, sizeof(struct mtd_ecc_stats)); } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) { fprintf(stderr, - "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n", - mgbb, pos, errno); + "mtd: MEMGETBADBLOCK returned %d at 0x%08llx: %s\n", + mgbb, pos, strerror(errno)); } else { return 0; // Success! } @@ -406,8 +405,11 @@ static int write_block(MtdWriteContext *ctx, const char *data) const MtdPartition *partition = ctx->partition; int fd = ctx->fd; - off_t pos = lseek(fd, 0, SEEK_CUR); - if (pos == (off_t) -1) return 1; + off_t pos = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_CUR)); + if (pos == (off_t) -1) { + printf("mtd: write_block: couldn't SEEK_CUR: %s\n", strerror(errno)); + return -1; + } ssize_t size = partition->erase_size; while (pos + size <= (int) partition->size) { @@ -416,8 +418,8 @@ static int write_block(MtdWriteContext *ctx, const char *data) if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) { add_bad_block_offset(ctx, pos); fprintf(stderr, - "mtd: not writing bad block at 0x%08lx (ret %d errno %d)\n", - pos, ret, errno); + "mtd: not writing bad block at 0x%08lx (ret %d): %s\n", + pos, ret, strerror(errno)); pos += partition->erase_size; continue; // Don't try to erase known factory-bad blocks. } @@ -440,15 +442,15 @@ static int write_block(MtdWriteContext *ctx, const char *data) continue; } #endif - if (lseek(fd, pos, SEEK_SET) != pos || - write(fd, data, size) != size) { + if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos || + TEMP_FAILURE_RETRY(write(fd, data, size)) != size) { printf("mtd: write error at 0x%08lx (%s)\n", pos, strerror(errno)); } char verify[size]; - if (lseek(fd, pos, SEEK_SET) != pos || - read(fd, verify, size) != size) { + if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos || + TEMP_FAILURE_RETRY(read(fd, verify, size)) != size) { printf("mtd: re-read error at 0x%08lx (%s)\n", pos, strerror(errno)); continue; @@ -522,8 +524,11 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) ctx->stored = 0; } - off_t pos = lseek(ctx->fd, 0, SEEK_CUR); - if ((off_t) pos == (off_t) -1) return pos; + off_t pos = TEMP_FAILURE_RETRY(lseek(ctx->fd, 0, SEEK_CUR)); + if ((off_t) pos == (off_t) -1) { + printf("mtd_erase_blocks: couldn't SEEK_CUR: %s\n", strerror(errno)); + return -1; + } const int total = (ctx->partition->size - pos) / ctx->partition->erase_size; if (blocks < 0) blocks = total; diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h index c57d45d50..6cbf37eec 100644 --- a/mtdutils/mtdutils.h +++ b/mtdutils/mtdutils.h @@ -45,12 +45,10 @@ typedef struct MtdWriteContext MtdWriteContext; MtdReadContext *mtd_read_partition(const MtdPartition *); ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len); void mtd_read_close(MtdReadContext *); -void mtd_read_skip_to(const MtdReadContext *, size_t offset); MtdWriteContext *mtd_write_partition(const MtdPartition *); ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len); off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ -off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos); int mtd_write_close(MtdWriteContext *); struct MtdPartition { diff --git a/mtp/Android.mk b/mtp/Android.mk index 283a283dd..204042557 100755 --- a/mtp/Android.mk +++ b/mtp/Android.mk @@ -29,7 +29,13 @@ LOCAL_SRC_FILES = \ twrpMtp.cpp \ mtp_MtpDatabase.cpp \ node.cpp -LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libstlport libdl libcutils libutils libaosprecovery +LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery + +ifneq ($(wildcard external/stlport/Android.mk),) + LOCAL_SHARED_LIBRARIES += libstlport +else + LOCAL_SHARED_LIBRARIES += libc++ +endif ifneq ($(TW_MTP_DEVICE),) LOCAL_CFLAGS += -DUSB_MTP_DEVICE=$(TW_MTP_DEVICE) @@ -44,7 +50,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := twrpmtp LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -DTWRPMTP -LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic external/stlport/stlport frameworks/base/include system/core/include bionic/libc/private/ +LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/ LOCAL_SRC_FILES = \ btree.cpp \ MtpDataPacket.cpp \ @@ -66,5 +72,5 @@ LOCAL_SRC_FILES = \ twrpMtp.cpp \ mtp_MtpDatabase.cpp \ node.cpp -LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libstlport libdl libcutils libutils libaosprecovery +LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery include $(BUILD_EXECUTABLE) diff --git a/openaes/src/ftime.h b/openaes/src/ftime.h index 963d7c176..80fbab769 100644 --- a/openaes/src/ftime.h +++ b/openaes/src/ftime.h @@ -33,3 +33,5 @@ struct timeb { short timezone; short dstflag; }; + +int ftime(struct timeb* tb); diff --git a/openaes/src/oaes_lib.c b/openaes/src/oaes_lib.c index 6e20b478f..0cc03946e 100644 --- a/openaes/src/oaes_lib.c +++ b/openaes/src/oaes_lib.c @@ -37,6 +37,7 @@ static const char _NR[] = { #include "ftime.h" #include <malloc.h> #include <string.h> +#include <unistd.h> #ifdef WIN32 #include <process.h> diff --git a/partitionmanager.cpp b/partitionmanager.cpp index bf4b38247..f25443915 100644 --- a/partitionmanager.cpp +++ b/partitionmanager.cpp @@ -309,7 +309,7 @@ void TWPartitionManager::Output_Partition(TWPartition* Part) { if (!Part->Fstab_File_System.empty()) printf(" Fstab_File_System: %s\n", Part->Fstab_File_System.c_str()); if (Part->Format_Block_Size != 0) - printf(" Format_Block_Size: %i\n", Part->Format_Block_Size); + printf(" Format_Block_Size: %lu\n", Part->Format_Block_Size); if (!Part->MTD_Name.empty()) printf(" MTD_Name: %s\n", Part->MTD_Name.c_str()); string back_meth = Part->Backup_Method_By_Name(); diff --git a/pigz/yarn.c b/pigz/yarn.c index 5e557f3f1..74db3fb4e 100644 --- a/pigz/yarn.c +++ b/pigz/yarn.c @@ -31,6 +31,7 @@ #include <stdio.h> /* fprintf(), stderr */ #include <stdlib.h> /* exit(), malloc(), free(), NULL */ #include <pthread.h> /* pthread_t, pthread_create(), pthread_join(), */ +#include <signal.h> /* sigaction, SIGUSR1 */ /* pthread_attr_t, pthread_attr_init(), pthread_attr_destroy(), PTHREAD_CREATE_JOINABLE, pthread_attr_setdetachstate(), pthread_self(), pthread_equal(), @@ -39,6 +40,7 @@ pthread_cond_t, PTHREAD_COND_INITIALIZER, pthread_cond_init(), pthread_cond_broadcast(), pthread_cond_wait(), pthread_cond_destroy() */ #include <errno.h> /* ENOMEM, EAGAIN, EINVAL */ +#include <string.h> /* memset */ /* interface definition */ #include "yarn.h" @@ -50,6 +52,11 @@ char *yarn_prefix = "yarn"; void (*yarn_abort)(int) = NULL; +void thread_exit_handler(int sig) +{ + printf("this signal is %d \n", sig); + pthread_exit(0); +} /* immediately exit -- use for errors that shouldn't ever happen */ local void fail(int err) @@ -263,6 +270,13 @@ thread *launch(void (*probe)(void *), void *payload) thread *th; struct capsule *capsule; pthread_attr_t attr; + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = thread_exit_handler; + ret = sigaction(SIGUSR1,&actions,NULL); /* construct the requested call and argument for the ignition() routine (allocated instead of automatic so that we're sure this will still be @@ -369,7 +383,7 @@ void destruct(thread *off_course) { int ret; - if ((ret = pthread_cancel(off_course->id)) != 0) + if ((ret = pthread_kill(off_course->id, SIGUSR1)) != 0) fail(ret); join(off_course); } diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk index a3cbd7a0e..25c31478e 100644 --- a/prebuilt/Android.mk +++ b/prebuilt/Android.mk @@ -18,6 +18,9 @@ ifneq ($(TW_USE_TOOLBOX), true) else RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/sh RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so + ifneq (,$(filter $(PLATFORM_SDK_VERSION), 23)) + RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/toybox + endif endif RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/pigz RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/dosfsck @@ -41,7 +44,6 @@ RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrecovery.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libusbhost.so -RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgccdemangle.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdl.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_com_err.so @@ -73,8 +75,12 @@ RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libblkid.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmmcutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbmlutils.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libflashutils.so -RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstlport.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libfusesideload.so +ifeq (,$(filter $(PLATFORM_SDK_VERSION), 23)) + # These libraries are no longer present in M + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstlport.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgccdemangle.so +endif ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) # libraries from lollipop RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbacktrace.so @@ -83,8 +89,18 @@ ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) # Dynamically loaded by lollipop libc and may prevent unmounting system if it is not present in sbin RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libnetd_client.so else - # Not available in lollipop - RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcorkscrew.so + ifneq (,$(filter $(PLATFORM_SDK_VERSION), 23)) + # Android M libraries + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbacktrace.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libunwind.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbase.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc++.so + # Dynamically loaded by libc and may prevent unmounting system if it is not present in sbin + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libnetd_client.so + else + # Not available in lollipop + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcorkscrew.so + endif endif RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmincrypttwrp.so RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/toolbox @@ -183,6 +199,15 @@ LOCAL_GENERATED_SOURCES := $(GEN) LOCAL_SRC_FILES := teamwin $(GEN) include $(BUILD_PREBUILT) +#permissive.sh +include $(CLEAR_VARS) +LOCAL_MODULE := permissive.sh +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) + #fix_permissions include $(CLEAR_VARS) LOCAL_MODULE := fix_permissions.sh diff --git a/prebuilt/permissive.sh b/prebuilt/permissive.sh new file mode 100755 index 000000000..f1046f94e --- /dev/null +++ b/prebuilt/permissive.sh @@ -0,0 +1,8 @@ +#!/sbin/sh + +# We use this shell script because the script will follow symlinks and +# different trees will use different binaries to supply the setenforce +# tool. Before M we use toolbox, M and beyond will use toybox. The init +# binary and init.rc will not follow symlinks. + +setenforce 0 diff --git a/prebuilt/relink.sh b/prebuilt/relink.sh index 48be3fcb9..2dd56147e 100755 --- a/prebuilt/relink.sh +++ b/prebuilt/relink.sh @@ -11,7 +11,7 @@ process_file() cp -f -p $src $dst fi - sed "s|/system/bin/linker64\x0|/sbin/linker64\x0\x0\x0\x0\x0\x0\x0|g" $src | sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" > $dst + sed "s|/system/bin/linker64\x0|/sbin/linker64\x0\x0\x0\x0\x0\x0\x0|g" $src | sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/lib64\x0|/sbin\x0\x0\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/lib\x0|/sbin\x0\x0\x0\x0\x0\x0\x0|g" > $dst if [ $1 == $(dirname $2) ]; then rm -f $src diff --git a/recovery.cpp b/recovery.cpp index 1d22b248a..b7a545898 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -31,6 +31,9 @@ #include <time.h> #include <unistd.h> +#include <base/file.h> +#include <base/stringprintf.h> + #include "bootloader.h" #include "common.h" #include "cutils/properties.h" @@ -43,20 +46,20 @@ #include "screen_ui.h" #include "device.h" #include "adb_install.h" -extern "C" { -#include "minadbd/adb.h" +#include "adb.h" #include "fuse_sideload.h" #include "fuse_sdcard_provider.h" -} struct selabel_handle *sehandle; static const struct option OPTIONS[] = { - { "send_intent", required_argument, NULL, 's' }, + { "send_intent", required_argument, NULL, 'i' }, { "update_package", required_argument, NULL, 'u' }, { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, { "show_text", no_argument, NULL, 't' }, + { "sideload", no_argument, NULL, 's' }, + { "sideload_auto_reboot", no_argument, NULL, 'a' }, { "just_exit", no_argument, NULL, 'x' }, { "locale", required_argument, NULL, 'l' }, { "stages", required_argument, NULL, 'g' }, @@ -65,8 +68,6 @@ static const struct option OPTIONS[] = { { NULL, 0, NULL, 0 }, }; -#define LAST_LOG_FILE "/cache/recovery/last_log" - static const char *CACHE_LOG_DIR = "/cache/recovery"; static const char *COMMAND_FILE = "/cache/recovery/command"; static const char *INTENT_FILE = "/cache/recovery/intent"; @@ -78,18 +79,14 @@ 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 +static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; +static const int KEEP_LOG_COUNT = 10; RecoveryUI* ui = NULL; char* locale = NULL; -char recovery_version[PROPERTY_VALUE_MAX+1]; char* stage = NULL; char* reason = NULL; +bool modified_flash = false; /* * The recovery tool communicates with the main system through /cache files. @@ -169,6 +166,11 @@ fopen_path(const char *path, const char *mode) { return fp; } +bool is_ro_debuggable() { + char value[PROPERTY_VALUE_MAX+1]; + return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1'); +} + static void redirect_stdio(const char* filename) { // If these fail, there's not really anywhere to complain... freopen(filename, "a", stdout); setbuf(stdout, NULL); @@ -265,87 +267,89 @@ 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); +// Read from kernel log into buffer and write out to file. +static void save_kernel_log(const char* destination) { + int 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); + LOGE("Error getting klog size: %s\n", strerror(errno)); 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); + std::string buffer(klog_buf_len, 0); + int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len); + if (n == -1) { + LOGE("Error in reading klog: %s\n", strerror(errno)); return; } - fwrite(buffer, n, 1, log); - check_and_fclose(log, destination); - free(buffer); + buffer.resize(n); + android::base::WriteStringToFile(buffer, destination); } // How much of the temp log we have copied to the copy in cache. static long tmplog_offset = 0; -static void -copy_log_file(const char* source, const char* destination, int append) { - FILE *log = fopen_path(destination, append ? "a" : "w"); - if (log == NULL) { +static void copy_log_file(const char* source, const char* destination, bool append) { + FILE* dest_fp = fopen_path(destination, append ? "a" : "w"); + if (dest_fp == nullptr) { LOGE("Can't open %s\n", destination); } else { - FILE *tmplog = fopen(source, "r"); - if (tmplog != NULL) { + FILE* source_fp = fopen(source, "r"); + if (source_fp != nullptr) { if (append) { - fseek(tmplog, tmplog_offset, SEEK_SET); // Since last write + fseek(source_fp, tmplog_offset, SEEK_SET); // Since last write } char buf[4096]; - while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log); + size_t bytes; + while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { + fwrite(buf, 1, bytes, dest_fp); + } if (append) { - tmplog_offset = ftell(tmplog); + tmplog_offset = ftell(source_fp); } - check_and_fclose(tmplog, source); + check_and_fclose(source_fp, source); } - check_and_fclose(log, destination); + check_and_fclose(dest_fp, destination); } } -// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max -// Overwrites any existing last_log.$max. -static void -rotate_last_logs(int max) { - char oldfn[256]; - char newfn[256]; - - int i; - for (i = max-1; i >= 0; --i) { - snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i); - snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1); - // ignore errors - rename(oldfn, newfn); +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. +// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. +// Overwrite any existing last_log.$max and last_kmsg.$max. +static void rotate_logs(int max) { + // Logs should only be rotated once. + static bool rotated = false; + if (rotated) { + return; + } + rotated = true; + ensure_path_mounted(LAST_LOG_FILE); + ensure_path_mounted(LAST_KMSG_FILE); + + for (int i = max-1; i >= 0; --i) { + std::string old_log = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d", + LAST_LOG_FILE, i); + std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1); + // Ignore errors if old_log doesn't exist. + rename(old_log.c_str(), new_log.c_str()); + + std::string old_kmsg = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d", + LAST_KMSG_FILE, i); + std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1); + rename(old_kmsg.c_str(), new_kmsg.c_str()); } } -static void -copy_logs() { +static void copy_logs() { + // We only rotate and record the log of the current session if there are + // actual attempts to modify the flash, such as wipes, installs from BCB + // or menu selections. This is to avoid unnecessary rotation (and + // possible deletion) of log files, if it does not do anything loggable. + if (!modified_flash) { + return; + } + + rotate_logs(KEEP_LOG_COUNT); + // Copy logs to cache so the system can find out what happened. copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false); @@ -413,8 +417,7 @@ typedef struct _saved_log_file { struct _saved_log_file* next; } saved_log_file; -static int -erase_volume(const char *volume) { +static bool erase_volume(const char* volume) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); ui->SetBackground(RecoveryUI::ERASING); @@ -423,9 +426,10 @@ erase_volume(const char *volume) { saved_log_file* head = NULL; if (is_cache) { - // If we're reformatting /cache, we load any - // "/cache/recovery/last*" files into memory, so we can restore - // them after the reformat. + // If we're reformatting /cache, we load any past logs + // (i.e. "/cache/recovery/last_*") and the current log + // ("/cache/recovery/log") into memory, so we can restore them after + // the reformat. ensure_path_mounted(volume); @@ -438,7 +442,7 @@ erase_volume(const char *volume) { strcat(path, "/"); int path_len = strlen(path); while ((de = readdir(d)) != NULL) { - if (strncmp(de->d_name, "last", 4) == 0) { + if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file)); strcpy(path+path_len, de->d_name); p->name = strdup(path); @@ -494,26 +498,7 @@ erase_volume(const char *volume) { copy_logs(); } - return result; -} - -static const char** -prepend_title(const char* const* headers) { - // count the number of lines in our title, plus the - // caller-provided headers. - int count = 3; // our title has 3 lines - const char* const* p; - for (p = headers; *p; ++p, ++count); - - const char** new_headers = (const char**)malloc((count+1) * sizeof(char*)); - const char** h = new_headers; - *(h++) = "Android system recovery <" EXPAND(RECOVERY_API_VERSION) "e>"; - *(h++) = recovery_version; - *(h++) = ""; - for (p = headers; *p; ++p, ++h) *h = *p; - *h = NULL; - - return new_headers; + return (result == 0); } static int @@ -546,12 +531,10 @@ get_menu_selection(const char* const * headers, const char* const * items, if (action < 0) { switch (action) { case Device::kHighlightUp: - --selected; - selected = ui->SelectMenu(selected); + selected = ui->SelectMenu(--selected); break; case Device::kHighlightDown: - ++selected; - selected = ui->SelectMenu(selected); + selected = ui->SelectMenu(++selected); break; case Device::kInvokeItem: chosen_item = selected; @@ -573,24 +556,15 @@ static int compare_string(const void* a, const void* b) { } // Returns a malloc'd path, or NULL. -static char* -browse_directory(const char* path, Device* device) { +static char* browse_directory(const char* path, Device* device) { ensure_path_mounted(path); - const char* MENU_HEADERS[] = { "Choose a package to install:", - path, - "", - NULL }; - DIR* d; - struct dirent* de; - d = opendir(path); + DIR* d = opendir(path); if (d == NULL) { LOGE("error opening %s: %s\n", path, strerror(errno)); return NULL; } - const char** headers = prepend_title(MENU_HEADERS); - int d_size = 0; int d_alloc = 10; char** dirs = (char**)malloc(d_alloc * sizeof(char*)); @@ -599,6 +573,7 @@ browse_directory(const char* path, Device* device) { char** zips = (char**)malloc(z_alloc * sizeof(char*)); zips[0] = strdup("../"); + struct dirent* de; while ((de = readdir(d)) != NULL) { int name_len = strlen(de->d_name); @@ -642,6 +617,8 @@ browse_directory(const char* path, Device* device) { z_size += d_size; zips[z_size] = NULL; + const char* headers[] = { "Choose a package to install:", path, NULL }; + char* result; int chosen_item = 0; while (true) { @@ -672,161 +649,135 @@ browse_directory(const char* path, Device* device) { } } - int i; - for (i = 0; i < z_size; ++i) free(zips[i]); + for (int i = 0; i < z_size; ++i) free(zips[i]); free(zips); - free(headers); return result; } -static void -wipe_data(int confirm, Device* device) { - if (confirm) { - static const char** title_headers = NULL; - - if (title_headers == NULL) { - const char* headers[] = { "Confirm wipe of all user data?", - " THIS CAN NOT BE UNDONE.", - "", - NULL }; - title_headers = prepend_title((const char**)headers); - } +static bool yes_no(Device* device, const char* question1, const char* question2) { + const char* headers[] = { question1, question2, NULL }; + const char* items[] = { " No", " Yes", NULL }; - const char* items[] = { " No", - " No", - " No", - " No", - " No", - " No", - " No", - " Yes -- delete all user data", // [7] - " No", - " No", - " No", - NULL }; - - int chosen_item = get_menu_selection(title_headers, items, 1, 0, device); - if (chosen_item != 7) { - return; - } - } - - ui->Print("\n-- Wiping data...\n"); - device->WipeData(); - erase_volume("/data"); - erase_volume("/cache"); - erase_persistent_partition(); - ui->Print("Data wipe complete.\n"); + int chosen_item = get_menu_selection(headers, items, 1, 0, device); + return (chosen_item == 1); } -static void file_to_ui(const char* fn) { - FILE *fp = fopen_path(fn, "re"); - if (fp == NULL) { - ui->Print(" Unable to open %s: %s\n", fn, strerror(errno)); - return; +// Return true on success. +static bool wipe_data(int should_confirm, Device* device) { + if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) { + return false; } - 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 % LINES_PER_PAGE == 0) { - // give the user time to glance at the entries - 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; + modified_flash = true; - 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"); - } - } - } + ui->Print("\n-- Wiping data...\n"); + bool success = + device->PreWipeData() && + erase_volume("/data") && + erase_volume("/cache") && + device->PostWipeData(); + ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); + return success; +} - // 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(); +// Return true on success. +static bool wipe_cache(bool should_confirm, Device* device) { + if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) { + return false; } - redirect_stdio(TEMPORARY_LOG_FILE); - fclose(fp); + modified_flash = true; + + ui->Print("\n-- Wiping cache...\n"); + bool success = erase_volume("/cache"); + ui->Print("Cache wipe %s.\n", success ? "complete" : "failed"); + return success; } static void choose_recovery_file(Device* device) { - unsigned int i; - unsigned int n; - static const char** title_headers = NULL; - char *filename; - const char* headers[] = { "Select file to view", - "", - NULL }; - // "Go back" + LAST_KMSG_FILE + KEEP_LOG_COUNT + terminating NULL entry - char* entries[KEEP_LOG_COUNT + 3]; + // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry + char* entries[1 + KEEP_LOG_COUNT * 2 + 1]; 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); - } + unsigned int n = 0; // 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) { + // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x + for (int i = 0; i < KEEP_LOG_COUNT; i++) { + char* log_file; + if (asprintf(&log_file, (i == 0) ? "%s" : "%s.%d", LAST_LOG_FILE, i) == -1) { // memory allocation failure - return early. Should never happen. return; } - if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) { - free(filename); - entries[n++] = NULL; - break; + if ((ensure_path_mounted(log_file) != 0) || (access(log_file, R_OK) == -1)) { + free(log_file); + } else { + entries[n++] = log_file; + } + + char* kmsg_file; + if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) { + // memory allocation failure - return early. Should never happen. + return; + } + if ((ensure_path_mounted(kmsg_file) != 0) || (access(kmsg_file, R_OK) == -1)) { + free(kmsg_file); + } else { + entries[n++] = kmsg_file; } - entries[n++] = filename; } - title_headers = prepend_title((const char**)headers); + entries[n++] = strdup("Back"); - while(1) { - int chosen_item = get_menu_selection(title_headers, entries, 1, 0, device); - if (chosen_item == 0) break; - file_to_ui(entries[chosen_item]); + const char* headers[] = { "Select file to view", nullptr }; + + while (true) { + int chosen_item = get_menu_selection(headers, entries, 1, 0, device); + if (strcmp(entries[chosen_item], "Back") == 0) break; + + // TODO: do we need to redirect? ShowFile could just avoid writing to stdio. + redirect_stdio("/dev/null"); + ui->ShowFile(entries[chosen_item]); + redirect_stdio(TEMPORARY_LOG_FILE); } - for (i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { + for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { free(entries[i]); } } +static int apply_from_sdcard(Device* device, bool* wipe_cache) { + modified_flash = true; + + if (ensure_path_mounted(SDCARD_ROOT) != 0) { + ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT); + return INSTALL_ERROR; + } + + char* path = browse_directory(SDCARD_ROOT, device); + if (path == NULL) { + ui->Print("\n-- No package file selected.\n"); + return INSTALL_ERROR; + } + + ui->Print("\n-- Install %s ...\n", path); + set_sdcard_update_bootloader_message(); + void* token = start_sdcard_fuse(path); + + int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, + TEMPORARY_INSTALL_FILE, false); + + finish_sdcard_fuse(token); + ensure_path_unmounted(SDCARD_ROOT); + return status; +} + // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION // means to take the default, which is to reboot or shutdown depending // on if the --shutdown_after flag was passed to recovery. static Device::BuiltinAction prompt_and_wait(Device* device, int status) { - const char* const* headers = prepend_title(device->GetMenuHeaders()); - for (;;) { finish_recovery(NULL); switch (status) { @@ -842,14 +793,14 @@ prompt_and_wait(Device* device, int status) { } ui->SetProgressType(RecoveryUI::EMPTY); - int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device); + int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device); // device-specific code may take some action here. It may // return one of the core actions handled in the switch // statement below. Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item); - int wipe_cache = 0; + bool should_wipe_cache = false; switch (chosen_action) { case Device::NO_ACTION: break; @@ -865,72 +816,45 @@ prompt_and_wait(Device* device, int status) { break; case Device::WIPE_CACHE: - ui->Print("\n-- Wiping cache...\n"); - erase_volume("/cache"); - ui->Print("Cache wipe complete.\n"); + wipe_cache(ui->IsTextVisible(), device); if (!ui->IsTextVisible()) return Device::NO_ACTION; break; - case Device::APPLY_EXT: { - ensure_path_mounted(SDCARD_ROOT); - char* path = browse_directory(SDCARD_ROOT, device); - if (path == NULL) { - ui->Print("\n-- No package file selected.\n", path); - break; - } - - ui->Print("\n-- Install %s ...\n", path); - set_sdcard_update_bootloader_message(); - void* token = start_sdcard_fuse(path); - - int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, &wipe_cache, - TEMPORARY_INSTALL_FILE, false); - - finish_sdcard_fuse(token); - ensure_path_unmounted(SDCARD_ROOT); - - if (status == INSTALL_SUCCESS && wipe_cache) { - ui->Print("\n-- Wiping cache (at package request)...\n"); - if (erase_volume("/cache")) { - ui->Print("Cache wipe failed.\n"); + case Device::APPLY_ADB_SIDELOAD: + case Device::APPLY_SDCARD: + { + bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); + if (adb) { + status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); } else { - ui->Print("Cache wipe complete.\n"); + status = apply_from_sdcard(device, &should_wipe_cache); + } + + if (status == INSTALL_SUCCESS && should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } } - } - if (status >= 0) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); + copy_logs(); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible } else { - ui->Print("\nInstall from sdcard complete.\n"); + ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card"); } } break; - } - - case Device::APPLY_CACHE: - ui->Print("\nAPPLY_CACHE is deprecated.\n"); - break; - case Device::READ_RECOVERY_LASTLOG: + case Device::VIEW_RECOVERY_LOGS: choose_recovery_file(device); break; - case Device::APPLY_ADB_SIDELOAD: - status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); - if (status >= 0) { - if (status != INSTALL_SUCCESS) { - ui->SetBackground(RecoveryUI::ERROR); - ui->Print("Installation aborted.\n"); - copy_logs(); - } else if (!ui->IsTextVisible()) { - return Device::NO_ACTION; // reboot if logs aren't visible - } else { - ui->Print("\nInstall from ADB complete.\n"); - } + case Device::MOUNT_SYSTEM: + if (ensure_path_mounted("/system") != -1) { + ui->Print("Mounted /system.\n"); } break; } @@ -993,31 +917,35 @@ main(int argc, char **argv) { // 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 == 2 && strcmp(argv[1], "--adbd") == 0) { - adb_main(); + adb_main(0, DEFAULT_ADB_PORT); return 0; } 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); const char *send_intent = NULL; const char *update_package = NULL; - int wipe_data = 0, wipe_cache = 0, show_text = 0; + bool should_wipe_data = false; + bool should_wipe_cache = false; + bool show_text = false; + bool sideload = false; + bool sideload_auto_reboot = false; bool just_exit = false; bool shutdown_after = false; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { switch (arg) { - case 's': send_intent = optarg; break; + case 'i': send_intent = optarg; break; case 'u': update_package = optarg; break; - case 'w': wipe_data = wipe_cache = 1; break; - case 'c': wipe_cache = 1; break; - case 't': show_text = 1; break; + case 'w': should_wipe_data = true; break; + case 'c': should_wipe_cache = true; break; + case 't': show_text = true; break; + case 's': sideload = true; break; + case 'a': sideload = true; sideload_auto_reboot = true; break; case 'x': just_exit = true; break; case 'l': locale = optarg; break; case 'g': { @@ -1093,17 +1021,16 @@ main(int argc, char **argv) { printf("\n"); property_list(print_property, NULL); - property_get("ro.build.display.id", recovery_version, ""); printf("\n"); + ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); + int status = INSTALL_SUCCESS; if (update_package != NULL) { - 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."); - } + status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + wipe_cache(false, device); } if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); @@ -1111,34 +1038,61 @@ main(int argc, char **argv) { // If this is an eng or userdebug build, then automatically // turn the text display on if the script fails so the error // message is visible. - char buffer[PROPERTY_VALUE_MAX+1]; - property_get("ro.build.fingerprint", buffer, ""); - if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) { + if (is_ro_debuggable()) { ui->ShowText(true); } } - } else if (wipe_data) { - 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 (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; - if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n"); + } else if (should_wipe_data) { + if (!wipe_data(false, device)) { + status = INSTALL_ERROR; + } + } else if (should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } else if (sideload) { + // 'adb reboot sideload' acts the same as user presses key combinations + // to enter the sideload mode. When 'sideload-auto-reboot' is used, text + // display will NOT be turned on by default. And it will reboot after + // sideload finishes even if there are errors. Unless one turns on the + // text display during the installation. This is to enable automated + // testing. + if (!sideload_auto_reboot) { + ui->ShowText(true); + } + status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } + ui->Print("\nInstall from ADB complete (status: %d).\n", status); + if (sideload_auto_reboot) { + ui->Print("Rebooting automatically.\n"); + } } else if (!just_exit) { status = INSTALL_NONE; // No command specified ui->SetBackground(RecoveryUI::NO_COMMAND); + + // http://b/17489952 + // If this is an eng or userdebug build, automatically turn on the + // text display if no command is specified. + if (is_ro_debuggable()) { + ui->ShowText(true); + } } - if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { + if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) { copy_logs(); ui->SetBackground(RecoveryUI::ERROR); } + Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { + if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) { Device::BuiltinAction temp = prompt_and_wait(device, status); - if (temp != Device::NO_ACTION) after = temp; + if (temp != Device::NO_ACTION) { + after = temp; + } } // Save logs and clean up before rebooting or shutting down. @@ -41,8 +41,6 @@ static struct fstab *fstab = NULL; extern struct selabel_handle *sehandle; -static const char* PERSISTENT_PATH = "/persistent"; - void load_volume_table() { int i; @@ -119,9 +117,10 @@ int ensure_path_mounted(const char* path) { } return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0); } else if (strcmp(v->fs_type, "ext4") == 0 || + strcmp(v->fs_type, "squashfs") == 0 || strcmp(v->fs_type, "vfat") == 0) { result = mount(v->blk_device, v->mount_point, v->fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, ""); + v->flags, v->fs_options); if (result == 0) return 0; LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno)); @@ -280,41 +279,6 @@ int format_volume(const char* volume) { return -1; } -int erase_persistent_partition() { - Volume *v = volume_for_path(PERSISTENT_PATH); - if (v == NULL) { - // most devices won't have /persistent, so this is not an error. - return 0; - } - - int fd = open(v->blk_device, O_RDWR); - uint64_t size = get_file_size(fd); - if (size == 0) { - LOGE("failed to stat size of /persistent\n"); - close(fd); - return -1; - } - - char oem_unlock_enabled; - lseek(fd, size - 1, SEEK_SET); - read(fd, &oem_unlock_enabled, 1); - - if (oem_unlock_enabled) { - if (wipe_block_device(fd, size)) { - LOGE("error wiping /persistent: %s\n", strerror(errno)); - close(fd); - return -1; - } - - lseek(fd, size - 1, SEEK_SET); - write(fd, &oem_unlock_enabled, 1); - } - - close(fd); - - return (int) oem_unlock_enabled; -} - int setup_install_mounts() { if (fstab == NULL) { LOGE("can't set up install mounts: no fstab loaded\n"); @@ -46,11 +46,6 @@ int format_volume(const char* volume); // mounted (/tmp and /cache) are mounted. Returns 0 on success. int setup_install_mounts(); -// Conditionally wipes the /persistent partition if it's marked -// to wipe. Returns -1 on failure, 1 if the partition was wiped -// and 0 if the partition was not wiped. -int erase_persistent_partition(); - #ifdef __cplusplus } #endif diff --git a/screen_ui.cpp b/screen_ui.cpp index 6fff30a25..e699538c7 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -28,6 +28,10 @@ #include <time.h> #include <unistd.h> +#include <vector> + +#include "base/strings.h" +#include "cutils/properties.h" #include "common.h" #include "device.h" #include "minui/minui.h" @@ -43,67 +47,60 @@ int twgr_text(int x, int y, const char *s); static int char_width; static int char_height; -// There's only (at most) one of these objects, and global callbacks -// (for pthread_create, and the input event system) need to find it, -// so use a global variable. -static ScreenRecoveryUI* self = NULL; - // Return the current time as a double (including fractions of a second). static double now() { struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); return tv.tv_sec + tv.tv_usec / 1000000.0; } ScreenRecoveryUI::ScreenRecoveryUI() : currentIcon(NONE), installingFrame(0), - locale(NULL), + locale(nullptr), rtl_locale(false), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), progress(0), pagesIdentical(false), - text_cols(0), - text_rows(0), - text_col(0), - text_row(0), - text_top(0), + text_cols_(0), + text_rows_(0), + text_(nullptr), + text_col_(0), + text_row_(0), + text_top_(0), show_text(false), show_text_ever(false), + menu_(nullptr), show_menu(false), - menu_top(0), menu_items(0), menu_sel(0), + file_viewer_text_(nullptr), animation_fps(20), installing_frames(-1), stage(-1), max_stage(-1) { - for (int i = 0; i < 5; i++) - backgroundIcon[i] = NULL; - - memset(text, 0, sizeof(text)); - - pthread_mutex_init(&updateMutex, NULL); - self = this; + for (int i = 0; i < 5; i++) { + backgroundIcon[i] = nullptr; + } + pthread_mutex_init(&updateMutex, nullptr); } // Clear the screen and draw the currently selected background icon (if any). // Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_background_locked(Icon icon) -{ +void ScreenRecoveryUI::draw_background_locked(Icon icon) { pagesIdentical = false; gr_color(0, 0, 0, 255); gr_clear(); if (icon) { - gr_surface surface = backgroundIcon[icon]; + GRSurface* surface = backgroundIcon[icon]; if (icon == INSTALLING_UPDATE || icon == ERASING) { surface = installation[installingFrame]; } - gr_surface text_surface = backgroundText[icon]; + GRSurface* text_surface = backgroundText[icon]; int iconWidth = gr_get_width(surface); int iconHeight = gr_get_height(surface); @@ -138,12 +135,11 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) // Draw the progress bar (if any) on the screen. Does not flip pages. // Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_progress_locked() -{ +void ScreenRecoveryUI::draw_progress_locked() { if (currentIcon == ERROR) return; if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - gr_surface icon = installation[installingFrame]; + GRSurface* icon = installation[installingFrame]; gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY); } @@ -186,6 +182,9 @@ void ScreenRecoveryUI::draw_progress_locked() void ScreenRecoveryUI::SetColor(UIElement e) { switch (e) { + case INFO: + gr_color(249, 194, 0, 255); + break; case HEADER: gr_color(247, 0, 6, 255); break; @@ -193,11 +192,14 @@ void ScreenRecoveryUI::SetColor(UIElement e) { case MENU_SEL_BG: gr_color(0, 106, 157, 255); break; + case MENU_SEL_BG_ACTIVE: + gr_color(0, 156, 100, 255); + break; case MENU_SEL_FG: gr_color(255, 255, 255, 255); break; case LOG: - gr_color(249, 194, 0, 255); + gr_color(196, 196, 196, 255); break; case TEXT_FILL: gr_color(0, 0, 0, 160); @@ -208,10 +210,38 @@ void ScreenRecoveryUI::SetColor(UIElement e) { } } +void ScreenRecoveryUI::DrawHorizontalRule(int* y) { + SetColor(MENU); + *y += 4; + gr_fill(0, *y, gr_fb_width(), *y + 2); + *y += 4; +} + +void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) { + gr_text(4, *y, line, bold); + *y += char_height + 4; +} + +void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) { + for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { + DrawTextLine(y, lines[i], false); + } +} + +static const char* REGULAR_HELP[] = { + "Use volume up/down and power.", + NULL +}; + +static const char* LONG_PRESS_HELP[] = { + "Any button cycles highlight.", + "Long-press activates.", + NULL +}; + // Redraw everything on the screen. Does not flip pages. // Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_screen_locked() -{ +void ScreenRecoveryUI::draw_screen_locked() { if (!show_text) { draw_background_locked(currentIcon); draw_progress_locked(); @@ -220,62 +250,67 @@ void ScreenRecoveryUI::draw_screen_locked() gr_clear(); int y = 0; - int i = 0; if (show_menu) { - SetColor(HEADER); + char recovery_fingerprint[PROPERTY_VALUE_MAX]; + property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, ""); - for (; i < menu_top + menu_items; ++i) { - if (i == menu_top) SetColor(MENU); + SetColor(INFO); + DrawTextLine(&y, "Android Recovery", true); + for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { + DrawTextLine(&y, chunk.c_str(), false); + } + DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); - if (i == menu_top + menu_sel) { - // draw the highlight bar - SetColor(MENU_SEL_BG); - gr_fill(0, y-2, gr_fb_width(), y+char_height+2); - // white text of selected item + SetColor(HEADER); + DrawTextLines(&y, menu_headers_); + + SetColor(MENU); + DrawHorizontalRule(&y); + y += 4; + for (int i = 0; i < menu_items; ++i) { + if (i == menu_sel) { + // Draw the highlight bar. + SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); + gr_fill(0, y - 2, gr_fb_width(), y + char_height + 2); + // Bold white text for the selected item. SetColor(MENU_SEL_FG); - if (menu[i][0]) gr_text(4, y, menu[i], 1); + gr_text(4, y, menu_[i], true); SetColor(MENU); } else { - if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); + gr_text(4, y, menu_[i], false); } - y += char_height+4; + y += char_height + 4; } - SetColor(MENU); - y += 4; - gr_fill(0, y, gr_fb_width(), y+2); - y += 4; - ++i; + DrawHorizontalRule(&y); } - SetColor(LOG); - // display from the bottom up, until we hit the top of the // screen, the bottom of the menu, or we've displayed the // entire text buffer. - int ty; - int row = (text_top+text_rows-1) % text_rows; - for (int ty = gr_fb_height() - char_height, count = 0; - ty > y+2 && count < text_rows; + SetColor(LOG); + int row = (text_top_ + text_rows_ - 1) % text_rows_; + size_t count = 0; + for (int ty = gr_fb_height() - char_height; + ty >= y && count < text_rows_; ty -= char_height, ++count) { - gr_text(4, ty, text[row], 0); + gr_text(0, ty, text_[row], false); --row; - if (row < 0) row = text_rows-1; + if (row < 0) row = text_rows_ - 1; } } } // Redraw everything on the screen and flip the screen (make it visible). // Should only be called with updateMutex locked. -void ScreenRecoveryUI::update_screen_locked() -{ +void ScreenRecoveryUI::update_screen_locked() { draw_screen_locked(); gr_flip(); } // Updates only the progress bar, if possible, otherwise redraws the screen. // Should only be called with updateMutex locked. -void ScreenRecoveryUI::update_progress_locked() -{return; + +void ScreenRecoveryUI::update_progress_locked() { if (show_text || !pagesIdentical) { draw_screen_locked(); // Must redraw the whole screen pagesIdentical = true; @@ -286,14 +321,14 @@ void ScreenRecoveryUI::update_progress_locked() } // Keeps the progress bar updated, even when the process is otherwise busy. -void* ScreenRecoveryUI::progress_thread(void *cookie) { - self->progress_loop(); - return NULL; +void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) { + reinterpret_cast<ScreenRecoveryUI*>(data)->ProgressThreadLoop(); + return nullptr; } -void ScreenRecoveryUI::progress_loop() { +void ScreenRecoveryUI::ProgressThreadLoop() { double interval = 1.0 / animation_fps; - for (;;) { + while (true) { double start = now(); pthread_mutex_lock(&updateMutex); @@ -330,44 +365,53 @@ void ScreenRecoveryUI::progress_loop() { } } -void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { +void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { int result = res_create_display_surface(filename, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } } -void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, gr_surface** surface) { +void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, GRSurface*** surface) { int result = res_create_multi_display_surface(filename, frames, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } } -void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) { +void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) { int result = res_create_localized_alpha_surface(filename, locale, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } } -void ScreenRecoveryUI::Init() -{ +static char** Alloc2d(size_t rows, size_t cols) { + char** result = new char*[rows]; + for (size_t i = 0; i < rows; ++i) { + result[i] = new char[cols]; + memset(result[i], 0, cols); + } + return result; +} + +void ScreenRecoveryUI::Init() { gr_init(); gr_font_size(&char_width, &char_height); + text_rows_ = gr_fb_height() / char_height; + text_cols_ = gr_fb_width() / char_width; - text_col = text_row = 0; - text_rows = gr_fb_height() / char_height; - if (text_rows > kMaxRows) text_rows = kMaxRows; - text_top = 1; + text_ = Alloc2d(text_rows_, text_cols_ + 1); + file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); + menu_ = Alloc2d(text_rows_, text_cols_ + 1); - text_cols = gr_fb_width() / char_width; - if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; + text_col_ = text_row_ = 0; + text_top_ = 1; - backgroundIcon[NONE] = NULL; + backgroundIcon[NONE] = nullptr; LoadBitmapArray("icon_installing", &installing_frames, &installation); - backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : NULL; + backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr; backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; @@ -382,7 +426,7 @@ void ScreenRecoveryUI::Init() LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); - pthread_create(&progress_t, NULL, progress_thread, NULL); + pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this); RecoveryUI::Init(); } @@ -409,12 +453,11 @@ void ScreenRecoveryUI::SetLocale(const char* new_locale) { } free(lang); } else { - new_locale = NULL; + new_locale = nullptr; } } -void ScreenRecoveryUI::SetBackground(Icon icon) -{ +void ScreenRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); currentIcon = icon; @@ -423,8 +466,7 @@ void ScreenRecoveryUI::SetBackground(Icon icon) pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::SetProgressType(ProgressType type) -{ +void ScreenRecoveryUI::SetProgressType(ProgressType type) { pthread_mutex_lock(&updateMutex); if (progressBarType != type) { progressBarType = type; @@ -436,11 +478,7 @@ void ScreenRecoveryUI::SetProgressType(ProgressType type) pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::ShowProgress(float portion, float seconds) -{ - DataManager::SetValue("ui_progress_portion", (float)(portion * 100.0)); - DataManager::SetValue("ui_progress_frames", seconds * 30); - +void ScreenRecoveryUI::ShowProgress(float portion, float seconds) { pthread_mutex_lock(&updateMutex); progressBarType = DETERMINATE; progressScopeStart += progressScopeSize; @@ -452,10 +490,7 @@ void ScreenRecoveryUI::ShowProgress(float portion, float seconds) pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::SetProgress(float fraction) -{ - DataManager::SetValue("ui_progress", (float) (fraction * 100.0)); return; - +void ScreenRecoveryUI::SetProgress(float fraction) { pthread_mutex_lock(&updateMutex); if (fraction < 0.0) fraction = 0.0; if (fraction > 1.0) fraction = 1.0; @@ -478,8 +513,7 @@ void ScreenRecoveryUI::SetStage(int current, int max) { pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::Print(const char *fmt, ...) -{ +void ScreenRecoveryUI::Print(const char *fmt, ...) { char buf[256]; va_list ap; va_start(ap, fmt); @@ -491,43 +525,133 @@ void ScreenRecoveryUI::Print(const char *fmt, ...) fputs(buf, stdout); - // This can get called before ui_init(), so be careful. pthread_mutex_lock(&updateMutex); - if (text_rows > 0 && text_cols > 0) { - char *ptr; - for (ptr = buf; *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col >= text_cols) { - text[text_row][text_col] = '\0'; - text_col = 0; - text_row = (text_row + 1) % text_rows; - if (text_row == text_top) text_top = (text_top + 1) % text_rows; + if (text_rows_ > 0 && text_cols_ > 0) { + for (const char* ptr = buf; *ptr != '\0'; ++ptr) { + if (*ptr == '\n' || text_col_ >= text_cols_) { + text_[text_row_][text_col_] = '\0'; + text_col_ = 0; + text_row_ = (text_row_ + 1) % text_rows_; + if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; } - if (*ptr != '\n') text[text_row][text_col++] = *ptr; + if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; } - text[text_row][text_col] = '\0'; + text_[text_row_][text_col_] = '\0'; update_screen_locked(); } pthread_mutex_unlock(&updateMutex); } +void ScreenRecoveryUI::PutChar(char ch) { + pthread_mutex_lock(&updateMutex); + if (ch != '\n') text_[text_row_][text_col_++] = ch; + if (ch == '\n' || text_col_ >= text_cols_) { + text_col_ = 0; + ++text_row_; + + if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; + } + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::ClearText() { + pthread_mutex_lock(&updateMutex); + text_col_ = 0; + text_row_ = 0; + text_top_ = 1; + for (size_t i = 0; i < text_rows_; ++i) { + memset(text_[i], 0, text_cols_ + 1); + } + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::ShowFile(FILE* fp) { + std::vector<long> offsets; + offsets.push_back(ftell(fp)); + ClearText(); + + struct stat sb; + fstat(fileno(fp), &sb); + + bool show_prompt = false; + while (true) { + if (show_prompt) { + Print("--(%d%% of %d bytes)--", + static_cast<int>(100 * (double(ftell(fp)) / double(sb.st_size))), + static_cast<int>(sb.st_size)); + Redraw(); + while (show_prompt) { + show_prompt = false; + int key = WaitKey(); + if (key == KEY_POWER || key == KEY_ENTER) { + return; + } else if (key == KEY_UP || key == KEY_VOLUMEUP) { + if (offsets.size() <= 1) { + show_prompt = true; + } else { + offsets.pop_back(); + fseek(fp, offsets.back(), SEEK_SET); + } + } else { + if (feof(fp)) { + return; + } + offsets.push_back(ftell(fp)); + } + } + ClearText(); + } + + int ch = getc(fp); + if (ch == EOF) { + while (text_row_ < text_rows_ - 1) PutChar('\n'); + show_prompt = true; + } else { + PutChar(ch); + if (text_col_ == 0 && text_row_ >= text_rows_ - 1) { + show_prompt = true; + } + } + } +} + +void ScreenRecoveryUI::ShowFile(const char* filename) { + FILE* fp = fopen_path(filename, "re"); + if (fp == nullptr) { + Print(" Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + + char** old_text = text_; + size_t old_text_col = text_col_; + size_t old_text_row = text_row_; + size_t old_text_top = text_top_; + + // Swap in the alternate screen and clear it. + text_ = file_viewer_text_; + ClearText(); + + ShowFile(fp); + fclose(fp); + + text_ = old_text; + text_col_ = old_text_col; + text_row_ = old_text_row; + text_top_ = old_text_top; +} + void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items, int initial_selection) { - int i; pthread_mutex_lock(&updateMutex); - if (text_rows > 0 && text_cols > 0) { - for (i = 0; i < text_rows; ++i) { - if (headers[i] == NULL) break; - strncpy(menu[i], headers[i], text_cols-1); - menu[i][text_cols-1] = '\0'; - } - menu_top = i; - for (; i < text_rows; ++i) { - if (items[i-menu_top] == NULL) break; - strncpy(menu[i], items[i-menu_top], text_cols-1); - menu[i][text_cols-1] = '\0'; + if (text_rows_ > 0 && text_cols_ > 0) { + menu_headers_ = headers; + size_t i = 0; + for (; i < text_rows_ && items[i] != nullptr; ++i) { + strncpy(menu_[i], items[i], text_cols_ - 1); + menu_[i][text_cols_ - 1] = '\0'; } - menu_items = i - menu_top; - show_menu = 1; + menu_items = i; + show_menu = true; menu_sel = initial_selection; update_screen_locked(); } @@ -535,13 +659,15 @@ void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const } int ScreenRecoveryUI::SelectMenu(int sel) { - int old_sel; pthread_mutex_lock(&updateMutex); - if (show_menu > 0) { - old_sel = menu_sel; + if (show_menu) { + int old_sel = menu_sel; menu_sel = sel; - if (menu_sel < 0) menu_sel = 0; - if (menu_sel >= menu_items) menu_sel = menu_items-1; + + // Wrap at top and bottom. + if (menu_sel < 0) menu_sel = menu_items - 1; + if (menu_sel >= menu_items) menu_sel = 0; + sel = menu_sel; if (menu_sel != old_sel) update_screen_locked(); } @@ -550,43 +676,44 @@ int ScreenRecoveryUI::SelectMenu(int sel) { } void ScreenRecoveryUI::EndMenu() { - int i; pthread_mutex_lock(&updateMutex); - if (show_menu > 0 && text_rows > 0 && text_cols > 0) { - show_menu = 0; + if (show_menu && text_rows_ > 0 && text_cols_ > 0) { + show_menu = false; update_screen_locked(); } pthread_mutex_unlock(&updateMutex); } -bool ScreenRecoveryUI::IsTextVisible() -{ +bool ScreenRecoveryUI::IsTextVisible() { pthread_mutex_lock(&updateMutex); int visible = show_text; pthread_mutex_unlock(&updateMutex); return visible; } -bool ScreenRecoveryUI::WasTextEverVisible() -{ +bool ScreenRecoveryUI::WasTextEverVisible() { pthread_mutex_lock(&updateMutex); int ever_visible = show_text_ever; pthread_mutex_unlock(&updateMutex); return ever_visible; } -void ScreenRecoveryUI::ShowText(bool visible) -{ +void ScreenRecoveryUI::ShowText(bool visible) { pthread_mutex_lock(&updateMutex); show_text = visible; - if (show_text) show_text_ever = 1; + if (show_text) show_text_ever = true; update_screen_locked(); pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::Redraw() -{ +void ScreenRecoveryUI::Redraw() { pthread_mutex_lock(&updateMutex); update_screen_locked(); pthread_mutex_unlock(&updateMutex); } + +void ScreenRecoveryUI::KeyLongPress(int) { + // Redraw so that if we're in the menu, the highlight + // will change color to indicate a successful long press. + Redraw(); +} diff --git a/screen_ui.h b/screen_ui.h index 01a33bfe2..ea05bf15f 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -18,6 +18,7 @@ #define RECOVERY_SCREEN_UI_H #include <pthread.h> +#include <stdio.h> #include "ui.h" #include "minui/minui.h" @@ -47,18 +48,23 @@ class ScreenRecoveryUI : public RecoveryUI { bool WasTextEverVisible(); // printing messages - void Print(const char* fmt, ...); // __attribute__((format(printf, 1, 2))); + void Print(const char* fmt, ...) __printflike(2, 3); + void ShowFile(const char* filename); // menu display void StartMenu(const char* const * headers, const char* const * items, - int initial_selection); + int initial_selection); int SelectMenu(int sel); void EndMenu(); + void KeyLongPress(int); + void Redraw(); - enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL }; - virtual void SetColor(UIElement e); + enum UIElement { + HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO + }; + void SetColor(UIElement e); private: Icon currentIcon; @@ -67,43 +73,43 @@ class ScreenRecoveryUI : public RecoveryUI { bool rtl_locale; pthread_mutex_t updateMutex; - gr_surface backgroundIcon[5]; - gr_surface backgroundText[5]; - gr_surface *installation; - gr_surface progressBarEmpty; - gr_surface progressBarFill; - gr_surface stageMarkerEmpty; - gr_surface stageMarkerFill; + GRSurface* backgroundIcon[5]; + GRSurface* backgroundText[5]; + GRSurface** installation; + GRSurface* progressBarEmpty; + GRSurface* progressBarFill; + GRSurface* stageMarkerEmpty; + GRSurface* stageMarkerFill; ProgressType progressBarType; float progressScopeStart, progressScopeSize, progress; double progressScopeTime, progressScopeDuration; - // true when both graphics pages are the same (except for the - // progress bar) + // true when both graphics pages are the same (except for the progress bar). bool pagesIdentical; - static const int kMaxCols = 96; - static const int kMaxRows = 96; + size_t text_cols_, text_rows_; + + // Log text overlay, displayed when a magic key is pressed. + char** text_; + size_t text_col_, text_row_, text_top_; - // Log text overlay, displayed when a magic key is pressed - char text[kMaxRows][kMaxCols]; - int text_cols, text_rows; - int text_col, text_row, text_top; bool show_text; bool show_text_ever; // has show_text ever been true? - char menu[kMaxRows][kMaxCols]; + char** menu_; + const char* const* menu_headers_; bool show_menu; - int menu_top, menu_items, menu_sel; + int menu_items, menu_sel; - pthread_t progress_t; + // An alternate text screen, swapped with 'text_' when we're viewing a log file. + char** file_viewer_text_; + + pthread_t progress_thread_; int animation_fps; int installing_frames; - protected: - private: int iconX, iconY; @@ -114,12 +120,21 @@ class ScreenRecoveryUI : public RecoveryUI { void draw_screen_locked(); void update_screen_locked(); void update_progress_locked(); - static void* progress_thread(void* cookie); - void progress_loop(); - void LoadBitmap(const char* filename, gr_surface* surface); - void LoadBitmapArray(const char* filename, int* frames, gr_surface** surface); - void LoadLocalizedBitmap(const char* filename, gr_surface* surface); + static void* ProgressThreadStartRoutine(void* data); + void ProgressThreadLoop(); + + void ShowFile(FILE*); + void PutChar(char); + void ClearText(); + + void DrawHorizontalRule(int* y); + void DrawTextLine(int* y, const char* line, bool bold); + void DrawTextLines(int* y, const char* const* lines); + + void LoadBitmap(const char* filename, GRSurface** surface); + void LoadBitmapArray(const char* filename, int* frames, GRSurface*** surface); + void LoadLocalizedBitmap(const char* filename, GRSurface** surface); }; #endif // RECOVERY_UI_H diff --git a/tarWrite.c b/tarWrite.c index e3963bbeb..98fa14cad 100644 --- a/tarWrite.c +++ b/tarWrite.c @@ -17,6 +17,9 @@ */ #include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> #include "libtar/libtar.h" #include "twcommon.h" @@ -71,7 +74,7 @@ ssize_t write_libtar_buffer(int fd, const void *buffer, size_t size) { // nothing to write return 0; } - if (write(fd, write_buffer, buffer_loc) != buffer_loc) { + if (write(fd, write_buffer, buffer_loc) != (int)buffer_loc) { LOGERR("Error writing tar file!\n"); buffer_loc = 0; return -1; diff --git a/tests/Android.mk b/tests/Android.mk index 0079be4c2..02a272a24 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -1,27 +1,25 @@ -# Build the unit tests. -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -# Build the unit tests. -test_src_files := \ - asn1_decoder_test.cpp +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# -shared_libraries := \ - liblog \ - libcutils - -static_libraries := \ - libgtest \ - libgtest_main \ - libverifier +LOCAL_PATH := $(call my-dir) -$(foreach file,$(test_src_files), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ - $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/..) \ - $(eval LOCAL_MODULE_TAGS := optional) \ - $(eval include $(BUILD_NATIVE_TEST)) \ -) +include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_STATIC_LIBRARIES := libverifier +LOCAL_SRC_FILES := asn1_decoder_test.cpp +LOCAL_MODULE := asn1_decoder_test +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +include $(BUILD_NATIVE_TEST) diff --git a/toolbox/Android.mk b/toolbox/Android.mk index e58b755e5..d54bfa440 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -4,110 +4,150 @@ include $(CLEAR_VARS) OUR_TOOLS := \ start \ stop \ - getprop \ - setprop + +ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) + OUR_TOOLS += \ + getprop \ + setprop +endif # If busybox does not have SELinux support, provide these tools with toolbox. # Note that RECOVERY_BUSYBOX_TOOLS will be empty if TW_USE_TOOLBOX == true. ifeq ($(TWHAVE_SELINUX), true) TOOLS_FOR_SELINUX := \ - ls \ - getenforce \ - chcon \ - restorecon \ - runcon \ - getsebool \ - setsebool \ - load_policy + ls + + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) + TOOLS_FOR_SELINUX += \ + load_policy \ + getenforce \ + chcon \ + restorecon \ + runcon \ + getsebool \ + setsebool + endif + OUR_TOOLS += $(filter-out $(RECOVERY_BUSYBOX_TOOLS), $(TOOLS_FOR_SELINUX)) # toolbox setenforce is used during init, so it needs to be included here # symlink is omitted at the very end if busybox already provides this - OUR_TOOLS += setenforce + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) + OUR_TOOLS += setenforce + endif endif ifeq ($(TW_USE_TOOLBOX), true) - ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) - OUR_TOOLS += \ - mknod \ - nohup + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) + # These are the only toolbox tools in M. The rest are now in toybox. BSD_TOOLS := \ - cat \ - chown \ - cp \ dd \ du \ - grep \ - kill \ - ln \ - mv \ - printenv \ - rm \ - rmdir \ - sleep \ - sync + + OUR_TOOLS := \ + df \ + iftop \ + ioctl \ + ionice \ + log \ + ls \ + lsof \ + mount \ + nandread \ + newfs_msdos \ + ps \ + prlimit \ + renice \ + sendevent \ + start \ + stop \ + top \ + uptime \ + watchprops \ + else - OUR_TOOLS += \ - cat \ - chown \ - dd \ - du \ - kill \ - ln \ - mv \ - printenv \ - rm \ - rmdir \ - setconsole \ - sleep \ - sync - endif + ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) + OUR_TOOLS += \ + mknod \ + nohup + BSD_TOOLS := \ + cat \ + chown \ + cp \ + dd \ + du \ + grep \ + kill \ + ln \ + mv \ + printenv \ + rm \ + rmdir \ + sleep \ + sync + else + OUR_TOOLS += \ + cat \ + chown \ + dd \ + du \ + kill \ + ln \ + mv \ + printenv \ + rm \ + rmdir \ + setconsole \ + sleep \ + sync + endif - OUR_TOOLS += \ - chmod \ - clear \ - cmp \ - date \ - df \ - dmesg \ - getevent \ - hd \ - id \ - ifconfig \ - iftop \ - insmod \ - ioctl \ - ionice \ - log \ - lsmod \ - lsof \ - md5 \ - mkdir \ - mkswap \ - mount \ - nandread \ - netstat \ - newfs_msdos \ - notify \ - ps \ - readlink \ - renice \ - rmmod \ - route \ - schedtop \ - sendevent \ - smd \ - swapoff \ - swapon \ - top \ - touch \ - umount \ - uptime \ - vmstat \ - watchprops \ - wipe - ifneq ($(TWHAVE_SELINUX), true) - OUR_TOOLS += ls + OUR_TOOLS += \ + chmod \ + clear \ + cmp \ + date \ + df \ + dmesg \ + getevent \ + hd \ + id \ + ifconfig \ + iftop \ + insmod \ + ioctl \ + ionice \ + log \ + lsmod \ + lsof \ + md5 \ + mkdir \ + mkswap \ + mount \ + nandread \ + netstat \ + newfs_msdos \ + notify \ + ps \ + readlink \ + renice \ + rmmod \ + route \ + schedtop \ + sendevent \ + smd \ + swapoff \ + swapon \ + top \ + touch \ + umount \ + uptime \ + vmstat \ + watchprops \ + wipe + ifneq ($(TWHAVE_SELINUX), true) + OUR_TOOLS += ls + endif endif endif @@ -135,7 +175,7 @@ ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) upstream-netbsd/lib/libutil/raise_default_signal.c endif -ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) +ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22 23)) LOCAL_CFLAGS += \ -std=gnu99 \ -Werror -Wno-unused-parameter \ @@ -165,7 +205,7 @@ ifeq ($(TWHAVE_SELINUX), true) LOCAL_SHARED_LIBRARIES += libselinux endif -ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) +ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22 23)) # libusbhost is only used by lsusb, and that isn't usually included in toolbox. # The linker strips out all the unused library code in the normal case. LOCAL_STATIC_LIBRARIES := libusbhost @@ -182,7 +222,7 @@ include $(BUILD_EXECUTABLE) $(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h -ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) +ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22 23)) ALL_TOOLS := $(BSD_TOOLS) $(OUR_TOOLS) else ALL_TOOLS := $(OUR_TOOLS) diff --git a/tools/ota/check-lost+found.c b/tools/ota/check-lost+found.c index da02f4602..8ce12d39f 100644 --- a/tools/ota/check-lost+found.c +++ b/tools/ota/check-lost+found.c @@ -26,6 +26,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <time.h> +#include <unistd.h> #include "private/android_filesystem_config.h" @@ -77,7 +78,7 @@ int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) snprintf(fn, sizeof(fn), "%s/%s", kPartitions[i], "dirty"); fd = open(fn, O_WRONLY|O_CREAT, 0444); if (fd >= 0) { // Don't sweat it if we can't write the file. - write(fd, fn, sizeof(fn)); // write, you know, some data + TEMP_FAILURE_RETRY(write(fd, fn, sizeof(fn))); // write, you know, some data close(fd); unlink(fn); } diff --git a/toybox/Android.mk b/toybox/Android.mk new file mode 100644 index 000000000..a879151fc --- /dev/null +++ b/toybox/Android.mk @@ -0,0 +1,371 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ifneq ($(wildcard external/toybox/Android.mk),) + +ifeq ($(TW_USE_TOOLBOX), true) + +LOCAL_PATH := external/toybox + +# +# To update: +# + +# git remote add toybox https://github.com/landley/toybox.git +# git fetch toybox +# git merge toybox/master +# mm -j32 +# # (Make any necessary Android.mk changes and test the new toybox.) +# git push aosp HEAD:master # Push directly, avoiding gerrit. +# git push aosp HEAD:refs/for/master # Push to gerrit. +# +# # Now commit any necessary Android.mk changes like normal: +# repo start post-sync . +# git commit -a + + +# +# To add a toy: +# + +# make menuconfig +# # (Select the toy you want to add.) +# make clean && make # Regenerate the generated files. +# # Edit LOCAL_SRC_FILES below to add the toy. +# # If you just want to use it as "toybox x" rather than "x", you can stop now. +# # If you want this toy to have a symbolic link in /system/bin, add the toy to ALL_TOOLS. + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + lib/args.c \ + lib/dirtree.c \ + lib/getmountlist.c \ + lib/help.c \ + lib/interestingtimes.c \ + lib/lib.c \ + lib/llist.c \ + lib/net.c \ + lib/portability.c \ + lib/xwrap.c \ + main.c \ + toys/android/getenforce.c \ + toys/android/getprop.c \ + toys/android/load_policy.c \ + toys/android/restorecon.c \ + toys/android/runcon.c \ + toys/android/setenforce.c \ + toys/android/setprop.c \ + toys/lsb/dmesg.c \ + toys/lsb/hostname.c \ + toys/lsb/killall.c \ + toys/lsb/md5sum.c \ + toys/lsb/mknod.c \ + toys/lsb/mktemp.c \ + toys/lsb/mount.c \ + toys/lsb/pidof.c \ + toys/lsb/seq.c \ + toys/lsb/sync.c \ + toys/lsb/umount.c \ + toys/other/acpi.c \ + toys/other/base64.c \ + toys/other/blkid.c \ + toys/other/blockdev.c \ + toys/other/bzcat.c \ + toys/other/chcon.c \ + toys/other/chroot.c \ + toys/other/clear.c \ + toys/other/dos2unix.c \ + toys/other/fallocate.c \ + toys/other/free.c \ + toys/other/freeramdisk.c \ + toys/other/fsfreeze.c \ + toys/other/help.c \ + toys/other/ifconfig.c \ + toys/other/inotifyd.c \ + toys/other/insmod.c \ + toys/other/losetup.c \ + toys/other/lsattr.c \ + toys/other/lsmod.c \ + toys/other/lsusb.c \ + toys/other/makedevs.c \ + toys/other/mkswap.c \ + toys/other/modinfo.c \ + toys/other/mountpoint.c \ + toys/other/nbd_client.c \ + toys/other/netcat.c \ + toys/other/partprobe.c \ + toys/other/pivot_root.c \ + toys/other/pmap.c \ + toys/other/printenv.c \ + toys/other/pwdx.c \ + toys/other/readlink.c \ + toys/other/realpath.c \ + toys/other/rev.c \ + toys/other/rfkill.c \ + toys/other/rmmod.c \ + toys/other/setsid.c \ + toys/other/stat.c \ + toys/other/swapoff.c \ + toys/other/swapon.c \ + toys/other/switch_root.c \ + toys/other/sysctl.c \ + toys/other/tac.c \ + toys/other/taskset.c \ + toys/other/timeout.c \ + toys/other/truncate.c \ + toys/other/usleep.c \ + toys/other/vconfig.c \ + toys/other/vmstat.c \ + toys/other/which.c \ + toys/other/yes.c \ + toys/pending/dd.c \ + toys/pending/expr.c \ + toys/pending/hwclock.c \ + toys/pending/more.c \ + toys/pending/pgrep.c \ + toys/pending/netstat.c \ + toys/pending/route.c \ + toys/pending/tar.c \ + toys/pending/top.c \ + toys/pending/tr.c \ + toys/pending/traceroute.c \ + toys/posix/basename.c \ + toys/posix/cal.c \ + toys/posix/cat.c \ + toys/posix/chgrp.c \ + toys/posix/chmod.c \ + toys/posix/cksum.c \ + toys/posix/cmp.c \ + toys/posix/comm.c \ + toys/posix/cp.c \ + toys/posix/cpio.c \ + toys/posix/cut.c \ + toys/posix/date.c \ + toys/posix/df.c \ + toys/posix/dirname.c \ + toys/posix/du.c \ + toys/posix/echo.c \ + toys/posix/env.c \ + toys/posix/expand.c \ + toys/posix/false.c \ + toys/posix/find.c \ + toys/posix/grep.c \ + toys/posix/head.c \ + toys/posix/id.c \ + toys/posix/kill.c \ + toys/posix/ln.c \ + toys/posix/ls.c \ + toys/posix/mkdir.c \ + toys/posix/mkfifo.c \ + toys/posix/nice.c \ + toys/posix/nl.c \ + toys/posix/nohup.c \ + toys/posix/od.c \ + toys/posix/paste.c \ + toys/posix/patch.c \ + toys/posix/printf.c \ + toys/posix/pwd.c \ + toys/posix/renice.c \ + toys/posix/rm.c \ + toys/posix/rmdir.c \ + toys/posix/sed.c \ + toys/posix/sleep.c \ + toys/posix/sort.c \ + toys/posix/split.c \ + toys/posix/strings.c \ + toys/posix/tail.c \ + toys/posix/tee.c \ + toys/posix/time.c \ + toys/posix/touch.c \ + toys/posix/true.c \ + toys/posix/tty.c \ + toys/posix/uname.c \ + toys/posix/uniq.c \ + toys/posix/wc.c \ + toys/posix/xargs.c \ + +LOCAL_CFLAGS += \ + -std=c99 \ + -Os \ + -Wno-char-subscripts \ + -Wno-sign-compare \ + -Wno-string-plus-int \ + -Wno-uninitialized \ + -Wno-unused-parameter \ + -funsigned-char \ + -ffunction-sections -fdata-sections \ + -fno-asynchronous-unwind-tables \ + +toybox_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android +LOCAL_CFLAGS += -DTOYBOX_VERSION='"$(toybox_version)"' + +LOCAL_CLANG := true + +LOCAL_SHARED_LIBRARIES := libcutils libselinux + +LOCAL_MODULE := toybox_recovery +LOCAL_MODULE_STEM := toybox +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_TAGS := optional + +# dupes: dd df du ls mount renice +# useless?: freeramdisk fsfreeze install makedevs mkfifo nbd-client +# partprobe pivot_root pwdx rev rfkill switch_root tty vconfig +# prefer BSD netcat instead?: nc netcat +# prefer efs2progs instead?: blkid chattr lsattr + +ALL_TOOLS := \ + acpi \ + basename \ + blkid \ + blockdev \ + bzcat \ + cal \ + cat \ + chcon \ + chgrp \ + chmod \ + chown \ + chroot \ + cksum \ + clear \ + comm \ + cmp \ + cp \ + cpio \ + cut \ + date \ + dirname \ + dmesg \ + dos2unix \ + echo \ + env \ + expand \ + expr \ + fallocate \ + false \ + find \ + free \ + getenforce \ + getprop \ + groups \ + head \ + hostname \ + hwclock \ + id \ + ifconfig \ + inotifyd \ + insmod \ + kill \ + load_policy \ + ln \ + logname \ + losetup \ + lsmod \ + lsusb \ + md5sum \ + mkdir \ + mknod \ + mkswap \ + mktemp \ + modinfo \ + more \ + mountpoint \ + mv \ + netstat \ + nice \ + nl \ + nohup \ + od \ + paste \ + patch \ + pgrep \ + pidof \ + pkill \ + pmap \ + printenv \ + printf \ + pwd \ + readlink \ + realpath \ + restorecon \ + rm \ + rmdir \ + rmmod \ + route \ + runcon \ + sed \ + seq \ + setenforce \ + setprop \ + setsid \ + sha1sum \ + sleep \ + sort \ + split \ + stat \ + strings \ + swapoff \ + swapon \ + sync \ + sysctl \ + tac \ + tail \ + tar \ + taskset \ + tee \ + time \ + timeout \ + touch \ + tr \ + true \ + truncate \ + umount \ + uname \ + uniq \ + unix2dos \ + usleep \ + vmstat \ + wc \ + which \ + whoami \ + xargs \ + yes \ + +# Install the symlinks. +LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toybox $(TARGET_RECOVERY_ROOT_OUT)/sbin/$(t);) + +include $(BUILD_EXECUTABLE) + +# Make /sbin/toolbox launchers for each tool +SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(ALL_TOOLS)) +$(SYMLINKS): TOYBOX_BINARY := $(LOCAL_MODULE_STEM) +$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> $(TOYBOX_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(TOYBOX_BINARY) $@ + +include $(CLEAR_VARS) +LOCAL_MODULE := toybox_symlinks +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := $(SYMLINKS) +include $(BUILD_PHONY_PACKAGE) + +endif + +endif diff --git a/twinstall.cpp b/twinstall.cpp index 91a1a3595..fb7b6678b 100644 --- a/twinstall.cpp +++ b/twinstall.cpp @@ -30,7 +30,6 @@ #include "twcommon.h" #include "mincrypt/rsa.h" #include "mincrypt/sha.h" -#include "minui/minui.h" #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "minzip/SysUtil.h" @@ -24,7 +24,6 @@ #include "cutils/properties.h" extern "C" { -#include "minadbd/adb.h" #include "bootloader.h" } @@ -45,6 +44,13 @@ extern "C" { #include "openrecoveryscript.hpp" #include "variables.h" #include "twrpDU.hpp" +#ifdef TW_USE_NEW_MINADBD +#include "adb.h" +#else +extern "C" { +#include "minadbd.old/adb.h" +} +#endif #ifdef HAVE_SELINUX #include "selinux/label.h" @@ -78,7 +84,11 @@ int main(int argc, char **argv) { // Handle ADB sideload if (argc == 3 && strcmp(argv[1], "--adbd") == 0) { property_set("ctl.stop", "adbd"); +#ifdef TW_USE_NEW_MINADBD + adb_main(0, DEFAULT_ADB_PORT); +#else adb_main(argv[2]); +#endif return 0; } diff --git a/twrpTar.cpp b/twrpTar.cpp index bbe512598..b6e4410ae 100644 --- a/twrpTar.cpp +++ b/twrpTar.cpp @@ -467,7 +467,8 @@ int twrpTar::extractTarFork(const unsigned long long *overall_size, unsigned lon twrpTar tars[9]; pthread_t tar_thread[9]; pthread_attr_t tattr; - int thread_count = 0, i, start_thread_id = 1, ret, thread_error = 0; + unsigned thread_count = 0, i, start_thread_id = 1; + int ret, thread_error = 0; void *thread_return; basefn = tarfn; diff --git a/twrpTar.hpp b/twrpTar.hpp index 63cb3752a..a486c4174 100644 --- a/twrpTar.hpp +++ b/twrpTar.hpp @@ -98,5 +98,5 @@ private: string password; std::vector<TarListStruct> *ItemList; - int thread_id; + unsigned thread_id; }; @@ -41,40 +41,59 @@ #define UI_WAIT_KEY_TIMEOUT_SEC 120 -// There's only (at most) one of these objects, and global callbacks -// (for pthread_create, and the input event system) need to find it, -// so use a global variable. -static RecoveryUI* self = NULL; - -RecoveryUI::RecoveryUI() : - key_queue_len(0), - key_last_down(-1), - key_long_press(false), - key_down_count(0), - enable_reboot(true), - consecutive_power_keys(0), - consecutive_alternate_keys(0), - last_key(-1) { - pthread_mutex_init(&key_queue_mutex, NULL); - pthread_cond_init(&key_queue_cond, NULL); - self = this; +RecoveryUI::RecoveryUI() + : key_queue_len(0), + key_last_down(-1), + key_long_press(false), + key_down_count(0), + enable_reboot(true), + consecutive_power_keys(0), + last_key(-1), + has_power_key(false), + has_up_key(false), + has_down_key(false) { + pthread_mutex_init(&key_queue_mutex, nullptr); + pthread_cond_init(&key_queue_cond, nullptr); memset(key_pressed, 0, sizeof(key_pressed)); } -void RecoveryUI::Init() { - ev_init(input_callback, NULL); - pthread_create(&input_t, NULL, input_thread, NULL); +void RecoveryUI::OnKeyDetected(int key_code) { + if (key_code == KEY_POWER) { + has_power_key = true; + } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) { + has_down_key = true; + } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) { + has_up_key = true; + } } +int RecoveryUI::InputCallback(int fd, uint32_t epevents, void* data) { + return reinterpret_cast<RecoveryUI*>(data)->OnInputEvent(fd, epevents); +} -int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data) -{ - struct input_event ev; - int ret; +// Reads input events, handles special hot keys, and adds to the key queue. +static void* InputThreadLoop(void*) { + while (true) { + if (!ev_wait(-1)) { + ev_dispatch(); + } + } + return nullptr; +} + +void RecoveryUI::Init() { + ev_init(InputCallback, this); + + ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); - ret = ev_get_input(fd, epevents, &ev); - if (ret) + pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr); +} + +int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { + struct input_event ev; + if (ev_get_input(fd, epevents, &ev) == -1) { return -1; + } if (ev.type == EV_SYN) { return 0; @@ -84,23 +103,24 @@ int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data) // the trackball. When it exceeds a threshold // (positive or negative), fake an up/down // key event. - self->rel_sum += ev.value; - if (self->rel_sum > 3) { - self->process_key(KEY_DOWN, 1); // press down key - self->process_key(KEY_DOWN, 0); // and release it - self->rel_sum = 0; - } else if (self->rel_sum < -3) { - self->process_key(KEY_UP, 1); // press up key - self->process_key(KEY_UP, 0); // and release it - self->rel_sum = 0; + rel_sum += ev.value; + if (rel_sum > 3) { + ProcessKey(KEY_DOWN, 1); // press down key + ProcessKey(KEY_DOWN, 0); // and release it + rel_sum = 0; + } else if (rel_sum < -3) { + ProcessKey(KEY_UP, 1); // press up key + ProcessKey(KEY_UP, 0); // and release it + rel_sum = 0; } } } else { - self->rel_sum = 0; + rel_sum = 0; } - if (ev.type == EV_KEY && ev.code <= KEY_MAX) - self->process_key(ev.code, ev.value); + if (ev.type == EV_KEY && ev.code <= KEY_MAX) { + ProcessKey(ev.code, ev.value); + } return 0; } @@ -117,7 +137,7 @@ int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data) // a key is registered. // // updown == 1 for key down events; 0 for key up events -void RecoveryUI::process_key(int key_code, int updown) { +void RecoveryUI::ProcessKey(int key_code, int updown) { bool register_key = false; bool long_press = false; bool reboot_enabled; @@ -128,13 +148,13 @@ void RecoveryUI::process_key(int key_code, int updown) { ++key_down_count; key_last_down = key_code; key_long_press = false; - pthread_t th; key_timer_t* info = new key_timer_t; info->ui = this; info->key_code = key_code; info->count = key_down_count; - pthread_create(&th, NULL, &RecoveryUI::time_key_helper, info); - pthread_detach(th); + pthread_t thread; + pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info); + pthread_detach(thread); } else { if (key_last_down == key_code) { long_press = key_long_press; @@ -146,8 +166,7 @@ void RecoveryUI::process_key(int key_code, int updown) { pthread_mutex_unlock(&key_queue_mutex); if (register_key) { - NextCheckKeyIsLong(long_press); - switch (CheckKey(key_code)) { + switch (CheckKey(key_code, long_press)) { case RecoveryUI::IGNORE: break; @@ -166,13 +185,6 @@ void RecoveryUI::process_key(int key_code, int updown) { case RecoveryUI::ENQUEUE: EnqueueKey(key_code); break; - - case RecoveryUI::MOUNT_SYSTEM: -#ifndef NO_RECOVERY_MOUNT - ensure_path_mounted("/system"); - Print("Mounted /system."); -#endif - break; } } } @@ -181,7 +193,7 @@ void* RecoveryUI::time_key_helper(void* cookie) { key_timer_t* info = (key_timer_t*) cookie; info->ui->time_key(info->key_code, info->count); delete info; - return NULL; + return nullptr; } void RecoveryUI::time_key(int key_code, int count) { @@ -205,19 +217,7 @@ void RecoveryUI::EnqueueKey(int key_code) { pthread_mutex_unlock(&key_queue_mutex); } - -// Reads input events, handles special hot keys, and adds to the key queue. -void* RecoveryUI::input_thread(void *cookie) -{ - for (;;) { - if (!ev_wait(-1)) - ev_dispatch(); - } - return NULL; -} - -int RecoveryUI::WaitKey() -{ +int RecoveryUI::WaitKey() { pthread_mutex_lock(&key_queue_mutex); // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is @@ -225,17 +225,16 @@ int RecoveryUI::WaitKey() do { struct timeval now; struct timespec timeout; - gettimeofday(&now, NULL); + gettimeofday(&now, nullptr); timeout.tv_sec = now.tv_sec; timeout.tv_nsec = now.tv_usec * 1000; timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; int rc = 0; while (key_queue_len == 0 && rc != ETIMEDOUT) { - rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, - &timeout); + rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout); } - } while (usb_connected() && key_queue_len == 0); + } while (IsUsbConnected() && key_queue_len == 0); int key = -1; if (key_queue_len > 0) { @@ -246,8 +245,7 @@ int RecoveryUI::WaitKey() return key; } -// Return true if USB is connected. -bool RecoveryUI::usb_connected() { +bool RecoveryUI::IsUsbConnected() { int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); if (fd < 0) { printf("failed to open /sys/class/android_usb/android0/state: %s\n", @@ -256,8 +254,8 @@ bool RecoveryUI::usb_connected() { } char buf; - /* USB is connected if android_usb state is CONNECTED or CONFIGURED */ - int connected = (read(fd, &buf, 1) == 1) && (buf == 'C'); + // USB is connected if android_usb state is CONNECTED or CONFIGURED. + int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C'); if (close(fd) < 0) { printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno)); @@ -265,31 +263,55 @@ bool RecoveryUI::usb_connected() { return connected; } -bool RecoveryUI::IsKeyPressed(int key) -{ +bool RecoveryUI::IsKeyPressed(int key) { pthread_mutex_lock(&key_queue_mutex); int pressed = key_pressed[key]; pthread_mutex_unlock(&key_queue_mutex); return pressed; } +bool RecoveryUI::IsLongPress() { + pthread_mutex_lock(&key_queue_mutex); + bool result = key_long_press; + pthread_mutex_unlock(&key_queue_mutex); + return result; +} + +bool RecoveryUI::HasThreeButtons() { + return has_power_key && has_up_key && has_down_key; +} + void RecoveryUI::FlushKeys() { pthread_mutex_lock(&key_queue_mutex); key_queue_len = 0; pthread_mutex_unlock(&key_queue_mutex); } -// The default CheckKey implementation assumes the device has power, -// volume up, and volume down keys. -// -// - Hold power and press vol-up to toggle display. -// - Press power seven times in a row to reboot. -// - Alternate vol-up and vol-down seven times to mount /system. -RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { - if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) { - return TOGGLE; +RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { + pthread_mutex_lock(&key_queue_mutex); + key_long_press = false; + pthread_mutex_unlock(&key_queue_mutex); + + // If we have power and volume up keys, that chord is the signal to toggle the text display. + if (HasThreeButtons()) { + if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) { + return TOGGLE; + } + } else { + // Otherwise long press of any button toggles to the text display, + // and there's no way to toggle back (but that's pretty useless anyway). + if (is_long_press && !IsTextVisible()) { + return TOGGLE; + } + + // Also, for button-limited devices, a long press is translated to KEY_ENTER. + if (is_long_press && IsTextVisible()) { + EnqueueKey(KEY_ENTER); + return IGNORE; + } } + // Press power seven times in a row to reboot. if (key == KEY_POWER) { pthread_mutex_lock(&key_queue_mutex); bool reboot_enabled = enable_reboot; @@ -305,27 +327,11 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { consecutive_power_keys = 0; } - if ((key == KEY_VOLUMEUP && - (last_key == KEY_VOLUMEDOWN || last_key == -1)) || - (key == KEY_VOLUMEDOWN && - (last_key == KEY_VOLUMEUP || last_key == -1))) { - ++consecutive_alternate_keys; - if (consecutive_alternate_keys >= 7) { - consecutive_alternate_keys = 0; - return MOUNT_SYSTEM; - } - } else { - consecutive_alternate_keys = 0; - } last_key = key; - - return ENQUEUE; -} - -void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) { + return IsTextVisible() ? ENQUEUE : IGNORE; } -void RecoveryUI::KeyLongPress(int key) { +void RecoveryUI::KeyLongPress(int) { } void RecoveryUI::SetEnableReboot(bool enabled) { @@ -31,10 +31,10 @@ class RecoveryUI { // Initialize the object; called before anything else. virtual void Init(); // Show a stage indicator. Call immediately after Init(). - virtual void SetStage(int current, int max) { } + virtual void SetStage(int current, int max) = 0; // After calling Init(), you can tell the UI what locale it is operating in. - virtual void SetLocale(const char* locale) { } + virtual void SetLocale(const char* locale) = 0; // Set the overall recovery state ("background image"). enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; @@ -63,34 +63,37 @@ class RecoveryUI { // Write a message to the on-screen log (shown if the user has // toggled on the text display). - virtual void Print(const char* fmt, ...) = 0; // __attribute__((format(printf, 1, 2))) = 0; + virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0; + + virtual void ShowFile(const char* filename) = 0; // --- key handling --- - // Wait for keypress and return it. May return -1 after timeout. + // Wait for a key and return it. May return -1 after timeout. virtual int WaitKey(); virtual bool IsKeyPressed(int key); + virtual bool IsLongPress(); + + // Returns true if you have the volume up/down and power trio typical + // of phones and tablets, false otherwise. + virtual bool HasThreeButtons(); // Erase any queued-up keys. virtual void FlushKeys(); - // Called on each keypress, even while operations are in progress. + // Called on each key press, even while operations are in progress. // Return value indicates whether an immediate operation should be // triggered (toggling the display, rebooting the device), or if // the key should be enqueued for use by the main thread. - enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE, MOUNT_SYSTEM }; - virtual KeyAction CheckKey(int key); - - // Called immediately before each call to CheckKey(), tell you if - // the key was long-pressed. - virtual void NextCheckKeyIsLong(bool is_long_press); + enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; + virtual KeyAction CheckKey(int key, bool is_long_press); // Called when a key is held down long enough to have been a // long-press (but before the key is released). This means that // if the key is eventually registered (released without any other - // keys being pressed in the meantime), NextCheckKeyIsLong() will - // be called with "true". + // keys being pressed in the meantime), CheckKey will be called with + // 'is_long_press' true. virtual void KeyLongPress(int key); // Normally in recovery there's a key sequence that triggers @@ -108,8 +111,8 @@ class RecoveryUI { virtual void StartMenu(const char* const * headers, const char* const * items, int initial_selection) = 0; - // Set the menu highlight to the given index, and return it (capped to - // the range [0..numitems). + // Set the menu highlight to the given index, wrapping if necessary. + // Returns the actual item selected. virtual int SelectMenu(int sel) = 0; // End menu mode, resetting the text overlay so that ui_print() @@ -132,21 +135,27 @@ private: int rel_sum; int consecutive_power_keys; - int consecutive_alternate_keys; int last_key; - typedef struct { + bool has_power_key; + bool has_up_key; + bool has_down_key; + + struct key_timer_t { RecoveryUI* ui; int key_code; int count; - } key_timer_t; + }; + + pthread_t input_thread_; + + void OnKeyDetected(int key_code); - pthread_t input_t; + static int InputCallback(int fd, uint32_t epevents, void* data); + int OnInputEvent(int fd, uint32_t epevents); + void ProcessKey(int key_code, int updown); - static void* input_thread(void* cookie); - static int input_callback(int fd, uint32_t epevents, void* data); - void process_key(int key_code, int updown); - bool usb_connected(); + bool IsUsbConnected(); static void* time_key_helper(void* cookie); void time_key(int key_code, int count); diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index e782ef43d..bd769f900 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -17,10 +17,11 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_C_INCLUDES += $(commands_recovery_local_path) -LOCAL_SRC_FILES := uncrypt.c -LOCAL_MODULE := uncrypt +LOCAL_SRC_FILES := uncrypt.cpp LOCAL_MODULE_TAGS := optional -LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils +LOCAL_MODULE := uncrypt + +LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils include $(BUILD_EXECUTABLE) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.cpp index 7fb0989a7..1db3013c6 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.cpp @@ -39,36 +39,43 @@ // Recovery can take this block map file and retrieve the underlying // file data to use as an update package. -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <sys/types.h> -#include <sys/stat.h> +#include <errno.h> #include <fcntl.h> #include <linux/fs.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> -#define LOG_TAG "uncrypt" -#include <log/log.h> +#include <base/file.h> +#include <base/strings.h> #include <cutils/properties.h> #include <fs_mgr.h> +#define LOG_TAG "uncrypt" +#include <log/log.h> #define WINDOW_SIZE 5 -#define RECOVERY_COMMAND_FILE "/cache/recovery/command" -#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" -#define CACHE_BLOCK_MAP "/cache/recovery/block.map" + +static const std::string cache_block_map = "/cache/recovery/block.map"; +static const std::string status_file = "/cache/recovery/uncrypt_status"; +static const std::string uncrypt_file = "/cache/recovery/uncrypt_file"; static struct fstab* fstab = NULL; -static int write_at_offset(unsigned char* buffer, size_t size, - int wfd, off64_t offset) -{ - lseek64(wfd, offset, SEEK_SET); +static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) { + if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) { + ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno)); + return -1; + } size_t written = 0; while (written < size) { - ssize_t wrote = write(wfd, buffer + written, size - written); - if (wrote < 0) { - ALOGE("error writing offset %lld: %s\n", offset, strerror(errno)); + ssize_t wrote = TEMP_FAILURE_RETRY(write(wfd, buffer + written, size - written)); + if (wrote == -1) { + ALOGE("error writing offset %lld: %s\n", (offset + written), strerror(errno)); return -1; } written += wrote; @@ -76,8 +83,7 @@ static int write_at_offset(unsigned char* buffer, size_t size, return 0; } -void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) -{ +static void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) { // If the current block start is < 0, set the start to the new // block. (This only happens for the very first block of the very // first range.) @@ -96,7 +102,7 @@ void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int ne // If there isn't enough room in the array, we need to expand it. if (*range_used >= *range_alloc) { *range_alloc *= 2; - *ranges = realloc(*ranges, *range_alloc * 2 * sizeof(int)); + *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int))); } ++*range_used; @@ -105,8 +111,7 @@ void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int ne } } -static struct fstab* read_fstab() -{ +static struct fstab* read_fstab() { fstab = NULL; // The fstab path is always "/fstab.${ro.hardware}". @@ -125,26 +130,26 @@ static struct fstab* read_fstab() return fstab; } -const char* find_block_device(const char* path, int* encryptable, int* encrypted) -{ +static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted) { // Look for a volume whose mount point is the prefix of path and // return its block device. Set encrypted if it's currently // encrypted. - int i; - for (i = 0; i < fstab->num_entries; ++i) { + for (int i = 0; i < fstab->num_entries; ++i) { struct fstab_rec* v = &fstab->recs[i]; - if (!v->mount_point) continue; + if (!v->mount_point) { + continue; + } int len = strlen(v->mount_point); if (strncmp(path, v->mount_point, len) == 0 && (path[len] == '/' || path[len] == 0)) { - *encrypted = 0; - *encryptable = 0; + *encrypted = false; + *encryptable = false; if (fs_mgr_is_encryptable(v)) { - *encryptable = 1; + *encryptable = true; char buffer[PROPERTY_VALUE_MAX+1]; if (property_get("ro.crypto.state", buffer, "") && strcmp(buffer, "encrypted") == 0) { - *encrypted = 1; + *encrypted = true; } } return v->blk_device; @@ -154,56 +159,37 @@ const char* find_block_device(const char* path, int* encryptable, int* encrypted return NULL; } -char* parse_recovery_command_file() +// Parse uncrypt_file to find the update package name. +static bool find_uncrypt_package(std::string& package_name) { - char* fn = NULL; - int count = 0; - char temp[1024]; - - FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); - if (f == NULL) { - return NULL; - } - 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; + if (!android::base::ReadFileToString(uncrypt_file, &package_name)) { + ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno)); + return false; } - FILE* fo = fdopen(fd, "w"); - while (fgets(temp, sizeof(temp), f)) { - printf("read: %s", temp); - if (strncmp(temp, "--update_package=/data/", strlen("--update_package=/data/")) == 0) { - fn = strdup(temp + strlen("--update_package=")); - strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n"); - } - fputs(temp, fo); - } - fclose(f); - fsync(fd); - fclose(fo); + // Remove the trailing '\n' if present. + package_name = android::base::Trim(package_name); - if (fn) { - char* newline = strchr(fn, '\n'); - if (newline) *newline = 0; - } - return fn; + return true; } -int produce_block_map(const char* path, const char* map_file, const char* blk_dev, - int encrypted) -{ - struct stat sb; - int ret; - +static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, + bool encrypted, int status_fd) { int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); - if (mapfd < 0) { + if (mapfd == -1) { ALOGE("failed to open %s\n", map_file); return -1; } FILE* mapf = fdopen(mapfd, "w"); - ret = stat(path, &sb); + // Make sure we can write to the status_file. + if (!android::base::WriteStringToFd("0\n", status_fd)) { + ALOGE("failed to update \"%s\"\n", status_file.c_str()); + return -1; + } + + struct stat sb; + int ret = stat(path, &sb); if (ret != 0) { ALOGE("failed to stat %s\n", path); return -1; @@ -214,20 +200,18 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; ALOGI(" file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks); - int* ranges; int range_alloc = 1; int range_used = 1; - ranges = malloc(range_alloc * 2 * sizeof(int)); + int* ranges = reinterpret_cast<int*>(malloc(range_alloc * 2 * sizeof(int))); ranges[0] = -1; ranges[1] = -1; fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); unsigned char* buffers[WINDOW_SIZE]; - int i; if (encrypted) { - for (i = 0; i < WINDOW_SIZE; ++i) { - buffers[i] = malloc(sb.st_blksize); + for (size_t i = 0; i < WINDOW_SIZE; ++i) { + buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize)); } } int head_block = 0; @@ -239,7 +223,6 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de ALOGE("failed to open fd for reading: %s\n", strerror(errno)); return -1; } - fsync(fd); int wfd = -1; if (encrypted) { @@ -250,7 +233,15 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de } } + int last_progress = 0; while (pos < sb.st_size) { + // Update the status file, progress must be between [0, 99]. + int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size))); + if (progress > last_progress) { + last_progress = progress; + android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd); + } + if ((tail+1) % WINDOW_SIZE == head) { // write out head buffer int block = head_block; @@ -261,7 +252,8 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de } add_block_to_ranges(&ranges, &range_alloc, &range_used, block); if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) { + if (write_at_offset(buffers[head], sb.st_blksize, wfd, + (off64_t)sb.st_blksize * block) != 0) { return -1; } } @@ -273,8 +265,9 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de if (encrypted) { size_t so_far = 0; while (so_far < sb.st_blksize && pos < sb.st_size) { - ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far); - if (this_read < 0) { + ssize_t this_read = + TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far)); + if (this_read == -1) { ALOGE("failed to read: %s\n", strerror(errno)); return -1; } @@ -300,7 +293,8 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de } add_block_to_ranges(&ranges, &range_alloc, &range_used, block); if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) { + if (write_at_offset(buffers[head], sb.st_blksize, wfd, + (off64_t)sb.st_blksize * block) != 0) { return -1; } } @@ -309,25 +303,30 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de } fprintf(mapf, "%d\n", range_used); - for (i = 0; i < range_used; ++i) { + for (int i = 0; i < range_used; ++i) { fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); } - fsync(mapfd); + if (fsync(mapfd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno)); + return -1; + } fclose(mapf); close(fd); if (encrypted) { - fsync(wfd); + if (fsync(wfd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); + return -1; + } close(wfd); } return 0; } -void wipe_misc() { +static void wipe_misc() { ALOGI("removing old commands from misc"); - int i; - for (i = 0; i < fstab->num_entries; ++i) { + for (int i = 0; i < fstab->num_entries; ++i) { struct fstab_rec* v = &fstab->recs[i]; if (!v->mount_point) continue; if (strcmp(v->mount_point, "/misc") == 0) { @@ -338,72 +337,49 @@ void wipe_misc() { size_t written = 0; size_t size = sizeof(zeroes); while (written < size) { - ssize_t w = write(fd, zeroes, size-written); - if (w < 0 && errno != EINTR) { + ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written)); + if (w == -1) { ALOGE("zero write failed: %s\n", strerror(errno)); return; } else { written += w; } } - fsync(fd); + if (fsync(fd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); + close(fd); + return; + } close(fd); } } } -void reboot_to_recovery() { +static void reboot_to_recovery() { ALOGI("rebooting to recovery"); property_set("sys.powerctl", "reboot,recovery"); sleep(10); ALOGE("reboot didn't succeed?"); } -int main(int argc, char** argv) -{ - const char* input_path; - const char* map_file; - int do_reboot = 1; +int uncrypt(const char* input_path, const char* map_file, int status_fd) { - if (argc != 1 && argc != 3) { - fprintf(stderr, "usage: %s [<transform_path> <map_file>]\n", argv[0]); - return 2; - } - - if (argc == 3) { - // when command-line args are given this binary is being used - // for debugging; don't reboot to recovery at the end. - input_path = argv[1]; - map_file = argv[2]; - do_reboot = 0; - } else { - input_path = parse_recovery_command_file(); - if (input_path == NULL) { - // if we're rebooting to recovery without a package (say, - // to wipe data), then we don't need to do anything before - // going to recovery. - ALOGI("no recovery command file or no update package arg"); - reboot_to_recovery(); - return 1; - } - map_file = CACHE_BLOCK_MAP; - } - - ALOGI("update package is %s", input_path); + ALOGI("update package is \"%s\"", input_path); // Turn the name of the file we're supposed to convert into an // absolute path, so we can find what filesystem it's on. char path[PATH_MAX+1]; if (realpath(input_path, path) == NULL) { - ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno)); + ALOGE("failed to convert \"%s\" to absolute path: %s", input_path, strerror(errno)); return 1; } - int encryptable; - int encrypted; if (read_fstab() == NULL) { return 1; } + + bool encryptable; + bool encrypted; const char* blk_dev = find_block_device(path, &encryptable, &encrypted); if (blk_dev == NULL) { ALOGE("failed to find block device for %s", path); @@ -423,18 +399,67 @@ int main(int argc, char** argv) // On /data we want to convert the file to a block map so that we // can read the package without mounting the partition. On /cache // and /sdcard we leave the file alone. - if (strncmp(path, "/data/", 6) != 0) { - // path does not start with "/data/"; leave it alone. - unlink(RECOVERY_COMMAND_FILE_TMP); - } else { + if (strncmp(path, "/data/", 6) == 0) { ALOGI("writing block map %s", map_file); - if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { + if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) { return 1; } } - wipe_misc(); - rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); - if (do_reboot) reboot_to_recovery(); + return 0; +} + +int main(int argc, char** argv) { + const char* input_path; + const char* map_file; + + if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) { + fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]); + return 2; + } + + // When uncrypt is started with "--reboot", it wipes misc and reboots. + // Otherwise it uncrypts the package and writes the block map. + if (argc == 2) { + if (read_fstab() == NULL) { + return 1; + } + wipe_misc(); + reboot_to_recovery(); + } else { + // The pipe has been created by the system server. + int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); + if (status_fd == -1) { + ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno)); + return 1; + } + + if (argc == 3) { + // when command-line args are given this binary is being used + // for debugging. + input_path = argv[1]; + map_file = argv[2]; + } else { + std::string package; + if (!find_uncrypt_package(package)) { + android::base::WriteStringToFd("-1\n", status_fd); + close(status_fd); + return 1; + } + input_path = package.c_str(); + map_file = cache_block_map.c_str(); + } + + int status = uncrypt(input_path, map_file, status_fd); + if (status != 0) { + android::base::WriteStringToFd("-1\n", status_fd); + close(status_fd); + return 1; + } + + android::base::WriteStringToFd("100\n", status_fd); + close(status_fd); + } + return 0; } diff --git a/updater/Android.mk b/updater/Android.mk index 3fbeef39f..bc763a0ea 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -97,7 +97,7 @@ $(inc) : $(inc_dep_file) $(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;) $(hide) echo "}" >> $@ -$(call intermediates-dir-for,EXECUTABLES,updater)/updater.o : $(inc) +$(call intermediates-dir-for,EXECUTABLES,updater,,,$(TARGET_PREFER_32_BIT))/updater.o : $(inc) LOCAL_C_INCLUDES += $(dir $(inc)) inc := diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL deleted file mode 100644 index e69de29bb..000000000 --- a/updater/MODULE_LICENSE_GPL +++ /dev/null diff --git a/updater/NOTICE b/updater/NOTICE deleted file mode 100644 index e77696ae8..000000000 --- a/updater/NOTICE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) 19yy <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/updater/blockimg.c b/updater/blockimg.c index 302689313..b006d10c5 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -16,13 +16,16 @@ #include <ctype.h> #include <errno.h> +#include <dirent.h> #include <fcntl.h> #include <inttypes.h> +#include <libgen.h> #include <pthread.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/ioctl.h> @@ -32,7 +35,7 @@ #include "applypatch/applypatch.h" #include "edify/expr.h" #include "mincrypt/sha.h" -#include "minzip/DirUtil.h" +#include "minzip/Hash.h" #include "updater.h" #define BLOCKSIZE 4096 @@ -46,6 +49,10 @@ #define BLKDISCARD _IO(0x12,119) #endif +#define STASH_DIRECTORY_BASE "/cache/recovery" +#define STASH_DIRECTORY_MODE 0700 +#define STASH_FILE_MODE 0600 + char* PrintSha1(const uint8_t* digest); typedef struct { @@ -80,44 +87,69 @@ static RangeSet* parse_range(char* text) { return out; } -static void readblock(int fd, uint8_t* data, size_t size) { +static int range_overlaps(RangeSet* r1, RangeSet* r2) { + int i, j, r1_0, r1_1, r2_0, r2_1; + + if (!r1 || !r2) { + return 0; + } + + for (i = 0; i < r1->count; ++i) { + r1_0 = r1->pos[i * 2]; + r1_1 = r1->pos[i * 2 + 1]; + + for (j = 0; j < r2->count; ++j) { + r2_0 = r2->pos[j * 2]; + r2_1 = r2->pos[j * 2 + 1]; + + if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) { + return 1; + } + } + } + + return 0; +} + +static int read_all(int fd, uint8_t* data, size_t size) { size_t so_far = 0; while (so_far < size) { - ssize_t r = read(fd, data+so_far, size-so_far); - if (r < 0 && errno != EINTR) { + ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far)); + if (r == -1) { fprintf(stderr, "read failed: %s\n", strerror(errno)); - return; - } else { - so_far += r; + return -1; } + so_far += r; } + return 0; } -static void writeblock(int fd, const uint8_t* data, size_t size) { +static int write_all(int fd, const uint8_t* data, size_t size) { size_t written = 0; while (written < size) { - ssize_t w = write(fd, data+written, size-written); - if (w < 0 && errno != EINTR) { + ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written)); + if (w == -1) { fprintf(stderr, "write failed: %s\n", strerror(errno)); - return; - } else { - written += w; + return -1; } + written += w; } + + if (fsync(fd) == -1) { + fprintf(stderr, "fsync failed: %s\n", strerror(errno)); + return -1; + } + + return 0; } -static void check_lseek(int fd, off64_t offset, int whence) { - while (true) { - off64_t ret = lseek64(fd, offset, whence); - if (ret < 0) { - if (errno != EINTR) { - fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); - exit(1); - } - } else { - break; - } +static bool check_lseek(int fd, off64_t offset, int whence) { + off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence)); + if (rc == -1) { + fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); + return false; } + return true; } static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { @@ -146,14 +178,21 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { if (rss->p_remain <= 0) { fprintf(stderr, "range sink write overrun"); - exit(1); + return 0; } ssize_t written = 0; while (size > 0) { size_t write_now = size; - if (rss->p_remain < write_now) write_now = rss->p_remain; - writeblock(rss->fd, data, write_now); + + if (rss->p_remain < write_now) { + write_now = rss->p_remain; + } + + if (write_all(rss->fd, data, write_now) == -1) { + break; + } + data += write_now; size -= write_now; @@ -164,12 +203,17 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { // move to the next block ++rss->p_block; if (rss->p_block < rss->tgt->count) { - rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; - check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); + rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] - + rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE; + + if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, + SEEK_SET)) { + break; + } } else { // we can't write any more; return how many bytes have // been written so far. - return written; + break; } } } @@ -245,6 +289,58 @@ static void* unzip_new_data(void* cookie) { return NULL; } +static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) { + int i; + size_t p = 0; + size_t size; + + if (!src || !buffer) { + return -1; + } + + for (i = 0; i < src->count; ++i) { + if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + return -1; + } + + size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE; + + if (read_all(fd, buffer + p, size) == -1) { + return -1; + } + + p += size; + } + + return 0; +} + +static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) { + int i; + size_t p = 0; + size_t size; + + if (!tgt || !buffer) { + return -1; + } + + for (i = 0; i < tgt->count; ++i) { + if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + return -1; + } + + size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE; + + if (write_all(fd, buffer + p, size) == -1) { + return -1; + } + + p += size; + } + + return 0; +} + // 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: @@ -255,30 +351,525 @@ static void* unzip_new_data(void* cookie) { // 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, +static int LoadSrcTgtVersion1(char** wordsave, RangeSet** tgt, int* src_blocks, uint8_t** buffer, size_t* buffer_alloc, int fd) { char* word; + int rc; - word = strtok_r(NULL, " ", &wordsave); + word = strtok_r(NULL, " ", wordsave); RangeSet* src = parse_range(word); if (tgt != NULL) { - word = strtok_r(NULL, " ", &wordsave); + 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; - } - + rc = ReadBlocks(src, *buffer, fd); *src_blocks = src->size; + free(src); + return rc; +} + +static int VerifyBlocks(const char *expected, const uint8_t *buffer, + size_t blocks, int printerror) { + char* hexdigest = NULL; + int rc = -1; + uint8_t digest[SHA_DIGEST_SIZE]; + + if (!expected || !buffer) { + return rc; + } + + SHA_hash(buffer, blocks * BLOCKSIZE, digest); + hexdigest = PrintSha1(digest); + + if (hexdigest != NULL) { + rc = strcmp(expected, hexdigest); + + if (rc != 0 && printerror) { + fprintf(stderr, "failed to verify blocks (expected %s, read %s)\n", + expected, hexdigest); + } + + free(hexdigest); + } + + return rc; +} + +static char* GetStashFileName(const char* base, const char* id, const char* postfix) { + char* fn; + int len; + int res; + + if (base == NULL) { + return NULL; + } + + if (id == NULL) { + id = ""; + } + + if (postfix == NULL) { + postfix = ""; + } + + len = strlen(STASH_DIRECTORY_BASE) + 1 + strlen(base) + 1 + strlen(id) + strlen(postfix) + 1; + fn = malloc(len); + + if (fn == NULL) { + fprintf(stderr, "failed to malloc %d bytes for fn\n", len); + return NULL; + } + + res = snprintf(fn, len, STASH_DIRECTORY_BASE "/%s/%s%s", base, id, postfix); + + if (res < 0 || res >= len) { + fprintf(stderr, "failed to format file name (return value %d)\n", res); + free(fn); + return NULL; + } + + return fn; +} + +typedef void (*StashCallback)(const char*, void*); + +// Does a best effort enumeration of stash files. Ignores possible non-file +// items in the stash directory and continues despite of errors. Calls the +// 'callback' function for each file and passes 'data' to the function as a +// parameter. + +static void EnumerateStash(const char* dirname, StashCallback callback, void* data) { + char* fn; + DIR* directory; + int len; + int res; + struct dirent* item; + + if (dirname == NULL || callback == NULL) { + return; + } + + directory = opendir(dirname); + + if (directory == NULL) { + if (errno != ENOENT) { + fprintf(stderr, "opendir \"%s\" failed: %s\n", dirname, strerror(errno)); + } + return; + } + + while ((item = readdir(directory)) != NULL) { + if (item->d_type != DT_REG) { + continue; + } + + len = strlen(dirname) + 1 + strlen(item->d_name) + 1; + fn = malloc(len); + + if (fn == NULL) { + fprintf(stderr, "failed to malloc %d bytes for fn\n", len); + continue; + } + + res = snprintf(fn, len, "%s/%s", dirname, item->d_name); + + if (res < 0 || res >= len) { + fprintf(stderr, "failed to format file name (return value %d)\n", res); + free(fn); + continue; + } + + callback(fn, data); + free(fn); + } + + if (closedir(directory) == -1) { + fprintf(stderr, "closedir \"%s\" failed: %s\n", dirname, strerror(errno)); + } +} + +static void UpdateFileSize(const char* fn, void* data) { + int* size = (int*) data; + struct stat st; + + if (!fn || !data) { + return; + } + + if (stat(fn, &st) == -1) { + fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno)); + return; + } + + *size += st.st_size; +} + +// Deletes the stash directory and all files in it. Assumes that it only +// contains files. There is nothing we can do about unlikely, but possible +// errors, so they are merely logged. + +static void DeleteFile(const char* fn, void* data) { + if (fn) { + fprintf(stderr, "deleting %s\n", fn); + + if (unlink(fn) == -1 && errno != ENOENT) { + fprintf(stderr, "unlink \"%s\" failed: %s\n", fn, strerror(errno)); + } + } +} + +static void DeletePartial(const char* fn, void* data) { + if (fn && strstr(fn, ".partial") != NULL) { + DeleteFile(fn, data); + } +} + +static void DeleteStash(const char* base) { + char* dirname; + + if (base == NULL) { + return; + } + + dirname = GetStashFileName(base, NULL, NULL); + + if (dirname == NULL) { + return; + } + + fprintf(stderr, "deleting stash %s\n", base); + EnumerateStash(dirname, DeleteFile, NULL); + + if (rmdir(dirname) == -1) { + if (errno != ENOENT && errno != ENOTDIR) { + fprintf(stderr, "rmdir \"%s\" failed: %s\n", dirname, strerror(errno)); + } + } + + free(dirname); +} + +static int LoadStash(const char* base, const char* id, int verify, int* blocks, uint8_t** buffer, + size_t* buffer_alloc, int printnoent) { + char *fn = NULL; + int blockcount = 0; + int fd = -1; + int rc = -1; + int res; + struct stat st; + + if (!base || !id || !buffer || !buffer_alloc) { + goto lsout; + } + + if (!blocks) { + blocks = &blockcount; + } + + fn = GetStashFileName(base, id, NULL); + + if (fn == NULL) { + goto lsout; + } + + res = stat(fn, &st); + + if (res == -1) { + if (errno != ENOENT || printnoent) { + fprintf(stderr, "stat \"%s\" failed: %s\n", fn, strerror(errno)); + } + goto lsout; + } + + fprintf(stderr, " loading %s\n", fn); + + if ((st.st_size % BLOCKSIZE) != 0) { + fprintf(stderr, "%s size %zd not multiple of block size %d", fn, st.st_size, BLOCKSIZE); + goto lsout; + } + + fd = TEMP_FAILURE_RETRY(open(fn, O_RDONLY)); + + if (fd == -1) { + fprintf(stderr, "open \"%s\" failed: %s\n", fn, strerror(errno)); + goto lsout; + } + + allocate(st.st_size, buffer, buffer_alloc); + + if (read_all(fd, *buffer, st.st_size) == -1) { + goto lsout; + } + + *blocks = st.st_size / BLOCKSIZE; + + if (verify && VerifyBlocks(id, *buffer, *blocks, 1) != 0) { + fprintf(stderr, "unexpected contents in %s\n", fn); + DeleteFile(fn, NULL); + goto lsout; + } + + rc = 0; + +lsout: + if (fd != -1) { + close(fd); + } + + if (fn) { + free(fn); + } + + return rc; +} + +static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buffer, + int checkspace, int *exists) { + char *fn = NULL; + char *cn = NULL; + int fd = -1; + int rc = -1; + int dfd = -1; + int res; + struct stat st; + + if (base == NULL || buffer == NULL) { + goto wsout; + } + + if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) { + fprintf(stderr, "not enough space to write stash\n"); + goto wsout; + } + + fn = GetStashFileName(base, id, ".partial"); + cn = GetStashFileName(base, id, NULL); + + if (fn == NULL || cn == NULL) { + goto wsout; + } + + if (exists) { + res = stat(cn, &st); + + if (res == 0) { + // The file already exists and since the name is the hash of the contents, + // it's safe to assume the contents are identical (accidental hash collisions + // are unlikely) + fprintf(stderr, " skipping %d existing blocks in %s\n", blocks, cn); + *exists = 1; + rc = 0; + goto wsout; + } + + *exists = 0; + } + + fprintf(stderr, " writing %d blocks to %s\n", blocks, cn); + + fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE)); + + if (fd == -1) { + fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno)); + goto wsout; + } + + if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) { + goto wsout; + } + + if (fsync(fd) == -1) { + fprintf(stderr, "fsync \"%s\" failed: %s\n", fn, strerror(errno)); + goto wsout; + } + + if (rename(fn, cn) == -1) { + fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", fn, cn, strerror(errno)); + goto wsout; + } + + const char* dname; + dname = dirname(cn); + dfd = TEMP_FAILURE_RETRY(open(dname, O_RDONLY | O_DIRECTORY)); + + if (dfd == -1) { + fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname, strerror(errno)); + goto wsout; + } + + if (fsync(dfd) == -1) { + fprintf(stderr, "fsync \"%s\" failed: %s\n", dname, strerror(errno)); + goto wsout; + } + + rc = 0; + +wsout: + if (fd != -1) { + close(fd); + } + + if (dfd != -1) { + close(dfd); + } + + if (fn) { + free(fn); + } + + if (cn) { + free(cn); + } + + return rc; +} + +// Creates a directory for storing stash files and checks if the /cache partition +// hash enough space for the expected amount of blocks we need to store. Returns +// >0 if we created the directory, zero if it existed already, and <0 of failure. + +static int CreateStash(State* state, int maxblocks, const char* blockdev, char** base) { + char* dirname = NULL; + const uint8_t* digest; + int rc = -1; + int res; + int size = 0; + SHA_CTX ctx; + struct stat st; + + if (blockdev == NULL || base == NULL) { + goto csout; + } + + // Stash directory should be different for each partition to avoid conflicts + // when updating multiple partitions at the same time, so we use the hash of + // the block device name as the base directory + SHA_init(&ctx); + SHA_update(&ctx, blockdev, strlen(blockdev)); + digest = SHA_final(&ctx); + *base = PrintSha1(digest); + + if (*base == NULL) { + goto csout; + } + + dirname = GetStashFileName(*base, NULL, NULL); + + if (dirname == NULL) { + goto csout; + } + + res = stat(dirname, &st); + + if (res == -1 && errno != ENOENT) { + ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname, strerror(errno)); + goto csout; + } else if (res != 0) { + fprintf(stderr, "creating stash %s\n", dirname); + res = mkdir(dirname, STASH_DIRECTORY_MODE); + + if (res != 0) { + ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname, strerror(errno)); + goto csout; + } + + if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) { + ErrorAbort(state, "not enough space for stash\n"); + goto csout; + } + + rc = 1; // Created directory + goto csout; + } + + fprintf(stderr, "using existing stash %s\n", dirname); + + // If the directory already exists, calculate the space already allocated to + // stash files and check if there's enough for all required blocks. Delete any + // partially completed stash files first. + + EnumerateStash(dirname, DeletePartial, NULL); + EnumerateStash(dirname, UpdateFileSize, &size); + + size = (maxblocks * BLOCKSIZE) - size; + + if (size > 0 && CacheSizeCheck(size) != 0) { + ErrorAbort(state, "not enough space for stash (%d more needed)\n", size); + goto csout; + } + + rc = 0; // Using existing directory + +csout: + if (dirname) { + free(dirname); + } + + return rc; +} + +static int SaveStash(const char* base, char** wordsave, uint8_t** buffer, size_t* buffer_alloc, + int fd, int usehash, int* isunresumable) { + char *id = NULL; + int res = -1; + int blocks = 0; + + if (!wordsave || !buffer || !buffer_alloc || !isunresumable) { + return -1; + } + + id = strtok_r(NULL, " ", wordsave); + + if (id == NULL) { + fprintf(stderr, "missing id field in stash command\n"); + return -1; + } + + if (usehash && LoadStash(base, id, 1, &blocks, buffer, buffer_alloc, 0) == 0) { + // Stash file already exists and has expected contents. Do not + // read from source again, as the source may have been already + // overwritten during a previous attempt. + return 0; + } + + if (LoadSrcTgtVersion1(wordsave, NULL, &blocks, buffer, buffer_alloc, fd) == -1) { + return -1; + } + + if (usehash && VerifyBlocks(id, *buffer, blocks, 1) != 0) { + // Source blocks have unexpected contents. If we actually need this + // data later, this is an unrecoverable error. However, the command + // that uses the data may have already completed previously, so the + // possible failure will occur during source block verification. + fprintf(stderr, "failed to load source blocks for stash %s\n", id); + return 0; + } + + fprintf(stderr, "stashing %d blocks to %s\n", blocks, id); + return WriteStash(base, id, blocks, *buffer, 0, NULL); +} + +static int FreeStash(const char* base, const char* id) { + char *fn = NULL; + + if (base == NULL || id == NULL) { + return -1; + } + + fn = GetStashFileName(base, id, NULL); + + if (fn == NULL) { + return -1; + } + + DeleteFile(fn, NULL); + free(fn); + + return 0; } static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) { @@ -312,64 +903,613 @@ static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) { // 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. +// target RangeSet. Any stashes required are loaded using LoadStash. -static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks, +static int LoadSrcTgtVersion2(char** wordsave, RangeSet** tgt, int* src_blocks, uint8_t** buffer, size_t* buffer_alloc, int fd, - uint8_t** stash_table) { + const char* stashbase, int* overlap) { char* word; + char* colonsave; + char* colon; + int id; + int res; + RangeSet* locs; + size_t stashalloc = 0; + uint8_t* stash = NULL; if (tgt != NULL) { - word = strtok_r(NULL, " ", &wordsave); + word = strtok_r(NULL, " ", wordsave); *tgt = parse_range(word); } - word = strtok_r(NULL, " ", &wordsave); + word = strtok_r(NULL, " ", wordsave); *src_blocks = strtol(word, NULL, 0); allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc); - word = strtok_r(NULL, " ", &wordsave); + word = strtok_r(NULL, " ", wordsave); if (word[0] == '-' && word[1] == '\0') { // no source ranges, only stashes } else { RangeSet* src = parse_range(word); + res = ReadBlocks(src, *buffer, fd); - 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; + if (overlap && tgt) { + *overlap = range_overlaps(src, *tgt); } + free(src); - word = strtok_r(NULL, " ", &wordsave); + if (res == -1) { + return -1; + } + + word = strtok_r(NULL, " ", wordsave); if (word == NULL) { // no stashes, only source range - return; + return 0; } - RangeSet* locs = parse_range(word); + locs = parse_range(word); MoveRange(*buffer, locs, *buffer); + free(locs); } - while ((word = strtok_r(NULL, " ", &wordsave)) != NULL) { + 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); + colonsave = NULL; + colon = strtok_r(word, ":", &colonsave); + + res = LoadStash(stashbase, colon, 0, NULL, &stash, &stashalloc, 1); + + if (res == -1) { + // These source blocks will fail verification if used later, but we + // will let the caller decide if this is a fatal failure + fprintf(stderr, "failed to load stash %s\n", colon); + continue; + } + 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; + locs = parse_range(colon); + + MoveRange(*buffer, locs, stash); free(locs); } + + if (stash) { + free(stash); + } + + return 0; +} + +// Parameters for transfer list command functions +typedef struct { + char* cmdname; + char* cpos; + char* freestash; + char* stashbase; + int canwrite; + int createdstash; + int fd; + int foundwrites; + int isunresumable; + int version; + int written; + NewThreadInfo nti; + pthread_t thread; + size_t bufsize; + uint8_t* buffer; + uint8_t* patch_start; +} CommandParameters; + +// Do a source/target load for move/bsdiff/imgdiff in version 3. +// +// Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which +// tells the function whether to expect separate source and targe block hashes, or +// if they are both the same and only one hash should be expected, and +// 'isunresumable', which receives a non-zero value if block verification fails in +// a way that the update cannot be resumed anymore. +// +// If the function is unable to load the necessary blocks or their contents don't +// match the hashes, the return value is -1 and the command should be aborted. +// +// If the return value is 1, the command has already been completed according to +// the contents of the target blocks, and should not be performed again. +// +// If the return value is 0, source blocks have expected content and the command +// can be performed. + +static int LoadSrcTgtVersion3(CommandParameters* params, RangeSet** tgt, int* src_blocks, + int onehash, int* overlap) { + char* srchash = NULL; + char* tgthash = NULL; + int stash_exists = 0; + int overlap_blocks = 0; + int rc = -1; + uint8_t* tgtbuffer = NULL; + + if (!params|| !tgt || !src_blocks || !overlap) { + goto v3out; + } + + srchash = strtok_r(NULL, " ", ¶ms->cpos); + + if (srchash == NULL) { + fprintf(stderr, "missing source hash\n"); + goto v3out; + } + + if (onehash) { + tgthash = srchash; + } else { + tgthash = strtok_r(NULL, " ", ¶ms->cpos); + + if (tgthash == NULL) { + fprintf(stderr, "missing target hash\n"); + goto v3out; + } + } + + if (LoadSrcTgtVersion2(¶ms->cpos, tgt, src_blocks, ¶ms->buffer, ¶ms->bufsize, + params->fd, params->stashbase, overlap) == -1) { + goto v3out; + } + + tgtbuffer = (uint8_t*) malloc((*tgt)->size * BLOCKSIZE); + + if (tgtbuffer == NULL) { + fprintf(stderr, "failed to allocate %d bytes\n", (*tgt)->size * BLOCKSIZE); + goto v3out; + } + + if (ReadBlocks(*tgt, tgtbuffer, params->fd) == -1) { + goto v3out; + } + + if (VerifyBlocks(tgthash, tgtbuffer, (*tgt)->size, 0) == 0) { + // Target blocks already have expected content, command should be skipped + rc = 1; + goto v3out; + } + + if (VerifyBlocks(srchash, params->buffer, *src_blocks, 1) == 0) { + // If source and target blocks overlap, stash the source blocks so we can + // resume from possible write errors + if (*overlap) { + fprintf(stderr, "stashing %d overlapping blocks to %s\n", *src_blocks, + srchash); + + if (WriteStash(params->stashbase, srchash, *src_blocks, params->buffer, 1, + &stash_exists) != 0) { + fprintf(stderr, "failed to stash overlapping source blocks\n"); + goto v3out; + } + + // Can be deleted when the write has completed + if (!stash_exists) { + params->freestash = srchash; + } + } + + // Source blocks have expected content, command can proceed + rc = 0; + goto v3out; + } + + if (*overlap && LoadStash(params->stashbase, srchash, 1, NULL, ¶ms->buffer, + ¶ms->bufsize, 1) == 0) { + // Overlapping source blocks were previously stashed, command can proceed. + // We are recovering from an interrupted command, so we don't know if the + // stash can safely be deleted after this command. + rc = 0; + goto v3out; + } + + // Valid source data not available, update cannot be resumed + fprintf(stderr, "partition has unexpected contents\n"); + params->isunresumable = 1; + +v3out: + if (tgtbuffer) { + free(tgtbuffer); + } + + return rc; +} + +static int PerformCommandMove(CommandParameters* params) { + int blocks = 0; + int overlap = 0; + int rc = -1; + int status = 0; + RangeSet* tgt = NULL; + + if (!params) { + goto pcmout; + } + + if (params->version == 1) { + status = LoadSrcTgtVersion1(¶ms->cpos, &tgt, &blocks, ¶ms->buffer, + ¶ms->bufsize, params->fd); + } else if (params->version == 2) { + status = LoadSrcTgtVersion2(¶ms->cpos, &tgt, &blocks, ¶ms->buffer, + ¶ms->bufsize, params->fd, params->stashbase, NULL); + } else if (params->version >= 3) { + status = LoadSrcTgtVersion3(params, &tgt, &blocks, 1, &overlap); + } + + if (status == -1) { + fprintf(stderr, "failed to read blocks for move\n"); + goto pcmout; + } + + if (status == 0) { + params->foundwrites = 1; + } else if (params->foundwrites) { + fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname); + } + + if (params->canwrite) { + if (status == 0) { + fprintf(stderr, " moving %d blocks\n", blocks); + + if (WriteBlocks(tgt, params->buffer, params->fd) == -1) { + goto pcmout; + } + } else { + fprintf(stderr, "skipping %d already moved blocks\n", blocks); + } + + } + + if (params->freestash) { + FreeStash(params->stashbase, params->freestash); + params->freestash = NULL; + } + + params->written += tgt->size; + rc = 0; + +pcmout: + if (tgt) { + free(tgt); + } + + return rc; +} + +static int PerformCommandStash(CommandParameters* params) { + if (!params) { + return -1; + } + + return SaveStash(params->stashbase, ¶ms->cpos, ¶ms->buffer, ¶ms->bufsize, + params->fd, (params->version >= 3), ¶ms->isunresumable); +} + +static int PerformCommandFree(CommandParameters* params) { + if (!params) { + return -1; + } + + if (params->createdstash || params->canwrite) { + return FreeStash(params->stashbase, params->cpos); + } + + return 0; +} + +static int PerformCommandZero(CommandParameters* params) { + char* range = NULL; + int i; + int j; + int rc = -1; + RangeSet* tgt = NULL; + + if (!params) { + goto pczout; + } + + range = strtok_r(NULL, " ", ¶ms->cpos); + + if (range == NULL) { + fprintf(stderr, "missing target blocks for zero\n"); + goto pczout; + } + + tgt = parse_range(range); + + fprintf(stderr, " zeroing %d blocks\n", tgt->size); + + allocate(BLOCKSIZE, ¶ms->buffer, ¶ms->bufsize); + memset(params->buffer, 0, BLOCKSIZE); + + if (params->canwrite) { + for (i = 0; i < tgt->count; ++i) { + if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + goto pczout; + } + + for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) { + if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) { + goto pczout; + } + } + } + } + + if (params->cmdname[0] == 'z') { + // Update only for the zero command, as the erase command will call + // this if DEBUG_ERASE is defined. + params->written += tgt->size; + } + + rc = 0; + +pczout: + if (tgt) { + free(tgt); + } + + return rc; +} + +static int PerformCommandNew(CommandParameters* params) { + char* range = NULL; + int rc = -1; + RangeSet* tgt = NULL; + RangeSinkState rss; + + if (!params) { + goto pcnout; + } + + range = strtok_r(NULL, " ", ¶ms->cpos); + + if (range == NULL) { + goto pcnout; + } + + tgt = parse_range(range); + + if (params->canwrite) { + fprintf(stderr, " writing %d blocks of new data\n", tgt->size); + + rss.fd = params->fd; + rss.tgt = tgt; + rss.p_block = 0; + rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; + + if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) { + goto pcnout; + } + + pthread_mutex_lock(¶ms->nti.mu); + params->nti.rss = &rss; + pthread_cond_broadcast(¶ms->nti.cv); + + while (params->nti.rss) { + pthread_cond_wait(¶ms->nti.cv, ¶ms->nti.mu); + } + + pthread_mutex_unlock(¶ms->nti.mu); + } + + params->written += tgt->size; + rc = 0; + +pcnout: + if (tgt) { + free(tgt); + } + + return rc; +} + +static int PerformCommandDiff(CommandParameters* params) { + char* logparams = NULL; + char* value = NULL; + int blocks = 0; + int overlap = 0; + int rc = -1; + int status = 0; + RangeSet* tgt = NULL; + RangeSinkState rss; + size_t len = 0; + size_t offset = 0; + Value patch_value; + + if (!params) { + goto pcdout; + } + + logparams = strdup(params->cpos); + value = strtok_r(NULL, " ", ¶ms->cpos); + + if (value == NULL) { + fprintf(stderr, "missing patch offset for %s\n", params->cmdname); + goto pcdout; + } + + offset = strtoul(value, NULL, 0); + + value = strtok_r(NULL, " ", ¶ms->cpos); + + if (value == NULL) { + fprintf(stderr, "missing patch length for %s\n", params->cmdname); + goto pcdout; + } + + len = strtoul(value, NULL, 0); + + if (params->version == 1) { + status = LoadSrcTgtVersion1(¶ms->cpos, &tgt, &blocks, ¶ms->buffer, + ¶ms->bufsize, params->fd); + } else if (params->version == 2) { + status = LoadSrcTgtVersion2(¶ms->cpos, &tgt, &blocks, ¶ms->buffer, + ¶ms->bufsize, params->fd, params->stashbase, NULL); + } else if (params->version >= 3) { + status = LoadSrcTgtVersion3(params, &tgt, &blocks, 0, &overlap); + } + + if (status == -1) { + fprintf(stderr, "failed to read blocks for diff\n"); + goto pcdout; + } + + if (status == 0) { + params->foundwrites = 1; + } else if (params->foundwrites) { + fprintf(stderr, "warning: commands executed out of order [%s]\n", params->cmdname); + } + + if (params->canwrite) { + if (status == 0) { + fprintf(stderr, "patching %d blocks to %d\n", blocks, tgt->size); + + patch_value.type = VAL_BLOB; + patch_value.size = len; + patch_value.data = (char*) (params->patch_start + offset); + + rss.fd = params->fd; + rss.tgt = tgt; + rss.p_block = 0; + rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; + + if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) { + goto pcdout; + } + + if (params->cmdname[0] == 'i') { // imgdiff + ApplyImagePatch(params->buffer, blocks * BLOCKSIZE, &patch_value, + &RangeSinkWrite, &rss, NULL, NULL); + } else { + ApplyBSDiffPatch(params->buffer, blocks * 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"); + } + } else { + fprintf(stderr, "skipping %d blocks already patched to %d [%s]\n", + blocks, tgt->size, logparams); + } + } + + if (params->freestash) { + FreeStash(params->stashbase, params->freestash); + params->freestash = NULL; + } + + params->written += tgt->size; + rc = 0; + +pcdout: + if (logparams) { + free(logparams); + } + + if (tgt) { + free(tgt); + } + + return rc; +} + +static int PerformCommandErase(CommandParameters* params) { + char* range = NULL; + int i; + int rc = -1; + RangeSet* tgt = NULL; + struct stat st; + uint64_t blocks[2]; + + if (DEBUG_ERASE) { + return PerformCommandZero(params); + } + + if (!params) { + goto pceout; + } + + if (fstat(params->fd, &st) == -1) { + fprintf(stderr, "failed to fstat device to erase: %s\n", strerror(errno)); + goto pceout; + } + + if (!S_ISBLK(st.st_mode)) { + fprintf(stderr, "not a block device; skipping erase\n"); + goto pceout; + } + + range = strtok_r(NULL, " ", ¶ms->cpos); + + if (range == NULL) { + fprintf(stderr, "missing target blocks for zero\n"); + goto pceout; + } + + tgt = parse_range(range); + + if (params->canwrite) { + fprintf(stderr, " erasing %d blocks\n", tgt->size); + + for (i = 0; i < tgt->count; ++i) { + // offset in bytes + blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE; + // length in bytes + blocks[1] = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * (uint64_t) BLOCKSIZE; + + if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) { + fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno)); + goto pceout; + } + } + } + + rc = 0; + +pceout: + if (tgt) { + free(tgt); + } + + return rc; +} + +// Definitions for transfer list command functions +typedef int (*CommandFunction)(CommandParameters*); + +typedef struct { + const char* name; + CommandFunction f; +} Command; + +// CompareCommands and CompareCommandNames are for the hash table + +static int CompareCommands(const void* c1, const void* c2) { + return strcmp(((const Command*) c1)->name, ((const Command*) c2)->name); +} + +static int CompareCommandNames(const void* c1, const void* c2) { + return strcmp(((const Command*) c1)->name, (const char*) c2); +} + +// HashString is used to hash command names for the hash table + +static unsigned int HashString(const char *s) { + unsigned int hash = 0; + if (s) { + while (*s) { + hash = hash * 33 + *s++; + } + } + return hash; } // args: @@ -378,379 +1518,372 @@ static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks, // - new data stream (filename within package.zip) // - patch stream (filename within package.zip, must be uncompressed) -Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { - Value* blockdev_filename; - Value* transfer_list_value; +static Value* PerformBlockImageUpdate(const char* name, State* state, int argc, Expr* argv[], + const Command* commands, int cmdcount, int dryrun) { + + char* line = NULL; + char* linesave = NULL; + char* logcmd = NULL; char* transfer_list = NULL; - Value* new_data_fn; - Value* patch_data_fn; - bool success = false; + CommandParameters params; + const Command* cmd = NULL; + const ZipEntry* new_entry = NULL; + const ZipEntry* patch_entry = NULL; + FILE* cmd_pipe = NULL; + HashTable* cmdht = NULL; + int i; + int res; + int rc = -1; + int stash_max_blocks = 0; + int total_blocks = 0; + pthread_attr_t attr; + unsigned int cmdhash; + UpdaterInfo* ui = NULL; + Value* blockdev_filename = NULL; + Value* new_data_fn = NULL; + Value* patch_data_fn = NULL; + Value* transfer_list_value = NULL; + ZipArchive* za = NULL; + + memset(¶ms, 0, sizeof(params)); + params.canwrite = !dryrun; + + fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update"); if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, - &new_data_fn, &patch_data_fn) < 0) { - return NULL; + &new_data_fn, &patch_data_fn) < 0) { + goto pbiudone; } if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, "blockdev_filename argument to %s must be string", name); - goto done; + goto pbiudone; } if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, "transfer_list argument to %s must be blob", name); - goto done; + goto pbiudone; } if (new_data_fn->type != VAL_STRING) { ErrorAbort(state, "new_data_fn argument to %s must be string", name); - goto done; + goto pbiudone; } if (patch_data_fn->type != VAL_STRING) { ErrorAbort(state, "patch_data_fn argument to %s must be string", name); - goto done; + goto pbiudone; } - UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - FILE* cmd_pipe = ui->cmd_pipe; + ui = (UpdaterInfo*) state->cookie; - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + if (ui == NULL) { + goto pbiudone; + } + + cmd_pipe = ui->cmd_pipe; + za = ui->package_zip; + + if (cmd_pipe == NULL || za == NULL) { + goto pbiudone; + } + + patch_entry = mzFindZipEntry(za, patch_data_fn->data); - 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; + fprintf(stderr, "%s(): no file \"%s\" in package", name, patch_data_fn->data); + goto pbiudone; } - uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr + - mzGetZipEntryOffset(patch_entry); + params.patch_start = ui->package_zip_addr + mzGetZipEntryOffset(patch_entry); + new_entry = mzFindZipEntry(za, new_data_fn->data); - 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; + fprintf(stderr, "%s(): no file \"%s\" in package", name, new_data_fn->data); + goto pbiudone; } - // 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 - // - // 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. - // - // 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); + params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR)); - 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); + if (params.fd == -1) { + fprintf(stderr, "open \"%s\" failed: %s\n", blockdev_filename->data, strerror(errno)); + goto pbiudone; + } - int i, j; + if (params.canwrite) { + params.nti.za = za; + params.nti.entry = new_entry; - char* linesave; - char* wordsave; + pthread_mutex_init(¶ms.nti.mu, NULL); + pthread_cond_init(¶ms.nti.cv, NULL); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - 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; + int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.nti); + if (error != 0) { + fprintf(stderr, "pthread_create failed: %s\n", strerror(error)); + goto pbiudone; + } } - 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); - // 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); + transfer_list_value->size + 1); + goto pbiudone; } + memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); transfer_list[transfer_list_value->size] = '\0'; + // First line in transfer list is the version number line = strtok_r(transfer_list, "\n", &linesave); + params.version = strtol(line, NULL, 0); - int version; - // first line in transfer list is the version number; currently - // there's only version 1. - 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; + if (params.version < 1 || params.version > 3) { + fprintf(stderr, "unexpected transfer list version [%s]\n", line); + goto pbiudone; } - printf("blockimg version is %d\n", version); - // second line in transfer list is the total number of blocks we - // expect to write. + fprintf(stderr, "blockimg version is %d\n", params.version); + + // 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** stash_table = NULL; - if (version >= 2) { - // Next line is how many stash entries are needed simultaneously. + total_blocks = strtol(line, NULL, 0); + + if (total_blocks < 0) { + ErrorAbort(state, "unexpected block count [%s]\n", line); + goto pbiudone; + } else if (total_blocks == 0) { + rc = 0; + goto pbiudone; + } + + if (params.version >= 2) { + // Third line is how many stash entries are needed simultaneously line = strtok_r(NULL, "\n", &linesave); - int stash_entries = strtol(line, NULL, 0); + fprintf(stderr, "maximum stash entries %s\n", line); - 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); + // Fourth line is the maximum number of blocks that will be stashed simultaneously + line = strtok_r(NULL, "\n", &linesave); + stash_max_blocks = strtol(line, NULL, 0); + + if (stash_max_blocks < 0) { + ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", line); + goto pbiudone; } - // 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); + if (stash_max_blocks >= 0) { + res = CreateStash(state, stash_max_blocks, blockdev_filename->data, + ¶ms.stashbase); + + if (res == -1) { + goto pbiudone; + } + + params.createdstash = res; + } } - uint8_t* buffer = NULL; - size_t buffer_alloc = 0; + // Build a hash table of the available commands + cmdht = mzHashTableCreate(cmdcount, NULL); + + for (i = 0; i < cmdcount; ++i) { + cmdhash = HashString(commands[i].name); + mzHashTableLookup(cmdht, cmdhash, (void*) &commands[i], CompareCommands, true); + } - // third and subsequent lines are all individual transfer commands. + // 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) { - 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); - } + logcmd = strdup(line); + params.cmdname = strtok_r(line, " ", ¶ms.cpos); - printf(" moving %d blocks\n", src_blocks); + if (params.cmdname == NULL) { + fprintf(stderr, "missing command [%s]\n", line); + goto pbiudone; + } - size_t 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; - } + cmdhash = HashString(params.cmdname); + cmd = (const Command*) mzHashTableLookup(cmdht, cmdhash, params.cmdname, + CompareCommandNames, false); - blocks_so_far += tgt->size; - fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); - fflush(cmd_pipe); + if (cmd == NULL) { + fprintf(stderr, "unexpected command [%s]\n", params.cmdname); + goto pbiudone; + } - 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); - 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 (cmd->f != NULL && cmd->f(¶ms) == -1) { + fprintf(stderr, "failed to execute command [%s]\n", + logcmd ? logcmd : params.cmdname); + goto pbiudone; + } - 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); - } + if (logcmd) { + free(logcmd); + logcmd = NULL; + } - free(tgt); - } else if (strcmp("new", style) == 0) { + if (params.canwrite) { + fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks); + fflush(cmd_pipe); + } + } - word = strtok_r(NULL, " ", &wordsave); - RangeSet* tgt = parse_range(word); + if (params.canwrite) { + pthread_join(params.thread, NULL); - printf(" writing %d blocks of new data\n", tgt->size); + fprintf(stderr, "wrote %d blocks; expected %d\n", params.written, total_blocks); + fprintf(stderr, "max alloc needed was %zu\n", params.bufsize); - 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); + // Delete stash only after successfully completing the update, as it + // may contain blocks needed to complete the update later. + DeleteStash(params.stashbase); + } else { + fprintf(stderr, "verified partition contents; update may be resumed\n"); + } - 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); + rc = 0; - blocks_so_far += tgt->size; - fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); - fflush(cmd_pipe); +pbiudone: + if (params.fd != -1) { + if (fsync(params.fd) == -1) { + fprintf(stderr, "fsync failed: %s\n", strerror(errno)); + } + close(params.fd); + } - 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); - - 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); - } + if (logcmd) { + free(logcmd); + } - printf(" patching %d blocks to %d\n", src_blocks, tgt->size); + if (cmdht) { + mzHashTableFree(cmdht); + } - Value patch_value; - patch_value.type = VAL_BLOB; - patch_value.size = patch_len; - patch_value.data = (char*)(patch_start + patch_offset); + if (params.buffer) { + free(params.buffer); + } - 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 (transfer_list) { + free(transfer_list); + } - if (style[0] == 'i') { // imgdiff - ApplyImagePatch(buffer, src_blocks * BLOCKSIZE, - &patch_value, - &RangeSinkWrite, &rss, NULL, NULL); - } else { - ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE, - &patch_value, 0, - &RangeSinkWrite, &rss, NULL); - } + if (blockdev_filename) { + FreeValue(blockdev_filename); + } - // 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"); - } + if (transfer_list_value) { + FreeValue(transfer_list_value); + } - blocks_so_far += tgt->size; - fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); - fflush(cmd_pipe); + if (new_data_fn) { + FreeValue(new_data_fn); + } - 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)); - } - } + if (patch_data_fn) { + FreeValue(patch_data_fn); + } - free(tgt); - } else { - printf(" ignoring erase (not block device)\n"); - } - } else { - fprintf(stderr, "unknown transfer style \"%s\"\n", style); - exit(1); - } + // Only delete the stash if the update cannot be resumed, or it's + // a verification run and we created the stash. + if (params.isunresumable || (!params.canwrite && params.createdstash)) { + DeleteStash(params.stashbase); } - pthread_join(new_data_thread, NULL); - success = true; + if (params.stashbase) { + free(params.stashbase); + } - free(buffer); - printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks); - printf("max alloc needed was %zu\n", buffer_alloc); + return StringValue(rc == 0 ? strdup("t") : strdup("")); +} - 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("")); +// 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 +// +// 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. +// +// 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.) +// +// In version 3, commands that read data from the partition (i.e. +// move/bsdiff/imgdiff/stash) have one or more additional hashes +// before the range parameters, which are used to check if the +// command has already been completed and verify the integrity of +// the source data. + +Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) { + // Commands which are not tested are set to NULL to skip them completely + const Command commands[] = { + { "bsdiff", PerformCommandDiff }, + { "erase", NULL }, + { "free", PerformCommandFree }, + { "imgdiff", PerformCommandDiff }, + { "move", PerformCommandMove }, + { "new", NULL }, + { "stash", PerformCommandStash }, + { "zero", NULL } + }; + + // Perform a dry run without writing to test if an update can proceed + return PerformBlockImageUpdate(name, state, argc, argv, commands, + sizeof(commands) / sizeof(commands[0]), 1); +} + +Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { + const Command commands[] = { + { "bsdiff", PerformCommandDiff }, + { "erase", PerformCommandErase }, + { "free", PerformCommandFree }, + { "imgdiff", PerformCommandDiff }, + { "move", PerformCommandMove }, + { "new", PerformCommandNew }, + { "stash", PerformCommandStash }, + { "zero", PerformCommandZero } + }; + + return PerformBlockImageUpdate(name, state, argc, argv, commands, + sizeof(commands) / sizeof(commands[0]), 0); } Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { @@ -772,7 +1905,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int fd = open(blockdev_filename->data, O_RDWR); if (fd < 0) { - ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); + ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno)); goto done; } @@ -784,9 +1917,19 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int i, j; for (i = 0; i < rs->count; ++i) { - check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET); + if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) { + ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data, + strerror(errno)); + goto done; + } + for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { - readblock(fd, buffer, BLOCKSIZE); + if (read_all(fd, buffer, BLOCKSIZE) == -1) { + ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data, + strerror(errno)); + goto done; + } + SHA_update(&ctx, buffer, BLOCKSIZE); } } @@ -804,6 +1947,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { } void RegisterBlockImageFunctions() { + RegisterFunction("block_image_verify", BlockImageVerifyFn); RegisterFunction("block_image_update", BlockImageUpdateFn); RegisterFunction("range_sha1", RangeSha1Fn); } diff --git a/updater/install.c b/updater/install.c index 7a73f28b9..d0e7d1aef 100644 --- a/updater/install.c +++ b/updater/install.c @@ -500,7 +500,7 @@ Value* PackageExtractDirFn(const char* name, State* state, struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default bool success = mzExtractRecursive(za, zip_path, dest_path, - MZ_EXTRACT_FILES_ONLY, ×tamp, + ×tamp, NULL, NULL, sehandle); free(zip_path); free(dest_path); diff --git a/updater/updater.c b/updater/updater.c index 78c0dc1a6..479675da7 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -18,6 +18,7 @@ #include <unistd.h> #include <stdlib.h> #include <fcntl.h> +#include <string.h> #include "edify/expr.h" #include "updater.h" diff --git a/verifier.cpp b/verifier.cpp index 764b935b3..98c733732 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -26,9 +26,10 @@ #include "mincrypt/sha.h" #include "mincrypt/sha256.h" -#include <string.h> -#include <stdio.h> #include <errno.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> //extern RecoveryUI* ui; diff --git a/verifier_test.cpp b/verifier_test.cpp index 3ba270de7..0c4503f44 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> +#include <string.h> #include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> /* #include "common.h" @@ -124,6 +126,8 @@ RecoveryUI* ui = NULL; // nothing but print. class FakeUI : public RecoveryUI { void Init() { } + void SetStage(int, int) { } + void SetLocale(const char*) { } void SetBackground(Icon icon) { } void SetProgressType(ProgressType determinate) { } @@ -139,6 +143,7 @@ class FakeUI : public RecoveryUI { vfprintf(stderr, fmt, ap); va_end(ap); } + void ShowFile(const char*) { } void StartMenu(const char* const * headers, const char* const * items, int initial_selection) { } |