Merge in lollipop and attempt to fix merge conflicts
This will probably not compile and may need additional work. For tracking purposes so we know what might still need looking at as none of this has been compiled and tested, here is a list of the merge conflicts that I attempted to fix before pushing this set of changes: git pull aosp lollipop-release remote: Finding sources: 100% (992/992) remote: Total 992 (delta 473), reused 992 (delta 473) Receiving objects: 100% (992/992), 1.51 MiB | 516.00 KiB/s, done. Resolving deltas: 100% (473/473), completed with 42 local objects. From https://android.googlesource.com/platform/bootable/recovery * branch lollipop-release -> FETCH_HEAD * [new branch] lollipop-release -> aosp/lollipop-release Auto-merging verifier_test.cpp CONFLICT (content): Merge conflict in verifier_test.cpp Auto-merging verifier.h CONFLICT (content): Merge conflict in verifier.h Auto-merging verifier.cpp CONFLICT (content): Merge conflict in verifier.cpp Auto-merging updater/updater.c Auto-merging updater/install.c CONFLICT (content): Merge conflict in updater/install.c Auto-merging updater/Android.mk CONFLICT (content): Merge conflict in updater/Android.mk Auto-merging uncrypt/Android.mk CONFLICT (content): Merge conflict in uncrypt/Android.mk Auto-merging ui.cpp CONFLICT (content): Merge conflict in ui.cpp Auto-merging screen_ui.cpp Auto-merging roots.cpp CONFLICT (content): Merge conflict in roots.cpp CONFLICT (rename/delete): res-hdpi/images/progress_fill.png deleted in HEAD and renamed incddb68b5ea. Versioncddb68b5eaof res-hdpi/images/progress_fill.png left in tree. CONFLICT (rename/delete): res-hdpi/images/progress_empty.png deleted in HEAD and renamed incddb68b5ea. Versioncddb68b5eaof res-hdpi/images/progress_empty.png left in tree. CONFLICT (rename/delete): res-hdpi/images/icon_error.png deleted in HEAD and renamed incddb68b5ea. Versioncddb68b5eaof res-hdpi/images/icon_error.png left in tree. Auto-merging recovery.cpp CONFLICT (content): Merge conflict in recovery.cpp Auto-merging minui/resources.c CONFLICT (content): Merge conflict in minui/resources.c Auto-merging minui/minui.h CONFLICT (content): Merge conflict in minui/minui.h Auto-merging minui/graphics.c CONFLICT (content): Merge conflict in minui/graphics.c Auto-merging minui/Android.mk CONFLICT (content): Merge conflict in minui/Android.mk Removing minelf/Retouch.h Removing minelf/Retouch.c Auto-merging minadbd/usb_linux_client.c CONFLICT (content): Merge conflict in minadbd/usb_linux_client.c Auto-merging minadbd/adb.h CONFLICT (content): Merge conflict in minadbd/adb.h Auto-merging minadbd/adb.c CONFLICT (content): Merge conflict in minadbd/adb.c Auto-merging minadbd/Android.mk CONFLICT (content): Merge conflict in minadbd/Android.mk Removing make-overlay.py Auto-merging install.h CONFLICT (content): Merge conflict in install.h Auto-merging etc/init.rc CONFLICT (content): Merge conflict in etc/init.rc Auto-merging bootloader.h Auto-merging applypatch/applypatch.c Auto-merging applypatch/Android.mk CONFLICT (content): Merge conflict in applypatch/Android.mk Auto-merging adb_install.cpp CONFLICT (content): Merge conflict in adb_install.cpp Auto-merging Android.mk CONFLICT (content): Merge conflict in Android.mk Automatic merge failed; fix conflicts and then commit the result. Change-Id: I3e0e03e48ad8550912111c7a5c9a140ed0267e2c
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, ©_file,
|
||||
RETOUCH_DO_MASK) < 0) {
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, ©_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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -407,6 +407,7 @@ int adb_main(const char* path)
|
||||
|
||||
fprintf(stderr, "userid is %d\n", getuid());
|
||||
*/
|
||||
|
||||
D("Event loop starting\n");
|
||||
|
||||
fdevent_loop();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
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);
|
||||
|
||||
fprintf(stderr, "sideload_service invoked\n");
|
||||
printf("sideload-host file size %llu block size %lu\n", file_size, block_size);
|
||||
|
||||
fd = adb_creat(ADB_SIDELOAD_FILENAME, 0644);
|
||||
if(fd < 0) {
|
||||
fprintf(stderr, "failed to create %s\n", ADB_SIDELOAD_FILENAME);
|
||||
adb_close(s);
|
||||
return;
|
||||
}
|
||||
int result = run_adb_fuse(sfd, file_size, block_size);
|
||||
|
||||
while(count > 0) {
|
||||
unsigned xfer = (count > 4096) ? 4096 : count;
|
||||
if(readx(s, buf, xfer)) break;
|
||||
if(writex(fd, buf, xfer)) break;
|
||||
count -= xfer;
|
||||
}
|
||||
|
||||
if(count == 0) {
|
||||
writex(s, "OKAY", 4);
|
||||
} else {
|
||||
writex(s, "FAIL", 4);
|
||||
}
|
||||
adb_close(fd);
|
||||
adb_close(s);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ struct {
|
||||
unsigned height;
|
||||
unsigned cwidth;
|
||||
unsigned cheight;
|
||||
unsigned char rundata[];
|
||||
unsigned char rundata[2973];
|
||||
} font = {
|
||||
.width = 960,
|
||||
.height = 18,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
// interpret the grayscale as alpha
|
||||
gr_font->texture->format = GGL_PIXEL_FORMAT_A_8;
|
||||
#if 0
|
||||
// Exercises many of the gr_*() functions; useful for testing.
|
||||
static void gr_test() {
|
||||
GRSurface** images;
|
||||
int frames;
|
||||
int result = res_create_multi_surface("icon_installing", &frames, &images);
|
||||
if (result < 0) {
|
||||
printf("create surface %d\n", result);
|
||||
gr_exit();
|
||||
return;
|
||||
}
|
||||
|
||||
time_t start = time(NULL);
|
||||
int x;
|
||||
for (x = 0; x <= 1200; ++x) {
|
||||
if (x < 400) {
|
||||
gr_color(0, 0, 0, 255);
|
||||
} else {
|
||||
gr_color(0, (x-400)%128, 0, 255);
|
||||
}
|
||||
gr_clear();
|
||||
|
||||
gr_color(255, 0, 0, 255);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
gr_fb_blank(true);
|
||||
gr_fb_blank(false);
|
||||
overscan_offset_x = gr_draw->width * overscan_percent / 100;
|
||||
overscan_offset_y = gr_draw->height * overscan_percent / 100;
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
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;
|
||||
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);
|
||||
exit:
|
||||
if (result < 0) {
|
||||
png_destroy_read_struct(png_ptr, info_ptr, NULL);
|
||||
}
|
||||
if (fp != NULL) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// "display" surfaces are transformed into the framebuffer's required
|
||||
// pixel format (currently only RGBX is supported) at load time, so
|
||||
// gr_blit() can be nothing more than a memcpy() for each row. The
|
||||
// next two functions are the only ones that know anything about the
|
||||
// framebuffer pixel format; they need to be modified if the
|
||||
// framebuffer format changes (but nothing else should).
|
||||
|
||||
// Allocate and return a 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->row_bytes = width * 4;
|
||||
surface->pixel_bytes = 4;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
// Copy 'input_row' to 'output_row', transforming it to the
|
||||
// framebuffer pixel format. The input format depends on the value of
|
||||
// 'channels':
|
||||
//
|
||||
// 1 - input is 8-bit grayscale
|
||||
// 3 - input is 24-bit RGB
|
||||
// 4 - input is 32-bit RGBA/RGBX
|
||||
//
|
||||
// 'width' is the number of pixels in the row.
|
||||
static void transform_rgb_to_draw(unsigned char* input_row,
|
||||
unsigned char* output_row,
|
||||
int channels, int width) {
|
||||
int x;
|
||||
unsigned char* ip = input_row;
|
||||
unsigned char* op = output_row;
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
// expand gray level to RGBX
|
||||
for (x = 0; x < width; ++x) {
|
||||
*op++ = *ip;
|
||||
*op++ = *ip;
|
||||
*op++ = *ip;
|
||||
*op++ = 0xff;
|
||||
ip++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// expand RGBA to RGBX
|
||||
for (x = 0; x < width; ++x) {
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
*op++ = 0xff;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// copy RGBA to RGBX
|
||||
memcpy(output_row, input_row, width*4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int res_create_display_surface(const char* name, 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* pData = (unsigned char*) (surface + 1);
|
||||
surface->version = sizeof(GGLSurface);
|
||||
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;
|
||||
}
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY) {
|
||||
alpha = 1;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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;
|
||||
*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;
|
||||
if (locale == NULL) {
|
||||
surface = malloc_surface(0);
|
||||
surface->width = 0;
|
||||
surface->height = 0;
|
||||
surface->row_bytes = 0;
|
||||
surface->pixel_bytes = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
size_t bytesRead = fread(header, 1, sizeof(header), fp);
|
||||
if (bytesRead != sizeof(header)) {
|
||||
result = -2;
|
||||
goto exit;
|
||||
}
|
||||
result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
|
||||
if (result < 0) return result;
|
||||
|
||||
if (png_sig_cmp(header, 0, sizeof(header))) {
|
||||
result = -3;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
result = -4;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
result = -5;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
result = -6;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
png_set_sig_bytes(png_ptr, sizeof(header));
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
char block_dev[PATH_MAX+1];
|
||||
size_t size;
|
||||
unsigned int blksize;
|
||||
unsigned int blocks;
|
||||
unsigned int range_count;
|
||||
unsigned int i;
|
||||
|
||||
assert(pMap != NULL);
|
||||
|
||||
if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
|
||||
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 (start + length > (long)fileLength) {
|
||||
LOGW("bad segment: st=%d len=%ld flen=%d\n",
|
||||
(int) start, length, (int) fileLength);
|
||||
if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) {
|
||||
LOGW("failed to parse block map header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* adjust to be page-aligned */
|
||||
adjust = start % DEFAULT_PAGE_SIZE;
|
||||
actualStart = start - adjust;
|
||||
actualLength = length + adjust;
|
||||
blocks = ((size-1) / blksize) + 1;
|
||||
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
pMap->baseAddr = memPtr;
|
||||
pMap->baseLength = actualLength;
|
||||
pMap->addr = (char*)memPtr + adjust;
|
||||
pMap->length = length;
|
||||
pMap->ranges[range_count-1].addr = reserve;
|
||||
pMap->ranges[range_count-1].length = blocks * blksize;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.)
|
||||
* 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 sysLoadFileInShmem(int fd, MemMapping* pMap);
|
||||
|
||||
/*
|
||||
* Map a file (from fd's current offset) into a shared,
|
||||
* read-only memory segment.
|
||||
*
|
||||
* 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*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
@@ -182,6 +182,17 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
break;
|
||||
} else 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;
|
||||
} 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;
|
||||
}
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
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
|
||||
new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/'
|
||||
result = browse_directory(new_path, device);
|
||||
if (result) break;
|
||||
} else {
|
||||
// selected a zip file: return the malloc'd path to the caller.
|
||||
result = strdup(new_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 118 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 322 B |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 118 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 322 B |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 118 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 322 B |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 118 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 322 B |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 116 KiB |