summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk53
-rw-r--r--CleanSpec.mk1
-rw-r--r--adb_install.cpp61
-rw-r--r--applypatch/Android.mk6
-rw-r--r--applypatch/applypatch.c111
-rw-r--r--applypatch/applypatch.h6
-rw-r--r--applypatch/bspatch.c9
-rw-r--r--applypatch/imgpatch.c11
-rw-r--r--applypatch/main.c4
-rw-r--r--asn1_decoder.cpp190
-rw-r--r--asn1_decoder.h36
-rw-r--r--bootloader.h15
-rw-r--r--default_device.cpp19
-rw-r--r--device.h6
-rw-r--r--edify/Android.mk2
-rw-r--r--edify/expr.c2
-rw-r--r--edify/expr.h2
-rw-r--r--edify/main.c7
-rw-r--r--edify/parser.y9
-rw-r--r--etc/init.rc38
-rw-r--r--fuse_sdcard_provider.c141
-rw-r--r--fuse_sdcard_provider.h23
-rw-r--r--fuse_sideload.c503
-rw-r--r--fuse_sideload.h38
-rw-r--r--install.cpp46
-rw-r--r--install.h2
-rw-r--r--interlace-frames.py53
-rw-r--r--make-overlay.py102
-rw-r--r--minadbd/Android.mk6
-rw-r--r--minadbd/adb.c1
-rw-r--r--minadbd/adb.h5
-rw-r--r--minadbd/fuse_adb_provider.c67
-rw-r--r--minadbd/fuse_adb_provider.h22
-rw-r--r--minadbd/services.c51
-rw-r--r--minadbd/sockets.c3
-rw-r--r--minadbd/transport.c25
-rw-r--r--minadbd/usb_linux_client.c2
-rw-r--r--minelf/Retouch.c196
-rw-r--r--minelf/Retouch.h45
-rw-r--r--minui/Android.mk7
-rw-r--r--minui/events.c86
-rw-r--r--minui/font_10x18.h2
-rw-r--r--minui/graphics.c333
-rw-r--r--minui/graphics.h50
-rw-r--r--minui/graphics_adf.c247
-rw-r--r--minui/graphics_fbdev.c217
-rw-r--r--minui/minui.h60
-rw-r--r--minui/resources.c435
-rw-r--r--minzip/DirUtil.c58
-rw-r--r--minzip/DirUtil.h8
-rw-r--r--minzip/SysUtil.c239
-rw-r--r--minzip/SysUtil.h55
-rw-r--r--minzip/Zip.c160
-rw-r--r--minzip/Zip.h23
-rw-r--r--mtdutils/flash_image.c3
-rw-r--r--recovery.cpp361
-rw-r--r--res-hdpi/images/erasing_text.pngbin0 -> 20528 bytes
-rw-r--r--res-hdpi/images/error_text.pngbin0 -> 11684 bytes
-rw-r--r--res-hdpi/images/icon_error.pngbin0 -> 19306 bytes
-rw-r--r--res-hdpi/images/icon_installing.pngbin0 -> 118562 bytes
-rw-r--r--res-hdpi/images/installing_text.pngbin0 -> 40946 bytes
-rw-r--r--res-hdpi/images/no_command_text.pngbin0 -> 25013 bytes
-rw-r--r--res-hdpi/images/progress_empty.pngbin0 -> 118 bytes
-rw-r--r--res-hdpi/images/progress_fill.pngbin0 -> 404 bytes
-rw-r--r--res-hdpi/images/stage_empty.pngbin0 -> 322 bytes
-rw-r--r--res-hdpi/images/stage_fill.pngbin0 -> 258 bytes
-rw-r--r--res-mdpi/images/erasing_text.pngbin0 -> 12208 bytes
-rw-r--r--res-mdpi/images/error_text.pngbin0 -> 7202 bytes
-rw-r--r--res-mdpi/images/icon_error.pngbin0 -> 19306 bytes
-rw-r--r--res-mdpi/images/icon_installing.pngbin0 -> 118562 bytes
-rw-r--r--res-mdpi/images/installing_text.pngbin0 -> 24575 bytes
-rw-r--r--res-mdpi/images/no_command_text.pngbin0 -> 14168 bytes
-rw-r--r--res-mdpi/images/progress_empty.pngbin0 -> 118 bytes
-rw-r--r--res-mdpi/images/progress_fill.pngbin0 -> 404 bytes
-rw-r--r--res-mdpi/images/stage_empty.pngbin0 -> 322 bytes
-rw-r--r--res-mdpi/images/stage_fill.pngbin0 -> 258 bytes
-rw-r--r--res-xhdpi/images/erasing_text.pngbin0 -> 29674 bytes
-rw-r--r--res-xhdpi/images/error_text.pngbin0 -> 16599 bytes
-rw-r--r--res-xhdpi/images/icon_error.pngbin0 -> 19306 bytes
-rw-r--r--res-xhdpi/images/icon_installing.pngbin0 -> 118562 bytes
-rw-r--r--res-xhdpi/images/installing_text.pngbin0 -> 63220 bytes
-rw-r--r--res-xhdpi/images/no_command_text.pngbin0 -> 37222 bytes
-rw-r--r--res-xhdpi/images/progress_empty.pngbin0 -> 118 bytes
-rw-r--r--res-xhdpi/images/progress_fill.pngbin0 -> 404 bytes
-rw-r--r--res-xhdpi/images/stage_empty.pngbin0 -> 322 bytes
-rw-r--r--res-xhdpi/images/stage_fill.pngbin0 -> 258 bytes
-rw-r--r--res-xxhdpi/images/erasing_text.pngbin0 -> 52598 bytes
-rw-r--r--res-xxhdpi/images/error_text.pngbin0 -> 27839 bytes
-rw-r--r--res-xxhdpi/images/icon_error.pngbin0 -> 19306 bytes
-rw-r--r--res-xxhdpi/images/icon_installing.pngbin0 -> 118562 bytes
-rw-r--r--res-xxhdpi/images/installing_text.pngbin0 -> 133331 bytes
-rw-r--r--res-xxhdpi/images/no_command_text.pngbin0 -> 63887 bytes
-rw-r--r--res-xxhdpi/images/progress_empty.pngbin0 -> 118 bytes
-rw-r--r--res-xxhdpi/images/progress_fill.pngbin0 -> 404 bytes
-rw-r--r--res-xxhdpi/images/stage_empty.pngbin0 -> 322 bytes
-rw-r--r--res-xxhdpi/images/stage_fill.pngbin0 -> 258 bytes
-rw-r--r--res-xxxhdpi/images/erasing_text.pngbin0 -> 85500 bytes
-rw-r--r--res-xxxhdpi/images/error_text.pngbin0 -> 40216 bytes
-rw-r--r--res-xxxhdpi/images/icon_error.pngbin0 -> 19306 bytes
-rw-r--r--res-xxxhdpi/images/icon_installing.pngbin0 -> 118562 bytes
-rw-r--r--res-xxxhdpi/images/installing_text.pngbin0 -> 184958 bytes
-rw-r--r--res-xxxhdpi/images/no_command_text.pngbin0 -> 109746 bytes
-rw-r--r--res-xxxhdpi/images/progress_empty.pngbin0 -> 118 bytes
-rw-r--r--res-xxxhdpi/images/progress_fill.pngbin0 -> 404 bytes
-rw-r--r--res-xxxhdpi/images/stage_empty.pngbin0 -> 322 bytes
-rw-r--r--res-xxxhdpi/images/stage_fill.pngbin0 -> 258 bytes
-rw-r--r--roots.cpp117
-rw-r--r--roots.h5
-rw-r--r--screen_ui.cpp158
-rw-r--r--screen_ui.h21
-rw-r--r--testdata/otasigned_ecdsa_sha256.zipbin0 -> 3085 bytes
-rw-r--r--testdata/testkey_ecdsa.pk8bin0 -> 138 bytes
-rw-r--r--testdata/testkey_ecdsa.x509.pem10
-rw-r--r--tests/Android.mk26
-rw-r--r--tests/asn1_decoder_test.cpp238
-rw-r--r--tools/ota/add-property-tag.c4
-rw-r--r--twrp.cpp6
-rw-r--r--ui.cpp72
-rw-r--r--ui.h18
-rw-r--r--uncrypt/Android.mk (renamed from minelf/Android.mk)13
-rw-r--r--uncrypt/uncrypt.c427
-rw-r--r--updater/Android.mk2
-rw-r--r--updater/MODULE_LICENSE_GPL0
-rw-r--r--updater/NOTICE339
-rw-r--r--updater/blockimg.c645
-rw-r--r--updater/blockimg.h22
-rw-r--r--updater/install.c501
-rw-r--r--updater/install.h2
-rw-r--r--updater/updater.c22
-rw-r--r--updater/updater.h3
-rw-r--r--verifier.cpp298
-rw-r--r--verifier.h25
-rw-r--r--verifier_test.cpp116
-rwxr-xr-xverifier_test.sh22
134 files changed, 5629 insertions, 1746 deletions
diff --git a/Android.mk b/Android.mk
index dfdff7dbc..835bb7184 100644
--- a/Android.mk
+++ b/Android.mk
@@ -52,13 +52,32 @@ ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC)
endif
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ recovery.cpp \
+ bootloader.cpp \
+ install.cpp \
+ roots.cpp \
+ ui.cpp \
+ screen_ui.cpp \
+ asn1_decoder.cpp \
+ verifier.cpp \
+ adb_install.cpp \
+ fuse_sdcard_provider.c
+
LOCAL_MODULE := recovery
#LOCAL_FORCE_STATIC_EXECUTABLE := true
+#ifeq ($(HOST_OS),linux)
+#LOCAL_REQUIRED_MODULES := mkfs.f2fs
+#endif
+
RECOVERY_API_VERSION := 3
RECOVERY_FSTAB_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+LOCAL_CFLAGS += -Wno-unused-parameter
#LOCAL_STATIC_LIBRARIES := \
# libext4_utils_static \
@@ -411,18 +430,44 @@ include $(BUILD_PHONY_PACKAGE)
RECOVERY_BUSYBOX_SYMLINKS :=
endif # !TW_USE_TOOLBOX
+# All the APIs for testing
+include $(CLEAR_VARS)
+LOCAL_MODULE := libverifier
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := \
+ asn1_decoder.cpp
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := fuse_sideload.c
+
+LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+
+LOCAL_MODULE := libfusesideload
+
+LOCAL_SHARED_LIBRARIES := libcutils libc libmincrypttwrp
+include $(BUILD_SHARED_LIBRARY)
+
include $(CLEAR_VARS)
LOCAL_MODULE := verifier_test
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
+
LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
+
+LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT
+LOCAL_CFLAGS += -Wno-unused-parameter
+
LOCAL_SRC_FILES := \
verifier_test.cpp \
+ asn1_decoder.cpp \
verifier.cpp \
ui.cpp
LOCAL_STATIC_LIBRARIES := \
libmincrypttwrp \
libminui \
+ libminzip \
libcutils \
libstdc++ \
libc
@@ -434,7 +479,7 @@ LOCAL_MODULE := libaosprecovery
LOCAL_MODULE_TAGS := eng optional
LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
LOCAL_SRC_FILES = adb_install.cpp bootloader.cpp verifier.cpp mtdutils/mtdutils.c legacy_property_service.c
-LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils
+LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload
LOCAL_STATIC_LIBRARIES += libmincrypttwrp
ifneq ($(BOARD_RECOVERY_BLDRMSG_OFFSET),)
@@ -445,10 +490,14 @@ include $(BUILD_SHARED_LIBRARY)
commands_recovery_local_path := $(LOCAL_PATH)
include $(LOCAL_PATH)/minui/Android.mk \
- $(LOCAL_PATH)/minelf/Android.mk \
$(LOCAL_PATH)/minadbd/Android.mk \
+ $(LOCAL_PATH)/minzip/Android.mk \
+ $(LOCAL_PATH)/minadbd/Android.mk \
+ $(LOCAL_PATH)/mtdutils/Android.mk \
+ $(LOCAL_PATH)/tests/Android.mk \
$(LOCAL_PATH)/tools/Android.mk \
$(LOCAL_PATH)/edify/Android.mk \
+ $(LOCAL_PATH)/uncrypt/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
$(LOCAL_PATH)/applypatch/Android.mk
diff --git a/CleanSpec.mk b/CleanSpec.mk
index ecf89ae75..e2d97d42b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -48,3 +48,4 @@
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes)
diff --git a/adb_install.cpp b/adb_install.cpp
index c18126d00..e10cb4a2e 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -30,7 +30,8 @@
#include "cutils/properties.h"
#include "adb_install.h"
extern "C" {
-#include "minadbd/adb.h"
+#include "minadbd/fuse_adb_provider.h"
+#include "fuse_sideload.h"
}
static RecoveryUI* ui = NULL;
@@ -78,6 +79,10 @@ maybe_restart_adbd() {
}
}
+// How long (in seconds) we wait for the host to start sending us a
+// package, before timing out.
+#define ADB_INSTALL_TIMEOUT 300
+
int
apply_from_adb(const char* install_file) {
@@ -92,22 +97,62 @@ apply_from_adb(const char* install_file) {
execl("/sbin/recovery", "recovery", "--adbd", install_file, NULL);
_exit(-1);
}
+
char child_prop[PROPERTY_VALUE_MAX];
sprintf(child_prop, "%i", child);
property_set("tw_child_pid", child_prop);
+
+ // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
+ // connects and starts serving a package. Poll for its
+ // appearance. (Note that inotify doesn't work with FUSE.)
+ int result;
int status;
- // TODO(dougz): there should be a way to cancel waiting for a
- // package (by pushing some button combo on the device). For now
- // you just have to 'adb sideload' a file that's not a valid
- // package, like "/dev/null".
- waitpid(child, &status, 0);
+ bool waited = false;
+ struct stat st;
+ for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
+ if (waitpid(child, &status, WNOHANG) != 0) {
+ result = INSTALL_ERROR;
+ waited = true;
+ break;
+ }
+
+ if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
+ if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) {
+ sleep(1);
+ continue;
+ } else {
+ ui->Print("\nTimed out waiting for package.\n\n", strerror(errno));
+ result = INSTALL_ERROR;
+ kill(child, SIGKILL);
+ break;
+ }
+ }
+ result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);
+ break;
+ }
+
+ if (!waited) {
+ // Calling stat() on this magic filename signals the minadbd
+ // subprocess to shut down.
+ stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+
+ // TODO(dougz): there should be a way to cancel waiting for a
+ // package (by pushing some button combo on the device). For now
+ // you just have to 'adb sideload' a file that's not a valid
+ // package, like "/dev/null".
+ waitpid(child, &status, 0);
+ }
+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- printf("status %d\n", WEXITSTATUS(status));
+ if (WEXITSTATUS(status) == 3) {
+ printf("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
+ } else if (!WIFSIGNALED(status)) {
+ printf("status %d\n", WEXITSTATUS(status));
+ }
}
set_usb_driver(false);
maybe_restart_adbd();
- struct stat st;
if (stat(install_file, &st) != 0) {
if (errno == ENOENT) {
printf("No package received.\n");
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 04ddfd874..2cce81f6d 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -40,7 +40,7 @@ LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES += $(commands_recovery_local_path)
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz libminelf
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz
LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
@@ -50,9 +50,9 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch_static
LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := optional eng
LOCAL_C_INCLUDES += $(commands_recovery_local_path)
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz libminelf
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz
LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index b5ff12c80..73195d96a 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
+#include <stdbool.h>
#include "mincrypt/sha.h"
#include "applypatch.h"
@@ -32,7 +33,7 @@
#include "edify/expr.h"
static int LoadPartitionContents(const char* filename, FileContents* file);
-static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
+static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
static int GenerateTarget(FileContents* source_file,
const Value* source_patch_value,
FileContents* copy_file,
@@ -45,14 +46,11 @@ static int GenerateTarget(FileContents* source_file,
static int mtd_partitions_scanned = 0;
-// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
-// the retouched entries back to their original value (such that SHA-1 checks
-// don't fail due to randomization); store the file contents and associated
+// Read a file into memory; store the file contents and associated
// metadata in *file.
//
// Return 0 on success.
-int LoadFileContents(const char* filename, FileContents* file,
- int retouch_flag) {
+int LoadFileContents(const char* filename, FileContents* file) {
file->data = NULL;
// A special 'filename' beginning with "MTD:" or "EMMC:" means to
@@ -89,20 +87,6 @@ int LoadFileContents(const char* filename, FileContents* file,
}
fclose(f);
- // apply_patch[_check] functions are blind to randomization. Randomization
- // is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
- // within a file, this means the file is assumed "corrupt" for simplicity.
- if (retouch_flag) {
- int32_t desired_offset = 0;
- if (retouch_mask_data(file->data, file->size,
- &desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
- printf("error trying to mask retouch entries\n");
- free(file->data);
- file->data = NULL;
- return -1;
- }
- }
-
SHA_hash(file->data, file->size, file->sha1);
return 0;
}
@@ -259,7 +243,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
break;
}
if (next != read) {
- printf("short read (%d bytes of %d) for partition \"%s\"\n",
+ printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
read, next, partition);
free(file->data);
file->data = NULL;
@@ -286,7 +270,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
// we have a match. stop reading the partition; we'll return
// the data we've read so far.
- printf("partition read matched size %d sha %s\n",
+ printf("partition read matched size %zu sha %s\n",
size[index[i]], sha1sum[index[i]]);
break;
}
@@ -337,7 +321,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
// Save the contents of the given FileContents object under the given
// filename. Return 0 on success.
int SaveFileContents(const char* filename, const FileContents* file) {
- int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
printf("failed to open \"%s\" for write: %s\n",
filename, strerror(errno));
@@ -352,8 +336,14 @@ int SaveFileContents(const char* filename, const FileContents* file) {
close(fd);
return -1;
}
- fsync(fd);
- close(fd);
+ if (fsync(fd) != 0) {
+ printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ if (close(fd) != 0) {
+ printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
if (chmod(filename, file->st.st_mode) != 0) {
printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
@@ -433,7 +423,7 @@ int WriteToPartition(unsigned char* data, size_t len,
size_t written = mtd_write_data(ctx, (char*)data, len);
if (written != len) {
- printf("only wrote %d of %d bytes to MTD %s\n",
+ printf("only wrote %zu of %zu bytes to MTD %s\n",
written, len, partition);
mtd_write_close(ctx);
return -1;
@@ -462,13 +452,11 @@ int WriteToPartition(unsigned char* data, size_t len,
}
int attempt;
- for (attempt = 0; attempt < 10; ++attempt) {
- size_t next_sync = start + (1<<20);
- printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start);
+ for (attempt = 0; attempt < 2; ++attempt) {
lseek(fd, start, SEEK_SET);
while (start < len) {
size_t to_write = len - start;
- if (to_write > 4096) to_write = 4096;
+ if (to_write > 1<<20) to_write = 1<<20;
ssize_t written = write(fd, data+start, to_write);
if (written < 0) {
@@ -481,12 +469,23 @@ int WriteToPartition(unsigned char* data, size_t len,
}
}
start += written;
- if (start >= next_sync) {
- fsync(fd);
- next_sync = start + (1<<20);
- }
}
- fsync(fd);
+ if (fsync(fd) != 0) {
+ printf("failed to sync to %s (%s)\n",
+ partition, strerror(errno));
+ return -1;
+ }
+ if (close(fd) != 0) {
+ printf("failed to close %s (%s)\n",
+ partition, strerror(errno));
+ return -1;
+ }
+ fd = open(partition, O_RDONLY);
+ if (fd < 0) {
+ printf("failed to reopen %s for verify (%s)\n",
+ partition, strerror(errno));
+ return -1;
+ }
// drop caches so our subsequent verification read
// won't just be reading the cache.
@@ -513,20 +512,20 @@ int WriteToPartition(unsigned char* data, size_t len,
if (errno == EINTR) {
read_count = 0;
} else {
- printf("verify read error %s at %d: %s\n",
+ 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 %d: %d %d %s\n",
+ printf("short verify read %s at %zu: %zd %zu %s\n",
partition, p, read_count, to_read, strerror(errno));
}
so_far += read_count;
}
if (memcmp(buffer, data+p, to_read)) {
- printf("verification failed starting at %d\n", p);
+ printf("verification failed starting at %zu\n", p);
start = p;
break;
}
@@ -537,8 +536,6 @@ int WriteToPartition(unsigned char* data, size_t len,
success = true;
break;
}
-
- sleep(2);
}
if (!success) {
@@ -550,11 +547,7 @@ int WriteToPartition(unsigned char* data, size_t len,
printf("error closing %s (%s)\n", partition, strerror(errno));
return -1;
}
- // hack: sync and sleep after closing in hopes of getting
- // the data actually onto flash.
- printf("sleeping after close\n");
sync();
- sleep(5);
break;
}
}
@@ -622,7 +615,7 @@ int applypatch_check(const char* filename,
// LoadFileContents is successful. (Useful for reading
// partitions, where the filename encodes the sha1s; no need to
// check them twice.)
- if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 0 ||
+ if (LoadFileContents(filename, &file) != 0 ||
(num_patches > 0 &&
FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
printf("file \"%s\" doesn't have any of expected "
@@ -637,7 +630,7 @@ int applypatch_check(const char* filename,
// exists and matches the sha1 we're looking for, the check still
// passes.
- if (LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK) != 0) {
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
printf("failed to load cache file\n");
return 1;
}
@@ -658,7 +651,7 @@ int ShowLicenses() {
return 0;
}
-ssize_t FileSink(unsigned char* data, ssize_t len, void* token) {
+ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) {
int fd = *(int *)token;
ssize_t done = 0;
ssize_t wrote;
@@ -679,7 +672,7 @@ typedef struct {
ssize_t pos;
} MemorySinkInfo;
-ssize_t MemorySink(unsigned char* data, ssize_t len, void* token) {
+ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
MemorySinkInfo* msi = (MemorySinkInfo*)token;
if (msi->size - msi->pos < len) {
return -1;
@@ -773,8 +766,7 @@ int applypatch(const char* source_filename,
const Value* copy_patch_value = NULL;
// We try to load the target file into the source_file object.
- if (LoadFileContents(target_filename, &source_file,
- RETOUCH_DO_MASK) == 0) {
+ if (LoadFileContents(target_filename, &source_file) == 0) {
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
// The early-exit case: the patch was already applied, this file
// has the desired hash, nothing for us to do.
@@ -793,8 +785,7 @@ int applypatch(const char* source_filename,
// target file, or we did but it's different from the source file.
free(source_file.data);
source_file.data = NULL;
- LoadFileContents(source_filename, &source_file,
- RETOUCH_DO_MASK);
+ LoadFileContents(source_filename, &source_file);
}
if (source_file.data != NULL) {
@@ -810,8 +801,7 @@ int applypatch(const char* source_filename,
source_file.data = NULL;
printf("source file is bad; trying copy\n");
- if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file,
- RETOUCH_DO_MASK) < 0) {
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
// fail.
printf("failed to read copy file\n");
return 1;
@@ -984,7 +974,8 @@ static int GenerateTarget(FileContents* source_file,
strcpy(outname, target_filename);
strcat(outname, ".patch");
- output = open(outname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ output = open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
+ S_IRUSR | S_IWUSR);
if (output < 0) {
printf("failed to open output file %s: %s\n",
outname, strerror(errno));
@@ -1015,8 +1006,14 @@ static int GenerateTarget(FileContents* source_file,
}
if (output >= 0) {
- fsync(output);
- close(output);
+ if (fsync(output) != 0) {
+ printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno));
+ result = 1;
+ }
+ if (close(output) != 0) {
+ printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno));
+ result = 1;
+ }
}
if (result != 0) {
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index f1f13a100..edec84812 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -19,7 +19,6 @@
#include <sys/stat.h>
#include "mincrypt/sha.h"
-#include "minelf/Retouch.h"
#include "edify/expr.h"
typedef struct _Patch {
@@ -41,7 +40,7 @@ typedef struct _FileContents {
// and use it as the source instead.
#define CACHE_TEMP_SOURCE "/cache/saved.file"
-typedef ssize_t (*SinkFn)(unsigned char*, ssize_t, void*);
+typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
// applypatch.c
int ShowLicenses();
@@ -61,8 +60,7 @@ int applypatch_check(const char* filename,
int num_patches,
char** const patch_sha1_str);
-int LoadFileContents(const char* filename, FileContents* file,
- int retouch_flag);
+int LoadFileContents(const char* filename, FileContents* file);
int SaveFileContents(const char* filename, const FileContents* file);
void FreeFileContents(FileContents* file);
int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
diff --git a/applypatch/bspatch.c b/applypatch/bspatch.c
index 2e80f81d0..b34ec2a88 100644
--- a/applypatch/bspatch.c
+++ b/applypatch/bspatch.c
@@ -112,9 +112,7 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
printf("short write of output: %d (%s)\n", errno, strerror(errno));
return 1;
}
- if (ctx) {
- SHA_update(ctx, new_data, new_size);
- }
+ if (ctx) SHA_update(ctx, new_data, new_size);
free(new_data);
return 0;
@@ -205,6 +203,11 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
ctrl[1] = offtin(buf+8);
ctrl[2] = offtin(buf+16);
+ if (ctrl[0] < 0 || ctrl[1] < 0) {
+ printf("corrupt patch (negative byte counts)\n");
+ return 1;
+ }
+
// Sanity check
if (newpos + ctrl[0] > *new_size) {
printf("corrupt patch (new file overrun)\n");
diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c
index 3a1df3872..33c448762 100644
--- a/applypatch/imgpatch.c
+++ b/applypatch/imgpatch.c
@@ -18,6 +18,7 @@
// format.
#include <stdio.h>
+#include <sys/cdefs.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
@@ -35,7 +36,7 @@
* file, and update the SHA context with the output data as well.
* Return 0 on success.
*/
-int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused,
const Value* patch,
SinkFn sink, void* token, SHA_CTX* ctx,
const Value* bonus_data) {
@@ -94,7 +95,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
printf("failed to read chunk %d raw data\n", i);
return -1;
}
- SHA_update(ctx, patch->data + pos, data_len);
+ if (ctx) SHA_update(ctx, patch->data + pos, data_len);
if (sink((unsigned char*)patch->data + pos,
data_len, token) != data_len) {
printf("failed to write chunk %d raw data\n", i);
@@ -132,7 +133,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
unsigned char* expanded_source = malloc(expanded_len);
if (expanded_source == NULL) {
- printf("failed to allocate %d bytes for expanded_source\n",
+ printf("failed to allocate %zu bytes for expanded_source\n",
expanded_len);
return -1;
}
@@ -163,7 +164,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
// We should have filled the output buffer exactly, except
// for the bonus_size.
if (strm.avail_out != bonus_size) {
- printf("source inflation short by %d bytes\n", strm.avail_out-bonus_size);
+ printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size);
return -1;
}
inflateEnd(&strm);
@@ -216,7 +217,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
(long)have);
return -1;
}
- SHA_update(ctx, temp_data, have);
+ if (ctx) SHA_update(ctx, temp_data, have);
} while (ret != Z_STREAM_END);
deflateEnd(&strm);
diff --git a/applypatch/main.c b/applypatch/main.c
index f61db5d9e..8e9fe80ef 100644
--- a/applypatch/main.c
+++ b/applypatch/main.c
@@ -74,7 +74,7 @@ static int ParsePatchArgs(int argc, char** argv,
(*patches)[i] = NULL;
} else {
FileContents fc;
- if (LoadFileContents(colon, &fc, RETOUCH_DONT_MASK) != 0) {
+ if (LoadFileContents(colon, &fc) != 0) {
goto abort;
}
(*patches)[i] = malloc(sizeof(Value));
@@ -103,7 +103,7 @@ int PatchMode(int argc, char** argv) {
Value* bonus = NULL;
if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
FileContents fc;
- if (LoadFileContents(argv[2], &fc, RETOUCH_DONT_MASK) != 0) {
+ if (LoadFileContents(argv[2], &fc) != 0) {
printf("failed to load bonus file %s\n", argv[2]);
return 1;
}
diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp
new file mode 100644
index 000000000..7280f7480
--- /dev/null
+++ b/asn1_decoder.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2013 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 <stdint.h>
+#include <string.h>
+
+#include "asn1_decoder.h"
+
+
+typedef struct asn1_context {
+ size_t length;
+ uint8_t* p;
+ int app_type;
+} asn1_context_t;
+
+
+static const int kMaskConstructed = 0xE0;
+static const int kMaskTag = 0x7F;
+static const int kMaskAppType = 0x1F;
+
+static const int kTagOctetString = 0x04;
+static const int kTagOid = 0x06;
+static const int kTagSequence = 0x30;
+static const int kTagSet = 0x31;
+static const int kTagConstructed = 0xA0;
+
+asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length) {
+ asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+ ctx->p = buffer;
+ ctx->length = length;
+ return ctx;
+}
+
+void asn1_context_free(asn1_context_t* ctx) {
+ free(ctx);
+}
+
+static inline int peek_byte(asn1_context_t* ctx) {
+ if (ctx->length <= 0) {
+ return -1;
+ }
+ return *ctx->p;
+}
+
+static inline int get_byte(asn1_context_t* ctx) {
+ if (ctx->length <= 0) {
+ return -1;
+ }
+ int byte = *ctx->p;
+ ctx->p++;
+ ctx->length--;
+ return byte;
+}
+
+static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) {
+ if (ctx->length < num_skip) {
+ return false;
+ }
+ ctx->p += num_skip;
+ ctx->length -= num_skip;
+ return true;
+}
+
+static bool decode_length(asn1_context_t* ctx, size_t* out_len) {
+ int num_octets = get_byte(ctx);
+ if (num_octets == -1) {
+ return false;
+ }
+ if ((num_octets & 0x80) == 0x00) {
+ *out_len = num_octets;
+ return 1;
+ }
+ num_octets &= kMaskTag;
+ if ((size_t)num_octets >= sizeof(size_t)) {
+ return false;
+ }
+ size_t length = 0;
+ for (int i = 0; i < num_octets; ++i) {
+ int byte = get_byte(ctx);
+ if (byte == -1) {
+ return false;
+ }
+ length <<= 8;
+ length += byte;
+ }
+ *out_len = length;
+ return true;
+}
+
+/**
+ * Returns the constructed type and advances the pointer. E.g. A0 -> 0
+ */
+asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) {
+ int type = get_byte(ctx);
+ if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
+ return NULL;
+ }
+ size_t length;
+ if (!decode_length(ctx, &length) || length > ctx->length) {
+ return NULL;
+ }
+ asn1_context_t* app_ctx = asn1_context_new(ctx->p, length);
+ app_ctx->app_type = type & kMaskAppType;
+ return app_ctx;
+}
+
+bool asn1_constructed_skip_all(asn1_context_t* ctx) {
+ int byte = peek_byte(ctx);
+ while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
+ skip_bytes(ctx, 1);
+ size_t length;
+ if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
+ return false;
+ }
+ byte = peek_byte(ctx);
+ }
+ return byte != -1;
+}
+
+int asn1_constructed_type(asn1_context_t* ctx) {
+ return ctx->app_type;
+}
+
+asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) {
+ if ((get_byte(ctx) & kMaskTag) != kTagSequence) {
+ return NULL;
+ }
+ size_t length;
+ if (!decode_length(ctx, &length) || length > ctx->length) {
+ return NULL;
+ }
+ return asn1_context_new(ctx->p, length);
+}
+
+asn1_context_t* asn1_set_get(asn1_context_t* ctx) {
+ if ((get_byte(ctx) & kMaskTag) != kTagSet) {
+ return NULL;
+ }
+ size_t length;
+ if (!decode_length(ctx, &length) || length > ctx->length) {
+ return NULL;
+ }
+ return asn1_context_new(ctx->p, length);
+}
+
+bool asn1_sequence_next(asn1_context_t* ctx) {
+ size_t length;
+ if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
+ return false;
+ }
+ return true;
+}
+
+bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length) {
+ if (get_byte(ctx) != kTagOid) {
+ return false;
+ }
+ if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
+ return false;
+ }
+ *oid = ctx->p;
+ return true;
+}
+
+bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length) {
+ if (get_byte(ctx) != kTagOctetString) {
+ return false;
+ }
+ if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
+ return false;
+ }
+ *octet_string = ctx->p;
+ return true;
+}
diff --git a/asn1_decoder.h b/asn1_decoder.h
new file mode 100644
index 000000000..b17141c44
--- /dev/null
+++ b/asn1_decoder.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 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 ASN1_DECODER_H_
+#define ASN1_DECODER_H_
+
+#include <stdint.h>
+
+typedef struct asn1_context asn1_context_t;
+
+asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length);
+void asn1_context_free(asn1_context_t* ctx);
+asn1_context_t* asn1_constructed_get(asn1_context_t* ctx);
+bool asn1_constructed_skip_all(asn1_context_t* ctx);
+int asn1_constructed_type(asn1_context_t* ctx);
+asn1_context_t* asn1_sequence_get(asn1_context_t* ctx);
+asn1_context_t* asn1_set_get(asn1_context_t* ctx);
+bool asn1_sequence_next(asn1_context_t* seq);
+bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length);
+bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length);
+
+#endif /* ASN1_DECODER_H_ */
diff --git a/bootloader.h b/bootloader.h
index 0a682b28a..15e0a5c99 100644
--- a/bootloader.h
+++ b/bootloader.h
@@ -38,11 +38,24 @@ extern "C" {
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
* other way around.
+ *
+ * The stage field is written by packages which restart themselves
+ * multiple times, so that the UI can reflect which invocation of the
+ * package it is. If the value is of the format "#/#" (eg, "1/3"),
+ * the UI will add a simple indicator of that status.
*/
struct bootloader_message {
char command[32];
char status[32];
- char recovery[1024];
+ char recovery[768];
+
+ // The 'recovery' field used to be 1024 bytes. It has only ever
+ // been used to store the recovery command line, so 768 bytes
+ // should be plenty. We carve off the last 256 bytes to store the
+ // stage string (for multistage packages) and possible future
+ // expansion.
+ char stage[32];
+ char reserved[224];
};
/* Read and write the bootloader command from the "misc" partition.
diff --git a/default_device.cpp b/default_device.cpp
index 648eaec4e..97806ac58 100644
--- a/default_device.cpp
+++ b/default_device.cpp
@@ -29,22 +29,15 @@ 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 DefaultUI : public ScreenRecoveryUI {
- public:
- virtual KeyAction CheckKey(int key) {
- if (key == KEY_HOME) {
- return TOGGLE;
- }
- return ENQUEUE;
- }
-};
-
class DefaultDevice : public Device {
public:
DefaultDevice() :
- ui(new DefaultUI) {
+ ui(new ScreenRecoveryUI) {
}
RecoveryUI* GetUI() { return ui; }
@@ -61,6 +54,7 @@ class DefaultDevice : public Device {
return kHighlightUp;
case KEY_ENTER:
+ case KEY_POWER:
return kInvokeItem;
}
}
@@ -74,6 +68,9 @@ class DefaultDevice : public Device {
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;
}
}
diff --git a/device.h b/device.h
index 583de75ef..8ff4ec031 100644
--- a/device.h
+++ b/device.h
@@ -65,8 +65,10 @@ class Device {
// - invoke a specific action (a menu position: any non-negative number)
virtual int HandleMenuKey(int key, int visible) = 0;
- enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE,
- APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE };
+ 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 };
// Perform a recovery action selected from the menu.
// 'menu_position' will be the item number of the selected menu
diff --git a/edify/Android.mk b/edify/Android.mk
index fac0ba712..61ed6fa17 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -23,6 +23,7 @@ LOCAL_SRC_FILES := \
LOCAL_CFLAGS := $(edify_cflags) -g -O0
LOCAL_MODULE := edify
LOCAL_YACCFLAGS := -v
+LOCAL_CFLAGS += -Wno-unused-parameter
include $(BUILD_HOST_EXECUTABLE)
@@ -34,6 +35,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(edify_src_files)
LOCAL_CFLAGS := $(edify_cflags)
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE := libedify
include $(BUILD_STATIC_LIBRARY)
diff --git a/edify/expr.c b/edify/expr.c
index a2f1f99d7..79f6282d8 100644
--- a/edify/expr.c
+++ b/edify/expr.c
@@ -287,13 +287,11 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
long l_int = strtol(left, &end, 10);
if (left[0] == '\0' || *end != '\0') {
- printf("[%s] is not an int\n", left);
goto done;
}
long r_int = strtol(right, &end, 10);
if (right[0] == '\0' || *end != '\0') {
- printf("[%s] is not an int\n", right);
goto done;
}
diff --git a/edify/expr.h b/edify/expr.h
index 0d8ed8f57..a9ed2f9c5 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -164,6 +164,8 @@ Value* StringValue(char* str);
// Free a Value object.
void FreeValue(Value* v);
+int parse_string(const char* str, Expr** root, int* error_count);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/edify/main.c b/edify/main.c
index 9e6bab7ca..b3fad53b8 100644
--- a/edify/main.c
+++ b/edify/main.c
@@ -30,9 +30,7 @@ int expect(const char* expr_str, const char* expected, int* errors) {
printf(".");
- yy_scan_string(expr_str);
- int error_count = 0;
- error = yyparse(&e, &error_count);
+ int error_count = parse_string(expr_str, &e, &error_count);
if (error > 0 || error_count > 0) {
printf("error parsing \"%s\" (%d errors)\n",
expr_str, error_count);
@@ -193,8 +191,7 @@ int main(int argc, char** argv) {
Expr* root;
int error_count = 0;
- yy_scan_bytes(buffer, size);
- int error = yyparse(&root, &error_count);
+ int error = parse_string(buffer, &root, &error_count);
printf("parse returned %d; %d errors encountered\n", error, error_count);
if (error == 0 || error_count > 0) {
diff --git a/edify/parser.y b/edify/parser.y
index 3f9ade144..f8fb2d12f 100644
--- a/edify/parser.y
+++ b/edify/parser.y
@@ -29,6 +29,10 @@ extern int gColumn;
void yyerror(Expr** root, int* error_count, const char* s);
int yyparse(Expr** root, int* error_count);
+struct yy_buffer_state;
+void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
+struct yy_buffer_state* yy_scan_string(const char* yystr);
+
%}
%locations
@@ -128,3 +132,8 @@ void yyerror(Expr** root, int* error_count, const char* s) {
printf("line %d col %d: %s\n", gLine, gColumn, s);
++*error_count;
}
+
+int parse_string(const char* str, Expr** root, int* error_count) {
+ yy_switch_to_buffer(yy_scan_string(str));
+ return yyparse(root, error_count);
+}
diff --git a/etc/init.rc b/etc/init.rc
index 8b797662c..67b6a6f14 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -1,6 +1,13 @@
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
@@ -16,11 +23,14 @@ on init
mkdir /system
mkdir /data
mkdir /cache
+ mkdir /sideload
mount tmpfs tmpfs /tmp
chown root shell /tmp
chmod 0775 /tmp
+ write /proc/sys/kernel/panic_on_oops 1
+
on fs
mkdir /dev/usb-ffs 0770 shell shell
mkdir /dev/usb-ffs/adb 0770 shell shell
@@ -37,13 +47,31 @@ on fs
on boot
-
ifup lo
hostname localhost
domainname localdomain
class_start default
+# Load properties from /system/ + /factory after fs mount.
+on load_all_props_action
+ load_all_props
+
+# Mount filesystems and start core system services.
+on late-init
+ trigger early-fs
+ trigger fs
+ trigger post-fs
+ trigger post-fs-data
+
+ # Load properties from /system/ + /factory after fs mount. Place
+ # this in another action so that the load will be scheduled after the prior
+ # issued fs triggers have completed.
+ trigger load_all_props_action
+
+ trigger early-boot
+ trigger boot
+
on property:sys.powerctl=*
powerctl ${sys.powerctl}
@@ -78,15 +106,19 @@ on property:sys.usb.config=adb
service ueventd /sbin/ueventd
critical
+ seclabel u:r:ueventd:s0
-service healthd /sbin/healthd -n
+service healthd /sbin/healthd -r
critical
+ seclabel u:r:healthd:s0
service recovery /sbin/recovery
+ seclabel u:r:recovery:s0
-service adbd /sbin/adbd recovery
+service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
disabled
socket adbd stream 660 system system
+ seclabel u:r:adbd:s0
# Always start adbd on userdebug and eng builds
on property:ro.debuggable=1
diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.c
new file mode 100644
index 000000000..19fb52df0
--- /dev/null
+++ b/fuse_sdcard_provider.c
@@ -0,0 +1,141 @@
+/*
+ * 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 <errno.h>
+#include <pthread.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "fuse_sideload.h"
+
+struct file_data {
+ int fd; // the underlying sdcard file
+
+ uint64_t file_size;
+ uint32_t block_size;
+};
+
+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));
+ 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;
+ }
+ fetch_size -= r;
+ buffer += r;
+ }
+
+ return 0;
+}
+
+static void close_file(void* cookie) {
+ struct file_data* fd = (struct file_data*)cookie;
+ close(fd->fd);
+}
+
+struct token {
+ pthread_t th;
+ const char* path;
+ int result;
+};
+
+static void* run_sdcard_fuse(void* cookie) {
+ struct token* t = (struct token*)cookie;
+
+ struct stat sb;
+ if (stat(t->path, &sb) < 0) {
+ fprintf(stderr, "failed to stat %s: %s\n", t->path, strerror(errno));
+ t->result = -1;
+ return NULL;
+ }
+
+ struct file_data fd;
+ struct provider_vtab vtab;
+
+ fd.fd = open(t->path, O_RDONLY);
+ if (fd.fd < 0) {
+ fprintf(stderr, "failed to open %s: %s\n", t->path, strerror(errno));
+ t->result = -1;
+ return NULL;
+ }
+ fd.file_size = sb.st_size;
+ fd.block_size = 65536;
+
+ vtab.read_block = read_block_file;
+ vtab.close = close_file;
+
+ t->result = run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size);
+ return NULL;
+}
+
+// How long (in seconds) we wait for the fuse-provided package file to
+// appear, before timing out.
+#define SDCARD_INSTALL_TIMEOUT 10
+
+void* start_sdcard_fuse(const char* path) {
+ struct token* t = malloc(sizeof(struct token));
+
+ t->path = path;
+ pthread_create(&(t->th), NULL, run_sdcard_fuse, t);
+
+ struct stat st;
+ int i;
+ for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
+ if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
+ if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
+ sleep(1);
+ continue;
+ } else {
+ return NULL;
+ }
+ }
+ }
+
+ // The installation process expects to find the sdcard unmounted.
+ // Unmount it with MNT_DETACH so that our open file continues to
+ // work but new references see it as unmounted.
+ umount2("/sdcard", MNT_DETACH);
+
+ return t;
+}
+
+void finish_sdcard_fuse(void* cookie) {
+ if (cookie == NULL) return;
+ struct token* t = (struct token*)cookie;
+
+ // Calling stat() on this magic filename signals the fuse
+ // filesystem to shut down.
+ struct stat st;
+ stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+
+ pthread_join(t->th, NULL);
+ free(t);
+}
diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h
new file mode 100644
index 000000000..dc2982ca0
--- /dev/null
+++ b/fuse_sdcard_provider.h
@@ -0,0 +1,23 @@
+/*
+ * 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_SDCARD_PROVIDER_H
+#define __FUSE_SDCARD_PROVIDER_H
+
+void* start_sdcard_fuse(const char* path);
+void finish_sdcard_fuse(void* token);
+
+#endif
diff --git a/fuse_sideload.c b/fuse_sideload.c
new file mode 100644
index 000000000..ab91defbf
--- /dev/null
+++ b/fuse_sideload.c
@@ -0,0 +1,503 @@
+/*
+ * 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.
+ */
+
+// This module creates a special filesystem containing two files.
+//
+// "/sideload/package.zip" appears to be a normal file, but reading
+// from it causes data to be fetched from the adb host. We can use
+// this to sideload packages over an adb connection without having to
+// store the entire package in RAM on the device.
+//
+// Because we may not trust the adb host, this filesystem maintains
+// the following invariant: each read of a given position returns the
+// same data as the first read at that position. That is, once a
+// section of the file is read, future reads of that section return
+// the same data. (Otherwise, a malicious adb host process could
+// return one set of bits when the package is read for signature
+// verification, and then different bits for when the package is
+// accessed by the installer.) If the adb host returns something
+// different than it did on the first read, the reader of the file
+// will see their read fail with EINVAL.
+//
+// The other file, "/sideload/exit", is used to control the subprocess
+// that creates this filesystem. Calling stat() on the exit file
+// causes the filesystem to be unmounted and the adb process on the
+// device shut down.
+//
+// Note that only the minimal set of file operations needed for these
+// two files is implemented. In particular, you can't opendir() or
+// readdir() on the "/sideload" directory; ls on it won't work.
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/fuse.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "mincrypt/sha256.h"
+#include "fuse_sideload.h"
+
+#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1)
+#define EXIT_FLAG_ID (FUSE_ROOT_ID+2)
+
+#define NO_STATUS 1
+#define NO_STATUS_EXIT 2
+
+struct fuse_data {
+ int ffd; // file descriptor for the fuse socket
+
+ struct provider_vtab* vtab;
+ void* cookie;
+
+ uint64_t file_size; // bytes
+
+ uint32_t block_size; // block size that the adb host is using to send the file to us
+ uint32_t file_blocks; // file size in block_size blocks
+
+ uid_t uid;
+ gid_t gid;
+
+ uint32_t curr_block; // cache the block most recently read from the host
+ uint8_t* block_data;
+
+ uint8_t* extra_block; // another block of storage for reads that
+ // span two blocks
+
+ uint8_t* hashes; // SHA-256 hash of each block (all zeros
+ // if block hasn't been read yet)
+};
+
+static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len)
+{
+ struct fuse_out_header hdr;
+ struct iovec vec[2];
+ int res;
+
+ hdr.len = len + sizeof(hdr);
+ hdr.error = 0;
+ hdr.unique = unique;
+
+ vec[0].iov_base = &hdr;
+ vec[0].iov_len = sizeof(hdr);
+ vec[1].iov_base = data;
+ vec[1].iov_len = len;
+
+ res = writev(fd->ffd, vec, 2);
+ if (res < 0) {
+ printf("*** REPLY FAILED *** %d\n", 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;
+
+ 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));
+
+ return NO_STATUS;
+}
+
+static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd,
+ uint64_t nodeid, uint64_t size, uint32_t mode) {
+ memset(attr, 0, sizeof(*attr));
+ attr->nlink = 1;
+ attr->uid = fd->uid;
+ attr->gid = fd->gid;
+ attr->blksize = 4096;
+
+ attr->ino = nodeid;
+ attr->size = size;
+ attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1);
+ attr->mode = mode;
+}
+
+static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+ const struct fuse_getattr_in* req = data;
+ struct fuse_attr_out out;
+ memset(&out, 0, sizeof(out));
+ out.attr_valid = 10;
+
+ if (hdr->nodeid == FUSE_ROOT_ID) {
+ fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
+ } else if (hdr->nodeid == PACKAGE_FILE_ID) {
+ fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
+ } else if (hdr->nodeid == EXIT_FLAG_ID) {
+ fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
+ } else {
+ return -ENOENT;
+ }
+
+ fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+}
+
+static int handle_lookup(void* data, struct fuse_data* fd,
+ const struct fuse_in_header* hdr) {
+ struct fuse_entry_out out;
+ memset(&out, 0, sizeof(out));
+ out.entry_valid = 10;
+ out.attr_valid = 10;
+
+ if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, data,
+ sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) {
+ out.nodeid = PACKAGE_FILE_ID;
+ out.generation = PACKAGE_FILE_ID;
+ fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
+ } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, data,
+ sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) {
+ out.nodeid = EXIT_FLAG_ID;
+ out.generation = EXIT_FLAG_ID;
+ fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
+ } else {
+ return -ENOENT;
+ }
+
+ fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+}
+
+static int handle_open(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+ const struct fuse_open_in* req = data;
+
+ if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
+ if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
+
+ struct fuse_open_out out;
+ memset(&out, 0, sizeof(out));
+ out.fh = 10; // an arbitrary number; we always use the same handle
+ fuse_reply(fd, hdr->unique, &out, sizeof(out));
+ return NO_STATUS;
+}
+
+static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+ return 0;
+}
+
+static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+ return 0;
+}
+
+// Fetch a block from the host into fd->curr_block and fd->block_data.
+// Returns 0 on successful fetch, negative otherwise.
+static int fetch_block(struct fuse_data* fd, uint32_t block) {
+ if (block == fd->curr_block) {
+ return 0;
+ }
+
+ if (block >= fd->file_blocks) {
+ memset(fd->block_data, 0, fd->block_size);
+ fd->curr_block = block;
+ return 0;
+ }
+
+ size_t fetch_size = fd->block_size;
+ if (block * fd->block_size + fetch_size > fd->file_size) {
+ // If we're reading the last (partial) block of the file,
+ // expect a shorter response from the host, and pad the rest
+ // of the block with zeroes.
+ fetch_size = fd->file_size - (block * fd->block_size);
+ memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
+ }
+
+ int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size);
+ if (result < 0) return result;
+
+ fd->curr_block = block;
+
+ // Verify the hash of the block we just got from the host.
+ //
+ // - If the hash of the just-received data matches the stored hash
+ // for the block, accept it.
+ // - If the stored hash is all zeroes, store the new hash and
+ // accept the block (this is the first time we've read this
+ // block).
+ // - Otherwise, return -EINVAL for the read.
+
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ SHA256_hash(fd->block_data, fd->block_size, hash);
+ uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_SIZE;
+ if (memcmp(hash, blockhash, SHA256_DIGEST_SIZE) == 0) {
+ return 0;
+ }
+
+ int i;
+ for (i = 0; i < SHA256_DIGEST_SIZE; ++i) {
+ if (blockhash[i] != 0) {
+ fd->curr_block = -1;
+ return -EIO;
+ }
+ }
+
+ memcpy(blockhash, hash, SHA256_DIGEST_SIZE);
+ return 0;
+}
+
+static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+ const struct fuse_read_in* req = data;
+ struct fuse_out_header outhdr;
+ struct iovec vec[3];
+ int vec_used;
+ int result;
+
+ if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
+
+ uint64_t offset = req->offset;
+ uint32_t size = req->size;
+
+ // The docs on the fuse kernel interface are vague about what to
+ // do when a read request extends past the end of the file. We
+ // can return a short read -- the return structure does include a
+ // length field -- but in testing that caused the program using
+ // the file to segfault. (I speculate that this is due to the
+ // reading program accessing it via mmap; maybe mmap dislikes when
+ // you return something short of a whole page?) To fix this we
+ // zero-pad reads that extend past the end of the file so we're
+ // always returning exactly as many bytes as were requested.
+ // (Users of the mapped file have to know its real length anyway.)
+
+ outhdr.len = sizeof(outhdr) + size;
+ outhdr.error = 0;
+ outhdr.unique = hdr->unique;
+ vec[0].iov_base = &outhdr;
+ vec[0].iov_len = sizeof(outhdr);
+
+ uint32_t block = offset / fd->block_size;
+ result = fetch_block(fd, block);
+ if (result != 0) return result;
+
+ // Two cases:
+ //
+ // - the read request is entirely within this block. In this
+ // case we can reply immediately.
+ //
+ // - the read request goes over into the next block. Note that
+ // since we mount the filesystem with max_read=block_size, a
+ // read can never span more than two blocks. In this case we
+ // copy the block to extra_block and issue a fetch for the
+ // following block.
+
+ uint32_t block_offset = offset - (block * fd->block_size);
+
+ if (size + block_offset <= fd->block_size) {
+ // First case: the read fits entirely in the first block.
+
+ vec[1].iov_base = fd->block_data + block_offset;
+ vec[1].iov_len = size;
+ vec_used = 2;
+ } else {
+ // Second case: the read spills over into the next block.
+
+ memcpy(fd->extra_block, fd->block_data + block_offset,
+ fd->block_size - block_offset);
+ vec[1].iov_base = fd->extra_block;
+ vec[1].iov_len = fd->block_size - block_offset;
+
+ result = fetch_block(fd, block+1);
+ if (result != 0) return result;
+ vec[2].iov_base = fd->block_data;
+ vec[2].iov_len = size - vec[1].iov_len;
+ vec_used = 3;
+ }
+
+ if (writev(fd->ffd, vec, vec_used) < 0) {
+ printf("*** READ REPLY FAILED: %s ***\n", strerror(errno));
+ }
+ return NO_STATUS;
+}
+
+int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
+ uint64_t file_size, uint32_t block_size)
+{
+ int result;
+
+ // If something's already mounted on our mountpoint, try to remove
+ // it. (Mostly in case of a previous abnormal exit.)
+ umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);
+
+ if (block_size < 1024) {
+ fprintf(stderr, "block size (%u) is too small\n", block_size);
+ return -1;
+ }
+ if (block_size > (1<<22)) { // 4 MiB
+ fprintf(stderr, "block size (%u) is too large\n", block_size);
+ return -1;
+ }
+
+ struct fuse_data fd;
+ memset(&fd, 0, sizeof(fd));
+ fd.vtab = vtab;
+ fd.cookie = cookie;
+ fd.file_size = file_size;
+ fd.block_size = block_size;
+ fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1);
+
+ if (fd.file_blocks > (1<<18)) {
+ fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
+ result = -1;
+ goto done;
+ }
+
+ fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE);
+ if (fd.hashes == NULL) {
+ fprintf(stderr, "failed to allocate %d bites for hashes\n",
+ fd.file_blocks * SHA256_DIGEST_SIZE);
+ result = -1;
+ goto done;
+ }
+
+ fd.uid = getuid();
+ fd.gid = getgid();
+
+ fd.curr_block = -1;
+ fd.block_data = (uint8_t*)malloc(block_size);
+ if (fd.block_data == NULL) {
+ fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
+ result = -1;
+ goto done;
+ }
+ fd.extra_block = (uint8_t*)malloc(block_size);
+ if (fd.extra_block == NULL) {
+ fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
+ result = -1;
+ goto done;
+ }
+
+ fd.ffd = open("/dev/fuse", O_RDWR);
+ if (fd.ffd < 0) {
+ perror("open /dev/fuse");
+ result = -1;
+ goto done;
+ }
+
+ char opts[256];
+ snprintf(opts, sizeof(opts),
+ ("fd=%d,user_id=%d,group_id=%d,max_read=%zu,"
+ "allow_other,rootmode=040000"),
+ fd.ffd, fd.uid, fd.gid, block_size);
+
+ result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT,
+ "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts);
+ if (result < 0) {
+ perror("mount");
+ goto done;
+ }
+ 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;
+ }
+ }
+ continue;
+ }
+
+ if ((size_t)len < sizeof(struct fuse_in_header)) {
+ fprintf(stderr, "request too short: len=%zu\n", (size_t)len);
+ continue;
+ }
+
+ struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer;
+ void* data = request_buffer + sizeof(struct fuse_in_header);
+
+ result = -ENOSYS;
+
+ switch (hdr->opcode) {
+ case FUSE_INIT:
+ result = handle_init(data, &fd, hdr);
+ break;
+
+ case FUSE_LOOKUP:
+ result = handle_lookup(data, &fd, hdr);
+ break;
+
+ case FUSE_GETATTR:
+ result = handle_getattr(data, &fd, hdr);
+ break;
+
+ case FUSE_OPEN:
+ result = handle_open(data, &fd, hdr);
+ break;
+
+ case FUSE_READ:
+ result = handle_read(data, &fd, hdr);
+ break;
+
+ case FUSE_FLUSH:
+ result = handle_flush(data, &fd, hdr);
+ break;
+
+ case FUSE_RELEASE:
+ result = handle_release(data, &fd, hdr);
+ break;
+
+ default:
+ fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode);
+ break;
+ }
+
+ if (result == NO_STATUS_EXIT) {
+ result = 0;
+ break;
+ }
+
+ if (result != NO_STATUS) {
+ struct fuse_out_header outhdr;
+ outhdr.len = sizeof(outhdr);
+ outhdr.error = result;
+ outhdr.unique = hdr->unique;
+ write(fd.ffd, &outhdr, sizeof(outhdr));
+ }
+ }
+
+ done:
+ fd.vtab->close(fd.cookie);
+
+ result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
+ if (result < 0) {
+ printf("fuse_sideload umount failed: %s\n", strerror(errno));
+ }
+
+ if (fd.ffd) close(fd.ffd);
+ free(fd.hashes);
+ free(fd.block_data);
+ free(fd.extra_block);
+
+ return result;
+}
diff --git a/fuse_sideload.h b/fuse_sideload.h
new file mode 100644
index 000000000..c0b16efbe
--- /dev/null
+++ b/fuse_sideload.h
@@ -0,0 +1,38 @@
+/*
+ * 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_SIDELOAD_H
+#define __FUSE_SIDELOAD_H
+
+// define the filenames created by the sideload FUSE filesystem
+#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload"
+#define FUSE_SIDELOAD_HOST_FILENAME "package.zip"
+#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME)
+#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit"
+#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG)
+
+struct provider_vtab {
+ // read a block
+ int (*read_block)(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
+
+ // close down
+ void (*close)(void* cookie);
+};
+
+int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
+ uint64_t file_size, uint32_t block_size);
+
+#endif
diff --git a/install.cpp b/install.cpp
index 797a525fd..9db5640a0 100644
--- a/install.cpp
+++ b/install.cpp
@@ -120,6 +120,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
pid_t pid = fork();
if (pid == 0) {
+ umask(022);
close(pipefd[0]);
execv(binary, (char* const*)args);
fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
@@ -159,6 +160,11 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
*wipe_cache = 1;
} else if (strcmp(command, "clear_display") == 0) {
ui->SetBackground(RecoveryUI::NONE);
+ } else if (strcmp(command, "enable_reboot") == 0) {
+ // packages can explicitly request that they want the user
+ // to be able to reboot during installation (useful for
+ // debugging packages that don't exit).
+ ui->SetEnableReboot(true);
} else {
LOGE("unknown command [%s]\n", command);
}
@@ -176,7 +182,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
}
static int
-really_install_package(const char *path, int* wipe_cache)
+really_install_package(const char *path, int* wipe_cache, bool needs_mount)
{
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
@@ -185,12 +191,22 @@ really_install_package(const char *path, int* wipe_cache)
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
LOGI("Update location: %s\n", path);
- if (ensure_path_mounted(path) != 0) {
- LOGE("Can't mount %s\n", path);
- return INSTALL_CORRUPT;
+ // Map the update package into memory.
+ ui->Print("Opening update package...\n");
+
+ if (path && needs_mount) {
+ if (path[0] == '@') {
+ ensure_path_mounted(path+1);
+ } else {
+ ensure_path_mounted(path);
+ }
}
- ui->Print("Opening update package...\n");
+ MemMapping map;
+ if (sysMapFile(path, &map) != 0) {
+ LOGE("failed to map file\n");
+ return INSTALL_CORRUPT;
+ }
int numKeys;
Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
@@ -203,31 +219,41 @@ really_install_package(const char *path, int* wipe_cache)
ui->Print("Verifying update package...\n");
int err;
- err = verify_file(path, loadedKeys, numKeys);
+ err = verify_file(map.addr, map.length, loadedKeys, numKeys);
free(loadedKeys);
LOGI("verify_file returned %d\n", err);
if (err != VERIFY_SUCCESS) {
LOGE("signature verification failed\n");
+ sysReleaseMap(&map);
return INSTALL_CORRUPT;
}
/* Try to open the package.
*/
ZipArchive zip;
- err = mzOpenZipArchive(path, &zip);
+ err = mzOpenZipArchive(map.addr, map.length, &zip);
if (err != 0) {
LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
+ sysReleaseMap(&map);
return INSTALL_CORRUPT;
}
/* Verify and install the contents of the package.
*/
ui->Print("Installing update...\n");
- return try_update_binary(path, &zip, wipe_cache);
+ ui->SetEnableReboot(false);
+ int result = try_update_binary(path, &zip, wipe_cache);
+ ui->SetEnableReboot(true);
+ ui->Print("\n");
+
+ sysReleaseMap(&map);
+
+ return result;
}
int
-install_package(const char* path, int* wipe_cache, const char* install_file)
+install_package(const char* path, int* wipe_cache, const char* install_file,
+ bool needs_mount)
{
FILE* install_log = fopen_path(install_file, "w");
if (install_log) {
@@ -241,7 +267,7 @@ install_package(const char* path, int* wipe_cache, const char* install_file)
LOGE("failed to set up expected mounts for install; aborting\n");
result = INSTALL_ERROR;
} else {
- result = really_install_package(path, wipe_cache);
+ result = really_install_package(path, wipe_cache, needs_mount);
}
if (install_log) {
fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
diff --git a/install.h b/install.h
index 043ce1383..b5ac410e4 100644
--- a/install.h
+++ b/install.h
@@ -29,7 +29,7 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE };
// returned and *wipe_cache is true on exit, caller should wipe the
// cache partition.
int install_package(const char *root_path, int* wipe_cache,
- const char* install_file);
+ const char* install_file, bool needs_mount);
RSAPublicKey* load_keys(const char* filename, int* numKeys);
#ifdef __cplusplus
diff --git a/interlace-frames.py b/interlace-frames.py
new file mode 100644
index 000000000..243e565e7
--- /dev/null
+++ b/interlace-frames.py
@@ -0,0 +1,53 @@
+# 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.
+
+"""Script to take a set of frames (PNG files) for a recovery animation
+and turn it into a single output image which contains the input frames
+interlaced by row. Run with the names of all the input frames on the
+command line, in order, followed by the name of the output file."""
+
+import sys
+try:
+ import Image
+ import PngImagePlugin
+except ImportError:
+ print "This script requires the Python Imaging Library to be installed."
+ sys.exit(1)
+
+frames = [Image.open(fn).convert("RGB") for fn in sys.argv[1:-1]]
+assert len(frames) > 0, "Must have at least one input frame."
+sizes = set()
+for fr in frames:
+ sizes.add(fr.size)
+
+assert len(sizes) == 1, "All input images must have the same size."
+w, h = sizes.pop()
+N = len(frames)
+
+out = Image.new("RGB", (w, h*N))
+for j in range(h):
+ for i in range(w):
+ for fn, f in enumerate(frames):
+ out.putpixel((i, j*N+fn), f.getpixel((i, j)))
+
+# When loading this image, the graphics library expects to find a text
+# chunk that specifies how many frames this animation represents. If
+# you post-process the output of this script with some kind of
+# optimizer tool (eg pngcrush or zopflipng) make sure that your
+# optimizer preserves this text chunk.
+
+meta = PngImagePlugin.PngInfo()
+meta.add_text("Frames", str(N))
+
+out.save(sys.argv[-1], pnginfo=meta)
diff --git a/make-overlay.py b/make-overlay.py
deleted file mode 100644
index 7f931b373..000000000
--- a/make-overlay.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright (C) 2011 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.
-
-"""Script to take a set of frames (PNG files) for a recovery
-"installing" icon animation and turn it into a base image plus a set
-of overlays, as needed by the recovery UI code. Run with the names of
-all the input frames on the command line, in order."""
-
-import sys
-try:
- import Image
-except ImportError:
- print "This script requires the Python Imaging Library to be installed."
- sys.exit(1)
-
-# Find the smallest box that contains all the pixels which change
-# between images.
-
-print "reading", sys.argv[1]
-base = Image.open(sys.argv[1])
-
-minmini = base.size[0]-1
-maxmaxi = 0
-minminj = base.size[1]-1
-maxmaxj = 0
-
-for top_name in sys.argv[2:]:
- print "reading", top_name
- top = Image.open(top_name)
-
- assert base.size == top.size
-
- mini = base.size[0]-1
- maxi = 0
- minj = base.size[1]-1
- maxj = 0
-
- h, w = base.size
- for j in range(w):
- for i in range(h):
- b = base.getpixel((i,j))
- t = top.getpixel((i,j))
- if b != t:
- if i < mini: mini = i
- if i > maxi: maxi = i
- if j < minj: minj = j
- if j > maxj: maxj = j
-
- minmini = min(minmini, mini)
- maxmaxi = max(maxmaxi, maxi)
- minminj = min(minminj, minj)
- maxmaxj = max(maxmaxj, maxj)
-
-w = maxmaxi - minmini + 1
-h = maxmaxj - minminj + 1
-
-# Now write out an image containing just that box, for each frame.
-
-for num, top_name in enumerate(sys.argv[1:]):
- top = Image.open(top_name)
-
- out = Image.new("RGB", (w, h))
- for i in range(w):
- for j in range(h):
- t = top.getpixel((i+minmini, j+minminj))
- out.putpixel((i, j), t)
-
- fn = "icon_installing_overlay%02d.png" % (num+1,)
- out.save(fn)
- print "saved", fn
-
-# Write out the base icon, which is the first frame with that box
-# blacked out (just to make the file smaller, since it's always
-# displayed with one of the overlays on top of it).
-
-for i in range(w):
- for j in range(h):
- base.putpixel((i+minmini, j+minminj), (0, 0, 0))
-fn = "icon_installing.png"
-base.save(fn)
-print "saved", fn
-
-# The device_ui_init() function needs to tell the recovery UI the
-# position of the overlay box.
-
-print
-print "add this to your device_ui_init() function:"
-print "-" * 40
-print " ui_parameters->install_overlay_offset_x = %d;" % (minmini,)
-print " ui_parameters->install_overlay_offset_y = %d;" % (minminj,)
-print "-" * 40
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index c08fd46b0..4430a2baa 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -13,6 +13,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
adb.c \
fdevent.c \
+ fuse_adb_provider.c \
transport.c \
transport_usb.c \
sockets.c \
@@ -26,8 +27,5 @@ LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE := libminadbd
-LOCAL_SHARED_LIBRARIES := libcutils libc
+LOCAL_SHARED_LIBRARIES := libfusesideload libcutils libc
include $(BUILD_SHARED_LIBRARY)
-
-
-
diff --git a/minadbd/adb.c b/minadbd/adb.c
index 4626bd3aa..c35e8300b 100644
--- a/minadbd/adb.c
+++ b/minadbd/adb.c
@@ -407,6 +407,7 @@ int adb_main(const char* path)
fprintf(stderr, "userid is %d\n", getuid());
*/
+
D("Event loop starting\n");
fdevent_loop();
diff --git a/minadbd/adb.h b/minadbd/adb.h
index c899b5862..08ee989d6 100644
--- a/minadbd/adb.h
+++ b/minadbd/adb.h
@@ -244,15 +244,11 @@ void kick_transport( atransport* t );
#if ADB_HOST
int get_available_local_transport_index();
#endif
-int init_socket_transport(atransport *t, int s, int port, int local);
void init_usb_transport(atransport *t, usb_handle *usb, int state);
/* for MacOS X cleanup */
void close_usb_devices();
-/* cause new transports to be init'd and added to the list */
-void register_socket_transport(int s, const char *serial, int port, int local);
-
/* these should only be used for the "adb disconnect" command */
void unregister_transport(atransport *t);
void unregister_all_tcp_transports();
@@ -404,6 +400,7 @@ int connection_state(atransport *t);
#define CS_RECOVERY 4
#define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */
#define CS_SIDELOAD 6
+#define CS_UNAUTHORIZED 7
extern int HOST;
extern int SHELL_EXIT_NOTIFY_FD;
diff --git a/minadbd/fuse_adb_provider.c b/minadbd/fuse_adb_provider.c
new file mode 100644
index 000000000..f80533a8c
--- /dev/null
+++ b/minadbd/fuse_adb_provider.c
@@ -0,0 +1,67 @@
+/*
+ * 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 <errno.h>
+
+#include "adb.h"
+#include "fuse_sideload.h"
+
+struct adb_data {
+ int sfd; // file descriptor for the adb channel
+
+ uint64_t file_size;
+ uint32_t block_size;
+};
+
+static int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+ struct adb_data* ad = (struct adb_data*)cookie;
+
+ char buf[10];
+ snprintf(buf, sizeof(buf), "%08u", block);
+ if (writex(ad->sfd, buf, 8) < 0) {
+ fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
+ return -EIO;
+ }
+
+ if (readx(ad->sfd, buffer, fetch_size) < 0) {
+ fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void close_adb(void* cookie) {
+ struct adb_data* ad = (struct adb_data*)cookie;
+
+ writex(ad->sfd, "DONEDONE", 8);
+}
+
+int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
+ struct adb_data ad;
+ struct provider_vtab vtab;
+
+ ad.sfd = sfd;
+ ad.file_size = file_size;
+ ad.block_size = block_size;
+
+ 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
new file mode 100644
index 000000000..0eb1f79d1
--- /dev/null
+++ b/minadbd/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/services.c b/minadbd/services.c
index aef37f7e4..218b84a38 100644
--- a/minadbd/services.c
+++ b/minadbd/services.c
@@ -22,6 +22,7 @@
#include "sysdeps.h"
#include "fdevent.h"
+#include "fuse_adb_provider.h"
#define TRACE_TAG TRACE_SERVICES
#include "adb.h"
@@ -43,44 +44,23 @@ void *service_bootstrap_func(void *x)
return 0;
}
-static void sideload_service(int s, void *cookie)
+static void sideload_host_service(int sfd, void* cookie)
{
- unsigned char buf[4096];
- unsigned count = (unsigned) cookie;
- int fd;
-
- fprintf(stderr, "sideload_service invoked\n");
-
- fd = adb_creat(ADB_SIDELOAD_FILENAME, 0644);
- if(fd < 0) {
- fprintf(stderr, "failed to create %s\n", ADB_SIDELOAD_FILENAME);
- adb_close(s);
- return;
- }
+ char* saveptr;
+ const char* s = strtok_r(cookie, ":", &saveptr);
+ uint64_t file_size = strtoull(s, NULL, 10);
+ s = strtok_r(NULL, ":", &saveptr);
+ uint32_t block_size = strtoul(s, NULL, 10);
- while(count > 0) {
- unsigned xfer = (count > 4096) ? 4096 : count;
- if(readx(s, buf, xfer)) break;
- if(writex(fd, buf, xfer)) break;
- count -= xfer;
- }
+ printf("sideload-host file size %llu block size %lu\n", file_size, block_size);
- if(count == 0) {
- writex(s, "OKAY", 4);
- } else {
- writex(s, "FAIL", 4);
- }
- adb_close(fd);
- adb_close(s);
+ int result = run_adb_fuse(sfd, file_size, block_size);
- if (count == 0) {
- fprintf(stderr, "adbd exiting after successful sideload\n");
- sleep(1);
- exit(0);
- }
+ printf("sideload_host finished\n");
+ sleep(1);
+ exit(result == 0 ? 0 : 1);
}
-
#if 0
static void echo_service(int fd, void *cookie)
{
@@ -149,7 +129,12 @@ int service_to_fd(const char *name)
int ret = -1;
if (!strncmp(name, "sideload:", 9)) {
- ret = create_service_thread(sideload_service, (void*) atoi(name + 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 0
} else if(!strncmp(name, "echo:", 5)){
ret = create_service_thread(echo_service, 0);
diff --git a/minadbd/sockets.c b/minadbd/sockets.c
index 2dd646159..817410d13 100644
--- a/minadbd/sockets.c
+++ b/minadbd/sockets.c
@@ -319,7 +319,8 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s)
while(avail > 0) {
r = adb_read(fd, x, avail);
- D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail);
+ D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
+ s->id, s->fd, r, r<0?errno:0, avail);
if(r > 0) {
avail -= r;
x += r;
diff --git a/minadbd/transport.c b/minadbd/transport.c
index ff2004932..92679f518 100644
--- a/minadbd/transport.c
+++ b/minadbd/transport.c
@@ -678,27 +678,6 @@ retry:
return result;
}
-void register_socket_transport(int s, const char *serial, int port, int local)
-{
- atransport *t = calloc(1, sizeof(atransport));
- char buff[32];
-
- if (!serial) {
- snprintf(buff, sizeof buff, "T-%p", t);
- serial = buff;
- }
- D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
- if ( init_socket_transport(t, s, port, local) < 0 ) {
- adb_close(s);
- free(t);
- return;
- }
- if(serial) {
- t->serial = strdup(serial);
- }
- register_transport(t);
-}
-
void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
{
atransport *t = calloc(1, sizeof(atransport));
@@ -734,7 +713,7 @@ int readx(int fd, void *ptr, size_t len)
char *p = ptr;
int r;
#if ADB_TRACE
- int len0 = len;
+ size_t len0 = len;
#endif
D("readx: fd=%d wanted=%d\n", fd, (int)len);
while(len > 0) {
@@ -755,7 +734,7 @@ int readx(int fd, void *ptr, size_t len)
}
#if ADB_TRACE
- D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len);
+ D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
dump_hex( ptr, len0 );
#endif
return 0;
diff --git a/minadbd/usb_linux_client.c b/minadbd/usb_linux_client.c
index c135d6396..29bab1558 100644
--- a/minadbd/usb_linux_client.c
+++ b/minadbd/usb_linux_client.c
@@ -388,7 +388,7 @@ static int bulk_read(int bulk_out, char *buf, size_t length)
ret = adb_read(bulk_out, buf + count, length - count);
if (ret < 0) {
if (errno != EINTR) {
- D("[ bulk_read failed fd=%d length=%d count=%d ]\n",
+ D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
bulk_out, length, count);
return ret;
}
diff --git a/minelf/Retouch.c b/minelf/Retouch.c
deleted file mode 100644
index d75eec1e8..000000000
--- a/minelf/Retouch.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2009 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 <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <strings.h>
-#include "Retouch.h"
-#include "applypatch/applypatch.h"
-
-typedef struct {
- int32_t mmap_addr;
- char tag[4]; /* 'P', 'R', 'E', ' ' */
-} prelink_info_t __attribute__((packed));
-
-#define false 0
-#define true 1
-
-static int32_t offs_prev;
-static uint32_t cont_prev;
-
-static void init_compression_state(void) {
- offs_prev = 0;
- cont_prev = 0;
-}
-
-// For details on the encoding used for relocation lists, please
-// refer to build/tools/retouch/retouch-prepare.c. The intent is to
-// save space by removing most of the inherent redundancy.
-
-static void decode_bytes(uint8_t *encoded_bytes, int encoded_size,
- int32_t *dst_offset, uint32_t *dst_contents) {
- if (encoded_size == 2) {
- *dst_offset = offs_prev + (((encoded_bytes[0]&0x60)>>5)+1)*4;
-
- // if the original was negative, we need to 1-pad before applying delta
- int32_t tmp = (((encoded_bytes[0] & 0x0000001f) << 8) |
- encoded_bytes[1]);
- if (tmp & 0x1000) tmp = 0xffffe000 | tmp;
- *dst_contents = cont_prev + tmp;
- } else if (encoded_size == 3) {
- *dst_offset = offs_prev + (((encoded_bytes[0]&0x30)>>4)+1)*4;
-
- // if the original was negative, we need to 1-pad before applying delta
- int32_t tmp = (((encoded_bytes[0] & 0x0000000f) << 16) |
- (encoded_bytes[1] << 8) |
- encoded_bytes[2]);
- if (tmp & 0x80000) tmp = 0xfff00000 | tmp;
- *dst_contents = cont_prev + tmp;
- } else {
- *dst_offset =
- (encoded_bytes[0]<<24) |
- (encoded_bytes[1]<<16) |
- (encoded_bytes[2]<<8) |
- encoded_bytes[3];
- if (*dst_offset == 0x3fffffff) *dst_offset = -1;
- *dst_contents =
- (encoded_bytes[4]<<24) |
- (encoded_bytes[5]<<16) |
- (encoded_bytes[6]<<8) |
- encoded_bytes[7];
- }
-}
-
-static uint8_t *decode_in_memory(uint8_t *encoded_bytes,
- int32_t *offset, uint32_t *contents) {
- int input_size, charIx;
- uint8_t input[8];
-
- input[0] = *(encoded_bytes++);
- if (input[0] & 0x80)
- input_size = 2;
- else if (input[0] & 0x40)
- input_size = 3;
- else
- input_size = 8;
-
- // we already read one byte..
- charIx = 1;
- while (charIx < input_size) {
- input[charIx++] = *(encoded_bytes++);
- }
-
- // depends on the decoder state!
- decode_bytes(input, input_size, offset, contents);
-
- offs_prev = *offset;
- cont_prev = *contents;
-
- return encoded_bytes;
-}
-
-int retouch_mask_data(uint8_t *binary_object,
- int32_t binary_size,
- int32_t *desired_offset,
- int32_t *retouch_offset) {
- retouch_info_t *r_info;
- prelink_info_t *p_info;
-
- int32_t target_offset = 0;
- if (desired_offset) target_offset = *desired_offset;
-
- int32_t p_offs = binary_size-sizeof(prelink_info_t); // prelink_info_t
- int32_t r_offs = p_offs-sizeof(retouch_info_t); // retouch_info_t
- int32_t b_offs; // retouch data blob
-
- // If not retouched, we say it was a match. This might get invoked on
- // non-retouched binaries, so that's why we need to do this.
- if (retouch_offset != NULL) *retouch_offset = target_offset;
- if (r_offs < 0) return (desired_offset == NULL) ?
- RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
- p_info = (prelink_info_t *)(binary_object+p_offs);
- r_info = (retouch_info_t *)(binary_object+r_offs);
- if (strncmp(p_info->tag, "PRE ", 4) ||
- strncmp(r_info->tag, "RETOUCH ", 8))
- return (desired_offset == NULL) ?
- RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
-
- b_offs = r_offs-r_info->blob_size;
- if (b_offs < 0) {
- printf("negative binary offset: %d = %d - %d\n",
- b_offs, r_offs, r_info->blob_size);
- return RETOUCH_DATA_ERROR;
- }
- uint8_t *b_ptr = binary_object+b_offs;
-
- // Retouched: let's go through the work then.
- int32_t offset_candidate = target_offset;
- bool offset_set = false, offset_mismatch = false;
- init_compression_state();
- while (b_ptr < (uint8_t *)r_info) {
- int32_t retouch_entry_offset;
- uint32_t *retouch_entry;
- uint32_t retouch_original_value;
-
- b_ptr = decode_in_memory(b_ptr,
- &retouch_entry_offset,
- &retouch_original_value);
- if (retouch_entry_offset < (-1) ||
- retouch_entry_offset >= b_offs) {
- printf("bad retouch_entry_offset: %d", retouch_entry_offset);
- return RETOUCH_DATA_ERROR;
- }
-
- // "-1" means this is the value in prelink_info_t, which also gets
- // randomized.
- if (retouch_entry_offset == -1)
- retouch_entry = (uint32_t *)&(p_info->mmap_addr);
- else
- retouch_entry = (uint32_t *)(binary_object+retouch_entry_offset);
-
- if (desired_offset)
- *retouch_entry = retouch_original_value + target_offset;
-
- // Infer the randomization shift, compare to previously inferred.
- int32_t offset_of_this_entry = (int32_t)(*retouch_entry-
- retouch_original_value);
- if (!offset_set) {
- offset_candidate = offset_of_this_entry;
- offset_set = true;
- } else {
- if (offset_candidate != offset_of_this_entry) {
- offset_mismatch = true;
- printf("offset is mismatched: %d, this entry is %d,"
- " original 0x%x @ 0x%x",
- offset_candidate, offset_of_this_entry,
- retouch_original_value, retouch_entry_offset);
- }
- }
- }
- if (b_ptr > (uint8_t *)r_info) {
- printf("b_ptr went too far: %p, while r_info is %p",
- b_ptr, r_info);
- return RETOUCH_DATA_ERROR;
- }
-
- if (offset_mismatch) return RETOUCH_DATA_MISMATCHED;
- if (retouch_offset != NULL) *retouch_offset = offset_candidate;
- return RETOUCH_DATA_MATCHED;
-}
diff --git a/minelf/Retouch.h b/minelf/Retouch.h
deleted file mode 100644
index 13bacd5ad..000000000
--- a/minelf/Retouch.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2009 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 _MINELF_RETOUCH
-#define _MINELF_RETOUCH
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-typedef struct {
- char tag[8]; /* "RETOUCH ", not zero-terminated */
- uint32_t blob_size; /* in bytes, located right before this struct */
-} retouch_info_t __attribute__((packed));
-
-#define RETOUCH_DONT_MASK 0
-#define RETOUCH_DO_MASK 1
-
-#define RETOUCH_DATA_ERROR 0 // This is bad. Should not happen.
-#define RETOUCH_DATA_MATCHED 1 // Up to an uniform random offset.
-#define RETOUCH_DATA_MISMATCHED 2 // Partially randomized, or total mess.
-#define RETOUCH_DATA_NOTAPPLICABLE 3 // Not retouched. Only when inferring.
-
-// Mask retouching in-memory. Used before apply_patch[_check].
-// Also used to determine status of retouching after a crash.
-//
-// If desired_offset is not NULL, then apply retouching instead,
-// and return that in retouch_offset.
-int retouch_mask_data(uint8_t *binary_object,
- int32_t binary_size,
- int32_t *desired_offset,
- int32_t *retouch_offset);
-#endif
diff --git a/minui/Android.mk b/minui/Android.mk
index 54eb06166..7b401567d 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := graphics_overlay.c events.c resources.c
+LOCAL_SRC_FILES := graphics_adf.c graphics_fbdev.c graphics_overlay.c events.c resources.c
ifneq ($(BOARD_CUSTOM_GRAPHICS),)
LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS)
else
@@ -25,6 +25,8 @@ else
endif
LOCAL_STATIC_LIBRARY := libpng
+LOCAL_WHOLE_STATIC_LIBRARIES += libadf
+
LOCAL_MODULE := libminui
# This used to compare against values in double-quotes (which are just
@@ -60,7 +62,7 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := graphics_overlay.c events.c resources.c
+LOCAL_SRC_FILES := graphics_adf.c graphics_fbdev.c graphics_overlay.c events.c resources.c
ifneq ($(BOARD_CUSTOM_GRAPHICS),)
LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS)
else
@@ -87,6 +89,7 @@ LOCAL_MODULE := libminui
LOCAL_ARM_MODE:= arm
LOCAL_SHARED_LIBRARIES := libpng libpixelflinger
+LOCAL_WHOLE_STATIC_LIBRARIES += libadf
# 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.
diff --git a/minui/events.c b/minui/events.c
index 2918afaa8..df7dad448 100644
--- a/minui/events.c
+++ b/minui/events.c
@@ -18,7 +18,7 @@
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
-#include <sys/poll.h>
+#include <sys/epoll.h>
#include <linux/input.h>
@@ -34,11 +34,15 @@
((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG)))
struct fd_info {
+ int fd;
ev_callback cb;
void *data;
};
-static struct pollfd ev_fds[MAX_DEVICES + MAX_MISC_FDS];
+static int epollfd;
+static struct epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
+static int npolledevents;
+
static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
static unsigned ev_count = 0;
@@ -50,6 +54,12 @@ int ev_init(ev_callback input_cb, void *data)
DIR *dir;
struct dirent *de;
int fd;
+ struct epoll_event ev;
+ bool epollctlfail = false;
+
+ epollfd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+ if (epollfd == -1)
+ return -1;
dir = opendir("/dev/input");
if(dir != 0) {
@@ -74,8 +84,15 @@ int ev_init(ev_callback input_cb, void *data)
continue;
}
- ev_fds[ev_count].fd = fd;
- ev_fds[ev_count].events = POLLIN;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
+ 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++;
@@ -84,59 +101,78 @@ int ev_init(ev_callback input_cb, void *data)
}
}
+ if (epollctlfail && !ev_count) {
+ close(epollfd);
+ epollfd = -1;
+ return -1;
+ }
+
return 0;
}
int ev_add_fd(int fd, ev_callback cb, void *data)
{
+ struct epoll_event ev;
+ int ret;
+
if (ev_misc_count == MAX_MISC_FDS || cb == NULL)
return -1;
- ev_fds[ev_count].fd = fd;
- ev_fds[ev_count].events = POLLIN;
- ev_fdinfo[ev_count].cb = cb;
- ev_fdinfo[ev_count].data = data;
- ev_count++;
- ev_misc_count++;
- return 0;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+ ret = epoll_ctl(epollfd, 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;
+}
+
+int ev_get_epollfd(void)
+{
+ return epollfd;
}
void ev_exit(void)
{
while (ev_count > 0) {
- close(ev_fds[--ev_count].fd);
+ close(ev_fdinfo[--ev_count].fd);
}
ev_misc_count = 0;
ev_dev_count = 0;
+ close(epollfd);
}
int ev_wait(int timeout)
{
- int r;
-
- r = poll(ev_fds, ev_count, timeout);
- if (r <= 0)
+ npolledevents = epoll_wait(epollfd, polledevents, ev_count, timeout);
+ if (npolledevents <= 0)
return -1;
return 0;
}
void ev_dispatch(void)
{
- unsigned n;
+ int n;
int ret;
- for (n = 0; n < ev_count; n++) {
- ev_callback cb = ev_fdinfo[n].cb;
- if (cb && (ev_fds[n].revents & ev_fds[n].events))
- cb(ev_fds[n].fd, ev_fds[n].revents, ev_fdinfo[n].data);
+ for (n = 0; n < npolledevents; n++) {
+ struct fd_info *fdi = 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, short revents, struct input_event *ev)
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev)
{
int r;
- if (revents & POLLIN) {
+ if (epevents & EPOLLIN) {
r = read(fd, ev, sizeof(*ev));
if (r == sizeof(*ev))
return 0;
@@ -157,11 +193,11 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data)
memset(key_bits, 0, sizeof(key_bits));
memset(ev_bits, 0, sizeof(ev_bits));
- ret = ioctl(ev_fds[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
+ ret = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
if (ret < 0 || !test_bit(EV_KEY, ev_bits))
continue;
- ret = ioctl(ev_fds[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
+ ret = ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
if (ret < 0)
continue;
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.c b/minui/graphics.c
index 8f951756d..470e735c9 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -29,44 +29,34 @@
#include <linux/fb.h>
#include <linux/kd.h>
-#include <pixelflinger/pixelflinger.h>
+#include <time.h>
#include "font_10x18.h"
#include "minui.h"
-
-#if defined(RECOVERY_BGRA)
-#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888
-#define PIXEL_SIZE 4
-#elif defined(RECOVERY_RGBX)
-#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
-#define PIXEL_SIZE 4
-#else
-#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
-#define PIXEL_SIZE 2
-#endif
-
-#define NUM_BUFFERS 2
+#include "graphics.h"
typedef struct {
- GGLSurface* texture;
- unsigned cwidth;
- unsigned cheight;
+ GRSurface* texture;
+ int cwidth;
+ int cheight;
} GRFont;
-static GRFont *gr_font = 0;
-static GGLContext *gr_context = 0;
-static GGLSurface gr_font_texture;
-static GGLSurface gr_framebuffer[NUM_BUFFERS];
-static GGLSurface gr_mem_surface;
-static unsigned gr_active_fb = 0;
-static unsigned double_buffering = 0;
+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 int gr_fb_fd = -1;
static int gr_vt_fd = -1;
+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 struct fb_var_screeninfo vi;
static struct fb_fix_screeninfo fi;
@@ -167,8 +157,6 @@ static int get_framebuffer(GGLSurface *fb)
if (vi.yres * fi.line_length * 2 > fi.smem_len)
return fd;
- double_buffering = 1;
-
fb->version = sizeof(*fb);
fb->width = vi.xres;
fb->height = vi.yres;
@@ -233,6 +221,11 @@ void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a
gl->color4xv(gl, color);
}
+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);
@@ -249,58 +242,118 @@ int gr_text(int x, int y, const char *s, ...)
return gr_text_impl(x, y, s, 0);
}
+static void text_blend(unsigned char* src_p, int src_row_bytes,
+ unsigned char* dst_p, int dst_row_bytes,
+ int width, int height)
+{
+ int i, j;
+ for (j = 0; j < height; ++j) {
+ unsigned char* sx = src_p;
+ unsigned char* px = dst_p;
+ for (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;
+ }
+}
+
+
int gr_text_impl(int x, int y, const char *s, int bold)
{
- GGLContext *gl = gr_context;
GRFont *font = gr_font;
unsigned off;
- if (!font->texture) return x;
+ if (!font->texture) return;
+ if (gr_current_a == 0) return;
bold = bold && (font->texture->height != font->cheight);
x += overscan_offset_x;
y += overscan_offset_y;
- gl->bindTexture(gl, font->texture);
- gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
- gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
- gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
- gl->enable(gl, GGL_TEXTURE_2D);
-
while((off = *s++)) {
off -= 32;
+ if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
if (off < 96) {
- gl->texCoord2i(gl, (off * font->cwidth) - x,
- (bold ? font->cheight : 0) - y);
- gl->recti(gl, x, y, x + font->cwidth, y + font->cheight);
+
+ unsigned char* src_p = font->texture->data + (off * 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;
}
-
- return x;
}
-void gr_texticon(int x, int y, gr_surface icon) {
- if (gr_context == NULL || icon == NULL) {
+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;
}
- GGLContext* gl = gr_context;
x += overscan_offset_x;
y += overscan_offset_y;
- gl->bindTexture(gl, (GGLSurface*) icon);
- gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
- gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
- gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
- gl->enable(gl, GGL_TEXTURE_2D);
+ if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return;
- int w = gr_get_width(icon);
- int h = gr_get_height(icon);
+ unsigned char* src_p = icon->data;
+ unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
- gl->texCoord2i(gl, -x, -y);
- gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon));
+ 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)
+{
+ gr_current_r = r;
+ gr_current_g = g;
+ gr_current_b = b;
+ gr_current_a = a;
+}
+
+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 {
+ int x, y;
+ unsigned char* px = gr_draw->data;
+ for (y = 0; y < gr_draw->height; ++y) {
+ for (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)
@@ -311,48 +364,82 @@ void gr_fill(int x1, int y1, int x2, int y2)
x2 += overscan_offset_x;
y2 += overscan_offset_y;
- GGLContext *gl = gr_context;
- gl->disable(gl, GGL_TEXTURE_2D);
- gl->recti(gl, x1, y1, x2, y2);
+ 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(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
- if (gr_context == NULL || source == NULL) {
+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;
}
- GGLContext *gl = gr_context;
dx += overscan_offset_x;
dy += overscan_offset_y;
- gl->bindTexture(gl, (GGLSurface*) source);
- gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
- gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
- gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
- gl->enable(gl, GGL_TEXTURE_2D);
- gl->texCoord2i(gl, sx - dx, sy - dy);
- gl->recti(gl, dx, dy, dx + w, dy + h);
+ 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(gr_surface surface) {
+unsigned int gr_get_width(GRSurface* surface) {
if (surface == NULL) {
return 0;
}
- return ((GGLSurface*) surface)->width;
+ return surface->width;
}
-unsigned int gr_get_height(gr_surface surface) {
+unsigned int gr_get_height(GRSurface* surface) {
if (surface == NULL) {
return 0;
}
- return ((GGLSurface*) surface)->height;
+ return surface->height;
}
static void gr_init_font(void)
{
gr_font = calloc(sizeof(*gr_font), 1);
- int res = res_create_surface("font", (void**)&(gr_font->texture));
+ 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
@@ -366,7 +453,8 @@ static void gr_init_font(void)
gr_font->texture = malloc(sizeof(*gr_font->texture));
gr_font->texture->width = font.width;
gr_font->texture->height = font.height;
- gr_font->texture->stride = font.width;
+ gr_font->texture->row_bytes = font.width;
+ gr_font->texture->pixel_bytes = 1;
unsigned char* bits = malloc(font.width * font.height);
gr_font->texture->data = (void*) bits;
@@ -381,17 +469,65 @@ static void gr_init_font(void)
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;
+ }
- // interpret the grayscale as alpha
- gr_font->texture->format = GGL_PIXEL_FORMAT_A_8;
+ 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);
+ gr_surface 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)
{
- gglInit(&gr_context);
- GGLContext *gl = gr_context;
-
gr_init_font();
+
gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
if (gr_vt_fd < 0) {
// This is non-fatal; post-Cupcake kernels don't have tty0.
@@ -403,10 +539,12 @@ int gr_init(void)
return -1;
}
- gr_fb_fd = get_framebuffer(gr_framebuffer);
- if (gr_fb_fd < 0) {
- gr_exit();
- return -1;
+ gr_backend = open_adf();
+ if (gr_backend) {
+ gr_draw = gr_backend->init(gr_backend);
+ if (!gr_draw) {
+ gr_backend->exit(gr_backend);
+ }
}
get_memory_surface(&gr_mem_surface);
@@ -420,12 +558,19 @@ int gr_init(void)
set_active_framebuffer(0);
gl->colorBuffer(gl, &gr_mem_surface);
- gl->activeTexture(gl, 0);
- gl->enable(gl, GGL_BLEND);
- gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
+ 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_fb_blank(true);
- gr_fb_blank(false);
+ gr_flip();
+ gr_flip();
if (!alloc_ion_mem(fi.line_length * vi.yres))
allocate_overlay(gr_fb_fd, gr_framebuffer);
@@ -443,6 +588,8 @@ void gr_exit(void)
free(gr_mem_surface.data);
+ gr_backend->exit(gr_backend);
+
ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
close(gr_vt_fd);
gr_vt_fd = -1;
@@ -450,17 +597,12 @@ void gr_exit(void)
int gr_fb_width(void)
{
- return gr_framebuffer[0].width - 2*overscan_offset_x;
+ return gr_draw->width - 2*overscan_offset_x;
}
int gr_fb_height(void)
{
- return gr_framebuffer[0].height - 2*overscan_offset_y;
-}
-
-gr_pixel *gr_fb_data(void)
-{
- return (unsigned short *) gr_mem_surface.data;
+ return gr_draw->height - 2*overscan_offset_y;
}
void gr_fb_blank(bool blank)
@@ -478,14 +620,11 @@ void gr_fb_blank(bool blank)
write(fd, blank ? "000" : brightness, 3);
close(fd);
#else
- int ret;
+ gr_backend->blank(gr_backend, blank);
+
if (blank)
free_overlay(gr_fb_fd);
- ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
- if (ret < 0)
- perror("ioctl(): blank");
-
if (!blank)
allocate_overlay(gr_fb_fd, gr_framebuffer);
#endif
diff --git a/minui/graphics.h b/minui/graphics.h
new file mode 100644
index 000000000..993e986ee
--- /dev/null
+++ b/minui/graphics.h
@@ -0,0 +1,50 @@
+/*
+ * 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_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include "minui.h"
+
+typedef struct minui_backend {
+ // Initializes the backend and returns a gr_surface to draw into.
+ gr_surface (*init)(struct 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.
+ gr_surface (*flip)(struct minui_backend*);
+
+ // Blank (or unblank) the screen.
+ void (*blank)(struct minui_backend*, bool);
+
+ // Device cleanup when drawing is done.
+ void (*exit)(struct minui_backend*);
+} minui_backend;
+
+minui_backend* open_fbdev();
+minui_backend* open_adf();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/minui/graphics_adf.c b/minui/graphics_adf.c
new file mode 100644
index 000000000..ac6d64e9e
--- /dev/null
+++ b/minui/graphics_adf.c
@@ -0,0 +1,247 @@
+/*
+ * 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 <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;
+ struct adf_surface_pdata surfaces[2];
+};
+
+static gr_surface adf_flip(struct minui_backend *backend);
+static void adf_blank(struct minui_backend *backend, bool blank);
+
+static int adf_surface_init(struct adf_pdata *pdata,
+ struct drm_mode_modeinfo *mode, struct 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 = 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(struct adf_pdata *pdata)
+{
+ struct 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(struct adf_pdata *pdata, struct 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 gr_surface adf_init(minui_backend *backend)
+{
+ struct adf_pdata *pdata = (struct adf_pdata *)backend;
+ adf_id_t *dev_ids = NULL;
+ ssize_t n_dev_ids, i;
+ gr_surface ret;
+
+#if 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++) {
+ struct 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 gr_surface adf_flip(struct minui_backend *backend)
+{
+ struct adf_pdata *pdata = (struct adf_pdata *)backend;
+ struct 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(struct minui_backend *backend, bool blank)
+{
+ struct adf_pdata *pdata = (struct adf_pdata *)backend;
+ adf_interface_blank(pdata->intf_fd,
+ blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
+}
+
+static void adf_surface_destroy(struct adf_surface_pdata *surf)
+{
+ munmap(surf->base.data, surf->pitch * surf->base.height);
+ close(surf->fd);
+}
+
+static void adf_exit(struct minui_backend *backend)
+{
+ struct adf_pdata *pdata = (struct 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()
+{
+ struct adf_pdata *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_fbdev.c b/minui/graphics_fbdev.c
new file mode 100644
index 000000000..c0c1bcb1a
--- /dev/null
+++ b/minui/graphics_fbdev.c
@@ -0,0 +1,217 @@
+/*
+ * 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 <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 gr_surface fbdev_init(minui_backend*);
+static gr_surface 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 struct 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)
+{
+ int ret;
+
+ ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
+ if (ret < 0)
+ perror("ioctl(): blank");
+}
+
+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 gr_surface fbdev_init(minui_backend* backend) {
+ int fd;
+ void *bits;
+
+ struct fb_fix_screeninfo fi;
+
+ fd = open("/dev/graphics/fb0", O_RDWR);
+ if (fd < 0) {
+ perror("cannot open fb0");
+ return NULL;
+ }
+
+ 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);
+
+ 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 = 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 gr_surface fbdev_flip(minui_backend* backend __unused) {
+ if (double_buffered) {
+ // 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.
+
+#if defined(RECOVERY_BGRA)
+ unsigned int idx;
+ unsigned char* ucfb_vaddr = (unsigned char*)gr_framebuffer[0].data;
+ unsigned char* ucbuffer_vaddr = (unsigned char*)gr_draw->data;
+ for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); idx += 4) {
+ ucfb_vaddr[idx ] = ucbuffer_vaddr[idx + 2];
+ ucfb_vaddr[idx + 1] = ucbuffer_vaddr[idx + 1];
+ ucfb_vaddr[idx + 2] = ucbuffer_vaddr[idx ];
+ ucfb_vaddr[idx + 3] = ucbuffer_vaddr[idx + 3];
+ }
+#else
+ memcpy(gr_framebuffer[0].data, gr_draw->data,
+ gr_draw->height * gr_draw->row_bytes);
+#endif
+ }
+ 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 103318aa7..82282486d 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -17,24 +17,34 @@
#ifndef _MINUI_H_
#define _MINUI_H_
+#include <sys/types.h>
+
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef void* gr_surface;
-typedef unsigned short gr_pixel;
+typedef struct {
+ int width;
+ int height;
+ int row_bytes;
+ int pixel_bytes;
+ unsigned char* data;
+} GRSurface;
+
+typedef GRSurface* gr_surface;
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_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);
@@ -43,7 +53,7 @@ void gr_fill(int x1, int y1, int x2, int y2);
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);
+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);
@@ -56,7 +66,7 @@ unsigned int gr_get_height(gr_surface surface);
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
struct input_event;
-typedef int (*ev_callback)(int fd, short revents, void *data);
+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);
@@ -71,14 +81,46 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
*/
int ev_wait(int timeout);
-int ev_get_input(int fd, short revents, struct input_event *ev);
+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);
-int res_create_localized_surface(const char* name, gr_surface* pSurface);
+// 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, 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);
+
+// Load a single alpha surface from a grayscale PNG image.
+int res_create_alpha_surface(const char* name, gr_surface* 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,
+ gr_surface* pSurface);
+
+// Free a surface allocated by any of the res_create_*_surface()
+// functions.
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);
diff --git a/minui/resources.c b/minui/resources.c
index 39f83c76f..92a3d316a 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -27,8 +27,6 @@
#include <linux/fb.h>
#include <linux/kd.h>
-#include <pixelflinger/pixelflinger.h>
-
#include <png.h>
#include "minui.h"
@@ -39,23 +37,22 @@ char *locale = NULL;
extern char* locale;
#endif
-// libpng gives "undefined reference to 'pow'" errors, and I have no
-// idea how to convince the build system to link with -lm. We don't
-// need this functionality (it's used for gamma adjustment) so provide
-// a dummy implementation to satisfy the linker.
-double pow(double x, double y) {
- return x * y;
+#define SURFACE_DATA_ALIGNMENT 8
+
+static gr_surface malloc_surface(size_t data_size) {
+ unsigned char* temp = malloc(sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT);
+ if (temp == NULL) return NULL;
+ gr_surface surface = (gr_surface) temp;
+ surface->data = temp + sizeof(GRSurface) +
+ (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT));
+ return surface;
}
-int res_create_surface(const char* name, gr_surface* pSurface) {
+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];
- GGLSurface* surface = NULL;
- int result = 0;
unsigned char header[8];
- png_structp png_ptr = NULL;
- png_infop info_ptr = NULL;
-
- *pSurface = NULL;
+ int result = 0;
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
resPath[sizeof(resPath)-1] = '\0';
@@ -76,114 +73,286 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
goto exit;
}
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (!png_ptr) {
+ *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) {
+ *info_ptr = png_create_info_struct(*png_ptr);
+ if (!*info_ptr) {
result = -5;
goto exit;
}
- if (setjmp(png_jmpbuf(png_ptr))) {
+ 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);
-
- int color_type = info_ptr->color_type;
- int bit_depth = info_ptr->bit_depth;
- int channels = info_ptr->channels;
- if (!(bit_depth == 8 &&
- ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
- (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
- (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
- color_type == PNG_COLOR_TYPE_GRAY))))) {
- return -7;
+ png_init_io(*png_ptr, fp);
+ png_set_sig_bytes(*png_ptr, sizeof(header));
+ png_read_info(*png_ptr, *info_ptr);
+
+ int color_type, bit_depth;
+ 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;
}
- size_t width = info_ptr->width;
- size_t height = info_ptr->height;
- size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
- size_t pixelSize = stride * height;
+ return result;
- surface = malloc(sizeof(GGLSurface) + pixelSize);
- if (surface == NULL) {
- result = -8;
- goto exit;
+ exit:
+ if (result < 0) {
+ png_destroy_read_struct(png_ptr, info_ptr, NULL);
}
- unsigned char* pData = (unsigned char*) (surface + 1);
- surface->version = sizeof(GGLSurface);
+ 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 gr_surface sufficient for storing an image of
+// the indicated size in the framebuffer pixel format.
+static gr_surface init_display_surface(png_uint_32 width, png_uint_32 height) {
+ gr_surface surface;
+
+ surface = malloc_surface(width * height * 4);
+ if (surface == NULL) return NULL;
+
surface->width = width;
surface->height = height;
- surface->stride = width; /* Yes, pixels, not bytes */
- surface->data = pData;
- surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 :
- ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8));
-
- int alpha = 0;
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(png_ptr);
- }
- if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(png_ptr);
- alpha = 1;
+ 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;
}
- if (color_type == PNG_COLOR_TYPE_GRAY) {
- alpha = 1;
+}
+
+int res_create_display_surface(const char* name, gr_surface* pSurface) {
+ gr_surface 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;
+
+ surface = init_display_surface(width, height);
+ if (surface == NULL) {
+ result = -8;
+ goto exit;
}
+ unsigned char* p_row = malloc(width * 4);
unsigned int y;
- if (channels == 3 || (channels == 1 && !alpha)) {
- for (y = 0; y < height; ++y) {
- unsigned char* pRow = pData + y * stride;
- png_read_row(png_ptr, pRow, NULL);
-
- int x;
- for(x = width - 1; x >= 0; x--) {
- int sx = x * 3;
- int dx = x * 4;
- unsigned char r = pRow[sx];
- unsigned char g = pRow[sx + 1];
- unsigned char b = pRow[sx + 2];
- unsigned char a = 0xff;
- pRow[dx ] = r; // r
- pRow[dx + 1] = g; // g
- pRow[dx + 2] = b; // b
- pRow[dx + 3] = a;
+ 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, gr_surface** pSurface) {
+ gr_surface* 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;
+
+ *pSurface = NULL;
+ *frames = -1;
+
+ result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
+ if (result < 0) return result;
+
+ *frames = 1;
+ png_textp text;
+ int num_text;
+ 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;
}
}
- } else {
- for (y = 0; y < height; ++y) {
- unsigned char* pRow = pData + y * stride;
- png_read_row(png_ptr, pRow, NULL);
+ 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 = malloc(*frames * sizeof(gr_surface));
+ 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;
}
}
- *pSurface = (gr_surface) surface;
+ unsigned char* p_row = malloc(width * 4);
+ unsigned int y;
+ 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 = (gr_surface*) surface;
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- if (fp != NULL) {
- fclose(fp);
- }
if (result < 0) {
if (surface) {
+ for (i = 0; i < *frames; ++i) {
+ if (surface[i]) free(surface[i]);
+ }
free(surface);
}
}
return result;
}
-static int matches_locale(const char* loc) {
+int res_create_alpha_surface(const char* name, gr_surface* pSurface) {
+ gr_surface 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;
+
+ 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;
@@ -200,100 +369,61 @@ static int matches_locale(const char* loc) {
return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
}
-int res_create_localized_surface(const char* name, gr_surface* pSurface) {
- char resPath[256];
- GGLSurface* surface = NULL;
+int res_create_localized_alpha_surface(const char* name,
+ const char* locale,
+ gr_surface* pSurface) {
+ gr_surface surface = NULL;
int result = 0;
- unsigned char header[8];
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
+ png_uint_32 width, height;
+ png_byte channels;
*pSurface = NULL;
- 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;
- }
-
- size_t bytesRead = fread(header, 1, sizeof(header), fp);
- if (bytesRead != sizeof(header)) {
- result = -2;
+ if (locale == NULL) {
+ surface = malloc_surface(0);
+ surface->width = 0;
+ surface->height = 0;
+ surface->row_bytes = 0;
+ surface->pixel_bytes = 1;
goto exit;
}
- if (png_sig_cmp(header, 0, sizeof(header))) {
- result = -3;
- goto exit;
- }
+ result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
+ if (result < 0) return result;
- 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);
-
- size_t width = info_ptr->width;
- size_t height = info_ptr->height;
- size_t stride = 4 * width;
-
- int color_type = info_ptr->color_type;
- int bit_depth = info_ptr->bit_depth;
- int channels = info_ptr->channels;
-
- if (!(bit_depth == 8 &&
- (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
- return -7;
+ if (channels != 1) {
+ result = -7;
goto exit;
}
unsigned char* row = malloc(width);
- int y;
+ png_uint_32 y;
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 = row+5;
+ char* loc = (char*)row+5;
- if (y+1+h >= height || matches_locale(loc)) {
+ if (y+1+h >= height || matches_locale(loc, locale)) {
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
- surface = malloc(sizeof(GGLSurface));
+ surface = malloc_surface(w*h);
if (surface == NULL) {
result = -8;
goto exit;
}
- unsigned char* pData = malloc(w*h);
-
- surface->version = sizeof(GGLSurface);
surface->width = w;
surface->height = h;
- surface->stride = w; /* Yes, pixels, not bytes */
- surface->data = pData;
- surface->format = GGL_PIXEL_FORMAT_A_8;
+ 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(pData + i*w, row, w);
+ memcpy(surface->data + i*w, row, w);
}
*pSurface = (gr_surface) surface;
@@ -308,21 +438,10 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) {
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
-
- if (fp != NULL) {
- fclose(fp);
- }
- if (result < 0) {
- if (surface) {
- free(surface);
- }
- }
+ if (result < 0 && surface != NULL) free(surface);
return result;
}
void res_free_surface(gr_surface surface) {
- GGLSurface* pSurface = (GGLSurface*) surface;
- if (pSurface) {
- free(pSurface);
- }
+ free(surface);
}
diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c
index 8dd5da1da..fe2c880ac 100644
--- a/minzip/DirUtil.c
+++ b/minzip/DirUtil.c
@@ -234,61 +234,3 @@ dirUnlinkHierarchy(const char *path)
/* delete target directory */
return rmdir(path);
}
-
-int
-dirSetHierarchyPermissions(const char *path,
- int uid, int gid, int dirMode, int fileMode)
-{
- struct stat st;
- if (lstat(path, &st)) {
- return -1;
- }
-
- /* ignore symlinks */
- if (S_ISLNK(st.st_mode)) {
- return 0;
- }
-
- /* directories and files get different permissions */
- if (chown(path, uid, gid) ||
- chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) {
- return -1;
- }
-
- /* recurse over directory components */
- if (S_ISDIR(st.st_mode)) {
- DIR *dir = opendir(path);
- if (dir == NULL) {
- return -1;
- }
-
- errno = 0;
- const struct dirent *de;
- while (errno == 0 && (de = readdir(dir)) != NULL) {
- if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
- continue;
- }
-
- char dn[PATH_MAX];
- snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
- if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) {
- errno = 0;
- } else if (errno == 0) {
- errno = -1;
- }
- }
-
- if (errno != 0) {
- int save = errno;
- closedir(dir);
- errno = save;
- return -1;
- }
-
- if (closedir(dir)) {
- return -1;
- }
- }
-
- return 0;
-}
diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h
index a5cfa761b..85a00128b 100644
--- a/minzip/DirUtil.h
+++ b/minzip/DirUtil.h
@@ -48,14 +48,6 @@ int dirCreateHierarchy(const char *path, int mode,
*/
int dirUnlinkHierarchy(const char *path);
-/* chown -R <uid>:<gid> <path>
- * chmod -R <mode> <path>
- *
- * Sets directories to <dirMode> and files to <fileMode>. Skips symlinks.
- */
-int dirSetHierarchyPermissions(const char *path,
- int uid, int gid, int dirMode, int fileMode);
-
#ifdef __cplusplus
}
#endif
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 31c76d6d4..ac6f5c33f 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -8,42 +8,17 @@
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
-#define LOG_TAG "minzip"
+#define LOG_TAG "sysutil"
#include "Log.h"
#include "SysUtil.h"
-/*
- * Having trouble finding a portable way to get this. sysconf(_SC_PAGE_SIZE)
- * seems appropriate, but we don't have that on the device. Some systems
- * have getpagesize(2), though the linux man page has some odd cautions.
- */
-#define DEFAULT_PAGE_SIZE 4096
-
-
-/*
- * Create an anonymous shared memory segment large enough to hold "length"
- * bytes. The actual segment may be larger because mmap() operates on
- * page boundaries (usually 4K).
- */
-static void* sysCreateAnonShmem(size_t length)
-{
- void* ptr;
-
- ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_ANON, -1, 0);
- if (ptr == MAP_FAILED) {
- LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
- strerror(errno));
- return NULL;
- }
-
- return ptr;
-}
-
static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
{
off_t start, end;
@@ -74,48 +49,13 @@ static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
}
/*
- * Pull the contents of a file into an new shared memory segment. We grab
- * everything from fd's current offset on.
- *
- * We need to know the length ahead of time so we can allocate a segment
- * of sufficient size.
- */
-int sysLoadFileInShmem(int fd, MemMapping* pMap)
-{
- off_t start;
- size_t length, actual;
- void* memPtr;
-
- assert(pMap != NULL);
-
- if (getFileStartAndLength(fd, &start, &length) < 0)
- return -1;
-
- memPtr = sysCreateAnonShmem(length);
- if (memPtr == NULL)
- return -1;
-
- pMap->baseAddr = pMap->addr = memPtr;
- pMap->baseLength = pMap->length = length;
-
- actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length));
- if (actual != length) {
- LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
- sysReleaseShmem(pMap);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Map a file (from fd's current offset) into a shared, read-only memory
+ * Map a file (from fd's current offset) into a private, read-only memory
* segment. The file offset must be a multiple of the page size.
*
* On success, returns 0 and fills out "pMap". On failure, returns a nonzero
* value and does not disturb "pMap".
*/
-int sysMapFileInShmem(int fd, MemMapping* pMap)
+static int sysMapFD(int fd, MemMapping* pMap)
{
off_t start;
size_t length;
@@ -126,87 +66,148 @@ int sysMapFileInShmem(int fd, MemMapping* pMap)
if (getFileStartAndLength(fd, &start, &length) < 0)
return -1;
- memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
+ memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start);
if (memPtr == MAP_FAILED) {
- LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+ LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length,
fd, (int) start, strerror(errno));
return -1;
}
- pMap->baseAddr = pMap->addr = memPtr;
- pMap->baseLength = pMap->length = length;
+ pMap->addr = memPtr;
+ pMap->length = length;
+ pMap->range_count = 1;
+ pMap->ranges = malloc(sizeof(MappedRange));
+ pMap->ranges[0].addr = memPtr;
+ pMap->ranges[0].length = length;
return 0;
}
-/*
- * Map part of a file (from fd's current offset) into a shared, read-only
- * memory segment.
- *
- * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
- * value and does not disturb "pMap".
- */
-int sysMapFileSegmentInShmem(int fd, off_t start, long length,
- MemMapping* pMap)
+static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
{
- off_t dummy;
- size_t fileLength, actualLength;
- off_t actualStart;
- int adjust;
- void* memPtr;
-
- assert(pMap != NULL);
+ char block_dev[PATH_MAX+1];
+ size_t size;
+ unsigned int blksize;
+ unsigned int blocks;
+ unsigned int range_count;
+ unsigned int i;
+
+ if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) {
+ LOGW("failed to read block device from header\n");
+ return -1;
+ }
+ for (i = 0; i < sizeof(block_dev); ++i) {
+ if (block_dev[i] == '\n') {
+ block_dev[i] = 0;
+ break;
+ }
+ }
- if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
+ if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) {
+ LOGW("failed to parse block map header\n");
return -1;
+ }
- if (start + length > (long)fileLength) {
- LOGW("bad segment: st=%d len=%ld flen=%d\n",
- (int) start, length, (int) fileLength);
+ blocks = ((size-1) / blksize) + 1;
+
+ pMap->range_count = range_count;
+ pMap->ranges = malloc(range_count * sizeof(MappedRange));
+ memset(pMap->ranges, 0, range_count * sizeof(MappedRange));
+
+ // Reserve enough contiguous address space for the whole file.
+ unsigned char* reserve;
+ reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (reserve == MAP_FAILED) {
+ LOGW("failed to reserve address space: %s\n", strerror(errno));
return -1;
}
- /* adjust to be page-aligned */
- adjust = start % DEFAULT_PAGE_SIZE;
- actualStart = start - adjust;
- actualLength = length + adjust;
+ pMap->ranges[range_count-1].addr = reserve;
+ pMap->ranges[range_count-1].length = blocks * blksize;
- memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
- fd, actualStart);
- if (memPtr == MAP_FAILED) {
- LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
- (int) actualLength, fd, (int) actualStart, strerror(errno));
+ int fd = open(block_dev, O_RDONLY);
+ if (fd < 0) {
+ LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno));
return -1;
}
- pMap->baseAddr = memPtr;
- pMap->baseLength = actualLength;
- pMap->addr = (char*)memPtr + adjust;
- pMap->length = length;
+ unsigned char* next = reserve;
+ for (i = 0; i < range_count; ++i) {
+ int start, end;
+ if (fscanf(mapf, "%d %d\n", &start, &end) != 2) {
+ LOGW("failed to parse range %d in block map\n", i);
+ return -1;
+ }
+
+ void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
+ if (addr == MAP_FAILED) {
+ LOGW("failed to map block %d: %s\n", i, strerror(errno));
+ return -1;
+ }
+ pMap->ranges[i].addr = addr;
+ pMap->ranges[i].length = (end-start)*blksize;
+
+ next += pMap->ranges[i].length;
+ }
+
+ pMap->addr = reserve;
+ pMap->length = size;
- LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
- (int) start, (int) length,
- pMap->baseAddr, (int) pMap->baseLength,
- pMap->addr, (int) pMap->length);
+ LOGI("mmapped %d ranges\n", range_count);
return 0;
}
+int sysMapFile(const char* fn, MemMapping* pMap)
+{
+ memset(pMap, 0, sizeof(*pMap));
+
+ if (fn && fn[0] == '@') {
+ // A map of blocks
+ FILE* mapf = fopen(fn+1, "r");
+ if (mapf == NULL) {
+ LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno));
+ return -1;
+ }
+
+ if (sysMapBlockFile(mapf, pMap) != 0) {
+ LOGW("Map of '%s' failed\n", fn);
+ return -1;
+ }
+
+ fclose(mapf);
+ } else {
+ // This is a regular file.
+ int fd = open(fn, O_RDONLY, 0);
+ if (fd < 0) {
+ LOGE("Unable to open '%s': %s\n", fn, strerror(errno));
+ return -1;
+ }
+
+ if (sysMapFD(fd, pMap) != 0) {
+ LOGE("Map of '%s' failed\n", fn);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ }
+ return 0;
+}
+
/*
* Release a memory mapping.
*/
-void sysReleaseShmem(MemMapping* pMap)
+void sysReleaseMap(MemMapping* pMap)
{
- if (pMap->baseAddr == NULL && pMap->baseLength == 0)
- return;
-
- if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
- LOGW("munmap(%p, %d) failed: %s\n",
- pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
- } else {
- LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
- pMap->baseAddr = NULL;
- pMap->baseLength = 0;
+ int i;
+ for (i = 0; i < pMap->range_count; ++i) {
+ if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) {
+ LOGW("munmap(%p, %d) failed: %s\n",
+ pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno));
+ }
}
+ free(pMap->ranges);
+ pMap->ranges = NULL;
+ pMap->range_count = 0;
}
-
diff --git a/minzip/SysUtil.h b/minzip/SysUtil.h
index ec3a4bcfb..7adff1e54 100644
--- a/minzip/SysUtil.h
+++ b/minzip/SysUtil.h
@@ -6,56 +6,47 @@
#ifndef _MINZIP_SYSUTIL
#define _MINZIP_SYSUTIL
-#include "inline_magic.h"
-
+#include <stdio.h>
#include <sys/types.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MappedRange {
+ void* addr;
+ size_t length;
+} MappedRange;
+
/*
* Use this to keep track of mapped segments.
*/
typedef struct MemMapping {
- void* addr; /* start of data */
- size_t length; /* length of data */
+ unsigned char* addr; /* start of data */
+ size_t length; /* length of data */
- void* baseAddr; /* page-aligned base address */
- size_t baseLength; /* length of mapping */
+ int range_count;
+ MappedRange* ranges;
} MemMapping;
-/* copy a map */
-INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) {
- *dst = *src;
-}
-
-/*
- * Load a file into a new shared memory segment. All data from the current
- * offset to the end of the file is pulled in.
- *
- * The segment is read-write, allowing VM fixups. (It should be modified
- * to support .gz/.zip compressed data.)
- *
- * On success, "pMap" is filled in, and zero is returned.
- */
-int sysLoadFileInShmem(int fd, MemMapping* pMap);
-
/*
- * Map a file (from fd's current offset) into a shared,
- * read-only memory segment.
+ * Map a file into a private, read-only memory segment. If 'fn'
+ * begins with an '@' character, it is a map of blocks to be mapped,
+ * otherwise it is treated as an ordinary file.
*
* On success, "pMap" is filled in, and zero is returned.
*/
-int sysMapFileInShmem(int fd, MemMapping* pMap);
-
-/*
- * Like sysMapFileInShmem, but on only part of a file.
- */
-int sysMapFileSegmentInShmem(int fd, off_t start, long length,
- MemMapping* pMap);
+int sysMapFile(const char* fn, MemMapping* pMap);
/*
* Release the pages associated with a shared memory segment.
*
* This does not free "pMap"; it just releases the memory.
*/
-void sysReleaseShmem(MemMapping* pMap);
+void sysReleaseMap(MemMapping* pMap);
+
+#ifdef __cplusplus
+}
+#endif
#endif /*_MINZIP_SYSUTIL*/
diff --git a/minzip/Zip.c b/minzip/Zip.c
index 439e5d9cd..70aff00cd 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -184,7 +184,7 @@ static int validFilename(const char *fileName, unsigned int fileNameLen)
*
* Returns "true" on success.
*/
-static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
+static bool parseZipArchive(ZipArchive* pArchive)
{
bool result = false;
const unsigned char* ptr;
@@ -196,7 +196,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
* signature for the first file (LOCSIG) or, if the archive doesn't
* have any files in it, the end-of-central-directory signature (ENDSIG).
*/
- val = get4LE(pMap->addr);
+ val = get4LE(pArchive->addr);
if (val == ENDSIG) {
LOGI("Found Zip archive, but it looks empty\n");
goto bail;
@@ -209,14 +209,14 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
* Find the EOCD. We'll find it immediately unless they have a file
* comment.
*/
- ptr = pMap->addr + pMap->length - ENDHDR;
+ ptr = pArchive->addr + pArchive->length - ENDHDR;
- while (ptr >= (const unsigned char*) pMap->addr) {
+ while (ptr >= (const unsigned char*) pArchive->addr) {
if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
break;
ptr--;
}
- if (ptr < (const unsigned char*) pMap->addr) {
+ if (ptr < (const unsigned char*) pArchive->addr) {
LOGI("Could not find end-of-central-directory in Zip\n");
goto bail;
}
@@ -230,9 +230,9 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
cdOffset = get4LE(ptr + ENDOFF);
LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
- if (numEntries == 0 || cdOffset >= pMap->length) {
+ if (numEntries == 0 || cdOffset >= pArchive->length) {
LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
- numEntries, cdOffset, pMap->length);
+ numEntries, cdOffset, pArchive->length);
goto bail;
}
@@ -245,14 +245,14 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
goto bail;
- ptr = pMap->addr + cdOffset;
+ ptr = pArchive->addr + cdOffset;
for (i = 0; i < numEntries; i++) {
ZipEntry* pEntry;
unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
const unsigned char* localHdr;
const char *fileName;
- if (ptr + CENHDR > (const unsigned char*)pMap->addr + pMap->length) {
+ if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) {
LOGW("Ran off the end (at %d)\n", i);
goto bail;
}
@@ -266,7 +266,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
extraLen = get2LE(ptr + CENEXT);
commentLen = get2LE(ptr + CENCOM);
fileName = (const char*)ptr + CENHDR;
- if (fileName + fileNameLen > (const char*)pMap->addr + pMap->length) {
+ if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) {
LOGW("Filename ran off the end (at %d)\n", i);
goto bail;
}
@@ -352,15 +352,15 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
}
pEntry->externalFileAttributes = get4LE(ptr + CENATX);
- // Perform pMap->addr + localHdrOffset, ensuring that it won't
+ // Perform pArchive->addr + localHdrOffset, ensuring that it won't
// overflow. This is needed because localHdrOffset is untrusted.
- if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pMap->addr,
+ if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr,
(uintptr_t)localHdrOffset)) {
LOGW("Integer overflow adding in parseZipArchive\n");
goto bail;
}
if ((uintptr_t)localHdr + LOCHDR >
- (uintptr_t)pMap->addr + pMap->length) {
+ (uintptr_t)pArchive->addr + pArchive->length) {
LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i);
goto bail;
}
@@ -374,7 +374,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
LOGW("Integer overflow adding in parseZipArchive\n");
goto bail;
}
- if ((size_t)pEntry->offset + pEntry->compLen > pMap->length) {
+ if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) {
LOGW("Data ran off the end (at %d)\n", i);
goto bail;
}
@@ -427,50 +427,30 @@ bail:
*
* On success, we fill out the contents of "pArchive".
*/
-int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive)
+int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
{
- MemMapping map;
int err;
- LOGV("Opening archive '%s' %p\n", fileName, pArchive);
-
- map.addr = NULL;
- memset(pArchive, 0, sizeof(*pArchive));
-
- pArchive->fd = open(fileName, O_RDONLY, 0);
- if (pArchive->fd < 0) {
- err = errno ? errno : -1;
- LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
- goto bail;
- }
-
- if (sysMapFileInShmem(pArchive->fd, &map) != 0) {
- err = -1;
- LOGW("Map of '%s' failed\n", fileName);
- goto bail;
- }
-
- if (map.length < ENDHDR) {
+ if (length < ENDHDR) {
err = -1;
LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length);
goto bail;
}
- if (!parseZipArchive(pArchive, &map)) {
+ pArchive->addr = addr;
+ pArchive->length = length;
+
+ if (!parseZipArchive(pArchive)) {
err = -1;
LOGV("Parsing '%s' failed\n", fileName);
goto bail;
}
err = 0;
- sysCopyMap(&pArchive->map, &map);
- map.addr = NULL;
bail:
if (err != 0)
mzCloseZipArchive(pArchive);
- if (map.addr != NULL)
- sysReleaseShmem(&map);
return err;
}
@@ -483,16 +463,10 @@ void mzCloseZipArchive(ZipArchive* pArchive)
{
LOGV("Closing archive %p\n", pArchive);
- if (pArchive->fd >= 0)
- close(pArchive->fd);
- if (pArchive->map.addr != NULL)
- sysReleaseShmem(&pArchive->map);
-
free(pArchive->pEntries);
mzHashTableFree(pArchive->pHash);
- pArchive->fd = -1;
pArchive->pHash = NULL;
pArchive->pEntries = NULL;
}
@@ -528,29 +502,7 @@ static bool processStoredEntry(const ZipArchive *pArchive,
const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
void *cookie)
{
- size_t bytesLeft = pEntry->compLen;
- while (bytesLeft > 0) {
- unsigned char buf[32 * 1024];
- ssize_t n;
- size_t count;
- bool ret;
-
- count = bytesLeft;
- if (count > sizeof(buf)) {
- count = sizeof(buf);
- }
- n = read(pArchive->fd, buf, count);
- if (n < 0 || (size_t)n != count) {
- LOGE("Can't read %zu bytes from zip file: %ld\n", count, n);
- return false;
- }
- ret = processFunction(buf, n, cookie);
- if (!ret) {
- return false;
- }
- bytesLeft -= count;
- }
- return true;
+ return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie);
}
static bool processDeflatedEntry(const ZipArchive *pArchive,
@@ -573,8 +525,8 @@ static bool processDeflatedEntry(const ZipArchive *pArchive,
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
+ zstream.next_in = pArchive->addr + pEntry->offset;
+ zstream.avail_in = pEntry->compLen;
zstream.next_out = (Bytef*) procBuf;
zstream.avail_out = sizeof(procBuf);
zstream.data_type = Z_UNKNOWN;
@@ -598,25 +550,6 @@ static bool processDeflatedEntry(const ZipArchive *pArchive,
* Loop while we have data.
*/
do {
- /* read as much as we can */
- if (zstream.avail_in == 0) {
- long getSize = (compRemaining > (long)sizeof(readBuf)) ?
- (long)sizeof(readBuf) : compRemaining;
- LOGVV("+++ reading %ld bytes (%ld left)\n",
- getSize, compRemaining);
-
- int cc = read(pArchive->fd, readBuf, getSize);
- if (cc != (int) getSize) {
- LOGW("inflate read failed (%d vs %ld)\n", cc, getSize);
- goto z_bail;
- }
-
- compRemaining -= getSize;
-
- zstream.next_in = readBuf;
- zstream.avail_in = getSize;
- }
-
/* uncompress the data */
zerr = inflate(&zstream, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
@@ -676,12 +609,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
bool ret = false;
off_t oldOff;
- /* save current offset */
- oldOff = lseek(pArchive->fd, 0, SEEK_CUR);
-
- /* Seek to the beginning of the entry's compressed data. */
- lseek(pArchive->fd, pEntry->offset, SEEK_SET);
-
switch (pEntry->compression) {
case STORED:
ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
@@ -695,8 +622,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
break;
}
- /* restore file offset */
- lseek(pArchive->fd, oldOff, SEEK_SET);
return ret;
}
@@ -772,13 +697,15 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
static bool writeProcessFunction(const unsigned char *data, int dataLen,
void *cookie)
{
- int fd = (int)cookie;
-
+ int fd = (int)(intptr_t)cookie;
+ if (dataLen == 0) {
+ return true;
+ }
ssize_t soFar = 0;
while (true) {
ssize_t n = write(fd, data+soFar, dataLen-soFar);
if (n <= 0) {
- LOGE("Error writing %ld bytes from zip file from %p: %s\n",
+ LOGE("Error writing %zd bytes from zip file from %p: %s\n",
dataLen-soFar, data+soFar, strerror(errno));
if (errno != EINTR) {
return false;
@@ -787,7 +714,7 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen,
soFar += n;
if (soFar == dataLen) return true;
if (soFar > dataLen) {
- LOGE("write overrun? (%ld bytes instead of %d)\n",
+ LOGE("write overrun? (%zd bytes instead of %d)\n",
soFar, dataLen);
return false;
}
@@ -802,7 +729,7 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
const ZipEntry *pEntry, int fd)
{
bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
- (void*)fd);
+ (void*)(intptr_t)fd);
if (!ret) {
LOGE("Can't extract entry to file.\n");
return false;
@@ -810,6 +737,23 @@ 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;
@@ -1123,7 +1067,8 @@ bool mzExtractRecursive(const ZipArchive *pArchive,
setfscreatecon(secontext);
}
- int fd = creat(targetFile, UNZIP_FILEMODE);
+ int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC
+ , UNZIP_FILEMODE);
if (secontext) {
freecon(secontext);
@@ -1138,7 +1083,12 @@ bool mzExtractRecursive(const ZipArchive *pArchive,
}
bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
- close(fd);
+ if (ok) {
+ ok = (fsync(fd) == 0);
+ }
+ if (close(fd) != 0) {
+ ok = false;
+ }
if (!ok) {
LOGE("Error extracting \"%s\"\n", targetFile);
ok = false;
diff --git a/minzip/Zip.h b/minzip/Zip.h
index c94282827..2054b38a4 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -46,11 +46,11 @@ typedef struct ZipEntry {
* One Zip archive. Treat as opaque.
*/
typedef struct ZipArchive {
- int fd;
- unsigned int numEntries;
- ZipEntry* pEntries;
- HashTable* pHash; // maps file name to ZipEntry
- MemMapping map;
+ unsigned int numEntries;
+ ZipEntry* pEntries;
+ HashTable* pHash; // maps file name to ZipEntry
+ unsigned char* addr;
+ size_t length;
} ZipArchive;
/*
@@ -68,7 +68,7 @@ typedef struct {
* On success, returns 0 and populates "pArchive". Returns nonzero errno
* value on failure.
*/
-int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive);
+int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive);
/*
* Close archive, releasing resources associated with it.
@@ -183,6 +183,17 @@ 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
* targetDir, which must exist and be a writable directory.
*
diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c
index a39d60001..5657dfc82 100644
--- a/mtdutils/flash_image.c
+++ b/mtdutils/flash_image.c
@@ -24,6 +24,9 @@
#include "cutils/log.h"
#include "mtdutils.h"
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
#define LOG_TAG "flash_image"
#define HEADER_SIZE 2048 // size of header to compare for equality
diff --git a/recovery.cpp b/recovery.cpp
index 64091305a..c6e12702d 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -48,6 +48,8 @@
#include "adb_install.h"
extern "C" {
#include "minadbd/adb.h"
+#include "fuse_sideload.h"
+#include "fuse_sdcard_provider.h"
}
extern "C" {
@@ -72,6 +74,9 @@ static const struct option OPTIONS[] = {
{ "show_text", no_argument, NULL, 't' },
{ "just_exit", no_argument, NULL, 'x' },
{ "locale", required_argument, NULL, 'l' },
+ { "stages", required_argument, NULL, 'g' },
+ { "shutdown_after", no_argument, NULL, 'p' },
+ { "reason", required_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 },
};
@@ -87,11 +92,14 @@ static const char *CACHE_ROOT = "/cache";
static const char *SDCARD_ROOT = "/sdcard";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
-static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
+
+#define KEEP_LOG_COUNT 10
RecoveryUI* ui = NULL;
char* locale = NULL;
char recovery_version[PROPERTY_VALUE_MAX+1];
+char* stage = NULL;
+char* reason = NULL;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -171,6 +179,12 @@ fopen_path(const char *path, const char *mode) {
return fp;
}
+static void redirect_stdio(const char* filename) {
+ // If these fail, there's not really anywhere to complain...
+ freopen(filename, "a", stdout); setbuf(stdout, NULL);
+ freopen(filename, "a", stderr); setbuf(stderr, NULL);
+}
+
// close a file, log an error if the error indicator is set
static void
check_and_fclose(FILE *fp, const char *name) {
@@ -188,13 +202,14 @@ get_args(int *argc, char ***argv) {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
+ stage = strndup(boot.stage, sizeof(boot.stage));
if (boot.command[0] != 0 && boot.command[0] != 255) {
- LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
+ LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
}
if (boot.status[0] != 0 && boot.status[0] != 255) {
- LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
+ LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
}
// --- if arguments weren't supplied, look in the bootloader control block
@@ -451,96 +466,6 @@ erase_volume(const char *volume) {
return result;
}
-static char*
-copy_sideloaded_package(const char* original_path) {
- if (ensure_path_mounted(original_path) != 0) {
- LOGE("Can't mount %s\n", original_path);
- return NULL;
- }
-
- if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) {
- LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR);
- return NULL;
- }
-
- if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) {
- if (errno != EEXIST) {
- LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
- return NULL;
- }
- }
-
- // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a
- // directory, owned by root, readable and writable only by root.
- struct stat st;
- if (stat(SIDELOAD_TEMP_DIR, &st) != 0) {
- LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
- return NULL;
- }
- if (!S_ISDIR(st.st_mode)) {
- LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR);
- return NULL;
- }
- if ((st.st_mode & 0777) != 0700) {
- LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode);
- return NULL;
- }
- if (st.st_uid != 0) {
- LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid);
- return NULL;
- }
-
- char copy_path[PATH_MAX];
- strcpy(copy_path, SIDELOAD_TEMP_DIR);
- strcat(copy_path, "/package.zip");
-
- char* buffer = (char*)malloc(BUFSIZ);
- if (buffer == NULL) {
- LOGE("Failed to allocate buffer\n");
- return NULL;
- }
-
- size_t read;
- FILE* fin = fopen(original_path, "rb");
- if (fin == NULL) {
- LOGE("Failed to open %s (%s)\n", original_path, strerror(errno));
- return NULL;
- }
- FILE* fout = fopen(copy_path, "wb");
- if (fout == NULL) {
- LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
-
- while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) {
- if (fwrite(buffer, 1, read, fout) != read) {
- LOGE("Short write of %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
- }
-
- free(buffer);
-
- if (fclose(fout) != 0) {
- LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
-
- if (fclose(fin) != 0) {
- LOGE("Failed to close %s (%s)\n", original_path, strerror(errno));
- return NULL;
- }
-
- // "adb push" is happy to overwrite read-only files when it's
- // running as root, but we'll try anyway.
- if (chmod(copy_path, 0400) != 0) {
- LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
-
- return strdup(copy_path);
-}
-
static const char**
prepend_title(const char* const* headers) {
// count the number of lines in our title, plus the
@@ -616,9 +541,9 @@ static int compare_string(const void* a, const void* b) {
return strcmp(*(const char**)a, *(const char**)b);
}
-static int
-update_directory(const char* path, const char* unmount_when_done,
- int* wipe_cache, Device* device) {
+// Returns a malloc'd path, or NULL.
+static char*
+browse_directory(const char* path, Device* device) {
ensure_path_mounted(path);
const char* MENU_HEADERS[] = { "Choose a package to install:",
@@ -630,10 +555,7 @@ update_directory(const char* path, const char* unmount_when_done,
d = opendir(path);
if (d == NULL) {
LOGE("error opening %s: %s\n", path, strerror(errno));
- if (unmount_when_done != NULL) {
- ensure_path_unmounted(unmount_when_done);
- }
- return 0;
+ return NULL;
}
const char** headers = prepend_title(MENU_HEADERS);
@@ -689,58 +611,41 @@ update_directory(const char* path, const char* unmount_when_done,
z_size += d_size;
zips[z_size] = NULL;
- int result;
+ char* result;
int chosen_item = 0;
- do {
+ while (true) {
chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
char* item = zips[chosen_item];
int item_len = strlen(item);
if (chosen_item == 0) { // item 0 is always "../"
// go up but continue browsing (if the caller is update_directory)
- result = -1;
+ result = NULL;
break;
- } else if (item[item_len-1] == '/') {
+ }
+
+ char new_path[PATH_MAX];
+ strlcpy(new_path, path, PATH_MAX);
+ strlcat(new_path, "/", PATH_MAX);
+ strlcat(new_path, item, PATH_MAX);
+
+ if (item[item_len-1] == '/') {
// recurse down into a subdirectory
- char new_path[PATH_MAX];
- strlcpy(new_path, path, PATH_MAX);
- strlcat(new_path, "/", PATH_MAX);
- strlcat(new_path, item, PATH_MAX);
new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/'
- result = update_directory(new_path, unmount_when_done, wipe_cache, device);
- if (result >= 0) break;
+ result = browse_directory(new_path, device);
+ if (result) break;
} else {
- // selected a zip file: attempt to install it, and return
- // the status to the caller.
- char new_path[PATH_MAX];
- strlcpy(new_path, path, PATH_MAX);
- strlcat(new_path, "/", PATH_MAX);
- strlcat(new_path, item, PATH_MAX);
-
- ui->Print("\n-- Install %s ...\n", path);
- set_sdcard_update_bootloader_message();
- char* copy = copy_sideloaded_package(new_path);
- if (unmount_when_done != NULL) {
- ensure_path_unmounted(unmount_when_done);
- }
- if (copy) {
- result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE);
- free(copy);
- } else {
- result = INSTALL_ERROR;
- }
+ // selected a zip file: return the malloc'd path to the caller.
+ result = strdup(new_path);
break;
}
- } while (true);
+ }
int i;
for (i = 0; i < z_size; ++i) free(zips[i]);
free(zips);
free(headers);
- if (unmount_when_done != NULL) {
- ensure_path_unmounted(unmount_when_done);
- }
return result;
}
@@ -780,10 +685,73 @@ wipe_data(int confirm, Device* device) {
device->WipeData();
erase_volume("/data");
erase_volume("/cache");
+ erase_persistent_partition();
ui->Print("Data wipe complete.\n");
}
-static void
+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;
+ }
+ char line[1024];
+ int ct = 0;
+ redirect_stdio("/dev/null");
+ while(fgets(line, sizeof(line), fp) != NULL) {
+ ui->Print("%s", line);
+ ct++;
+ if (ct % 30 == 0) {
+ // give the user time to glance at the entries
+ ui->WaitKey();
+ }
+ }
+ redirect_stdio(TEMPORARY_LOG_FILE);
+ fclose(fp);
+}
+
+static void choose_recovery_file(Device* device) {
+ int i;
+ static const char** title_headers = NULL;
+ char *filename;
+ const char* headers[] = { "Select file to view",
+ "",
+ NULL };
+ char* entries[KEEP_LOG_COUNT + 2];
+ memset(entries, 0, sizeof(entries));
+
+ for (i = 0; i < KEEP_LOG_COUNT; i++) {
+ char *filename;
+ if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), 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[i+1] = NULL;
+ break;
+ }
+ entries[i+1] = filename;
+ }
+
+ entries[0] = strdup("Go back");
+ title_headers = prepend_title((const char**)headers);
+
+ 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]);
+ }
+
+ for (i = 0; i < KEEP_LOG_COUNT + 1; i++) {
+ free(entries[i]);
+ }
+}
+
+// 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());
@@ -807,27 +775,48 @@ prompt_and_wait(Device* device, int status) {
// device-specific code may take some action here. It may
// return one of the core actions handled in the switch
// statement below.
- chosen_item = device->InvokeMenuItem(chosen_item);
+ Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
+
+ int wipe_cache = 0;
+ switch (chosen_action) {
+ case Device::NO_ACTION:
+ break;
- int wipe_cache;
- switch (chosen_item) {
case Device::REBOOT:
- return;
+ case Device::SHUTDOWN:
+ case Device::REBOOT_BOOTLOADER:
+ return chosen_action;
case Device::WIPE_DATA:
wipe_data(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return;
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
break;
case Device::WIPE_CACHE:
ui->Print("\n-- Wiping cache...\n");
erase_volume("/cache");
ui->Print("Cache wipe complete.\n");
- if (!ui->IsTextVisible()) return;
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
break;
- case Device::APPLY_EXT:
- status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device);
+ 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")) {
@@ -836,39 +825,26 @@ prompt_and_wait(Device* device, int status) {
ui->Print("Cache wipe complete.\n");
}
}
+
if (status >= 0) {
if (status != INSTALL_SUCCESS) {
ui->SetBackground(RecoveryUI::ERROR);
ui->Print("Installation aborted.\n");
} else if (!ui->IsTextVisible()) {
- return; // reboot if logs aren't visible
+ return Device::NO_ACTION; // reboot if logs aren't visible
} else {
ui->Print("\nInstall from sdcard complete.\n");
}
}
break;
+ }
case Device::APPLY_CACHE:
- // Don't unmount cache at the end of this.
- status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device);
- 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");
- } else {
- ui->Print("Cache wipe complete.\n");
- }
- }
- if (status >= 0) {
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- } else if (!ui->IsTextVisible()) {
- return; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from cache complete.\n");
- }
- }
+ ui->Print("\nAPPLY_CACHE is deprecated.\n");
+ break;
+
+ case Device::READ_RECOVERY_LASTLOG:
+ choose_recovery_file(device);
break;
case Device::APPLY_ADB_SIDELOAD:
@@ -879,7 +855,7 @@ prompt_and_wait(Device* device, int status) {
ui->Print("Installation aborted.\n");
copy_logs();
} else if (!ui->IsTextVisible()) {
- return; // reboot if logs aren't visible
+ return Device::NO_ACTION; // reboot if logs aren't visible
} else {
ui->Print("\nInstall from ADB complete.\n");
}
@@ -939,9 +915,7 @@ main(int argc, char **argv) {
time_t start = time(NULL);
- // If these fail, there's not really anywhere to complain...
- freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
- freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
+ redirect_stdio(TEMPORARY_LOG_FILE);
// If this binary is started with the single argument "--adbd",
// instead of being the normal recovery binary, it turns into kind
@@ -955,6 +929,7 @@ main(int argc, char **argv) {
return 0;
}
+<<<<<<< HEAD
printf("Starting TWRP %s on %s", TW_VERSION_STR, ctime(&start));
Device* device = make_device();
@@ -984,20 +959,19 @@ main(int argc, char **argv) {
load_volume_table();
ensure_path_mounted(LAST_LOG_FILE);
- rotate_last_logs(10);
+ rotate_last_logs(KEEP_LOG_COUNT);
get_args(&argc, &argv);
- int previous_runs = 0;
const char *send_intent = NULL;
const char *update_package = NULL;
int wipe_data = 0, wipe_cache = 0, show_text = 0;
bool just_exit = false;
bool perform_backup = false;
+ bool shutdown_after = false;
int arg;
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
- case 'p': previous_runs = atoi(optarg); break;
case 's': send_intent = optarg; break;
case 'u': update_package = optarg; break;
case 'w': wipe_data = wipe_cache = 1; break;
@@ -1005,6 +979,16 @@ main(int argc, char **argv) {
case 't': show_text = 1; break;
case 'x': just_exit = true; break;
case 'l': locale = optarg; break;
+ case 'g': {
+ if (stage == NULL || *stage == '\0') {
+ char buffer[20] = "1/";
+ strncat(buffer, optarg, sizeof(buffer)-3);
+ stage = strdup(buffer);
+ }
+ break;
+ }
+ case 'p': shutdown_after = true; break;
+ case 'r': reason = optarg; break;
case '?':
LOGE("Invalid command argument\n");
continue;
@@ -1015,13 +999,21 @@ main(int argc, char **argv) {
load_locale_from_cache();
}
printf("locale is [%s]\n", locale);
+ printf("stage is [%s]\n", stage);
+ printf("reason is [%s]\n", reason);
Device* device = make_device();
ui = device->GetUI();
gCurrentUI = ui;
- ui->Init();
ui->SetLocale(locale);
+ ui->Init();
+
+ int st_cur, st_max;
+ if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) {
+ ui->SetStage(st_cur, st_max);
+ }
+
ui->SetBackground(RecoveryUI::NONE);
if (show_text) ui->ShowText(true);
@@ -1102,7 +1094,7 @@ main(int argc, char **argv) {
else
status = INSTALL_ERROR;
/*
- status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
+ 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.");
@@ -1128,6 +1120,7 @@ main(int argc, char **argv) {
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) {
@@ -1186,11 +1179,13 @@ main(int argc, char **argv) {
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
}
+ Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
- prompt_and_wait(device, status);
+ Device::BuiltinAction temp = prompt_and_wait(device, status);
+ if (temp != Device::NO_ACTION) after = temp;
}
- // Otherwise, get ready to boot the main system...
+ // Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
ui->Print("Rebooting...\n");
char backup_arg_char[50];
@@ -1213,5 +1208,23 @@ main(int argc, char **argv) {
reboot(RB_AUTOBOOT);
#endif
property_set(ANDROID_RB_PROPERTY, "reboot,");
+
+ switch (after) {
+ case Device::SHUTDOWN:
+ ui->Print("Shutting down...\n");
+ property_set(ANDROID_RB_PROPERTY, "shutdown,");
+ break;
+
+ case Device::REBOOT_BOOTLOADER:
+ ui->Print("Rebooting to bootloader...\n");
+ property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+ break;
+
+ default:
+ ui->Print("Rebooting...\n");
+ property_set(ANDROID_RB_PROPERTY, "reboot,");
+ break;
+ }
+ sleep(5); // should reboot before this finishes
return EXIT_SUCCESS;
}
diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png
new file mode 100644
index 000000000..774244c84
--- /dev/null
+++ b/res-hdpi/images/erasing_text.png
Binary files differ
diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png
new file mode 100644
index 000000000..64a57ec4e
--- /dev/null
+++ b/res-hdpi/images/error_text.png
Binary files differ
diff --git a/res-hdpi/images/icon_error.png b/res-hdpi/images/icon_error.png
new file mode 100644
index 000000000..cb3d1ab22
--- /dev/null
+++ b/res-hdpi/images/icon_error.png
Binary files differ
diff --git a/res-hdpi/images/icon_installing.png b/res-hdpi/images/icon_installing.png
new file mode 100644
index 000000000..c2c020162
--- /dev/null
+++ b/res-hdpi/images/icon_installing.png
Binary files differ
diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png
new file mode 100644
index 000000000..33b54f1bf
--- /dev/null
+++ b/res-hdpi/images/installing_text.png
Binary files differ
diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png
new file mode 100644
index 000000000..9927ecb6d
--- /dev/null
+++ b/res-hdpi/images/no_command_text.png
Binary files differ
diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png
new file mode 100644
index 000000000..72581832c
--- /dev/null
+++ b/res-hdpi/images/progress_empty.png
Binary files differ
diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png
new file mode 100644
index 000000000..becf87bdf
--- /dev/null
+++ b/res-hdpi/images/progress_fill.png
Binary files differ
diff --git a/res-hdpi/images/stage_empty.png b/res-hdpi/images/stage_empty.png
new file mode 100644
index 000000000..251ec1969
--- /dev/null
+++ b/res-hdpi/images/stage_empty.png
Binary files differ
diff --git a/res-hdpi/images/stage_fill.png b/res-hdpi/images/stage_fill.png
new file mode 100644
index 000000000..1ab79e862
--- /dev/null
+++ b/res-hdpi/images/stage_fill.png
Binary files differ
diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png
new file mode 100644
index 000000000..fd86c3f6e
--- /dev/null
+++ b/res-mdpi/images/erasing_text.png
Binary files differ
diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png
new file mode 100644
index 000000000..f1b44c9b3
--- /dev/null
+++ b/res-mdpi/images/error_text.png
Binary files differ
diff --git a/res-mdpi/images/icon_error.png b/res-mdpi/images/icon_error.png
new file mode 100644
index 000000000..cb3d1ab22
--- /dev/null
+++ b/res-mdpi/images/icon_error.png
Binary files differ
diff --git a/res-mdpi/images/icon_installing.png b/res-mdpi/images/icon_installing.png
new file mode 100644
index 000000000..c2c020162
--- /dev/null
+++ b/res-mdpi/images/icon_installing.png
Binary files differ
diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png
new file mode 100644
index 000000000..064b2a317
--- /dev/null
+++ b/res-mdpi/images/installing_text.png
Binary files differ
diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png
new file mode 100644
index 000000000..1f29b8951
--- /dev/null
+++ b/res-mdpi/images/no_command_text.png
Binary files differ
diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png
new file mode 100644
index 000000000..72581832c
--- /dev/null
+++ b/res-mdpi/images/progress_empty.png
Binary files differ
diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png
new file mode 100644
index 000000000..becf87bdf
--- /dev/null
+++ b/res-mdpi/images/progress_fill.png
Binary files differ
diff --git a/res-mdpi/images/stage_empty.png b/res-mdpi/images/stage_empty.png
new file mode 100644
index 000000000..251ec1969
--- /dev/null
+++ b/res-mdpi/images/stage_empty.png
Binary files differ
diff --git a/res-mdpi/images/stage_fill.png b/res-mdpi/images/stage_fill.png
new file mode 100644
index 000000000..1ab79e862
--- /dev/null
+++ b/res-mdpi/images/stage_fill.png
Binary files differ
diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png
new file mode 100644
index 000000000..f88e0e6a8
--- /dev/null
+++ b/res-xhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png
new file mode 100644
index 000000000..c3a4cc6f8
--- /dev/null
+++ b/res-xhdpi/images/error_text.png
Binary files differ
diff --git a/res-xhdpi/images/icon_error.png b/res-xhdpi/images/icon_error.png
new file mode 100644
index 000000000..cb3d1ab22
--- /dev/null
+++ b/res-xhdpi/images/icon_error.png
Binary files differ
diff --git a/res-xhdpi/images/icon_installing.png b/res-xhdpi/images/icon_installing.png
new file mode 100644
index 000000000..c2c020162
--- /dev/null
+++ b/res-xhdpi/images/icon_installing.png
Binary files differ
diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png
new file mode 100644
index 000000000..a4dacd0f6
--- /dev/null
+++ b/res-xhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png
new file mode 100644
index 000000000..eb34e94b3
--- /dev/null
+++ b/res-xhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png
new file mode 100644
index 000000000..72581832c
--- /dev/null
+++ b/res-xhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png
new file mode 100644
index 000000000..becf87bdf
--- /dev/null
+++ b/res-xhdpi/images/progress_fill.png
Binary files differ
diff --git a/res-xhdpi/images/stage_empty.png b/res-xhdpi/images/stage_empty.png
new file mode 100644
index 000000000..251ec1969
--- /dev/null
+++ b/res-xhdpi/images/stage_empty.png
Binary files differ
diff --git a/res-xhdpi/images/stage_fill.png b/res-xhdpi/images/stage_fill.png
new file mode 100644
index 000000000..1ab79e862
--- /dev/null
+++ b/res-xhdpi/images/stage_fill.png
Binary files differ
diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png
new file mode 100644
index 000000000..c87fd52b4
--- /dev/null
+++ b/res-xxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png
new file mode 100644
index 000000000..486e951df
--- /dev/null
+++ b/res-xxhdpi/images/error_text.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_error.png b/res-xxhdpi/images/icon_error.png
new file mode 100644
index 000000000..cb3d1ab22
--- /dev/null
+++ b/res-xxhdpi/images/icon_error.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_installing.png b/res-xxhdpi/images/icon_installing.png
new file mode 100644
index 000000000..c2c020162
--- /dev/null
+++ b/res-xxhdpi/images/icon_installing.png
Binary files differ
diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png
new file mode 100644
index 000000000..ef6e8f3f0
--- /dev/null
+++ b/res-xxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png
new file mode 100644
index 000000000..cc98bb18a
--- /dev/null
+++ b/res-xxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png
new file mode 100644
index 000000000..72581832c
--- /dev/null
+++ b/res-xxhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png
new file mode 100644
index 000000000..becf87bdf
--- /dev/null
+++ b/res-xxhdpi/images/progress_fill.png
Binary files differ
diff --git a/res-xxhdpi/images/stage_empty.png b/res-xxhdpi/images/stage_empty.png
new file mode 100644
index 000000000..251ec1969
--- /dev/null
+++ b/res-xxhdpi/images/stage_empty.png
Binary files differ
diff --git a/res-xxhdpi/images/stage_fill.png b/res-xxhdpi/images/stage_fill.png
new file mode 100644
index 000000000..1ab79e862
--- /dev/null
+++ b/res-xxhdpi/images/stage_fill.png
Binary files differ
diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png
new file mode 100644
index 000000000..612e7a390
--- /dev/null
+++ b/res-xxxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png
new file mode 100644
index 000000000..50d2fadb5
--- /dev/null
+++ b/res-xxxhdpi/images/error_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_error.png b/res-xxxhdpi/images/icon_error.png
new file mode 100644
index 000000000..cb3d1ab22
--- /dev/null
+++ b/res-xxxhdpi/images/icon_error.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_installing.png b/res-xxxhdpi/images/icon_installing.png
new file mode 100644
index 000000000..c2c020162
--- /dev/null
+++ b/res-xxxhdpi/images/icon_installing.png
Binary files differ
diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png
new file mode 100644
index 000000000..9bd093bf4
--- /dev/null
+++ b/res-xxxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png
new file mode 100644
index 000000000..6354e6a99
--- /dev/null
+++ b/res-xxxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png
new file mode 100644
index 000000000..72581832c
--- /dev/null
+++ b/res-xxxhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png
new file mode 100644
index 000000000..becf87bdf
--- /dev/null
+++ b/res-xxxhdpi/images/progress_fill.png
Binary files differ
diff --git a/res-xxxhdpi/images/stage_empty.png b/res-xxxhdpi/images/stage_empty.png
new file mode 100644
index 000000000..251ec1969
--- /dev/null
+++ b/res-xxxhdpi/images/stage_empty.png
Binary files differ
diff --git a/res-xxxhdpi/images/stage_fill.png b/res-xxxhdpi/images/stage_fill.png
new file mode 100644
index 000000000..1ab79e862
--- /dev/null
+++ b/res-xxxhdpi/images/stage_fill.png
Binary files differ
diff --git a/roots.cpp b/roots.cpp
index 63a8eaf3b..d69245f24 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -19,8 +19,10 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <ctype.h>
+#include <fcntl.h>
extern "C" {
#include <fs_mgr.h>
@@ -30,12 +32,17 @@ extern "C" {
#include "roots.h"
#include "common.h"
#include "make_ext4fs.h"
-#include "partitions.hpp"
+extern "C" {
+#include "wipe.h"
+#include "cryptfs.h"
+}
static struct fstab *fstab = NULL;
extern struct selabel_handle *sehandle;
+static const char* PERSISTENT_PATH = "/persistent";
+
void load_volume_table()
{
int i;
@@ -47,7 +54,7 @@ void load_volume_table()
return;
}
- ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk", 0);
+ ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");
if (ret < 0 ) {
LOGE("failed to add /tmp entry to fstab\n");
fs_mgr_free_fstab(fstab);
@@ -157,6 +164,20 @@ int ensure_path_unmounted(const char* path) {
return unmount_mounted_volume(mv);
}
+static int exec_cmd(const char* path, char* const argv[]) {
+ int status;
+ pid_t child;
+ if ((child = vfork()) == 0) {
+ execv(path, argv);
+ _exit(-1);
+ }
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ LOGE("%s failed with status %d\n", path, WEXITSTATUS(status));
+ }
+ return WEXITSTATUS(status);
+}
+
int format_volume(const char* volume) {
if (PartitionManager.Wipe_By_Path(volume))
return 0;
@@ -205,10 +226,51 @@ int format_volume(const char* volume) {
return 0;
}
- if (strcmp(v->fs_type, "ext4") == 0) {
- int result = make_ext4fs(v->blk_device, v->length, volume, sehandle);
+ if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "f2fs") == 0) {
+ // if there's a key_loc that looks like a path, it should be a
+ // block device for storing encryption metadata. wipe it too.
+ if (v->key_loc != NULL && v->key_loc[0] == '/') {
+ LOGI("wiping %s\n", v->key_loc);
+ int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ LOGE("format_volume: failed to open %s\n", v->key_loc);
+ return -1;
+ }
+ wipe_block_device(fd, get_file_size(fd));
+ close(fd);
+ }
+
+ ssize_t length = 0;
+ if (v->length != 0) {
+ length = v->length;
+ } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
+ length = -CRYPT_FOOTER_OFFSET;
+ }
+ int result;
+ if (strcmp(v->fs_type, "ext4") == 0) {
+ result = make_ext4fs(v->blk_device, length, volume, sehandle);
+ } else { /* Has to be f2fs because we checked earlier. */
+ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
+ LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type);
+ return -1;
+ }
+ if (length < 0) {
+ LOGE("format_volume: negative length (%zd) not supported on %s\n", length, v->fs_type);
+ return -1;
+ }
+ char *num_sectors;
+ if (asprintf(&num_sectors, "%zd", length / 512) <= 0) {
+ LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device);
+ return -1;
+ }
+ const char *f2fs_path = "/sbin/mkfs.f2fs";
+ const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL};
+
+ result = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
+ free(num_sectors);
+ }
if (result != 0) {
- LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device);
+ LOGE("format_volume: make %s failed on %s with %d(%s)\n", v->fs_type, v->blk_device, result, strerror(errno));
return -1;
}
return 0;
@@ -218,6 +280,41 @@ 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");
@@ -228,10 +325,16 @@ int setup_install_mounts() {
if (strcmp(v->mount_point, "/tmp") == 0 ||
strcmp(v->mount_point, "/cache") == 0) {
- if (ensure_path_mounted(v->mount_point) != 0) return -1;
+ if (ensure_path_mounted(v->mount_point) != 0) {
+ LOGE("failed to mount %s\n", v->mount_point);
+ return -1;
+ }
} else {
- if (ensure_path_unmounted(v->mount_point) != 0) return -1;
+ if (ensure_path_unmounted(v->mount_point) != 0) {
+ LOGE("failed to unmount %s\n", v->mount_point);
+ return -1;
+ }
}
}
return 0;
diff --git a/roots.h b/roots.h
index 230d9ded3..b62a5b13a 100644
--- a/roots.h
+++ b/roots.h
@@ -46,6 +46,11 @@ 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 7e7bd8a99..6fff30a25 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -58,6 +58,7 @@ static double now() {
ScreenRecoveryUI::ScreenRecoveryUI() :
currentIcon(NONE),
installingFrame(0),
+ locale(NULL),
rtl_locale(false),
progressBarType(EMPTY),
progressScopeStart(0),
@@ -75,67 +76,59 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
menu_top(0),
menu_items(0),
menu_sel(0),
-
- // These values are correct for the default image resources
- // provided with the android platform. Devices which use
- // different resources should have a subclass of ScreenRecoveryUI
- // that overrides Init() to set these values appropriately and
- // then call the superclass Init().
animation_fps(20),
- indeterminate_frames(6),
- installing_frames(7),
- install_overlay_offset_x(13),
- install_overlay_offset_y(190),
- overlay_offset_x(-1),
- overlay_offset_y(-1) {
+ 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;
}
-// Draw the given frame over the installation overlay animation. The
-// background is not cleared or draw with the base icon first; we
-// assume that the frame already contains some other frame of the
-// animation. Does nothing if no overlay animation is defined.
-// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::draw_install_overlay_locked(int frame) {
- if (installationOverlay == NULL || overlay_offset_x < 0) return;
- gr_surface surface = installationOverlay[frame];
- int iconWidth = gr_get_width(surface);
- int iconHeight = gr_get_height(surface);
- gr_blit(surface, 0, 0, iconWidth, iconHeight,
- overlay_offset_x, overlay_offset_y);
-}
-
// 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)
{
pagesIdentical = false;
gr_color(0, 0, 0, 255);
- gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+ gr_clear();
if (icon) {
gr_surface surface = backgroundIcon[icon];
+ if (icon == INSTALLING_UPDATE || icon == ERASING) {
+ surface = installation[installingFrame];
+ }
gr_surface text_surface = backgroundText[icon];
int iconWidth = gr_get_width(surface);
int iconHeight = gr_get_height(surface);
int textWidth = gr_get_width(text_surface);
int textHeight = gr_get_height(text_surface);
+ int stageHeight = gr_get_height(stageMarkerEmpty);
+
+ int sh = (max_stage >= 0) ? stageHeight : 0;
- int iconX = (gr_fb_width() - iconWidth) / 2;
- int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2;
+ iconX = (gr_fb_width() - iconWidth) / 2;
+ iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2;
int textX = (gr_fb_width() - textWidth) / 2;
- int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40;
+ int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40;
gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
- if (icon == INSTALLING_UPDATE || icon == ERASING) {
- draw_install_overlay_locked(installingFrame);
+ if (stageHeight > 0) {
+ int sw = gr_get_width(stageMarkerEmpty);
+ int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
+ int y = iconY + iconHeight + 20;
+ for (int i = 0; i < max_stage; ++i) {
+ gr_blit((i < stage) ? stageMarkerFill : stageMarkerEmpty,
+ 0, 0, sw, stageHeight, x, y);
+ x += sw;
+ }
}
gr_color(255, 255, 255, 255);
@@ -150,7 +143,8 @@ void ScreenRecoveryUI::draw_progress_locked()
if (currentIcon == ERROR) return;
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
- draw_install_overlay_locked(installingFrame);
+ gr_surface icon = installation[installingFrame];
+ gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY);
}
if (progressBarType != EMPTY) {
@@ -187,18 +181,6 @@ void ScreenRecoveryUI::draw_progress_locked()
}
}
}
-
- if (progressBarType == INDETERMINATE) {
- static int frame = 0;
- gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
- // in RTL locales, we run the animation backwards, which
- // makes the spinner spin the other way.
- if (rtl_locale) {
- frame = (frame + indeterminate_frames - 1) % indeterminate_frames;
- } else {
- frame = (frame + 1) % indeterminate_frames;
- }
- }
}
}
@@ -230,12 +212,12 @@ void ScreenRecoveryUI::SetColor(UIElement e) {
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_screen_locked()
{
- draw_background_locked(currentIcon);
- draw_progress_locked();
-
- if (show_text) {
- SetColor(TEXT_FILL);
- gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+ if (!show_text) {
+ draw_background_locked(currentIcon);
+ draw_progress_locked();
+ } else {
+ gr_color(0, 0, 0, 255);
+ gr_clear();
int y = 0;
int i = 0;
@@ -325,12 +307,6 @@ void ScreenRecoveryUI::progress_loop() {
redraw = 1;
}
- // update the progress bar animation, if active
- // skip this if we have a text overlay (too expensive to update)
- if (progressBarType == INDETERMINATE && !show_text) {
- redraw = 1;
- }
-
// move the progress bar forward on timed intervals, if configured
int duration = progressScopeDuration;
if (progressBarType == DETERMINATE && duration > 0) {
@@ -355,14 +331,21 @@ void ScreenRecoveryUI::progress_loop() {
}
void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) {
- int result = res_create_surface(filename, 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) {
+ 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) {
- int result = res_create_localized_surface(filename, surface);
+ int result = res_create_localized_alpha_surface(filename, locale, surface);
if (result < 0) {
LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
}
@@ -382,51 +365,31 @@ void ScreenRecoveryUI::Init()
text_cols = gr_fb_width() / char_width;
if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
- LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
+ backgroundIcon[NONE] = NULL;
+ LoadBitmapArray("icon_installing", &installing_frames, &installation);
+ backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : NULL;
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
LoadBitmap("icon_error", &backgroundIcon[ERROR]);
backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
LoadBitmap("progress_empty", &progressBarEmpty);
LoadBitmap("progress_fill", &progressBarFill);
+ LoadBitmap("stage_empty", &stageMarkerEmpty);
+ LoadBitmap("stage_fill", &stageMarkerFill);
LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
- int i;
-
- progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames *
- sizeof(gr_surface));
- for (i = 0; i < indeterminate_frames; ++i) {
- char filename[40];
- // "indeterminate01.png", "indeterminate02.png", ...
- sprintf(filename, "indeterminate%02d", i+1);
- LoadBitmap(filename, progressBarIndeterminate+i);
- }
-
- if (installing_frames > 0) {
- installationOverlay = (gr_surface*)malloc(installing_frames *
- sizeof(gr_surface));
- for (i = 0; i < installing_frames; ++i) {
- char filename[40];
- // "icon_installing_overlay01.png",
- // "icon_installing_overlay02.png", ...
- sprintf(filename, "icon_installing_overlay%02d", i+1);
- LoadBitmap(filename, installationOverlay+i);
- }
- } else {
- installationOverlay = NULL;
- }
-
pthread_create(&progress_t, NULL, progress_thread, NULL);
RecoveryUI::Init();
}
-void ScreenRecoveryUI::SetLocale(const char* locale) {
- if (locale) {
+void ScreenRecoveryUI::SetLocale(const char* new_locale) {
+ if (new_locale) {
+ this->locale = new_locale;
char* lang = strdup(locale);
for (char* p = lang; *p; ++p) {
if (*p == '_') {
@@ -445,6 +408,8 @@ void ScreenRecoveryUI::SetLocale(const char* locale) {
rtl_locale = true;
}
free(lang);
+ } else {
+ new_locale = NULL;
}
}
@@ -452,16 +417,6 @@ void ScreenRecoveryUI::SetBackground(Icon icon)
{
pthread_mutex_lock(&updateMutex);
- // Adjust the offset to account for the positioning of the
- // base image on the screen.
- if (backgroundIcon[icon] != NULL) {
- gr_surface bg = backgroundIcon[icon];
- gr_surface text = backgroundText[icon];
- overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2;
- overlay_offset_y = install_overlay_offset_y +
- (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2;
- }
-
currentIcon = icon;
update_screen_locked();
@@ -506,7 +461,7 @@ void ScreenRecoveryUI::SetProgress(float fraction)
if (fraction > 1.0) fraction = 1.0;
if (progressBarType == DETERMINATE && fraction > progress) {
// Skip updates that aren't visibly different.
- int width = gr_get_width(progressBarIndeterminate[0]);
+ int width = gr_get_width(progressBarEmpty);
float scale = width * progressScopeSize;
if ((int) (progress * scale) != (int) (fraction * scale)) {
progress = fraction;
@@ -516,6 +471,13 @@ void ScreenRecoveryUI::SetProgress(float fraction)
pthread_mutex_unlock(&updateMutex);
}
+void ScreenRecoveryUI::SetStage(int current, int max) {
+ pthread_mutex_lock(&updateMutex);
+ stage = current;
+ max_stage = max;
+ pthread_mutex_unlock(&updateMutex);
+}
+
void ScreenRecoveryUI::Print(const char *fmt, ...)
{
char buf[256];
diff --git a/screen_ui.h b/screen_ui.h
index fc35d95b6..01a33bfe2 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -39,6 +39,8 @@ class ScreenRecoveryUI : public RecoveryUI {
void ShowProgress(float portion, float seconds);
void SetProgress(float fraction);
+ void SetStage(int current, int max);
+
// text log
void ShowText(bool visible);
bool IsTextVisible();
@@ -58,21 +60,20 @@ class ScreenRecoveryUI : public RecoveryUI {
enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL };
virtual void SetColor(UIElement e);
- protected:
- int install_overlay_offset_x, install_overlay_offset_y;
-
private:
Icon currentIcon;
int installingFrame;
+ const char* locale;
bool rtl_locale;
pthread_mutex_t updateMutex;
gr_surface backgroundIcon[5];
gr_surface backgroundText[5];
- gr_surface *installationOverlay;
- gr_surface *progressBarIndeterminate;
+ gr_surface *installation;
gr_surface progressBarEmpty;
gr_surface progressBarFill;
+ gr_surface stageMarkerEmpty;
+ gr_surface stageMarkerFill;
ProgressType progressBarType;
@@ -100,11 +101,14 @@ class ScreenRecoveryUI : public RecoveryUI {
pthread_t progress_t;
int animation_fps;
- int indeterminate_frames;
int installing_frames;
- int overlay_offset_x, overlay_offset_y;
+ protected:
+ private:
+
+ int iconX, iconY;
+
+ int stage, max_stage;
- void draw_install_overlay_locked(int frame);
void draw_background_locked(Icon icon);
void draw_progress_locked();
void draw_screen_locked();
@@ -114,6 +118,7 @@ class ScreenRecoveryUI : public RecoveryUI {
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);
};
diff --git a/testdata/otasigned_ecdsa_sha256.zip b/testdata/otasigned_ecdsa_sha256.zip
new file mode 100644
index 000000000..999fcdd0f
--- /dev/null
+++ b/testdata/otasigned_ecdsa_sha256.zip
Binary files differ
diff --git a/testdata/testkey_ecdsa.pk8 b/testdata/testkey_ecdsa.pk8
new file mode 100644
index 000000000..9a521c8cf
--- /dev/null
+++ b/testdata/testkey_ecdsa.pk8
Binary files differ
diff --git a/testdata/testkey_ecdsa.x509.pem b/testdata/testkey_ecdsa.x509.pem
new file mode 100644
index 000000000..b12283645
--- /dev/null
+++ b/testdata/testkey_ecdsa.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBezCCASACCQC4g5wurPSmtzAKBggqhkjOPQQDAjBFMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMB4XDTEzMTAwODIxMTAxM1oXDTE0MTAwODIxMTAxM1owRTELMAkGA1UE
+BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
+ZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGcO1QDowF2E
+RboWVmAYI2oXTr5MHAJ4xpMUFsrWVvoktYSN2RhNuOl5jZGvSBsQII9p/4qfjLmS
+TBaCfQ0Xmt4wCgYIKoZIzj0EAwIDSQAwRgIhAIJjWmZAwngc2VcHUhYp2oSLoCQ+
+P+7AtbAn5242AqfOAiEAghO0t6jTKs0LUhLJrQwbOkHyZMVdZaG2vcwV9y9H5Qc=
+-----END CERTIFICATE-----
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 000000000..4d99d5249
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,26 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+ asn1_decoder_test.cpp
+
+shared_libraries := \
+ liblog \
+ libcutils
+
+static_libraries := \
+ libgtest \
+ libgtest_main \
+ libverifier
+
+$(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 include $(BUILD_NATIVE_TEST)) \
+) \ No newline at end of file
diff --git a/tests/asn1_decoder_test.cpp b/tests/asn1_decoder_test.cpp
new file mode 100644
index 000000000..af96d87d2
--- /dev/null
+++ b/tests/asn1_decoder_test.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "asn1_decoder_test"
+
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "asn1_decoder.h"
+
+namespace android {
+
+class Asn1DecoderTest : public testing::Test {
+};
+
+TEST_F(Asn1DecoderTest, Empty_Failure) {
+ uint8_t empty[] = { };
+ asn1_context_t* ctx = asn1_context_new(empty, sizeof(empty));
+
+ EXPECT_EQ(NULL, asn1_constructed_get(ctx));
+ EXPECT_FALSE(asn1_constructed_skip_all(ctx));
+ EXPECT_EQ(0, asn1_constructed_type(ctx));
+ EXPECT_EQ(NULL, asn1_sequence_get(ctx));
+ EXPECT_EQ(NULL, asn1_set_get(ctx));
+ EXPECT_FALSE(asn1_sequence_next(ctx));
+
+ uint8_t* junk;
+ size_t length;
+ EXPECT_FALSE(asn1_oid_get(ctx, &junk, &length));
+ EXPECT_FALSE(asn1_octet_string_get(ctx, &junk, &length));
+
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, ConstructedGet_TruncatedLength_Failure) {
+ uint8_t truncated[] = { 0xA0, 0x82, };
+ asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
+ EXPECT_EQ(NULL, asn1_constructed_get(ctx));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, ConstructedGet_LengthTooBig_Failure) {
+ uint8_t truncated[] = { 0xA0, 0x8a, 0xA5, 0x5A, 0xA5, 0x5A,
+ 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, };
+ asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
+ EXPECT_EQ(NULL, asn1_constructed_get(ctx));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, ConstructedGet_TooSmallForChild_Failure) {
+ uint8_t data[] = { 0xA5, 0x02, 0x06, 0x01, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ asn1_context_t* ptr = asn1_constructed_get(ctx);
+ ASSERT_NE((asn1_context_t*)NULL, ptr);
+ EXPECT_EQ(5, asn1_constructed_type(ptr));
+ uint8_t* oid;
+ size_t length;
+ EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length));
+ asn1_context_free(ptr);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, ConstructedGet_Success) {
+ uint8_t data[] = { 0xA5, 0x03, 0x06, 0x01, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ asn1_context_t* ptr = asn1_constructed_get(ctx);
+ ASSERT_NE((asn1_context_t*)NULL, ptr);
+ EXPECT_EQ(5, asn1_constructed_type(ptr));
+ uint8_t* oid;
+ size_t length;
+ ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length));
+ EXPECT_EQ(1U, length);
+ EXPECT_EQ(0x01U, *oid);
+ asn1_context_free(ptr);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, ConstructedSkipAll_TruncatedLength_Failure) {
+ uint8_t truncated[] = { 0xA2, 0x82, };
+ asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
+ EXPECT_FALSE(asn1_constructed_skip_all(ctx));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, ConstructedSkipAll_Success) {
+ uint8_t data[] = { 0xA0, 0x03, 0x02, 0x01, 0x01,
+ 0xA1, 0x03, 0x02, 0x01, 0x01,
+ 0x06, 0x01, 0xA5, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ ASSERT_TRUE(asn1_constructed_skip_all(ctx));
+ uint8_t* oid;
+ size_t length;
+ ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length));
+ EXPECT_EQ(1U, length);
+ EXPECT_EQ(0xA5U, *oid);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, SequenceGet_TruncatedLength_Failure) {
+ uint8_t truncated[] = { 0x30, 0x82, };
+ asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
+ EXPECT_EQ(NULL, asn1_sequence_get(ctx));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, SequenceGet_TooSmallForChild_Failure) {
+ uint8_t data[] = { 0x30, 0x02, 0x06, 0x01, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ asn1_context_t* ptr = asn1_sequence_get(ctx);
+ ASSERT_NE((asn1_context_t*)NULL, ptr);
+ uint8_t* oid;
+ size_t length;
+ EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length));
+ asn1_context_free(ptr);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, SequenceGet_Success) {
+ uint8_t data[] = { 0x30, 0x03, 0x06, 0x01, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ asn1_context_t* ptr = asn1_sequence_get(ctx);
+ ASSERT_NE((asn1_context_t*)NULL, ptr);
+ uint8_t* oid;
+ size_t length;
+ ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length));
+ EXPECT_EQ(1U, length);
+ EXPECT_EQ(0x01U, *oid);
+ asn1_context_free(ptr);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, SetGet_TruncatedLength_Failure) {
+ uint8_t truncated[] = { 0x31, 0x82, };
+ asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
+ EXPECT_EQ(NULL, asn1_set_get(ctx));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, SetGet_TooSmallForChild_Failure) {
+ uint8_t data[] = { 0x31, 0x02, 0x06, 0x01, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ asn1_context_t* ptr = asn1_set_get(ctx);
+ ASSERT_NE((asn1_context_t*)NULL, ptr);
+ uint8_t* oid;
+ size_t length;
+ EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length));
+ asn1_context_free(ptr);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, SetGet_Success) {
+ uint8_t data[] = { 0x31, 0x03, 0x06, 0x01, 0xBA, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ asn1_context_t* ptr = asn1_set_get(ctx);
+ ASSERT_NE((asn1_context_t*)NULL, ptr);
+ uint8_t* oid;
+ size_t length;
+ ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length));
+ EXPECT_EQ(1U, length);
+ EXPECT_EQ(0xBAU, *oid);
+ asn1_context_free(ptr);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, OidGet_LengthZero_Failure) {
+ uint8_t data[] = { 0x06, 0x00, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ uint8_t* oid;
+ size_t length;
+ EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, OidGet_TooSmall_Failure) {
+ uint8_t data[] = { 0x06, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ uint8_t* oid;
+ size_t length;
+ EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, OidGet_Success) {
+ uint8_t data[] = { 0x06, 0x01, 0x99, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ uint8_t* oid;
+ size_t length;
+ ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length));
+ EXPECT_EQ(1U, length);
+ EXPECT_EQ(0x99U, *oid);
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, OctetStringGet_LengthZero_Failure) {
+ uint8_t data[] = { 0x04, 0x00, 0x55, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ uint8_t* string;
+ size_t length;
+ ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, OctetStringGet_TooSmall_Failure) {
+ uint8_t data[] = { 0x04, 0x01, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ uint8_t* string;
+ size_t length;
+ ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length));
+ asn1_context_free(ctx);
+}
+
+TEST_F(Asn1DecoderTest, OctetStringGet_Success) {
+ uint8_t data[] = { 0x04, 0x01, 0xAA, };
+ asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
+ uint8_t* string;
+ size_t length;
+ ASSERT_TRUE(asn1_octet_string_get(ctx, &string, &length));
+ EXPECT_EQ(1U, length);
+ EXPECT_EQ(0xAAU, *string);
+ asn1_context_free(ctx);
+}
+
+} // namespace android
diff --git a/tools/ota/add-property-tag.c b/tools/ota/add-property-tag.c
index 5277edd9c..aab30b2d0 100644
--- a/tools/ota/add-property-tag.c
+++ b/tools/ota/add-property-tag.c
@@ -57,9 +57,9 @@ void write_tagged(FILE *out, const char *line, const char *tag, int number) {
const char *end = line + strlen(line);
while (end > line && isspace(end[-1])) --end;
if (number > 0) {
- fprintf(out, "%.*s%s%d%s", end - line, line, tag, number, end);
+ fprintf(out, "%.*s%s%d%s", (int)(end - line), line, tag, number, end);
} else {
- fprintf(out, "%.*s%s%s", end - line, line, tag, end);
+ fprintf(out, "%.*s%s%s", (int)(end - line), line, tag, end);
}
}
diff --git a/twrp.cpp b/twrp.cpp
index 7361396ad..2849af759 100644
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -91,7 +91,7 @@ int main(int argc, char **argv) {
property_set("ro.twrp.version", TW_VERSION_STR);
time_t StartupTime = time(NULL);
- printf("Starting TWRP %s on %s", TW_VERSION_STR, ctime(&StartupTime));
+ printf("Starting TWRP %s on %s (pid %d)", TW_VERSION_STR, ctime(&StartupTime), getpid());
// Load default values to set DataManager constants and handle ifdefs
DataManager::SetDefaultValues();
@@ -160,7 +160,7 @@ int main(int argc, char **argv) {
PartitionManager.Mount_By_Path("/cache", true);
string Zip_File, Reboot_Value;
- bool Cache_Wipe = false, Factory_Reset = false, Perform_Backup = false;
+ bool Cache_Wipe = false, Factory_Reset = false, Perform_Backup = false, Shutdown = false;
{
TWPartition* misc = PartitionManager.Find_Partition_By_Path("/misc");
@@ -206,6 +206,8 @@ int main(int argc, char **argv) {
Cache_Wipe = true;
} else if (*argptr == 'n') {
Perform_Backup = true;
+ } else if (*argptr == 'p') {
+ Shutdown = true;
} else if (*argptr == 's') {
ptr = argptr;
index2 = 0;
diff --git a/ui.cpp b/ui.cpp
index 44656d10f..8e5b0f0e0 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -33,6 +33,7 @@
#endif
#include "common.h"
+#include "roots.h"
#include "device.h"
#include "minui/minui.h"
#include "screen_ui.h"
@@ -49,10 +50,15 @@ RecoveryUI::RecoveryUI() :
key_queue_len(0),
key_last_down(-1),
key_long_press(false),
- key_down_count(0) {
+ 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;
+ memset(key_pressed, 0, sizeof(key_pressed));
}
void RecoveryUI::Init() {
@@ -61,12 +67,12 @@ void RecoveryUI::Init() {
}
-int RecoveryUI::input_callback(int fd, short revents, void* data)
+int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
{
struct input_event ev;
int ret;
- ret = ev_get_input(fd, revents, &ev);
+ ret = ev_get_input(fd, epevents, &ev);
if (ret)
return -1;
@@ -114,6 +120,7 @@ int RecoveryUI::input_callback(int fd, short revents, void* data)
void RecoveryUI::process_key(int key_code, int updown) {
bool register_key = false;
bool long_press = false;
+ bool reboot_enabled;
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
@@ -135,6 +142,7 @@ void RecoveryUI::process_key(int key_code, int updown) {
}
key_last_down = -1;
}
+ reboot_enabled = enable_reboot;
pthread_mutex_unlock(&key_queue_mutex);
if (register_key) {
@@ -149,13 +157,22 @@ void RecoveryUI::process_key(int key_code, int updown) {
case RecoveryUI::REBOOT:
#ifdef ANDROID_RB_RESTART
- android_reboot(ANDROID_RB_RESTART, 0, 0);
+ if (reboot_enabled) {
+ android_reboot(ANDROID_RB_RESTART, 0, 0);
+ }
#endif
break;
case RecoveryUI::ENQUEUE:
EnqueueKey(key_code);
break;
+
+ case RecoveryUI::MOUNT_SYSTEM:
+#ifndef NO_RECOVERY_MOUNT
+ ensure_path_mounted("/system");
+ Print("Mounted /system.");
+#endif
+ break;
}
}
}
@@ -262,8 +279,47 @@ void RecoveryUI::FlushKeys() {
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) {
- return RecoveryUI::ENQUEUE;
+ if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) {
+ return TOGGLE;
+ }
+
+ if (key == KEY_POWER) {
+ pthread_mutex_lock(&key_queue_mutex);
+ bool reboot_enabled = enable_reboot;
+ pthread_mutex_unlock(&key_queue_mutex);
+
+ if (reboot_enabled) {
+ ++consecutive_power_keys;
+ if (consecutive_power_keys >= 7) {
+ return REBOOT;
+ }
+ }
+ } else {
+ 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) {
@@ -271,3 +327,9 @@ void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
void RecoveryUI::KeyLongPress(int key) {
}
+
+void RecoveryUI::SetEnableReboot(bool enabled) {
+ pthread_mutex_lock(&key_queue_mutex);
+ enable_reboot = enabled;
+ pthread_mutex_unlock(&key_queue_mutex);
+}
diff --git a/ui.h b/ui.h
index 6c8987a33..31a8a7fb1 100644
--- a/ui.h
+++ b/ui.h
@@ -30,6 +30,8 @@ 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) { }
// After calling Init(), you can tell the UI what locale it is operating in.
virtual void SetLocale(const char* locale) { }
@@ -77,7 +79,7 @@ class RecoveryUI {
// 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 };
+ enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE, MOUNT_SYSTEM };
virtual KeyAction CheckKey(int key);
// Called immediately before each call to CheckKey(), tell you if
@@ -91,6 +93,13 @@ class RecoveryUI {
// be called with "true".
virtual void KeyLongPress(int key);
+ // Normally in recovery there's a key sequence that triggers
+ // immediate reboot of the device, regardless of what recovery is
+ // doing (with the default CheckKey implementation, it's pressing
+ // the power button 7 times in row). Call this to enable or
+ // disable that feature. It is enabled by default.
+ virtual void SetEnableReboot(bool enabled);
+
// --- menu display ---
// Display some header text followed by a menu of items, which appears
@@ -119,8 +128,13 @@ private:
int key_last_down; // under key_queue_mutex
bool key_long_press; // under key_queue_mutex
int key_down_count; // under key_queue_mutex
+ bool enable_reboot; // under key_queue_mutex
int rel_sum;
+ int consecutive_power_keys;
+ int consecutive_alternate_keys;
+ int last_key;
+
typedef struct {
RecoveryUI* ui;
int key_code;
@@ -130,7 +144,7 @@ private:
pthread_t input_t;
static void* input_thread(void* cookie);
- static int input_callback(int fd, short revents, void* data);
+ static int input_callback(int fd, uint32_t epevents, void* data);
void process_key(int key_code, int updown);
bool usb_connected();
diff --git a/minelf/Android.mk b/uncrypt/Android.mk
index 10818eae4..b8755fb08 100644
--- a/minelf/Android.mk
+++ b/uncrypt/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 The Android Open Source Project
+# 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.
@@ -13,15 +13,14 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- Retouch.c
+include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(commands_recovery_local_path)
+LOCAL_SRC_FILES := uncrypt.c
-LOCAL_MODULE := libminelf
+LOCAL_MODULE := uncrypt
-LOCAL_CFLAGS += -Wall
+LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_EXECUTABLE)
diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c
new file mode 100644
index 000000000..189fa57e1
--- /dev/null
+++ b/uncrypt/uncrypt.c
@@ -0,0 +1,427 @@
+/*
+ * 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.
+ */
+
+// This program takes a file on an ext4 filesystem and produces a list
+// of the blocks that file occupies, which enables the file contents
+// to be read directly from the block device without mounting the
+// filesystem.
+//
+// If the filesystem is using an encrypted block device, it will also
+// read the file and rewrite it to the same blocks of the underlying
+// (unencrypted) block device, so the file contents can be read
+// without the need for the decryption key.
+//
+// The output of this program is a "block map" which looks like this:
+//
+// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
+// 49652 4096 # file size in bytes, block size
+// 3 # count of block ranges
+// 1000 1008 # block range 0
+// 2100 2102 # ... block range 1
+// 30 33 # ... block range 2
+//
+// Each block range represents a half-open interval; the line "30 33"
+// reprents the blocks [30, 31, 32].
+//
+// 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 <fcntl.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
+
+#define LOG_TAG "uncrypt"
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <fs_mgr.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 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);
+ 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));
+ return -1;
+ }
+ written += wrote;
+ }
+ return 0;
+}
+
+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.)
+ if ((*ranges)[*range_used*2-2] < 0) {
+ (*ranges)[*range_used*2-2] = new_block;
+ (*ranges)[*range_used*2-1] = new_block;
+ }
+
+ if (new_block == (*ranges)[*range_used*2-1]) {
+ // If the new block comes immediately after the current range,
+ // all we have to do is extend the current range.
+ ++(*ranges)[*range_used*2-1];
+ } else {
+ // We need to start a new range.
+
+ // 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));
+ }
+
+ ++*range_used;
+ (*ranges)[*range_used*2-2] = new_block;
+ (*ranges)[*range_used*2-1] = new_block+1;
+ }
+}
+
+static struct fstab* read_fstab()
+{
+ fstab = NULL;
+
+ // The fstab path is always "/fstab.${ro.hardware}".
+ char fstab_path[PATH_MAX+1] = "/fstab.";
+ if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) {
+ ALOGE("failed to get ro.hardware\n");
+ return NULL;
+ }
+
+ fstab = fs_mgr_read_fstab(fstab_path);
+ if (!fstab) {
+ ALOGE("failed to read %s\n", fstab_path);
+ return NULL;
+ }
+
+ return fstab;
+}
+
+const char* find_block_device(const char* path, int* encryptable, int* 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) {
+ struct fstab_rec* v = &fstab->recs[i];
+ 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;
+ if (fs_mgr_is_encryptable(v)) {
+ *encryptable = 1;
+ char buffer[PROPERTY_VALUE_MAX+1];
+ if (property_get("ro.crypto.state", buffer, "") &&
+ strcmp(buffer, "encrypted") == 0) {
+ *encrypted = 1;
+ }
+ }
+ return v->blk_device;
+ }
+ }
+
+ return NULL;
+}
+
+char* parse_recovery_command_file()
+{
+ char* fn = NULL;
+ int count = 0;
+ char temp[1024];
+
+ FILE* f = fopen(RECOVERY_COMMAND_FILE, "r");
+ if (f == NULL) {
+ return NULL;
+ }
+ FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "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);
+ fclose(fo);
+
+ if (fn) {
+ char* newline = strchr(fn, '\n');
+ if (newline) *newline = 0;
+ }
+ return fn;
+}
+
+int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
+ int encrypted)
+{
+ struct stat sb;
+ int ret;
+
+ FILE* mapf = fopen(map_file, "w");
+
+ ret = stat(path, &sb);
+ if (ret != 0) {
+ ALOGE("failed to stat %s\n", path);
+ return -1;
+ }
+
+ ALOGI(" block size: %ld bytes\n", (long)sb.st_blksize);
+
+ 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));
+ 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);
+ }
+ }
+ int head_block = 0;
+ int head = 0, tail = 0;
+ size_t pos = 0;
+
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ALOGE("failed to open fd for reading: %s\n", strerror(errno));
+ return -1;
+ }
+ fsync(fd);
+
+ int wfd = -1;
+ if (encrypted) {
+ wfd = open(blk_dev, O_WRONLY);
+ if (wfd < 0) {
+ ALOGE("failed to open fd for writing: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ while (pos < sb.st_size) {
+ if ((tail+1) % WINDOW_SIZE == head) {
+ // write out head buffer
+ int block = head_block;
+ ret = ioctl(fd, FIBMAP, &block);
+ if (ret != 0) {
+ ALOGE("failed to find block %d\n", head_block);
+ return -1;
+ }
+ 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) {
+ return -1;
+ }
+ }
+ head = (head + 1) % WINDOW_SIZE;
+ ++head_block;
+ }
+
+ // read next block to tail
+ 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) {
+ ALOGE("failed to read: %s\n", strerror(errno));
+ return -1;
+ }
+ so_far += this_read;
+ pos += this_read;
+ }
+ } else {
+ // If we're not encrypting; we don't need to actually read
+ // anything, just skip pos forward as if we'd read a
+ // block.
+ pos += sb.st_blksize;
+ }
+ tail = (tail+1) % WINDOW_SIZE;
+ }
+
+ while (head != tail) {
+ // write out head buffer
+ int block = head_block;
+ ret = ioctl(fd, FIBMAP, &block);
+ if (ret != 0) {
+ ALOGE("failed to find block %d\n", head_block);
+ return -1;
+ }
+ 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) {
+ return -1;
+ }
+ }
+ head = (head + 1) % WINDOW_SIZE;
+ ++head_block;
+ }
+
+ fprintf(mapf, "%d\n", range_used);
+ for (i = 0; i < range_used; ++i) {
+ fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
+ }
+
+ fclose(mapf);
+ close(fd);
+ if (encrypted) {
+ close(wfd);
+ }
+
+ return 0;
+}
+
+void wipe_misc() {
+ ALOGI("removing old commands from misc");
+ int i;
+ for (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) {
+ int fd = open(v->blk_device, O_WRONLY);
+ uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery
+ memset(zeroes, 0, sizeof(zeroes));
+
+ 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) {
+ ALOGE("zero write failed: %s\n", strerror(errno));
+ return;
+ } else {
+ written += w;
+ }
+ }
+
+ close(fd);
+ }
+ }
+}
+
+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;
+
+ 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);
+
+ // 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));
+ return 1;
+ }
+
+ int encryptable;
+ int encrypted;
+ if (read_fstab() == NULL) {
+ return 1;
+ }
+ const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
+ if (blk_dev == NULL) {
+ ALOGE("failed to find block device for %s", path);
+ return 1;
+ }
+
+ // If the filesystem it's on isn't encrypted, we only produce the
+ // block map, we don't rewrite the file contents (it would be
+ // pointless to do so).
+ ALOGI("encryptable: %s\n", encryptable ? "yes" : "no");
+ ALOGI(" encrypted: %s\n", encrypted ? "yes" : "no");
+
+ // Recovery supports installing packages from 3 paths: /cache,
+ // /data, and /sdcard. (On a particular device, other locations
+ // may work, but those are three we actually expect.)
+ //
+ // 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 {
+ ALOGI("writing block map %s", map_file);
+ if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) {
+ return 1;
+ }
+ }
+
+ wipe_misc();
+ rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE);
+ if (do_reboot) reboot_to_recovery();
+ return 0;
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index d86fb9e5f..5c2896963 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir)
updater_src_files := \
install.c \
+ blockimg.c \
updater.c
#
@@ -20,6 +21,7 @@ LOCAL_SRC_FILES := $(updater_src_files)
ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
LOCAL_CFLAGS += -DUSE_EXT4
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_STATIC_LIBRARIES += \
libext4_utils \
diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/updater/MODULE_LICENSE_GPL
diff --git a/updater/NOTICE b/updater/NOTICE
new file mode 100644
index 000000000..e77696ae8
--- /dev/null
+++ b/updater/NOTICE
@@ -0,0 +1,339 @@
+ 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
new file mode 100644
index 000000000..c3319c973
--- /dev/null
+++ b/updater/blockimg.c
@@ -0,0 +1,645 @@
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "applypatch/applypatch.h"
+#include "edify/expr.h"
+#include "mincrypt/sha.h"
+#include "minzip/DirUtil.h"
+#include "updater.h"
+
+#define BLOCKSIZE 4096
+
+// Set this to 0 to interpret 'erase' transfers to mean do a
+// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
+// erase to mean fill the region with zeroes.
+#define DEBUG_ERASE 0
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+char* PrintSha1(const uint8_t* digest);
+
+typedef struct {
+ int count;
+ int size;
+ int pos[0];
+} RangeSet;
+
+static RangeSet* parse_range(char* text) {
+ char* save;
+ int num;
+ num = strtol(strtok_r(text, ",", &save), NULL, 0);
+
+ RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
+ if (out == NULL) {
+ fprintf(stderr, "failed to allocate range of %lu bytes\n",
+ sizeof(RangeSet) + num * sizeof(int));
+ exit(1);
+ }
+ out->count = num / 2;
+ out->size = 0;
+ int i;
+ for (i = 0; i < num; ++i) {
+ out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
+ if (i%2) {
+ out->size += out->pos[i];
+ } else {
+ out->size -= out->pos[i];
+ }
+ }
+
+ return out;
+}
+
+static void readblock(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) {
+ fprintf(stderr, "read failed: %s\n", strerror(errno));
+ return;
+ } else {
+ so_far += r;
+ }
+ }
+}
+
+static void writeblock(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) {
+ fprintf(stderr, "write failed: %s\n", strerror(errno));
+ return;
+ } else {
+ written += w;
+ }
+ }
+}
+
+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 void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
+ // if the buffer's big enough, reuse it.
+ if (size <= *buffer_alloc) return;
+
+ free(*buffer);
+
+ *buffer = (uint8_t*) malloc(size);
+ if (*buffer == NULL) {
+ fprintf(stderr, "failed to allocate %zu bytes\n", size);
+ exit(1);
+ }
+ *buffer_alloc = size;
+}
+
+typedef struct {
+ int fd;
+ RangeSet* tgt;
+ int p_block;
+ size_t p_remain;
+} RangeSinkState;
+
+static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
+ RangeSinkState* rss = (RangeSinkState*) token;
+
+ if (rss->p_remain <= 0) {
+ fprintf(stderr, "range sink write overrun");
+ exit(1);
+ }
+
+ 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);
+ data += write_now;
+ size -= write_now;
+
+ rss->p_remain -= write_now;
+ written += write_now;
+
+ if (rss->p_remain == 0) {
+ // 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);
+ } else {
+ // we can't write any more; return how many bytes have
+ // been written so far.
+ return written;
+ }
+ }
+ }
+
+ return written;
+}
+
+// All of the data for all the 'new' transfers is contained in one
+// file in the update package, concatenated together in the order in
+// which transfers.list will need it. We want to stream it out of the
+// archive (it's compressed) without writing it to a temp file, but we
+// can't write each section until it's that transfer's turn to go.
+//
+// To achieve this, we expand the new data from the archive in a
+// background thread, and block that threads 'receive uncompressed
+// data' function until the main thread has reached a point where we
+// want some new data to be written. We signal the background thread
+// with the destination for the data and block the main thread,
+// waiting for the background thread to complete writing that section.
+// Then it signals the main thread to wake up and goes back to
+// blocking waiting for a transfer.
+//
+// NewThreadInfo is the struct used to pass information back and forth
+// between the two threads. When the main thread wants some data
+// written, it sets rss to the destination location and signals the
+// condition. When the background thread is done writing, it clears
+// rss and signals the condition again.
+
+typedef struct {
+ ZipArchive* za;
+ const ZipEntry* entry;
+
+ RangeSinkState* rss;
+
+ pthread_mutex_t mu;
+ pthread_cond_t cv;
+} NewThreadInfo;
+
+static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
+ NewThreadInfo* nti = (NewThreadInfo*) cookie;
+
+ while (size > 0) {
+ // Wait for nti->rss to be non-NULL, indicating some of this
+ // data is wanted.
+ pthread_mutex_lock(&nti->mu);
+ while (nti->rss == NULL) {
+ pthread_cond_wait(&nti->cv, &nti->mu);
+ }
+ pthread_mutex_unlock(&nti->mu);
+
+ // At this point nti->rss is set, and we own it. The main
+ // thread is waiting for it to disappear from nti.
+ ssize_t written = RangeSinkWrite(data, size, nti->rss);
+ data += written;
+ size -= written;
+
+ if (nti->rss->p_block == nti->rss->tgt->count) {
+ // we have written all the bytes desired by this rss.
+
+ pthread_mutex_lock(&nti->mu);
+ nti->rss = NULL;
+ pthread_cond_broadcast(&nti->cv);
+ pthread_mutex_unlock(&nti->mu);
+ }
+ }
+
+ return true;
+}
+
+static void* unzip_new_data(void* cookie) {
+ NewThreadInfo* nti = (NewThreadInfo*) cookie;
+ mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
+ return NULL;
+}
+
+// args:
+// - block device (or file) to modify in-place
+// - transfer list (blob)
+// - 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;
+ char* transfer_list = NULL;
+ Value* new_data_fn;
+ Value* patch_data_fn;
+ bool success = false;
+
+ if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
+ &new_data_fn, &patch_data_fn) < 0) {
+ return NULL;
+ }
+
+ if (blockdev_filename->type != VAL_STRING) {
+ ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
+ goto done;
+ }
+ if (transfer_list_value->type != VAL_BLOB) {
+ ErrorAbort(state, "transfer_list argument to %s must be blob", name);
+ goto done;
+ }
+ if (new_data_fn->type != VAL_STRING) {
+ ErrorAbort(state, "new_data_fn argument to %s must be string", name);
+ goto done;
+ }
+ if (patch_data_fn->type != VAL_STRING) {
+ ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
+ goto done;
+ }
+
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+ FILE* cmd_pipe = ui->cmd_pipe;
+
+ ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+
+ const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data);
+ if (patch_entry == NULL) {
+ ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
+ goto done;
+ }
+
+ uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr +
+ mzGetZipEntryOffset(patch_entry);
+
+ const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data);
+ if (new_entry == NULL) {
+ ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data);
+ goto done;
+ }
+
+ // The transfer list is a text file containing commands to
+ // transfer data from one place to another on the target
+ // partition. We parse it and execute the commands in order:
+ //
+ // zero [rangeset]
+ // - fill the indicated blocks with zeros
+ //
+ // new [rangeset]
+ // - fill the blocks with data read from the new_data file
+ //
+ // bsdiff patchstart patchlen [src rangeset] [tgt rangeset]
+ // imgdiff patchstart patchlen [src rangeset] [tgt rangeset]
+ // - read the source blocks, apply a patch, write result to
+ // target blocks. bsdiff or imgdiff specifies the type of
+ // patch.
+ //
+ // move [src rangeset] [tgt rangeset]
+ // - copy data from source blocks to target blocks (no patch
+ // needed; rangesets are the same size)
+ //
+ // erase [rangeset]
+ // - mark the given blocks as empty
+ //
+ // The creator of the transfer list will guarantee that no block
+ // is read (ie, used as the source for a patch or move) after it
+ // has been written.
+ //
+ // Within one command the source and target ranges may overlap so
+ // in general we need to read the entire source into memory before
+ // writing anything to the target blocks.
+ //
+ // All the patch data is concatenated into one patch_data file in
+ // the update package. It must be stored uncompressed because we
+ // memory-map it in directly from the archive. (Since patches are
+ // already compressed, we lose very little by not compressing
+ // their concatenation.)
+
+ pthread_t new_data_thread;
+ NewThreadInfo nti;
+ nti.za = za;
+ nti.entry = new_entry;
+ nti.rss = NULL;
+ pthread_mutex_init(&nti.mu, NULL);
+ pthread_cond_init(&nti.cv, NULL);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_create(&new_data_thread, &attr, unzip_new_data, &nti);
+
+ int i, j;
+
+ char* linesave;
+ char* wordsave;
+
+ int fd = open(blockdev_filename->data, O_RDWR);
+ if (fd < 0) {
+ ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
+ goto done;
+ }
+
+ char* line;
+ char* word;
+
+ // The data in transfer_list_value is not necessarily
+ // null-terminated, so we need to copy it to a new buffer and add
+ // the null that strtok_r will need.
+ transfer_list = malloc(transfer_list_value->size+1);
+ if (transfer_list == NULL) {
+ fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
+ transfer_list_value->size+1);
+ exit(1);
+ }
+ memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
+ transfer_list[transfer_list_value->size] = '\0';
+
+ line = strtok_r(transfer_list, "\n", &linesave);
+
+ // first line in transfer list is the version number; currently
+ // there's only version 1.
+ if (strcmp(line, "1") != 0) {
+ ErrorAbort(state, "unexpected transfer list version [%s]\n", line);
+ goto done;
+ }
+
+ // second line in transfer list is the total number of blocks we
+ // expect to write.
+ line = strtok_r(NULL, "\n", &linesave);
+ int total_blocks = strtol(line, NULL, 0);
+ // shouldn't happen, but avoid divide by zero.
+ if (total_blocks == 0) ++total_blocks;
+ int blocks_so_far = 0;
+
+ uint8_t* buffer = NULL;
+ size_t buffer_alloc = 0;
+
+ // third and subsequent lines are all individual transfer commands.
+ for (line = strtok_r(NULL, "\n", &linesave); line;
+ line = strtok_r(NULL, "\n", &linesave)) {
+ char* style;
+ style = strtok_r(line, " ", &wordsave);
+
+ if (strcmp("move", style) == 0) {
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* src = parse_range(word);
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* tgt = parse_range(word);
+
+ printf(" moving %d blocks\n", src->size);
+
+ allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc);
+ size_t p = 0;
+ for (i = 0; i < src->count; ++i) {
+ check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
+ size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
+ readblock(fd, buffer+p, sz);
+ p += sz;
+ }
+
+ p = 0;
+ for (i = 0; i < tgt->count; ++i) {
+ check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
+ size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE;
+ writeblock(fd, buffer+p, sz);
+ p += sz;
+ }
+
+ blocks_so_far += tgt->size;
+ fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
+ fflush(cmd_pipe);
+
+ free(src);
+ free(tgt);
+
+ } else if (strcmp("zero", style) == 0 ||
+ (DEBUG_ERASE && strcmp("erase", style) == 0)) {
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* tgt = parse_range(word);
+
+ printf(" zeroing %d blocks\n", tgt->size);
+
+ allocate(BLOCKSIZE, &buffer, &buffer_alloc);
+ memset(buffer, 0, BLOCKSIZE);
+ for (i = 0; i < tgt->count; ++i) {
+ check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
+ for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) {
+ writeblock(fd, buffer, BLOCKSIZE);
+ }
+ }
+
+ if (style[0] == 'z') { // "zero" but not "erase"
+ blocks_so_far += tgt->size;
+ fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
+ fflush(cmd_pipe);
+ }
+
+ free(tgt);
+ } else if (strcmp("new", style) == 0) {
+
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* tgt = parse_range(word);
+
+ printf(" writing %d blocks of new data\n", tgt->size);
+
+ RangeSinkState rss;
+ rss.fd = fd;
+ rss.tgt = tgt;
+ rss.p_block = 0;
+ rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
+ check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);
+
+ pthread_mutex_lock(&nti.mu);
+ nti.rss = &rss;
+ pthread_cond_broadcast(&nti.cv);
+ while (nti.rss) {
+ pthread_cond_wait(&nti.cv, &nti.mu);
+ }
+ pthread_mutex_unlock(&nti.mu);
+
+ blocks_so_far += tgt->size;
+ fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
+ fflush(cmd_pipe);
+
+ free(tgt);
+
+ } else if (strcmp("bsdiff", style) == 0 ||
+ strcmp("imgdiff", style) == 0) {
+ word = strtok_r(NULL, " ", &wordsave);
+ size_t patch_offset = strtoul(word, NULL, 0);
+ word = strtok_r(NULL, " ", &wordsave);
+ size_t patch_len = strtoul(word, NULL, 0);
+
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* src = parse_range(word);
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* tgt = parse_range(word);
+
+ printf(" patching %d blocks to %d\n", src->size, tgt->size);
+
+ // Read the source into memory.
+ allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc);
+ size_t p = 0;
+ for (i = 0; i < src->count; ++i) {
+ check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
+ size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
+ readblock(fd, buffer+p, sz);
+ p += sz;
+ }
+
+ Value patch_value;
+ patch_value.type = VAL_BLOB;
+ patch_value.size = patch_len;
+ patch_value.data = (char*)(patch_start + patch_offset);
+
+ RangeSinkState rss;
+ rss.fd = fd;
+ rss.tgt = tgt;
+ rss.p_block = 0;
+ rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
+ check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);
+
+ if (style[0] == 'i') { // imgdiff
+ ApplyImagePatch(buffer, src->size * BLOCKSIZE,
+ &patch_value,
+ &RangeSinkWrite, &rss, NULL, NULL);
+ } else {
+ ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE,
+ &patch_value, 0,
+ &RangeSinkWrite, &rss, NULL);
+ }
+
+ // We expect the output of the patcher to fill the tgt ranges exactly.
+ if (rss.p_block != tgt->count || rss.p_remain != 0) {
+ fprintf(stderr, "range sink underrun?\n");
+ }
+
+ blocks_so_far += tgt->size;
+ fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
+ fflush(cmd_pipe);
+
+ free(src);
+ free(tgt);
+ } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) {
+ struct stat st;
+ if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) {
+ word = strtok_r(NULL, " ", &wordsave);
+ RangeSet* tgt = parse_range(word);
+
+ printf(" erasing %d blocks\n", tgt->size);
+
+ for (i = 0; i < tgt->count; ++i) {
+ uint64_t range[2];
+ // offset in bytes
+ range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE;
+ // len in bytes
+ range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE;
+
+ if (ioctl(fd, BLKDISCARD, &range) < 0) {
+ printf(" blkdiscard failed: %s\n", strerror(errno));
+ }
+ }
+
+ free(tgt);
+ } else {
+ printf(" ignoring erase (not block device)\n");
+ }
+ } else {
+ fprintf(stderr, "unknown transfer style \"%s\"\n", style);
+ exit(1);
+ }
+ }
+
+ pthread_join(new_data_thread, NULL);
+ success = true;
+
+ free(buffer);
+ printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks);
+ printf("max alloc needed was %zu\n", buffer_alloc);
+
+ done:
+ free(transfer_list);
+ FreeValue(blockdev_filename);
+ FreeValue(transfer_list_value);
+ FreeValue(new_data_fn);
+ FreeValue(patch_data_fn);
+ return StringValue(success ? strdup("t") : strdup(""));
+}
+
+Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
+ Value* blockdev_filename;
+ Value* ranges;
+ const uint8_t* digest = NULL;
+ if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
+ return NULL;
+ }
+
+ if (blockdev_filename->type != VAL_STRING) {
+ ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
+ goto done;
+ }
+ if (ranges->type != VAL_STRING) {
+ ErrorAbort(state, "ranges argument to %s must be string", name);
+ goto done;
+ }
+
+ 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;
+ }
+
+ RangeSet* rs = parse_range(ranges->data);
+ uint8_t buffer[BLOCKSIZE];
+
+ SHA_CTX ctx;
+ SHA_init(&ctx);
+
+ int i, j;
+ for (i = 0; i < rs->count; ++i) {
+ check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET);
+ for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
+ readblock(fd, buffer, BLOCKSIZE);
+ SHA_update(&ctx, buffer, BLOCKSIZE);
+ }
+ }
+ digest = SHA_final(&ctx);
+ close(fd);
+
+ done:
+ FreeValue(blockdev_filename);
+ FreeValue(ranges);
+ if (digest == NULL) {
+ return StringValue(strdup(""));
+ } else {
+ return StringValue(PrintSha1(digest));
+ }
+}
+
+void RegisterBlockImageFunctions() {
+ RegisterFunction("block_image_update", BlockImageUpdateFn);
+ RegisterFunction("range_sha1", RangeSha1Fn);
+}
diff --git a/updater/blockimg.h b/updater/blockimg.h
new file mode 100644
index 000000000..2f4ad3c04
--- /dev/null
+++ b/updater/blockimg.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 _UPDATER_BLOCKIMG_H_
+#define _UPDATER_BLOCKIMG_H_
+
+void RegisterBlockImageFunctions();
+
+#endif
diff --git a/updater/install.c b/updater/install.c
index ff4dd5829..003064799 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -34,6 +34,9 @@
#include <linux/xattr.h>
#include <inttypes.h>
+#include "bootloader.h"
+#include "applypatch/applypatch.h"
+#include "cutils/android_reboot.h"
#include "cutils/misc.h"
#include "cutils/properties.h"
#include "edify/expr.h"
@@ -44,27 +47,73 @@
#include "updater.h"
#include "applypatch/applypatch.h"
#include "flashutils/flashutils.h"
+#include "install.h"
#ifdef USE_EXT4
#include "make_ext4fs.h"
+#include "wipe.h"
#endif
+void uiPrint(State* state, char* buffer) {
+ char* line = strtok(buffer, "\n");
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+ while (line) {
+ fprintf(ui->cmd_pipe, "ui_print %s\n", line);
+ line = strtok(NULL, "\n");
+ }
+ fprintf(ui->cmd_pipe, "ui_print\n");
+}
+
+__attribute__((__format__(printf, 2, 3))) __nonnull((2))
+void uiPrintf(State* state, const char* format, ...) {
+ char error_msg[1024];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(error_msg, sizeof(error_msg), format, ap);
+ va_end(ap);
+ uiPrint(state, error_msg);
+}
+
+// Take a sha-1 digest and return it as a newly-allocated hex string.
+char* PrintSha1(const uint8_t* digest) {
+ char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
+ int i;
+ const char* alphabet = "0123456789abcdef";
+ for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+ buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
+ buffer[i*2+1] = alphabet[digest[i] & 0xf];
+ }
+ buffer[i*2] = '\0';
+ return buffer;
+}
+
// mount(fs_type, partition_type, location, mount_point)
//
// fs_type="yaffs2" partition_type="MTD" location=partition
// fs_type="ext4" partition_type="EMMC" location=device
Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
- if (argc != 4) {
- return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
+ if (argc != 4 && argc != 5) {
+ return ErrorAbort(state, "%s() expects 4-5 args, got %d", name, argc);
}
char* fs_type;
char* partition_type;
char* location;
char* mount_point;
- if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
+ char* mount_options;
+ bool has_mount_options;
+ if (argc == 5) {
+ has_mount_options = true;
+ if (ReadArgs(state, argv, 5, &fs_type, &partition_type,
+ &location, &mount_point, &mount_options) < 0) {
+ return NULL;
+ }
+ } else {
+ has_mount_options = false;
+ if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
&location, &mount_point) < 0) {
- return NULL;
+ return NULL;
+ }
}
if (strlen(fs_type) == 0) {
@@ -104,13 +153,13 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
const MtdPartition* mtd;
mtd = mtd_find_partition_by_name(location);
if (mtd == NULL) {
- printf("%s: no mtd partition named \"%s\"",
+ uiPrintf(state, "%s: no mtd partition named \"%s\"",
name, location);
result = strdup("");
goto done;
}
if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) {
- printf("mtd mount of %s failed: %s\n",
+ uiPrintf(state, "mtd mount of %s failed: %s\n",
location, strerror(errno));
result = strdup("");
goto done;
@@ -118,8 +167,9 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
result = mount_point;
} else {
if (mount(location, mount_point, fs_type,
- MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
- printf("%s: failed to mount %s at %s: %s\n",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME,
+ has_mount_options ? mount_options : "") < 0) {
+ uiPrintf(state, "%s: failed to mount %s at %s: %s\n",
name, location, mount_point, strerror(errno));
result = strdup("");
} else {
@@ -132,6 +182,7 @@ done:
free(partition_type);
free(location);
if (result != mount_point) free(mount_point);
+ if (has_mount_options) free(mount_options);
return StringValue(result);
}
@@ -182,10 +233,14 @@ Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
scan_mounted_volumes();
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
if (vol == NULL) {
- printf("unmount of %s failed; no such volume\n", mount_point);
+ uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point);
result = strdup("");
} else {
- unmount_mounted_volume(vol);
+ int ret = unmount_mounted_volume(vol);
+ if (ret != 0) {
+ uiPrintf(state, "unmount of %s failed (%d): %s\n",
+ mount_point, ret, strerror(errno));
+ }
result = mount_point;
}
@@ -194,14 +249,29 @@ done:
return StringValue(result);
}
+static int exec_cmd(const char* path, char* const argv[]) {
+ int status;
+ pid_t child;
+ if ((child = vfork()) == 0) {
+ execv(path, argv);
+ _exit(-1);
+ }
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ printf("%s failed with status %d\n", path, WEXITSTATUS(status));
+ }
+ return WEXITSTATUS(status);
+}
+
// format(fs_type, partition_type, location, fs_size, mount_point)
//
// fs_type="yaffs2" partition_type="MTD" location=partition fs_size=<bytes> mount_point=<location>
// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
-// if fs_size == 0, then make_ext4fs uses the entire partition.
+// fs_type="f2fs" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
+// if fs_size == 0, then make fs uses the entire partition.
// if fs_size > 0, that is the size to use
-// if fs_size < 0, then reserve that many bytes at the end of the partition
+// if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs")
Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 5) {
@@ -273,6 +343,24 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
result = location;
+ } else if (strcmp(fs_type, "f2fs") == 0) {
+ char *num_sectors;
+ if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) {
+ printf("format_volume: failed to create %s command for %s\n", fs_type, location);
+ result = strdup("");
+ goto done;
+ }
+ const char *f2fs_path = "/sbin/mkfs.f2fs";
+ const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL};
+ int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
+ free(num_sectors);
+ if (status != 0) {
+ printf("%s: mkfs.f2fs failed (%d) on %s",
+ name, status, location);
+ result = strdup("");
+ goto done;
+ }
+ result = location;
#endif
} else {
printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"",
@@ -286,6 +374,44 @@ done:
return StringValue(result);
}
+Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) {
+ char* result = NULL;
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ char* src_name;
+ char* dst_name;
+
+ if (ReadArgs(state, argv, 2, &src_name, &dst_name) < 0) {
+ return NULL;
+ }
+ if (strlen(src_name) == 0) {
+ ErrorAbort(state, "src_name argument to %s() can't be empty", name);
+ goto done;
+ }
+ if (strlen(dst_name) == 0) {
+ ErrorAbort(state, "dst_name argument to %s() can't be empty", name);
+ goto done;
+ }
+ if (make_parents(dst_name) != 0) {
+ ErrorAbort(state, "Creating parent of %s failed, error %s",
+ dst_name, strerror(errno));
+ } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) {
+ // File was already moved
+ result = dst_name;
+ } else if (rename(src_name, dst_name) != 0) {
+ ErrorAbort(state, "Rename of %s to %s failed, error %s",
+ src_name, dst_name, strerror(errno));
+ } else {
+ result = dst_name;
+ }
+
+done:
+ free(src_name);
+ if (result != dst_name) free(dst_name);
+ return StringValue(result);
+}
Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
char** paths = malloc(argc * sizeof(char*));
@@ -386,19 +512,23 @@ Value* PackageExtractDirFn(const char* name, State* state,
// function (the char* returned is actually a FileContents*).
Value* PackageExtractFileFn(const char* name, State* state,
int argc, Expr* argv[]) {
- if (argc != 1 && argc != 2) {
+ if (argc < 1 || argc > 2) {
return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
name, argc);
}
bool success = false;
+
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+
if (argc == 2) {
// The two-argument version extracts to a file.
+ ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+
char* zip_path;
char* dest_path;
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
- ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
printf("%s: no %s in package\n", name, zip_path);
@@ -467,7 +597,7 @@ static int make_parents(char* name) {
*p = '\0';
if (make_parents(name) < 0) return -1;
int result = mkdir(name, 0700);
- if (result == 0) printf("symlink(): created [%s]\n", name);
+ if (result == 0) printf("created [%s]\n", name);
*p = '/';
if (result == 0 || errno == EEXIST) {
// successfully created or already existed; we're done
@@ -525,88 +655,6 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(""));
}
-
-Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = NULL;
- bool recursive = (strcmp(name, "set_perm_recursive") == 0);
-
- int min_args = 4 + (recursive ? 1 : 0);
- if (argc < min_args) {
- return ErrorAbort(state, "%s() expects %d+ args, got %d",
- name, min_args, argc);
- }
-
- char** args = ReadVarArgs(state, argc, argv);
- if (args == NULL) return NULL;
-
- char* end;
- int i;
- int bad = 0;
-
- int uid = strtoul(args[0], &end, 0);
- if (*end != '\0' || args[0][0] == 0) {
- ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
- goto done;
- }
-
- int gid = strtoul(args[1], &end, 0);
- if (*end != '\0' || args[1][0] == 0) {
- ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
- goto done;
- }
-
- if (recursive) {
- int dir_mode = strtoul(args[2], &end, 0);
- if (*end != '\0' || args[2][0] == 0) {
- ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
- goto done;
- }
-
- int file_mode = strtoul(args[3], &end, 0);
- if (*end != '\0' || args[3][0] == 0) {
- ErrorAbort(state, "%s: \"%s\" not a valid filemode",
- name, args[3]);
- goto done;
- }
-
- for (i = 4; i < argc; ++i) {
- dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
- }
- } else {
- int mode = strtoul(args[2], &end, 0);
- if (*end != '\0' || args[2][0] == 0) {
- ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
- goto done;
- }
-
- for (i = 3; i < argc; ++i) {
- if (chown(args[i], uid, gid) < 0) {
- printf("%s: chown of %s to %d %d failed: %s\n",
- name, args[i], uid, gid, strerror(errno));
- ++bad;
- }
- if (chmod(args[i], mode) < 0) {
- printf("%s: chmod of %s to %o failed: %s\n",
- name, args[i], mode, strerror(errno));
- ++bad;
- }
- }
- }
- result = strdup("");
-
-done:
- for (i = 0; i < argc; ++i) {
- free(args[i]);
- }
- free(args);
-
- if (bad) {
- free(result);
- return ErrorAbort(state, "%s: some changes failed", name);
- }
- return StringValue(result);
-}
-
struct perm_parsed_args {
bool has_uid;
uid_t uid;
@@ -624,7 +672,7 @@ struct perm_parsed_args {
uint64_t capabilities;
};
-static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
+static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) {
int i;
struct perm_parsed_args parsed;
int bad = 0;
@@ -639,7 +687,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.uid = uid;
parsed.has_uid = true;
} else {
- printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -650,7 +698,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.gid = gid;
parsed.has_gid = true;
} else {
- printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -661,7 +709,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.mode = mode;
parsed.has_mode = true;
} else {
- printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -672,7 +720,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.dmode = mode;
parsed.has_dmode = true;
} else {
- printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -683,7 +731,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.fmode = mode;
parsed.has_fmode = true;
} else {
- printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -694,7 +742,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.capabilities = capabilities;
parsed.has_capabilities = true;
} else {
- printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -704,7 +752,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.selabel = args[i+1];
parsed.has_selabel = true;
} else {
- printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -721,71 +769,71 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
}
static int ApplyParsedPerms(
+ State * state,
const char* filename,
const struct stat *statptr,
struct perm_parsed_args parsed)
{
int bad = 0;
+ if (parsed.has_selabel) {
+ if (lsetfilecon(filename, parsed.selabel) != 0) {
+ uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
+ filename, parsed.selabel, strerror(errno));
+ bad++;
+ }
+ }
+
/* ignore symlinks */
if (S_ISLNK(statptr->st_mode)) {
- return 0;
+ return bad;
}
if (parsed.has_uid) {
if (chown(filename, parsed.uid, -1) < 0) {
- printf("ApplyParsedPerms: chown of %s to %d failed: %s\n",
- filename, parsed.uid, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n",
+ filename, parsed.uid, strerror(errno));
bad++;
}
}
if (parsed.has_gid) {
if (chown(filename, -1, parsed.gid) < 0) {
- printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
- filename, parsed.gid, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
+ filename, parsed.gid, strerror(errno));
bad++;
}
}
if (parsed.has_mode) {
if (chmod(filename, parsed.mode) < 0) {
- printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
- filename, parsed.mode, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.mode, strerror(errno));
bad++;
}
}
if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) {
if (chmod(filename, parsed.dmode) < 0) {
- printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
- filename, parsed.dmode, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.dmode, strerror(errno));
bad++;
}
}
if (parsed.has_fmode && S_ISREG(statptr->st_mode)) {
if (chmod(filename, parsed.fmode) < 0) {
- printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
filename, parsed.fmode, strerror(errno));
bad++;
}
}
- if (parsed.has_selabel) {
- // TODO: Don't silently ignore ENOTSUP
- if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) {
- printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
- filename, parsed.selabel, strerror(errno));
- bad++;
- }
- }
-
if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) {
if (parsed.capabilities == 0) {
if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) {
// Report failure unless it's ENODATA (attribute not set)
- printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
+ uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
filename, parsed.capabilities, strerror(errno));
bad++;
}
@@ -798,8 +846,8 @@ static int ApplyParsedPerms(
cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32);
cap_data.data[1].inheritable = 0;
if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
- printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
- filename, parsed.capabilities, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
+ filename, parsed.capabilities, strerror(errno));
bad++;
}
}
@@ -811,10 +859,11 @@ static int ApplyParsedPerms(
// nftw doesn't allow us to pass along context, so we need to use
// global variables. *sigh*
static struct perm_parsed_args recursive_parsed_args;
+static State* recursive_state;
static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr,
int fileflags, struct FTW *pfwt) {
- return ApplyParsedPerms(filename, statptr, recursive_parsed_args);
+ return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args);
}
static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -839,14 +888,16 @@ static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv
goto done;
}
- struct perm_parsed_args parsed = ParsePermArgs(argc, args);
+ struct perm_parsed_args parsed = ParsePermArgs(state, argc, args);
if (recursive) {
recursive_parsed_args = parsed;
+ recursive_state = state;
bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
+ recursive_state = NULL;
} else {
- bad += ApplyParsedPerms(args[0], &sb, parsed);
+ bad += ApplyParsedPerms(state, args[0], &sb, parsed);
}
done:
@@ -885,8 +936,8 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
// file_getprop(file, key)
//
// interprets 'file' as a getprop-style file (key=value pairs, one
-// per line, # comment lines and blank lines okay), and returns the value
-// for 'key' (or "" if it isn't defined).
+// per line. # comment lines,blank lines, lines without '=' ignored),
+// and returns the value for 'key' (or "" if it isn't defined).
Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* buffer = NULL;
@@ -913,7 +964,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
buffer = malloc(st.st_size+1);
if (buffer == NULL) {
- ErrorAbort(state, "%s: failed to alloc %lld bytes", name, st.st_size+1);
+ ErrorAbort(state, "%s: failed to alloc %lld bytes", name, (long long)st.st_size+1);
goto done;
}
@@ -926,7 +977,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
if (fread(buffer, 1, st.st_size, f) != st.st_size) {
ErrorAbort(state, "%s: failed to read %lld bytes from %s",
- name, st.st_size+1, filename);
+ name, (long long)st.st_size+1, filename);
fclose(f);
goto done;
}
@@ -944,9 +995,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* equal = strchr(line, '=');
if (equal == NULL) {
- ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
- name, line, filename);
- goto done;
+ continue;
}
// trim whitespace between key and '='
@@ -1048,8 +1097,8 @@ Value* ApplyPatchSpaceFn(const char* name, State* state,
return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
}
+// apply_patch(file, size, init_sha1, tgt_sha1, patch)
-// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 6 || (argc % 2) == 1) {
return ErrorAbort(state, "%s(): expected at least 6 args and an "
@@ -1168,15 +1217,7 @@ Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
}
free(args);
buffer[size] = '\0';
-
- char* line = strtok(buffer, "\n");
- while (line) {
- fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
- "ui_print %s\n", line);
- line = strtok(NULL, "\n");
- }
- fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
-
+ uiPrint(state, buffer);
return StringValue(buffer);
}
@@ -1234,19 +1275,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(buffer));
}
-// Take a sha-1 digest and return it as a newly-allocated hex string.
-static char* PrintSha1(uint8_t* digest) {
- char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
- int i;
- const char* alphabet = "0123456789abcdef";
- for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
- buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
- buffer[i*2+1] = alphabet[digest[i] & 0xf];
- }
- buffer[i*2] = '\0';
- return buffer;
-}
-
// sha1_check(data)
// to return the sha1 of the data (given in the format returned by
// read_file).
@@ -1266,7 +1294,6 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
}
if (args[0]->size < 0) {
- printf("%s(): no file contents received", name);
return StringValue(strdup(""));
}
uint8_t digest[SHA_DIGEST_SIZE];
@@ -1318,13 +1345,12 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
v->type = VAL_BLOB;
FileContents fc;
- if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
- ErrorAbort(state, "%s() loading \"%s\" failed: %s",
- name, filename, strerror(errno));
+ if (LoadFileContents(filename, &fc) != 0) {
free(filename);
- free(v);
+ v->size = -1;
+ v->data = NULL;
free(fc.data);
- return NULL;
+ return v;
}
v->size = fc.size;
@@ -1334,6 +1360,135 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
return v;
}
+// Immediately reboot the device. Recovery is not finished normally,
+// so if you reboot into recovery it will re-start applying the
+// current package (because nothing has cleared the copy of the
+// arguments stored in the BCB).
+//
+// The argument is the partition name passed to the android reboot
+// property. It can be "recovery" to boot from the recovery
+// partition, or "" (empty string) to boot from the regular boot
+// partition.
+Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ char* filename;
+ char* property;
+ if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL;
+
+ char buffer[80];
+
+ // zero out the 'command' field of the bootloader message.
+ memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command));
+ FILE* f = fopen(filename, "r+b");
+ fseek(f, offsetof(struct bootloader_message, command), SEEK_SET);
+ fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
+ fclose(f);
+ free(filename);
+
+ strcpy(buffer, "reboot,");
+ if (property != NULL) {
+ strncat(buffer, property, sizeof(buffer)-10);
+ }
+
+ property_set(ANDROID_RB_PROPERTY, buffer);
+
+ sleep(5);
+ free(property);
+ ErrorAbort(state, "%s() failed to reboot", name);
+ return NULL;
+}
+
+// Store a string value somewhere that future invocations of recovery
+// can access it. This value is called the "stage" and can be used to
+// drive packages that need to do reboots in the middle of
+// installation and keep track of where they are in the multi-stage
+// install.
+//
+// The first argument is the block device for the misc partition
+// ("/misc" in the fstab), which is where this value is stored. The
+// second argument is the string to store; it should not exceed 31
+// bytes.
+Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ char* filename;
+ char* stagestr;
+ if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL;
+
+ // Store this value in the misc partition, immediately after the
+ // bootloader message that the main recovery uses to save its
+ // arguments in case of the device restarting midway through
+ // package installation.
+ FILE* f = fopen(filename, "r+b");
+ fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
+ int to_write = strlen(stagestr)+1;
+ int max_size = sizeof(((struct bootloader_message*)0)->stage);
+ if (to_write > max_size) {
+ to_write = max_size;
+ stagestr[max_size-1] = 0;
+ }
+ fwrite(stagestr, to_write, 1, f);
+ fclose(f);
+
+ free(stagestr);
+ return StringValue(filename);
+}
+
+// Return the value most recently saved with SetStageFn. The argument
+// is the block device for the misc partition.
+Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 1) {
+ return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
+ }
+
+ char* filename;
+ if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
+
+ char buffer[sizeof(((struct bootloader_message*)0)->stage)];
+ FILE* f = fopen(filename, "rb");
+ fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
+ fread(buffer, sizeof(buffer), 1, f);
+ fclose(f);
+ buffer[sizeof(buffer)-1] = '\0';
+
+ return StringValue(strdup(buffer));
+}
+
+Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ char* filename;
+ char* len_str;
+ if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL;
+
+ size_t len = strtoull(len_str, NULL, 0);
+ int fd = open(filename, O_WRONLY, 0644);
+ int success = wipe_block_device(fd, len);
+
+ free(filename);
+ free(len_str);
+
+ close(fd);
+
+ return StringValue(strdup(success ? "t" : ""));
+}
+
+Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 0) {
+ return ErrorAbort(state, "%s() expects no args, got %d", name, argc);
+ }
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+ fprintf(ui->cmd_pipe, "enable_reboot\n");
+ return StringValue(strdup("t"));
+}
+
void RegisterInstallFunctions() {
RegisterFunction("mount", MountFn);
RegisterFunction("is_mounted", IsMountedFn);
@@ -1347,11 +1502,6 @@ void RegisterInstallFunctions() {
RegisterFunction("package_extract_file", PackageExtractFileFn);
RegisterFunction("symlink", SymlinkFn);
- // Maybe, at some future point, we can delete these functions? They have been
- // replaced by perm_set and perm_set_recursive.
- RegisterFunction("set_perm", SetPermFn);
- RegisterFunction("set_perm_recursive", SetPermFn);
-
// Usage:
// set_metadata("filename", "key1", "value1", "key2", "value2", ...)
// Example:
@@ -1372,12 +1522,21 @@ void RegisterInstallFunctions() {
RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
+ RegisterFunction("wipe_block_device", WipeBlockDeviceFn);
+
RegisterFunction("read_file", ReadFileFn);
RegisterFunction("sha1_check", Sha1CheckFn);
+ RegisterFunction("rename", RenameFn);
RegisterFunction("wipe_cache", WipeCacheFn);
RegisterFunction("ui_print", UIPrintFn);
RegisterFunction("run_program", RunProgramFn);
+
+ RegisterFunction("reboot_now", RebootNowFn);
+ RegisterFunction("get_stage", GetStageFn);
+ RegisterFunction("set_stage", SetStageFn);
+
+ RegisterFunction("enable_reboot", EnableRebootFn);
}
diff --git a/updater/install.h b/updater/install.h
index 94f344f8e..659c8b41c 100644
--- a/updater/install.h
+++ b/updater/install.h
@@ -19,4 +19,6 @@
void RegisterInstallFunctions();
+static int make_parents(char* name);
+
#endif
diff --git a/updater/updater.c b/updater/updater.c
index 39c52b49d..78c0dc1a6 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -22,7 +22,9 @@
#include "edify/expr.h"
#include "updater.h"
#include "install.h"
+#include "blockimg.h"
#include "minzip/Zip.h"
+#include "minzip/SysUtil.h"
// Generated by the makefile, this function defines the
// RegisterDeviceExtensions() function, which calls all the
@@ -68,19 +70,24 @@ int main(int argc, char** argv) {
// Extract the script from the package.
- char* package_data = argv[3];
+ const char* package_filename = argv[3];
+ MemMapping map;
+ if (sysMapFile(package_filename, &map) != 0) {
+ printf("failed to map package %s\n", argv[3]);
+ return 3;
+ }
ZipArchive za;
int err;
- err = mzOpenZipArchive(package_data, &za);
+ err = mzOpenZipArchive(map.addr, map.length, &za);
if (err != 0) {
printf("failed to open package %s: %s\n",
- package_data, strerror(err));
+ argv[3], strerror(err));
return 3;
}
const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
if (script_entry == NULL) {
- printf("failed to find %s in %s\n", SCRIPT_NAME, package_data);
+ printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename);
return 4;
}
@@ -112,6 +119,7 @@ int main(int argc, char** argv) {
RegisterBuiltins();
RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
RegisterDeviceExtensions();
FinishRegistration();
@@ -119,8 +127,7 @@ int main(int argc, char** argv) {
Expr* root;
int error_count = 0;
- yy_scan_string(script);
- int error = yyparse(&root, &error_count);
+ int error = parse_string(script, &root, &error_count);
if (error != 0 || error_count > 0) {
printf("%d parse errors\n", error_count);
return 6;
@@ -150,6 +157,8 @@ int main(int argc, char** argv) {
updater_info.cmd_pipe = cmd_pipe;
updater_info.package_zip = &za;
updater_info.version = atoi(version);
+ updater_info.package_zip_addr = map.addr;
+ updater_info.package_zip_len = map.length;
State state;
state.cookie = &updater_info;
@@ -180,6 +189,7 @@ int main(int argc, char** argv) {
if (updater_info.package_zip) {
mzCloseZipArchive(updater_info.package_zip);
}
+ sysReleaseMap(&map);
free(script);
return 0;
diff --git a/updater/updater.h b/updater/updater.h
index d2e901141..d1dfdd05e 100644
--- a/updater/updater.h
+++ b/updater/updater.h
@@ -27,6 +27,9 @@ typedef struct {
FILE* cmd_pipe;
ZipArchive* package_zip;
int version;
+
+ uint8_t* package_zip_addr;
+ size_t package_zip_len;
} UpdaterInfo;
extern struct selabel_handle *sehandle;
diff --git a/verifier.cpp b/verifier.cpp
index ccc0742d2..b96ba3a0d 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -14,10 +14,14 @@
* limitations under the License.
*/
+#include "asn1_decoder.h"
#include "common.h"
-#include "verifier.h"
#include "ui.h"
+#include "verifier.h"
+#include "mincrypt/dsa_sig.h"
+#include "mincrypt/p256.h"
+#include "mincrypt/p256_ecdsa.h"
#include "mincrypt/rsa.h"
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
@@ -30,13 +34,85 @@
#define PUBLIC_KEYS_FILE "/res/keys"
+/*
+ * Simple version of PKCS#7 SignedData extraction. This extracts the
+ * signature OCTET STRING to be used for signature verification.
+ *
+ * For full details, see http://www.ietf.org/rfc/rfc3852.txt
+ *
+ * The PKCS#7 structure looks like:
+ *
+ * SEQUENCE (ContentInfo)
+ * OID (ContentType)
+ * [0] (content)
+ * SEQUENCE (SignedData)
+ * INTEGER (version CMSVersion)
+ * SET (DigestAlgorithmIdentifiers)
+ * SEQUENCE (EncapsulatedContentInfo)
+ * [0] (CertificateSet OPTIONAL)
+ * [1] (RevocationInfoChoices OPTIONAL)
+ * SET (SignerInfos)
+ * SEQUENCE (SignerInfo)
+ * INTEGER (CMSVersion)
+ * SEQUENCE (SignerIdentifier)
+ * SEQUENCE (DigestAlgorithmIdentifier)
+ * SEQUENCE (SignatureAlgorithmIdentifier)
+ * OCTET STRING (SignatureValue)
+ */
+static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_der,
+ size_t* sig_der_length) {
+ asn1_context_t* ctx = asn1_context_new(pkcs7_der, pkcs7_der_len);
+ if (ctx == NULL) {
+ return false;
+ }
+
+ asn1_context_t* pkcs7_seq = asn1_sequence_get(ctx);
+ if (pkcs7_seq != NULL && asn1_sequence_next(pkcs7_seq)) {
+ asn1_context_t *signed_data_app = asn1_constructed_get(pkcs7_seq);
+ if (signed_data_app != NULL) {
+ asn1_context_t* signed_data_seq = asn1_sequence_get(signed_data_app);
+ if (signed_data_seq != NULL
+ && asn1_sequence_next(signed_data_seq)
+ && asn1_sequence_next(signed_data_seq)
+ && asn1_sequence_next(signed_data_seq)
+ && asn1_constructed_skip_all(signed_data_seq)) {
+ asn1_context_t *sig_set = asn1_set_get(signed_data_seq);
+ if (sig_set != NULL) {
+ asn1_context_t* sig_seq = asn1_sequence_get(sig_set);
+ if (sig_seq != NULL
+ && asn1_sequence_next(sig_seq)
+ && asn1_sequence_next(sig_seq)
+ && asn1_sequence_next(sig_seq)
+ && asn1_sequence_next(sig_seq)) {
+ uint8_t* sig_der_ptr;
+ if (asn1_octet_string_get(sig_seq, &sig_der_ptr, sig_der_length)) {
+ *sig_der = (uint8_t*) malloc(*sig_der_length);
+ if (*sig_der != NULL) {
+ memcpy(*sig_der, sig_der_ptr, *sig_der_length);
+ }
+ }
+ asn1_context_free(sig_seq);
+ }
+ asn1_context_free(sig_set);
+ }
+ asn1_context_free(signed_data_seq);
+ }
+ asn1_context_free(signed_data_app);
+ }
+ asn1_context_free(pkcs7_seq);
+ }
+ asn1_context_free(ctx);
+
+ return *sig_der != NULL;
+}
+
// Look for an RSA signature embedded in the .ZIP file comment given
// the path to the zip. Verify it matches one of the given public
// keys.
//
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
// or no key matches the signature).
-int verify_file(const char* path) {
+int verify_file(unsigned char* addr, size_t length) {
//ui->SetProgress(0.0);
int numKeys;
@@ -47,12 +123,6 @@ int verify_file(const char* path) {
}
LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
- FILE* f = fopen(path, "rb");
- if (f == NULL) {
- LOGE("failed to open %s (%s)\n", path, strerror(errno));
- return VERIFY_FAILURE;
- }
-
// An archive with a whole-file signature will end in six bytes:
//
// (2-byte signature start) $ff $ff (2-byte comment size)
@@ -64,34 +134,25 @@ int verify_file(const char* path) {
#define FOOTER_SIZE 6
- if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
- LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
- fclose(f);
+ if (length < FOOTER_SIZE) {
+ LOGE("not big enough to contain footer\n");
return VERIFY_FAILURE;
}
- unsigned char footer[FOOTER_SIZE];
- if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
- LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
- fclose(f);
- return VERIFY_FAILURE;
- }
+ unsigned char* footer = addr + length - FOOTER_SIZE;
if (footer[2] != 0xff || footer[3] != 0xff) {
LOGE("footer is wrong\n");
- fclose(f);
return VERIFY_FAILURE;
}
size_t comment_size = footer[4] + (footer[5] << 8);
size_t signature_start = footer[0] + (footer[1] << 8);
- LOGI("comment is %d bytes; signature %d bytes from end\n",
+ LOGI("comment is %zu bytes; signature %zu bytes from end\n",
comment_size, signature_start);
- if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
- // "signature" block isn't big enough to contain an RSA block.
- LOGE("signature is too short\n");
- fclose(f);
+ if (signature_start <= FOOTER_SIZE) {
+ LOGE("Signature start is in the footer");
return VERIFY_FAILURE;
}
@@ -101,9 +162,8 @@ int verify_file(const char* path) {
// comment length.
size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
- if (fseek(f, -eocd_size, SEEK_END) != 0) {
- LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
- fclose(f);
+ if (length < eocd_size) {
+ LOGE("not big enough to contain EOCD\n");
return VERIFY_FAILURE;
}
@@ -111,26 +171,15 @@ int verify_file(const char* path) {
// This is everything except the signature data and length, which
// includes all of the EOCD except for the comment length field (2
// bytes) and the comment data.
- size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
+ size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
- unsigned char* eocd = (unsigned char*)malloc(eocd_size);
- if (eocd == NULL) {
- LOGE("malloc for EOCD record failed\n");
- fclose(f);
- return VERIFY_FAILURE;
- }
- if (fread(eocd, 1, eocd_size, f) != eocd_size) {
- LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
- fclose(f);
- return VERIFY_FAILURE;
- }
+ unsigned char* eocd = addr + length - eocd_size;
// If this is really is the EOCD record, it will begin with the
// magic number $50 $4b $05 $06.
if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
eocd[2] != 0x05 || eocd[3] != 0x06) {
LOGE("signature length doesn't match EOCD marker\n");
- fclose(f);
return VERIFY_FAILURE;
}
@@ -143,7 +192,6 @@ int verify_file(const char* path) {
// which could be exploitable. Fail verification if
// this sequence occurs anywhere after the real one.
LOGE("EOCD marker occurs after start of EOCD\n");
- fclose(f);
return VERIFY_FAILURE;
}
}
@@ -163,39 +211,42 @@ int verify_file(const char* path) {
SHA256_CTX sha256_ctx;
SHA_init(&sha1_ctx);
SHA256_init(&sha256_ctx);
- unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE);
- if (buffer == NULL) {
- LOGE("failed to alloc memory for sha1 buffer\n");
- fclose(f);
- return VERIFY_FAILURE;
- }
double frac = -1.0;
size_t so_far = 0;
- fseek(f, 0, SEEK_SET);
while (so_far < signed_len) {
- size_t size = BUFFER_SIZE;
- if (signed_len - so_far < size) size = signed_len - so_far;
- if (fread(buffer, 1, size, f) != size) {
- LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
- fclose(f);
- return VERIFY_FAILURE;
- }
- if (need_sha1) SHA_update(&sha1_ctx, buffer, size);
- if (need_sha256) SHA256_update(&sha256_ctx, buffer, size);
+ size_t size = signed_len - so_far;
+ if (size > BUFFER_SIZE) size = BUFFER_SIZE;
+
+ if (need_sha1) SHA_update(&sha1_ctx, addr + so_far, size);
+ if (need_sha256) SHA256_update(&sha256_ctx, addr + so_far, size);
so_far += size;
+
double f = so_far / (double)signed_len;
if (f > frac + 0.02 || size == so_far) {
//ui->SetProgress(f);
frac = f;
}
}
- fclose(f);
- free(buffer);
const uint8_t* sha1 = SHA_final(&sha1_ctx);
const uint8_t* sha256 = SHA256_final(&sha256_ctx);
+ uint8_t* sig_der = NULL;
+ size_t sig_der_length = 0;
+
+ size_t signature_size = signature_start - FOOTER_SIZE;
+ if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der,
+ &sig_der_length)) {
+ LOGE("Could not find signature DER block\n");
+ return VERIFY_FAILURE;
+ }
+
+ /*
+ * Check to make sure at least one of the keys matches the signature. Since
+ * any key can match, we need to try each before determining a verification
+ * failure has happened.
+ */
for (i = 0; i < numKeys; ++i) {
const uint8_t* hash;
switch (pKeys[i].hash_len) {
@@ -206,17 +257,47 @@ int verify_file(const char* path) {
// The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
// the signing tool appends after the signature itself.
- if (RSA_verify(pKeys[i].public_key, eocd + eocd_size - 6 - RSANUMBYTES,
- RSANUMBYTES, hash, pKeys[i].hash_len)) {
- LOGI("whole-file signature verified against key %d\n", i);
- free(eocd);
+ if (pKeys[i].key_type == Certificate::RSA) {
+ if (sig_der_length < RSANUMBYTES) {
+ // "signature" block isn't big enough to contain an RSA block.
+ LOGI("signature is too short for RSA key %zu\n", i);
+ continue;
+ }
+
+ if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES,
+ hash, pKeys[i].hash_len)) {
+ LOGI("failed to verify against RSA key %zu\n", i);
+ continue;
+ }
+
+ LOGI("whole-file signature verified against RSA key %zu\n", i);
+ free(sig_der);
+ return VERIFY_SUCCESS;
+ } else if (pKeys[i].key_type == Certificate::EC
+ && pKeys[i].hash_len == SHA256_DIGEST_SIZE) {
+ p256_int r, s;
+ if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) {
+ LOGI("Not a DSA signature block for EC key %zu\n", i);
+ continue;
+ }
+
+ p256_int p256_hash;
+ p256_from_bin(hash, &p256_hash);
+ if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y),
+ &p256_hash, &r, &s)) {
+ LOGI("failed to verify against EC key %zu\n", i);
+ continue;
+ }
+
+ LOGI("whole-file signature verified against EC key %zu\n", i);
+ free(sig_der);
return VERIFY_SUCCESS;
} else {
- LOGI("failed to verify against key %d\n", i);
+ LOGI("Unknown key type %d\n", pKeys[i].key_type);
}
LOGI("i: %i, eocd_size: %i, RSANUMBYTES: %i\n", i, eocd_size, RSANUMBYTES);
}
- free(eocd);
+ free(sig_der);
LOGE("failed to verify whole-file signature\n");
return VERIFY_FAILURE;
}
@@ -248,6 +329,7 @@ int verify_file(const char* path) {
// 2: 2048-bit RSA key with e=65537 and SHA-1 hash
// 3: 2048-bit RSA key with e=3 and SHA-256 hash
// 4: 2048-bit RSA key with e=65537 and SHA-256 hash
+// 5: 256-bit EC key using the NIST P-256 curve parameters and SHA-256 hash
//
// Returns NULL if the file failed to parse, or if it contain zero keys.
Certificate*
@@ -268,28 +350,41 @@ load_keys(const char* filename, int* numKeys) {
++*numKeys;
out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate));
Certificate* cert = out + (*numKeys - 1);
- cert->public_key = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
+ memset(cert, '\0', sizeof(Certificate));
char start_char;
if (fscanf(f, " %c", &start_char) != 1) goto exit;
if (start_char == '{') {
// a version 1 key has no version specifier.
- cert->public_key->exponent = 3;
+ cert->key_type = Certificate::RSA;
+ cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
+ cert->rsa->exponent = 3;
cert->hash_len = SHA_DIGEST_SIZE;
} else if (start_char == 'v') {
int version;
if (fscanf(f, "%d {", &version) != 1) goto exit;
switch (version) {
case 2:
- cert->public_key->exponent = 65537;
+ cert->key_type = Certificate::RSA;
+ cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
+ cert->rsa->exponent = 65537;
cert->hash_len = SHA_DIGEST_SIZE;
break;
case 3:
- cert->public_key->exponent = 3;
+ cert->key_type = Certificate::RSA;
+ cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
+ cert->rsa->exponent = 3;
cert->hash_len = SHA256_DIGEST_SIZE;
break;
case 4:
- cert->public_key->exponent = 65537;
+ cert->key_type = Certificate::RSA;
+ cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
+ cert->rsa->exponent = 65537;
+ cert->hash_len = SHA256_DIGEST_SIZE;
+ break;
+ case 5:
+ cert->key_type = Certificate::EC;
+ cert->ec = (ECPublicKey*)calloc(1, sizeof(ECPublicKey));
cert->hash_len = SHA256_DIGEST_SIZE;
break;
default:
@@ -297,23 +392,55 @@ load_keys(const char* filename, int* numKeys) {
}
}
- RSAPublicKey* key = cert->public_key;
- if (fscanf(f, " %i , 0x%x , { %u",
- &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
- goto exit;
- }
- if (key->len != RSANUMWORDS) {
- LOGE("key length (%d) does not match expected size\n", key->len);
+ if (cert->key_type == Certificate::RSA) {
+ RSAPublicKey* key = cert->rsa;
+ if (fscanf(f, " %i , 0x%x , { %u",
+ &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
+ goto exit;
+ }
+ if (key->len != RSANUMWORDS) {
+ LOGE("key length (%d) does not match expected size\n", key->len);
+ goto exit;
+ }
+ for (i = 1; i < key->len; ++i) {
+ if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
+ }
+ if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
+ for (i = 1; i < key->len; ++i) {
+ if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
+ }
+ fscanf(f, " } } ");
+
+ LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
+ } else if (cert->key_type == Certificate::EC) {
+ ECPublicKey* key = cert->ec;
+ int key_len;
+ unsigned int byte;
+ uint8_t x_bytes[P256_NBYTES];
+ uint8_t y_bytes[P256_NBYTES];
+ if (fscanf(f, " %i , { %u", &key_len, &byte) != 2) goto exit;
+ if (key_len != P256_NBYTES) {
+ LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES);
+ goto exit;
+ }
+ x_bytes[P256_NBYTES - 1] = byte;
+ for (i = P256_NBYTES - 2; i >= 0; --i) {
+ if (fscanf(f, " , %u", &byte) != 1) goto exit;
+ x_bytes[i] = byte;
+ }
+ if (fscanf(f, " } , { %u", &byte) != 1) goto exit;
+ y_bytes[P256_NBYTES - 1] = byte;
+ for (i = P256_NBYTES - 2; i >= 0; --i) {
+ if (fscanf(f, " , %u", &byte) != 1) goto exit;
+ y_bytes[i] = byte;
+ }
+ fscanf(f, " } } ");
+ p256_from_bin(x_bytes, &key->x);
+ p256_from_bin(y_bytes, &key->y);
+ } else {
+ LOGE("Unknown key type %d\n", cert->key_type);
goto exit;
}
- for (i = 1; i < key->len; ++i) {
- if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
- }
- if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
- for (i = 1; i < key->len; ++i) {
- if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
- }
- fscanf(f, " } } ");
// if the line ends in a comma, this file has more keys.
switch (fgetc(f)) {
@@ -329,7 +456,10 @@ load_keys(const char* filename, int* numKeys) {
LOGE("unexpected character between keys\n");
goto exit;
}
+<<<<<<< HEAD
LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
+=======
+>>>>>>> cddb68b5eafbeba696d5276bda1f1a9f70bbde42
}
}
diff --git a/verifier.h b/verifier.h
index d70417340..43fd5adcd 100644
--- a/verifier.h
+++ b/verifier.h
@@ -17,6 +17,7 @@
#ifndef _RECOVERY_VERIFIER_H
#define _RECOVERY_VERIFIER_H
+#include "mincrypt/p256.h"
#include "mincrypt/rsa.h"
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
@@ -26,14 +27,30 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
typedef struct Certificate {
+
+typedef struct {
+ p256_int x;
+ p256_int y;
+} ECPublicKey;
+
+typedef struct {
+ typedef enum {
+ RSA,
+ EC,
+ } KeyType;
+
int hash_len; // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256)
- RSAPublicKey* public_key;
+ KeyType key_type;
+ RSAPublicKey* rsa;
+ ECPublicKey* ec;
} Certificate;
-/* Look in the file for a signature footer, and verify that it
- * matches one of the given keys. Return one of the constants below.
+/* addr and length define a an update package file that has been
+ * loaded (or mmap'ed, or whatever) into memory. Verify that the file
+ * is signed and the signature matches one of the given keys. Return
+ * one of the constants below.
*/
-int verify_file(const char* path);
+int verify_file(unsigned char* addr, size_t length);
Certificate* load_keys(const char* filename, int* numKeys);
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 20aa3d1de..3ba270de7 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -17,6 +17,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
/*
#include "common.h"
@@ -25,6 +28,7 @@
#include "ui.h"
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
+#include "minzip/SysUtil.h"
// This is build/target/product/security/testkey.x509.pem after being
// dumped out by dumpkey.jar.
@@ -102,6 +106,18 @@ RSAPublicKey test_f4_key =
65537
};
+ECPublicKey test_ec_key =
+ {
+ {
+ {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
+ 0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
+ },
+ {
+ {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
+ 0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
+ }
+ };
+
RecoveryUI* ui = NULL;
// verifier expects to find a UI object; we provide one that does
@@ -138,37 +154,93 @@ ui_print(const char* format, ...) {
va_end(ap);
}
+static Certificate* add_certificate(Certificate** certsp, int* num_keys,
+ Certificate::KeyType key_type) {
+ int i = *num_keys;
+ *num_keys = *num_keys + 1;
+ *certsp = (Certificate*) realloc(*certsp, *num_keys * sizeof(Certificate));
+ Certificate* certs = *certsp;
+ certs[i].rsa = NULL;
+ certs[i].ec = NULL;
+ certs[i].key_type = key_type;
+ certs[i].hash_len = SHA_DIGEST_SIZE;
+ return &certs[i];
+}
+
int main(int argc, char **argv) {
- if (argc < 2 || argc > 4) {
- fprintf(stderr, "Usage: %s [-sha256] [-f4 | -file <keys>] <package>\n", argv[0]);
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s [-sha256] [-ec | -f4 | -file <keys>] <package>\n", argv[0]);
return 2;
}
+ Certificate* certs = NULL;
+ int num_keys = 0;
- Certificate default_cert;
- Certificate* cert = &default_cert;
- cert->public_key = &test_key;
- cert->hash_len = SHA_DIGEST_SIZE;
- int num_keys = 1;
- ++argv;
- if (strcmp(argv[0], "-sha256") == 0) {
- ++argv;
- cert->hash_len = SHA256_DIGEST_SIZE;
+ int argn = 1;
+ while (argn < argc) {
+ if (strcmp(argv[argn], "-sha256") == 0) {
+ if (num_keys == 0) {
+ fprintf(stderr, "May only specify -sha256 after key type\n");
+ return 2;
+ }
+ ++argn;
+ Certificate* cert = &certs[num_keys - 1];
+ cert->hash_len = SHA256_DIGEST_SIZE;
+ } else if (strcmp(argv[argn], "-ec") == 0) {
+ ++argn;
+ Certificate* cert = add_certificate(&certs, &num_keys, Certificate::EC);
+ cert->ec = &test_ec_key;
+ } else if (strcmp(argv[argn], "-e3") == 0) {
+ ++argn;
+ Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA);
+ cert->rsa = &test_key;
+ } else if (strcmp(argv[argn], "-f4") == 0) {
+ ++argn;
+ Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA);
+ cert->rsa = &test_f4_key;
+ } else if (strcmp(argv[argn], "-file") == 0) {
+ if (certs != NULL) {
+ fprintf(stderr, "Cannot specify -file with other certs specified\n");
+ return 2;
+ }
+ ++argn;
+ certs = load_keys(argv[argn], &num_keys);
+ ++argn;
+ } else if (argv[argn][0] == '-') {
+ fprintf(stderr, "Unknown argument %s\n", argv[argn]);
+ return 2;
+ } else {
+ break;
+ }
}
- if (strcmp(argv[0], "-f4") == 0) {
- ++argv;
- cert->public_key = &test_f4_key;
- } else if (strcmp(argv[0], "-file") == 0) {
- ++argv;
- cert = load_keys(argv[0], &num_keys);
- ++argv;
+
+ if (argn == argc) {
+ fprintf(stderr, "Must specify package to verify\n");
+ return 2;
+ }
+
+ if (num_keys == 0) {
+ certs = (Certificate*) calloc(1, sizeof(Certificate));
+ if (certs == NULL) {
+ fprintf(stderr, "Failure allocating memory for default certificate\n");
+ return 1;
+ }
+ certs->key_type = Certificate::RSA;
+ certs->rsa = &test_key;
+ certs->ec = NULL;
+ certs->hash_len = SHA_DIGEST_SIZE;
+ num_keys = 1;
}
ui = new FakeUI();
-/*
- int result = verify_file(*argv, cert, num_keys);
-*/
- int result = verify_file(*argv);
+ MemMapping map;
+ if (sysMapFile(argv[argn], &map) != 0) {
+ fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno));
+ return 4;
+ }
+
+ int result = verify_file(map.addr, map.length);
+
if (result == VERIFY_SUCCESS) {
printf("VERIFIED\n");
return 0;
diff --git a/verifier_test.sh b/verifier_test.sh
index 65f77f401..4761cef4a 100755
--- a/verifier_test.sh
+++ b/verifier_test.sh
@@ -81,20 +81,30 @@ expect_fail unsigned.zip
expect_fail jarsigned.zip
# success cases
-expect_succeed otasigned.zip
+expect_succeed otasigned.zip -e3
expect_succeed otasigned_f4.zip -f4
-expect_succeed otasigned_sha256.zip -sha256
-expect_succeed otasigned_f4_sha256.zip -sha256 -f4
+expect_succeed otasigned_sha256.zip -e3 -sha256
+expect_succeed otasigned_f4_sha256.zip -f4 -sha256
+expect_succeed otasigned_ecdsa_sha256.zip -ec -sha256
+
+# success with multiple keys
+expect_succeed otasigned.zip -f4 -e3
+expect_succeed otasigned_f4.zip -ec -f4
+expect_succeed otasigned_sha256.zip -ec -e3 -e3 -sha256
+expect_succeed otasigned_f4_sha256.zip -ec -sha256 -e3 -f4 -sha256
+expect_succeed otasigned_ecdsa_sha256.zip -f4 -sha256 -e3 -ec -sha256
# verified against different key
expect_fail otasigned.zip -f4
-expect_fail otasigned_f4.zip
+expect_fail otasigned_f4.zip -e3
+expect_fail otasigned_ecdsa_sha256.zip -e3 -sha256
# verified against right key but wrong hash algorithm
-expect_fail otasigned.zip -sha256
-expect_fail otasigned_f4.zip -sha256 -f4
+expect_fail otasigned.zip -e3 -sha256
+expect_fail otasigned_f4.zip -f4 -sha256
expect_fail otasigned_sha256.zip
expect_fail otasigned_f4_sha256.zip -f4
+expect_fail otasigned_ecdsa_sha256.zip
# various other cases
expect_fail random.zip