Compare commits
267 Commits
Author | SHA1 | Date | |
---|---|---|---|
adf906d034 | |||
7f4ff5cd3a | |||
23ce6b14ae | |||
56606a2da3 | |||
ceddcd59ac | |||
54a284568b | |||
3b4135826d | |||
38e8b2b6a9 | |||
f0e31b89b9 | |||
d632c0def4 | |||
2098707b02 | |||
6c7745d928 | |||
4e625e8cd2 | |||
852bb420e7 | |||
d4060c3eb6 | |||
e074d8d543 | |||
5aaa8238f7 | |||
92077c15d6 | |||
fae335e159 | |||
d63eaef179 | |||
ecd32fa2e8 | |||
1bf4f695d4 | |||
37186b19bc | |||
d8038e15f8 | |||
062d6b0bb3 | |||
6440ed585f | |||
b9c595c654 | |||
1e8aabad34 | |||
0837091e8d | |||
d634bb2d2b | |||
cd44ab973e | |||
49af23cbce | |||
8ec9418782 | |||
52d3f205b5 | |||
5cd94a9a73 | |||
e25908bd87 | |||
a6522b3584 | |||
598cfc7e9d | |||
c290861d4b | |||
e51e47d814 | |||
63e0476b6e | |||
8b5e1856b3 | |||
14239d291a | |||
f8b21c2b4d | |||
59bcc7eb3f | |||
1741dcdb26 | |||
cdb63355c1 | |||
58131e760c | |||
387080a40b | |||
f4e3a67a38 | |||
cd9e3ce55b | |||
77c452689d | |||
4206851070 | |||
917c38af20 | |||
2c3cc50356 | |||
bca9243339 | |||
a2f6c69e7f | |||
ada3f3ba33 | |||
fd1579b7c0 | |||
1e9f60a967 | |||
8430655931 | |||
f3534d08eb | |||
b7538c721e | |||
2f73e58ef8 | |||
7fe4d7bbcb | |||
6d32ac029c | |||
8fa54d19f1 | |||
59be31ed31 | |||
5552aa38f5 | |||
a5f64e66dd | |||
6771aca715 | |||
7c17e8828c | |||
71ef11f2d5 | |||
1d53c4ea63 | |||
dec83e2551 | |||
b4bb02bdfb | |||
d6380d2cb4 | |||
24a0124b7c | |||
68b0190858 | |||
3f99539c4d | |||
32f5c7ec94 | |||
a958ac458d | |||
d221668138 | |||
5a3caa1cfe | |||
24cecb1d08 | |||
6923cc3223 | |||
6a26e7c23f | |||
c0970934e2 | |||
04159f738f | |||
fe84a7ff84 | |||
78686294eb | |||
d717892353 | |||
f953555bde | |||
b0a2503e02 | |||
1c61c29837 | |||
622e82ace9 | |||
da3a58289f | |||
707fa6d289 | |||
581bd861d1 | |||
a496b51a83 | |||
16f0b49bea | |||
75b930f396 | |||
5899ac9ca0 | |||
d3243fe047 | |||
6f406aab03 | |||
f721594cd0 | |||
23116c95c7 | |||
3fb8d302f4 | |||
4b249cd022 | |||
7e0a72c8b3 | |||
b9546a8047 | |||
face588f64 | |||
6da075c92b | |||
3f38f328cf | |||
ee57bbc1b6 | |||
928d605435 | |||
54305a8497 | |||
a85d7cc65a | |||
4a8e7d2a09 | |||
3836f72fbf | |||
6bd595cc32 | |||
3a25cf553d | |||
85c2a5386b | |||
8c866dc252 | |||
5306db57e5 | |||
8bbbbd4445 | |||
ea46fe29d9 | |||
19459f31fa | |||
60d7ee07ba | |||
a9483087a0 | |||
13d8fccf50 | |||
32e4111df5 | |||
72a1db6358 | |||
f68aaaf20c | |||
2bda3e9fa0 | |||
73098ab18a | |||
99fb6fef11 | |||
e81cb750fd | |||
81d8ee59bb | |||
0317378bf6 | |||
ea2f2428a1 | |||
a1749d93bc | |||
db8dedf12f | |||
185baeeb79 | |||
261dde9f48 | |||
001c5b508f | |||
49f5689b42 | |||
79ce82cab3 | |||
33370db187 | |||
01098d245c | |||
36d02893cd | |||
ff32e8c7e5 | |||
a37e9b1f19 | |||
5740b042b5 | |||
69b75410de | |||
007439bef6 | |||
7400dc2700 | |||
981b0cd1e1 | |||
225c6b4673 | |||
1fa52ecdbf | |||
bcdd00359c | |||
466e67a582 | |||
1a7ee5384d | |||
7e5a661064 | |||
f9476fbfe8 | |||
c788c26397 | |||
8ce0be4956 | |||
3ab130fa9e | |||
bf055bb1be | |||
a3c2f735d7 | |||
841b2bf352 | |||
3a976a79bf | |||
e923487ff6 | |||
1f14c9a1f1 | |||
6060e5c6df | |||
4c1eed2573 | |||
0d4ff2fcc6 | |||
89d385c4d9 | |||
22d79a5c5e | |||
17a47098d2 | |||
c652e41d91 | |||
54ec81fe86 | |||
988500b615 | |||
1c8ca2e40b | |||
f93d8166ef | |||
fd8fb0c492 | |||
60babf8ba7 | |||
196c25c777 | |||
a3f89eabb7 | |||
34c98df78a | |||
54e2e86c57 | |||
0cf0e33b5f | |||
b9955b8373 | |||
34109de24e | |||
eb1ac27bf0 | |||
7bd5c66075 | |||
20697b965c | |||
23412e6f14 | |||
608fa02e1a | |||
64893ccc09 | |||
c457ff6436 | |||
bec02d57fb | |||
fc382dfc75 | |||
d8f7c9b85e | |||
ad3db099d5 | |||
898ef399d3 | |||
0bbfe3d901 | |||
4275c3cfc8 | |||
fbf3c10e45 | |||
2b0fdc6571 | |||
b128f54d0d | |||
f8aaf0a77f | |||
47cace9836 | |||
d683785ec9 | |||
fb2e3af3f9 | |||
e77e091522 | |||
84cbfb6cb4 | |||
cf2b2a2e8f | |||
e3da02e7bc | |||
d9c9d10d9d | |||
8edb00c990 | |||
32eb0a8c87 | |||
6c301e244d | |||
9dbc027b5f | |||
c3885fabda | |||
9931f7f3c1 | |||
cbb9129345 | |||
ddd6a2865d | |||
d9d9d1785a | |||
9b9c2114bd | |||
37bee62aef | |||
573fd7b68b | |||
825915dc6c | |||
b2ee9201be | |||
8caf81fa24 | |||
f28c916e73 | |||
9d5be8488f | |||
07e1dca706 | |||
985d95f9f4 | |||
1c4ceae38f | |||
1c10ff34b5 | |||
683c462803 | |||
0f03d1408d | |||
596271fa71 | |||
7fce23fbb5 | |||
f554ceb050 | |||
29fcea1564 | |||
275acbe70a | |||
796901d3b0 | |||
d5ebb7b06d | |||
0555388b8c | |||
5d30026ddb | |||
fc3ada0c11 | |||
d1b19b9c98 | |||
49283858fb | |||
1066d2c319 | |||
36a9c1e530 | |||
19faefad05 | |||
b2b467c757 | |||
58bde316e2 | |||
e6faba0580 | |||
bc012de46e | |||
cf5b17055b | |||
97c618f378 | |||
c5c389f8f2 | |||
fac53c1677 | |||
6d12e0d6f8 |
196
Android.mk
@ -1,27 +1,134 @@
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
commands_recovery_local_path := $(LOCAL_PATH)
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
# LOCAL_CPP_EXTENSION := .c
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
recovery.c \
|
||||
bootloader.c \
|
||||
extendedcommands.c \
|
||||
nandroid.c \
|
||||
legacy.c \
|
||||
commands.c \
|
||||
firmware.c \
|
||||
recovery.c \
|
||||
install.c \
|
||||
roots.c \
|
||||
ui.c \
|
||||
verifier.c
|
||||
|
||||
LOCAL_SRC_FILES += \
|
||||
reboot.c \
|
||||
setprop.c
|
||||
|
||||
ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
LOCAL_SRC_FILES += \
|
||||
firmware.c \
|
||||
bootloader.c
|
||||
else
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_NO_MISC_PARTITION
|
||||
endif
|
||||
|
||||
ifdef BOARD_RECOVERY_IGNORE_BOOTABLES
|
||||
LOCAL_CFLAGS += -DBOARD_RECOVERY_IGNORE_BOOTABLES
|
||||
endif
|
||||
|
||||
ifdef BOARD_HIJACK_RECOVERY_PATH
|
||||
LOCAL_CFLAGS += -DBOARD_HIJACK_RECOVERY_PATH=\"$(BOARD_HIJACK_RECOVERY_PATH)\"
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES += test_roots.c
|
||||
|
||||
LOCAL_MODULE := recovery
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
RECOVERY_VERSION := ClockworkMod Recovery v2.5.1.0
|
||||
LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
|
||||
RECOVERY_API_VERSION := 2
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
|
||||
ifeq ($(BOARD_HAS_NO_SELECT_BUTTON),true)
|
||||
LOCAL_CFLAGS += -DKEY_POWER_IS_SELECT_ITEM
|
||||
endif
|
||||
|
||||
ifdef BOARD_SDCARD_DEVICE_PRIMARY
|
||||
LOCAL_CFLAGS += -DSDCARD_DEVICE_PRIMARY=\"$(BOARD_SDCARD_DEVICE_PRIMARY)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SDCARD_DEVICE_SECONDARY
|
||||
LOCAL_CFLAGS += -DSDCARD_DEVICE_SECONDARY=\"$(BOARD_SDCARD_DEVICE_SECONDARY)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SDEXT_DEVICE
|
||||
LOCAL_CFLAGS += -DSDEXT_DEVICE=\"$(BOARD_SDEXT_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SDEXT_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DSDEXT_FILESYSTEM=\"$(BOARD_SDEXT_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATA_DEVICE
|
||||
LOCAL_CFLAGS += -DDATA_DEVICE=\"$(BOARD_DATA_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATA_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DDATA_FILESYSTEM=\"$(BOARD_DATA_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATADATA_DEVICE
|
||||
LOCAL_CFLAGS += -DDATADATA_DEVICE=\"$(BOARD_DATADATA_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATADATA_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DDATADATA_FILESYSTEM=\"$(BOARD_DATADATA_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_CACHE_DEVICE
|
||||
LOCAL_CFLAGS += -DCACHE_DEVICE=\"$(BOARD_CACHE_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_CACHE_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DCACHE_FILESYSTEM=\"$(BOARD_CACHE_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SYSTEM_DEVICE
|
||||
LOCAL_CFLAGS += -DSYSTEM_DEVICE=\"$(BOARD_SYSTEM_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SYSTEM_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DSYSTEM_FILESYSTEM=\"$(BOARD_SYSTEM_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_HAS_DATADATA
|
||||
LOCAL_CFLAGS += -DHAS_DATADATA
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATA_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DDATA_FILESYSTEM_OPTIONS=\"$(BOARD_DATA_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATADATA_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DDATADATA_FILESYSTEM_OPTIONS=\"$(BOARD_DATADATA_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_CACHE_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DCACHE_FILESYSTEM_OPTIONS=\"$(BOARD_CACHE_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SYSTEM_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DSYSTEM_FILESYSTEM_OPTIONS=\"$(BOARD_SYSTEM_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_HAS_MTD_CACHE
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_MTD_CACHE
|
||||
endif
|
||||
|
||||
ifdef BOARD_HAS_SMALL_RECOVERY
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_SMALL_RECOVERY
|
||||
endif
|
||||
|
||||
# This binary is in the recovery ramdisk, which is otherwise a copy of root.
|
||||
# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses
|
||||
# a (redundant) copy of the binary in /system/bin for user builds.
|
||||
@ -29,32 +136,71 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt
|
||||
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libcutils
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
|
||||
LOCAL_SRC_FILES += default_recovery_ui.c
|
||||
else
|
||||
LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING)
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image libmtdutils
|
||||
LOCAL_STATIC_LIBRARIES += libamend
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt
|
||||
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
|
||||
LOCAL_STATIC_LIBRARIES += libstdc++ libc
|
||||
|
||||
# Specify a C-includable file containing the OTA public keys.
|
||||
# This is built in config/Makefile.
|
||||
# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT
|
||||
# PRODUCTS/BUILD TYPES. ***
|
||||
# TODO: make recovery read the keys from an external file.
|
||||
RECOVERY_INSTALL_OTA_KEYS_INC := \
|
||||
$(call intermediates-dir-for,PACKAGING,ota_keys_inc)/keys.inc
|
||||
# Let install.c say #include "keys.inc"
|
||||
LOCAL_C_INCLUDES += $(dir $(RECOVERY_INSTALL_OTA_KEYS_INC))
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# Depend on the generated keys.inc containing the OTA public keys.
|
||||
$(intermediates)/install.o: $(RECOVERY_INSTALL_OTA_KEYS_INC)
|
||||
RECOVERY_LINKS := amend busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
|
||||
|
||||
# nc is provided by external/netcat
|
||||
SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
|
||||
$(SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
|
||||
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
|
||||
@echo "Symlink: $@ -> $(RECOVERY_BINARY)"
|
||||
@mkdir -p $(dir $@)
|
||||
@rm -rf $@
|
||||
$(hide) ln -sf $(RECOVERY_BINARY) $@
|
||||
|
||||
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
|
||||
|
||||
# Now let's do recovery symlinks
|
||||
BUSYBOX_LINKS := $(shell cat external/busybox/busybox-minimal.links)
|
||||
SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
|
||||
$(SYMLINKS): BUSYBOX_BINARY := busybox
|
||||
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
|
||||
@echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
|
||||
@mkdir -p $(dir $@)
|
||||
@rm -rf $@
|
||||
$(hide) ln -sf $(BUSYBOX_BINARY) $@
|
||||
|
||||
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := nandroid-md5.sh
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := nandroid-md5.sh
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := killrecovery.sh
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := killrecovery.sh
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(commands_recovery_local_path)/amend/Android.mk
|
||||
include $(commands_recovery_local_path)/minui/Android.mk
|
||||
include $(commands_recovery_local_path)/minzip/Android.mk
|
||||
include $(commands_recovery_local_path)/mtdutils/Android.mk
|
||||
include $(commands_recovery_local_path)/tools/Android.mk
|
||||
include $(commands_recovery_local_path)/edify/Android.mk
|
||||
include $(commands_recovery_local_path)/updater/Android.mk
|
||||
include $(commands_recovery_local_path)/utilities/Android.mk
|
||||
commands_recovery_local_path :=
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
||||
include $(commands_recovery_local_path)/amend/Android.mk
|
||||
include $(commands_recovery_local_path)/minzip/Android.mk
|
||||
include $(commands_recovery_local_path)/mtdutils/Android.mk
|
||||
include $(commands_recovery_local_path)/tools/Android.mk
|
||||
commands_recovery_local_path :=
|
||||
|
@ -125,6 +125,19 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* delete <srcdir> <dstdir>
|
||||
*/
|
||||
static int
|
||||
cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
//xxx
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mark <resource> dirty|clean
|
||||
*/
|
||||
static int
|
||||
@ -165,6 +178,9 @@ registerUpdateCommands()
|
||||
ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
|
12
bootloader.c
@ -198,7 +198,7 @@ int write_update_for_bootloader(
|
||||
header.version = UPDATE_VERSION;
|
||||
header.size = header_size;
|
||||
|
||||
header.image_offset = mtd_erase_blocks(write, 0);
|
||||
off_t image_start_pos = mtd_erase_blocks(write, 0);
|
||||
header.image_length = update_length;
|
||||
if ((int) header.image_offset == -1 ||
|
||||
mtd_write_data(write, update, update_length) != update_length) {
|
||||
@ -206,6 +206,8 @@ int write_update_for_bootloader(
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
off_t busy_start_pos = mtd_erase_blocks(write, 0);
|
||||
header.image_offset = mtd_find_write_start(write, image_start_pos);
|
||||
|
||||
header.bitmap_width = bitmap_width;
|
||||
header.bitmap_height = bitmap_height;
|
||||
@ -213,7 +215,6 @@ int write_update_for_bootloader(
|
||||
|
||||
int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
|
||||
|
||||
header.busy_bitmap_offset = mtd_erase_blocks(write, 0);
|
||||
header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
|
||||
if ((int) header.busy_bitmap_offset == -1 ||
|
||||
mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
|
||||
@ -221,8 +222,9 @@ int write_update_for_bootloader(
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
off_t fail_start_pos = mtd_erase_blocks(write, 0);
|
||||
header.busy_bitmap_offset = mtd_find_write_start(write, busy_start_pos);
|
||||
|
||||
header.fail_bitmap_offset = mtd_erase_blocks(write, 0);
|
||||
header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
|
||||
if ((int) header.fail_bitmap_offset == -1 ||
|
||||
mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
|
||||
@ -230,6 +232,8 @@ int write_update_for_bootloader(
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
mtd_erase_blocks(write, 0);
|
||||
header.fail_bitmap_offset = mtd_find_write_start(write, fail_start_pos);
|
||||
|
||||
/* Write the header last, after all the blocks it refers to, so that
|
||||
* when the magic number is installed everything is valid.
|
||||
@ -252,7 +256,7 @@ int write_update_for_bootloader(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mtd_erase_blocks(write, 0) != (off_t) header.image_offset) {
|
||||
if (mtd_erase_blocks(write, 0) != image_start_pos) {
|
||||
LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
|
209
commands.c
@ -39,6 +39,8 @@
|
||||
#include "minzip/Zip.h"
|
||||
#include "roots.h"
|
||||
|
||||
#include "extendedcommands.h"
|
||||
|
||||
static int gDidShowProgress = 0;
|
||||
|
||||
#define UNUSED(p) ((void)(p))
|
||||
@ -107,6 +109,10 @@ cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
|
||||
CHECK_BOOL();
|
||||
NO_PERMS(permissions);
|
||||
|
||||
if (!script_assert_enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If our argument is false, return non-zero (failure)
|
||||
* If our argument is true, return zero (success)
|
||||
*/
|
||||
@ -139,7 +145,15 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[],
|
||||
LOGE("Can't format %s\n", root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAS_DATADATA
|
||||
if (0 == strcmp(root, "DATA:")) {
|
||||
ret = format_root_device("DATADATA:");
|
||||
if (ret != 0) {
|
||||
LOGE("Can't format %s\n", root);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -342,39 +356,40 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Copy the program file to temporary storage.
|
||||
if (!is_package_root_path(argv[0])) {
|
||||
LOGE("Command %s: non-package program file \"%s\" not supported\n",
|
||||
name, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
const ZipArchive *package;
|
||||
if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
|
||||
LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const ZipEntry *entry = mzFindZipEntry(package, path);
|
||||
if (entry == NULL) {
|
||||
LOGE("Can't find %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *binary = "/tmp/run_program_binary";
|
||||
unlink(binary); // just to be sure
|
||||
int fd = creat(binary, 0755);
|
||||
if (fd < 0) {
|
||||
LOGE("Can't make %s\n", binary);
|
||||
return 1;
|
||||
char path[PATH_MAX];
|
||||
if (!is_package_root_path(argv[0])) {
|
||||
// Copy the program file to temporary storage.
|
||||
binary = argv[0];
|
||||
strcpy(path, binary);
|
||||
}
|
||||
bool ok = mzExtractZipEntryToFile(package, entry, fd);
|
||||
close(fd);
|
||||
else
|
||||
{
|
||||
const ZipArchive *package;
|
||||
if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
|
||||
LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
LOGE("Can't copy %s\n", path);
|
||||
return 1;
|
||||
const ZipEntry *entry = mzFindZipEntry(package, path);
|
||||
if (entry == NULL) {
|
||||
LOGE("Can't find %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unlink(binary); // just to be sure
|
||||
int fd = creat(binary, 0755);
|
||||
if (fd < 0) {
|
||||
LOGE("Can't make %s\n", binary);
|
||||
return 1;
|
||||
}
|
||||
bool ok = mzExtractZipEntryToFile(package, entry, fd);
|
||||
close(fd);
|
||||
|
||||
if (!ok) {
|
||||
LOGE("Can't copy %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a copy of argv to NULL-terminate it, as execv requires
|
||||
@ -391,7 +406,7 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) {
|
||||
return 0;
|
||||
} else {
|
||||
LOGE("Error in %s\n(Status %d)\n", path, status);
|
||||
@ -611,11 +626,13 @@ cmd_write_firmware_image(const char *name, void *cookie,
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
if (remember_firmware_update(type, context.data, context.total_bytes)) {
|
||||
LOGE("Can't store %s image\n", type);
|
||||
free(context.data);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -753,6 +770,119 @@ cmd_done(const char *name, void *cookie, int argc, const char *argv[],
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
|
||||
char* backup_name = NULL;
|
||||
char backup_path[PATH_MAX];
|
||||
switch(argc)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
char backup_path[PATH_MAX];
|
||||
nandroid_generate_timestamp_path(backup_path);
|
||||
backup_name = backup_path;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
backup_name = argv[0];
|
||||
break;
|
||||
default:
|
||||
LOGE("Command %s requires zero or one argument\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return nandroid_backup(backup_name);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
|
||||
int restoreboot = 1;
|
||||
int restoresystem = 1;
|
||||
int restoredata = 1;
|
||||
int restorecache = 1;
|
||||
int restoresdext = 1;
|
||||
int i;
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "noboot") == 0)
|
||||
restoreboot = 0;
|
||||
else if (strcmp(argv[i], "nosystem") == 0)
|
||||
restoresystem = 0;
|
||||
else if (strcmp(argv[i], "nodata") == 0)
|
||||
restoredata = 0;
|
||||
else if (strcmp(argv[i], "nocache") == 0)
|
||||
restorecache = 0;
|
||||
else if (strcmp(argv[i], "nosd-ext") == 0)
|
||||
restorecache = 0;
|
||||
}
|
||||
|
||||
return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache, restoresdext);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_sleep(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
|
||||
if (argc != 1) {
|
||||
LOGE("Command %s requires exactly one argument\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int seconds = atoi(argv[0]);
|
||||
sleep(seconds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_print(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
|
||||
char message[1024];
|
||||
message[0] = NULL;
|
||||
int i;
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
strcat(message, argv[i]);
|
||||
strcat(message, " ");
|
||||
}
|
||||
strcat(message, "\n");
|
||||
|
||||
ui_print(message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
|
||||
if (argc != 1) {
|
||||
LOGE("Command %s requires exactly one argument\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return install_zip(argv[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
@ -1116,6 +1246,21 @@ register_update_commands(RecoveryCommandContext *ctx)
|
||||
ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("sleep", CMD_ARGS_WORDS, cmd_sleep, (void *)ctx);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("print", CMD_ARGS_WORDS, cmd_print, (void *)ctx);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("install_zip", CMD_ARGS_WORDS, cmd_install_zip, (void *)ctx);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
/*
|
||||
* Functions
|
||||
*/
|
||||
|
14
common.h
@ -33,10 +33,13 @@ void ui_clear_key_queue();
|
||||
// so keep the output short and not too cryptic.
|
||||
void ui_print(const char *fmt, ...);
|
||||
|
||||
void ui_reset_text_col();
|
||||
void ui_set_show_text(int value);
|
||||
|
||||
// Display some header text followed by a menu of items, which appears
|
||||
// at the top of the screen (in place of any scrolling ui_print()
|
||||
// output, if necessary).
|
||||
void ui_start_menu(char** headers, char** items);
|
||||
int ui_start_menu(char** headers, char** items);
|
||||
// Set the menu highlight to the given index, and return it (capped to
|
||||
// the range [0..numitems).
|
||||
int ui_menu_select(int sel);
|
||||
@ -44,10 +47,12 @@ int ui_menu_select(int sel);
|
||||
// statements will be displayed.
|
||||
void ui_end_menu();
|
||||
|
||||
int ui_get_showing_back_button();
|
||||
void ui_set_showing_back_button(int showBackButton);
|
||||
|
||||
// Set the icon (normally the only thing visible besides the progress bar).
|
||||
enum {
|
||||
BACKGROUND_ICON_NONE,
|
||||
BACKGROUND_ICON_UNPACKING,
|
||||
BACKGROUND_ICON_INSTALLING,
|
||||
BACKGROUND_ICON_ERROR,
|
||||
BACKGROUND_ICON_FIRMWARE_INSTALLING,
|
||||
@ -69,7 +74,7 @@ void ui_set_progress(float fraction); // 0.0 - 1.0 within the defined scope
|
||||
|
||||
// Default allocation of progress bar segments to operations
|
||||
static const int VERIFICATION_PROGRESS_TIME = 60;
|
||||
static const float VERIFICATION_PROGRESS_FRACTION = 0.5;
|
||||
static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
|
||||
static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
|
||||
static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
|
||||
|
||||
@ -91,4 +96,7 @@ void ui_reset_progress();
|
||||
#define LOGD(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define EXPAND(x) STRINGIFY(x)
|
||||
|
||||
#endif // RECOVERY_COMMON_H
|
||||
|
96
default_recovery_ui.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 <linux/input.h>
|
||||
|
||||
#include "recovery_ui.h"
|
||||
#include "common.h"
|
||||
#include "extendedcommands.h"
|
||||
|
||||
char* MENU_HEADERS[] = { NULL };
|
||||
|
||||
char* MENU_ITEMS[] = { "reboot system now",
|
||||
"apply sdcard:update.zip",
|
||||
"wipe data/factory reset",
|
||||
"wipe cache partition",
|
||||
"install zip from sdcard",
|
||||
"backup and restore",
|
||||
"mounts and storage",
|
||||
"advanced",
|
||||
NULL };
|
||||
|
||||
int device_toggle_display(volatile char* key_pressed, int key_code) {
|
||||
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
|
||||
if (alt && key_code == KEY_L)
|
||||
return 1;
|
||||
// allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
|
||||
if (ui_get_showing_back_button()) {
|
||||
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
|
||||
}
|
||||
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END);
|
||||
}
|
||||
|
||||
int device_reboot_now(volatile char* key_pressed, int key_code) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_handle_key(int key_code, int visible) {
|
||||
if (visible) {
|
||||
switch (key_code) {
|
||||
case KEY_CAPSLOCK:
|
||||
case KEY_DOWN:
|
||||
case KEY_VOLUMEDOWN:
|
||||
return HIGHLIGHT_DOWN;
|
||||
|
||||
case KEY_LEFTSHIFT:
|
||||
case KEY_UP:
|
||||
case KEY_VOLUMEUP:
|
||||
return HIGHLIGHT_UP;
|
||||
|
||||
case KEY_POWER:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display())
|
||||
return GO_BACK;
|
||||
break;
|
||||
case KEY_LEFTBRACE:
|
||||
case KEY_ENTER:
|
||||
case BTN_MOUSE:
|
||||
case KEY_CENTER:
|
||||
case KEY_CAMERA:
|
||||
case KEY_F21:
|
||||
case KEY_SEND:
|
||||
return SELECT_ITEM;
|
||||
|
||||
case KEY_END:
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_BACK:
|
||||
if (!get_allow_toggle_display())
|
||||
return GO_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ACTION;
|
||||
}
|
||||
|
||||
int device_perform_action(int which) {
|
||||
return which;
|
||||
}
|
||||
|
||||
int device_wipe_data() {
|
||||
return 0;
|
||||
}
|
39
edify/Android.mk
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright 2009 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
edify_src_files := \
|
||||
lexer.l \
|
||||
parser.y \
|
||||
expr.c
|
||||
|
||||
# "-x c" forces the lex/yacc files to be compiled as c;
|
||||
# the build system otherwise forces them to be c++.
|
||||
edify_cflags := -x c
|
||||
|
||||
#
|
||||
# Build the host-side command line tool
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(edify_src_files) \
|
||||
main.c
|
||||
|
||||
LOCAL_CFLAGS := $(edify_cflags) -g -O0
|
||||
LOCAL_MODULE := edify
|
||||
LOCAL_YACCFLAGS := -v
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
#
|
||||
# Build the device-side library
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(edify_src_files)
|
||||
|
||||
LOCAL_CFLAGS := $(edify_cflags)
|
||||
LOCAL_MODULE := libedify
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
108
edify/README
Normal file
@ -0,0 +1,108 @@
|
||||
Update scripts (from donut onwards) are written in a new little
|
||||
scripting language ("edify") that is superficially somewhat similar to
|
||||
the old one ("amend"). This is a brief overview of the new language.
|
||||
|
||||
- The entire script is a single expression.
|
||||
|
||||
- All expressions are string-valued.
|
||||
|
||||
- String literals appear in double quotes. \n, \t, \", and \\ are
|
||||
understood, as are hexadecimal escapes like \x4a.
|
||||
|
||||
- String literals consisting of only letters, numbers, colons,
|
||||
underscores, slashes, and periods don't need to be in double quotes.
|
||||
|
||||
- The following words are reserved:
|
||||
|
||||
if then else endif
|
||||
|
||||
They have special meaning when unquoted. (In quotes, they are just
|
||||
string literals.)
|
||||
|
||||
- When used as a boolean, the empty string is "false" and all other
|
||||
strings are "true".
|
||||
|
||||
- All functions are actually macros (in the Lisp sense); the body of
|
||||
the function can control which (if any) of the arguments are
|
||||
evaluated. This means that functions can act as control
|
||||
structures.
|
||||
|
||||
- Operators (like "&&" and "||") are just syntactic sugar for builtin
|
||||
functions, so they can act as control structures as well.
|
||||
|
||||
- ";" is a binary operator; evaluating it just means to first evaluate
|
||||
the left side, then the right. It can also appear after any
|
||||
expression.
|
||||
|
||||
- Comments start with "#" and run to the end of the line.
|
||||
|
||||
|
||||
|
||||
Some examples:
|
||||
|
||||
- There's no distinction between quoted and unquoted strings; the
|
||||
quotes are only needed if you want characters like whitespace to
|
||||
appear in the string. The following expressions all evaluate to the
|
||||
same string.
|
||||
|
||||
"a b"
|
||||
a + " " + b
|
||||
"a" + " " + "b"
|
||||
"a\x20b"
|
||||
a + "\x20b"
|
||||
concat(a, " ", "b")
|
||||
"concat"(a, " ", "b")
|
||||
|
||||
As shown in the last example, function names are just strings,
|
||||
too. They must be string *literals*, however. This is not legal:
|
||||
|
||||
("con" + "cat")(a, " ", b) # syntax error!
|
||||
|
||||
|
||||
- The ifelse() builtin takes three arguments: it evaluates exactly
|
||||
one of the second and third, depending on whether the first one is
|
||||
true. There is also some syntactic sugar to make expressions that
|
||||
look like if/else statements:
|
||||
|
||||
# these are all equivalent
|
||||
ifelse(something(), "yes", "no")
|
||||
if something() then yes else no endif
|
||||
if something() then "yes" else "no" endif
|
||||
|
||||
The else part is optional.
|
||||
|
||||
if something() then "yes" endif # if something() is false,
|
||||
# evaluates to false
|
||||
|
||||
ifelse(condition(), "", abort()) # abort() only called if
|
||||
# condition() is false
|
||||
|
||||
The last example is equivalent to:
|
||||
|
||||
assert(condition())
|
||||
|
||||
|
||||
- The && and || operators can be used similarly; they evaluate their
|
||||
second argument only if it's needed to determine the truth of the
|
||||
expression. Their value is the value of the last-evaluated
|
||||
argument:
|
||||
|
||||
file_exists("/data/system/bad") && delete("/data/system/bad")
|
||||
|
||||
file_exists("/data/system/missing") || create("/data/system/missing")
|
||||
|
||||
get_it() || "xxx" # returns value of get_it() if that value is
|
||||
# true, otherwise returns "xxx"
|
||||
|
||||
|
||||
- The purpose of ";" is to simulate imperative statements, of course,
|
||||
but the operator can be used anywhere. Its value is the value of
|
||||
its right side:
|
||||
|
||||
concat(a;b;c, d, e;f) # evaluates to "cdf"
|
||||
|
||||
A more useful example might be something like:
|
||||
|
||||
ifelse(condition(),
|
||||
(first_step(); second_step();), # second ; is optional
|
||||
alternative_procedure())
|
432
edify/expr.c
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "expr.h"
|
||||
|
||||
// Functions should:
|
||||
//
|
||||
// - return a malloc()'d string
|
||||
// - if Evaluate() on any argument returns NULL, return NULL.
|
||||
|
||||
int BooleanString(const char* s) {
|
||||
return s[0] != '\0';
|
||||
}
|
||||
|
||||
char* Evaluate(State* state, Expr* expr) {
|
||||
return expr->fn(expr->name, state, expr->argc, expr->argv);
|
||||
}
|
||||
|
||||
char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc == 0) {
|
||||
return strdup("");
|
||||
}
|
||||
char** strings = malloc(argc * sizeof(char*));
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strings[i] = NULL;
|
||||
}
|
||||
char* result = NULL;
|
||||
int length = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strings[i] = Evaluate(state, argv[i]);
|
||||
if (strings[i] == NULL) {
|
||||
goto done;
|
||||
}
|
||||
length += strlen(strings[i]);
|
||||
}
|
||||
|
||||
result = malloc(length+1);
|
||||
int p = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strcpy(result+p, strings[i]);
|
||||
p += strlen(strings[i]);
|
||||
}
|
||||
result[p] = '\0';
|
||||
|
||||
done:
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(strings[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup("ifelse expects 2 or 3 arguments");
|
||||
return NULL;
|
||||
}
|
||||
char* cond = Evaluate(state, argv[0]);
|
||||
if (cond == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BooleanString(cond) == true) {
|
||||
free(cond);
|
||||
return Evaluate(state, argv[1]);
|
||||
} else {
|
||||
if (argc == 3) {
|
||||
free(cond);
|
||||
return Evaluate(state, argv[2]);
|
||||
} else {
|
||||
return cond;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* msg = NULL;
|
||||
if (argc > 0) {
|
||||
msg = Evaluate(state, argv[0]);
|
||||
}
|
||||
free(state->errmsg);
|
||||
if (msg) {
|
||||
state->errmsg = msg;
|
||||
} else {
|
||||
state->errmsg = strdup("called abort()");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char* v = Evaluate(state, argv[i]);
|
||||
if (v == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int b = BooleanString(v);
|
||||
free(v);
|
||||
if (!b) {
|
||||
int prefix_len;
|
||||
int len = argv[i]->end - argv[i]->start;
|
||||
char* err_src = malloc(len + 20);
|
||||
strcpy(err_src, "assert failed: ");
|
||||
prefix_len = strlen(err_src);
|
||||
memcpy(err_src + prefix_len, state->script + argv[i]->start, len);
|
||||
err_src[prefix_len + len] = '\0';
|
||||
free(state->errmsg);
|
||||
state->errmsg = err_src;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* val = Evaluate(state, argv[0]);
|
||||
if (val == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int v = strtol(val, NULL, 10);
|
||||
sleep(v);
|
||||
return val;
|
||||
}
|
||||
|
||||
char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char* v = Evaluate(state, argv[i]);
|
||||
if (v == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
fputs(v, stdout);
|
||||
free(v);
|
||||
}
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
char* LogicalAndFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
if (BooleanString(left) == true) {
|
||||
free(left);
|
||||
return Evaluate(state, argv[1]);
|
||||
} else {
|
||||
return left;
|
||||
}
|
||||
}
|
||||
|
||||
char* LogicalOrFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
if (BooleanString(left) == false) {
|
||||
free(left);
|
||||
return Evaluate(state, argv[1]);
|
||||
} else {
|
||||
return left;
|
||||
}
|
||||
}
|
||||
|
||||
char* LogicalNotFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* val = Evaluate(state, argv[0]);
|
||||
if (val == NULL) return NULL;
|
||||
bool bv = BooleanString(val);
|
||||
free(val);
|
||||
if (bv) {
|
||||
return strdup("");
|
||||
} else {
|
||||
return strdup("t");
|
||||
}
|
||||
}
|
||||
|
||||
char* SubstringFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* needle = Evaluate(state, argv[0]);
|
||||
if (needle == NULL) return NULL;
|
||||
char* haystack = Evaluate(state, argv[1]);
|
||||
if (haystack == NULL) {
|
||||
free(needle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* result = strdup(strstr(haystack, needle) ? "t" : "");
|
||||
free(needle);
|
||||
free(haystack);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
char* right = Evaluate(state, argv[1]);
|
||||
if (right == NULL) {
|
||||
free(left);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
|
||||
free(left);
|
||||
free(right);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
char* right = Evaluate(state, argv[1]);
|
||||
if (right == NULL) {
|
||||
free(left);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
|
||||
free(left);
|
||||
free(right);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
free(left);
|
||||
return Evaluate(state, argv[1]);
|
||||
}
|
||||
|
||||
char* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup("less_than_int expects 2 arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* left;
|
||||
char* right;
|
||||
if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL;
|
||||
|
||||
bool result = false;
|
||||
char* end;
|
||||
|
||||
long l_int = strtol(left, &end, 10);
|
||||
if (left[0] == '\0' || *end != '\0') {
|
||||
fprintf(stderr, "[%s] is not an int\n", left);
|
||||
goto done;
|
||||
}
|
||||
|
||||
long r_int = strtol(right, &end, 10);
|
||||
if (right[0] == '\0' || *end != '\0') {
|
||||
fprintf(stderr, "[%s] is not an int\n", right);
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = l_int < r_int;
|
||||
|
||||
done:
|
||||
free(left);
|
||||
free(right);
|
||||
return strdup(result ? "t" : "");
|
||||
}
|
||||
|
||||
char* GreaterThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup("greater_than_int expects 2 arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Expr* temp[2];
|
||||
temp[0] = argv[1];
|
||||
temp[1] = argv[0];
|
||||
|
||||
return LessThanIntFn(name, state, 2, temp);
|
||||
}
|
||||
|
||||
char* Literal(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return strdup(name);
|
||||
}
|
||||
|
||||
Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
Expr* e = malloc(sizeof(Expr));
|
||||
e->fn = fn;
|
||||
e->name = "(operator)";
|
||||
e->argc = count;
|
||||
e->argv = malloc(count * sizeof(Expr*));
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
e->argv[i] = va_arg(v, Expr*);
|
||||
}
|
||||
va_end(v);
|
||||
e->start = loc.start;
|
||||
e->end = loc.end;
|
||||
return e;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// the function table
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static int fn_entries = 0;
|
||||
static int fn_size = 0;
|
||||
NamedFunction* fn_table = NULL;
|
||||
|
||||
void RegisterFunction(const char* name, Function fn) {
|
||||
if (fn_entries >= fn_size) {
|
||||
fn_size = fn_size*2 + 1;
|
||||
fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
|
||||
}
|
||||
fn_table[fn_entries].name = name;
|
||||
fn_table[fn_entries].fn = fn;
|
||||
++fn_entries;
|
||||
}
|
||||
|
||||
static int fn_entry_compare(const void* a, const void* b) {
|
||||
const char* na = ((const NamedFunction*)a)->name;
|
||||
const char* nb = ((const NamedFunction*)b)->name;
|
||||
return strcmp(na, nb);
|
||||
}
|
||||
|
||||
void FinishRegistration() {
|
||||
qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
|
||||
}
|
||||
|
||||
Function FindFunction(const char* name) {
|
||||
NamedFunction key;
|
||||
key.name = name;
|
||||
NamedFunction* nf = bsearch(&key, fn_table, fn_entries,
|
||||
sizeof(NamedFunction), fn_entry_compare);
|
||||
if (nf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return nf->fn;
|
||||
}
|
||||
|
||||
void RegisterBuiltins() {
|
||||
RegisterFunction("ifelse", IfElseFn);
|
||||
RegisterFunction("abort", AbortFn);
|
||||
RegisterFunction("assert", AssertFn);
|
||||
RegisterFunction("concat", ConcatFn);
|
||||
RegisterFunction("is_substring", SubstringFn);
|
||||
RegisterFunction("stdout", StdoutFn);
|
||||
RegisterFunction("sleep", SleepFn);
|
||||
|
||||
RegisterFunction("less_than_int", LessThanIntFn);
|
||||
RegisterFunction("greater_than_int", GreaterThanIntFn);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// convenience methods for functions
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' char* (the ... is
|
||||
// zero or more char** to put them in). If any expression evaluates
|
||||
// to NULL, free the rest and return -1. Return 0 on success.
|
||||
int ReadArgs(State* state, Expr* argv[], int count, ...) {
|
||||
char** args = malloc(count * sizeof(char*));
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
args[i] = Evaluate(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
va_end(v);
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
free(args[j]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
*(va_arg(v, char**)) = args[i];
|
||||
}
|
||||
va_end(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of char*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// strings it contains.
|
||||
char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
|
||||
char** args = (char**)malloc(argc * sizeof(char*));
|
||||
int i = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
args[i] = Evaluate(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
free(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns NULL.
|
||||
char* ErrorAbort(State* state, char* format, ...) {
|
||||
char* buffer = malloc(4096);
|
||||
va_list v;
|
||||
va_start(v, format);
|
||||
vsnprintf(buffer, 4096, format, v);
|
||||
va_end(v);
|
||||
free(state->errmsg);
|
||||
state->errmsg = buffer;
|
||||
return NULL;
|
||||
}
|
126
edify/expr.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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 _EXPRESSION_H
|
||||
#define _EXPRESSION_H
|
||||
|
||||
#include "yydefs.h"
|
||||
|
||||
#define MAX_STRING_LEN 1024
|
||||
|
||||
typedef struct Expr Expr;
|
||||
|
||||
typedef struct {
|
||||
// Optional pointer to app-specific data; the core of edify never
|
||||
// uses this value.
|
||||
void* cookie;
|
||||
|
||||
// The source of the original script. Must be NULL-terminated,
|
||||
// and in writable memory (Evaluate may make temporary changes to
|
||||
// it but will restore it when done).
|
||||
char* script;
|
||||
|
||||
// The error message (if any) returned if the evaluation aborts.
|
||||
// Should be NULL initially, will be either NULL or a malloc'd
|
||||
// pointer after Evaluate() returns.
|
||||
char* errmsg;
|
||||
} State;
|
||||
|
||||
typedef char* (*Function)(const char* name, State* state,
|
||||
int argc, Expr* argv[]);
|
||||
|
||||
struct Expr {
|
||||
Function fn;
|
||||
char* name;
|
||||
int argc;
|
||||
Expr** argv;
|
||||
int start, end;
|
||||
};
|
||||
|
||||
char* Evaluate(State* state, Expr* expr);
|
||||
|
||||
// Glue to make an Expr out of a literal.
|
||||
char* Literal(const char* name, State* state, int argc, Expr* argv[]);
|
||||
|
||||
// Functions corresponding to various syntactic sugar operators.
|
||||
// ("concat" is also available as a builtin function, to concatenate
|
||||
// more than two strings.)
|
||||
char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
|
||||
// Convenience function for building expressions with a fixed number
|
||||
// of arguments.
|
||||
Expr* Build(Function fn, YYLTYPE loc, int count, ...);
|
||||
|
||||
// Global builtins, registered by RegisterBuiltins().
|
||||
char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
|
||||
|
||||
// For setting and getting the global error string (when returning
|
||||
// NULL from a function).
|
||||
void SetError(const char* message); // makes a copy
|
||||
const char* GetError(); // retains ownership
|
||||
void ClearError();
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
Function fn;
|
||||
} NamedFunction;
|
||||
|
||||
// Register a new function. The same Function may be registered under
|
||||
// multiple names, but a given name should only be used once.
|
||||
void RegisterFunction(const char* name, Function fn);
|
||||
|
||||
// Register all the builtins.
|
||||
void RegisterBuiltins();
|
||||
|
||||
// Call this after all calls to RegisterFunction() but before parsing
|
||||
// any scripts to finish building the function table.
|
||||
void FinishRegistration();
|
||||
|
||||
// Find the Function for a given name; return NULL if no such function
|
||||
// exists.
|
||||
Function FindFunction(const char* name);
|
||||
|
||||
|
||||
// --- convenience functions for use in functions ---
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' char* (the ... is
|
||||
// zero or more char** to put them in). If any expression evaluates
|
||||
// to NULL, free the rest and return -1. Return 0 on success.
|
||||
int ReadArgs(State* state, Expr* argv[], int count, ...);
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of char*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// strings it contains.
|
||||
char** ReadVarArgs(State* state, int argc, Expr* argv[]);
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns NULL.
|
||||
char* ErrorAbort(State* state, char* format, ...);
|
||||
|
||||
|
||||
#endif // _EXPRESSION_H
|
110
edify/lexer.l
Normal file
@ -0,0 +1,110 @@
|
||||
%{
|
||||
/*
|
||||
* 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 "expr.h"
|
||||
#include "yydefs.h"
|
||||
#include "parser.h"
|
||||
|
||||
int gLine = 1;
|
||||
int gColumn = 1;
|
||||
int gPos = 0;
|
||||
|
||||
// TODO: enforce MAX_STRING_LEN during lexing
|
||||
char string_buffer[MAX_STRING_LEN];
|
||||
char* string_pos;
|
||||
|
||||
#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \
|
||||
gColumn+=yyleng; gPos+=yyleng;} while(0)
|
||||
|
||||
%}
|
||||
|
||||
%x STR
|
||||
|
||||
%option noyywrap
|
||||
|
||||
%%
|
||||
|
||||
|
||||
\" {
|
||||
BEGIN(STR);
|
||||
string_pos = string_buffer;
|
||||
yylloc.start = gPos;
|
||||
++gColumn;
|
||||
++gPos;
|
||||
}
|
||||
|
||||
<STR>{
|
||||
\" {
|
||||
++gColumn;
|
||||
++gPos;
|
||||
BEGIN(INITIAL);
|
||||
*string_pos = '\0';
|
||||
yylval.str = strdup(string_buffer);
|
||||
yylloc.end = gPos;
|
||||
return STRING;
|
||||
}
|
||||
|
||||
\\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; }
|
||||
\\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; }
|
||||
\\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; }
|
||||
\\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; }
|
||||
|
||||
\\x[0-9a-fA-F]{2} {
|
||||
gColumn += yyleng;
|
||||
gPos += yyleng;
|
||||
int val;
|
||||
sscanf(yytext+2, "%x", &val);
|
||||
*string_pos++ = val;
|
||||
}
|
||||
|
||||
\n {
|
||||
++gLine;
|
||||
++gPos;
|
||||
gColumn = 1;
|
||||
*string_pos++ = yytext[0];
|
||||
}
|
||||
|
||||
. {
|
||||
++gColumn;
|
||||
++gPos;
|
||||
*string_pos++ = yytext[0];
|
||||
}
|
||||
}
|
||||
|
||||
if ADVANCE; return IF;
|
||||
then ADVANCE; return THEN;
|
||||
else ADVANCE; return ELSE;
|
||||
endif ADVANCE; return ENDIF;
|
||||
|
||||
[a-zA-Z0-9_:/.]+ {
|
||||
ADVANCE;
|
||||
yylval.str = strdup(yytext);
|
||||
return STRING;
|
||||
}
|
||||
|
||||
\&\& ADVANCE; return AND;
|
||||
\|\| ADVANCE; return OR;
|
||||
== ADVANCE; return EQ;
|
||||
!= ADVANCE; return NE;
|
||||
|
||||
[+(),!;] ADVANCE; return yytext[0];
|
||||
|
||||
[ \t]+ ADVANCE;
|
||||
|
||||
(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1;
|
||||
|
||||
. return BAD;
|
213
edify/main.c
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "expr.h"
|
||||
#include "parser.h"
|
||||
|
||||
extern int yyparse(Expr** root, int* error_count);
|
||||
|
||||
int expect(const char* expr_str, const char* expected, int* errors) {
|
||||
Expr* e;
|
||||
int error;
|
||||
char* result;
|
||||
|
||||
printf(".");
|
||||
|
||||
yy_scan_string(expr_str);
|
||||
int error_count = 0;
|
||||
error = yyparse(&e, &error_count);
|
||||
if (error > 0 || error_count > 0) {
|
||||
fprintf(stderr, "error parsing \"%s\" (%d errors)\n",
|
||||
expr_str, error_count);
|
||||
++*errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
State state;
|
||||
state.cookie = NULL;
|
||||
state.script = expr_str;
|
||||
state.errmsg = NULL;
|
||||
|
||||
result = Evaluate(&state, e);
|
||||
free(state.errmsg);
|
||||
if (result == NULL && expected != NULL) {
|
||||
fprintf(stderr, "error evaluating \"%s\"\n", expr_str);
|
||||
++*errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (result == NULL && expected == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(result, expected) != 0) {
|
||||
fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n",
|
||||
expr_str, expected, result);
|
||||
++*errors;
|
||||
free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int test() {
|
||||
int errors = 0;
|
||||
|
||||
expect("a", "a", &errors);
|
||||
expect("\"a\"", "a", &errors);
|
||||
expect("\"\\x61\"", "a", &errors);
|
||||
expect("# this is a comment\n"
|
||||
" a\n"
|
||||
" \n",
|
||||
"a", &errors);
|
||||
|
||||
|
||||
// sequence operator
|
||||
expect("a; b; c", "c", &errors);
|
||||
|
||||
// string concat operator
|
||||
expect("a + b", "ab", &errors);
|
||||
expect("a + \n \"b\"", "ab", &errors);
|
||||
expect("a + b +\nc\n", "abc", &errors);
|
||||
|
||||
// string concat function
|
||||
expect("concat(a, b)", "ab", &errors);
|
||||
expect("concat(a,\n \"b\")", "ab", &errors);
|
||||
expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
|
||||
expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
|
||||
|
||||
// logical and
|
||||
expect("a && b", "b", &errors);
|
||||
expect("a && \"\"", "", &errors);
|
||||
expect("\"\" && b", "", &errors);
|
||||
expect("\"\" && \"\"", "", &errors);
|
||||
expect("\"\" && abort()", "", &errors); // test short-circuiting
|
||||
expect("t && abort()", NULL, &errors);
|
||||
|
||||
// logical or
|
||||
expect("a || b", "a", &errors);
|
||||
expect("a || \"\"", "a", &errors);
|
||||
expect("\"\" || b", "b", &errors);
|
||||
expect("\"\" || \"\"", "", &errors);
|
||||
expect("a || abort()", "a", &errors); // test short-circuiting
|
||||
expect("\"\" || abort()", NULL, &errors);
|
||||
|
||||
// logical not
|
||||
expect("!a", "", &errors);
|
||||
expect("! \"\"", "t", &errors);
|
||||
expect("!!a", "t", &errors);
|
||||
|
||||
// precedence
|
||||
expect("\"\" == \"\" && b", "b", &errors);
|
||||
expect("a + b == ab", "t", &errors);
|
||||
expect("ab == a + b", "t", &errors);
|
||||
expect("a + (b == ab)", "a", &errors);
|
||||
expect("(ab == a) + b", "b", &errors);
|
||||
|
||||
// substring function
|
||||
expect("is_substring(cad, abracadabra)", "t", &errors);
|
||||
expect("is_substring(abrac, abracadabra)", "t", &errors);
|
||||
expect("is_substring(dabra, abracadabra)", "t", &errors);
|
||||
expect("is_substring(cad, abracxadabra)", "", &errors);
|
||||
expect("is_substring(abrac, axbracadabra)", "", &errors);
|
||||
expect("is_substring(dabra, abracadabrxa)", "", &errors);
|
||||
|
||||
// ifelse function
|
||||
expect("ifelse(t, yes, no)", "yes", &errors);
|
||||
expect("ifelse(!t, yes, no)", "no", &errors);
|
||||
expect("ifelse(t, yes, abort())", "yes", &errors);
|
||||
expect("ifelse(!t, abort(), no)", "no", &errors);
|
||||
|
||||
// if "statements"
|
||||
expect("if t then yes else no endif", "yes", &errors);
|
||||
expect("if \"\" then yes else no endif", "no", &errors);
|
||||
expect("if \"\" then yes endif", "", &errors);
|
||||
expect("if \"\"; t then yes endif", "yes", &errors);
|
||||
|
||||
// numeric comparisons
|
||||
expect("less_than_int(3, 14)", "t", &errors);
|
||||
expect("less_than_int(14, 3)", "", &errors);
|
||||
expect("less_than_int(x, 3)", "", &errors);
|
||||
expect("less_than_int(3, x)", "", &errors);
|
||||
expect("greater_than_int(3, 14)", "", &errors);
|
||||
expect("greater_than_int(14, 3)", "t", &errors);
|
||||
expect("greater_than_int(x, 3)", "", &errors);
|
||||
expect("greater_than_int(3, x)", "", &errors);
|
||||
|
||||
printf("\n");
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
void ExprDump(int depth, Expr* n, char* script) {
|
||||
printf("%*s", depth*2, "");
|
||||
char temp = script[n->end];
|
||||
script[n->end] = '\0';
|
||||
printf("%s %p (%d-%d) \"%s\"\n",
|
||||
n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
|
||||
script+n->start);
|
||||
script[n->end] = temp;
|
||||
int i;
|
||||
for (i = 0; i < n->argc; ++i) {
|
||||
ExprDump(depth+1, n->argv[i], script);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
RegisterBuiltins();
|
||||
FinishRegistration();
|
||||
|
||||
if (argc == 1) {
|
||||
return test() != 0;
|
||||
}
|
||||
|
||||
FILE* f = fopen(argv[1], "r");
|
||||
char buffer[8192];
|
||||
int size = fread(buffer, 1, 8191, f);
|
||||
fclose(f);
|
||||
buffer[size] = '\0';
|
||||
|
||||
Expr* root;
|
||||
int error_count = 0;
|
||||
yy_scan_bytes(buffer, size);
|
||||
int error = yyparse(&root, &error_count);
|
||||
printf("parse returned %d; %d errors encountered\n", error, error_count);
|
||||
if (error == 0 || error_count > 0) {
|
||||
|
||||
ExprDump(0, root, buffer);
|
||||
|
||||
State state;
|
||||
state.cookie = NULL;
|
||||
state.script = buffer;
|
||||
state.errmsg = NULL;
|
||||
|
||||
char* result = Evaluate(&state, root);
|
||||
if (result == NULL) {
|
||||
printf("result was NULL, message is: %s\n",
|
||||
(state.errmsg == NULL ? "(NULL)" : state.errmsg));
|
||||
free(state.errmsg);
|
||||
} else {
|
||||
printf("result is [%s]\n", result);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
130
edify/parser.y
Normal file
@ -0,0 +1,130 @@
|
||||
%{
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "expr.h"
|
||||
#include "yydefs.h"
|
||||
#include "parser.h"
|
||||
|
||||
extern int gLine;
|
||||
extern int gColumn;
|
||||
|
||||
void yyerror(Expr** root, int* error_count, const char* s);
|
||||
int yyparse(Expr** root, int* error_count);
|
||||
|
||||
%}
|
||||
|
||||
%locations
|
||||
|
||||
%union {
|
||||
char* str;
|
||||
Expr* expr;
|
||||
struct {
|
||||
int argc;
|
||||
Expr** argv;
|
||||
} args;
|
||||
}
|
||||
|
||||
%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
|
||||
%token <str> STRING BAD
|
||||
%type <expr> expr
|
||||
%type <args> arglist
|
||||
|
||||
%parse-param {Expr** root}
|
||||
%parse-param {int* error_count}
|
||||
%error-verbose
|
||||
|
||||
/* declarations in increasing order of precedence */
|
||||
%left ';'
|
||||
%left ','
|
||||
%left OR
|
||||
%left AND
|
||||
%left EQ NE
|
||||
%left '+'
|
||||
%right '!'
|
||||
|
||||
%%
|
||||
|
||||
input: expr { *root = $1; }
|
||||
;
|
||||
|
||||
expr: STRING {
|
||||
$$ = malloc(sizeof(Expr));
|
||||
$$->fn = Literal;
|
||||
$$->name = $1;
|
||||
$$->argc = 0;
|
||||
$$->argv = NULL;
|
||||
$$->start = @$.start;
|
||||
$$->end = @$.end;
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
|
||||
| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
|
||||
| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); }
|
||||
| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; }
|
||||
| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); }
|
||||
| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); }
|
||||
| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); }
|
||||
| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); }
|
||||
| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); }
|
||||
| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); }
|
||||
| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
|
||||
| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
|
||||
| STRING '(' arglist ')' {
|
||||
$$ = malloc(sizeof(Expr));
|
||||
$$->fn = FindFunction($1);
|
||||
if ($$->fn == NULL) {
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
|
||||
yyerror(root, error_count, buffer);
|
||||
YYERROR;
|
||||
}
|
||||
$$->name = $1;
|
||||
$$->argc = $3.argc;
|
||||
$$->argv = $3.argv;
|
||||
$$->start = @$.start;
|
||||
$$->end = @$.end;
|
||||
}
|
||||
;
|
||||
|
||||
arglist: /* empty */ {
|
||||
$$.argc = 0;
|
||||
$$.argv = NULL;
|
||||
}
|
||||
| expr {
|
||||
$$.argc = 1;
|
||||
$$.argv = malloc(sizeof(Expr*));
|
||||
$$.argv[0] = $1;
|
||||
}
|
||||
| arglist ',' expr {
|
||||
$$.argc = $1.argc + 1;
|
||||
$$.argv = realloc($$.argv, $$.argc * sizeof(Expr*));
|
||||
$$.argv[$$.argc-1] = $3;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerror(Expr** root, int* error_count, const char* s) {
|
||||
if (strlen(s) == 0) {
|
||||
s = "syntax error";
|
||||
}
|
||||
printf("line %d col %d: %s\n", gLine, gColumn, s);
|
||||
++*error_count;
|
||||
}
|
36
edify/yydefs.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 _YYDEFS_H_
|
||||
#define _YYDEFS_H_
|
||||
|
||||
#define YYLTYPE YYLTYPE
|
||||
typedef struct {
|
||||
int start, end;
|
||||
} YYLTYPE;
|
||||
|
||||
#define YYLLOC_DEFAULT(Current, Rhs, N) \
|
||||
do { \
|
||||
if (N) { \
|
||||
(Current).start = YYRHSLOC(Rhs, 1).start; \
|
||||
(Current).end = YYRHSLOC(Rhs, N).end; \
|
||||
} else { \
|
||||
(Current).start = YYRHSLOC(Rhs, 0).start; \
|
||||
(Current).end = YYRHSLOC(Rhs, 0).end; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
962
extendedcommands.c
Normal file
@ -0,0 +1,962 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "bootloader.h"
|
||||
#include "common.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "firmware.h"
|
||||
#include "install.h"
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/DirUtil.h"
|
||||
#include "roots.h"
|
||||
#include "recovery_ui.h"
|
||||
|
||||
#include "commands.h"
|
||||
#include "amend/amend.h"
|
||||
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "mtdutils/dump_image.h"
|
||||
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
|
||||
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
|
||||
|
||||
#include "extendedcommands.h"
|
||||
#include "nandroid.h"
|
||||
|
||||
int signature_check_enabled = 1;
|
||||
int script_assert_enabled = 1;
|
||||
static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
|
||||
|
||||
void
|
||||
toggle_signature_check()
|
||||
{
|
||||
signature_check_enabled = !signature_check_enabled;
|
||||
ui_print("Signature Check: %s\n", signature_check_enabled ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
void toggle_script_asserts()
|
||||
{
|
||||
script_assert_enabled = !script_assert_enabled;
|
||||
ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
int install_zip(const char* packagefilepath)
|
||||
{
|
||||
ui_print("\n-- Installing: %s\n", packagefilepath);
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
set_sdcard_update_bootloader_message();
|
||||
#endif
|
||||
int status = install_package(packagefilepath);
|
||||
ui_reset_progress();
|
||||
if (status != INSTALL_SUCCESS) {
|
||||
ui_set_background(BACKGROUND_ICON_ERROR);
|
||||
ui_print("Installation aborted.\n");
|
||||
return 1;
|
||||
}
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
if (firmware_update_pending()) {
|
||||
ui_print("\nReboot via menu to complete\ninstallation.\n");
|
||||
}
|
||||
#endif
|
||||
ui_set_background(BACKGROUND_ICON_NONE);
|
||||
ui_print("\nInstall from sdcard complete.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* INSTALL_MENU_ITEMS[] = { "apply sdcard:update.zip",
|
||||
"choose zip from sdcard",
|
||||
"toggle signature verification",
|
||||
"toggle script asserts",
|
||||
NULL };
|
||||
#define ITEM_APPLY_SDCARD 0
|
||||
#define ITEM_CHOOSE_ZIP 1
|
||||
#define ITEM_SIG_CHECK 2
|
||||
#define ITEM_ASSERTS 3
|
||||
|
||||
void show_install_update_menu()
|
||||
{
|
||||
static char* headers[] = { "Apply update from .zip file on SD card",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
for (;;)
|
||||
{
|
||||
int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0);
|
||||
switch (chosen_item)
|
||||
{
|
||||
case ITEM_ASSERTS:
|
||||
toggle_script_asserts();
|
||||
break;
|
||||
case ITEM_SIG_CHECK:
|
||||
toggle_signature_check();
|
||||
break;
|
||||
case ITEM_APPLY_SDCARD:
|
||||
{
|
||||
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
|
||||
install_zip(SDCARD_PACKAGE_FILE);
|
||||
break;
|
||||
}
|
||||
case ITEM_CHOOSE_ZIP:
|
||||
show_choose_zip_menu();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void free_string_array(char** array)
|
||||
{
|
||||
if (array == NULL)
|
||||
return;
|
||||
char* cursor = array[0];
|
||||
int i = 0;
|
||||
while (cursor != NULL)
|
||||
{
|
||||
free(cursor);
|
||||
cursor = array[++i];
|
||||
}
|
||||
free(array);
|
||||
}
|
||||
|
||||
char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int* numFiles)
|
||||
{
|
||||
char path[PATH_MAX] = "";
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
int total = 0;
|
||||
int i;
|
||||
char** files = NULL;
|
||||
int pass;
|
||||
*numFiles = 0;
|
||||
int dirLen = strlen(directory);
|
||||
|
||||
dir = opendir(directory);
|
||||
if (dir == NULL) {
|
||||
ui_print("Couldn't open directory.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int extension_length = 0;
|
||||
if (fileExtensionOrDirectory != NULL)
|
||||
extension_length = strlen(fileExtensionOrDirectory);
|
||||
|
||||
int isCounting = 1;
|
||||
i = 0;
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
while ((de=readdir(dir)) != NULL) {
|
||||
// skip hidden files
|
||||
if (de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
// NULL means that we are gathering directories, so skip this
|
||||
if (fileExtensionOrDirectory != NULL)
|
||||
{
|
||||
// make sure that we can have the desired extension (prevent seg fault)
|
||||
if (strlen(de->d_name) < extension_length)
|
||||
continue;
|
||||
// compare the extension
|
||||
if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat info;
|
||||
char fullFileName[PATH_MAX];
|
||||
strcpy(fullFileName, directory);
|
||||
strcat(fullFileName, de->d_name);
|
||||
stat(fullFileName, &info);
|
||||
// make sure it is a directory
|
||||
if (!(S_ISDIR(info.st_mode)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pass == 0)
|
||||
{
|
||||
total++;
|
||||
continue;
|
||||
}
|
||||
|
||||
files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2);
|
||||
strcpy(files[i], directory);
|
||||
strcat(files[i], de->d_name);
|
||||
if (fileExtensionOrDirectory == NULL)
|
||||
strcat(files[i], "/");
|
||||
i++;
|
||||
}
|
||||
if (pass == 1)
|
||||
break;
|
||||
if (total == 0)
|
||||
break;
|
||||
rewinddir(dir);
|
||||
*numFiles = total;
|
||||
files = (char**) malloc((total+1)*sizeof(char*));
|
||||
files[total]=NULL;
|
||||
}
|
||||
|
||||
if(closedir(dir) < 0) {
|
||||
LOGE("Failed to close directory.");
|
||||
}
|
||||
|
||||
if (total==0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// sort the result
|
||||
if (files != NULL) {
|
||||
for (i = 0; i < total; i++) {
|
||||
int curMax = -1;
|
||||
int j;
|
||||
for (j = 0; j < total - i; j++) {
|
||||
if (curMax == -1 || strcmp(files[curMax], files[j]) < 0)
|
||||
curMax = j;
|
||||
}
|
||||
char* temp = files[curMax];
|
||||
files[curMax] = files[total - i - 1];
|
||||
files[total - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
// pass in NULL for fileExtensionOrDirectory and you will get a directory chooser
|
||||
char* choose_file_menu(const char* directory, const char* fileExtensionOrDirectory, const char* headers[])
|
||||
{
|
||||
char path[PATH_MAX] = "";
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
int numFiles = 0;
|
||||
int numDirs = 0;
|
||||
int i;
|
||||
char* return_value = NULL;
|
||||
int dir_len = strlen(directory);
|
||||
|
||||
char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles);
|
||||
char** dirs = NULL;
|
||||
if (fileExtensionOrDirectory != NULL)
|
||||
dirs = gather_files(directory, NULL, &numDirs);
|
||||
int total = numDirs + numFiles;
|
||||
if (total == 0)
|
||||
{
|
||||
ui_print("No files found.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
char** list = (char**) malloc((total + 1) * sizeof(char*));
|
||||
list[total] = NULL;
|
||||
|
||||
|
||||
for (i = 0 ; i < numDirs; i++)
|
||||
{
|
||||
list[i] = strdup(dirs[i] + dir_len);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < numFiles; i++)
|
||||
{
|
||||
list[numDirs + i] = strdup(files[i] + dir_len);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int chosen_item = get_menu_selection(headers, list, 0);
|
||||
if (chosen_item == GO_BACK)
|
||||
break;
|
||||
static char ret[PATH_MAX];
|
||||
if (chosen_item < numDirs)
|
||||
{
|
||||
char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers);
|
||||
if (subret != NULL)
|
||||
{
|
||||
strcpy(ret, subret);
|
||||
return_value = ret;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
strcpy(ret, files[chosen_item - numDirs]);
|
||||
return_value = ret;
|
||||
break;
|
||||
}
|
||||
free_string_array(list);
|
||||
}
|
||||
|
||||
free_string_array(files);
|
||||
free_string_array(dirs);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void show_choose_zip_menu()
|
||||
{
|
||||
if (ensure_root_path_mounted("SDCARD:") != 0) {
|
||||
LOGE ("Can't mount /sdcard\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static char* headers[] = { "Choose a zip to apply",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
char* file = choose_file_menu("/sdcard/", ".zip", headers);
|
||||
if (file == NULL)
|
||||
return;
|
||||
char sdcard_package_file[1024];
|
||||
strcpy(sdcard_package_file, "SDCARD:");
|
||||
strcat(sdcard_package_file, file + strlen("/sdcard/"));
|
||||
static char* confirm_install = "Confirm install?";
|
||||
static char confirm[PATH_MAX];
|
||||
sprintf(confirm, "Yes - Install %s", basename(file));
|
||||
if (confirm_selection(confirm_install, confirm))
|
||||
install_zip(sdcard_package_file);
|
||||
}
|
||||
|
||||
// This was pulled from bionic: The default system command always looks
|
||||
// for shell in /system/bin/sh. This is bad.
|
||||
#define _PATH_BSHELL "/sbin/sh"
|
||||
|
||||
extern char **environ;
|
||||
int
|
||||
__system(const char *command)
|
||||
{
|
||||
pid_t pid;
|
||||
sig_t intsave, quitsave;
|
||||
sigset_t mask, omask;
|
||||
int pstat;
|
||||
char *argp[] = {"sh", "-c", NULL, NULL};
|
||||
|
||||
if (!command) /* just checking... */
|
||||
return(1);
|
||||
|
||||
argp[2] = (char *)command;
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &mask, &omask);
|
||||
switch (pid = vfork()) {
|
||||
case -1: /* error */
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
return(-1);
|
||||
case 0: /* child */
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
execve(_PATH_BSHELL, argp, environ);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN);
|
||||
quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
|
||||
pid = waitpid(pid, (int *)&pstat, 0);
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
(void)bsd_signal(SIGINT, intsave);
|
||||
(void)bsd_signal(SIGQUIT, quitsave);
|
||||
return (pid == -1 ? -1 : pstat);
|
||||
}
|
||||
|
||||
void show_nandroid_restore_menu()
|
||||
{
|
||||
if (ensure_root_path_mounted("SDCARD:") != 0) {
|
||||
LOGE ("Can't mount /sdcard\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static char* headers[] = { "Choose an image to restore",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
if (confirm_selection("Confirm restore?", "Yes - Restore"))
|
||||
nandroid_restore(file, 1, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
void show_mount_usb_storage_menu()
|
||||
{
|
||||
char command[PATH_MAX];
|
||||
sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", SDCARD_DEVICE_PRIMARY);
|
||||
__system(command);
|
||||
static char* headers[] = { "USB Mass Storage device",
|
||||
"Leaving this menu unmount",
|
||||
"your SD card from your PC.",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char* list[] = { "Unmount", NULL };
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int chosen_item = get_menu_selection(headers, list, 0);
|
||||
if (chosen_item == GO_BACK || chosen_item == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
__system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file");
|
||||
__system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable");
|
||||
}
|
||||
|
||||
int confirm_selection(const char* title, const char* confirm)
|
||||
{
|
||||
struct stat info;
|
||||
if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info))
|
||||
return 1;
|
||||
|
||||
char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL };
|
||||
char* items[] = { "No",
|
||||
"No",
|
||||
"No",
|
||||
"No",
|
||||
"No",
|
||||
"No",
|
||||
"No",
|
||||
confirm, //" Yes -- wipe partition", // [7
|
||||
"No",
|
||||
"No",
|
||||
"No",
|
||||
NULL };
|
||||
|
||||
int chosen_item = get_menu_selection(confirm_headers, items, 0);
|
||||
return chosen_item == 7;
|
||||
}
|
||||
|
||||
int format_non_mtd_device(const char* root)
|
||||
{
|
||||
// if this is SDEXT:, don't worry about it.
|
||||
if (0 == strcmp(root, "SDEXT:"))
|
||||
{
|
||||
struct stat st;
|
||||
if (0 != stat(SDEXT_DEVICE, &st))
|
||||
{
|
||||
ui_print("No app2sd partition found. Skipping format of /sd-ext.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
translate_root_path(root, path, PATH_MAX);
|
||||
if (0 != ensure_root_path_mounted(root))
|
||||
{
|
||||
ui_print("Error mounting %s!\n", path);
|
||||
ui_print("Skipping format...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char tmp[PATH_MAX];
|
||||
sprintf(tmp, "rm -rf %s/*", path);
|
||||
__system(tmp);
|
||||
sprintf(tmp, "rm -rf %s/.*", path);
|
||||
__system(tmp);
|
||||
|
||||
ensure_root_path_unmounted(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MOUNTABLE_COUNT 5
|
||||
#define MTD_COUNT 4
|
||||
#define MMC_COUNT 2
|
||||
|
||||
void show_partition_menu()
|
||||
{
|
||||
static char* headers[] = { "Mounts and Storage Menu",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
typedef char* string;
|
||||
string mounts[MOUNTABLE_COUNT][3] = {
|
||||
{ "mount /system", "unmount /system", "SYSTEM:" },
|
||||
{ "mount /data", "unmount /data", "DATA:" },
|
||||
{ "mount /cache", "unmount /cache", "CACHE:" },
|
||||
{ "mount /sdcard", "unmount /sdcard", "SDCARD:" },
|
||||
{ "mount /sd-ext", "unmount /sd-ext", "SDEXT:" }
|
||||
};
|
||||
|
||||
string mtds[MTD_COUNT][2] = {
|
||||
{ "format boot", "BOOT:" },
|
||||
{ "format system", "SYSTEM:" },
|
||||
{ "format data", "DATA:" },
|
||||
{ "format cache", "CACHE:" },
|
||||
};
|
||||
|
||||
string mmcs[MMC_COUNT][3] = {
|
||||
{ "format sdcard", "SDCARD:" },
|
||||
{ "format sd-ext", "SDEXT:" }
|
||||
};
|
||||
|
||||
static char* confirm_format = "Confirm format?";
|
||||
static char* confirm = "Yes - Format";
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int ismounted[MOUNTABLE_COUNT];
|
||||
int i;
|
||||
static string options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null
|
||||
for (i = 0; i < MOUNTABLE_COUNT; i++)
|
||||
{
|
||||
ismounted[i] = is_root_path_mounted(mounts[i][2]);
|
||||
options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0];
|
||||
}
|
||||
|
||||
for (i = 0; i < MTD_COUNT; i++)
|
||||
{
|
||||
options[MOUNTABLE_COUNT + i] = mtds[i][0];
|
||||
}
|
||||
|
||||
for (i = 0; i < MMC_COUNT; i++)
|
||||
{
|
||||
options[MOUNTABLE_COUNT + MTD_COUNT + i] = mmcs[i][0];
|
||||
}
|
||||
|
||||
options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT] = "mount USB storage";
|
||||
options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1] = NULL;
|
||||
|
||||
int chosen_item = get_menu_selection(headers, options, 0);
|
||||
if (chosen_item == GO_BACK)
|
||||
break;
|
||||
if (chosen_item == MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
|
||||
{
|
||||
show_mount_usb_storage_menu();
|
||||
}
|
||||
else if (chosen_item < MOUNTABLE_COUNT)
|
||||
{
|
||||
if (ismounted[chosen_item])
|
||||
{
|
||||
if (0 != ensure_root_path_unmounted(mounts[chosen_item][2]))
|
||||
ui_print("Error unmounting %s!\n", mounts[chosen_item][2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != ensure_root_path_mounted(mounts[chosen_item][2]))
|
||||
ui_print("Error mounting %s!\n", mounts[chosen_item][2]);
|
||||
}
|
||||
}
|
||||
else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT)
|
||||
{
|
||||
chosen_item = chosen_item - MOUNTABLE_COUNT;
|
||||
if (!confirm_selection(confirm_format, confirm))
|
||||
continue;
|
||||
ui_print("Formatting %s...\n", mtds[chosen_item][1]);
|
||||
if (0 != format_root_device(mtds[chosen_item][1]))
|
||||
ui_print("Error formatting %s!\n", mtds[chosen_item][1]);
|
||||
else
|
||||
ui_print("Done.\n");
|
||||
}
|
||||
else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
|
||||
{
|
||||
chosen_item = chosen_item - MOUNTABLE_COUNT - MTD_COUNT;
|
||||
if (!confirm_selection(confirm_format, confirm))
|
||||
continue;
|
||||
ui_print("Formatting %s...\n", mmcs[chosen_item][1]);
|
||||
if (0 != format_non_mtd_device(mmcs[chosen_item][1]))
|
||||
ui_print("Error formatting %s!\n", mmcs[chosen_item][1]);
|
||||
else
|
||||
ui_print("Done.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand"
|
||||
|
||||
int extendedcommand_file_exists()
|
||||
{
|
||||
struct stat file_info;
|
||||
return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
|
||||
}
|
||||
|
||||
int run_script_from_buffer(char* script_data, int script_len, char* filename)
|
||||
{
|
||||
/* Parse the script. Note that the script and parse tree are never freed.
|
||||
*/
|
||||
const AmCommandList *commands = parseAmendScript(script_data, script_len);
|
||||
if (commands == NULL) {
|
||||
printf("Syntax error in update script\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("Parsed %.*s\n", script_len, filename);
|
||||
}
|
||||
|
||||
/* Execute the script.
|
||||
*/
|
||||
int ret = execCommandList((ExecContext *)1, commands);
|
||||
if (ret != 0) {
|
||||
int num = ret;
|
||||
char *line = NULL, *next = script_data;
|
||||
while (next != NULL && ret-- > 0) {
|
||||
line = next;
|
||||
next = memchr(line, '\n', script_data + script_len - line);
|
||||
if (next != NULL) *next++ = '\0';
|
||||
}
|
||||
printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_script(char* filename)
|
||||
{
|
||||
struct stat file_info;
|
||||
if (0 != stat(filename, &file_info)) {
|
||||
printf("Error executing stat on file: %s\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int script_len = file_info.st_size;
|
||||
char* script_data = (char*)malloc(script_len + 1);
|
||||
FILE *file = fopen(filename, "rb");
|
||||
fread(script_data, script_len, 1, file);
|
||||
// supposedly not necessary, but let's be safe.
|
||||
script_data[script_len] = '\0';
|
||||
fclose(file);
|
||||
LOGI("Running script:\n");
|
||||
LOGI("\n%s\n", script_data);
|
||||
|
||||
int ret = run_script_from_buffer(script_data, script_len, filename);
|
||||
free(script_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int run_and_remove_extendedcommand()
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
sprintf(tmp, "cp %s /tmp/%s", EXTENDEDCOMMAND_SCRIPT, basename(EXTENDEDCOMMAND_SCRIPT));
|
||||
__system(tmp);
|
||||
remove(EXTENDEDCOMMAND_SCRIPT);
|
||||
int i = 0;
|
||||
for (i = 20; i > 0; i--) {
|
||||
ui_print("Waiting for SD Card to mount (%ds)\n", i);
|
||||
if (ensure_root_path_mounted("SDCARD:") == 0) {
|
||||
ui_print("SD Card mounted...\n");
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
remove("/sdcard/clockworkmod/.recoverycheckpoint");
|
||||
if (i == 0) {
|
||||
ui_print("Timed out waiting for SD card... continuing anyways.");
|
||||
}
|
||||
|
||||
sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
|
||||
return run_script(tmp);
|
||||
}
|
||||
|
||||
int amend_main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("Usage: amend <script>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
RecoveryCommandContext ctx = { NULL };
|
||||
if (register_update_commands(&ctx)) {
|
||||
LOGE("Can't install update commands\n");
|
||||
}
|
||||
return run_script(argv[1]);
|
||||
}
|
||||
|
||||
void show_nandroid_advanced_restore_menu()
|
||||
{
|
||||
if (ensure_root_path_mounted("SDCARD:") != 0) {
|
||||
LOGE ("Can't mount /sdcard\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static char* advancedheaders[] = { "Choose an image to restore",
|
||||
"",
|
||||
"Choose an image to restore",
|
||||
"first. The next menu will",
|
||||
"you more options.",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, advancedheaders);
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
static char* headers[] = { "Nandroid Advanced Restore",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char* list[] = { "Restore boot",
|
||||
"Restore system",
|
||||
"Restore data",
|
||||
"Restore cache",
|
||||
"Restore sd-ext",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static char* confirm_restore = "Confirm restore?";
|
||||
|
||||
int chosen_item = get_menu_selection(headers, list, 0);
|
||||
switch (chosen_item)
|
||||
{
|
||||
case 0:
|
||||
if (confirm_selection(confirm_restore, "Yes - Restore boot"))
|
||||
nandroid_restore(file, 1, 0, 0, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
if (confirm_selection(confirm_restore, "Yes - Restore system"))
|
||||
nandroid_restore(file, 0, 1, 0, 0, 0);
|
||||
break;
|
||||
case 2:
|
||||
if (confirm_selection(confirm_restore, "Yes - Restore data"))
|
||||
nandroid_restore(file, 0, 0, 1, 0, 0);
|
||||
break;
|
||||
case 3:
|
||||
if (confirm_selection(confirm_restore, "Yes - Restore cache"))
|
||||
nandroid_restore(file, 0, 0, 0, 1, 0);
|
||||
break;
|
||||
case 4:
|
||||
if (confirm_selection(confirm_restore, "Yes - Restore sd-ext"))
|
||||
nandroid_restore(file, 0, 0, 0, 0, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void show_nandroid_menu()
|
||||
{
|
||||
static char* headers[] = { "Nandroid",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char* list[] = { "Backup",
|
||||
"Restore",
|
||||
"Advanced Restore",
|
||||
NULL
|
||||
};
|
||||
|
||||
int chosen_item = get_menu_selection(headers, list, 0);
|
||||
switch (chosen_item)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
char backup_path[PATH_MAX];
|
||||
time_t t = time(NULL);
|
||||
struct tm *tmp = localtime(&t);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
|
||||
}
|
||||
else
|
||||
{
|
||||
strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
|
||||
}
|
||||
nandroid_backup(backup_path);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
show_nandroid_restore_menu();
|
||||
break;
|
||||
case 2:
|
||||
show_nandroid_advanced_restore_menu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wipe_battery_stats()
|
||||
{
|
||||
ensure_root_path_mounted("DATA:");
|
||||
remove("/data/system/batterystats.bin");
|
||||
ensure_root_path_unmounted("DATA:");
|
||||
}
|
||||
|
||||
void show_advanced_menu()
|
||||
{
|
||||
static char* headers[] = { "Advanced and Debugging Menu",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char* list[] = { "Reboot Recovery",
|
||||
"Wipe Dalvik Cache",
|
||||
"Wipe Battery Stats",
|
||||
"Report Error",
|
||||
"Key Test",
|
||||
#ifndef BOARD_HAS_SMALL_RECOVERY
|
||||
"Partition SD Card",
|
||||
"Fix Permissions",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int chosen_item = get_menu_selection(headers, list, 0);
|
||||
if (chosen_item == GO_BACK)
|
||||
break;
|
||||
switch (chosen_item)
|
||||
{
|
||||
case 0:
|
||||
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery");
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
if (0 != ensure_root_path_mounted("DATA:"))
|
||||
break;
|
||||
ensure_root_path_mounted("SDEXT:");
|
||||
ensure_root_path_mounted("CACHE:");
|
||||
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) {
|
||||
__system("rm -r /data/dalvik-cache");
|
||||
__system("rm -r /cache/dalvik-cache");
|
||||
__system("rm -r /sd-ext/dalvik-cache");
|
||||
}
|
||||
ensure_root_path_unmounted("DATA:");
|
||||
ui_print("Dalvik Cache wiped.\n");
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Battery Stats"))
|
||||
wipe_battery_stats();
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
handle_failure(1);
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
ui_print("Outputting key codes.\n");
|
||||
ui_print("Go back to end debugging.\n");
|
||||
int key;
|
||||
int action;
|
||||
do
|
||||
{
|
||||
key = ui_wait_key();
|
||||
action = device_handle_key(key, 1);
|
||||
ui_print("Key: %d\n", key);
|
||||
}
|
||||
while (action != GO_BACK);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
static char* ext_sizes[] = { "128M",
|
||||
"256M",
|
||||
"512M",
|
||||
"1024M",
|
||||
NULL };
|
||||
|
||||
static char* swap_sizes[] = { "0M",
|
||||
"32M",
|
||||
"64M",
|
||||
"128M",
|
||||
"256M",
|
||||
NULL };
|
||||
|
||||
static char* ext_headers[] = { "Ext Size", "", NULL };
|
||||
static char* swap_headers[] = { "Swap Size", "", NULL };
|
||||
|
||||
int ext_size = get_menu_selection(ext_headers, ext_sizes, 0);
|
||||
if (ext_size == GO_BACK)
|
||||
continue;
|
||||
|
||||
int swap_size = get_menu_selection(swap_headers, swap_sizes, 0);
|
||||
if (swap_size == GO_BACK)
|
||||
continue;
|
||||
|
||||
char sddevice[256];
|
||||
const RootInfo *ri = get_root_info_for_path("SDCARD:");
|
||||
strcpy(sddevice, ri->device);
|
||||
// we only want the mmcblk, not the partition
|
||||
sddevice[strlen("/dev/block/mmcblkX")] = NULL;
|
||||
char cmd[PATH_MAX];
|
||||
setenv("SDPATH", sddevice, 1);
|
||||
sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
|
||||
ui_print("Partitioning SD Card... please wait...\n");
|
||||
if (0 == __system(cmd))
|
||||
ui_print("Done!\n");
|
||||
else
|
||||
ui_print("An error occured while partitioning your SD Card. Please see /tmp/recovery.log for more details.\n");
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
ensure_root_path_mounted("SYSTEM:");
|
||||
ensure_root_path_mounted("DATA:");
|
||||
ui_print("Fixing permissions...\n");
|
||||
__system("fix_permissions");
|
||||
ui_print("Done!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_fstab_root(char *root_path, FILE *file)
|
||||
{
|
||||
RootInfo *info = get_root_info_for_path(root_path);
|
||||
if (info == NULL) {
|
||||
LOGW("Unable to get root info for %s during fstab generation!", root_path);
|
||||
return;
|
||||
}
|
||||
MtdPartition *mtd = get_root_mtd_partition(root_path);
|
||||
if (mtd != NULL)
|
||||
{
|
||||
fprintf(file, "/dev/block/mtdblock%d ", mtd->device_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "%s ", info->device);
|
||||
}
|
||||
|
||||
fprintf(file, "%s ", info->mount_point);
|
||||
fprintf(file, "%s %s\n", info->filesystem, info->filesystem_options == NULL ? "rw" : info->filesystem_options);
|
||||
}
|
||||
|
||||
void create_fstab()
|
||||
{
|
||||
__system("touch /etc/mtab");
|
||||
FILE *file = fopen("/etc/fstab", "w");
|
||||
if (file == NULL) {
|
||||
LOGW("Unable to create /etc/fstab!");
|
||||
return;
|
||||
}
|
||||
write_fstab_root("CACHE:", file);
|
||||
write_fstab_root("DATA:", file);
|
||||
#ifdef HAS_DATADATA
|
||||
write_fstab_root("DATADATA:", file);
|
||||
#endif
|
||||
write_fstab_root("SYSTEM:", file);
|
||||
write_fstab_root("SDCARD:", file);
|
||||
write_fstab_root("SDEXT:", file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void handle_failure(int ret)
|
||||
{
|
||||
if (ret == 0)
|
||||
return;
|
||||
if (0 != ensure_root_path_mounted("SDCARD:"))
|
||||
return;
|
||||
mkdir("/sdcard/clockworkmod", S_IRWXU);
|
||||
__system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log");
|
||||
ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n");
|
||||
}
|
46
extendedcommands.h
Normal file
@ -0,0 +1,46 @@
|
||||
extern int signature_check_enabled;
|
||||
extern int script_assert_enabled;
|
||||
|
||||
void
|
||||
toggle_signature_check();
|
||||
|
||||
void
|
||||
toggle_script_asserts();
|
||||
|
||||
void
|
||||
show_choose_zip_menu();
|
||||
|
||||
int
|
||||
do_nandroid_backup(const char* backup_name);
|
||||
|
||||
int
|
||||
do_nandroid_restore();
|
||||
|
||||
void
|
||||
show_nandroid_restore_menu();
|
||||
|
||||
void
|
||||
show_nandroid_menu();
|
||||
|
||||
void
|
||||
show_partition_menu();
|
||||
|
||||
void
|
||||
show_choose_zip_menu();
|
||||
|
||||
int
|
||||
install_zip(const char* packagefilepath);
|
||||
|
||||
int
|
||||
__system(const char *command);
|
||||
|
||||
void
|
||||
show_advanced_menu();
|
||||
|
||||
int
|
||||
format_non_mtd_device(const char* root);
|
||||
|
||||
void
|
||||
wipe_battery_stats();
|
||||
|
||||
void create_fstab();
|
@ -39,6 +39,10 @@ int remember_firmware_update(const char *type, const char *data, int length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return true if there is a firmware update pending.
|
||||
int firmware_update_pending() {
|
||||
return update_data != NULL && update_length > 0;
|
||||
}
|
||||
|
||||
/* Bootloader / Recovery Flow
|
||||
*
|
||||
|
@ -23,6 +23,9 @@
|
||||
*/
|
||||
int remember_firmware_update(const char *type, const char *data, int length);
|
||||
|
||||
/* Returns true if a firmware update has been saved. */
|
||||
int firmware_update_pending();
|
||||
|
||||
/* If an update was saved, reboot into the bootloader now to install it.
|
||||
* Returns 0 if no radio image was defined, nonzero on error,
|
||||
* doesn't return at all on success...
|
||||
|
411
install.c
@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "amend/amend.h"
|
||||
#include "common.h"
|
||||
#include "install.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
@ -30,121 +32,312 @@
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "roots.h"
|
||||
#include "verifier.h"
|
||||
#include "firmware.h"
|
||||
#include "legacy.h"
|
||||
|
||||
/* List of public keys */
|
||||
static const RSAPublicKey keys[] = {
|
||||
#include "keys.inc"
|
||||
};
|
||||
#include "extendedcommands.h"
|
||||
|
||||
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
|
||||
|
||||
static const ZipEntry *
|
||||
find_update_script(ZipArchive *zip)
|
||||
{
|
||||
//TODO: Get the location of this script from the MANIFEST.MF file
|
||||
return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
|
||||
}
|
||||
|
||||
static int read_data(ZipArchive *zip, const ZipEntry *entry,
|
||||
char** ppData, int* pLength) {
|
||||
int len = (int)mzGetZipEntryUncompLen(entry);
|
||||
if (len <= 0) {
|
||||
LOGE("Bad data length %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
char *data = malloc(len + 1);
|
||||
if (data == NULL) {
|
||||
LOGE("Can't allocate %d bytes for data\n", len + 1);
|
||||
return -2;
|
||||
}
|
||||
bool ok = mzReadZipEntry(zip, entry, data, len);
|
||||
if (!ok) {
|
||||
LOGE("Error while reading data\n");
|
||||
free(data);
|
||||
return -3;
|
||||
}
|
||||
data[len] = '\0'; // not necessary, but just to be safe
|
||||
*ppData = data;
|
||||
if (pLength) {
|
||||
*pLength = len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
|
||||
#define PUBLIC_KEYS_FILE "/res/keys"
|
||||
|
||||
// The update binary ask us to install a firmware file on reboot. Set
|
||||
// that up. Takes ownership of type and filename.
|
||||
static int
|
||||
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
|
||||
{
|
||||
/* Read the entire script into a buffer.
|
||||
*/
|
||||
int script_len;
|
||||
char* script_data;
|
||||
if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) {
|
||||
LOGE("Can't read update script\n");
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
handle_firmware_update(char* type, char* filename, ZipArchive* zip) {
|
||||
unsigned int data_size;
|
||||
const ZipEntry* entry = NULL;
|
||||
|
||||
/* Parse the script. Note that the script and parse tree are never freed.
|
||||
*/
|
||||
const AmCommandList *commands = parseAmendScript(script_data, script_len);
|
||||
if (commands == NULL) {
|
||||
LOGE("Syntax error in update script\n");
|
||||
return INSTALL_ERROR;
|
||||
} else {
|
||||
UnterminatedString name = mzGetZipEntryFileName(update_script_entry);
|
||||
LOGI("Parsed %.*s\n", name.len, name.str);
|
||||
}
|
||||
|
||||
/* Execute the script.
|
||||
*/
|
||||
int ret = execCommandList((ExecContext *)1, commands);
|
||||
if (ret != 0) {
|
||||
int num = ret;
|
||||
char *line, *next = script_data;
|
||||
while (next != NULL && ret-- > 0) {
|
||||
line = next;
|
||||
next = memchr(line, '\n', script_data + script_len - line);
|
||||
if (next != NULL) *next++ = '\0';
|
||||
if (strncmp(filename, "PACKAGE:", 8) == 0) {
|
||||
entry = mzFindZipEntry(zip, filename+8);
|
||||
if (entry == NULL) {
|
||||
LOGE("Failed to find \"%s\" in package", filename+8);
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
|
||||
data_size = entry->uncompLen;
|
||||
} else {
|
||||
struct stat st_data;
|
||||
if (stat(filename, &st_data) < 0) {
|
||||
LOGE("Error stat'ing %s: %s\n", filename, strerror(errno));
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
data_size = st_data.st_size;
|
||||
}
|
||||
|
||||
LOGI("type is %s; size is %d; file is %s\n",
|
||||
type, data_size, filename);
|
||||
|
||||
char* data = malloc(data_size);
|
||||
if (data == NULL) {
|
||||
LOGI("Can't allocate %d bytes for firmware data\n", data_size);
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
ui_print("Installation complete.\n");
|
||||
if (entry) {
|
||||
if (mzReadZipEntry(zip, entry, data, data_size) == false) {
|
||||
LOGE("Failed to read \"%s\" from package", filename+8);
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
} else {
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
LOGE("Failed to open %s: %s\n", filename, strerror(errno));
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
if (fread(data, 1, data_size, f) != data_size) {
|
||||
LOGE("Failed to read firmware data: %s\n", strerror(errno));
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
if (remember_firmware_update(type, data, data_size)) {
|
||||
LOGE("Can't store %s image\n", type);
|
||||
free(data);
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
#endif
|
||||
free(filename);
|
||||
|
||||
return INSTALL_SUCCESS;
|
||||
}
|
||||
|
||||
// If the package contains an update binary, extract it and run it.
|
||||
static int
|
||||
try_update_binary(const char *path, ZipArchive *zip) {
|
||||
const ZipEntry* binary_entry =
|
||||
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
|
||||
if (binary_entry == NULL) {
|
||||
return INSTALL_UPDATE_BINARY_MISSING;
|
||||
}
|
||||
|
||||
char* binary = "/tmp/update_binary";
|
||||
unlink(binary);
|
||||
int fd = creat(binary, 0755);
|
||||
if (fd < 0) {
|
||||
LOGE("Can't make %s\n", binary);
|
||||
return 1;
|
||||
}
|
||||
bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
|
||||
close(fd);
|
||||
|
||||
if (!ok) {
|
||||
LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pipefd[2];
|
||||
pipe(pipefd);
|
||||
|
||||
// When executing the update binary contained in the package, the
|
||||
// arguments passed are:
|
||||
//
|
||||
// - the version number for this interface
|
||||
//
|
||||
// - an fd to which the program can write in order to update the
|
||||
// progress bar. The program can write single-line commands:
|
||||
//
|
||||
// progress <frac> <secs>
|
||||
// fill up the next <frac> part of of the progress bar
|
||||
// over <secs> seconds. If <secs> is zero, use
|
||||
// set_progress commands to manually control the
|
||||
// progress of this segment of the bar
|
||||
//
|
||||
// set_progress <frac>
|
||||
// <frac> should be between 0.0 and 1.0; sets the
|
||||
// progress bar within the segment defined by the most
|
||||
// recent progress command.
|
||||
//
|
||||
// firmware <"hboot"|"radio"> <filename>
|
||||
// arrange to install the contents of <filename> in the
|
||||
// given partition on reboot. (API v2: <filename> may
|
||||
// start with "PACKAGE:" to indicate taking a file from
|
||||
// the OTA package.)
|
||||
//
|
||||
// ui_print <string>
|
||||
// display <string> on the screen.
|
||||
//
|
||||
// - the name of the package zip file.
|
||||
//
|
||||
|
||||
char** args = malloc(sizeof(char*) * 5);
|
||||
args[0] = binary;
|
||||
args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
|
||||
args[2] = malloc(10);
|
||||
sprintf(args[2], "%d", pipefd[1]);
|
||||
args[3] = (char*)path;
|
||||
args[4] = NULL;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(pipefd[0]);
|
||||
execv(binary, args);
|
||||
fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
close(pipefd[1]);
|
||||
|
||||
char* firmware_type = NULL;
|
||||
char* firmware_filename = NULL;
|
||||
|
||||
char buffer[1024];
|
||||
FILE* from_child = fdopen(pipefd[0], "r");
|
||||
while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
|
||||
char* command = strtok(buffer, " \n");
|
||||
if (command == NULL) {
|
||||
continue;
|
||||
} else if (strcmp(command, "progress") == 0) {
|
||||
char* fraction_s = strtok(NULL, " \n");
|
||||
char* seconds_s = strtok(NULL, " \n");
|
||||
|
||||
float fraction = strtof(fraction_s, NULL);
|
||||
int seconds = strtol(seconds_s, NULL, 10);
|
||||
|
||||
ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),
|
||||
seconds);
|
||||
} else if (strcmp(command, "set_progress") == 0) {
|
||||
char* fraction_s = strtok(NULL, " \n");
|
||||
float fraction = strtof(fraction_s, NULL);
|
||||
ui_set_progress(fraction);
|
||||
} else if (strcmp(command, "firmware") == 0) {
|
||||
char* type = strtok(NULL, " \n");
|
||||
char* filename = strtok(NULL, " \n");
|
||||
|
||||
if (type != NULL && filename != NULL) {
|
||||
if (firmware_type != NULL) {
|
||||
LOGE("ignoring attempt to do multiple firmware updates");
|
||||
} else {
|
||||
firmware_type = strdup(type);
|
||||
firmware_filename = strdup(filename);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(command, "ui_print") == 0) {
|
||||
char* str = strtok(NULL, "\n");
|
||||
if (str) {
|
||||
ui_print(str);
|
||||
} else {
|
||||
ui_print("\n");
|
||||
}
|
||||
} else {
|
||||
LOGE("unknown command [%s]\n", command);
|
||||
}
|
||||
}
|
||||
fclose(from_child);
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
if (firmware_type != NULL) {
|
||||
return handle_firmware_update(firmware_type, firmware_filename, zip);
|
||||
} else {
|
||||
return INSTALL_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
handle_update_package(const char *path, ZipArchive *zip)
|
||||
{
|
||||
// Give verification half the progress bar...
|
||||
ui_print("Verifying update package...\n");
|
||||
ui_show_progress(
|
||||
VERIFICATION_PROGRESS_FRACTION,
|
||||
VERIFICATION_PROGRESS_TIME);
|
||||
|
||||
if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) {
|
||||
LOGE("Verification failed\n");
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
// Update should take the rest of the progress bar.
|
||||
ui_print("Installing update...\n");
|
||||
|
||||
const ZipEntry *script_entry;
|
||||
script_entry = find_update_script(zip);
|
||||
if (script_entry == NULL) {
|
||||
LOGE("Can't find update script\n");
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
LOGI("Trying update-binary.\n");
|
||||
int result = try_update_binary(path, zip);
|
||||
|
||||
if (register_package_root(zip, path) < 0) {
|
||||
LOGE("Can't register package root\n");
|
||||
return INSTALL_ERROR;
|
||||
if (result == INSTALL_UPDATE_BINARY_MISSING)
|
||||
{
|
||||
register_package_root(NULL, NULL); // Unregister package root
|
||||
if (register_package_root(zip, path) < 0) {
|
||||
LOGE("Can't register package root\n");
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
const ZipEntry *script_entry;
|
||||
script_entry = find_update_script(zip);
|
||||
LOGI("Trying update-script.\n");
|
||||
result = handle_update_script(zip, script_entry);
|
||||
if (result == INSTALL_UPDATE_SCRIPT_MISSING)
|
||||
result = INSTALL_ERROR;
|
||||
}
|
||||
|
||||
int ret = handle_update_script(zip, script_entry);
|
||||
|
||||
register_package_root(NULL, NULL); // Unregister package root
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reads a file containing one or more public keys as produced by
|
||||
// DumpPublicKey: this is an RSAPublicKey struct as it would appear
|
||||
// as a C source literal, eg:
|
||||
//
|
||||
// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
|
||||
//
|
||||
// (Note that the braces and commas in this example are actual
|
||||
// characters the parser expects to find in the file; the ellipses
|
||||
// indicate more numbers omitted from this example.)
|
||||
//
|
||||
// The file may contain multiple keys in this format, separated by
|
||||
// commas. The last key must not be followed by a comma.
|
||||
//
|
||||
// Returns NULL if the file failed to parse, or if it contain zero keys.
|
||||
static RSAPublicKey*
|
||||
load_keys(const char* filename, int* numKeys) {
|
||||
RSAPublicKey* out = NULL;
|
||||
*numKeys = 0;
|
||||
|
||||
FILE* f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
LOGE("opening %s: %s\n", filename, strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
int i;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
++*numKeys;
|
||||
out = realloc(out, *numKeys * sizeof(RSAPublicKey));
|
||||
RSAPublicKey* key = out + (*numKeys - 1);
|
||||
if (fscanf(f, " { %i , %i , { %i",
|
||||
&(key->len), &(key->n0inv), &(key->n[0])) != 3) {
|
||||
goto exit;
|
||||
}
|
||||
if (key->len != RSANUMWORDS) {
|
||||
LOGE("key length (%d) does not match expected size\n", key->len);
|
||||
goto exit;
|
||||
}
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit;
|
||||
}
|
||||
if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit;
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit;
|
||||
}
|
||||
fscanf(f, " } } ");
|
||||
|
||||
// if the line ends in a comma, this file has more keys.
|
||||
switch (fgetc(f)) {
|
||||
case ',':
|
||||
// more keys to come.
|
||||
break;
|
||||
|
||||
case EOF:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGE("unexpected character between keys\n");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return out;
|
||||
|
||||
exit:
|
||||
if (f) fclose(f);
|
||||
free(out);
|
||||
*numKeys = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
@ -169,10 +362,36 @@ install_package(const char *root_path)
|
||||
ui_print("Opening update package...\n");
|
||||
LOGI("Update file path: %s\n", path);
|
||||
|
||||
int err;
|
||||
|
||||
if (signature_check_enabled) {
|
||||
int numKeys;
|
||||
RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
|
||||
if (loadedKeys == NULL) {
|
||||
LOGE("Failed to load keys\n");
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
|
||||
|
||||
// Give verification half the progress bar...
|
||||
ui_print("Verifying update package...\n");
|
||||
ui_show_progress(
|
||||
VERIFICATION_PROGRESS_FRACTION,
|
||||
VERIFICATION_PROGRESS_TIME);
|
||||
|
||||
err = verify_file(path, loadedKeys, numKeys);
|
||||
free(loadedKeys);
|
||||
LOGI("verify_file returned %d\n", err);
|
||||
if (err != VERIFY_SUCCESS) {
|
||||
LOGE("signature verification failed\n");
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to open the package.
|
||||
*/
|
||||
ZipArchive zip;
|
||||
int err = mzOpenZipArchive(path, &zip);
|
||||
err = mzOpenZipArchive(path, &zip);
|
||||
if (err != 0) {
|
||||
LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
|
||||
return INSTALL_CORRUPT;
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
|
||||
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_SCRIPT_MISSING, INSTALL_UPDATE_BINARY_MISSING };
|
||||
int install_package(const char *root_path);
|
||||
|
||||
#endif // RECOVERY_INSTALL_H_
|
||||
|
22
killrecovery.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/sbin/sh
|
||||
mkdir -p /sd-ext
|
||||
rm /cache/recovery/command
|
||||
rm /cache/update.zip
|
||||
touch /tmp/.ignorebootmessage
|
||||
kill $(ps | grep /sbin/adbd)
|
||||
kill $(ps | grep /sbin/recovery)
|
||||
|
||||
# On the Galaxy S, the recovery comes test signed, but the
|
||||
# recovery is not automatically restarted.
|
||||
if [ -f /init.smdkc110.rc ]
|
||||
then
|
||||
/sbin/recovery &
|
||||
fi
|
||||
|
||||
# Droid X
|
||||
if [ -f /init.mapphone_cdma.rc ]
|
||||
then
|
||||
/sbin/recovery &
|
||||
fi
|
||||
|
||||
exit 1
|
124
legacy.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "install.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/SysUtil.h"
|
||||
#include "minzip/Zip.h"
|
||||
#include "mtdutils/mounts.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "roots.h"
|
||||
#include "verifier.h"
|
||||
#include "firmware.h"
|
||||
|
||||
#include "amend/amend.h"
|
||||
#include "common.h"
|
||||
#include "install.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/SysUtil.h"
|
||||
#include "minzip/Zip.h"
|
||||
#include "mtdutils/mounts.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "roots.h"
|
||||
#include "verifier.h"
|
||||
|
||||
static int read_data(ZipArchive *zip, const ZipEntry *entry,
|
||||
char** ppData, int* pLength) {
|
||||
int len = (int)mzGetZipEntryUncompLen(entry);
|
||||
if (len <= 0) {
|
||||
LOGE("Bad data length %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
char *data = malloc(len + 1);
|
||||
if (data == NULL) {
|
||||
LOGE("Can't allocate %d bytes for data\n", len + 1);
|
||||
return -2;
|
||||
}
|
||||
bool ok = mzReadZipEntry(zip, entry, data, len);
|
||||
if (!ok) {
|
||||
LOGE("Error while reading data\n");
|
||||
free(data);
|
||||
return -3;
|
||||
}
|
||||
data[len] = '\0'; // not necessary, but just to be safe
|
||||
*ppData = data;
|
||||
if (pLength) {
|
||||
*pLength = len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
|
||||
{
|
||||
/* Read the entire script into a buffer.
|
||||
*/
|
||||
int script_len;
|
||||
char* script_data;
|
||||
if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) {
|
||||
LOGE("Can't read update script\n");
|
||||
return INSTALL_UPDATE_SCRIPT_MISSING;
|
||||
}
|
||||
|
||||
/* Parse the script. Note that the script and parse tree are never freed.
|
||||
*/
|
||||
const AmCommandList *commands = parseAmendScript(script_data, script_len);
|
||||
if (commands == NULL) {
|
||||
LOGE("Syntax error in update script\n");
|
||||
return INSTALL_ERROR;
|
||||
} else {
|
||||
UnterminatedString name = mzGetZipEntryFileName(update_script_entry);
|
||||
LOGI("Parsed %.*s\n", name.len, name.str);
|
||||
}
|
||||
|
||||
/* Execute the script.
|
||||
*/
|
||||
int ret = execCommandList((ExecContext *)1, commands);
|
||||
if (ret != 0) {
|
||||
int num = ret;
|
||||
char *line, *next = script_data;
|
||||
while (next != NULL && ret-- > 0) {
|
||||
line = next;
|
||||
next = memchr(line, '\n', script_data + script_len - line);
|
||||
if (next != NULL) *next++ = '\0';
|
||||
}
|
||||
LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
ui_print("Installation complete.\n");
|
||||
return INSTALL_SUCCESS;
|
||||
}
|
||||
|
||||
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
|
||||
|
||||
const ZipEntry *
|
||||
find_update_script(ZipArchive *zip)
|
||||
{
|
||||
//TODO: Get the location of this script from the MANIFEST.MF file
|
||||
return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
|
||||
}
|
5
legacy.h
Normal file
@ -0,0 +1,5 @@
|
||||
int
|
||||
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry);
|
||||
|
||||
const ZipEntry *
|
||||
find_update_script(ZipArchive *zip);
|
@ -115,6 +115,7 @@ static void set_active_framebuffer(unsigned n)
|
||||
if (n > 1) return;
|
||||
vi.yres_virtual = vi.yres * 2;
|
||||
vi.yoffset = n * vi.yres;
|
||||
vi.bits_per_pixel = 16;
|
||||
if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
||||
perror("active fb swap failed");
|
||||
}
|
||||
|
@ -41,22 +41,6 @@ unsigned int gr_get_height(gr_surface surface);
|
||||
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
|
||||
struct input_event;
|
||||
|
||||
// Dream-specific key codes
|
||||
#define KEY_DREAM_HOME 102 // = KEY_HOME
|
||||
#define KEY_DREAM_RED 107 // = KEY_END
|
||||
#define KEY_DREAM_VOLUMEDOWN 114 // = KEY_VOLUMEDOWN
|
||||
#define KEY_DREAM_VOLUMEUP 115 // = KEY_VOLUMEUP
|
||||
#define KEY_DREAM_SYM 127 // = KEY_COMPOSE
|
||||
#define KEY_DREAM_MENU 139 // = KEY_MENU
|
||||
#define KEY_DREAM_BACK 158 // = KEY_BACK
|
||||
#define KEY_DREAM_FOCUS 211 // = KEY_HP (light touch on camera)
|
||||
#define KEY_DREAM_CAMERA 212 // = KEY_CAMERA
|
||||
#define KEY_DREAM_AT 215 // = KEY_EMAIL
|
||||
#define KEY_DREAM_GREEN 231
|
||||
#define KEY_DREAM_FATTOUCH 258 // = BTN_2 ???
|
||||
#define KEY_DREAM_BALL 272 // = BTN_MOUSE
|
||||
#define KEY_DREAM_TOUCH 330 // = BTN_TOUCH
|
||||
|
||||
int ev_init(void);
|
||||
void ev_exit(void);
|
||||
int ev_get(struct input_event *ev, unsigned dont_wait);
|
||||
|
@ -29,97 +29,84 @@
|
||||
|
||||
#include <pixelflinger/pixelflinger.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include "minui.h"
|
||||
|
||||
// File signature for BMP files.
|
||||
// The letters 'BM' as a little-endian unsigned short.
|
||||
|
||||
#define BMP_SIGNATURE 0x4d42
|
||||
|
||||
typedef struct {
|
||||
// constant, value should equal BMP_SIGNATURE
|
||||
unsigned short bfType;
|
||||
// size of the file in bytes.
|
||||
unsigned long bfSize;
|
||||
// must always be set to zero.
|
||||
unsigned short bfReserved1;
|
||||
// must always be set to zero.
|
||||
unsigned short bfReserved2;
|
||||
// offset from the beginning of the file to the bitmap data.
|
||||
unsigned long bfOffBits;
|
||||
|
||||
// The BITMAPINFOHEADER:
|
||||
// size of the BITMAPINFOHEADER structure, in bytes.
|
||||
unsigned long biSize;
|
||||
// width of the image, in pixels.
|
||||
unsigned long biWidth;
|
||||
// height of the image, in pixels.
|
||||
unsigned long biHeight;
|
||||
// number of planes of the target device, must be set to 1.
|
||||
unsigned short biPlanes;
|
||||
// number of bits per pixel.
|
||||
unsigned short biBitCount;
|
||||
// type of compression, zero means no compression.
|
||||
unsigned long biCompression;
|
||||
// size of the image data, in bytes. If there is no compression,
|
||||
// it is valid to set this member to zero.
|
||||
unsigned long biSizeImage;
|
||||
// horizontal pixels per meter on the designated targer device,
|
||||
// usually set to zero.
|
||||
unsigned long biXPelsPerMeter;
|
||||
// vertical pixels per meter on the designated targer device,
|
||||
// usually set to zero.
|
||||
unsigned long biYPelsPerMeter;
|
||||
// number of colors used in the bitmap, if set to zero the
|
||||
// number of colors is calculated using the biBitCount member.
|
||||
unsigned long biClrUsed;
|
||||
// number of color that are 'important' for the bitmap,
|
||||
// if set to zero, all colors are important.
|
||||
unsigned long biClrImportant;
|
||||
} __attribute__((packed)) BitMapFileHeader;
|
||||
// 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;
|
||||
}
|
||||
|
||||
int res_create_surface(const char* name, gr_surface* pSurface) {
|
||||
char resPath[256];
|
||||
BitMapFileHeader header;
|
||||
GGLSurface* surface = NULL;
|
||||
int result = 0;
|
||||
|
||||
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.bmp", name);
|
||||
unsigned char header[8];
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
|
||||
resPath[sizeof(resPath)-1] = '\0';
|
||||
int fd = open(resPath, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
FILE* fp = fopen(resPath, "rb");
|
||||
if (fp == NULL) {
|
||||
result = -1;
|
||||
goto exit;
|
||||
}
|
||||
size_t bytesRead = read(fd, &header, sizeof(header));
|
||||
|
||||
size_t bytesRead = fread(header, 1, sizeof(header), fp);
|
||||
if (bytesRead != sizeof(header)) {
|
||||
result = -2;
|
||||
goto exit;
|
||||
}
|
||||
if (header.bfType != BMP_SIGNATURE) {
|
||||
result = -3; // Not a legal header
|
||||
|
||||
if (png_sig_cmp(header, 0, sizeof(header))) {
|
||||
result = -3;
|
||||
goto exit;
|
||||
}
|
||||
if (header.biPlanes != 1) {
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
result = -4;
|
||||
goto exit;
|
||||
}
|
||||
if (!(header.biBitCount == 24 || header.biBitCount == 32)) {
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
result = -5;
|
||||
goto exit;
|
||||
}
|
||||
if (header.biCompression != 0) {
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
result = -6;
|
||||
goto exit;
|
||||
}
|
||||
size_t width = header.biWidth;
|
||||
size_t height = header.biHeight;
|
||||
|
||||
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;
|
||||
size_t pixelSize = stride * height;
|
||||
|
||||
|
||||
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 && channels != 4) ||
|
||||
(color_type != PNG_COLOR_TYPE_RGB &&
|
||||
color_type != PNG_COLOR_TYPE_RGBA)) {
|
||||
return -7;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
surface = malloc(sizeof(GGLSurface) + pixelSize);
|
||||
if (surface == NULL) {
|
||||
result = -7;
|
||||
result = -8;
|
||||
goto exit;
|
||||
}
|
||||
unsigned char* pData = (unsigned char*) (surface + 1);
|
||||
@ -128,63 +115,43 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
|
||||
surface->height = height;
|
||||
surface->stride = width; /* Yes, pixels, not bytes */
|
||||
surface->data = pData;
|
||||
surface->format = (header.biBitCount == 24) ?
|
||||
surface->format = (channels == 3) ?
|
||||
GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
|
||||
|
||||
// Source pixel bytes are stored B G R {A}
|
||||
|
||||
lseek(fd, header.bfOffBits, SEEK_SET);
|
||||
size_t y;
|
||||
if (header.biBitCount == 24) { // RGB
|
||||
size_t inputStride = (((3 * width + 3) >> 2) << 2);
|
||||
for (y = 0; y < height; y++) {
|
||||
unsigned char* pRow = pData + (height - (y + 1)) * stride;
|
||||
bytesRead = read(fd, pRow, inputStride);
|
||||
if (bytesRead != inputStride) {
|
||||
result = -8;
|
||||
goto exit;
|
||||
}
|
||||
int y;
|
||||
if (channels == 3) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
unsigned char* pRow = pData + y * stride;
|
||||
png_read_row(png_ptr, pRow, NULL);
|
||||
|
||||
int x;
|
||||
for(x = width - 1; x >= 0; x--) {
|
||||
int sx = x * 3;
|
||||
int dx = x * 4;
|
||||
unsigned char b = pRow[sx];
|
||||
unsigned char r = pRow[sx];
|
||||
unsigned char g = pRow[sx + 1];
|
||||
unsigned char r = pRow[sx + 2];
|
||||
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 + 2] = b; // b
|
||||
pRow[dx + 3] = a;
|
||||
}
|
||||
}
|
||||
} else { // RGBA
|
||||
for (y = 0; y < height; y++) {
|
||||
unsigned char* pRow = pData + (height - (y + 1)) * stride;
|
||||
bytesRead = read(fd, pRow, stride);
|
||||
if (bytesRead != stride) {
|
||||
result = -9;
|
||||
goto exit;
|
||||
}
|
||||
size_t x;
|
||||
for(x = 0; x < width; x++) {
|
||||
size_t xx = x * 4;
|
||||
unsigned char b = pRow[xx];
|
||||
unsigned char g = pRow[xx + 1];
|
||||
unsigned char r = pRow[xx + 2];
|
||||
unsigned char a = pRow[xx + 3];
|
||||
pRow[xx ] = r;
|
||||
pRow[xx + 1] = g;
|
||||
pRow[xx + 2] = b;
|
||||
pRow[xx + 3] = a;
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < height; ++y) {
|
||||
unsigned char* pRow = pData + y * stride;
|
||||
png_read_row(png_ptr, pRow, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
*pSurface = (gr_surface) surface;
|
||||
|
||||
exit:
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
if (fp != NULL) {
|
||||
fclose(fp);
|
||||
}
|
||||
if (result < 0) {
|
||||
if (surface) {
|
||||
|
38
minzip/Zip.c
@ -41,7 +41,7 @@ enum {
|
||||
CENSIZ = 20,
|
||||
CENLEN = 24,
|
||||
CENNAM = 28,
|
||||
CENEXT = 30,
|
||||
CENEXT = 30,
|
||||
CENCOM = 32,
|
||||
CENDSK = 34,
|
||||
CENATT = 36,
|
||||
@ -66,13 +66,13 @@ enum {
|
||||
|
||||
LOCSIG = 0x04034b50, // PK34
|
||||
LOCHDR = 30,
|
||||
|
||||
|
||||
LOCVER = 4,
|
||||
LOCFLG = 6,
|
||||
LOCHOW = 8,
|
||||
LOCTIM = 10,
|
||||
LOCCRC = 14,
|
||||
LOCSIZ = 18,
|
||||
LOCSIZ = 18,
|
||||
LOCLEN = 22,
|
||||
LOCNAM = 26,
|
||||
LOCEXT = 28,
|
||||
@ -757,7 +757,7 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
|
||||
{
|
||||
CopyProcessArgs args;
|
||||
bool ret;
|
||||
|
||||
|
||||
args.buf = buf;
|
||||
args.bufLen = bufLen;
|
||||
ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction,
|
||||
@ -770,15 +770,29 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
|
||||
}
|
||||
|
||||
static bool writeProcessFunction(const unsigned char *data, int dataLen,
|
||||
void *fd)
|
||||
void *cookie)
|
||||
{
|
||||
ssize_t n = write((int)fd, data, dataLen);
|
||||
if (n != dataLen) {
|
||||
LOGE("Can't write %d bytes (only %ld) from zip file: %s\n",
|
||||
dataLen, n, strerror(errno));
|
||||
return false;
|
||||
int fd = (int)cookie;
|
||||
|
||||
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",
|
||||
dataLen-soFar, data+soFar, strerror(errno));
|
||||
if (errno != EINTR) {
|
||||
return false;
|
||||
}
|
||||
} else if (n > 0) {
|
||||
soFar += n;
|
||||
if (soFar == dataLen) return true;
|
||||
if (soFar > dataLen) {
|
||||
LOGE("write overrun? (%ld bytes instead of %d)\n",
|
||||
soFar, dataLen);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -788,7 +802,7 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
|
||||
const ZipEntry *pEntry, int fd)
|
||||
{
|
||||
bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
|
||||
(void *)fd);
|
||||
(void*)fd);
|
||||
if (!ret) {
|
||||
LOGE("Can't extract entry to file.\n");
|
||||
return false;
|
||||
|
@ -15,9 +15,78 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := flash_image.c
|
||||
LOCAL_MODULE := flash_image
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils
|
||||
LOCAL_SHARED_LIBRARIES := libcutils libc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := dump_image.c
|
||||
LOCAL_MODULE := dump_image
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils
|
||||
LOCAL_SHARED_LIBRARIES := libcutils libc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := erase_image.c
|
||||
LOCAL_MODULE := erase_image
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils
|
||||
LOCAL_SHARED_LIBRARIES := libcutils libc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := flash_image.c
|
||||
LOCAL_MODULE := libflash_image
|
||||
LOCAL_CFLAGS += -Dmain=flash_image_main
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := dump_image.c
|
||||
LOCAL_MODULE := libdump_image
|
||||
LOCAL_CFLAGS += -Dmain=dump_image_main
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := erase_image.c
|
||||
LOCAL_MODULE := liberase_image
|
||||
LOCAL_CFLAGS += -Dmain=erase_image_main
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := dump_image.c
|
||||
LOCAL_MODULE := utility_dump_image
|
||||
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
|
||||
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
|
||||
LOCAL_MODULE_STEM := dump_image
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := flash_image.c
|
||||
LOCAL_MODULE := utility_flash_image
|
||||
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
|
||||
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
|
||||
LOCAL_MODULE_STEM := flash_image
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := erase_image.c
|
||||
LOCAL_MODULE := utility_erase_image
|
||||
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
|
||||
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
|
||||
LOCAL_MODULE_STEM := erase_image
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
17
mtdutils/driver.c
Normal file
@ -0,0 +1,17 @@
|
||||
#undef main
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (strstr(argv[0], "flash_image") != NULL)
|
||||
return flash_image_main(argc, argv);
|
||||
if (strstr(argv[0], "dump_image") != NULL)
|
||||
return dump_image_main(argc, argv);
|
||||
if (strstr(argv[0], "mkyaffs2image") != NULL)
|
||||
return mkyaffs2image_main(argc, argv);
|
||||
if (strstr(argv[0], "unyaffs") != NULL)
|
||||
return unyaffs_main(argc, argv);
|
||||
return 0;
|
||||
}
|
137
mtdutils/dump_image.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "cutils/log.h"
|
||||
#include "mtdutils.h"
|
||||
#include "dump_image.h"
|
||||
|
||||
#ifdef LOG_TAG
|
||||
#undef LOG_TAG
|
||||
#endif
|
||||
|
||||
#define LOG_TAG "dump_image"
|
||||
|
||||
#define BLOCK_SIZE 2048
|
||||
#define SPARE_SIZE (BLOCK_SIZE >> 5)
|
||||
|
||||
static int die(const char *msg, ...) {
|
||||
int err = errno;
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
char buf[1024];
|
||||
vsnprintf(buf, sizeof(buf), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if (err != 0) {
|
||||
strlcat(buf, ": ", sizeof(buf));
|
||||
strlcat(buf, strerror(err), sizeof(buf));
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Read a flash partition and write it to an image file. */
|
||||
|
||||
int dump_image(char* partition_name, char* filename, dump_image_callback callback) {
|
||||
MtdReadContext *in;
|
||||
const MtdPartition *partition;
|
||||
char buf[BLOCK_SIZE + SPARE_SIZE];
|
||||
size_t partition_size;
|
||||
size_t read_size;
|
||||
size_t total;
|
||||
int fd;
|
||||
int wrote;
|
||||
int len;
|
||||
|
||||
if (mtd_scan_partitions() <= 0)
|
||||
return die("error scanning partitions");
|
||||
|
||||
partition = mtd_find_partition_by_name(partition_name);
|
||||
if (partition == NULL)
|
||||
return die("can't find %s partition", partition_name);
|
||||
|
||||
if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
|
||||
return die("can't get info of partition %s", partition_name);
|
||||
}
|
||||
|
||||
if (!strcmp(filename, "-")) {
|
||||
fd = fileno(stdout);
|
||||
}
|
||||
else {
|
||||
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
return die("error opening %s", filename);
|
||||
|
||||
in = mtd_read_partition(partition);
|
||||
if (in == NULL) {
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
return die("error opening %s: %s\n", partition_name, strerror(errno));
|
||||
}
|
||||
|
||||
total = 0;
|
||||
while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
|
||||
wrote = write(fd, buf, len);
|
||||
if (wrote != len) {
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
return die("error writing %s", filename);
|
||||
}
|
||||
total += BLOCK_SIZE;
|
||||
if (callback != NULL)
|
||||
callback(total, partition_size);
|
||||
}
|
||||
|
||||
mtd_read_close(in);
|
||||
|
||||
if (close(fd)) {
|
||||
unlink(filename);
|
||||
return die("error closing %s", filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ssize_t (*read_func) (MtdReadContext *, char *, size_t);
|
||||
MtdReadContext *in;
|
||||
const MtdPartition *partition;
|
||||
char buf[BLOCK_SIZE + SPARE_SIZE];
|
||||
size_t partition_size;
|
||||
size_t read_size;
|
||||
size_t total;
|
||||
int fd;
|
||||
int wrote;
|
||||
int len;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return dump_image(argv[1], argv[2], NULL);
|
||||
}
|
8
mtdutils/dump_image.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef DUMP_IMAGE_H
|
||||
#define DUMP_IMAGE_H
|
||||
|
||||
typedef void (*dump_image_callback) (int partition_dumped, int partition_size);
|
||||
|
||||
int dump_image(char* partition_name, char* filename, dump_image_callback callback);
|
||||
|
||||
#endif
|
88
mtdutils/erase_image.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* Portions Copyright (C) 2010 Magnus Eriksson <packetlss@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
|
||||
#include "cutils/log.h"
|
||||
#include "mtdutils.h"
|
||||
|
||||
#ifdef LOG_TAG
|
||||
#undef LOG_TAG
|
||||
#endif
|
||||
|
||||
#define LOG_TAG "erase_image"
|
||||
|
||||
static int die(const char *msg, ...) {
|
||||
int err = errno;
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
char buf[1024];
|
||||
vsnprintf(buf, sizeof(buf), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if (err != 0) {
|
||||
strlcat(buf, ": ", sizeof(buf));
|
||||
strlcat(buf, strerror(err), sizeof(buf));
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
LOGE("%s\n", buf);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
int erase_image(char* partition_name) {
|
||||
MtdWriteContext *out;
|
||||
size_t erased;
|
||||
size_t total_size;
|
||||
size_t erase_size;
|
||||
|
||||
if (mtd_scan_partitions() <= 0) die("error scanning partitions");
|
||||
const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
|
||||
if (partition == NULL) return die("can't find %s partition", partition_name);
|
||||
|
||||
out = mtd_write_partition(partition);
|
||||
if (out == NULL) return die("could not estabilish write context for %s", partition_name);
|
||||
|
||||
// do the actual erase, -1 = full partition erase
|
||||
erased = mtd_erase_blocks(out, -1);
|
||||
|
||||
// erased = bytes erased, if zero, something borked
|
||||
if (!erased) return die("error erasing %s", partition_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Erase a mtd partition */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <partition>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return erase_image(argv[1]);
|
||||
}
|
@ -28,13 +28,6 @@
|
||||
|
||||
#include "mtdutils.h"
|
||||
|
||||
struct MtdPartition {
|
||||
int device_index;
|
||||
unsigned int size;
|
||||
unsigned int erase_size;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct MtdReadContext {
|
||||
const MtdPartition *partition;
|
||||
char *buffer;
|
||||
@ -47,6 +40,10 @@ struct MtdWriteContext {
|
||||
char *buffer;
|
||||
size_t stored;
|
||||
int fd;
|
||||
|
||||
off_t* bad_block_offsets;
|
||||
int bad_block_alloc;
|
||||
int bad_block_count;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -283,21 +280,35 @@ static int read_block(const MtdPartition *partition, int fd, char *data)
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
loff_t pos = lseek64(fd, 0, SEEK_CUR);
|
||||
|
||||
ssize_t size = partition->erase_size;
|
||||
int mgbb;
|
||||
|
||||
while (pos + size <= (int) partition->size) {
|
||||
if (lseek(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
|
||||
fprintf(stderr, "mtd: read error at 0x%08lx (%s)\n",
|
||||
if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
|
||||
fprintf(stderr, "mtd: read error at 0x%08llx (%s)\n",
|
||||
pos, strerror(errno));
|
||||
} else if (ioctl(fd, ECCGETSTATS, &after)) {
|
||||
fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
|
||||
return -1;
|
||||
} else if (after.failed != before.failed) {
|
||||
fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08lx\n",
|
||||
fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
|
||||
after.corrected - before.corrected,
|
||||
after.failed - before.failed, pos);
|
||||
} else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
|
||||
fprintf(stderr,
|
||||
"mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
|
||||
mgbb, pos, errno);
|
||||
} else {
|
||||
return 0; // Success!
|
||||
int i;
|
||||
for (i = 0; i < size; ++i) {
|
||||
if (data[i] != 0) {
|
||||
return 0; // Success!
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "mtd: read all-zero block at 0x%08llx; skipping\n",
|
||||
pos);
|
||||
}
|
||||
|
||||
pos += partition->erase_size;
|
||||
@ -326,6 +337,10 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
|
||||
read += ctx->partition->erase_size;
|
||||
}
|
||||
|
||||
if (read >= len) {
|
||||
return read;
|
||||
}
|
||||
|
||||
// Read the next block into the buffer
|
||||
if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
|
||||
if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
|
||||
@ -348,6 +363,10 @@ MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
|
||||
MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
|
||||
if (ctx == NULL) return NULL;
|
||||
|
||||
ctx->bad_block_offsets = NULL;
|
||||
ctx->bad_block_alloc = 0;
|
||||
ctx->bad_block_count = 0;
|
||||
|
||||
ctx->buffer = malloc(partition->erase_size);
|
||||
if (ctx->buffer == NULL) {
|
||||
free(ctx);
|
||||
@ -368,8 +387,20 @@ MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static int write_block(const MtdPartition *partition, int fd, const char *data)
|
||||
static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
|
||||
if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
|
||||
ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
|
||||
ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
|
||||
ctx->bad_block_alloc * sizeof(off_t));
|
||||
}
|
||||
ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
|
||||
}
|
||||
|
||||
static int write_block(MtdWriteContext *ctx, const char *data)
|
||||
{
|
||||
const MtdPartition *partition = ctx->partition;
|
||||
int fd = ctx->fd;
|
||||
|
||||
off_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
if (pos == (off_t) -1) return 1;
|
||||
|
||||
@ -377,6 +408,7 @@ static int write_block(const MtdPartition *partition, int fd, const char *data)
|
||||
while (pos + size <= (int) partition->size) {
|
||||
loff_t bpos = pos;
|
||||
if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) {
|
||||
add_bad_block_offset(ctx, pos);
|
||||
fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos);
|
||||
pos += partition->erase_size;
|
||||
continue; // Don't try to erase known factory-bad blocks.
|
||||
@ -418,6 +450,7 @@ static int write_block(const MtdPartition *partition, int fd, const char *data)
|
||||
}
|
||||
|
||||
// Try to erase it once more as we give up on this block
|
||||
add_bad_block_offset(ctx, pos);
|
||||
fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
|
||||
ioctl(fd, MEMERASE, &erase_info);
|
||||
pos += partition->erase_size;
|
||||
@ -443,13 +476,13 @@ ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
|
||||
|
||||
// If a complete block was accumulated, write it
|
||||
if (ctx->stored == ctx->partition->erase_size) {
|
||||
if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
|
||||
if (write_block(ctx, ctx->buffer)) return -1;
|
||||
ctx->stored = 0;
|
||||
}
|
||||
|
||||
// Write complete blocks directly from the user's buffer
|
||||
while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
|
||||
if (write_block(ctx->partition, ctx->fd, data + wrote)) return -1;
|
||||
if (write_block(ctx, data + wrote)) return -1;
|
||||
wrote += ctx->partition->erase_size;
|
||||
}
|
||||
}
|
||||
@ -463,7 +496,7 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
|
||||
if (ctx->stored > 0) {
|
||||
size_t zero = ctx->partition->erase_size - ctx->stored;
|
||||
memset(ctx->buffer + ctx->stored, 0, zero);
|
||||
if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
|
||||
if (write_block(ctx, ctx->buffer)) return -1;
|
||||
ctx->stored = 0;
|
||||
}
|
||||
|
||||
@ -504,7 +537,23 @@ int mtd_write_close(MtdWriteContext *ctx)
|
||||
// Make sure any pending data gets written
|
||||
if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
|
||||
if (close(ctx->fd)) r = -1;
|
||||
free(ctx->bad_block_offsets);
|
||||
free(ctx->buffer);
|
||||
free(ctx);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Return the offset of the first good block at or after pos (which
|
||||
* might be pos itself).
|
||||
*/
|
||||
off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
|
||||
int i;
|
||||
for (i = 0; i < ctx->bad_block_count; ++i) {
|
||||
if (ctx->bad_block_offsets[i] == pos) {
|
||||
pos += ctx->partition->erase_size;
|
||||
} else if (ctx->bad_block_offsets[i] > pos) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
@ -49,6 +49,14 @@ void mtd_read_close(MtdReadContext *);
|
||||
MtdWriteContext *mtd_write_partition(const MtdPartition *);
|
||||
ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len);
|
||||
off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */
|
||||
off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
|
||||
int mtd_write_close(MtdWriteContext *);
|
||||
|
||||
struct MtdPartition {
|
||||
int device_index;
|
||||
unsigned int size;
|
||||
unsigned int erase_size;
|
||||
char *name;
|
||||
};
|
||||
|
||||
#endif // MTDUTILS_H_
|
||||
|
4
nandroid-md5.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/sbin/sh
|
||||
cd $1
|
||||
md5sum *img > nandroid.md5
|
||||
return $?
|
368
nandroid.c
Normal file
@ -0,0 +1,368 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "bootloader.h"
|
||||
#include "common.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "firmware.h"
|
||||
#include "install.h"
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/DirUtil.h"
|
||||
#include "roots.h"
|
||||
#include "recovery_ui.h"
|
||||
|
||||
#include "commands.h"
|
||||
#include "amend/amend.h"
|
||||
|
||||
#include "mtdutils/dump_image.h"
|
||||
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
|
||||
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
|
||||
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include "extendedcommands.h"
|
||||
#include "nandroid.h"
|
||||
|
||||
int print_and_error(char* message) {
|
||||
ui_print(message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int yaffs_files_total = 0;
|
||||
int yaffs_files_count = 0;
|
||||
void yaffs_callback(char* filename)
|
||||
{
|
||||
char* justfile = basename(filename);
|
||||
if (strlen(justfile) < 30)
|
||||
ui_print(justfile);
|
||||
yaffs_files_count++;
|
||||
if (yaffs_files_total != 0)
|
||||
ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total);
|
||||
ui_reset_text_col();
|
||||
}
|
||||
|
||||
void compute_directory_stats(char* directory)
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
sprintf(tmp, "find %s | wc -l > /tmp/dircount", directory);
|
||||
__system(tmp);
|
||||
char count_text[100];
|
||||
FILE* f = fopen("/tmp/dircount", "r");
|
||||
fread(count_text, 1, sizeof(count_text), f);
|
||||
fclose(f);
|
||||
yaffs_files_count = 0;
|
||||
yaffs_files_total = atoi(count_text);
|
||||
ui_reset_progress();
|
||||
ui_show_progress(1, 0);
|
||||
}
|
||||
|
||||
int nandroid_backup_partition_extended(const char* backup_path, char* root, int umount_when_finished) {
|
||||
int ret = 0;
|
||||
char mount_point[PATH_MAX];
|
||||
translate_root_path(root, mount_point, PATH_MAX);
|
||||
char* name = basename(mount_point);
|
||||
|
||||
struct stat file_info;
|
||||
mkyaffs2image_callback callback = NULL;
|
||||
if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
|
||||
callback = yaffs_callback;
|
||||
}
|
||||
|
||||
ui_print("Backing up %s...\n", name);
|
||||
if (0 != (ret = ensure_root_path_mounted(root) != 0)) {
|
||||
ui_print("Can't mount %s!\n", mount_point);
|
||||
return ret;
|
||||
}
|
||||
compute_directory_stats(mount_point);
|
||||
char tmp[PATH_MAX];
|
||||
sprintf(tmp, "%s/%s.img", backup_path, name);
|
||||
ret = mkyaffs2image(mount_point, tmp, 0, callback);
|
||||
if (umount_when_finished) {
|
||||
ensure_root_path_unmounted(root);
|
||||
}
|
||||
if (0 != ret) {
|
||||
ui_print("Error while making a yaffs2 image of %s!\n", mount_point);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nandroid_backup_partition(const char* backup_path, char* root) {
|
||||
return nandroid_backup_partition_extended(backup_path, root, 1);
|
||||
}
|
||||
|
||||
int nandroid_backup(const char* backup_path)
|
||||
{
|
||||
ui_set_background(BACKGROUND_ICON_INSTALLING);
|
||||
|
||||
if (ensure_root_path_mounted("SDCARD:") != 0)
|
||||
return print_and_error("Can't mount /sdcard\n");
|
||||
|
||||
int ret;
|
||||
struct statfs s;
|
||||
if (0 != (ret = statfs("/sdcard", &s)))
|
||||
return print_and_error("Unable to stat /sdcard\n");
|
||||
uint64_t bavail = s.f_bavail;
|
||||
uint64_t bsize = s.f_bsize;
|
||||
uint64_t sdcard_free = bavail * bsize;
|
||||
uint64_t sdcard_free_mb = sdcard_free / (uint64_t)(1024 * 1024);
|
||||
ui_print("SD Card space free: %lluMB\n", sdcard_free_mb);
|
||||
if (sdcard_free_mb < 150)
|
||||
ui_print("There may not be enough free space to complete backup... continuing...\n");
|
||||
|
||||
char tmp[PATH_MAX];
|
||||
sprintf(tmp, "mkdir -p %s", backup_path);
|
||||
__system(tmp);
|
||||
|
||||
#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
|
||||
ui_print("Backing up boot...\n");
|
||||
sprintf(tmp, "%s/%s", backup_path, "boot.img");
|
||||
ret = dump_image("boot", tmp, NULL);
|
||||
if (0 != ret)
|
||||
return print_and_error("Error while dumping boot image!\n");
|
||||
|
||||
ui_print("Backing up recovery...\n");
|
||||
sprintf(tmp, "%s/%s", backup_path, "recovery.img");
|
||||
ret = dump_image("recovery", tmp, NULL);
|
||||
if (0 != ret)
|
||||
return print_and_error("Error while dumping recovery image!\n");
|
||||
#endif
|
||||
|
||||
if (0 != (ret = nandroid_backup_partition(backup_path, "SYSTEM:")))
|
||||
return ret;
|
||||
|
||||
if (0 != (ret = nandroid_backup_partition(backup_path, "DATA:")))
|
||||
return ret;
|
||||
|
||||
#ifdef HAS_DATADATA
|
||||
if (0 != (ret = nandroid_backup_partition(backup_path, "DATADATA:")))
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
struct stat st;
|
||||
if (0 != stat("/sdcard/.android_secure", &st))
|
||||
{
|
||||
ui_print("No /sdcard/.android_secure found. Skipping backup of applications on external storage.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != (ret = nandroid_backup_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (0 != (ret = nandroid_backup_partition_extended(backup_path, "CACHE:", 0)))
|
||||
return ret;
|
||||
|
||||
if (0 != stat(SDEXT_DEVICE, &st))
|
||||
{
|
||||
ui_print("No sd-ext found. Skipping backup of sd-ext.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != ensure_root_path_mounted("SDEXT:"))
|
||||
ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.\n");
|
||||
else if (0 != (ret = nandroid_backup_partition(backup_path, "SDEXT:")))
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui_print("Generating md5 sum...\n");
|
||||
sprintf(tmp, "nandroid-md5.sh %s", backup_path);
|
||||
if (0 != (ret = __system(tmp))) {
|
||||
ui_print("Error while generating md5 sum!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sync();
|
||||
ui_set_background(BACKGROUND_ICON_NONE);
|
||||
ui_reset_progress();
|
||||
ui_print("\nBackup complete!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*format_function)(char* root);
|
||||
|
||||
static void ensure_directory(const char* dir) {
|
||||
char tmp[PATH_MAX];
|
||||
sprintf(tmp, "mkdir -p %s", dir);
|
||||
__system(tmp);
|
||||
}
|
||||
|
||||
int nandroid_restore_partition_extended(const char* backup_path, const char* root, int umount_when_finished) {
|
||||
int ret = 0;
|
||||
char mount_point[PATH_MAX];
|
||||
translate_root_path(root, mount_point, PATH_MAX);
|
||||
char* name = basename(mount_point);
|
||||
|
||||
char tmp[PATH_MAX];
|
||||
sprintf(tmp, "%s/%s.img", backup_path, name);
|
||||
struct stat file_info;
|
||||
if (0 != (ret = statfs(tmp, &file_info))) {
|
||||
ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ensure_directory(mount_point);
|
||||
|
||||
unyaffs_callback callback = NULL;
|
||||
if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
|
||||
callback = yaffs_callback;
|
||||
}
|
||||
|
||||
ui_print("Restoring %s...\n", name);
|
||||
/*
|
||||
if (0 != (ret = ensure_root_path_unmounted(root))) {
|
||||
ui_print("Can't unmount %s!\n", mount_point);
|
||||
return ret;
|
||||
}
|
||||
*/
|
||||
if (0 != (ret = format_root_device(root))) {
|
||||
ui_print("Error while formatting %s!\n", root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (0 != (ret = ensure_root_path_mounted(root))) {
|
||||
ui_print("Can't mount %s!\n", mount_point);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (0 != (ret = unyaffs(tmp, mount_point, callback))) {
|
||||
ui_print("Error while restoring %s!\n", mount_point);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (umount_when_finished) {
|
||||
ensure_root_path_unmounted(root);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nandroid_restore_partition(const char* backup_path, const char* root) {
|
||||
return nandroid_restore_partition_extended(backup_path, root, 1);
|
||||
}
|
||||
|
||||
int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext)
|
||||
{
|
||||
ui_set_background(BACKGROUND_ICON_INSTALLING);
|
||||
ui_show_indeterminate_progress();
|
||||
yaffs_files_total = 0;
|
||||
|
||||
if (ensure_root_path_mounted("SDCARD:") != 0)
|
||||
return print_and_error("Can't mount /sdcard\n");
|
||||
|
||||
char tmp[PATH_MAX];
|
||||
|
||||
ui_print("Checking MD5 sums...\n");
|
||||
sprintf(tmp, "cd %s && md5sum -c nandroid.md5", backup_path);
|
||||
if (0 != __system(tmp))
|
||||
return print_and_error("MD5 mismatch!\n");
|
||||
|
||||
int ret;
|
||||
#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
|
||||
if (restore_boot)
|
||||
{
|
||||
ui_print("Erasing boot before restore...\n");
|
||||
if (0 != (ret = format_root_device("BOOT:")))
|
||||
return print_and_error("Error while formatting BOOT:!\n");
|
||||
sprintf(tmp, "flash_image boot %s/boot.img", backup_path);
|
||||
ui_print("Restoring boot image...\n");
|
||||
if (0 != (ret = __system(tmp))) {
|
||||
ui_print("Error while flashing boot image!");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "SYSTEM:")))
|
||||
return ret;
|
||||
|
||||
if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATA:")))
|
||||
return ret;
|
||||
|
||||
#ifdef HAS_DATADATA
|
||||
if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATADATA:")))
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
|
||||
return ret;
|
||||
|
||||
if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "CACHE:", 0)))
|
||||
return ret;
|
||||
|
||||
if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "SDEXT:")))
|
||||
return ret;
|
||||
|
||||
sync();
|
||||
ui_set_background(BACKGROUND_ICON_NONE);
|
||||
ui_reset_progress();
|
||||
ui_print("\nRestore complete!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nandroid_generate_timestamp_path(char* backup_path)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm *tmp = localtime(&t);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
|
||||
}
|
||||
else
|
||||
{
|
||||
strftime(backup_path, PATH_MAX, "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
|
||||
}
|
||||
}
|
||||
|
||||
int nandroid_usage()
|
||||
{
|
||||
printf("Usage: nandroid backup\n");
|
||||
printf("Usage: nandroid restore <directory>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nandroid_main(int argc, char** argv)
|
||||
{
|
||||
if (argc > 3 || argc < 2)
|
||||
return nandroid_usage();
|
||||
|
||||
if (strcmp("backup", argv[1]) == 0)
|
||||
{
|
||||
if (argc != 2)
|
||||
return nandroid_usage();
|
||||
|
||||
char backup_path[PATH_MAX];
|
||||
nandroid_generate_timestamp_path(backup_path);
|
||||
return nandroid_backup(backup_path);
|
||||
}
|
||||
|
||||
if (strcmp("restore", argv[1]) == 0)
|
||||
{
|
||||
if (argc != 3)
|
||||
return nandroid_usage();
|
||||
return nandroid_restore(argv[2], 1, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
return nandroid_usage();
|
||||
}
|
9
nandroid.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef NANDROID_H
|
||||
#define NANDROID_H
|
||||
|
||||
int nandroid_main(int argc, char** argv);
|
||||
int nandroid_backup(const char* backup_path);
|
||||
int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext);
|
||||
void nandroid_generate_timestamp_path(char* backup_path);
|
||||
|
||||
#endif
|
72
reboot.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
int reboot_main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
int nosync = 0;
|
||||
int poweroff = 0;
|
||||
int force = 0;
|
||||
|
||||
opterr = 0;
|
||||
do {
|
||||
int c;
|
||||
|
||||
c = getopt(argc, argv, "npf");
|
||||
|
||||
if (c == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'n':
|
||||
nosync = 1;
|
||||
break;
|
||||
case 'p':
|
||||
poweroff = 1;
|
||||
break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} while (1);
|
||||
|
||||
if(argc > optind + 1) {
|
||||
fprintf(stderr, "%s: too many arguments\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(!nosync)
|
||||
sync();
|
||||
|
||||
if(force || argc > optind) {
|
||||
if(poweroff)
|
||||
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
|
||||
else if(argc > optind)
|
||||
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]);
|
||||
else
|
||||
ret = reboot(RB_AUTOBOOT);
|
||||
} else {
|
||||
if(poweroff) {
|
||||
property_set("ctl.start", "poweroff");
|
||||
ret = 0;
|
||||
} else {
|
||||
property_set("ctl.start", "reboot");
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret < 0) {
|
||||
perror("reboot");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "reboot returned\n");
|
||||
return 0;
|
||||
}
|
385
recovery.c
@ -27,9 +27,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "bootloader.h"
|
||||
#include "commands.h"
|
||||
#include "common.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "firmware.h"
|
||||
@ -37,14 +37,21 @@
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/DirUtil.h"
|
||||
#include "roots.h"
|
||||
#include "recovery_ui.h"
|
||||
|
||||
#include "extendedcommands.h"
|
||||
#include "commands.h"
|
||||
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "send_intent", required_argument, NULL, 's' },
|
||||
{ "update_package", required_argument, NULL, 'u' },
|
||||
{ "wipe_data", no_argument, NULL, 'w' },
|
||||
{ "wipe_cache", no_argument, NULL, 'c' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
static int allow_display_toggle = 1;
|
||||
|
||||
static const char *COMMAND_FILE = "CACHE:recovery/command";
|
||||
static const char *INTENT_FILE = "CACHE:recovery/intent";
|
||||
static const char *LOG_FILE = "CACHE:recovery/log";
|
||||
@ -130,7 +137,7 @@ fopen_root_path(const char *root_path, const char *mode) {
|
||||
if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
|
||||
|
||||
FILE *fp = fopen(path, mode);
|
||||
if (fp == NULL) LOGE("Can't open %s\n", path);
|
||||
if (fp == NULL && root_path != COMMAND_FILE) LOGE("Can't open %s\n", path);
|
||||
return fp;
|
||||
}
|
||||
|
||||
@ -150,7 +157,9 @@ static void
|
||||
get_args(int *argc, char ***argv) {
|
||||
struct bootloader_message boot;
|
||||
memset(&boot, 0, sizeof(boot));
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
|
||||
#endif
|
||||
|
||||
if (boot.command[0] != 0 && boot.command[0] != 255) {
|
||||
LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
|
||||
@ -160,8 +169,10 @@ get_args(int *argc, char ***argv) {
|
||||
LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
|
||||
}
|
||||
|
||||
struct stat file_info;
|
||||
|
||||
// --- if arguments weren't supplied, look in the bootloader control block
|
||||
if (*argc <= 1) {
|
||||
if (*argc <= 1 && 0 != stat("/tmp/.ignorebootmessage", &file_info)) {
|
||||
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
|
||||
const char *arg = strtok(boot.recovery, "\n");
|
||||
if (arg != NULL && !strcmp(arg, "recovery")) {
|
||||
@ -205,9 +216,22 @@ get_args(int *argc, char ***argv) {
|
||||
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
|
||||
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
|
||||
}
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
set_bootloader_message(&boot);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
void
|
||||
set_sdcard_update_bootloader_message()
|
||||
{
|
||||
struct bootloader_message boot;
|
||||
memset(&boot, 0, sizeof(boot));
|
||||
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
|
||||
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
|
||||
set_bootloader_message(&boot);
|
||||
}
|
||||
#endif
|
||||
|
||||
// clear the recovery command and prepare to boot a (hopefully working) system,
|
||||
// copy our log file to cache as well (for the system to read), and
|
||||
@ -242,10 +266,12 @@ finish_recovery(const char *send_intent)
|
||||
check_and_fclose(log, LOG_FILE);
|
||||
}
|
||||
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
// Reset the bootloader message to revert to a normal main system boot.
|
||||
struct bootloader_message boot;
|
||||
memset(&boot, 0, sizeof(boot));
|
||||
set_bootloader_message(&boot);
|
||||
#endif
|
||||
|
||||
// Remove the command file, so recovery won't repeat indefinitely.
|
||||
char path[PATH_MAX] = "";
|
||||
@ -258,27 +284,6 @@ finish_recovery(const char *send_intent)
|
||||
sync(); // For good measure.
|
||||
}
|
||||
|
||||
#define TEST_AMEND 0
|
||||
#if TEST_AMEND
|
||||
static void
|
||||
test_amend()
|
||||
{
|
||||
extern int test_symtab(void);
|
||||
extern int test_cmd_fn(void);
|
||||
extern int test_permissions(void);
|
||||
int ret;
|
||||
LOGD("Testing symtab...\n");
|
||||
ret = test_symtab();
|
||||
LOGD(" returned %d\n", ret);
|
||||
LOGD("Testing cmd_fn...\n");
|
||||
ret = test_cmd_fn();
|
||||
LOGD(" returned %d\n", ret);
|
||||
LOGD("Testing permissions...\n");
|
||||
ret = test_permissions();
|
||||
LOGD(" returned %d\n", ret);
|
||||
}
|
||||
#endif // TEST_AMEND
|
||||
|
||||
static int
|
||||
erase_root(const char *root)
|
||||
{
|
||||
@ -288,77 +293,188 @@ erase_root(const char *root)
|
||||
return format_root_device(root);
|
||||
}
|
||||
|
||||
static void
|
||||
prompt_and_wait()
|
||||
{
|
||||
char* headers[] = { "Android system recovery utility",
|
||||
"",
|
||||
"Use trackball to highlight;",
|
||||
"click to select.",
|
||||
"",
|
||||
NULL };
|
||||
|
||||
// these constants correspond to elements of the items[] list.
|
||||
#define ITEM_REBOOT 0
|
||||
#define ITEM_APPLY_SDCARD 1
|
||||
#define ITEM_WIPE_DATA 2
|
||||
char* items[] = { "reboot system now [Home+Back]",
|
||||
"apply sdcard:update.zip [Alt+S]",
|
||||
"wipe data/factory reset [Alt+W]",
|
||||
static char**
|
||||
prepend_title(char** headers) {
|
||||
char* title[] = { EXPAND(RECOVERY_VERSION),
|
||||
"",
|
||||
NULL };
|
||||
|
||||
ui_start_menu(headers, items);
|
||||
// count the number of lines in our title, plus the
|
||||
// caller-provided headers.
|
||||
int count = 0;
|
||||
char** p;
|
||||
for (p = title; *p; ++p, ++count);
|
||||
for (p = headers; *p; ++p, ++count);
|
||||
|
||||
char** new_headers = malloc((count+1) * sizeof(char*));
|
||||
char** h = new_headers;
|
||||
for (p = title; *p; ++p, ++h) *h = *p;
|
||||
for (p = headers; *p; ++p, ++h) *h = *p;
|
||||
*h = NULL;
|
||||
|
||||
return new_headers;
|
||||
}
|
||||
|
||||
int
|
||||
get_menu_selection(char** headers, char** items, int menu_only) {
|
||||
// throw away keys pressed previously, so user doesn't
|
||||
// accidentally trigger menu items.
|
||||
ui_clear_key_queue();
|
||||
|
||||
int item_count = ui_start_menu(headers, items);
|
||||
int selected = 0;
|
||||
int chosen_item = -1;
|
||||
|
||||
finish_recovery(NULL);
|
||||
ui_reset_progress();
|
||||
for (;;) {
|
||||
// Some users with dead enter keys need a way to turn on power to select.
|
||||
// Jiggering across the wrapping menu is one "secret" way to enable it.
|
||||
// We can't rely on /cache or /sdcard since they may not be available.
|
||||
int wrap_count = 0;
|
||||
|
||||
while (chosen_item < 0 && chosen_item != GO_BACK) {
|
||||
int key = ui_wait_key();
|
||||
int alt = ui_key_pressed(KEY_LEFTALT) || ui_key_pressed(KEY_RIGHTALT);
|
||||
int visible = ui_text_visible();
|
||||
|
||||
if (key == KEY_DREAM_BACK && ui_key_pressed(KEY_DREAM_HOME)) {
|
||||
// Wait for the keys to be released, to avoid triggering
|
||||
// special boot modes (like coming back into recovery!).
|
||||
while (ui_key_pressed(KEY_DREAM_BACK) ||
|
||||
ui_key_pressed(KEY_DREAM_HOME)) {
|
||||
usleep(1000);
|
||||
int action = device_handle_key(key, visible);
|
||||
|
||||
int old_selected = selected;
|
||||
|
||||
if (action < 0) {
|
||||
switch (action) {
|
||||
case HIGHLIGHT_UP:
|
||||
--selected;
|
||||
selected = ui_menu_select(selected);
|
||||
break;
|
||||
case HIGHLIGHT_DOWN:
|
||||
++selected;
|
||||
selected = ui_menu_select(selected);
|
||||
break;
|
||||
case SELECT_ITEM:
|
||||
chosen_item = selected;
|
||||
if (ui_get_showing_back_button()) {
|
||||
if (chosen_item == item_count) {
|
||||
chosen_item = GO_BACK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NO_ACTION:
|
||||
break;
|
||||
case GO_BACK:
|
||||
chosen_item = GO_BACK;
|
||||
break;
|
||||
}
|
||||
chosen_item = ITEM_REBOOT;
|
||||
} else if (alt && key == KEY_W) {
|
||||
chosen_item = ITEM_WIPE_DATA;
|
||||
} else if (alt && key == KEY_S) {
|
||||
chosen_item = ITEM_APPLY_SDCARD;
|
||||
} else if ((key == KEY_DOWN || key == KEY_VOLUMEDOWN) && visible) {
|
||||
++selected;
|
||||
selected = ui_menu_select(selected);
|
||||
} else if ((key == KEY_UP || key == KEY_VOLUMEUP) && visible) {
|
||||
--selected;
|
||||
selected = ui_menu_select(selected);
|
||||
} else if (key == BTN_MOUSE && visible) {
|
||||
chosen_item = selected;
|
||||
} else if (!menu_only) {
|
||||
chosen_item = action;
|
||||
}
|
||||
|
||||
if (chosen_item >= 0) {
|
||||
// turn off the menu, letting ui_print() to scroll output
|
||||
// on the screen.
|
||||
ui_end_menu();
|
||||
if (abs(selected - old_selected) > 1) {
|
||||
wrap_count++;
|
||||
if (wrap_count == 3) {
|
||||
wrap_count = 0;
|
||||
if (ui_get_showing_back_button()) {
|
||||
ui_print("Back menu button disabled.\n");
|
||||
ui_set_showing_back_button(0);
|
||||
}
|
||||
else {
|
||||
ui_print("Back menu button enabled.\n");
|
||||
ui_set_showing_back_button(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (chosen_item) {
|
||||
case ITEM_REBOOT:
|
||||
return;
|
||||
ui_end_menu();
|
||||
ui_clear_key_queue();
|
||||
return chosen_item;
|
||||
}
|
||||
|
||||
case ITEM_WIPE_DATA:
|
||||
ui_print("\n-- Wiping data...\n");
|
||||
erase_root("DATA:");
|
||||
static void
|
||||
wipe_data(int confirm) {
|
||||
if (confirm) {
|
||||
static char** title_headers = NULL;
|
||||
|
||||
if (title_headers == NULL) {
|
||||
char* headers[] = { "Confirm wipe of all user data?",
|
||||
" THIS CAN NOT BE UNDONE.",
|
||||
"",
|
||||
NULL };
|
||||
title_headers = prepend_title(headers);
|
||||
}
|
||||
|
||||
char* items[] = { " No",
|
||||
" No",
|
||||
" No",
|
||||
" No",
|
||||
" No",
|
||||
" No",
|
||||
" No",
|
||||
" Yes -- delete all user data", // [7]
|
||||
" No",
|
||||
" No",
|
||||
" No",
|
||||
NULL };
|
||||
|
||||
int chosen_item = get_menu_selection(title_headers, items, 1);
|
||||
if (chosen_item != 7) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ui_print("\n-- Wiping data...\n");
|
||||
device_wipe_data();
|
||||
erase_root("DATA:");
|
||||
#ifdef HAS_DATADATA
|
||||
erase_root("DATADATA:");
|
||||
#endif
|
||||
erase_root("CACHE:");
|
||||
erase_root("SDEXT:");
|
||||
erase_root("SDCARD:/.android_secure");
|
||||
ui_print("Data wipe complete.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
prompt_and_wait()
|
||||
{
|
||||
char** headers = prepend_title(MENU_HEADERS);
|
||||
|
||||
for (;;) {
|
||||
finish_recovery(NULL);
|
||||
ui_reset_progress();
|
||||
|
||||
allow_display_toggle = 1;
|
||||
int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0);
|
||||
allow_display_toggle = 0;
|
||||
|
||||
// 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_perform_action(chosen_item);
|
||||
|
||||
switch (chosen_item) {
|
||||
case ITEM_REBOOT:
|
||||
return;
|
||||
|
||||
case ITEM_WIPE_DATA:
|
||||
wipe_data(ui_text_visible());
|
||||
if (!ui_text_visible()) return;
|
||||
break;
|
||||
|
||||
case ITEM_WIPE_CACHE:
|
||||
if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache"))
|
||||
{
|
||||
ui_print("\n-- Wiping cache...\n");
|
||||
erase_root("CACHE:");
|
||||
ui_print("Data wipe complete.\n");
|
||||
ui_print("Cache wipe complete.\n");
|
||||
if (!ui_text_visible()) return;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_APPLY_SDCARD:
|
||||
case ITEM_APPLY_SDCARD:
|
||||
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
|
||||
{
|
||||
ui_print("\n-- Install from sdcard...\n");
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
set_sdcard_update_bootloader_message();
|
||||
#endif
|
||||
int status = install_package(SDCARD_PACKAGE_FILE);
|
||||
if (status != INSTALL_SUCCESS) {
|
||||
ui_set_background(BACKGROUND_ICON_ERROR);
|
||||
@ -366,23 +482,31 @@ prompt_and_wait()
|
||||
} else if (!ui_text_visible()) {
|
||||
return; // reboot if logs aren't visible
|
||||
} else {
|
||||
ui_print("Install from sdcard complete.\n");
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
if (firmware_update_pending()) {
|
||||
ui_print("\nReboot via menu to complete\n"
|
||||
"installation.\n");
|
||||
} else {
|
||||
ui_print("\nInstall from sdcard complete.\n");
|
||||
}
|
||||
#else
|
||||
ui_print("\nInstall from sdcard complete.\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// if we didn't return from this function to reboot, show
|
||||
// the menu again.
|
||||
ui_start_menu(headers, items);
|
||||
selected = 0;
|
||||
chosen_item = -1;
|
||||
|
||||
finish_recovery(NULL);
|
||||
ui_reset_progress();
|
||||
|
||||
// throw away keys pressed while the command was running,
|
||||
// so user doesn't accidentally trigger menu items.
|
||||
ui_clear_key_queue();
|
||||
}
|
||||
break;
|
||||
case ITEM_INSTALL_ZIP:
|
||||
show_install_update_menu();
|
||||
break;
|
||||
case ITEM_NANDROID:
|
||||
show_nandroid_menu();
|
||||
break;
|
||||
case ITEM_PARTITION:
|
||||
show_partition_menu();
|
||||
break;
|
||||
case ITEM_ADVANCED:
|
||||
show_advanced_menu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,6 +520,32 @@ print_property(const char *key, const char *name, void *cookie)
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (strstr(argv[0], "recovery") == NULL)
|
||||
{
|
||||
if (strstr(argv[0], "flash_image") != NULL)
|
||||
return flash_image_main(argc, argv);
|
||||
if (strstr(argv[0], "dump_image") != NULL)
|
||||
return dump_image_main(argc, argv);
|
||||
if (strstr(argv[0], "erase_image") != NULL)
|
||||
return erase_image_main(argc, argv);
|
||||
if (strstr(argv[0], "mkyaffs2image") != NULL)
|
||||
return mkyaffs2image_main(argc, argv);
|
||||
if (strstr(argv[0], "unyaffs") != NULL)
|
||||
return unyaffs_main(argc, argv);
|
||||
if (strstr(argv[0], "amend"))
|
||||
return amend_main(argc, argv);
|
||||
if (strstr(argv[0], "nandroid"))
|
||||
return nandroid_main(argc, argv);
|
||||
if (strstr(argv[0], "reboot"))
|
||||
return reboot_main(argc, argv);
|
||||
if (strstr(argv[0], "setprop"))
|
||||
return setprop_main(argc, argv);
|
||||
return busybox_driver(argc, argv);
|
||||
}
|
||||
__system("/sbin/postrecoveryboot.sh");
|
||||
create_fstab();
|
||||
|
||||
int is_user_initiated_recovery = 0;
|
||||
time_t start = time(NULL);
|
||||
|
||||
// If these fail, there's not really anywhere to complain...
|
||||
@ -404,6 +554,7 @@ main(int argc, char **argv)
|
||||
fprintf(stderr, "Starting recovery on %s", ctime(&start));
|
||||
|
||||
ui_init();
|
||||
ui_print(EXPAND(RECOVERY_VERSION)"\n");
|
||||
get_args(&argc, &argv);
|
||||
|
||||
int previous_runs = 0;
|
||||
@ -434,33 +585,57 @@ main(int argc, char **argv)
|
||||
property_list(print_property, NULL);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
#if TEST_AMEND
|
||||
test_amend();
|
||||
#endif
|
||||
|
||||
int status = INSTALL_SUCCESS;
|
||||
|
||||
RecoveryCommandContext ctx = { NULL };
|
||||
if (register_update_commands(&ctx)) {
|
||||
LOGE("Can't install update commands\n");
|
||||
}
|
||||
|
||||
int status = INSTALL_SUCCESS;
|
||||
|
||||
if (update_package != NULL) {
|
||||
if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR;
|
||||
status = install_package(update_package);
|
||||
if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
|
||||
} else if (wipe_data || wipe_cache) {
|
||||
if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR;
|
||||
} else if (wipe_data) {
|
||||
if (device_wipe_data()) status = INSTALL_ERROR;
|
||||
if (erase_root("DATA:")) status = INSTALL_ERROR;
|
||||
if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
|
||||
if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
|
||||
} else if (wipe_cache) {
|
||||
if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
|
||||
if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
|
||||
} else {
|
||||
LOGI("Checking for extendedcommand...\n");
|
||||
status = INSTALL_ERROR; // No command specified
|
||||
// we are starting up in user initiated recovery here
|
||||
// let's set up some default options
|
||||
signature_check_enabled = 0;
|
||||
script_assert_enabled = 0;
|
||||
is_user_initiated_recovery = 1;
|
||||
ui_set_show_text(1);
|
||||
|
||||
if (extendedcommand_file_exists()) {
|
||||
LOGI("Running extendedcommand...\n");
|
||||
int ret;
|
||||
if (0 == (ret = run_and_remove_extendedcommand())) {
|
||||
status = INSTALL_SUCCESS;
|
||||
ui_set_show_text(0);
|
||||
}
|
||||
else {
|
||||
handle_failure(ret);
|
||||
}
|
||||
} else {
|
||||
LOGI("Skipping execution of extendedcommand, file not found...\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
|
||||
if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR);
|
||||
if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait();
|
||||
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
// If there is a radio image pending, reboot now to install it.
|
||||
maybe_install_firmware_update(send_intent);
|
||||
#endif
|
||||
|
||||
// Otherwise, get ready to boot the main system...
|
||||
finish_recovery(send_intent);
|
||||
@ -469,3 +644,7 @@ main(int argc, char **argv)
|
||||
reboot(RB_AUTOBOOT);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int get_allow_toggle_display() {
|
||||
return allow_display_toggle;
|
||||
}
|
||||
|
87
recovery_ui.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 _RECOVERY_UI_H
|
||||
#define _RECOVERY_UI_H
|
||||
|
||||
// Called in the input thread when a new key (key_code) is pressed.
|
||||
// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
|
||||
// keys are already pressed. Return true if the text display should
|
||||
// be toggled.
|
||||
extern int device_toggle_display(volatile char* key_pressed, int key_code);
|
||||
|
||||
// Called in the input thread when a new key (key_code) is pressed.
|
||||
// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
|
||||
// keys are already pressed. Return true if the device should reboot
|
||||
// immediately.
|
||||
extern int device_reboot_now(volatile char* key_pressed, int key_code);
|
||||
|
||||
// Called from the main thread when recovery is waiting for input and
|
||||
// a key is pressed. key is the code of the key pressed; visible is
|
||||
// true if the recovery menu is being shown. Implementations can call
|
||||
// ui_key_pressed() to discover if other keys are being held down.
|
||||
// Return one of the defined constants below in order to:
|
||||
//
|
||||
// - move the menu highlight (HIGHLIGHT_*)
|
||||
// - invoke the highlighted item (SELECT_ITEM)
|
||||
// - do nothing (NO_ACTION)
|
||||
// - invoke a specific action (a menu position: any non-negative number)
|
||||
extern int device_handle_key(int key, int visible);
|
||||
|
||||
// Perform a recovery action selected from the menu. 'which' will be
|
||||
// the item number of the selected menu item, or a non-negative number
|
||||
// returned from device_handle_key(). The menu will be hidden when
|
||||
// this is called; implementations can call ui_print() to print
|
||||
// information to the screen.
|
||||
extern int device_perform_action(int which);
|
||||
|
||||
// Called when we do a wipe data/factory reset operation (either via a
|
||||
// reboot from the main system with the --wipe_data flag, or when the
|
||||
// user boots into recovery manually and selects the option from the
|
||||
// menu.) Can perform whatever device-specific wiping actions are
|
||||
// needed. Return 0 on success. The userdata and cache partitions
|
||||
// are erased after this returns (whether it returns success or not).
|
||||
int device_wipe_data();
|
||||
|
||||
#define NO_ACTION -1
|
||||
|
||||
#define HIGHLIGHT_UP -2
|
||||
#define HIGHLIGHT_DOWN -3
|
||||
#define SELECT_ITEM -4
|
||||
#define GO_BACK -5
|
||||
|
||||
#define ITEM_REBOOT 0
|
||||
#define ITEM_APPLY_SDCARD 1
|
||||
#define ITEM_WIPE_DATA 2
|
||||
#define ITEM_WIPE_CACHE 3
|
||||
#define ITEM_INSTALL_ZIP 4
|
||||
#define ITEM_NANDROID 5
|
||||
#define ITEM_PARTITION 6
|
||||
#define ITEM_ADVANCED 7
|
||||
|
||||
// Header text to display above the main menu.
|
||||
extern char* MENU_HEADERS[];
|
||||
|
||||
// Text of menu items.
|
||||
extern char* MENU_ITEMS[];
|
||||
|
||||
int
|
||||
get_menu_selection(char** headers, char** items, int menu_only);
|
||||
|
||||
void
|
||||
set_sdcard_update_bootloader_message();
|
||||
|
||||
#endif
|
Before Width: | Height: | Size: 89 KiB |
BIN
res/images/icon_error.png
Executable file
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 89 KiB |
BIN
res/images/icon_firmware_error.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 89 KiB |
BIN
res/images/icon_firmware_install.png
Executable file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 89 KiB |
BIN
res/images/icon_installing.png
Executable file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate1.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate2.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate3.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate4.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate5.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate6.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 136 B |
BIN
res/images/progress_bar_empty.png
Normal file
After Width: | Height: | Size: 148 B |
Before Width: | Height: | Size: 294 B |
BIN
res/images/progress_bar_empty_left_round.png
Normal file
After Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 294 B |
BIN
res/images/progress_bar_empty_right_round.png
Normal file
After Width: | Height: | Size: 211 B |
Before Width: | Height: | Size: 136 B |
BIN
res/images/progress_bar_fill.png
Normal file
After Width: | Height: | Size: 117 B |
Before Width: | Height: | Size: 294 B |
BIN
res/images/progress_bar_left_round.png
Normal file
After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 294 B |
BIN
res/images/progress_bar_right_round.png
Normal file
After Width: | Height: | Size: 192 B |
66
roots.c
@ -21,20 +21,15 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "mtdutils/mounts.h"
|
||||
#include "minzip/Zip.h"
|
||||
#include "roots.h"
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *device;
|
||||
const char *device2; // If the first one doesn't work (may be NULL)
|
||||
const char *partition_name;
|
||||
const char *mount_point;
|
||||
const char *filesystem;
|
||||
} RootInfo;
|
||||
#include "extendedcommands.h"
|
||||
|
||||
/* Canonical pointers.
|
||||
xxx may just want to use enums
|
||||
@ -44,21 +39,26 @@ static const char g_raw[] = "@\0g_raw";
|
||||
static const char g_package_file[] = "@\0g_package_file";
|
||||
|
||||
static RootInfo g_roots[] = {
|
||||
{ "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw },
|
||||
{ "CACHE:", g_mtd_device, NULL, "cache", "/cache", "yaffs2" },
|
||||
{ "DATA:", g_mtd_device, NULL, "userdata", "/data", "yaffs2" },
|
||||
{ "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw },
|
||||
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file },
|
||||
{ "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw },
|
||||
{ "SDCARD:", "/dev/block/mmcblk0p1", "/dev/block/mmcblk0", NULL, "/sdcard", "vfat" },
|
||||
{ "SYSTEM:", g_mtd_device, NULL, "system", "/system", "yaffs2" },
|
||||
{ "TMP:", NULL, NULL, NULL, "/tmp", NULL },
|
||||
{ "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw, NULL },
|
||||
{ "CACHE:", CACHE_DEVICE, NULL, "cache", "/cache", CACHE_FILESYSTEM, CACHE_FILESYSTEM_OPTIONS },
|
||||
{ "DATA:", DATA_DEVICE, NULL, "userdata", "/data", DATA_FILESYSTEM, DATA_FILESYSTEM_OPTIONS },
|
||||
#ifdef HAS_DATADATA
|
||||
{ "DATADATA:", DATADATA_DEVICE, NULL, "datadata", "/datadata", DATADATA_FILESYSTEM, DATADATA_FILESYSTEM_OPTIONS },
|
||||
#endif
|
||||
{ "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw, NULL },
|
||||
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file, NULL },
|
||||
{ "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw, NULL },
|
||||
{ "SDCARD:", SDCARD_DEVICE_PRIMARY, SDCARD_DEVICE_SECONDARY, NULL, "/sdcard", "vfat", NULL },
|
||||
{ "SDEXT:", SDEXT_DEVICE, NULL, NULL, "/sd-ext", SDEXT_FILESYSTEM, NULL },
|
||||
{ "SYSTEM:", SYSTEM_DEVICE, NULL, "system", "/system", SYSTEM_FILESYSTEM, SYSTEM_FILESYSTEM_OPTIONS },
|
||||
{ "MBM:", g_mtd_device, NULL, "mbm", NULL, g_raw, NULL },
|
||||
{ "TMP:", NULL, NULL, NULL, "/tmp", NULL, NULL },
|
||||
};
|
||||
#define NUM_ROOTS (sizeof(g_roots) / sizeof(g_roots[0]))
|
||||
|
||||
// TODO: for SDCARD:, try /dev/block/mmcblk0 if mmcblk0p1 fails
|
||||
|
||||
static const RootInfo *
|
||||
const RootInfo *
|
||||
get_root_info_for_path(const char *root_path)
|
||||
{
|
||||
const char *c;
|
||||
@ -207,6 +207,19 @@ is_root_path_mounted(const char *root_path)
|
||||
return internal_root_mounted(info) >= 0;
|
||||
}
|
||||
|
||||
static int mount_internal(const char* device, const char* mount_point, const char* filesystem, const char* filesystem_options)
|
||||
{
|
||||
if (strcmp(filesystem, "auto") != 0 && filesystem_options == NULL) {
|
||||
return mount(device, mount_point, filesystem, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
|
||||
}
|
||||
else {
|
||||
char mount_cmd[PATH_MAX];
|
||||
const char* options = filesystem_options == NULL ? "noatime,nodiratime,nodev" : filesystem_options;
|
||||
sprintf(mount_cmd, "mount -t %s -o%s %s %s", filesystem, options, device, mount_point);
|
||||
return __system(mount_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ensure_root_path_mounted(const char *root_path)
|
||||
{
|
||||
@ -247,8 +260,7 @@ ensure_root_path_mounted(const char *root_path)
|
||||
}
|
||||
|
||||
mkdir(info->mount_point, 0755); // in case it doesn't already exist
|
||||
if (mount(info->device, info->mount_point, info->filesystem,
|
||||
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
|
||||
if (mount_internal(info->device, info->mount_point, info->filesystem, info->filesystem_options)) {
|
||||
if (info->device2 == NULL) {
|
||||
LOGE("Can't mount %s\n(%s)\n", info->device, strerror(errno));
|
||||
return -1;
|
||||
@ -300,7 +312,12 @@ get_root_mtd_partition(const char *root_path)
|
||||
if (info == NULL || info->device != g_mtd_device ||
|
||||
info->partition_name == NULL)
|
||||
{
|
||||
#ifdef BOARD_HAS_MTD_CACHE
|
||||
if (strcmp(root_path, "CACHE:") != 0)
|
||||
return NULL;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
mtd_scan_partitions();
|
||||
return mtd_find_partition_by_name(info->partition_name);
|
||||
@ -316,17 +333,19 @@ format_root_device(const char *root)
|
||||
while (*c != '\0' && *c != ':') {
|
||||
c++;
|
||||
}
|
||||
/*
|
||||
if (c[0] != ':' || c[1] != '\0') {
|
||||
LOGW("format_root_device: bad root name \"%s\"\n", root);
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
const RootInfo *info = get_root_info_for_path(root);
|
||||
if (info == NULL || info->device == NULL) {
|
||||
LOGW("format_root_device: can't resolve \"%s\"\n", root);
|
||||
return -1;
|
||||
}
|
||||
if (info->mount_point != NULL) {
|
||||
if (info->mount_point != NULL && info->device == g_mtd_device) {
|
||||
/* Don't try to format a mounted device.
|
||||
*/
|
||||
int ret = ensure_root_path_unmounted(root);
|
||||
@ -364,7 +383,6 @@ format_root_device(const char *root)
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO: handle other device types (sdcard, etc.)
|
||||
LOGW("format_root_device: can't handle non-mtd device \"%s\"\n", root);
|
||||
return -1;
|
||||
|
||||
return format_non_mtd_device(root);
|
||||
}
|
||||
|
75
roots.h
@ -20,6 +20,71 @@
|
||||
#include "minzip/Zip.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
|
||||
#ifndef SDCARD_DEVICE_PRIMARY
|
||||
#define SDCARD_DEVICE_PRIMARY "/dev/block/mmcblk0p1"
|
||||
#endif
|
||||
|
||||
#ifndef SDCARD_DEVICE_SECONDARY
|
||||
#define SDCARD_DEVICE_SECONDARY "/dev/block/mmcblk0"
|
||||
#endif
|
||||
|
||||
#ifndef SDEXT_DEVICE
|
||||
#define SDEXT_DEVICE "/dev/block/mmcblk0p2"
|
||||
#endif
|
||||
|
||||
#ifndef SDEXT_FILESYSTEM
|
||||
#define SDEXT_FILESYSTEM "auto"
|
||||
#endif
|
||||
|
||||
#ifndef DATA_DEVICE
|
||||
#define DATA_DEVICE g_mtd_device
|
||||
#endif
|
||||
|
||||
#ifndef DATA_FILESYSTEM
|
||||
#define DATA_FILESYSTEM "yaffs2"
|
||||
#endif
|
||||
|
||||
#ifndef DATADATA_DEVICE
|
||||
#define DATADATA_DEVICE g_mtd_device
|
||||
#endif
|
||||
|
||||
#ifndef DATADATA_FILESYSTEM
|
||||
#define DATADATA_FILESYSTEM "yaffs2"
|
||||
#endif
|
||||
|
||||
#ifndef CACHE_DEVICE
|
||||
#define CACHE_DEVICE g_mtd_device
|
||||
#endif
|
||||
|
||||
#ifndef CACHE_FILESYSTEM
|
||||
#define CACHE_FILESYSTEM "yaffs2"
|
||||
#endif
|
||||
|
||||
#ifndef SYSTEM_DEVICE
|
||||
#define SYSTEM_DEVICE g_mtd_device
|
||||
#endif
|
||||
|
||||
#ifndef SYSTEM_FILESYSTEM
|
||||
#define SYSTEM_FILESYSTEM "yaffs2"
|
||||
#endif
|
||||
|
||||
#ifndef DATA_FILESYSTEM_OPTIONS
|
||||
#define DATA_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef CACHE_FILESYSTEM_OPTIONS
|
||||
#define CACHE_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef DATADATA_FILESYSTEM_OPTIONS
|
||||
#define DATADATA_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef SYSTEM_FILESYSTEM_OPTIONS
|
||||
#define SYSTEM_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
|
||||
/* Any of the "root_path" arguments can be paths with relative
|
||||
* components, like "SYSTEM:a/b/c".
|
||||
*/
|
||||
@ -60,4 +125,14 @@ const MtdPartition *get_root_mtd_partition(const char *root_path);
|
||||
*/
|
||||
int format_root_device(const char *root);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *device;
|
||||
const char *device2; // If the first one doesn't work (may be NULL)
|
||||
const char *partition_name;
|
||||
const char *mount_point;
|
||||
const char *filesystem;
|
||||
const char *filesystem_options;
|
||||
} RootInfo;
|
||||
|
||||
#endif // RECOVERY_ROOTS_H_
|
||||
|
18
setprop.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
int setprop_main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 3) {
|
||||
fprintf(stderr,"usage: setprop <key> <value>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(property_set(argv[1], argv[2])){
|
||||
fprintf(stderr,"could not set property\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -14,11 +14,6 @@
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := make-update-script
|
||||
LOCAL_SRC_FILES := make-update-script.c
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 "private/android_filesystem_config.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Recursively walk the directory tree at <sysdir>/<subdir>, writing
|
||||
* script commands to set permissions and create symlinks.
|
||||
* Assume the contents already have the specified default permissions,
|
||||
* so only output commands if they need to be changed from the defaults.
|
||||
*
|
||||
* Note that permissions are set by fs_config(), which uses a lookup table of
|
||||
* Android permissions. They are not drawn from the build host filesystem.
|
||||
*/
|
||||
static void walk_files(
|
||||
const char *sysdir, const char *subdir,
|
||||
unsigned default_uid, unsigned default_gid,
|
||||
unsigned default_dir_mode, unsigned default_file_mode) {
|
||||
const char *sep = strcmp(subdir, "") ? "/" : "";
|
||||
|
||||
char fn[PATH_MAX];
|
||||
unsigned dir_uid = 0, dir_gid = 0, dir_mode = 0;
|
||||
snprintf(fn, PATH_MAX, "system%s%s", sep, subdir);
|
||||
fs_config(fn, 1, &dir_uid, &dir_gid, &dir_mode);
|
||||
|
||||
snprintf(fn, PATH_MAX, "%s%s%s", sysdir, sep, subdir);
|
||||
DIR *dir = opendir(fn);
|
||||
if (dir == NULL) {
|
||||
perror(fn);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* We can use "set_perm" and "set_perm_recursive" to set file permissions
|
||||
* (owner, group, and file mode) for individual files and entire subtrees.
|
||||
* We want to use set_perm_recursive efficiently to avoid setting the
|
||||
* permissions of every single file in the system image individually.
|
||||
*
|
||||
* What we do is recursively set our entire subtree to the permissions
|
||||
* used by the first file we encounter, and then use "set_perm" to adjust
|
||||
* the permissions of subsequent files which don't match the first one.
|
||||
* This is bad if the first file is an outlier, but it generally works.
|
||||
* Subdirectories can do the same thing recursively if they're different.
|
||||
*/
|
||||
|
||||
int is_first = 1;
|
||||
const struct dirent *e;
|
||||
while ((e = readdir(dir))) {
|
||||
// Skip over "." and ".." entries
|
||||
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
|
||||
|
||||
if (e->d_type == DT_LNK) { // Symlink
|
||||
|
||||
// Symlinks don't really have permissions, so this is orthogonal.
|
||||
snprintf(fn, PATH_MAX, "%s/%s%s%s", sysdir, subdir, sep, e->d_name);
|
||||
int len = readlink(fn, fn, PATH_MAX - 1);
|
||||
if (len <= 0) {
|
||||
perror(fn);
|
||||
exit(1);
|
||||
}
|
||||
fn[len] = '\0';
|
||||
printf("symlink %s SYSTEM:%s%s%s\n", fn, subdir, sep, e->d_name);
|
||||
|
||||
} else if (e->d_type == DT_DIR) { // Subdirectory
|
||||
|
||||
// Use the parent directory as the model for default permissions.
|
||||
// We haven't seen a file, so just make up some file defaults.
|
||||
if (is_first && (
|
||||
dir_mode != default_dir_mode ||
|
||||
dir_uid != default_uid || dir_gid != default_gid)) {
|
||||
default_uid = dir_uid;
|
||||
default_gid = dir_gid;
|
||||
default_dir_mode = dir_mode;
|
||||
default_file_mode = dir_mode & default_file_mode & 0666;
|
||||
printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n",
|
||||
default_uid, default_gid,
|
||||
default_dir_mode, default_file_mode,
|
||||
subdir);
|
||||
}
|
||||
|
||||
is_first = 0;
|
||||
|
||||
// Recursively handle the subdirectory.
|
||||
// Note, the recursive call handles the directory's own permissions.
|
||||
snprintf(fn, PATH_MAX, "%s%s%s", subdir, sep, e->d_name);
|
||||
walk_files(sysdir, fn,
|
||||
default_uid, default_gid,
|
||||
default_dir_mode, default_file_mode);
|
||||
|
||||
} else { // Ordinary file
|
||||
|
||||
// Get the file's desired permissions.
|
||||
unsigned file_uid = 0, file_gid = 0, file_mode = 0;
|
||||
snprintf(fn, PATH_MAX, "system/%s%s%s", subdir, sep, e->d_name);
|
||||
fs_config(fn, 0, &file_uid, &file_gid, &file_mode);
|
||||
|
||||
// If this is the first file, its mode gets to become the default.
|
||||
if (is_first && (
|
||||
dir_mode != default_dir_mode ||
|
||||
file_mode != default_file_mode ||
|
||||
dir_uid != default_uid || file_uid != default_uid ||
|
||||
dir_gid != default_gid || file_gid != default_gid)) {
|
||||
default_uid = dir_uid;
|
||||
default_gid = dir_gid;
|
||||
default_dir_mode = dir_mode;
|
||||
default_file_mode = file_mode;
|
||||
printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n",
|
||||
default_uid, default_gid,
|
||||
default_dir_mode, default_file_mode,
|
||||
subdir);
|
||||
}
|
||||
|
||||
is_first = 0;
|
||||
|
||||
// Otherwise, override this file if it doesn't match the defaults.
|
||||
if (file_mode != default_file_mode ||
|
||||
file_uid != default_uid || file_gid != default_gid) {
|
||||
printf("set_perm %d %d 0%o SYSTEM:%s%s%s\n",
|
||||
file_uid, file_gid, file_mode,
|
||||
subdir, sep, e->d_name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Set the directory's permissions directly, if they never got set.
|
||||
if (dir_mode != default_dir_mode ||
|
||||
dir_uid != default_uid || dir_gid != default_gid) {
|
||||
printf("set_perm %d %d 0%o SYSTEM:%s\n",
|
||||
dir_uid, dir_gid, dir_mode, subdir);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the update script (in "Amend", see commands/recovery/commands.c)
|
||||
* for the complete-reinstall OTA update packages the build system makes.
|
||||
*
|
||||
* The generated script makes a variety of sanity checks about the device,
|
||||
* erases and reinstalls system files, and sets file permissions appropriately.
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: %s systemdir android-info.txt >update-script\n",
|
||||
argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// ensure basic recovery script language compatibility
|
||||
printf("assert compatible_with(\"0.2\") == \"true\"\n");
|
||||
|
||||
// if known, make sure the device name is correct
|
||||
const char *device = getenv("TARGET_DEVICE");
|
||||
if (device != NULL) {
|
||||
printf("assert getprop(\"ro.product.device\") == \"%s\" || "
|
||||
"getprop(\"ro.build.product\") == \"%s\"\n", device, device);
|
||||
}
|
||||
|
||||
// scan android-info.txt to enforce compatibility with the target system
|
||||
FILE *fp = fopen(argv[2], "r");
|
||||
if (fp == NULL) {
|
||||
perror(argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The lines we're looking for look like:
|
||||
// version-bootloader=x.yy.zzzz|x.yy.zzzz|...
|
||||
// or:
|
||||
// require version-bootloader=x.yy.zzzz|x.yy.zzzz|...
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
const char *name = strtok(line, "="), *value = strtok(NULL, "|\n");
|
||||
if (value != NULL &&
|
||||
(!strcmp(name, "version-bootloader") ||
|
||||
!strcmp(name, "require version-bootloader"))) {
|
||||
printf("assert getprop(\"ro.bootloader\") == \"%s\"", value);
|
||||
|
||||
while ((value = strtok(NULL, "|\n")) != NULL) {
|
||||
printf(" || getprop(\"ro.bootloader\") == \"%s\"", value);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
// We also used to check version-baseband, but we update radio.img
|
||||
// ourselves, so there's no need.
|
||||
}
|
||||
|
||||
// erase the boot sector first, so if the update gets interrupted,
|
||||
// the system will reboot into the recovery partition and start over.
|
||||
printf("format BOOT:\n");
|
||||
|
||||
// write the radio image (actually just loads it into RAM for now)
|
||||
printf("show_progress 0.1 0\n");
|
||||
printf("write_radio_image PACKAGE:radio.img\n");
|
||||
|
||||
// erase and reinstall the system image
|
||||
printf("show_progress 0.5 0\n");
|
||||
printf("format SYSTEM:\n");
|
||||
printf("copy_dir PACKAGE:system SYSTEM:\n");
|
||||
|
||||
// walk the files in the system image, set their permissions, etc.
|
||||
// use -1 for default values to force permissions to be set explicitly.
|
||||
walk_files(argv[1], "", -1, -1, -1, -1);
|
||||
|
||||
// as the last step, write the boot sector.
|
||||
printf("show_progress 0.2 0\n");
|
||||
printf("write_raw_image PACKAGE:boot.img BOOT:\n");
|
||||
|
||||
// after the end of the script, the radio will be written to cache
|
||||
// leave some space in the progress bar for this operation
|
||||
printf("show_progress 0.2 10\n");
|
||||
return 0;
|
||||
}
|
140
ui.c
@ -27,9 +27,19 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "minui/minui.h"
|
||||
#include "recovery_ui.h"
|
||||
|
||||
#ifdef KEY_POWER_IS_SELECT_ITEM
|
||||
static int gShowBackButton = 1;
|
||||
#else
|
||||
static int gShowBackButton = 0;
|
||||
#endif
|
||||
|
||||
#define MAX_COLS 64
|
||||
#define MAX_ROWS 32
|
||||
#define MAX_ROWS 32
|
||||
|
||||
#define MENU_MAX_COLS 64
|
||||
#define MENU_MAX_ROWS 250
|
||||
|
||||
#define CHAR_WIDTH 10
|
||||
#define CHAR_HEIGHT 18
|
||||
@ -44,9 +54,9 @@ static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
|
||||
static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
|
||||
static gr_surface gProgressBarEmpty[NUM_SIDES];
|
||||
static gr_surface gProgressBarFill[NUM_SIDES];
|
||||
static int ui_has_initialized = 0;
|
||||
|
||||
static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_UNPACKING], "icon_unpacking" },
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" },
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING],
|
||||
@ -89,9 +99,10 @@ static int text_cols = 0, text_rows = 0;
|
||||
static int text_col = 0, text_row = 0, text_top = 0;
|
||||
static int show_text = 0;
|
||||
|
||||
static char menu[MAX_ROWS][MAX_COLS];
|
||||
static char menu[MENU_MAX_ROWS][MENU_MAX_COLS];
|
||||
static int show_menu = 0;
|
||||
static int menu_top = 0, menu_items = 0, menu_sel = 0;
|
||||
static int menu_show_start = 0; // this is line which menu display is starting at
|
||||
|
||||
// Key event input queue
|
||||
static pthread_mutex_t key_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
@ -164,10 +175,15 @@ static void draw_text_line(int row, const char* t) {
|
||||
}
|
||||
}
|
||||
|
||||
#define MENU_TEXT_COLOR 7, 133, 74, 255
|
||||
#define NORMAL_TEXT_COLOR 200, 200, 200, 255
|
||||
#define HEADER_TEXT_COLOR NORMAL_TEXT_COLOR
|
||||
|
||||
// Redraw everything on the screen. Does not flip pages.
|
||||
// Should only be called with gUpdateMutex locked.
|
||||
static void draw_screen_locked(void)
|
||||
{
|
||||
if (!ui_has_initialized) return;
|
||||
draw_background_locked(gCurrentIcon);
|
||||
draw_progress_locked();
|
||||
|
||||
@ -176,29 +192,43 @@ static void draw_screen_locked(void)
|
||||
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int row = 0; // current row that we are drawing on
|
||||
if (show_menu) {
|
||||
gr_color(64, 96, 255, 255);
|
||||
gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT,
|
||||
gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1);
|
||||
gr_color(MENU_TEXT_COLOR);
|
||||
gr_fill(0, (menu_top + menu_sel - menu_show_start) * CHAR_HEIGHT,
|
||||
gr_fb_width(), (menu_top + menu_sel - menu_show_start + 1)*CHAR_HEIGHT+1);
|
||||
|
||||
for (; i < menu_top + menu_items; ++i) {
|
||||
gr_color(HEADER_TEXT_COLOR);
|
||||
for (i = 0; i < menu_top; ++i) {
|
||||
draw_text_line(i, menu[i]);
|
||||
row++;
|
||||
}
|
||||
|
||||
if (menu_items - menu_show_start + menu_top >= MAX_ROWS)
|
||||
j = MAX_ROWS - menu_top;
|
||||
else
|
||||
j = menu_items - menu_show_start;
|
||||
|
||||
gr_color(MENU_TEXT_COLOR);
|
||||
for (i = menu_show_start + menu_top; i < (menu_show_start + menu_top + j); ++i) {
|
||||
if (i == menu_top + menu_sel) {
|
||||
gr_color(255, 255, 255, 255);
|
||||
draw_text_line(i, menu[i]);
|
||||
gr_color(64, 96, 255, 255);
|
||||
draw_text_line(i - menu_show_start , menu[i]);
|
||||
gr_color(MENU_TEXT_COLOR);
|
||||
} else {
|
||||
draw_text_line(i, menu[i]);
|
||||
gr_color(MENU_TEXT_COLOR);
|
||||
draw_text_line(i - menu_show_start, menu[i]);
|
||||
}
|
||||
row++;
|
||||
}
|
||||
gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
|
||||
gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
|
||||
++i;
|
||||
gr_fill(0, row*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
|
||||
gr_fb_width(), row*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
|
||||
}
|
||||
|
||||
gr_color(255, 255, 0, 255);
|
||||
|
||||
for (; i < text_rows; ++i) {
|
||||
draw_text_line(i, text[(i+text_top) % text_rows]);
|
||||
gr_color(NORMAL_TEXT_COLOR);
|
||||
for (; row < text_rows; ++row) {
|
||||
draw_text_line(row, text[(row+text_top) % text_rows]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,6 +237,7 @@ static void draw_screen_locked(void)
|
||||
// Should only be called with gUpdateMutex locked.
|
||||
static void update_screen_locked(void)
|
||||
{
|
||||
if (!ui_has_initialized) return;
|
||||
draw_screen_locked();
|
||||
gr_flip();
|
||||
}
|
||||
@ -215,6 +246,7 @@ static void update_screen_locked(void)
|
||||
// Should only be called with gUpdateMutex locked.
|
||||
static void update_progress_locked(void)
|
||||
{
|
||||
if (!ui_has_initialized) return;
|
||||
if (show_text || !gPagesIdentical) {
|
||||
draw_screen_locked(); // Must redraw the whole screen
|
||||
gPagesIdentical = 1;
|
||||
@ -308,20 +340,14 @@ static void *input_thread(void *cookie)
|
||||
}
|
||||
pthread_mutex_unlock(&key_queue_mutex);
|
||||
|
||||
// Alt+L or Home+End: toggle log display
|
||||
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
|
||||
if ((alt && ev.code == KEY_L && ev.value > 0) ||
|
||||
(key_pressed[KEY_HOME] && ev.code == KEY_END && ev.value > 0)) {
|
||||
if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
show_text = !show_text;
|
||||
update_screen_locked();
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
|
||||
// Green+Menu+Red: reboot immediately
|
||||
if (ev.code == KEY_DREAM_RED &&
|
||||
key_pressed[KEY_DREAM_MENU] &&
|
||||
key_pressed[KEY_DREAM_GREEN]) {
|
||||
if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
|
||||
reboot(RB_AUTOBOOT);
|
||||
}
|
||||
}
|
||||
@ -330,6 +356,7 @@ static void *input_thread(void *cookie)
|
||||
|
||||
void ui_init(void)
|
||||
{
|
||||
ui_has_initialized = 1;
|
||||
gr_init();
|
||||
ev_init();
|
||||
|
||||
@ -345,7 +372,11 @@ void ui_init(void)
|
||||
for (i = 0; BITMAPS[i].name != NULL; ++i) {
|
||||
int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
|
||||
if (result < 0) {
|
||||
LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
|
||||
if (result == -2) {
|
||||
LOGI("Bitmap %s missing header\n", BITMAPS[i].name);
|
||||
} else {
|
||||
LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
|
||||
}
|
||||
*BITMAPS[i].surface = NULL;
|
||||
}
|
||||
}
|
||||
@ -460,7 +491,17 @@ void ui_print(const char *fmt, ...)
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
|
||||
void ui_start_menu(char** headers, char** items) {
|
||||
void ui_reset_text_col()
|
||||
{
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
text_col = 0;
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
|
||||
#define MENU_ITEM_HEADER " - "
|
||||
#define MENU_ITEM_HEADER_LENGTH strlen(MENU_ITEM_HEADER)
|
||||
|
||||
int ui_start_menu(char** headers, char** items) {
|
||||
int i;
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
if (text_rows > 0 && text_cols > 0) {
|
||||
@ -470,17 +511,28 @@ void ui_start_menu(char** headers, char** items) {
|
||||
menu[i][text_cols-1] = '\0';
|
||||
}
|
||||
menu_top = i;
|
||||
for (; i < text_rows; ++i) {
|
||||
for (; i < MENU_MAX_ROWS; ++i) {
|
||||
if (items[i-menu_top] == NULL) break;
|
||||
strncpy(menu[i], items[i-menu_top], text_cols-1);
|
||||
strcpy(menu[i], MENU_ITEM_HEADER);
|
||||
strncpy(menu[i] + MENU_ITEM_HEADER_LENGTH, items[i-menu_top], text_cols-1 - MENU_ITEM_HEADER_LENGTH);
|
||||
menu[i][text_cols-1] = '\0';
|
||||
}
|
||||
|
||||
if (gShowBackButton) {
|
||||
strcpy(menu[i], " - +++++Go Back+++++");
|
||||
++i;
|
||||
}
|
||||
|
||||
menu_items = i - menu_top;
|
||||
show_menu = 1;
|
||||
menu_sel = 0;
|
||||
menu_sel = menu_show_start = 0;
|
||||
update_screen_locked();
|
||||
}
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
if (gShowBackButton) {
|
||||
return menu_items - 1;
|
||||
}
|
||||
return menu_items;
|
||||
}
|
||||
|
||||
int ui_menu_select(int sel) {
|
||||
@ -489,9 +541,21 @@ int ui_menu_select(int sel) {
|
||||
if (show_menu > 0) {
|
||||
old_sel = menu_sel;
|
||||
menu_sel = sel;
|
||||
if (menu_sel < 0) menu_sel = 0;
|
||||
if (menu_sel >= menu_items) menu_sel = menu_items-1;
|
||||
|
||||
if (menu_sel < 0) menu_sel = menu_items + menu_sel;
|
||||
if (menu_sel >= menu_items) menu_sel = menu_sel - menu_items;
|
||||
|
||||
|
||||
if (menu_sel < menu_show_start && menu_show_start > 0) {
|
||||
menu_show_start = menu_sel;
|
||||
}
|
||||
|
||||
if (menu_sel - menu_show_start + menu_top >= text_rows) {
|
||||
menu_show_start = menu_sel + menu_top - text_rows + 1;
|
||||
}
|
||||
|
||||
sel = menu_sel;
|
||||
|
||||
if (menu_sel != old_sel) update_screen_locked();
|
||||
}
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
@ -540,3 +604,15 @@ void ui_clear_key_queue() {
|
||||
key_queue_len = 0;
|
||||
pthread_mutex_unlock(&key_queue_mutex);
|
||||
}
|
||||
|
||||
void ui_set_show_text(int value) {
|
||||
show_text = value;
|
||||
}
|
||||
|
||||
void ui_set_showing_back_button(int showBackButton) {
|
||||
gShowBackButton = showBackButton;
|
||||
}
|
||||
|
||||
int ui_get_showing_back_button() {
|
||||
return gShowBackButton;
|
||||
}
|
72
updater/Android.mk
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright 2009 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
updater_src_files := \
|
||||
install.c \
|
||||
updater.c
|
||||
|
||||
#
|
||||
# Build a statically-linked binary to include in OTA packages
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Build only in eng, so we don't end up with a copy of this in /system
|
||||
# on user builds. (TODO: find a better way to build device binaries
|
||||
# needed only for OTA packages.)
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
|
||||
LOCAL_SRC_FILES := $(updater_src_files)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
|
||||
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
|
||||
LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
|
||||
|
||||
# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
|
||||
# named "Register_<libname>()". Here we emit a little C function that
|
||||
# gets #included by updater.c. It calls all those registration
|
||||
# functions.
|
||||
|
||||
# Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS.
|
||||
# These libs are also linked in with updater, but we don't try to call
|
||||
# any sort of registration function for these. Use this variable for
|
||||
# any subsidiary static libraries required for your registered
|
||||
# extension libs.
|
||||
|
||||
inc := $(call intermediates-dir-for,PACKAGING,updater_extensions)/register.inc
|
||||
|
||||
# During the first pass of reading the makefiles, we dump the list of
|
||||
# extension libs to a temp file, then copy that to the ".list" file if
|
||||
# it is different than the existing .list (if any). The register.inc
|
||||
# file then uses the .list as a prerequisite, so it is only rebuilt
|
||||
# (and updater.o recompiled) when the list of extension libs changes.
|
||||
|
||||
junk := $(shell mkdir -p $(dir $(inc));\
|
||||
echo $(TARGET_RECOVERY_UPDATER_LIBS) > $(inc).temp;\
|
||||
diff -q $(inc).temp $(inc).list || cp -f $(inc).temp $(inc).list)
|
||||
|
||||
$(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS)
|
||||
$(inc) : $(inc).list
|
||||
$(hide) mkdir -p $(dir $@)
|
||||
$(hide) echo "" > $@
|
||||
$(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@)
|
||||
$(hide) echo "void RegisterDeviceExtensions() {" >> $@
|
||||
$(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@)
|
||||
$(hide) echo "}" >> $@
|
||||
|
||||
$(call intermediates-dir-for,EXECUTABLES,updater)/updater.o : $(inc)
|
||||
LOCAL_C_INCLUDES += $(dir $(inc))
|
||||
|
||||
LOCAL_MODULE := updater
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
|
||||
file := $(PRODUCT_OUT)/utilities/update-binary
|
||||
ALL_PREBUILT += $(file)
|
||||
$(file) : $(TARGET_OUT)/bin/updater | $(ACP)
|
||||
$(transform-prebuilt-to-target)
|
855
updater/install.c
Normal file
@ -0,0 +1,855 @@
|
||||
/*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cutils/misc.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "edify/expr.h"
|
||||
#include "minzip/DirUtil.h"
|
||||
#include "mtdutils/mounts.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "updater.h"
|
||||
|
||||
|
||||
// mount(type, location, mount_point)
|
||||
//
|
||||
// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
|
||||
// type="vfat" location="/dev/block/<whatever>" to mount a device
|
||||
char* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 3) {
|
||||
return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
|
||||
}
|
||||
char* type;
|
||||
char* location;
|
||||
char* mount_point;
|
||||
if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(type) == 0) {
|
||||
ErrorAbort(state, "type argument to %s() can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(location) == 0) {
|
||||
ErrorAbort(state, "location argument to %s() can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(mount_point) == 0) {
|
||||
ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
mkdir(mount_point, 0755);
|
||||
|
||||
if (strcmp(type, "MTD") == 0) {
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition* mtd;
|
||||
mtd = mtd_find_partition_by_name(location);
|
||||
if (mtd == NULL) {
|
||||
fprintf(stderr, "%s: no mtd partition named \"%s\"",
|
||||
name, location);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) {
|
||||
fprintf(stderr, "mtd mount of %s failed: %s\n",
|
||||
location, strerror(errno));
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
result = mount_point;
|
||||
} else {
|
||||
if (mount(location, mount_point, type,
|
||||
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
|
||||
fprintf(stderr, "%s: failed to mount %s at %s: %s\n",
|
||||
name, location, mount_point, strerror(errno));
|
||||
result = strdup("");
|
||||
} else {
|
||||
result = mount_point;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
free(type);
|
||||
free(location);
|
||||
if (result != mount_point) free(mount_point);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// is_mounted(mount_point)
|
||||
char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
|
||||
}
|
||||
char* mount_point;
|
||||
if (ReadArgs(state, argv, 1, &mount_point) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(mount_point) == 0) {
|
||||
ErrorAbort(state, "mount_point argument to unmount() can't be empty");
|
||||
goto done;
|
||||
}
|
||||
|
||||
scan_mounted_volumes();
|
||||
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
|
||||
if (vol == NULL) {
|
||||
result = strdup("");
|
||||
} else {
|
||||
result = mount_point;
|
||||
}
|
||||
|
||||
done:
|
||||
if (result != mount_point) free(mount_point);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
|
||||
}
|
||||
char* mount_point;
|
||||
if (ReadArgs(state, argv, 1, &mount_point) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(mount_point) == 0) {
|
||||
ErrorAbort(state, "mount_point argument to unmount() can't be empty");
|
||||
goto done;
|
||||
}
|
||||
|
||||
scan_mounted_volumes();
|
||||
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
|
||||
if (vol == NULL) {
|
||||
fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point);
|
||||
result = strdup("");
|
||||
} else {
|
||||
unmount_mounted_volume(vol);
|
||||
result = mount_point;
|
||||
}
|
||||
|
||||
done:
|
||||
if (result != mount_point) free(mount_point);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// format(type, location)
|
||||
//
|
||||
// type="MTD" location=partition
|
||||
char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 2) {
|
||||
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
|
||||
}
|
||||
char* type;
|
||||
char* location;
|
||||
if (ReadArgs(state, argv, 2, &type, &location) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(type) == 0) {
|
||||
ErrorAbort(state, "type argument to %s() can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(location) == 0) {
|
||||
ErrorAbort(state, "location argument to %s() can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (strcmp(type, "MTD") == 0) {
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition* mtd = mtd_find_partition_by_name(location);
|
||||
if (mtd == NULL) {
|
||||
fprintf(stderr, "%s: no mtd partition named \"%s\"",
|
||||
name, location);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
||||
if (ctx == NULL) {
|
||||
fprintf(stderr, "%s: can't write \"%s\"", name, location);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
if (mtd_erase_blocks(ctx, -1) == -1) {
|
||||
mtd_write_close(ctx);
|
||||
fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
if (mtd_write_close(ctx) != 0) {
|
||||
fprintf(stderr, "%s: failed to close \"%s\"", name, location);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
result = location;
|
||||
} else {
|
||||
fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
|
||||
}
|
||||
|
||||
done:
|
||||
free(type);
|
||||
if (result != location) free(location);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char** paths = malloc(argc * sizeof(char*));
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
paths[i] = Evaluate(state, argv[i]);
|
||||
if (paths[i] == NULL) {
|
||||
int j;
|
||||
for (j = 0; j < i; ++i) {
|
||||
free(paths[j]);
|
||||
}
|
||||
free(paths);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool recursive = (strcmp(name, "delete_recursive") == 0);
|
||||
|
||||
int success = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0)
|
||||
++success;
|
||||
free(paths[i]);
|
||||
}
|
||||
free(paths);
|
||||
|
||||
char buffer[10];
|
||||
sprintf(buffer, "%d", success);
|
||||
return strdup(buffer);
|
||||
}
|
||||
|
||||
|
||||
char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
|
||||
}
|
||||
char* frac_str;
|
||||
char* sec_str;
|
||||
if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double frac = strtod(frac_str, NULL);
|
||||
int sec = strtol(sec_str, NULL, 10);
|
||||
|
||||
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
|
||||
fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
|
||||
|
||||
free(sec_str);
|
||||
return frac_str;
|
||||
}
|
||||
|
||||
char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
|
||||
}
|
||||
char* frac_str;
|
||||
if (ReadArgs(state, argv, 1, &frac_str) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double frac = strtod(frac_str, NULL);
|
||||
|
||||
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
|
||||
fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
|
||||
|
||||
return frac_str;
|
||||
}
|
||||
|
||||
// package_extract_dir(package_path, destination_path)
|
||||
char* PackageExtractDirFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
|
||||
}
|
||||
char* zip_path;
|
||||
char* dest_path;
|
||||
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
|
||||
|
||||
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
|
||||
|
||||
// To create a consistent system image, never use the clock for timestamps.
|
||||
struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
|
||||
|
||||
bool success = mzExtractRecursive(za, zip_path, dest_path,
|
||||
MZ_EXTRACT_FILES_ONLY, ×tamp,
|
||||
NULL, NULL);
|
||||
free(zip_path);
|
||||
free(dest_path);
|
||||
return strdup(success ? "t" : "");
|
||||
}
|
||||
|
||||
|
||||
// package_extract_file(package_path, destination_path)
|
||||
char* PackageExtractFileFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
|
||||
}
|
||||
char* zip_path;
|
||||
char* dest_path;
|
||||
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
|
||||
|
||||
bool success = false;
|
||||
|
||||
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
|
||||
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
|
||||
if (entry == NULL) {
|
||||
fprintf(stderr, "%s: no %s in package\n", name, zip_path);
|
||||
goto done;
|
||||
}
|
||||
|
||||
FILE* f = fopen(dest_path, "wb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "%s: can't open %s for write: %s\n",
|
||||
name, dest_path, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
success = mzExtractZipEntryToFile(za, entry, fileno(f));
|
||||
fclose(f);
|
||||
|
||||
done:
|
||||
free(zip_path);
|
||||
free(dest_path);
|
||||
return strdup(success ? "t" : "");
|
||||
}
|
||||
|
||||
|
||||
// symlink target src1 src2 ...
|
||||
// unlinks any previously existing src1, src2, etc before creating symlinks.
|
||||
char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc == 0) {
|
||||
return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
|
||||
}
|
||||
char* target;
|
||||
target = Evaluate(state, argv[0]);
|
||||
if (target == NULL) return NULL;
|
||||
|
||||
char** srcs = ReadVarArgs(state, argc-1, argv+1);
|
||||
if (srcs == NULL) {
|
||||
free(target);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < argc-1; ++i) {
|
||||
if (unlink(srcs[i]) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "%s: failed to remove %s: %s\n",
|
||||
name, srcs[i], strerror(errno));
|
||||
}
|
||||
}
|
||||
if (symlink(target, srcs[i]) < 0) {
|
||||
fprintf(stderr, "%s: failed to symlink %s to %s: %s\n",
|
||||
name, srcs[i], target, strerror(errno));
|
||||
}
|
||||
free(srcs[i]);
|
||||
}
|
||||
free(srcs);
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
|
||||
char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
bool recursive = (strcmp(name, "set_perm_recursive") == 0);
|
||||
|
||||
int min_args = 4 + (recursive ? 1 : 0);
|
||||
if (argc < min_args) {
|
||||
return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
|
||||
}
|
||||
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) return NULL;
|
||||
|
||||
char* end;
|
||||
int i;
|
||||
|
||||
int uid = strtoul(args[0], &end, 0);
|
||||
if (*end != '\0' || args[0][0] == 0) {
|
||||
ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
int gid = strtoul(args[1], &end, 0);
|
||||
if (*end != '\0' || args[1][0] == 0) {
|
||||
ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
int dir_mode = strtoul(args[2], &end, 0);
|
||||
if (*end != '\0' || args[2][0] == 0) {
|
||||
ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
int file_mode = strtoul(args[3], &end, 0);
|
||||
if (*end != '\0' || args[3][0] == 0) {
|
||||
ErrorAbort(state, "%s: \"%s\" not a valid filemode",
|
||||
name, args[3]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 4; i < argc; ++i) {
|
||||
dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
|
||||
}
|
||||
} else {
|
||||
int mode = strtoul(args[2], &end, 0);
|
||||
if (*end != '\0' || args[2][0] == 0) {
|
||||
ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 3; i < argc; ++i) {
|
||||
if (chown(args[i], uid, gid) < 0) {
|
||||
fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n",
|
||||
name, args[i], uid, gid, strerror(errno));
|
||||
}
|
||||
if (chmod(args[i], mode) < 0) {
|
||||
fprintf(stderr, "%s: chmod of %s to %o failed: %s\n",
|
||||
name, args[i], mode, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
result = strdup("");
|
||||
|
||||
done:
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
|
||||
}
|
||||
char* key;
|
||||
key = Evaluate(state, argv[0]);
|
||||
if (key == NULL) return NULL;
|
||||
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get(key, value, "");
|
||||
free(key);
|
||||
|
||||
return strdup(value);
|
||||
}
|
||||
|
||||
|
||||
// file_getprop(file, key)
|
||||
//
|
||||
// interprets 'file' as a getprop-style file (key=value pairs, one
|
||||
// per line, # comment lines and blank lines okay), and returns the value
|
||||
// for 'key' (or "" if it isn't defined).
|
||||
char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
char* buffer = NULL;
|
||||
char* filename;
|
||||
char* key;
|
||||
if (ReadArgs(state, argv, 2, &filename, &key) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(filename, &st) < 0) {
|
||||
ErrorAbort(state, "%s: failed to stat \"%s\": %s",
|
||||
name, filename, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
#define MAX_FILE_GETPROP_SIZE 65536
|
||||
|
||||
if (st.st_size > MAX_FILE_GETPROP_SIZE) {
|
||||
ErrorAbort(state, "%s too large for %s (max %d)",
|
||||
filename, name, MAX_FILE_GETPROP_SIZE);
|
||||
goto done;
|
||||
}
|
||||
|
||||
buffer = malloc(st.st_size+1);
|
||||
if (buffer == NULL) {
|
||||
ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1);
|
||||
goto done;
|
||||
}
|
||||
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
ErrorAbort(state, "%s: failed to open %s: %s",
|
||||
name, filename, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (fread(buffer, 1, st.st_size, f) != st.st_size) {
|
||||
ErrorAbort(state, "%s: failed to read %d bytes from %s",
|
||||
name, st.st_size+1, filename);
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
buffer[st.st_size] = '\0';
|
||||
|
||||
fclose(f);
|
||||
|
||||
char* line = strtok(buffer, "\n");
|
||||
do {
|
||||
// skip whitespace at start of line
|
||||
while (*line && isspace(*line)) ++line;
|
||||
|
||||
// comment or blank line: skip to next line
|
||||
if (*line == '\0' || *line == '#') continue;
|
||||
|
||||
char* equal = strchr(line, '=');
|
||||
if (equal == NULL) {
|
||||
ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
|
||||
name, line, filename);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// trim whitespace between key and '='
|
||||
char* key_end = equal-1;
|
||||
while (key_end > line && isspace(*key_end)) --key_end;
|
||||
key_end[1] = '\0';
|
||||
|
||||
// not the key we're looking for
|
||||
if (strcmp(key, line) != 0) continue;
|
||||
|
||||
// skip whitespace after the '=' to the start of the value
|
||||
char* val_start = equal+1;
|
||||
while(*val_start && isspace(*val_start)) ++val_start;
|
||||
|
||||
// trim trailing whitespace
|
||||
char* val_end = val_start + strlen(val_start)-1;
|
||||
while (val_end > val_start && isspace(*val_end)) --val_end;
|
||||
val_end[1] = '\0';
|
||||
|
||||
result = strdup(val_start);
|
||||
break;
|
||||
|
||||
} while ((line = strtok(NULL, "\n")));
|
||||
|
||||
if (result == NULL) result = strdup("");
|
||||
|
||||
done:
|
||||
free(filename);
|
||||
free(key);
|
||||
free(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static bool write_raw_image_cb(const unsigned char* data,
|
||||
int data_len, void* ctx) {
|
||||
int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
|
||||
if (r == data_len) return true;
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// write_raw_image(file, partition)
|
||||
char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
|
||||
char* partition;
|
||||
char* filename;
|
||||
if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(partition) == 0) {
|
||||
ErrorAbort(state, "partition argument to %s can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(filename) == 0) {
|
||||
ErrorAbort(state, "file argument to %s can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
||||
if (mtd == NULL) {
|
||||
fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
|
||||
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
||||
if (ctx == NULL) {
|
||||
fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
|
||||
name, partition);
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
|
||||
bool success;
|
||||
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "%s: can't open %s: %s\n",
|
||||
name, filename, strerror(errno));
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
|
||||
success = true;
|
||||
char* buffer = malloc(BUFSIZ);
|
||||
int read;
|
||||
while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
|
||||
int wrote = mtd_write_data(ctx, buffer, read);
|
||||
success = success && (wrote == read);
|
||||
if (!success) {
|
||||
fprintf(stderr, "mtd_write_data to %s failed: %s\n",
|
||||
partition, strerror(errno));
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
fclose(f);
|
||||
|
||||
if (mtd_erase_blocks(ctx, -1) == -1) {
|
||||
fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
|
||||
}
|
||||
if (mtd_write_close(ctx) != 0) {
|
||||
fprintf(stderr, "%s: error closing write of %s\n", name, partition);
|
||||
}
|
||||
|
||||
printf("%s %s partition from %s\n",
|
||||
success ? "wrote" : "failed to write", partition, filename);
|
||||
|
||||
result = success ? partition : strdup("");
|
||||
|
||||
done:
|
||||
if (result != partition) free(partition);
|
||||
free(filename);
|
||||
return result;
|
||||
}
|
||||
|
||||
// write_firmware_image(file, partition)
|
||||
//
|
||||
// partition is "radio" or "hboot"
|
||||
// file is not used until after updater exits
|
||||
//
|
||||
// TODO: this should live in some HTC-specific library
|
||||
char* WriteFirmwareImageFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
|
||||
char* partition;
|
||||
char* filename;
|
||||
if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(partition) == 0) {
|
||||
ErrorAbort(state, "partition argument to %s can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(filename) == 0) {
|
||||
ErrorAbort(state, "file argument to %s can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe;
|
||||
fprintf(cmd, "firmware %s %s\n", partition, filename);
|
||||
|
||||
printf("will write %s firmware from %s\n", partition, filename);
|
||||
result = partition;
|
||||
|
||||
done:
|
||||
if (result != partition) free(partition);
|
||||
free(filename);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
extern int applypatch(int argc, char** argv);
|
||||
|
||||
// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...)
|
||||
// apply_patch_check(file, sha1, ...)
|
||||
// apply_patch_space(bytes)
|
||||
char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
printf("in applypatchfn (%s)\n", name);
|
||||
|
||||
char* prepend = NULL;
|
||||
if (strstr(name, "check") != NULL) {
|
||||
prepend = "-c";
|
||||
} else if (strstr(name, "space") != NULL) {
|
||||
prepend = "-s";
|
||||
}
|
||||
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) return NULL;
|
||||
|
||||
// insert the "program name" argv[0] and a copy of the "prepend"
|
||||
// string (if any) at the start of the args.
|
||||
|
||||
int extra = 1 + (prepend != NULL ? 1 : 0);
|
||||
char** temp = malloc((argc+extra) * sizeof(char*));
|
||||
memcpy(temp+extra, args, argc * sizeof(char*));
|
||||
temp[0] = strdup("updater");
|
||||
if (prepend) {
|
||||
temp[1] = strdup(prepend);
|
||||
}
|
||||
free(args);
|
||||
args = temp;
|
||||
argc += extra;
|
||||
|
||||
printf("calling applypatch\n");
|
||||
fflush(stdout);
|
||||
int result = applypatch(argc, args);
|
||||
printf("applypatch returned %d\n", result);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
|
||||
switch (result) {
|
||||
case 0: return strdup("t");
|
||||
case 1: return strdup("");
|
||||
default: return ErrorAbort(state, "applypatch couldn't parse args");
|
||||
}
|
||||
}
|
||||
|
||||
char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
size += strlen(args[i]);
|
||||
}
|
||||
char* buffer = malloc(size+1);
|
||||
size = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strcpy(buffer+size, args[i]);
|
||||
size += strlen(args[i]);
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
buffer[size] = '\0';
|
||||
|
||||
char* line = strtok(buffer, "\n");
|
||||
while (line) {
|
||||
fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
|
||||
"ui_print %s\n", line);
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc < 1) {
|
||||
return ErrorAbort(state, "%s() expects at least 1 arg", name);
|
||||
}
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char** args2 = malloc(sizeof(char*) * (argc+1));
|
||||
memcpy(args2, args, sizeof(char*) * argc);
|
||||
args2[argc] = NULL;
|
||||
|
||||
fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
execv(args2[0], args2);
|
||||
fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
fprintf(stderr, "run_program: child exited with status %d\n",
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(stderr, "run_program: child terminated by signal %d\n",
|
||||
WTERMSIG(status));
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
free(args2);
|
||||
|
||||
char buffer[20];
|
||||
sprintf(buffer, "%d", status);
|
||||
|
||||
return strdup(buffer);
|
||||
}
|
||||
|
||||
|
||||
void RegisterInstallFunctions() {
|
||||
RegisterFunction("mount", MountFn);
|
||||
RegisterFunction("is_mounted", IsMountedFn);
|
||||
RegisterFunction("unmount", UnmountFn);
|
||||
RegisterFunction("format", FormatFn);
|
||||
RegisterFunction("show_progress", ShowProgressFn);
|
||||
RegisterFunction("set_progress", SetProgressFn);
|
||||
RegisterFunction("delete", DeleteFn);
|
||||
RegisterFunction("delete_recursive", DeleteFn);
|
||||
RegisterFunction("package_extract_dir", PackageExtractDirFn);
|
||||
RegisterFunction("package_extract_file", PackageExtractFileFn);
|
||||
RegisterFunction("symlink", SymlinkFn);
|
||||
RegisterFunction("set_perm", SetPermFn);
|
||||
RegisterFunction("set_perm_recursive", SetPermFn);
|
||||
|
||||
RegisterFunction("getprop", GetPropFn);
|
||||
RegisterFunction("file_getprop", FileGetPropFn);
|
||||
RegisterFunction("write_raw_image", WriteRawImageFn);
|
||||
RegisterFunction("write_firmware_image", WriteFirmwareImageFn);
|
||||
|
||||
RegisterFunction("apply_patch", ApplyPatchFn);
|
||||
RegisterFunction("apply_patch_check", ApplyPatchFn);
|
||||
RegisterFunction("apply_patch_space", ApplyPatchFn);
|
||||
|
||||
RegisterFunction("ui_print", UIPrintFn);
|
||||
|
||||
RegisterFunction("run_program", RunProgramFn);
|
||||
}
|
22
updater/install.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 _UPDATER_INSTALL_H_
|
||||
#define _UPDATER_INSTALL_H_
|
||||
|
||||
void RegisterInstallFunctions();
|
||||
|
||||
#endif
|
134
updater/updater.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "edify/expr.h"
|
||||
#include "updater.h"
|
||||
#include "install.h"
|
||||
#include "minzip/Zip.h"
|
||||
|
||||
// Generated by the makefile, this function defines the
|
||||
// RegisterDeviceExtensions() function, which calls all the
|
||||
// registration functions for device-specific extensions.
|
||||
#include "register.inc"
|
||||
|
||||
// Where in the package we expect to find the edify script to execute.
|
||||
// (Note it's "updateR-script", not the older "update-script".)
|
||||
#define SCRIPT_NAME "META-INF/com/google/android/updater-script"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "unexpected number of arguments (%d)\n", argc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* version = argv[1];
|
||||
if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') {
|
||||
// We support version "1" or "2".
|
||||
fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n",
|
||||
argv[1]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Set up the pipe for sending commands back to the parent process.
|
||||
|
||||
int fd = atoi(argv[2]);
|
||||
FILE* cmd_pipe = fdopen(fd, "wb");
|
||||
setlinebuf(cmd_pipe);
|
||||
|
||||
// Extract the script from the package.
|
||||
|
||||
char* package_data = argv[3];
|
||||
ZipArchive za;
|
||||
int err;
|
||||
err = mzOpenZipArchive(package_data, &za);
|
||||
if (err != 0) {
|
||||
fprintf(stderr, "failed to open package %s: %s\n",
|
||||
package_data, strerror(err));
|
||||
return 3;
|
||||
}
|
||||
|
||||
const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
|
||||
if (script_entry == NULL) {
|
||||
fprintf(stderr, "failed to find %s in %s\n", SCRIPT_NAME, package_data);
|
||||
return 4;
|
||||
}
|
||||
|
||||
char* script = malloc(script_entry->uncompLen+1);
|
||||
if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) {
|
||||
fprintf(stderr, "failed to read script from package\n");
|
||||
return 5;
|
||||
}
|
||||
script[script_entry->uncompLen] = '\0';
|
||||
|
||||
// Configure edify's functions.
|
||||
|
||||
RegisterBuiltins();
|
||||
RegisterInstallFunctions();
|
||||
RegisterDeviceExtensions();
|
||||
FinishRegistration();
|
||||
|
||||
// Parse the script.
|
||||
|
||||
Expr* root;
|
||||
int error_count = 0;
|
||||
yy_scan_string(script);
|
||||
int error = yyparse(&root, &error_count);
|
||||
if (error != 0 || error_count > 0) {
|
||||
fprintf(stderr, "%d parse errors\n", error_count);
|
||||
return 6;
|
||||
}
|
||||
|
||||
// Evaluate the parsed script.
|
||||
|
||||
UpdaterInfo updater_info;
|
||||
updater_info.cmd_pipe = cmd_pipe;
|
||||
updater_info.package_zip = &za;
|
||||
|
||||
State state;
|
||||
state.cookie = &updater_info;
|
||||
state.script = script;
|
||||
state.errmsg = NULL;
|
||||
|
||||
char* result = Evaluate(&state, root);
|
||||
if (result == NULL) {
|
||||
if (state.errmsg == NULL) {
|
||||
fprintf(stderr, "script aborted (no error message)\n");
|
||||
fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
|
||||
} else {
|
||||
fprintf(stderr, "script aborted: %s\n", state.errmsg);
|
||||
char* line = strtok(state.errmsg, "\n");
|
||||
while (line) {
|
||||
fprintf(cmd_pipe, "ui_print %s\n", line);
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
fprintf(cmd_pipe, "ui_print\n");
|
||||
}
|
||||
free(state.errmsg);
|
||||
return 7;
|
||||
} else {
|
||||
fprintf(stderr, "script result was [%s]\n", result);
|
||||
free(result);
|
||||
}
|
||||
|
||||
mzCloseZipArchive(&za);
|
||||
free(script);
|
||||
|
||||
return 0;
|
||||
}
|
28
updater/updater.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 _UPDATER_UPDATER_H_
|
||||
#define _UPDATER_UPDATER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include "minzip/Zip.h"
|
||||
|
||||
typedef struct {
|
||||
FILE* cmd_pipe;
|
||||
ZipArchive* package_zip;
|
||||
} UpdaterInfo;
|
||||
|
||||
#endif
|
45
utilities/Android.mk
Normal file
@ -0,0 +1,45 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
ifndef BOARD_HAS_SMALL_RECOVERY
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := e2fsck
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := fix_permissions
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := parted
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := sdparted
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := tune2fs
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
endif
|
BIN
utilities/e2fsck
Normal file
476
utilities/fix_permissions
Normal file
@ -0,0 +1,476 @@
|
||||
#! /system/bin/sh
|
||||
#
|
||||
# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh
|
||||
#
|
||||
# fix_permissions - fixes permissions on Android data directories after upgrade
|
||||
# shade@chemlab.org
|
||||
#
|
||||
# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/
|
||||
# implementation by: Cyanogen
|
||||
# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro
|
||||
#
|
||||
# v1.1-v1.31r3 - many improvements and concepts from XDA developers.
|
||||
# v1.34 through v2.00 - A lot of frustration [by Kastro]
|
||||
# v2.01 - Completely rewrote the script for SPEED, thanks for the input farmatito
|
||||
# /data/data depth recursion is tweaked;
|
||||
# fixed single mode;
|
||||
# functions created for modularity;
|
||||
# logging can be disabled via CLI for more speed;
|
||||
# runtime computation added to end (Runtime: mins secs);
|
||||
# progress (current # of total) added to screen;
|
||||
# fixed CLI argument parsing, now you can have more than one option!;
|
||||
# debug cli option;
|
||||
# verbosity can be disabled via CLI option for less noise;;
|
||||
# [by Kastro, (XDA: k4str0), twitter;mattcarver]
|
||||
# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups,
|
||||
# fix help text, implement simulated run (-s) [farmatito]
|
||||
# v2.03 - fixed chown group ownership output [Kastro]
|
||||
|
||||
VERSION="2.03"
|
||||
|
||||
# Defaults
|
||||
DEBUG=0 # Debug off by default
|
||||
LOGGING=1 # Logging on by default
|
||||
VERBOSE=1 # Verbose on by default
|
||||
|
||||
# Messages
|
||||
UID_MSG="Changing user ownership for:"
|
||||
GID_MSG="Changing group ownership for:"
|
||||
PERM_MSG="Changing permissions for:"
|
||||
|
||||
# Programs needed
|
||||
ECHO="busybox echo"
|
||||
GREP="busybox grep"
|
||||
EGREP="busybox egrep"
|
||||
CAT="busybox cat"
|
||||
CHOWN="busybox chown"
|
||||
CHMOD="busybox chmod"
|
||||
MOUNT="busybox mount"
|
||||
UMOUNT="busybox umount"
|
||||
CUT="busybox cut"
|
||||
FIND="busybox find"
|
||||
LS="busybox ls"
|
||||
TR="busybox tr"
|
||||
TEE="busybox tee"
|
||||
TEST="busybox test"
|
||||
SED="busybox sed"
|
||||
RM="busybox rm"
|
||||
WC="busybox wc"
|
||||
EXPR="busybox expr"
|
||||
DATE="busybox date"
|
||||
|
||||
# Initialise vars
|
||||
CODEPATH=""
|
||||
UID=""
|
||||
GID=""
|
||||
PACKAGE=""
|
||||
REMOVE=0
|
||||
NOSYSTEM=0
|
||||
ONLY_ONE=""
|
||||
SIMULATE=0
|
||||
SYSREMOUNT=0
|
||||
SYSMOUNT=0
|
||||
DATAMOUNT=0
|
||||
SYSSDMOUNT=0
|
||||
FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
|
||||
FP_STARTEPOCH=$( $DATE +%s )
|
||||
|
||||
fp_usage()
|
||||
{
|
||||
$ECHO "Usage $0 [OPTIONS] [APK_PATH]"
|
||||
$ECHO " -d turn on debug"
|
||||
$ECHO " -f fix only package APK_PATH"
|
||||
$ECHO " -l disable logging for this run (faster)"
|
||||
$ECHO " -r remove stale data directories"
|
||||
$ECHO " of uninstalled packages while fixing permissions"
|
||||
$ECHO " -s simulate only"
|
||||
$ECHO " -u check only non-system directories"
|
||||
$ECHO " -v disable verbosity for this run (less output)"
|
||||
$ECHO " -V print version"
|
||||
$ECHO " -h this help"
|
||||
}
|
||||
|
||||
fp_parseargs()
|
||||
{
|
||||
# Parse options
|
||||
while $TEST $# -ne 0; do
|
||||
case "$1" in
|
||||
-d)
|
||||
DEBUG=1
|
||||
;;
|
||||
-f)
|
||||
if $TEST $# -lt 2; then
|
||||
$ECHO "$0: missing argument for option $1"
|
||||
exit 1
|
||||
else
|
||||
if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then
|
||||
ONLY_ONE=$2
|
||||
shift;
|
||||
else
|
||||
$ECHO "$0: missing argument for option $1"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
-r)
|
||||
REMOVE=1
|
||||
;;
|
||||
-s)
|
||||
SIMULATE=1
|
||||
;;
|
||||
-l)
|
||||
if $TEST $LOGGING -eq 0; then
|
||||
LOGGING=1
|
||||
else
|
||||
LOGGING=0
|
||||
fi
|
||||
;;
|
||||
-v)
|
||||
if $TEST $VERBOSE -eq 0; then
|
||||
VERBOSE=1
|
||||
else
|
||||
VERBOSE=0
|
||||
fi
|
||||
;;
|
||||
-u)
|
||||
NOSYSTEM=1
|
||||
;;
|
||||
-V)
|
||||
$ECHO "$0 $VERSION"
|
||||
exit 0
|
||||
;;
|
||||
-h)
|
||||
fp_usage
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
$ECHO "$0: unknown option $1"
|
||||
$ECHO
|
||||
fp_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift;
|
||||
done
|
||||
}
|
||||
|
||||
fp_print()
|
||||
{
|
||||
MSG=$@
|
||||
if $TEST $LOGGING -eq 1; then
|
||||
$ECHO $MSG | $TEE -a $LOG_FILE
|
||||
else
|
||||
$ECHO $MSG
|
||||
fi
|
||||
}
|
||||
|
||||
fp_start()
|
||||
{
|
||||
if $TEST $SIMULATE -eq 0 ; then
|
||||
if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then
|
||||
DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 )
|
||||
if $TEST $DEBUG -eq 1; then
|
||||
fp_print "/system mounted on $DEVICE"
|
||||
fi
|
||||
if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then
|
||||
$MOUNT -o remount,rw $DEVICE /system
|
||||
SYSREMOUNT=1
|
||||
fi
|
||||
else
|
||||
$MOUNT /system > /dev/null 2>&1
|
||||
SYSMOUNT=1
|
||||
fi
|
||||
|
||||
if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then
|
||||
$MOUNT /data > /dev/null 2>&1
|
||||
DATAMOUNT=1
|
||||
fi
|
||||
|
||||
if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " /system/sd " "/proc/mounts" ) -eq 0; then
|
||||
$MOUNT /system/sd > /dev/null 2>&1
|
||||
SYSSDMOUNT=1
|
||||
fi
|
||||
fi
|
||||
if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then
|
||||
LOG_FILE="/data/fix_permissions.log"
|
||||
else
|
||||
LOG_FILE="/sdcard/fix_permissions.log"
|
||||
fi
|
||||
if $TEST ! -e "$LOG_FILE"; then
|
||||
> $LOG_FILE
|
||||
fi
|
||||
|
||||
fp_print "$0 $VERSION started at $FP_STARTTIME"
|
||||
}
|
||||
|
||||
fp_chown_uid()
|
||||
{
|
||||
FP_OLDUID=$1
|
||||
FP_UID=$2
|
||||
FP_FILE=$3
|
||||
|
||||
#if user ownership doesn't equal then change them
|
||||
if $TEST "$FP_OLDUID" != "$FP_UID"; then
|
||||
if $TEST $VERBOSE -ne 0; then
|
||||
fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'"
|
||||
fi
|
||||
if $TEST $SIMULATE -eq 0; then
|
||||
$CHOWN $FP_UID "$FP_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
fp_chown_gid()
|
||||
{
|
||||
FP_OLDGID=$1
|
||||
FP_GID=$2
|
||||
FP_FILE=$3
|
||||
|
||||
#if group ownership doesn't equal then change them
|
||||
if $TEST "$FP_OLDGID" != "$FP_GID"; then
|
||||
if $TEST $VERBOSE -ne 0; then
|
||||
fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'"
|
||||
fi
|
||||
if $TEST $SIMULATE -eq 0; then
|
||||
$CHOWN :$FP_GID "$FP_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
fp_chmod()
|
||||
{
|
||||
FP_OLDPER=$1
|
||||
FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 )
|
||||
FP_PERSTR=$2
|
||||
FP_PERNUM=$3
|
||||
FP_FILE=$4
|
||||
|
||||
#if the permissions are not equal
|
||||
if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then
|
||||
if $TEST $VERBOSE -ne 0; then
|
||||
fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)"
|
||||
fi
|
||||
#change the permissions
|
||||
if $TEST $SIMULATE -eq 0; then
|
||||
$CHMOD $FP_PERNUM "$FP_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
fp_all()
|
||||
{
|
||||
FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $WC -l )
|
||||
I=0
|
||||
$CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | while read all_line; do
|
||||
I=$( $EXPR $I + 1 )
|
||||
fp_package "$all_line" $I $FP_NUMS
|
||||
done
|
||||
}
|
||||
|
||||
fp_single()
|
||||
{
|
||||
FP_SFOUND=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE | wc -l )
|
||||
if $TEST $FP_SFOUND -gt 1; then
|
||||
fp_print "Cannot perform single operation on $FP_SFOUND matched package(s)."
|
||||
elif $TEST $FP_SFOUND = "" -o $FP_SFOUND -eq 0; then
|
||||
fp_print "Could not find the package you specified in the packages.xml file."
|
||||
else
|
||||
FP_SPKG=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE )
|
||||
fp_package "${FP_SPKG}" 1 1
|
||||
fi
|
||||
}
|
||||
|
||||
fp_package()
|
||||
{
|
||||
pkgline=$1
|
||||
curnum=$2
|
||||
endnum=$3
|
||||
CODEPATH=$( $ECHO $pkgline | $SED 's%.* codePath="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
|
||||
PACKAGE=$( $ECHO $pkgline | $SED 's%.* name="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
|
||||
UID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
|
||||
GID=$UID
|
||||
APPDIR=$( $ECHO $CODEPATH | $SED 's%^\(.*\)/.*%\1%' )
|
||||
APK=$( $ECHO $CODEPATH | $SED 's%^.*/\(.*\..*\)$%\1%' )
|
||||
|
||||
#debug
|
||||
if $TEST $DEBUG -eq 1; then
|
||||
fp_print "CODEPATH: $CODEPATH APPDIR: $APPDIR APK:$APK UID/GID:$UID:$GID"
|
||||
fi
|
||||
|
||||
#check for existence of apk
|
||||
if $TEST -e $CODEPATH; then
|
||||
fp_print "Processing ($curnum of $endnum): $PACKAGE..."
|
||||
|
||||
#lets get existing permissions of CODEPATH
|
||||
OLD_UGD=$( $LS -ln "$CODEPATH" )
|
||||
OLD_PER=$( $ECHO $OLD_UGD | $CUT -d ' ' -f1 )
|
||||
OLD_UID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f3 )
|
||||
OLD_GID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f4 )
|
||||
|
||||
#apk source dirs
|
||||
if $TEST "$APPDIR" = "/system/app"; then
|
||||
#skip system apps if set
|
||||
if $TEST "$NOSYSTEM" = "1"; then
|
||||
fp_print "***SKIPPING SYSTEM APP ($PACKAGE)!"
|
||||
return
|
||||
fi
|
||||
fp_chown_uid $OLD_UID 0 "$CODEPATH"
|
||||
fp_chown_gid $OLD_GID 0 "$CODEPATH"
|
||||
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
|
||||
elif $TEST "$APPDIR" = "/data/app" || $TEST "$APPDIR" = "/sd-ext/app"; then
|
||||
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
|
||||
fp_chown_gid $OLD_GID 1000 "$CODEPATH"
|
||||
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
|
||||
elif $TEST "$APPDIR" = "/data/app-private" || $TEST "$APPDIR" = "/sd-ext/app-private"; then
|
||||
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
|
||||
fp_chown_gid $OLD_GID $GID "$CODEPATH"
|
||||
fp_chmod $OLD_PER "rw-r-----" 640 "$CODEPATH"
|
||||
fi
|
||||
else
|
||||
fp_print "$CODEPATH does not exist ($curnum of $endnum). Reinstall..."
|
||||
if $TEST $REMOVE -eq 1; then
|
||||
if $TEST -d /data/data/$PACKAGE ; then
|
||||
fp_print "Removing stale dir /data/data/$PACKAGE"
|
||||
if $TEST $SIMULATE -eq 0 ; then
|
||||
$RM -R /data/data/$PACKAGE
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#the data/data for the package
|
||||
if $TEST -d "/data/data/$PACKAGE"; then
|
||||
#find all directories in /data/data/$PACKAGE
|
||||
$FIND /data/data/$PACKAGE -type d -exec $LS -ldn {} \; | while read dataline; do
|
||||
#get existing permissions of that directory
|
||||
OLD_PER=$( $ECHO $dataline | $CUT -d ' ' -f1 )
|
||||
OLD_UID=$( $ECHO $dataline | $CUT -d ' ' -f3 )
|
||||
OLD_GID=$( $ECHO $dataline | $CUT -d ' ' -f4 )
|
||||
FILEDIR=$( $ECHO $dataline | $CUT -d ' ' -f9 )
|
||||
FOURDIR=$( $ECHO $FILEDIR | $CUT -d '/' -f5 )
|
||||
|
||||
#set defaults for iteration
|
||||
ISLIB=0
|
||||
REVPERM=755
|
||||
REVPSTR="rwxr-xr-x"
|
||||
REVUID=$UID
|
||||
REVGID=$GID
|
||||
|
||||
if $TEST "$FOURDIR" = ""; then
|
||||
#package directory, perms:755 owner:$UID:$GID
|
||||
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
|
||||
elif $TEST "$FOURDIR" = "lib"; then
|
||||
#lib directory, perms:755 owner:1000:1000
|
||||
#lib files, perms:755 owner:1000:1000
|
||||
ISLIB=1
|
||||
REVPERM=755
|
||||
REVPSTR="rwxr-xr-x"
|
||||
REVUID=1000
|
||||
REVGID=1000
|
||||
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
|
||||
elif $TEST "$FOURDIR" = "shared_prefs"; then
|
||||
#shared_prefs directories, perms:771 owner:$UID:$GID
|
||||
#shared_prefs files, perms:660 owner:$UID:$GID
|
||||
REVPERM=660
|
||||
REVPSTR="rw-rw----"
|
||||
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
||||
elif $TEST "$FOURDIR" = "databases"; then
|
||||
#databases directories, perms:771 owner:$UID:$GID
|
||||
#databases files, perms:660 owner:$UID:$GID
|
||||
REVPERM=660
|
||||
REVPSTR="rw-rw----"
|
||||
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
||||
elif $TEST "$FOURDIR" = "cache"; then
|
||||
#cache directories, perms:771 owner:$UID:$GID
|
||||
#cache files, perms:600 owner:$UID:GID
|
||||
REVPERM=600
|
||||
REVPSTR="rw-------"
|
||||
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
||||
else
|
||||
#other directories, perms:771 owner:$UID:$GID
|
||||
REVPERM=771
|
||||
REVPSTR="rwxrwx--x"
|
||||
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
||||
fi
|
||||
|
||||
#change ownership of directories matched
|
||||
if $TEST "$ISLIB" = "1"; then
|
||||
fp_chown_uid $OLD_UID 1000 "$FILEDIR"
|
||||
fp_chown_gid $OLD_GID 1000 "$FILEDIR"
|
||||
else
|
||||
fp_chown_uid $OLD_UID $UID "$FILEDIR"
|
||||
fp_chown_gid $OLD_GID $GID "$FILEDIR"
|
||||
fi
|
||||
|
||||
#if any files exist in directory with improper permissions reset them
|
||||
$FIND $FILEDIR -type f -maxdepth 1 ! -perm $REVPERM -exec $LS -ln {} \; | while read subline; do
|
||||
OLD_PER=$( $ECHO $subline | $CUT -d ' ' -f1 )
|
||||
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
|
||||
fp_chmod $OLD_PER $REVPSTR $REVPERM "$SUBFILE"
|
||||
done
|
||||
|
||||
#if any files exist in directory with improper user reset them
|
||||
$FIND $FILEDIR -type f -maxdepth 1 ! -user $REVUID -exec $LS -ln {} \; | while read subline; do
|
||||
OLD_UID=$( $ECHO $subline | $CUT -d ' ' -f3 )
|
||||
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
|
||||
fp_chown_uid $OLD_UID $REVUID "$SUBFILE"
|
||||
done
|
||||
|
||||
#if any files exist in directory with improper group reset them
|
||||
$FIND $FILEDIR -type f -maxdepth 1 ! -group $REVGID -exec $LS -ln {} \; | while read subline; do
|
||||
OLD_GID=$( $ECHO $subline | $CUT -d ' ' -f4 )
|
||||
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
|
||||
fp_chown_gid $OLD_GID $REVGID "$SUBFILE"
|
||||
done
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
date_diff()
|
||||
{
|
||||
if $TEST $# -ne 2; then
|
||||
FP_DDM="E"
|
||||
FP_DDS="E"
|
||||
return
|
||||
fi
|
||||
FP_DDD=$( $EXPR $2 - $1 )
|
||||
FP_DDM=$( $EXPR $FP_DDD / 60 )
|
||||
FP_DDS=$( $EXPR $FP_DDD % 60 )
|
||||
}
|
||||
|
||||
fp_end()
|
||||
{
|
||||
if $TEST $SYSREMOUNT -eq 1; then
|
||||
$MOUNT -o remount,ro $DEVICE /system > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if $TEST $SYSSDMOUNT -eq 1; then
|
||||
$UMOUNT /system/sd > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if $TEST $SYSMOUNT -eq 1; then
|
||||
$UMOUNT /system > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if $TEST $DATAMOUNT -eq 1; then
|
||||
$UMOUNT /data > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
|
||||
FP_ENDEPOCH=$( $DATE +%s )
|
||||
|
||||
date_diff $FP_STARTEPOCH $FP_ENDEPOCH
|
||||
|
||||
fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)"
|
||||
}
|
||||
|
||||
#MAIN SCRIPT
|
||||
|
||||
fp_parseargs $@
|
||||
fp_start
|
||||
if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then
|
||||
fp_single "$ONLY_ONE"
|
||||
else
|
||||
fp_all
|
||||
fi
|
||||
fp_end
|
BIN
utilities/parted
Normal file
637
utilities/sdparted
Normal file
@ -0,0 +1,637 @@
|
||||
#!/sbin/sh
|
||||
|
||||
# do logging, if not excluded with -x
|
||||
LOGFILE="/data/sdparted.log"
|
||||
[ "$1" != "-x" ] && echo "$0" "$@" >> "$LOGFILE" && "$0" -x "$@" 2>&1 | tee -a "$LOGFILE" && exit
|
||||
shift
|
||||
|
||||
ShowError() { echo ; echo " err: $1" ; echo ; exit 1 ; }
|
||||
|
||||
ShowMessage() { echo ; echo " msg: $1" ; }
|
||||
|
||||
ShowHelp() {
|
||||
|
||||
cat <<DONEHELP
|
||||
|
||||
$SCRIPTNAME v$SCRIPTREV created by $MYNAME
|
||||
|
||||
if you use this script in your work, please give some credit. thanks.
|
||||
|
||||
requirements: cm-recovery-v1.4
|
||||
|
||||
usage: $SCRIPTNAME [options]
|
||||
|
||||
|
||||
options:
|
||||
|
||||
--fatsize|-fs SIZE[MG] set the size of the fat32 partition to <SIZE>.
|
||||
default=total sdcard size - (ext + swap)
|
||||
|
||||
--extsize|-es SIZE[MG] set the size of the ext partition to <SIZE>.
|
||||
default=$EXTSIZE
|
||||
|
||||
--swapsize|-ss SIZE[MG] set the size of the swap partition to <SIZE>.
|
||||
if set to 0, no swap partition will be created.
|
||||
default=$SWAPSIZE
|
||||
|
||||
--extfs|-efs TYPE set the filesystem of ext partition to <TYPE>.
|
||||
valid types=ext2, ext3, ext4
|
||||
default=$EXTFS
|
||||
|
||||
|
||||
--upgradefs|-ufs TYPE upgrades existing ext partition to <TYPE>.
|
||||
this operation will NOT wipe your sdcard and
|
||||
cannot be used with any partition creation options.
|
||||
valid types=ext3, ext4
|
||||
|
||||
--downgradefs|-dfs TYPE downgrades existing ext partition to <TYPE>.
|
||||
this operation will NOT wipe your sdcard and
|
||||
cannot be used with any partition creation options.
|
||||
valid types=ext2
|
||||
|
||||
|
||||
--interactive|-i interactive mode
|
||||
|
||||
--help|-h display this help
|
||||
|
||||
--printonly|-po display sdcard information
|
||||
|
||||
--silent|-s do not prompt user, not even initial warning.
|
||||
|
||||
|
||||
examples:
|
||||
$SCRIPTNAME creates swap=$SWAPSIZE ext2=$EXTSIZE fat32=remaining free space
|
||||
$SCRIPTNAME -efs ext4 creates swap=$SWAPSIZE ext4=$EXTSIZE fat32=remaining free space
|
||||
$SCRIPTNAME -fs 1.5G -efs ext3 creates swap=$SWAPSIZE ext3=$EXTSIZE fat32=1536
|
||||
$SCRIPTNAME -es 256M -ss 0 creates no swap ext2=256 fat32=remaining free space
|
||||
$SCRIPTNAME -ufs ext4 upgrades ext partition to ext4
|
||||
|
||||
DONEHELP
|
||||
|
||||
}
|
||||
|
||||
UserAbort() {
|
||||
|
||||
WHILEEXIT=
|
||||
|
||||
while [ -z "$WHILEEXIT" ]
|
||||
do
|
||||
echo -n "do you want to continue? (Y/n) "
|
||||
read response
|
||||
echo
|
||||
[ "$response" = "Y" ] || [ "$response" = "n" ] || [ "$response" = "N" ] && WHILEEXIT="$response"
|
||||
done
|
||||
|
||||
echo "$response" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
|
||||
[ "$response" != "Y" ]
|
||||
|
||||
}
|
||||
|
||||
UnmountAll () {
|
||||
|
||||
# unmount all partitions so we can work with $SDPATH
|
||||
# i'm assuming no more than 3 partitions
|
||||
# maybe make a little more elegant later
|
||||
echo -n "unmounting all partitions..."
|
||||
umount "$FATPATH" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
umount "$EXTPATH" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
umount "$SWAPPATH" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
|
||||
CheckReqs() {
|
||||
|
||||
echo -n "checking script requirements..."
|
||||
# check for valid sdcard
|
||||
[ -e $SDPATH ] || ShowError "$SDPATH does not exist!"
|
||||
|
||||
# look for necessary programs
|
||||
[ -e $CMPARTED ] || ShowError "$CMPARTED does not exist!"
|
||||
[ -e $CMTUNE2FS ] || ShowError "$CMTUNE2FS does not exist!"
|
||||
[ -e $CME2FSCK ] || ShowError "$CME2FSCK does not exist!"
|
||||
|
||||
# verify cm-v1.4
|
||||
PARTEDREV=`"$CMPARTED" "$SDPATH" version | grep Parted | cut -d" " -f3`
|
||||
[ "$PARTEDREV" == "1.8.8.1.179-aef3" ] || ShowError "you are not using parted v1.8.8.1.179-aef3!"
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
CheckTableType() {
|
||||
|
||||
TABLETYPE=`"$CMPARTED" "$SDPATH" print | grep Table: | cut -d" " -f3`
|
||||
|
||||
[ "$TABLETYPE" == "loop" -o "$TABLETYPE" == "msdos" ] && TTISOK=1 || TTISOK=0
|
||||
[ "$TABLETYPE" == "loop" ] && TTISLOOP=1 || TTISLOOP=0
|
||||
[ "$TABLETYPE" == "msdos" ] && TTISMSDOS=1 || TTISMOSDOS=0
|
||||
|
||||
}
|
||||
|
||||
ValidateExtArg() {
|
||||
|
||||
FUNC_RET="nonzero"
|
||||
|
||||
# validating argument
|
||||
[ "$1" != "ext2" ] && [ "$1" != "ext3" ] && [ "$1" != "ext4" ] && FUNC_RET=
|
||||
|
||||
[ -z "$FUNC_RET" ] && [ -z "$IMODE" ] && ShowError "$1 is not a valid filesystem."
|
||||
[ -z "$FUNC_RET" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid filesystem."
|
||||
|
||||
# return valid argument
|
||||
[ -n "$FUNC_RET" ] && FUNC_RET="$1"
|
||||
|
||||
}
|
||||
|
||||
ValidateSizeArg() {
|
||||
|
||||
# check for zero-length arg to protect expr length
|
||||
[ -z "$1" ] && ShowError "zero-length argument passed to size-validator"
|
||||
|
||||
SIZEMB=
|
||||
ARGLEN=`expr length $1`
|
||||
SIZELEN=$(($ARGLEN-1))
|
||||
SIZEARG=`expr substr $1 1 $SIZELEN`
|
||||
SIZEUNIT=`expr substr $1 $ARGLEN 1`
|
||||
|
||||
# check if SIZEARG is an integer
|
||||
if [ $SIZEARG -eq $SIZEARG 2> /dev/null ] ; then
|
||||
# look for G
|
||||
[ "$SIZEUNIT" == "G" ] && SIZEMB=$(($SIZEARG * 1024))
|
||||
# look for M
|
||||
[ "$SIZEUNIT" == "M" ] && SIZEMB=$SIZEARG
|
||||
# no units on arg AND prevents using bogus size units
|
||||
[ -z "$SIZEMB" ] && [ $SIZEUNIT -eq $SIZEUNIT 2> /dev/null ] && SIZEMB=$1
|
||||
# check if SIZEARG is a floating point number, GB only
|
||||
elif [ `expr index "$SIZEARG" .` != 0 ] && [ "$SIZEUNIT" == "G" ] ; then
|
||||
INT=`echo "$SIZEARG" | cut -d"." -f1`
|
||||
FRAC=`echo "$SIZEARG" | cut -d"." -f2`
|
||||
SIGDIGITS=`expr length $FRAC`
|
||||
|
||||
[ -z "$INT" ] && INT=0
|
||||
INTMB=$(($INT * 1024))
|
||||
FRACMB=$((($FRAC * 1024) / (10**$SIGDIGITS)))
|
||||
SIZEMB=$(($INTMB + $FRACMB))
|
||||
# it's not a valid size
|
||||
else
|
||||
[ -z "$IMODE" ] && ShowError "$1 is not a valid size"
|
||||
fi
|
||||
|
||||
[ -z "$SIZEMB" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid size"
|
||||
|
||||
# return valid argument in MB
|
||||
FUNC_RET=$SIZEMB
|
||||
|
||||
}
|
||||
|
||||
CalculatePartitions() {
|
||||
|
||||
# get size of sdcard in MB & do some math
|
||||
SDSIZEMB=`"$CMPARTED" "$SDPATH" unit MB print | grep $SDPATH | cut -d" " -f3`
|
||||
SDSIZE=${SDSIZEMB%MB}
|
||||
[ -n "$FATSIZE" ] || FATSIZE=$(($SDSIZE - $EXTSIZE - $SWAPSIZE))
|
||||
EXTEND=$(($FATSIZE + $EXTSIZE))
|
||||
SWAPEND=$(($EXTEND + $SWAPSIZE))
|
||||
|
||||
# check for fatsize of 0
|
||||
[ $FATSIZE -le 0 ] && ShowError "must have a fat32 partition greater than 0MB"
|
||||
|
||||
# check for zero-length sdsize...
|
||||
# indicative of parted not reporting length
|
||||
# correctly b/c of error on sdcard
|
||||
[ -z "$SDSIZE" ] && ShowError "zero-length argument passed to partition-calculator"
|
||||
|
||||
# make sure we're not being asked to do the impossible
|
||||
[ $(($FATSIZE + $EXTSIZE + $SWAPSIZE)) -gt $SDSIZE ] && [ -z "$IMODE" ] && ShowError "sum of requested partitions is greater than sdcard size"
|
||||
|
||||
}
|
||||
|
||||
|
||||
UpgradeDowngradeOnly() {
|
||||
|
||||
if [ -n "$UEXTFSONLY" ] ; then
|
||||
echo
|
||||
[ -n "$CREATEPART" ] && ShowError "cannot use upgrade option when creating partitions, use -efs instead"
|
||||
[ -n "$DEXTFSONLY" ] && ShowError "cannot upgrade AND downgrade, it just doesn't make sense"
|
||||
echo "you have chosen to upgrade $EXTPATH to $UEXTFSONLY."
|
||||
echo "this action will NOT delete any data from sdcard."
|
||||
echo
|
||||
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
|
||||
echo
|
||||
UpgradeExt "$UEXTFSONLY"
|
||||
ShowCardInfo
|
||||
elif [ -n "$DEXTFSONLY" ] ; then
|
||||
echo
|
||||
[ -n "$CREATEPART" ] && ShowError "cannot use downgrade option when creating partitions."
|
||||
[ -n "$UEXTFSONLY" ] && ShowError "cannot downgrade AND upgrade, it just doesn't make sense."
|
||||
echo "you have chosen to downgrade $EXTPATH to $DEXTFSONLY."
|
||||
echo "this action will NOT delete any data from sdcard."
|
||||
echo
|
||||
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
|
||||
echo
|
||||
DowngradeExt "$DEXTFSONLY"
|
||||
ShowCardInfo
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
PrepareSdCard() {
|
||||
|
||||
echo
|
||||
if [ $TTISOK -eq 0 ] ; then
|
||||
echo "partition 1 may not be aligned to cylinder boundaries."
|
||||
echo "to continue, this must be corrected."
|
||||
elif [ $TTISLOOP -gt 0 ] ; then
|
||||
echo "your sdcard's partition table type is $TABLETYPE."
|
||||
echo "to continue, partition table must be set to 'msdos'."
|
||||
elif [ $TTISMSDOS -gt 0 ] ; then
|
||||
# just a reminder..in a later version,
|
||||
# i may implement resizing of partitions,
|
||||
# so this will be unnecessary. but until then...
|
||||
echo "to continue, all existing partitions must be removed."
|
||||
else
|
||||
# this is not good, and should never happen
|
||||
# if it does, there is a serious problem
|
||||
ShowError "sdcard failed table type check."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "this action will remove all data from your sdcard."
|
||||
echo
|
||||
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
|
||||
|
||||
[ $TTISOK -eq 0 ] && echo -n "correcting cylinder boundaries..."
|
||||
[ $TTISLOOP -gt 0 ] && echo -n "setting partition table to msdos..."
|
||||
[ $TTISMSDOS -gt 0 ] && echo -n "removing all partitions..."
|
||||
|
||||
"$CMPARTED" -s "$SDPATH" mklabel msdos 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
ShowActions() {
|
||||
|
||||
echo
|
||||
echo "total size of sdcard=$SDSIZEMB"
|
||||
echo
|
||||
echo "the following actions will be performed:"
|
||||
echo " -create $FATSIZE""MB fat32 partition"
|
||||
[ $EXTSIZE -gt 0 ] && echo " -create $EXTSIZE""MB ext2 partition"
|
||||
[ $SWAPSIZE -gt 0 ] && echo " -create $SWAPSIZE""MB swap partition"
|
||||
[ "$EXTFS" != "ext2" ] && echo " -ext2 partition will be upgraded to $EXTFS"
|
||||
echo
|
||||
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
ShowCardInfo() {
|
||||
|
||||
CheckTableType
|
||||
|
||||
echo
|
||||
echo "retrieving current sdcard information..."
|
||||
|
||||
if [ $TTISOK -gt 0 ] ; then
|
||||
echo
|
||||
parted "$SDPATH" print
|
||||
echo
|
||||
echo "script log is located @ /data/sdparted.log"
|
||||
exit 0
|
||||
else
|
||||
echo
|
||||
echo "partition 1 may not be aligned to cylinder boundaries."
|
||||
ShowError "cannot complete print operation."
|
||||
fi
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
|
||||
PartitionSdCard() {
|
||||
|
||||
echo "performing selected actions..."
|
||||
echo
|
||||
|
||||
if [ $FATSIZE -gt 0 ] ; then
|
||||
echo -n "creating fat32 partition..."
|
||||
"$CMPARTED" -s "$SDPATH" mkpartfs primary fat32 0 "$FATSIZE"MB 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
fi
|
||||
|
||||
if [ $EXTSIZE -gt 0 ] ; then
|
||||
echo -n "creating ext2 partition..."
|
||||
"$CMPARTED" -s "$SDPATH" mkpartfs primary ext2 "$FATSIZE"MB "$EXTEND"MB 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
fi
|
||||
|
||||
if [ $SWAPSIZE -gt 0 ] ; then
|
||||
echo -n "creating swap partition..."
|
||||
"$CMPARTED" -s "$SDPATH" mkpartfs primary linux-swap "$EXTEND"MB "$SWAPEND"MB 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
fi
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
UpgradeExt() {
|
||||
|
||||
# check for no upgrade
|
||||
[ "$1" == "ext2" ] && return
|
||||
# check for ext partition
|
||||
[ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist"
|
||||
|
||||
# have to use -m switch for this check b/c parted incorrectly
|
||||
# reports all ext partitions as ext2 when running print <number>
|
||||
CHECKEXTFS=`"$CMPARTED" -m "$SDPATH" print | grep ext | cut -d":" -f5`
|
||||
[ "$CHECKEXTFS" == "$1" ] && ShowError "$EXTPATH is already $1"
|
||||
|
||||
# grabbed the code bits for ext3 from upgrade_fs(credit:cyanogen)
|
||||
# check for ext2...must upgrade to ext3 first b4 ext4
|
||||
if [ "$1" == "ext3" -o "$1" == "ext4" ] ; then
|
||||
echo -n "adding journaling to $EXTPATH..."
|
||||
umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
|
||||
"$CME2FSCK" -p "$EXTPATH" 2>&1 >>"$LOGFILE"
|
||||
"$CMTUNE2FS" -c0 -i0 -j "$EXTPATH" 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
fi
|
||||
|
||||
# and got convert to ext4 from xda-forum(credit:Denkai)
|
||||
if [ "$1" == "ext4" ] ; then
|
||||
echo -n "converting $EXTPATH to ext4 filesystem..."
|
||||
umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
|
||||
"$CMTUNE2FS" -O extents,uninit_bg,dir_index "$EXTPATH" 2>&1 >>"$LOGFILE"
|
||||
"$CME2FSCK" -fpDC0 "$EXTPATH" 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
fi
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
DowngradeExt() {
|
||||
|
||||
# check for ext partition
|
||||
[ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist"
|
||||
|
||||
# have to use print for this check b/c parted incorrectly
|
||||
# reports all ext partitions as ext2 when running print <number>
|
||||
CHECKEXTFS=`"$CMPARTED" -m "$SDPATH" print | grep ext | cut -d":" -f5`
|
||||
[ "$CHECKEXTFS" == "$1" ] && ShowError "$EXTPATH is already $1"
|
||||
|
||||
if [ "$CHECKEXTFS" == "ext4" -o "$1" == "ext3" ] ; then
|
||||
# interweb says downgrading from ext4 is not possible
|
||||
# without a backup/restore procedure.
|
||||
# if i figure it out, i'll implement it.
|
||||
ShowError "downgrading from ext4 is not currently supported"
|
||||
fi
|
||||
|
||||
if [ "$1" == "ext2" ] ; then
|
||||
echo -n "removing journaling from $EXTPATH..."
|
||||
umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
|
||||
"$CMTUNE2FS" -O ^has_journal "$EXTPATH" 2>&1 >>"$LOGFILE"
|
||||
"$CME2FSCK" -fp "$EXTPATH" 2>&1 >>"$LOGFILE"
|
||||
echo "done"
|
||||
fi
|
||||
echo
|
||||
|
||||
}
|
||||
|
||||
|
||||
Interactive() {
|
||||
|
||||
cat <<DONEINSTRUCT
|
||||
|
||||
sdparted interactive mode
|
||||
|
||||
rules:
|
||||
1. no answer means you accept default value
|
||||
2. enter '0' for no partition
|
||||
|
||||
DONEINSTRUCT
|
||||
|
||||
UserAbort && ShowError "script canceled by user"
|
||||
|
||||
CalculatePartitions
|
||||
|
||||
GetSwapSize
|
||||
|
||||
FATSIZE=
|
||||
|
||||
CalculatePartitions
|
||||
|
||||
GetExtSize
|
||||
|
||||
GetExtType
|
||||
|
||||
FATSIZE=
|
||||
|
||||
CalculatePartitions
|
||||
|
||||
GetFatSize
|
||||
|
||||
}
|
||||
|
||||
GetSwapSize() {
|
||||
|
||||
SWAPTEST=
|
||||
|
||||
while [ -z "$SWAPTEST" ]
|
||||
do
|
||||
echo
|
||||
echo -n "swap partition size [default=$SWAPSIZE]: "
|
||||
read SWAPRESP
|
||||
|
||||
[ -z "$SWAPRESP" ] && SWAPRESP="$SWAPSIZE"
|
||||
echo "$SWAPRESP" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
|
||||
ValidateSizeArg "$SWAPRESP"
|
||||
SWAPTEST="$FUNC_RET"
|
||||
[ -n "$SWAPTEST" ] && [ $SWAPTEST -gt $SDSIZE ] && ShowMessage "$SWAPRESP > available space($(($SDSIZE))M)." && SWAPTEST=
|
||||
done
|
||||
|
||||
SWAPSIZE=$SWAPTEST
|
||||
|
||||
}
|
||||
|
||||
GetExtSize() {
|
||||
|
||||
EXTTEST=
|
||||
|
||||
while [ -z "$EXTTEST" ]
|
||||
do
|
||||
echo
|
||||
echo -n "ext partition size [default=$EXTSIZE]: "
|
||||
read EXTRESP
|
||||
|
||||
[ -z "$EXTRESP" ] && EXTRESP="$EXTSIZE"
|
||||
echo "$EXTRESP" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
|
||||
ValidateSizeArg "$EXTRESP"
|
||||
EXTTEST="$FUNC_RET"
|
||||
|
||||
[ -n "$EXTTEST" ] && [ $EXTTEST -gt $(($SDSIZE - $SWAPSIZE)) ] && ShowMessage "$EXTRESP > available space($(($SDSIZE - $SWAPSIZE))M)." && EXTTEST=
|
||||
done
|
||||
|
||||
EXTSIZE=$EXTTEST
|
||||
|
||||
}
|
||||
|
||||
GetExtType() {
|
||||
|
||||
FSTEST=
|
||||
|
||||
while [ -z "$FSTEST" ]
|
||||
do
|
||||
echo
|
||||
echo -n "ext partition type [default=$EXTFS]: "
|
||||
read FSRESP
|
||||
|
||||
[ -z "$FSRESP" ] && FSRESP="$EXTFS"
|
||||
echo "$FSRESP" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
|
||||
ValidateExtArg "$FSRESP"
|
||||
FSTEST="$FUNC_RET"
|
||||
done
|
||||
|
||||
EXTFS="$FSTEST"
|
||||
|
||||
}
|
||||
|
||||
GetFatSize() {
|
||||
|
||||
FATTEST=
|
||||
|
||||
while [ -z "$FATTEST" ]
|
||||
do
|
||||
echo
|
||||
echo -n "fat partition size [default=$FATSIZE]: "
|
||||
read FATRESP
|
||||
|
||||
[ -z "$FATRESP" ] && FATRESP="$FATSIZE"
|
||||
echo "$FATRESP" > /dev/null 2>&1 >>"$LOGFILE"
|
||||
|
||||
ValidateSizeArg "$FATRESP"
|
||||
FATTEST="$FUNC_RET"
|
||||
|
||||
[ -n "$FATTEST" ] && [ $FATTEST -gt $FATSIZE ] && ShowMessage "$FATRESP > available space($(($SDSIZE - $SWAPSIZE - $EXTSIZE))M)." && FATTEST=
|
||||
[ -n "$FATTEST" ] && [ $FATTEST -le 0 ] && ShowMessage "must have a fat32 partition greater than 0MB" && FATTEST=
|
||||
done
|
||||
|
||||
FATSIZE=$FATTEST
|
||||
|
||||
}
|
||||
|
||||
|
||||
SCRIPTNAME="sdparted"
|
||||
SCRIPTREV="0.6"
|
||||
MYNAME="51dusty"
|
||||
|
||||
IMODE=
|
||||
SILENTRUN=
|
||||
CREATEPART=
|
||||
FUNC_RET=
|
||||
|
||||
UEXTFSONLY=
|
||||
DEXTFSONLY=
|
||||
|
||||
TTISOK=
|
||||
TTISLOOP=
|
||||
TTISMSDOS=
|
||||
|
||||
SDSIZE=
|
||||
SDSIZEMB=
|
||||
if [ -z "$SDPATH" ]
|
||||
then
|
||||
SDPATH="/dev/block/mmcblk0"
|
||||
else
|
||||
echo Found SDPATH=$SDPATH
|
||||
fi
|
||||
|
||||
FATSIZE=
|
||||
FATTYPE="fat32"
|
||||
FATPATH=$SDPATH"p1"
|
||||
|
||||
EXTSIZE=512
|
||||
EXTFS="ext2"
|
||||
EXTPATH=$SDPATH"p2"
|
||||
EXTEND=
|
||||
|
||||
SWAPSIZE=32
|
||||
SWAPTYPE="linux-swap"
|
||||
SWAPPATH=$SDPATH"p3"
|
||||
SWAPEND=
|
||||
|
||||
CMPARTED="/sbin/parted"
|
||||
CMTUNE2FS="/sbin/tune2fs"
|
||||
CME2FSCK="/sbin/e2fsck"
|
||||
|
||||
# give the output some breathing room
|
||||
echo "$SCRIPTREV" >> "$LOGFILE"
|
||||
echo
|
||||
|
||||
# check for arguments
|
||||
while [ $# -gt 0 ] ; do
|
||||
case "$1" in
|
||||
|
||||
-h|--help) ShowHelp ; exit 0 ;;
|
||||
|
||||
-fs|--fatsize) shift ; ValidateSizeArg "$1" ; FATSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
|
||||
-es|--extsize) shift ; ValidateSizeArg "$1" ; EXTSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
|
||||
-ss|--swapsize) shift ; ValidateSizeArg "$1" ; SWAPSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
|
||||
-efs|--extfs) shift ; ValidateExtArg "$1" ; EXTFS="$FUNC_RET" ; CREATEPART="$1" ;;
|
||||
|
||||
-ufs|--upgradefs) shift ; ValidateExtArg "$1" ; UEXTFSONLY="$FUNC_RET" ;;
|
||||
-dfs|--downgradefs) shift ; ValidateExtArg "$1" ; DEXTFSONLY="$FUNC_RET" ;;
|
||||
|
||||
-i|--interactive) IMODE="$1" ;;
|
||||
|
||||
-s|--silent) SILENTRUN="$1" ;;
|
||||
|
||||
-po|--printonly) ShowCardInfo ;;
|
||||
|
||||
*) ShowHelp ; ShowError "unknown argument '$1'" ;;
|
||||
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# can't do silent when in interactive mode
|
||||
[ -n "$IMODE" ] && SILENTRUN=
|
||||
|
||||
# make sure sdcard exists and all needed files are here
|
||||
CheckReqs
|
||||
|
||||
# unmount all
|
||||
UnmountAll
|
||||
|
||||
# upgrade only? downgrade only?
|
||||
UpgradeDowngradeOnly
|
||||
|
||||
# check table
|
||||
CheckTableType
|
||||
|
||||
# prep card
|
||||
PrepareSdCard
|
||||
|
||||
# check for interactive mode
|
||||
[ -n "$IMODE" ] && Interactive
|
||||
|
||||
# do some math
|
||||
CalculatePartitions
|
||||
|
||||
# last chance to cancel
|
||||
ShowActions
|
||||
|
||||
# partition card
|
||||
PartitionSdCard
|
||||
|
||||
# upgrade fs if necessary
|
||||
UpgradeExt "$EXTFS"
|
||||
|
||||
# say goodbye and show print output
|
||||
ShowCardInfo
|
BIN
utilities/tune2fs
Executable file
443
verifier.c
@ -17,345 +17,168 @@
|
||||
#include "common.h"
|
||||
#include "verifier.h"
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
#include "mincrypt/sha.h"
|
||||
|
||||
#include <netinet/in.h> /* required for resolv.h */
|
||||
#include <resolv.h> /* for base64 codec */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Return an allocated buffer with the contents of a zip file entry. */
|
||||
static char *slurpEntry(const ZipArchive *pArchive, const ZipEntry *pEntry) {
|
||||
if (!mzIsZipEntryIntact(pArchive, pEntry)) {
|
||||
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
|
||||
LOGE("Invalid %.*s\n", fn.len, fn.str);
|
||||
return NULL;
|
||||
// Look for an RSA signature embedded in the .ZIP file comment given
|
||||
// the path to the zip. Verify it matches one of the given public
|
||||
// keys.
|
||||
//
|
||||
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
|
||||
// or no key matches the signature).
|
||||
|
||||
int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) {
|
||||
ui_set_progress(0.0);
|
||||
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (f == NULL) {
|
||||
LOGE("failed to open %s (%s)\n", path, strerror(errno));
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
int len = mzGetZipEntryUncompLen(pEntry);
|
||||
char *buf = malloc(len + 1);
|
||||
if (buf == NULL) {
|
||||
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
|
||||
LOGE("Can't allocate %d bytes for %.*s\n", len, fn.len, fn.str);
|
||||
return NULL;
|
||||
// An archive with a whole-file signature will end in six bytes:
|
||||
//
|
||||
// $ff $ff (2-byte comment size) (2-byte signature start)
|
||||
//
|
||||
// (As far as the ZIP format is concerned, these are part of the
|
||||
// archive comment.) We start by reading this footer, this tells
|
||||
// us how far back from the end we have to start reading to find
|
||||
// the whole comment.
|
||||
|
||||
#define FOOTER_SIZE 6
|
||||
|
||||
if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
|
||||
LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
if (!mzReadZipEntry(pArchive, pEntry, buf, len)) {
|
||||
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
|
||||
LOGE("Can't read %.*s\n", fn.len, fn.str);
|
||||
free(buf);
|
||||
return NULL;
|
||||
unsigned char footer[FOOTER_SIZE];
|
||||
if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
|
||||
LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
struct DigestContext {
|
||||
SHA_CTX digest;
|
||||
unsigned *doneBytes;
|
||||
unsigned totalBytes;
|
||||
};
|
||||
|
||||
|
||||
/* mzProcessZipEntryContents callback to update an SHA-1 hash context. */
|
||||
static bool updateHash(const unsigned char *data, int dataLen, void *cookie) {
|
||||
struct DigestContext *context = (struct DigestContext *) cookie;
|
||||
SHA_update(&context->digest, data, dataLen);
|
||||
if (context->doneBytes != NULL) {
|
||||
*context->doneBytes += dataLen;
|
||||
if (context->totalBytes > 0) {
|
||||
ui_set_progress(*context->doneBytes * 1.0 / context->totalBytes);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Get the SHA-1 digest of a zip file entry. */
|
||||
static bool digestEntry(const ZipArchive *pArchive, const ZipEntry *pEntry,
|
||||
unsigned *doneBytes, unsigned totalBytes,
|
||||
uint8_t digest[SHA_DIGEST_SIZE]) {
|
||||
struct DigestContext context;
|
||||
SHA_init(&context.digest);
|
||||
context.doneBytes = doneBytes;
|
||||
context.totalBytes = totalBytes;
|
||||
if (!mzProcessZipEntryContents(pArchive, pEntry, updateHash, &context)) {
|
||||
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
|
||||
LOGE("Can't digest %.*s\n", fn.len, fn.str);
|
||||
return false;
|
||||
if (footer[2] != 0xff || footer[3] != 0xff) {
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
memcpy(digest, SHA_final(&context.digest), SHA_DIGEST_SIZE);
|
||||
int comment_size = footer[4] + (footer[5] << 8);
|
||||
int signature_start = footer[0] + (footer[1] << 8);
|
||||
LOGI("comment is %d bytes; signature %d bytes from end\n",
|
||||
comment_size, signature_start);
|
||||
|
||||
#ifdef LOG_VERBOSE
|
||||
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
|
||||
char base64[SHA_DIGEST_SIZE * 3];
|
||||
b64_ntop(digest, SHA_DIGEST_SIZE, base64, sizeof(base64));
|
||||
LOGV("sha1(%.*s) = %s\n", fn.len, fn.str, base64);
|
||||
#endif
|
||||
if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
|
||||
// "signature" block isn't big enough to contain an RSA block.
|
||||
LOGE("signature is too short\n");
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#define EOCD_HEADER_SIZE 22
|
||||
|
||||
// The end-of-central-directory record is 22 bytes plus any
|
||||
// comment length.
|
||||
size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
|
||||
|
||||
/* Find a /META-INF/xxx.SF signature file signed by a matching xxx.RSA file. */
|
||||
static const ZipEntry *verifySignature(const ZipArchive *pArchive,
|
||||
const RSAPublicKey *pKeys, unsigned int numKeys) {
|
||||
static const char prefix[] = "META-INF/";
|
||||
static const char rsa[] = ".RSA", sf[] = ".SF";
|
||||
if (fseek(f, -eocd_size, SEEK_END) != 0) {
|
||||
LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
unsigned int i, j;
|
||||
for (i = 0; i < mzZipEntryCount(pArchive); ++i) {
|
||||
const ZipEntry *rsaEntry = mzGetZipEntryAt(pArchive, i);
|
||||
UnterminatedString rsaName = mzGetZipEntryFileName(rsaEntry);
|
||||
int rsaLen = mzGetZipEntryUncompLen(rsaEntry);
|
||||
if (rsaLen >= RSANUMBYTES && rsaName.len > sizeof(prefix) &&
|
||||
!strncmp(rsaName.str, prefix, sizeof(prefix) - 1) &&
|
||||
!strncmp(rsaName.str + rsaName.len - sizeof(rsa) + 1,
|
||||
rsa, sizeof(rsa) - 1)) {
|
||||
char *sfName = malloc(rsaName.len - sizeof(rsa) + sizeof(sf) + 1);
|
||||
if (sfName == NULL) {
|
||||
LOGE("Can't allocate %d bytes for filename\n", rsaName.len);
|
||||
continue;
|
||||
}
|
||||
// Determine how much of the file is covered by the signature.
|
||||
// This is everything except the signature data and length, which
|
||||
// includes all of the EOCD except for the comment length field (2
|
||||
// bytes) and the comment data.
|
||||
size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
|
||||
|
||||
/* Replace .RSA with .SF */
|
||||
strncpy(sfName, rsaName.str, rsaName.len - sizeof(rsa) + 1);
|
||||
strcpy(sfName + rsaName.len - sizeof(rsa) + 1, sf);
|
||||
const ZipEntry *sfEntry = mzFindZipEntry(pArchive, sfName);
|
||||
unsigned char* eocd = malloc(eocd_size);
|
||||
if (eocd == NULL) {
|
||||
LOGE("malloc for EOCD record failed\n");
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
if (fread(eocd, 1, eocd_size, f) != eocd_size) {
|
||||
LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
if (sfEntry == NULL) {
|
||||
LOGW("Missing signature file %s\n", sfName);
|
||||
free(sfName);
|
||||
continue;
|
||||
}
|
||||
// If this is really is the EOCD record, it will begin with the
|
||||
// magic number $50 $4b $05 $06.
|
||||
if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
|
||||
eocd[2] != 0x05 || eocd[3] != 0x06) {
|
||||
LOGE("signature length doesn't match EOCD marker\n");
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
free(sfName);
|
||||
|
||||
uint8_t sfDigest[SHA_DIGEST_SIZE];
|
||||
if (!digestEntry(pArchive, sfEntry, NULL, 0, sfDigest)) continue;
|
||||
|
||||
char *rsaBuf = slurpEntry(pArchive, rsaEntry);
|
||||
if (rsaBuf == NULL) continue;
|
||||
|
||||
/* Try to verify the signature with all the keys. */
|
||||
uint8_t *sig = (uint8_t *) rsaBuf + rsaLen - RSANUMBYTES;
|
||||
for (j = 0; j < numKeys; ++j) {
|
||||
if (RSA_verify(&pKeys[j], sig, RSANUMBYTES, sfDigest)) {
|
||||
free(rsaBuf);
|
||||
LOGI("Verified %.*s\n", rsaName.len, rsaName.str);
|
||||
return sfEntry;
|
||||
}
|
||||
}
|
||||
|
||||
free(rsaBuf);
|
||||
LOGW("Can't verify %.*s\n", rsaName.len, rsaName.str);
|
||||
int i;
|
||||
for (i = 4; i < eocd_size-3; ++i) {
|
||||
if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b &&
|
||||
eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
|
||||
// if the sequence $50 $4b $05 $06 appears anywhere after
|
||||
// the real one, minzip will find the later (wrong) one,
|
||||
// which could be exploitable. Fail verification if
|
||||
// this sequence occurs anywhere after the real one.
|
||||
LOGE("EOCD marker occurs after start of EOCD\n");
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
LOGE("No signature (%d files)\n", mzZipEntryCount(pArchive));
|
||||
return NULL;
|
||||
}
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
SHA_CTX ctx;
|
||||
SHA_init(&ctx);
|
||||
unsigned char* buffer = malloc(BUFFER_SIZE);
|
||||
if (buffer == NULL) {
|
||||
LOGE("failed to alloc memory for sha1 buffer\n");
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
/* Verify /META-INF/MANIFEST.MF against the digest in a signature file. */
|
||||
static const ZipEntry *verifyManifest(const ZipArchive *pArchive,
|
||||
const ZipEntry *sfEntry) {
|
||||
static const char prefix[] = "SHA1-Digest-Manifest: ", eol[] = "\r\n";
|
||||
uint8_t expected[SHA_DIGEST_SIZE + 3], actual[SHA_DIGEST_SIZE];
|
||||
|
||||
char *sfBuf = slurpEntry(pArchive, sfEntry);
|
||||
if (sfBuf == NULL) return NULL;
|
||||
|
||||
char *line, *save;
|
||||
for (line = strtok_r(sfBuf, eol, &save); line != NULL;
|
||||
line = strtok_r(NULL, eol, &save)) {
|
||||
if (!strncasecmp(prefix, line, sizeof(prefix) - 1)) {
|
||||
UnterminatedString fn = mzGetZipEntryFileName(sfEntry);
|
||||
const char *digest = line + sizeof(prefix) - 1;
|
||||
int n = b64_pton(digest, expected, sizeof(expected));
|
||||
if (n != SHA_DIGEST_SIZE) {
|
||||
LOGE("Invalid base64 in %.*s: %s (%d)\n",
|
||||
fn.len, fn.str, digest, n);
|
||||
line = NULL;
|
||||
}
|
||||
break;
|
||||
double frac = -1.0;
|
||||
size_t so_far = 0;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
while (so_far < signed_len) {
|
||||
int size = BUFFER_SIZE;
|
||||
if (signed_len - so_far < size) size = signed_len - so_far;
|
||||
if (fread(buffer, 1, size, f) != size) {
|
||||
LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
SHA_update(&ctx, buffer, size);
|
||||
so_far += size;
|
||||
double f = so_far / (double)signed_len;
|
||||
if (f > frac + 0.02 || size == so_far) {
|
||||
ui_set_progress(f);
|
||||
frac = f;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
free(buffer);
|
||||
|
||||
free(sfBuf);
|
||||
|
||||
if (line == NULL) {
|
||||
LOGE("No digest manifest in signature file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *mfName = "META-INF/MANIFEST.MF";
|
||||
const ZipEntry *mfEntry = mzFindZipEntry(pArchive, mfName);
|
||||
if (mfEntry == NULL) {
|
||||
LOGE("No manifest file %s\n", mfName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!digestEntry(pArchive, mfEntry, NULL, 0, actual)) return NULL;
|
||||
if (memcmp(expected, actual, SHA_DIGEST_SIZE)) {
|
||||
UnterminatedString fn = mzGetZipEntryFileName(sfEntry);
|
||||
LOGE("Wrong digest for %s in %.*s\n", mfName, fn.len, fn.str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGI("Verified %s\n", mfName);
|
||||
return mfEntry;
|
||||
}
|
||||
|
||||
|
||||
/* Verify all the files in a Zip archive against the manifest. */
|
||||
static bool verifyArchive(const ZipArchive *pArchive, const ZipEntry *mfEntry) {
|
||||
static const char namePrefix[] = "Name: ";
|
||||
static const char contPrefix[] = " "; // Continuation of the filename
|
||||
static const char digestPrefix[] = "SHA1-Digest: ";
|
||||
static const char eol[] = "\r\n";
|
||||
|
||||
char *mfBuf = slurpEntry(pArchive, mfEntry);
|
||||
if (mfBuf == NULL) return false;
|
||||
|
||||
/* we're using calloc() here, so the initial state of the array is false */
|
||||
bool *unverified = (bool *) calloc(mzZipEntryCount(pArchive), sizeof(bool));
|
||||
if (unverified == NULL) {
|
||||
LOGE("Can't allocate valid flags\n");
|
||||
free(mfBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Mark all the files in the archive that need to be verified.
|
||||
* As we scan the manifest and check signatures, we'll unset these flags.
|
||||
* At the end, we'll make sure that all the flags are unset.
|
||||
*/
|
||||
|
||||
unsigned i, totalBytes = 0;
|
||||
for (i = 0; i < mzZipEntryCount(pArchive); ++i) {
|
||||
const ZipEntry *entry = mzGetZipEntryAt(pArchive, i);
|
||||
UnterminatedString fn = mzGetZipEntryFileName(entry);
|
||||
int len = mzGetZipEntryUncompLen(entry);
|
||||
|
||||
// Don't validate: directories, the manifest, *.RSA, and *.SF.
|
||||
|
||||
if (entry == mfEntry) {
|
||||
LOGV("Skipping manifest %.*s\n", fn.len, fn.str);
|
||||
} else if (fn.len > 0 && fn.str[fn.len-1] == '/' && len == 0) {
|
||||
LOGV("Skipping directory %.*s\n", fn.len, fn.str);
|
||||
} else if (!strncasecmp(fn.str, "META-INF/", 9) && (
|
||||
!strncasecmp(fn.str + fn.len - 4, ".RSA", 4) ||
|
||||
!strncasecmp(fn.str + fn.len - 3, ".SF", 3))) {
|
||||
LOGV("Skipping signature %.*s\n", fn.len, fn.str);
|
||||
} else {
|
||||
unverified[i] = true;
|
||||
totalBytes += len;
|
||||
const uint8_t* sha1 = SHA_final(&ctx);
|
||||
for (i = 0; i < numKeys; ++i) {
|
||||
// The 6 bytes is the "$ff $ff (signature_start) (comment_size)" that
|
||||
// the signing tool appends after the signature itself.
|
||||
if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
|
||||
RSANUMBYTES, sha1)) {
|
||||
LOGI("whole-file signature verified\n");
|
||||
free(eocd);
|
||||
return VERIFY_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned doneBytes = 0;
|
||||
char *line, *save, *name = NULL;
|
||||
for (line = strtok_r(mfBuf, eol, &save); line != NULL;
|
||||
line = strtok_r(NULL, eol, &save)) {
|
||||
if (!strncasecmp(line, namePrefix, sizeof(namePrefix) - 1)) {
|
||||
// "Name:" introducing a new stanza
|
||||
if (name != NULL) {
|
||||
LOGE("No digest:\n %s\n", name);
|
||||
break;
|
||||
}
|
||||
|
||||
name = strdup(line + sizeof(namePrefix) - 1);
|
||||
if (name == NULL) {
|
||||
LOGE("Can't copy filename in %s\n", line);
|
||||
break;
|
||||
}
|
||||
} else if (!strncasecmp(line, contPrefix, sizeof(contPrefix) - 1)) {
|
||||
// Continuing a long name (nothing else should be continued)
|
||||
const char *tail = line + sizeof(contPrefix) - 1;
|
||||
if (name == NULL) {
|
||||
LOGE("Unexpected continuation:\n %s\n", tail);
|
||||
}
|
||||
|
||||
char *concat;
|
||||
if (asprintf(&concat, "%s%s", name, tail) < 0) {
|
||||
LOGE("Can't append continuation %s\n", tail);
|
||||
break;
|
||||
}
|
||||
free(name);
|
||||
name = concat;
|
||||
} else if (!strncasecmp(line, digestPrefix, sizeof(digestPrefix) - 1)) {
|
||||
// "Digest:" supplying a hash code for the current stanza
|
||||
const char *base64 = line + sizeof(digestPrefix) - 1;
|
||||
if (name == NULL) {
|
||||
LOGE("Unexpected digest:\n %s\n", base64);
|
||||
break;
|
||||
}
|
||||
|
||||
const ZipEntry *entry = mzFindZipEntry(pArchive, name);
|
||||
if (entry == NULL) {
|
||||
LOGE("Missing file:\n %s\n", name);
|
||||
break;
|
||||
}
|
||||
if (!mzIsZipEntryIntact(pArchive, entry)) {
|
||||
LOGE("Corrupt file:\n %s\n", name);
|
||||
break;
|
||||
}
|
||||
if (!unverified[mzGetZipEntryIndex(pArchive, entry)]) {
|
||||
LOGE("Unexpected file:\n %s\n", name);
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t expected[SHA_DIGEST_SIZE + 3], actual[SHA_DIGEST_SIZE];
|
||||
int n = b64_pton(base64, expected, sizeof(expected));
|
||||
if (n != SHA_DIGEST_SIZE) {
|
||||
LOGE("Invalid base64:\n %s\n %s\n", name, base64);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!digestEntry(pArchive, entry, &doneBytes, totalBytes, actual) ||
|
||||
memcmp(expected, actual, SHA_DIGEST_SIZE) != 0) {
|
||||
LOGE("Wrong digest:\n %s\n", name);
|
||||
break;
|
||||
}
|
||||
|
||||
LOGI("Verified %s\n", name);
|
||||
unverified[mzGetZipEntryIndex(pArchive, entry)] = false;
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (name != NULL) free(name);
|
||||
free(mfBuf);
|
||||
|
||||
for (i = 0; i < mzZipEntryCount(pArchive) && !unverified[i]; ++i) ;
|
||||
free(unverified);
|
||||
|
||||
// This means we didn't get to the end of the manifest successfully.
|
||||
if (line != NULL) return false;
|
||||
|
||||
if (i < mzZipEntryCount(pArchive)) {
|
||||
const ZipEntry *entry = mzGetZipEntryAt(pArchive, i);
|
||||
UnterminatedString fn = mzGetZipEntryFileName(entry);
|
||||
LOGE("No digest for %.*s\n", fn.len, fn.str);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool verify_jar_signature(const ZipArchive *pArchive,
|
||||
const RSAPublicKey *pKeys, int numKeys) {
|
||||
const ZipEntry *sfEntry = verifySignature(pArchive, pKeys, numKeys);
|
||||
if (sfEntry == NULL) return false;
|
||||
|
||||
const ZipEntry *mfEntry = verifyManifest(pArchive, sfEntry);
|
||||
if (mfEntry == NULL) return false;
|
||||
|
||||
return verifyArchive(pArchive, mfEntry);
|
||||
free(eocd);
|
||||
LOGE("failed to verify whole-file signature\n");
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
12
verifier.h
@ -17,14 +17,14 @@
|
||||
#ifndef _RECOVERY_VERIFIER_H
|
||||
#define _RECOVERY_VERIFIER_H
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
|
||||
/*
|
||||
* Check the digital signature (as applied by jarsigner) on a Zip archive.
|
||||
* Every file in the archive must be signed by one of the supplied RSA keys.
|
||||
/* Look in the file for a signature footer, and verify that it
|
||||
* matches one of the given keys. Return one of the constants below.
|
||||
*/
|
||||
bool verify_jar_signature(const ZipArchive *pArchive,
|
||||
const RSAPublicKey *pKeys, int numKeys);
|
||||
int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys);
|
||||
|
||||
#define VERIFY_SUCCESS 0
|
||||
#define VERIFY_FAILURE 1
|
||||
|
||||
#endif /* _RECOVERY_VERIFIER_H */
|
||||
|