Compare commits
395 Commits
cupcake
...
froyo-stab
Author | SHA1 | Date | |
---|---|---|---|
8df69c0660 | |||
229a543bb3 | |||
517a16b98f | |||
9397e322c3 | |||
ddc2e39dcf | |||
d90ad5dedf | |||
3847a2ddd9 | |||
44bd4947d2 | |||
ac731c88e3 | |||
9acb022ab8 | |||
37d96f7414 | |||
0b23331f23 | |||
4123b58299 | |||
158d25cae4 | |||
7c50645a6c | |||
e5c7e0eaca | |||
12154200a7 | |||
d771acbed0 | |||
1b86754eaa | |||
19447c0550 | |||
fef77c0253 | |||
7a77aec362 | |||
487821abe5 | |||
9456641236 | |||
1805cfe02e | |||
815ca5d230 | |||
0209a62c7d | |||
adf906d034 | |||
7f4ff5cd3a | |||
cb71c2f334 | |||
e17a78ddda | |||
4e10b135cc | |||
56c1b3b576 | |||
68df48c28f | |||
28a41b4df2 | |||
5a9fe1c964 | |||
b5a36a0e20 | |||
e5678e9249 | |||
4233c7fb3c | |||
23ce6b14ae | |||
91e9e830e2 | |||
fad25ab75b | |||
56606a2da3 | |||
ceddcd59ac | |||
54a284568b | |||
3b4135826d | |||
38e8b2b6a9 | |||
f0e31b89b9 | |||
d632c0def4 | |||
2098707b02 | |||
6c7745d928 | |||
4e625e8cd2 | |||
852bb420e7 | |||
d4060c3eb6 | |||
e074d8d543 | |||
5aaa8238f7 | |||
ef83d3ea87 | |||
702294d5b8 | |||
f146bc1b74 | |||
d61e56ab36 | |||
9565424508 | |||
17bba907ac | |||
4ca9b4c3a0 | |||
efa6530dbd | |||
b41b4589c2 | |||
fdda0d66d3 | |||
30e5b7f6dc | |||
6e5851647a | |||
92077c15d6 | |||
fae335e159 | |||
d63eaef179 | |||
ecd32fa2e8 | |||
1bf4f695d4 | |||
37186b19bc | |||
d8038e15f8 | |||
82c2ca262e | |||
d3cc60b036 | |||
062d6b0bb3 | |||
6440ed585f | |||
b9c595c654 | |||
d823d5f327 | |||
1e8aabad34 | |||
789ab6bed2 | |||
916f5538f9 | |||
f2954b5b64 | |||
107629b02d | |||
0837091e8d | |||
d634bb2d2b | |||
0eb14b30e0 | |||
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 | |||
2654f5aae1 | |||
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 | |||
dcc38b3c15 | |||
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 | |||
5b695f393e | |||
ff32e8c7e5 | |||
a37e9b1f19 | |||
5740b042b5 | |||
69b75410de | |||
007439bef6 | |||
c4351c7910 | |||
7400dc2700 | |||
981b0cd1e1 | |||
225c6b4673 | |||
1fa52ecdbf | |||
bcdd00359c | |||
466e67a582 | |||
1a7ee5384d | |||
7e5a661064 | |||
f9476fbfe8 | |||
c788c26397 | |||
8ce0be4956 | |||
3ab130fa9e | |||
bf055bb1be | |||
583fc12c3d | |||
a3c2f735d7 | |||
512536a54a | |||
21854ccdb2 | |||
841b2bf352 | |||
3a976a79bf | |||
e923487ff6 | |||
1f14c9a1f1 | |||
6060e5c6df | |||
4c1eed2573 | |||
0d4ff2fcc6 | |||
89d385c4d9 | |||
2e068dc330 | |||
da846fcf1b | |||
4c382b1365 | |||
e08991e02a | |||
93dbe07ff6 | |||
efa1bab94c | |||
6aece33b3f | |||
b551724ceb | |||
aa062531aa | |||
687bc12ccf | |||
883b4c8be5 | |||
4e9332cb0b | |||
22d79a5c5e | |||
4c5f9f3416 | |||
b765729081 | |||
be47155f75 | |||
158657bc5c | |||
9b514530a6 | |||
d36308c26d | |||
8fae8279fa | |||
17a47098d2 | |||
25215285c4 | |||
9b430e11d6 | |||
bd6181ad58 | |||
0523156775 | |||
002c9dfb80 | |||
73ae31ce0a | |||
2278a04a09 | |||
3b0f484776 | |||
9acf28a390 | |||
6149073651 | |||
c652e41d91 | |||
b8f506fb37 | |||
6e5be9b24c | |||
052acd61c8 | |||
b2ce982d43 | |||
a43c44f31f | |||
f88cea6ded | |||
4011770f2d | |||
31f0fc2235 | |||
2ec8a1929f | |||
022229c47e | |||
d93a25459c | |||
d641a0e141 | |||
54ec81fe86 | |||
42ab176195 | |||
988500b615 | |||
1c8ca2e40b | |||
4526d4fe62 | |||
8f8bc4cb48 | |||
f93d8166ef | |||
83a25d7380 | |||
486aa29063 | |||
fd8fb0c492 | |||
66c76bcfcf | |||
d16fb221cd | |||
60babf8ba7 | |||
b9ad6dfd81 | |||
2f4fc56183 | |||
196c25c777 | |||
49c82ce553 | |||
b5d542cd40 | |||
a3f89eabb7 | |||
af42fa0a7d | |||
9a77b613f3 | |||
65a56909a3 | |||
2f39bf9a9f | |||
cbf9038038 | |||
619ec2f3aa | |||
6785c2534a | |||
34c98df78a | |||
c2d666bd4f | |||
50a8a71f0b | |||
54e2e86c57 | |||
60151a295c | |||
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 |
143
Android.mk
@ -1,27 +1,63 @@
|
||||
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 \
|
||||
mounts.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.3
|
||||
LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
|
||||
RECOVERY_API_VERSION := 2
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
|
||||
BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_SDCARD_DEVICE_PRIMARY BOARD_SDCARD_DEVICE_SECONDARY BOARD_SDEXT_DEVICE BOARD_SDEXT_FILESYSTEM BOARD_DATA_DEVICE BOARD_DATA_FILESYSTEM BOARD_DATADATA_DEVICE BOARD_DATADATA_FILESYSTEM BOARD_CACHE_DEVICE BOARD_CACHE_FILESYSTEM BOARD_SYSTEM_DEVICE BOARD_SYSTEM_FILESYSTEM BOARD_HAS_DATADATA BOARD_DATA_FILESYSTEM_OPTIONS BOARD_DATADATA_FILESYSTEM_OPTIONS BOARD_CACHE_FILESYSTEM_OPTIONS BOARD_SYSTEM_FILESYSTEM_OPTIONS BOARD_HAS_MTD_CACHE BOARD_USES_BMLUTILS BOARD_USES_MMCUTILS BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY
|
||||
|
||||
$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
|
||||
$(if $($(board_define)), \
|
||||
$(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
|
||||
) \
|
||||
)
|
||||
|
||||
# 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 +65,93 @@ 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 += libstdc++ libc
|
||||
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
|
||||
|
||||
# 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))
|
||||
LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libamend
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
|
||||
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
|
||||
LOCAL_STATIC_LIBRARIES += libstdc++ libc
|
||||
|
||||
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
|
||||
|
||||
include $(commands_recovery_local_path)/minui/Android.mk
|
||||
# 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 $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := verifier_test.c verifier.c
|
||||
|
||||
LOCAL_MODULE := verifier_test
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
||||
include $(commands_recovery_local_path)/amend/Android.mk
|
||||
include $(commands_recovery_local_path)/bmlutils/Android.mk
|
||||
include $(commands_recovery_local_path)/flashutils/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)/mmcutils/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)/applypatch/Android.mk
|
||||
include $(commands_recovery_local_path)/utilities/Android.mk
|
||||
commands_recovery_local_path :=
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
||||
|
49
CleanSpec.mk
Normal file
@ -0,0 +1,49 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
# If you don't need to do a full clean build but would like to touch
|
||||
# a file or delete some intermediate files, add a clean step to the end
|
||||
# of the list. These steps will only be run once, if they haven't been
|
||||
# run before.
|
||||
#
|
||||
# E.g.:
|
||||
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
|
||||
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
|
||||
#
|
||||
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
|
||||
# files that are missing or have been moved.
|
||||
#
|
||||
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
|
||||
# Use $(OUT_DIR) to refer to the "out" directory.
|
||||
#
|
||||
# If you need to re-do something that's already mentioned, just copy
|
||||
# the command and add it to the bottom of the list. E.g., if a change
|
||||
# that you made last week required touching a file and a change you
|
||||
# made today requires touching the same file, just copy the old
|
||||
# touch step and add it to the end of the list.
|
||||
#
|
||||
# ************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||
# ************************************************
|
||||
|
||||
# For example:
|
||||
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
|
||||
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
|
||||
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
|
||||
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
|
||||
|
||||
# ************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||
# ************************************************
|
@ -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;
|
||||
|
||||
|
61
applypatch/Android.mk
Normal file
@ -0,0 +1,61 @@
|
||||
# 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.
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c
|
||||
LOCAL_MODULE := libapplypatch
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
|
||||
LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := main.c
|
||||
LOCAL_MODULE := applypatch
|
||||
LOCAL_C_INCLUDES += bootable/recovery
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
|
||||
LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := main.c
|
||||
LOCAL_MODULE := applypatch_static
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_C_INCLUDES += bootable/recovery
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
|
||||
LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c
|
||||
LOCAL_MODULE := imgdiff
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_C_INCLUDES += external/zlib external/bzip2
|
||||
LOCAL_STATIC_LIBRARIES += libz libbz
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
endif # !TARGET_SIMULATOR
|
826
applypatch/applypatch.c
Normal file
@ -0,0 +1,826 @@
|
||||
/*
|
||||
* 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 <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mincrypt/sha.h"
|
||||
#include "applypatch.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "edify/expr.h"
|
||||
|
||||
int SaveFileContents(const char* filename, FileContents file);
|
||||
int LoadMTDContents(const char* filename, FileContents* file);
|
||||
int ParseSha1(const char* str, uint8_t* digest);
|
||||
ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
|
||||
|
||||
static int mtd_partitions_scanned = 0;
|
||||
|
||||
// Read a file into memory; store it and its associated metadata in
|
||||
// *file. Return 0 on success.
|
||||
int LoadFileContents(const char* filename, FileContents* file) {
|
||||
file->data = NULL;
|
||||
|
||||
// A special 'filename' beginning with "MTD:" means to load the
|
||||
// contents of an MTD partition.
|
||||
if (strncmp(filename, "MTD:", 4) == 0) {
|
||||
return LoadMTDContents(filename, file);
|
||||
}
|
||||
|
||||
if (stat(filename, &file->st) != 0) {
|
||||
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
file->size = file->st.st_size;
|
||||
file->data = malloc(file->size);
|
||||
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
printf("failed to open \"%s\": %s\n", filename, strerror(errno));
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = fread(file->data, 1, file->size, f);
|
||||
if (bytes_read != file->size) {
|
||||
printf("short read of \"%s\" (%ld bytes of %ld)\n",
|
||||
filename, (long)bytes_read, (long)file->size);
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
SHA(file->data, file->size, file->sha1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t* size_array;
|
||||
// comparison function for qsort()ing an int array of indexes into
|
||||
// size_array[].
|
||||
static int compare_size_indices(const void* a, const void* b) {
|
||||
int aa = *(int*)a;
|
||||
int bb = *(int*)b;
|
||||
if (size_array[aa] < size_array[bb]) {
|
||||
return -1;
|
||||
} else if (size_array[aa] > size_array[bb]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeFileContents(FileContents* file) {
|
||||
if (file) free(file->data);
|
||||
free(file);
|
||||
}
|
||||
|
||||
// Load the contents of an MTD partition into the provided
|
||||
// FileContents. filename should be a string of the form
|
||||
// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:...".
|
||||
// The smallest size_n bytes for which that prefix of the mtd contents
|
||||
// has the corresponding sha1 hash will be loaded. It is acceptable
|
||||
// for a size value to be repeated with different sha1s. Will return
|
||||
// 0 on success.
|
||||
//
|
||||
// This complexity is needed because if an OTA installation is
|
||||
// interrupted, the partition might contain either the source or the
|
||||
// target data, which might be of different lengths. We need to know
|
||||
// the length in order to read from MTD (there is no "end-of-file"
|
||||
// marker), so the caller must specify the possible lengths and the
|
||||
// hash of the data, and we'll do the load expecting to find one of
|
||||
// those hashes.
|
||||
int LoadMTDContents(const char* filename, FileContents* file) {
|
||||
#ifdef BOARD_USES_MTDUTILS
|
||||
char* copy = strdup(filename);
|
||||
const char* magic = strtok(copy, ":");
|
||||
if (strcmp(magic, "MTD") != 0) {
|
||||
printf("LoadMTDContents called with bad filename (%s)\n",
|
||||
filename);
|
||||
return -1;
|
||||
}
|
||||
const char* partition = strtok(NULL, ":");
|
||||
|
||||
int i;
|
||||
int colons = 0;
|
||||
for (i = 0; filename[i] != '\0'; ++i) {
|
||||
if (filename[i] == ':') {
|
||||
++colons;
|
||||
}
|
||||
}
|
||||
if (colons < 3 || colons%2 == 0) {
|
||||
printf("LoadMTDContents called with bad filename (%s)\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
int pairs = (colons-1)/2; // # of (size,sha1) pairs in filename
|
||||
int* index = malloc(pairs * sizeof(int));
|
||||
size_t* size = malloc(pairs * sizeof(size_t));
|
||||
char** sha1sum = malloc(pairs * sizeof(char*));
|
||||
|
||||
for (i = 0; i < pairs; ++i) {
|
||||
const char* size_str = strtok(NULL, ":");
|
||||
size[i] = strtol(size_str, NULL, 10);
|
||||
if (size[i] == 0) {
|
||||
printf("LoadMTDContents called with bad size (%s)\n", filename);
|
||||
return -1;
|
||||
}
|
||||
sha1sum[i] = strtok(NULL, ":");
|
||||
index[i] = i;
|
||||
}
|
||||
|
||||
// sort the index[] array so it indexes the pairs in order of
|
||||
// increasing size.
|
||||
size_array = size;
|
||||
qsort(index, pairs, sizeof(int), compare_size_indices);
|
||||
|
||||
if (!mtd_partitions_scanned) {
|
||||
mtd_scan_partitions();
|
||||
mtd_partitions_scanned = 1;
|
||||
}
|
||||
|
||||
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
||||
if (mtd == NULL) {
|
||||
printf("mtd partition \"%s\" not found (loading %s)\n",
|
||||
partition, filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MtdReadContext* ctx = mtd_read_partition(mtd);
|
||||
if (ctx == NULL) {
|
||||
printf("failed to initialize read of mtd partition \"%s\"\n",
|
||||
partition);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SHA_CTX sha_ctx;
|
||||
SHA_init(&sha_ctx);
|
||||
uint8_t parsed_sha[SHA_DIGEST_SIZE];
|
||||
|
||||
// allocate enough memory to hold the largest size.
|
||||
file->data = malloc(size[index[pairs-1]]);
|
||||
char* p = (char*)file->data;
|
||||
file->size = 0; // # bytes read so far
|
||||
|
||||
for (i = 0; i < pairs; ++i) {
|
||||
// Read enough additional bytes to get us up to the next size
|
||||
// (again, we're trying the possibilities in order of increasing
|
||||
// size).
|
||||
size_t next = size[index[i]] - file->size;
|
||||
size_t read = 0;
|
||||
if (next > 0) {
|
||||
read = mtd_read_data(ctx, p, next);
|
||||
if (next != read) {
|
||||
printf("short read (%d bytes of %d) for partition \"%s\"\n",
|
||||
read, next, partition);
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
SHA_update(&sha_ctx, p, read);
|
||||
file->size += read;
|
||||
}
|
||||
|
||||
// Duplicate the SHA context and finalize the duplicate so we can
|
||||
// check it against this pair's expected hash.
|
||||
SHA_CTX temp_ctx;
|
||||
memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
|
||||
const uint8_t* sha_so_far = SHA_final(&temp_ctx);
|
||||
|
||||
if (ParseSha1(sha1sum[index[i]], parsed_sha) != 0) {
|
||||
printf("failed to parse sha1 %s in %s\n",
|
||||
sha1sum[index[i]], filename);
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
|
||||
// we have a match. stop reading the partition; we'll return
|
||||
// the data we've read so far.
|
||||
printf("mtd read matched size %d sha %s\n",
|
||||
size[index[i]], sha1sum[index[i]]);
|
||||
break;
|
||||
}
|
||||
|
||||
p += read;
|
||||
}
|
||||
|
||||
mtd_read_close(ctx);
|
||||
|
||||
if (i == pairs) {
|
||||
// Ran off the end of the list of (size,sha1) pairs without
|
||||
// finding a match.
|
||||
printf("contents of MTD partition \"%s\" didn't match %s\n",
|
||||
partition, filename);
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint8_t* sha_final = SHA_final(&sha_ctx);
|
||||
for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
|
||||
file->sha1[i] = sha_final[i];
|
||||
}
|
||||
|
||||
// Fake some stat() info.
|
||||
file->st.st_mode = 0644;
|
||||
file->st.st_uid = 0;
|
||||
file->st.st_gid = 0;
|
||||
|
||||
free(copy);
|
||||
free(index);
|
||||
free(size);
|
||||
free(sha1sum);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
printf("mtd utils not supported.\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Save the contents of the given FileContents object under the given
|
||||
// filename. Return 0 on success.
|
||||
int SaveFileContents(const char* filename, FileContents file) {
|
||||
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if (fd < 0) {
|
||||
printf("failed to open \"%s\" for write: %s\n",
|
||||
filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_written = FileSink(file.data, file.size, &fd);
|
||||
if (bytes_written != file.size) {
|
||||
printf("short write of \"%s\" (%ld bytes of %ld) (%s)\n",
|
||||
filename, (long)bytes_written, (long)file.size,
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
|
||||
if (chmod(filename, file.st.st_mode) != 0) {
|
||||
printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (chown(filename, file.st.st_uid, file.st.st_gid) != 0) {
|
||||
printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write a memory buffer to target_mtd partition, a string of the form
|
||||
// "MTD:<partition>[:...]". Return 0 on success.
|
||||
int WriteToMTDPartition(unsigned char* data, size_t len,
|
||||
const char* target_mtd) {
|
||||
#ifdef BOARD_USES_MTDUTILS
|
||||
char* partition = strchr(target_mtd, ':');
|
||||
if (partition == NULL) {
|
||||
printf("bad MTD target name \"%s\"\n", target_mtd);
|
||||
return -1;
|
||||
}
|
||||
++partition;
|
||||
// Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
|
||||
// We want just the partition name "boot".
|
||||
partition = strdup(partition);
|
||||
char* end = strchr(partition, ':');
|
||||
if (end != NULL)
|
||||
*end = '\0';
|
||||
|
||||
if (!mtd_partitions_scanned) {
|
||||
mtd_scan_partitions();
|
||||
mtd_partitions_scanned = 1;
|
||||
}
|
||||
|
||||
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
||||
if (mtd == NULL) {
|
||||
printf("mtd partition \"%s\" not found for writing\n", partition);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
||||
if (ctx == NULL) {
|
||||
printf("failed to init mtd partition \"%s\" for writing\n",
|
||||
partition);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t written = mtd_write_data(ctx, (char*)data, len);
|
||||
if (written != len) {
|
||||
printf("only wrote %d of %d bytes to MTD %s\n",
|
||||
written, len, partition);
|
||||
mtd_write_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mtd_erase_blocks(ctx, -1) < 0) {
|
||||
printf("error finishing mtd write of %s\n", partition);
|
||||
mtd_write_close(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mtd_write_close(ctx)) {
|
||||
printf("error closing mtd write of %s\n", partition);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(partition);
|
||||
return 0;
|
||||
#else
|
||||
printf("mtd utils not supported.\n");
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Take a string 'str' of 40 hex digits and parse it into the 20
|
||||
// byte array 'digest'. 'str' may contain only the digest or be of
|
||||
// the form "<digest>:<anything>". Return 0 on success, -1 on any
|
||||
// error.
|
||||
int ParseSha1(const char* str, uint8_t* digest) {
|
||||
int i;
|
||||
const char* ps = str;
|
||||
uint8_t* pd = digest;
|
||||
for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
|
||||
int digit;
|
||||
if (*ps >= '0' && *ps <= '9') {
|
||||
digit = *ps - '0';
|
||||
} else if (*ps >= 'a' && *ps <= 'f') {
|
||||
digit = *ps - 'a' + 10;
|
||||
} else if (*ps >= 'A' && *ps <= 'F') {
|
||||
digit = *ps - 'A' + 10;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (i % 2 == 0) {
|
||||
*pd = digit << 4;
|
||||
} else {
|
||||
*pd |= digit;
|
||||
++pd;
|
||||
}
|
||||
}
|
||||
if (*ps != '\0') return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Search an array of sha1 strings for one matching the given sha1.
|
||||
// Return the index of the match on success, or -1 if no match is
|
||||
// found.
|
||||
int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
|
||||
int num_patches) {
|
||||
int i;
|
||||
uint8_t patch_sha1[SHA_DIGEST_SIZE];
|
||||
for (i = 0; i < num_patches; ++i) {
|
||||
if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
|
||||
memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Returns 0 if the contents of the file (argv[2]) or the cached file
|
||||
// match any of the sha1's on the command line (argv[3:]). Returns
|
||||
// nonzero otherwise.
|
||||
int applypatch_check(const char* filename,
|
||||
int num_patches, char** const patch_sha1_str) {
|
||||
FileContents file;
|
||||
file.data = NULL;
|
||||
|
||||
// It's okay to specify no sha1s; the check will pass if the
|
||||
// LoadFileContents is successful. (Useful for reading MTD
|
||||
// partitions, where the filename encodes the sha1s; no need to
|
||||
// check them twice.)
|
||||
if (LoadFileContents(filename, &file) != 0 ||
|
||||
(num_patches > 0 &&
|
||||
FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
|
||||
printf("file \"%s\" doesn't have any of expected "
|
||||
"sha1 sums; checking cache\n", filename);
|
||||
|
||||
free(file.data);
|
||||
|
||||
// If the source file is missing or corrupted, it might be because
|
||||
// we were killed in the middle of patching it. A copy of it
|
||||
// should have been made in CACHE_TEMP_SOURCE. If that file
|
||||
// exists and matches the sha1 we're looking for, the check still
|
||||
// passes.
|
||||
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
|
||||
printf("failed to load cache file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
|
||||
printf("cache bits don't match any sha1 for \"%s\"\n", filename);
|
||||
free(file.data);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(file.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ShowLicenses() {
|
||||
ShowBSDiffLicense();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t FileSink(unsigned char* data, ssize_t len, void* token) {
|
||||
int fd = *(int *)token;
|
||||
ssize_t done = 0;
|
||||
ssize_t wrote;
|
||||
while (done < (ssize_t) len) {
|
||||
wrote = write(fd, data+done, len-done);
|
||||
if (wrote <= 0) {
|
||||
printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
|
||||
return done;
|
||||
}
|
||||
done += wrote;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned char* buffer;
|
||||
ssize_t size;
|
||||
ssize_t pos;
|
||||
} MemorySinkInfo;
|
||||
|
||||
ssize_t MemorySink(unsigned char* data, ssize_t len, void* token) {
|
||||
MemorySinkInfo* msi = (MemorySinkInfo*)token;
|
||||
if (msi->size - msi->pos < len) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(msi->buffer + msi->pos, data, len);
|
||||
msi->pos += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
// Return the amount of free space (in bytes) on the filesystem
|
||||
// containing filename. filename must exist. Return -1 on error.
|
||||
size_t FreeSpaceForFile(const char* filename) {
|
||||
struct statfs sf;
|
||||
if (statfs(filename, &sf) != 0) {
|
||||
printf("failed to statfs %s: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return sf.f_bsize * sf.f_bfree;
|
||||
}
|
||||
|
||||
int CacheSizeCheck(size_t bytes) {
|
||||
if (MakeFreeSpaceOnCache(bytes) < 0) {
|
||||
printf("unable to make %ld bytes available on /cache\n", (long)bytes);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This function applies binary patches to files in a way that is safe
|
||||
// (the original file is not touched until we have the desired
|
||||
// replacement for it) and idempotent (it's okay to run this program
|
||||
// multiple times).
|
||||
//
|
||||
// - if the sha1 hash of <target_filename> is <target_sha1_string>,
|
||||
// does nothing and exits successfully.
|
||||
//
|
||||
// - otherwise, if the sha1 hash of <source_filename> is one of the
|
||||
// entries in <patch_sha1_str>, the corresponding patch from
|
||||
// <patch_data> (which must be a VAL_BLOB) is applied to produce a
|
||||
// new file (the type of patch is automatically detected from the
|
||||
// blob daat). If that new file has sha1 hash <target_sha1_str>,
|
||||
// moves it to replace <target_filename>, and exits successfully.
|
||||
// Note that if <source_filename> and <target_filename> are not the
|
||||
// same, <source_filename> is NOT deleted on success.
|
||||
// <target_filename> may be the string "-" to mean "the same as
|
||||
// source_filename".
|
||||
//
|
||||
// - otherwise, or if any error is encountered, exits with non-zero
|
||||
// status.
|
||||
//
|
||||
// <source_filename> may refer to an MTD partition to read the source
|
||||
// data. See the comments for the LoadMTDContents() function above
|
||||
// for the format of such a filename.
|
||||
|
||||
int applypatch(const char* source_filename,
|
||||
const char* target_filename,
|
||||
const char* target_sha1_str,
|
||||
size_t target_size,
|
||||
int num_patches,
|
||||
char** const patch_sha1_str,
|
||||
Value** patch_data) {
|
||||
printf("\napplying patch to %s\n", source_filename);
|
||||
|
||||
if (target_filename[0] == '-' &&
|
||||
target_filename[1] == '\0') {
|
||||
target_filename = source_filename;
|
||||
}
|
||||
|
||||
uint8_t target_sha1[SHA_DIGEST_SIZE];
|
||||
if (ParseSha1(target_sha1_str, target_sha1) != 0) {
|
||||
printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileContents copy_file;
|
||||
FileContents source_file;
|
||||
const Value* source_patch_value = NULL;
|
||||
const Value* copy_patch_value = NULL;
|
||||
int made_copy = 0;
|
||||
|
||||
// We try to load the target file into the source_file object.
|
||||
if (LoadFileContents(target_filename, &source_file) == 0) {
|
||||
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
|
||||
// The early-exit case: the patch was already applied, this file
|
||||
// has the desired hash, nothing for us to do.
|
||||
printf("\"%s\" is already target; no patch needed\n",
|
||||
target_filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (source_file.data == NULL ||
|
||||
(target_filename != source_filename &&
|
||||
strcmp(target_filename, source_filename) != 0)) {
|
||||
// Need to load the source file: either we failed to load the
|
||||
// target file, or we did but it's different from the source file.
|
||||
free(source_file.data);
|
||||
LoadFileContents(source_filename, &source_file);
|
||||
}
|
||||
|
||||
if (source_file.data != NULL) {
|
||||
int to_use = FindMatchingPatch(source_file.sha1,
|
||||
patch_sha1_str, num_patches);
|
||||
if (to_use >= 0) {
|
||||
source_patch_value = patch_data[to_use];
|
||||
}
|
||||
}
|
||||
|
||||
if (source_patch_value == NULL) {
|
||||
free(source_file.data);
|
||||
printf("source file is bad; trying copy\n");
|
||||
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) {
|
||||
// fail.
|
||||
printf("failed to read copy file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int to_use = FindMatchingPatch(copy_file.sha1,
|
||||
patch_sha1_str, num_patches);
|
||||
if (to_use > 0) {
|
||||
copy_patch_value = patch_data[to_use];
|
||||
}
|
||||
|
||||
if (copy_patch_value == NULL) {
|
||||
// fail.
|
||||
printf("copy file doesn't match source SHA-1s either\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int retry = 1;
|
||||
SHA_CTX ctx;
|
||||
int output;
|
||||
MemorySinkInfo msi;
|
||||
FileContents* source_to_use;
|
||||
char* outname;
|
||||
|
||||
// assume that target_filename (eg "/system/app/Foo.apk") is located
|
||||
// on the same filesystem as its top-level directory ("/system").
|
||||
// We need something that exists for calling statfs().
|
||||
char target_fs[strlen(target_filename)+1];
|
||||
char* slash = strchr(target_filename+1, '/');
|
||||
if (slash != NULL) {
|
||||
int count = slash - target_filename;
|
||||
strncpy(target_fs, target_filename, count);
|
||||
target_fs[count] = '\0';
|
||||
} else {
|
||||
strcpy(target_fs, target_filename);
|
||||
}
|
||||
|
||||
do {
|
||||
// Is there enough room in the target filesystem to hold the patched
|
||||
// file?
|
||||
|
||||
if (strncmp(target_filename, "MTD:", 4) == 0) {
|
||||
// If the target is an MTD partition, we're actually going to
|
||||
// write the output to /tmp and then copy it to the partition.
|
||||
// statfs() always returns 0 blocks free for /tmp, so instead
|
||||
// we'll just assume that /tmp has enough space to hold the file.
|
||||
|
||||
// We still write the original source to cache, in case the MTD
|
||||
// write is interrupted.
|
||||
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
|
||||
printf("not enough free space on /cache\n");
|
||||
return 1;
|
||||
}
|
||||
if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
|
||||
printf("failed to back up source file\n");
|
||||
return 1;
|
||||
}
|
||||
made_copy = 1;
|
||||
retry = 0;
|
||||
} else {
|
||||
int enough_space = 0;
|
||||
if (retry > 0) {
|
||||
size_t free_space = FreeSpaceForFile(target_fs);
|
||||
int enough_space =
|
||||
(free_space > (target_size * 3 / 2)); // 50% margin of error
|
||||
printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n",
|
||||
(long)target_size, (long)free_space, retry, enough_space);
|
||||
}
|
||||
|
||||
if (!enough_space) {
|
||||
retry = 0;
|
||||
}
|
||||
|
||||
if (!enough_space && source_patch_value != NULL) {
|
||||
// Using the original source, but not enough free space. First
|
||||
// copy the source file to cache, then delete it from the original
|
||||
// location.
|
||||
|
||||
if (strncmp(source_filename, "MTD:", 4) == 0) {
|
||||
// It's impossible to free space on the target filesystem by
|
||||
// deleting the source if the source is an MTD partition. If
|
||||
// we're ever in a state where we need to do this, fail.
|
||||
printf("not enough free space for target but source is MTD\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
|
||||
printf("not enough free space on /cache\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
|
||||
printf("failed to back up source file\n");
|
||||
return 1;
|
||||
}
|
||||
made_copy = 1;
|
||||
unlink(source_filename);
|
||||
|
||||
size_t free_space = FreeSpaceForFile(target_fs);
|
||||
printf("(now %ld bytes free for target)\n", (long)free_space);
|
||||
}
|
||||
}
|
||||
|
||||
const Value* patch;
|
||||
if (source_patch_value != NULL) {
|
||||
source_to_use = &source_file;
|
||||
patch = source_patch_value;
|
||||
} else {
|
||||
source_to_use = ©_file;
|
||||
patch = copy_patch_value;
|
||||
}
|
||||
|
||||
if (patch->type != VAL_BLOB) {
|
||||
printf("patch is not a blob\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SinkFn sink = NULL;
|
||||
void* token = NULL;
|
||||
output = -1;
|
||||
outname = NULL;
|
||||
if (strncmp(target_filename, "MTD:", 4) == 0) {
|
||||
// We store the decoded output in memory.
|
||||
msi.buffer = malloc(target_size);
|
||||
if (msi.buffer == NULL) {
|
||||
printf("failed to alloc %ld bytes for output\n",
|
||||
(long)target_size);
|
||||
return 1;
|
||||
}
|
||||
msi.pos = 0;
|
||||
msi.size = target_size;
|
||||
sink = MemorySink;
|
||||
token = &msi;
|
||||
} else {
|
||||
// We write the decoded output to "<tgt-file>.patch".
|
||||
outname = (char*)malloc(strlen(target_filename) + 10);
|
||||
strcpy(outname, target_filename);
|
||||
strcat(outname, ".patch");
|
||||
|
||||
output = open(outname, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if (output < 0) {
|
||||
printf("failed to open output file %s: %s\n",
|
||||
outname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
sink = FileSink;
|
||||
token = &output;
|
||||
}
|
||||
|
||||
char* header = patch->data;
|
||||
ssize_t header_bytes_read = patch->size;
|
||||
|
||||
SHA_init(&ctx);
|
||||
|
||||
int result;
|
||||
|
||||
if (header_bytes_read >= 8 &&
|
||||
memcmp(header, "BSDIFF40", 8) == 0) {
|
||||
result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
|
||||
patch, 0, sink, token, &ctx);
|
||||
} else if (header_bytes_read >= 8 &&
|
||||
memcmp(header, "IMGDIFF2", 8) == 0) {
|
||||
result = ApplyImagePatch(source_to_use->data, source_to_use->size,
|
||||
patch, sink, token, &ctx);
|
||||
} else {
|
||||
printf("Unknown patch file format\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (output >= 0) {
|
||||
fsync(output);
|
||||
close(output);
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
if (retry == 0) {
|
||||
printf("applying patch failed\n");
|
||||
return result != 0;
|
||||
} else {
|
||||
printf("applying patch failed; retrying\n");
|
||||
}
|
||||
if (outname != NULL) {
|
||||
unlink(outname);
|
||||
}
|
||||
} else {
|
||||
// succeeded; no need to retry
|
||||
break;
|
||||
}
|
||||
} while (retry-- > 0);
|
||||
|
||||
const uint8_t* current_target_sha1 = SHA_final(&ctx);
|
||||
if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
|
||||
printf("patch did not produce expected sha1\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (output < 0) {
|
||||
// Copy the temp file to the MTD partition.
|
||||
if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
|
||||
printf("write of patched data to %s failed\n", target_filename);
|
||||
return 1;
|
||||
}
|
||||
free(msi.buffer);
|
||||
} else {
|
||||
// Give the .patch file the same owner, group, and mode of the
|
||||
// original source file.
|
||||
if (chmod(outname, source_to_use->st.st_mode) != 0) {
|
||||
printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (chown(outname, source_to_use->st.st_uid,
|
||||
source_to_use->st.st_gid) != 0) {
|
||||
printf("chown of \"%s\" failed: %s\n", outname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Finally, rename the .patch file to replace the target file.
|
||||
if (rename(outname, target_filename) != 0) {
|
||||
printf("rename of .patch to \"%s\" failed: %s\n",
|
||||
target_filename, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If this run of applypatch created the copy, and we're here, we
|
||||
// can delete it.
|
||||
if (made_copy) unlink(CACHE_TEMP_SOURCE);
|
||||
|
||||
// Success!
|
||||
return 0;
|
||||
}
|
84
applypatch/applypatch.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _APPLYPATCH_H
|
||||
#define _APPLYPATCH_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "mincrypt/sha.h"
|
||||
#include "edify/expr.h"
|
||||
|
||||
typedef struct _Patch {
|
||||
uint8_t sha1[SHA_DIGEST_SIZE];
|
||||
const char* patch_filename;
|
||||
} Patch;
|
||||
|
||||
typedef struct _FileContents {
|
||||
uint8_t sha1[SHA_DIGEST_SIZE];
|
||||
unsigned char* data;
|
||||
ssize_t size;
|
||||
struct stat st;
|
||||
} FileContents;
|
||||
|
||||
// When there isn't enough room on the target filesystem to hold the
|
||||
// patched version of the file, we copy the original here and delete
|
||||
// it to free up space. If the expected source file doesn't exist, or
|
||||
// is corrupted, we look to see if this file contains the bits we want
|
||||
// and use it as the source instead.
|
||||
#define CACHE_TEMP_SOURCE "/cache/saved.file"
|
||||
|
||||
typedef ssize_t (*SinkFn)(unsigned char*, ssize_t, void*);
|
||||
|
||||
// applypatch.c
|
||||
int ShowLicenses();
|
||||
size_t FreeSpaceForFile(const char* filename);
|
||||
int CacheSizeCheck(size_t bytes);
|
||||
int ParseSha1(const char* str, uint8_t* digest);
|
||||
|
||||
int applypatch(const char* source_filename,
|
||||
const char* target_filename,
|
||||
const char* target_sha1_str,
|
||||
size_t target_size,
|
||||
int num_patches,
|
||||
char** const patch_sha1_str,
|
||||
Value** patch_data);
|
||||
int applypatch_check(const char* filename,
|
||||
int num_patches,
|
||||
char** const patch_sha1_str);
|
||||
|
||||
// Read a file into memory; store it and its associated metadata in
|
||||
// *file. Return 0 on success.
|
||||
int LoadFileContents(const char* filename, FileContents* file);
|
||||
void FreeFileContents(FileContents* file);
|
||||
|
||||
// bsdiff.c
|
||||
void ShowBSDiffLicense();
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx);
|
||||
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
unsigned char** new_data, ssize_t* new_size);
|
||||
|
||||
// imgpatch.c
|
||||
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx);
|
||||
|
||||
// freecache.c
|
||||
int MakeFreeSpaceOnCache(size_t bytes_needed);
|
||||
|
||||
#endif
|
350
applypatch/applypatch.sh
Executable file
@ -0,0 +1,350 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# A test suite for applypatch. Run in a client where you have done
|
||||
# envsetup, choosecombo, etc.
|
||||
#
|
||||
# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your
|
||||
# system partition.
|
||||
#
|
||||
#
|
||||
# TODO: find some way to get this run regularly along with the rest of
|
||||
# the tests.
|
||||
|
||||
EMULATOR_PORT=5580
|
||||
DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata
|
||||
|
||||
# This must be the filename that applypatch uses for its copies.
|
||||
CACHE_TEMP_SOURCE=/cache/saved.file
|
||||
|
||||
# Put all binaries and files here. We use /cache because it's a
|
||||
# temporary filesystem in the emulator; it's created fresh each time
|
||||
# the emulator starts.
|
||||
WORK_DIR=/system
|
||||
|
||||
# partition that WORK_DIR is located on, without the leading slash
|
||||
WORK_FS=system
|
||||
|
||||
# set to 0 to use a device instead
|
||||
USE_EMULATOR=1
|
||||
|
||||
# ------------------------
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
|
||||
if [ "$USE_EMULATOR" == 1 ]; then
|
||||
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
|
||||
pid_emulator=$!
|
||||
ADB="adb -s emulator-$EMULATOR_PORT "
|
||||
else
|
||||
ADB="adb -d "
|
||||
fi
|
||||
|
||||
echo "waiting to connect to device"
|
||||
$ADB wait-for-device
|
||||
echo "device is available"
|
||||
$ADB remount
|
||||
# free up enough space on the system partition for the test to run.
|
||||
$ADB shell rm -r /system/media
|
||||
|
||||
# run a command on the device; exit with the exit status of the device
|
||||
# command.
|
||||
run_command() {
|
||||
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
|
||||
}
|
||||
|
||||
testname() {
|
||||
echo
|
||||
echo "$1"...
|
||||
testname="$1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo
|
||||
echo FAIL: $testname
|
||||
echo
|
||||
[ "$open_pid" == "" ] || kill $open_pid
|
||||
[ "$pid_emulator" == "" ] || kill $pid_emulator
|
||||
exit 1
|
||||
}
|
||||
|
||||
sha1() {
|
||||
sha1sum $1 | awk '{print $1}'
|
||||
}
|
||||
|
||||
free_space() {
|
||||
run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# not necessary if we're about to kill the emulator, but nice for
|
||||
# running on real devices or already-running emulators.
|
||||
testname "removing test files"
|
||||
run_command rm $WORK_DIR/bloat.dat
|
||||
run_command rm $WORK_DIR/old.file
|
||||
run_command rm $WORK_DIR/foo
|
||||
run_command rm $WORK_DIR/patch.bsdiff
|
||||
run_command rm $WORK_DIR/applypatch
|
||||
run_command rm $CACHE_TEMP_SOURCE
|
||||
run_command rm /cache/bloat*.dat
|
||||
|
||||
[ "$pid_emulator" == "" ] || kill $pid_emulator
|
||||
|
||||
if [ $# == 0 ]; then
|
||||
rm -rf $tmpdir
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup leave_tmp
|
||||
|
||||
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
|
||||
|
||||
BAD1_SHA1=$(printf "%040x" $RANDOM)
|
||||
BAD2_SHA1=$(printf "%040x" $RANDOM)
|
||||
OLD_SHA1=$(sha1 $DATA_DIR/old.file)
|
||||
NEW_SHA1=$(sha1 $DATA_DIR/new.file)
|
||||
NEW_SIZE=$(stat -c %s $DATA_DIR/new.file)
|
||||
|
||||
# --------------- basic execution ----------------------
|
||||
|
||||
testname "usage message"
|
||||
run_command $WORK_DIR/applypatch && fail
|
||||
|
||||
testname "display license"
|
||||
run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail
|
||||
|
||||
|
||||
# --------------- check mode ----------------------
|
||||
|
||||
$ADB push $DATA_DIR/old.file $WORK_DIR
|
||||
|
||||
testname "check mode single"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
|
||||
|
||||
testname "check mode multiple"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
|
||||
|
||||
testname "check mode failure"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
|
||||
|
||||
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
|
||||
# put some junk in the old file
|
||||
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
|
||||
|
||||
testname "check mode cache (corrupted) single"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
|
||||
|
||||
testname "check mode cache (corrupted) multiple"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
|
||||
|
||||
testname "check mode cache (corrupted) failure"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
|
||||
|
||||
# remove the old file entirely
|
||||
run_command rm $WORK_DIR/old.file
|
||||
|
||||
testname "check mode cache (missing) single"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
|
||||
|
||||
testname "check mode cache (missing) multiple"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
|
||||
|
||||
testname "check mode cache (missing) failure"
|
||||
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
|
||||
|
||||
|
||||
# --------------- apply patch ----------------------
|
||||
|
||||
$ADB push $DATA_DIR/old.file $WORK_DIR
|
||||
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
|
||||
echo hello > $tmpdir/foo
|
||||
$ADB push $tmpdir/foo $WORK_DIR
|
||||
|
||||
# Check that the partition has enough space to apply the patch without
|
||||
# copying. If it doesn't, we'll be testing the low-space condition
|
||||
# when we intend to test the not-low-space condition.
|
||||
testname "apply patches (with enough space)"
|
||||
free_kb=$(free_space $WORK_FS)
|
||||
echo "${free_kb}kb free on /$WORK_FS."
|
||||
if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
|
||||
echo "Not enough space on /$WORK_FS to patch test file."
|
||||
echo
|
||||
echo "This doesn't mean that applypatch is necessarily broken;"
|
||||
echo "just that /$WORK_FS doesn't have enough free space to"
|
||||
echo "properly run this test."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
testname "apply bsdiff patch"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
testname "reapply bsdiff patch"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
|
||||
# --------------- apply patch in new location ----------------------
|
||||
|
||||
$ADB push $DATA_DIR/old.file $WORK_DIR
|
||||
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
|
||||
|
||||
# Check that the partition has enough space to apply the patch without
|
||||
# copying. If it doesn't, we'll be testing the low-space condition
|
||||
# when we intend to test the not-low-space condition.
|
||||
testname "apply patch to new location (with enough space)"
|
||||
free_kb=$(free_space $WORK_FS)
|
||||
echo "${free_kb}kb free on /$WORK_FS."
|
||||
if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
|
||||
echo "Not enough space on /$WORK_FS to patch test file."
|
||||
echo
|
||||
echo "This doesn't mean that applypatch is necessarily broken;"
|
||||
echo "just that /$WORK_FS doesn't have enough free space to"
|
||||
echo "properly run this test."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_command rm $WORK_DIR/new.file
|
||||
run_command rm $CACHE_TEMP_SOURCE
|
||||
|
||||
testname "apply bsdiff patch to new location"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/new.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
testname "reapply bsdiff patch to new location"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/new.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
|
||||
# put some junk in the old file
|
||||
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
|
||||
|
||||
testname "apply bsdiff patch to new location with corrupted source"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail
|
||||
$ADB pull $WORK_DIR/new.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
# put some junk in the cache copy, too
|
||||
run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail
|
||||
|
||||
run_command rm $WORK_DIR/new.file
|
||||
testname "apply bsdiff patch to new location with corrupted source and copy (no new file)"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
|
||||
|
||||
# put some junk in the new file
|
||||
run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail
|
||||
|
||||
testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
|
||||
|
||||
# --------------- apply patch with low space on /system ----------------------
|
||||
|
||||
$ADB push $DATA_DIR/old.file $WORK_DIR
|
||||
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
|
||||
|
||||
free_kb=$(free_space $WORK_FS)
|
||||
echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that."
|
||||
echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
|
||||
run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
|
||||
free_kb=$(free_space $WORK_FS)
|
||||
echo "${free_kb}kb free on /$WORK_FS now."
|
||||
|
||||
testname "apply bsdiff patch with low space"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
testname "reapply bsdiff patch with low space"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
# --------------- apply patch with low space on /system and /cache ----------------------
|
||||
|
||||
$ADB push $DATA_DIR/old.file $WORK_DIR
|
||||
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
|
||||
|
||||
free_kb=$(free_space $WORK_FS)
|
||||
echo "${free_kb}kb free on /$WORK_FS"
|
||||
|
||||
run_command mkdir /cache/subdir
|
||||
run_command 'echo > /cache/subdir/a.file'
|
||||
run_command 'echo > /cache/a.file'
|
||||
run_command mkdir /cache/recovery /cache/recovery/otatest
|
||||
run_command 'echo > /cache/recovery/otatest/b.file'
|
||||
run_command "echo > $CACHE_TEMP_SOURCE"
|
||||
free_kb=$(free_space cache)
|
||||
echo "${free_kb}kb free on /cache; we'll soon fix that."
|
||||
run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail
|
||||
run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail
|
||||
free_kb=$(free_space cache)
|
||||
echo "${free_kb}kb free on /cache now."
|
||||
|
||||
testname "apply bsdiff patch with low space, full cache, can't delete enough"
|
||||
$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$!
|
||||
echo "open_pid is $open_pid"
|
||||
|
||||
# size check should fail even though it deletes some stuff
|
||||
run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail
|
||||
run_command ls /cache/bloat_small.dat && fail # was deleted
|
||||
run_command ls /cache/a.file && fail # was deleted
|
||||
run_command ls /cache/recovery/otatest/b.file && fail # was deleted
|
||||
run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
|
||||
run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
|
||||
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
|
||||
|
||||
# should fail; not enough files can be deleted
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
|
||||
run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
|
||||
run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
|
||||
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
|
||||
|
||||
kill $open_pid # /cache/bloat_large.dat is no longer open
|
||||
|
||||
testname "apply bsdiff patch with low space, full cache, can delete enough"
|
||||
|
||||
# should succeed after deleting /cache/bloat_large.dat
|
||||
run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail
|
||||
run_command ls /cache/bloat_large.dat && fail # was deleted
|
||||
run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
|
||||
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
|
||||
|
||||
# should succeed
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
|
||||
run_command ls $CACHE_TEMP_SOURCE && fail # was deleted because patching overwrote it, then deleted it
|
||||
|
||||
# --------------- apply patch from cache ----------------------
|
||||
|
||||
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
|
||||
# put some junk in the old file
|
||||
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
|
||||
|
||||
testname "apply bsdiff patch from cache (corrupted source) with low space"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
|
||||
# remove the old file entirely
|
||||
run_command rm $WORK_DIR/old.file
|
||||
|
||||
testname "apply bsdiff patch from cache (missing source) with low space"
|
||||
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
|
||||
$ADB pull $WORK_DIR/old.file $tmpdir/patched
|
||||
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
|
||||
|
||||
|
||||
# --------------- cleanup ----------------------
|
||||
|
||||
cleanup
|
||||
|
||||
echo
|
||||
echo PASS
|
||||
echo
|
||||
|
410
applypatch/bsdiff.c
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Most of this code comes from bsdiff.c from the bsdiff-4.3
|
||||
* distribution, which is:
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright 2003-2005 Colin Percival
|
||||
* All rights reserved
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted providing that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MIN(x,y) (((x)<(y)) ? (x) : (y))
|
||||
|
||||
static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
|
||||
{
|
||||
off_t i,j,k,x,tmp,jj,kk;
|
||||
|
||||
if(len<16) {
|
||||
for(k=start;k<start+len;k+=j) {
|
||||
j=1;x=V[I[k]+h];
|
||||
for(i=1;k+i<start+len;i++) {
|
||||
if(V[I[k+i]+h]<x) {
|
||||
x=V[I[k+i]+h];
|
||||
j=0;
|
||||
};
|
||||
if(V[I[k+i]+h]==x) {
|
||||
tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
|
||||
j++;
|
||||
};
|
||||
};
|
||||
for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
|
||||
if(j==1) I[k]=-1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
x=V[I[start+len/2]+h];
|
||||
jj=0;kk=0;
|
||||
for(i=start;i<start+len;i++) {
|
||||
if(V[I[i]+h]<x) jj++;
|
||||
if(V[I[i]+h]==x) kk++;
|
||||
};
|
||||
jj+=start;kk+=jj;
|
||||
|
||||
i=start;j=0;k=0;
|
||||
while(i<jj) {
|
||||
if(V[I[i]+h]<x) {
|
||||
i++;
|
||||
} else if(V[I[i]+h]==x) {
|
||||
tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
|
||||
j++;
|
||||
} else {
|
||||
tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
|
||||
k++;
|
||||
};
|
||||
};
|
||||
|
||||
while(jj+j<kk) {
|
||||
if(V[I[jj+j]+h]==x) {
|
||||
j++;
|
||||
} else {
|
||||
tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
|
||||
k++;
|
||||
};
|
||||
};
|
||||
|
||||
if(jj>start) split(I,V,start,jj-start,h);
|
||||
|
||||
for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
|
||||
if(jj==kk-1) I[jj]=-1;
|
||||
|
||||
if(start+len>kk) split(I,V,kk,start+len-kk,h);
|
||||
}
|
||||
|
||||
static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
|
||||
{
|
||||
off_t buckets[256];
|
||||
off_t i,h,len;
|
||||
|
||||
for(i=0;i<256;i++) buckets[i]=0;
|
||||
for(i=0;i<oldsize;i++) buckets[old[i]]++;
|
||||
for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
|
||||
for(i=255;i>0;i--) buckets[i]=buckets[i-1];
|
||||
buckets[0]=0;
|
||||
|
||||
for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
|
||||
I[0]=oldsize;
|
||||
for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
|
||||
V[oldsize]=0;
|
||||
for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
|
||||
I[0]=-1;
|
||||
|
||||
for(h=1;I[0]!=-(oldsize+1);h+=h) {
|
||||
len=0;
|
||||
for(i=0;i<oldsize+1;) {
|
||||
if(I[i]<0) {
|
||||
len-=I[i];
|
||||
i-=I[i];
|
||||
} else {
|
||||
if(len) I[i-len]=-len;
|
||||
len=V[I[i]]+1-i;
|
||||
split(I,V,i,len,h);
|
||||
i+=len;
|
||||
len=0;
|
||||
};
|
||||
};
|
||||
if(len) I[i-len]=-len;
|
||||
};
|
||||
|
||||
for(i=0;i<oldsize+1;i++) I[V[i]]=i;
|
||||
}
|
||||
|
||||
static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize)
|
||||
{
|
||||
off_t i;
|
||||
|
||||
for(i=0;(i<oldsize)&&(i<newsize);i++)
|
||||
if(old[i]!=new[i]) break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static off_t search(off_t *I,u_char *old,off_t oldsize,
|
||||
u_char *new,off_t newsize,off_t st,off_t en,off_t *pos)
|
||||
{
|
||||
off_t x,y;
|
||||
|
||||
if(en-st<2) {
|
||||
x=matchlen(old+I[st],oldsize-I[st],new,newsize);
|
||||
y=matchlen(old+I[en],oldsize-I[en],new,newsize);
|
||||
|
||||
if(x>y) {
|
||||
*pos=I[st];
|
||||
return x;
|
||||
} else {
|
||||
*pos=I[en];
|
||||
return y;
|
||||
}
|
||||
};
|
||||
|
||||
x=st+(en-st)/2;
|
||||
if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) {
|
||||
return search(I,old,oldsize,new,newsize,x,en,pos);
|
||||
} else {
|
||||
return search(I,old,oldsize,new,newsize,st,x,pos);
|
||||
};
|
||||
}
|
||||
|
||||
static void offtout(off_t x,u_char *buf)
|
||||
{
|
||||
off_t y;
|
||||
|
||||
if(x<0) y=-x; else y=x;
|
||||
|
||||
buf[0]=y%256;y-=buf[0];
|
||||
y=y/256;buf[1]=y%256;y-=buf[1];
|
||||
y=y/256;buf[2]=y%256;y-=buf[2];
|
||||
y=y/256;buf[3]=y%256;y-=buf[3];
|
||||
y=y/256;buf[4]=y%256;y-=buf[4];
|
||||
y=y/256;buf[5]=y%256;y-=buf[5];
|
||||
y=y/256;buf[6]=y%256;y-=buf[6];
|
||||
y=y/256;buf[7]=y%256;
|
||||
|
||||
if(x<0) buf[7]|=0x80;
|
||||
}
|
||||
|
||||
// This is main() from bsdiff.c, with the following changes:
|
||||
//
|
||||
// - old, oldsize, new, newsize are arguments; we don't load this
|
||||
// data from files. old and new are owned by the caller; we
|
||||
// don't free them at the end.
|
||||
//
|
||||
// - the "I" block of memory is owned by the caller, who passes a
|
||||
// pointer to *I, which can be NULL. This way if we call
|
||||
// bsdiff() multiple times with the same 'old' data, we only do
|
||||
// the qsufsort() step the first time.
|
||||
//
|
||||
int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* new, off_t newsize,
|
||||
const char* patch_filename)
|
||||
{
|
||||
int fd;
|
||||
off_t *I;
|
||||
off_t scan,pos,len;
|
||||
off_t lastscan,lastpos,lastoffset;
|
||||
off_t oldscore,scsc;
|
||||
off_t s,Sf,lenf,Sb,lenb;
|
||||
off_t overlap,Ss,lens;
|
||||
off_t i;
|
||||
off_t dblen,eblen;
|
||||
u_char *db,*eb;
|
||||
u_char buf[8];
|
||||
u_char header[32];
|
||||
FILE * pf;
|
||||
BZFILE * pfbz2;
|
||||
int bz2err;
|
||||
|
||||
if (*IP == NULL) {
|
||||
off_t* V;
|
||||
*IP = malloc((oldsize+1) * sizeof(off_t));
|
||||
V = malloc((oldsize+1) * sizeof(off_t));
|
||||
qsufsort(*IP, V, old, oldsize);
|
||||
free(V);
|
||||
}
|
||||
I = *IP;
|
||||
|
||||
if(((db=malloc(newsize+1))==NULL) ||
|
||||
((eb=malloc(newsize+1))==NULL)) err(1,NULL);
|
||||
dblen=0;
|
||||
eblen=0;
|
||||
|
||||
/* Create the patch file */
|
||||
if ((pf = fopen(patch_filename, "w")) == NULL)
|
||||
err(1, "%s", patch_filename);
|
||||
|
||||
/* Header is
|
||||
0 8 "BSDIFF40"
|
||||
8 8 length of bzip2ed ctrl block
|
||||
16 8 length of bzip2ed diff block
|
||||
24 8 length of new file */
|
||||
/* File is
|
||||
0 32 Header
|
||||
32 ?? Bzip2ed ctrl block
|
||||
?? ?? Bzip2ed diff block
|
||||
?? ?? Bzip2ed extra block */
|
||||
memcpy(header,"BSDIFF40",8);
|
||||
offtout(0, header + 8);
|
||||
offtout(0, header + 16);
|
||||
offtout(newsize, header + 24);
|
||||
if (fwrite(header, 32, 1, pf) != 1)
|
||||
err(1, "fwrite(%s)", patch_filename);
|
||||
|
||||
/* Compute the differences, writing ctrl as we go */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
|
||||
scan=0;len=0;
|
||||
lastscan=0;lastpos=0;lastoffset=0;
|
||||
while(scan<newsize) {
|
||||
oldscore=0;
|
||||
|
||||
for(scsc=scan+=len;scan<newsize;scan++) {
|
||||
len=search(I,old,oldsize,new+scan,newsize-scan,
|
||||
0,oldsize,&pos);
|
||||
|
||||
for(;scsc<scan+len;scsc++)
|
||||
if((scsc+lastoffset<oldsize) &&
|
||||
(old[scsc+lastoffset] == new[scsc]))
|
||||
oldscore++;
|
||||
|
||||
if(((len==oldscore) && (len!=0)) ||
|
||||
(len>oldscore+8)) break;
|
||||
|
||||
if((scan+lastoffset<oldsize) &&
|
||||
(old[scan+lastoffset] == new[scan]))
|
||||
oldscore--;
|
||||
};
|
||||
|
||||
if((len!=oldscore) || (scan==newsize)) {
|
||||
s=0;Sf=0;lenf=0;
|
||||
for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
|
||||
if(old[lastpos+i]==new[lastscan+i]) s++;
|
||||
i++;
|
||||
if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
|
||||
};
|
||||
|
||||
lenb=0;
|
||||
if(scan<newsize) {
|
||||
s=0;Sb=0;
|
||||
for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
|
||||
if(old[pos-i]==new[scan-i]) s++;
|
||||
if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
|
||||
};
|
||||
};
|
||||
|
||||
if(lastscan+lenf>scan-lenb) {
|
||||
overlap=(lastscan+lenf)-(scan-lenb);
|
||||
s=0;Ss=0;lens=0;
|
||||
for(i=0;i<overlap;i++) {
|
||||
if(new[lastscan+lenf-overlap+i]==
|
||||
old[lastpos+lenf-overlap+i]) s++;
|
||||
if(new[scan-lenb+i]==
|
||||
old[pos-lenb+i]) s--;
|
||||
if(s>Ss) { Ss=s; lens=i+1; };
|
||||
};
|
||||
|
||||
lenf+=lens-overlap;
|
||||
lenb-=lens;
|
||||
};
|
||||
|
||||
for(i=0;i<lenf;i++)
|
||||
db[dblen+i]=new[lastscan+i]-old[lastpos+i];
|
||||
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
|
||||
eb[eblen+i]=new[lastscan+lenf+i];
|
||||
|
||||
dblen+=lenf;
|
||||
eblen+=(scan-lenb)-(lastscan+lenf);
|
||||
|
||||
offtout(lenf,buf);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
|
||||
offtout((scan-lenb)-(lastscan+lenf),buf);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
|
||||
offtout((pos-lenb)-(lastpos+lenf),buf);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
|
||||
lastscan=scan-lenb;
|
||||
lastpos=pos-lenb;
|
||||
lastoffset=pos-scan;
|
||||
};
|
||||
};
|
||||
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
|
||||
|
||||
/* Compute size of compressed ctrl data */
|
||||
if ((len = ftello(pf)) == -1)
|
||||
err(1, "ftello");
|
||||
offtout(len-32, header + 8);
|
||||
|
||||
/* Write compressed diff data */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
|
||||
|
||||
/* Compute size of compressed diff data */
|
||||
if ((newsize = ftello(pf)) == -1)
|
||||
err(1, "ftello");
|
||||
offtout(newsize - len, header + 16);
|
||||
|
||||
/* Write compressed extra data */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
|
||||
|
||||
/* Seek to the beginning, write the header, and close the file */
|
||||
if (fseeko(pf, 0, SEEK_SET))
|
||||
err(1, "fseeko");
|
||||
if (fwrite(header, 32, 1, pf) != 1)
|
||||
err(1, "fwrite(%s)", patch_filename);
|
||||
if (fclose(pf))
|
||||
err(1, "fclose");
|
||||
|
||||
/* Free the memory we used */
|
||||
free(db);
|
||||
free(eb);
|
||||
|
||||
return 0;
|
||||
}
|
252
applypatch/bspatch.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This file is a nearly line-for-line copy of bspatch.c from the
|
||||
// bsdiff-4.3 distribution; the primary differences being how the
|
||||
// input and output data are read and the error handling. Running
|
||||
// applypatch with the -l option will display the bsdiff license
|
||||
// notice.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bzlib.h>
|
||||
|
||||
#include "mincrypt/sha.h"
|
||||
#include "applypatch.h"
|
||||
|
||||
void ShowBSDiffLicense() {
|
||||
puts("The bsdiff library used herein is:\n"
|
||||
"\n"
|
||||
"Copyright 2003-2005 Colin Percival\n"
|
||||
"All rights reserved\n"
|
||||
"\n"
|
||||
"Redistribution and use in source and binary forms, with or without\n"
|
||||
"modification, are permitted providing that the following conditions\n"
|
||||
"are met:\n"
|
||||
"1. Redistributions of source code must retain the above copyright\n"
|
||||
" notice, this list of conditions and the following disclaimer.\n"
|
||||
"2. Redistributions in binary form must reproduce the above copyright\n"
|
||||
" notice, this list of conditions and the following disclaimer in the\n"
|
||||
" documentation and/or other materials provided with the distribution.\n"
|
||||
"\n"
|
||||
"THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
|
||||
"IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
|
||||
"WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
|
||||
"ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
|
||||
"DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
|
||||
"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
|
||||
"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
|
||||
"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
|
||||
"STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
|
||||
"IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
|
||||
"POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
"\n------------------\n\n"
|
||||
"This program uses Julian R Seward's \"libbzip2\" library, available\n"
|
||||
"from http://www.bzip.org/.\n"
|
||||
);
|
||||
}
|
||||
|
||||
static off_t offtin(u_char *buf)
|
||||
{
|
||||
off_t y;
|
||||
|
||||
y=buf[7]&0x7F;
|
||||
y=y*256;y+=buf[6];
|
||||
y=y*256;y+=buf[5];
|
||||
y=y*256;y+=buf[4];
|
||||
y=y*256;y+=buf[3];
|
||||
y=y*256;y+=buf[2];
|
||||
y=y*256;y+=buf[1];
|
||||
y=y*256;y+=buf[0];
|
||||
|
||||
if(buf[7]&0x80) y=-y;
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
|
||||
stream->next_out = (char*)buffer;
|
||||
stream->avail_out = size;
|
||||
while (stream->avail_out > 0) {
|
||||
int bzerr = BZ2_bzDecompress(stream);
|
||||
if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
|
||||
printf("bz error %d decompressing\n", bzerr);
|
||||
return -1;
|
||||
}
|
||||
if (stream->avail_out > 0) {
|
||||
printf("need %d more bytes\n", stream->avail_out);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx) {
|
||||
|
||||
unsigned char* new_data;
|
||||
ssize_t new_size;
|
||||
if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset,
|
||||
&new_data, &new_size) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sink(new_data, new_size, token) < new_size) {
|
||||
printf("short write of output: %d (%s)\n", errno, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (ctx) {
|
||||
SHA_update(ctx, new_data, new_size);
|
||||
}
|
||||
free(new_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
unsigned char** new_data, ssize_t* new_size) {
|
||||
// Patch data format:
|
||||
// 0 8 "BSDIFF40"
|
||||
// 8 8 X
|
||||
// 16 8 Y
|
||||
// 24 8 sizeof(newfile)
|
||||
// 32 X bzip2(control block)
|
||||
// 32+X Y bzip2(diff block)
|
||||
// 32+X+Y ??? bzip2(extra block)
|
||||
// with control block a set of triples (x,y,z) meaning "add x bytes
|
||||
// from oldfile to x bytes from the diff block; copy y bytes from the
|
||||
// extra block; seek forwards in oldfile by z bytes".
|
||||
|
||||
unsigned char* header = (unsigned char*) patch->data + patch_offset;
|
||||
if (memcmp(header, "BSDIFF40", 8) != 0) {
|
||||
printf("corrupt bsdiff patch file header (magic number)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t ctrl_len, data_len;
|
||||
ctrl_len = offtin(header+8);
|
||||
data_len = offtin(header+16);
|
||||
*new_size = offtin(header+24);
|
||||
|
||||
if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
|
||||
printf("corrupt patch file header (data lengths)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bzerr;
|
||||
|
||||
bz_stream cstream;
|
||||
cstream.next_in = patch->data + patch_offset + 32;
|
||||
cstream.avail_in = ctrl_len;
|
||||
cstream.bzalloc = NULL;
|
||||
cstream.bzfree = NULL;
|
||||
cstream.opaque = NULL;
|
||||
if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
|
||||
printf("failed to bzinit control stream (%d)\n", bzerr);
|
||||
}
|
||||
|
||||
bz_stream dstream;
|
||||
dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
|
||||
dstream.avail_in = data_len;
|
||||
dstream.bzalloc = NULL;
|
||||
dstream.bzfree = NULL;
|
||||
dstream.opaque = NULL;
|
||||
if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
|
||||
printf("failed to bzinit diff stream (%d)\n", bzerr);
|
||||
}
|
||||
|
||||
bz_stream estream;
|
||||
estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
|
||||
estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
|
||||
estream.bzalloc = NULL;
|
||||
estream.bzfree = NULL;
|
||||
estream.opaque = NULL;
|
||||
if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
|
||||
printf("failed to bzinit extra stream (%d)\n", bzerr);
|
||||
}
|
||||
|
||||
*new_data = malloc(*new_size);
|
||||
if (*new_data == NULL) {
|
||||
printf("failed to allocate %ld bytes of memory for output file\n",
|
||||
(long)*new_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
off_t oldpos = 0, newpos = 0;
|
||||
off_t ctrl[3];
|
||||
off_t len_read;
|
||||
int i;
|
||||
unsigned char buf[24];
|
||||
while (newpos < *new_size) {
|
||||
// Read control data
|
||||
if (FillBuffer(buf, 24, &cstream) != 0) {
|
||||
printf("error while reading control stream\n");
|
||||
return 1;
|
||||
}
|
||||
ctrl[0] = offtin(buf);
|
||||
ctrl[1] = offtin(buf+8);
|
||||
ctrl[2] = offtin(buf+16);
|
||||
|
||||
// Sanity check
|
||||
if (newpos + ctrl[0] > *new_size) {
|
||||
printf("corrupt patch (new file overrun)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read diff string
|
||||
if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) {
|
||||
printf("error while reading diff stream\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Add old data to diff string
|
||||
for (i = 0; i < ctrl[0]; ++i) {
|
||||
if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
|
||||
(*new_data)[newpos+i] += old_data[oldpos+i];
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust pointers
|
||||
newpos += ctrl[0];
|
||||
oldpos += ctrl[0];
|
||||
|
||||
// Sanity check
|
||||
if (newpos + ctrl[1] > *new_size) {
|
||||
printf("corrupt patch (new file overrun)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read extra string
|
||||
if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) {
|
||||
printf("error while reading extra stream\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Adjust pointers
|
||||
newpos += ctrl[1];
|
||||
oldpos += ctrl[2];
|
||||
}
|
||||
|
||||
BZ2_bzDecompressEnd(&cstream);
|
||||
BZ2_bzDecompressEnd(&dstream);
|
||||
BZ2_bzDecompressEnd(&estream);
|
||||
return 0;
|
||||
}
|
172
applypatch/freecache.c
Normal file
@ -0,0 +1,172 @@
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "applypatch.h"
|
||||
|
||||
static int EliminateOpenFiles(char** files, int file_count) {
|
||||
DIR* d;
|
||||
struct dirent* de;
|
||||
d = opendir("/proc");
|
||||
if (d == NULL) {
|
||||
printf("error opening /proc: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while ((de = readdir(d)) != 0) {
|
||||
int i;
|
||||
for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i);
|
||||
if (de->d_name[i]) continue;
|
||||
|
||||
// de->d_name[i] is numeric
|
||||
|
||||
char path[FILENAME_MAX];
|
||||
strcpy(path, "/proc/");
|
||||
strcat(path, de->d_name);
|
||||
strcat(path, "/fd/");
|
||||
|
||||
DIR* fdd;
|
||||
struct dirent* fdde;
|
||||
fdd = opendir(path);
|
||||
if (fdd == NULL) {
|
||||
printf("error opening %s: %s\n", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
while ((fdde = readdir(fdd)) != 0) {
|
||||
char fd_path[FILENAME_MAX];
|
||||
char link[FILENAME_MAX];
|
||||
strcpy(fd_path, path);
|
||||
strcat(fd_path, fdde->d_name);
|
||||
|
||||
int count;
|
||||
count = readlink(fd_path, link, sizeof(link)-1);
|
||||
if (count >= 0) {
|
||||
link[count] = '\0';
|
||||
|
||||
// This is inefficient, but it should only matter if there are
|
||||
// lots of files in /cache, and lots of them are open (neither
|
||||
// of which should be true, especially in recovery).
|
||||
if (strncmp(link, "/cache/", 7) == 0) {
|
||||
int j;
|
||||
for (j = 0; j < file_count; ++j) {
|
||||
if (files[j] && strcmp(files[j], link) == 0) {
|
||||
printf("%s is open by %s\n", link, de->d_name);
|
||||
free(files[j]);
|
||||
files[j] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(fdd);
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FindExpendableFiles(char*** names, int* entries) {
|
||||
DIR* d;
|
||||
struct dirent* de;
|
||||
int size = 32;
|
||||
*entries = 0;
|
||||
*names = malloc(size * sizeof(char*));
|
||||
|
||||
char path[FILENAME_MAX];
|
||||
|
||||
// We're allowed to delete unopened regular files in any of these
|
||||
// directories.
|
||||
const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
|
||||
d = opendir(dirs[i]);
|
||||
if (d == NULL) {
|
||||
printf("error opening %s: %s\n", dirs[i], strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for regular files in the directory (not in any subdirectories).
|
||||
while ((de = readdir(d)) != 0) {
|
||||
strcpy(path, dirs[i]);
|
||||
strcat(path, "/");
|
||||
strcat(path, de->d_name);
|
||||
|
||||
// We can't delete CACHE_TEMP_SOURCE; if it's there we might have
|
||||
// restarted during installation and could be depending on it to
|
||||
// be there.
|
||||
if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue;
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
if (*entries >= size) {
|
||||
size *= 2;
|
||||
*names = realloc(*names, size * sizeof(char*));
|
||||
}
|
||||
(*names)[(*entries)++] = strdup(path);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
printf("%d regular files in deletable directories\n", *entries);
|
||||
|
||||
if (EliminateOpenFiles(*names, *entries) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MakeFreeSpaceOnCache(size_t bytes_needed) {
|
||||
size_t free_now = FreeSpaceForFile("/cache");
|
||||
printf("%ld bytes free on /cache (%ld needed)\n",
|
||||
(long)free_now, (long)bytes_needed);
|
||||
|
||||
if (free_now >= bytes_needed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char** names;
|
||||
int entries;
|
||||
|
||||
if (FindExpendableFiles(&names, &entries) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (entries == 0) {
|
||||
// nothing we can delete to free up space!
|
||||
printf("no files can be deleted to free space on /cache\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We could try to be smarter about which files to delete: the
|
||||
// biggest ones? the smallest ones that will free up enough space?
|
||||
// the oldest? the newest?
|
||||
//
|
||||
// Instead, we'll be dumb.
|
||||
|
||||
int i;
|
||||
for (i = 0; i < entries && free_now < bytes_needed; ++i) {
|
||||
if (names[i]) {
|
||||
unlink(names[i]);
|
||||
free_now = FreeSpaceForFile("/cache");
|
||||
printf("deleted %s; now %ld bytes free\n", names[i], (long)free_now);
|
||||
free(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < entries; ++i) {
|
||||
free(names[i]);
|
||||
}
|
||||
free(names);
|
||||
|
||||
return (free_now >= bytes_needed) ? 0 : -1;
|
||||
}
|
1010
applypatch/imgdiff.c
Normal file
30
applypatch/imgdiff.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Image patch chunk types
|
||||
#define CHUNK_NORMAL 0
|
||||
#define CHUNK_GZIP 1 // version 1 only
|
||||
#define CHUNK_DEFLATE 2 // version 2 only
|
||||
#define CHUNK_RAW 3 // version 2 only
|
||||
|
||||
// The gzip header size is actually variable, but we currently don't
|
||||
// support gzipped data with any of the optional fields, so for now it
|
||||
// will always be ten bytes. See RFC 1952 for the definition of the
|
||||
// gzip format.
|
||||
#define GZIP_HEADER_LEN 10
|
||||
|
||||
// The gzip footer size really is fixed.
|
||||
#define GZIP_FOOTER_LEN 8
|
118
applypatch/imgdiff_test.sh
Executable file
@ -0,0 +1,118 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# A script for testing imgdiff/applypatch. It takes two full OTA
|
||||
# packages as arguments. It generates (on the host) patches for all
|
||||
# the zip/jar/apk files they have in common, as well as boot and
|
||||
# recovery images. It then applies the patches on the device (or
|
||||
# emulator) and checks that the resulting file is correct.
|
||||
|
||||
EMULATOR_PORT=5580
|
||||
|
||||
# set to 0 to use a device instead
|
||||
USE_EMULATOR=0
|
||||
|
||||
# where on the device to do all the patching.
|
||||
WORK_DIR=/data/local/tmp
|
||||
|
||||
START_OTA_PACKAGE=$1
|
||||
END_OTA_PACKAGE=$2
|
||||
|
||||
# ------------------------
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
|
||||
if [ "$USE_EMULATOR" == 1 ]; then
|
||||
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
|
||||
pid_emulator=$!
|
||||
ADB="adb -s emulator-$EMULATOR_PORT "
|
||||
else
|
||||
ADB="adb -d "
|
||||
fi
|
||||
|
||||
echo "waiting to connect to device"
|
||||
$ADB wait-for-device
|
||||
|
||||
# run a command on the device; exit with the exit status of the device
|
||||
# command.
|
||||
run_command() {
|
||||
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
|
||||
}
|
||||
|
||||
testname() {
|
||||
echo
|
||||
echo "$1"...
|
||||
testname="$1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo
|
||||
echo FAIL: $testname
|
||||
echo
|
||||
[ "$open_pid" == "" ] || kill $open_pid
|
||||
[ "$pid_emulator" == "" ] || kill $pid_emulator
|
||||
exit 1
|
||||
}
|
||||
|
||||
sha1() {
|
||||
sha1sum $1 | awk '{print $1}'
|
||||
}
|
||||
|
||||
size() {
|
||||
stat -c %s $1 | tr -d '\n'
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# not necessary if we're about to kill the emulator, but nice for
|
||||
# running on real devices or already-running emulators.
|
||||
testname "removing test files"
|
||||
run_command rm $WORK_DIR/applypatch
|
||||
run_command rm $WORK_DIR/source
|
||||
run_command rm $WORK_DIR/target
|
||||
run_command rm $WORK_DIR/patch
|
||||
|
||||
[ "$pid_emulator" == "" ] || kill $pid_emulator
|
||||
|
||||
rm -rf $tmpdir
|
||||
}
|
||||
|
||||
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
|
||||
|
||||
patch_and_apply() {
|
||||
local fn=$1
|
||||
shift
|
||||
|
||||
unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source
|
||||
unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target
|
||||
imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch
|
||||
bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs
|
||||
echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)"
|
||||
echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt
|
||||
$ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed"
|
||||
run_command rm /data/local/tmp/target
|
||||
$ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed"
|
||||
run_command /data/local/tmp/applypatch /data/local/tmp/source \
|
||||
/data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \
|
||||
$(sha1 $tmpdir/source):/data/local/tmp/patch \
|
||||
|| fail "applypatch of $fn failed"
|
||||
$ADB pull /data/local/tmp/target $tmpdir/result
|
||||
diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!"
|
||||
}
|
||||
|
||||
# --------------- basic execution ----------------------
|
||||
|
||||
for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \
|
||||
sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do
|
||||
patch_and_apply $i -z
|
||||
done
|
||||
patch_and_apply boot.img
|
||||
patch_and_apply system/recovery.img
|
||||
|
||||
|
||||
# --------------- cleanup ----------------------
|
||||
|
||||
cleanup
|
||||
|
||||
echo
|
||||
echo PASS
|
||||
echo
|
||||
|
219
applypatch/imgpatch.c
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// See imgdiff.c in this directory for a description of the patch file
|
||||
// format.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "zlib.h"
|
||||
#include "mincrypt/sha.h"
|
||||
#include "applypatch.h"
|
||||
#include "imgdiff.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
* Apply the patch given in 'patch_filename' to the source data given
|
||||
* by (old_data, old_size). Write the patched output to the 'output'
|
||||
* file, and update the SHA context with the output data as well.
|
||||
* Return 0 on success.
|
||||
*/
|
||||
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx) {
|
||||
ssize_t pos = 12;
|
||||
char* header = patch->data;
|
||||
if (patch->size < 12) {
|
||||
printf("patch too short to contain header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
|
||||
// (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
|
||||
// CHUNK_GZIP.)
|
||||
if (memcmp(header, "IMGDIFF2", 8) != 0) {
|
||||
printf("corrupt patch file header (magic number)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int num_chunks = Read4(header+8);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < num_chunks; ++i) {
|
||||
// each chunk's header record starts with 4 bytes.
|
||||
if (pos + 4 > patch->size) {
|
||||
printf("failed to read chunk %d record\n", i);
|
||||
return -1;
|
||||
}
|
||||
int type = Read4(patch->data + pos);
|
||||
pos += 4;
|
||||
|
||||
if (type == CHUNK_NORMAL) {
|
||||
char* normal_header = patch->data + pos;
|
||||
pos += 24;
|
||||
if (pos > patch->size) {
|
||||
printf("failed to read chunk %d normal header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t src_start = Read8(normal_header);
|
||||
size_t src_len = Read8(normal_header+8);
|
||||
size_t patch_offset = Read8(normal_header+16);
|
||||
|
||||
ApplyBSDiffPatch(old_data + src_start, src_len,
|
||||
patch, patch_offset, sink, token, ctx);
|
||||
} else if (type == CHUNK_RAW) {
|
||||
char* raw_header = patch->data + pos;
|
||||
pos += 4;
|
||||
if (pos > patch->size) {
|
||||
printf("failed to read chunk %d raw header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t data_len = Read4(raw_header);
|
||||
|
||||
if (pos + data_len > patch->size) {
|
||||
printf("failed to read chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
SHA_update(ctx, patch->data + pos, data_len);
|
||||
if (sink((unsigned char*)patch->data + pos,
|
||||
data_len, token) != data_len) {
|
||||
printf("failed to write chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
pos += data_len;
|
||||
} else if (type == CHUNK_DEFLATE) {
|
||||
// deflate chunks have an additional 60 bytes in their chunk header.
|
||||
char* deflate_header = patch->data + pos;
|
||||
pos += 60;
|
||||
if (pos > patch->size) {
|
||||
printf("failed to read chunk %d deflate header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t src_start = Read8(deflate_header);
|
||||
size_t src_len = Read8(deflate_header+8);
|
||||
size_t patch_offset = Read8(deflate_header+16);
|
||||
size_t expanded_len = Read8(deflate_header+24);
|
||||
size_t target_len = Read8(deflate_header+32);
|
||||
int level = Read4(deflate_header+40);
|
||||
int method = Read4(deflate_header+44);
|
||||
int windowBits = Read4(deflate_header+48);
|
||||
int memLevel = Read4(deflate_header+52);
|
||||
int strategy = Read4(deflate_header+56);
|
||||
|
||||
// Decompress the source data; the chunk header tells us exactly
|
||||
// how big we expect it to be when decompressed.
|
||||
|
||||
unsigned char* expanded_source = malloc(expanded_len);
|
||||
if (expanded_source == NULL) {
|
||||
printf("failed to allocate %d bytes for expanded_source\n",
|
||||
expanded_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = src_len;
|
||||
strm.next_in = (unsigned char*)(old_data + src_start);
|
||||
strm.avail_out = expanded_len;
|
||||
strm.next_out = expanded_source;
|
||||
|
||||
int ret;
|
||||
ret = inflateInit2(&strm, -15);
|
||||
if (ret != Z_OK) {
|
||||
printf("failed to init source inflation: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Because we've provided enough room to accommodate the output
|
||||
// data, we expect one call to inflate() to suffice.
|
||||
ret = inflate(&strm, Z_SYNC_FLUSH);
|
||||
if (ret != Z_STREAM_END) {
|
||||
printf("source inflation returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
// We should have filled the output buffer exactly.
|
||||
if (strm.avail_out != 0) {
|
||||
printf("source inflation short by %d bytes\n", strm.avail_out);
|
||||
return -1;
|
||||
}
|
||||
inflateEnd(&strm);
|
||||
|
||||
// Next, apply the bsdiff patch (in memory) to the uncompressed
|
||||
// data.
|
||||
unsigned char* uncompressed_target_data;
|
||||
ssize_t uncompressed_target_size;
|
||||
if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
|
||||
patch, patch_offset,
|
||||
&uncompressed_target_data,
|
||||
&uncompressed_target_size) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now compress the target data and append it to the output.
|
||||
|
||||
// we're done with the expanded_source data buffer, so we'll
|
||||
// reuse that memory to receive the output of deflate.
|
||||
unsigned char* temp_data = expanded_source;
|
||||
ssize_t temp_size = expanded_len;
|
||||
if (temp_size < 32768) {
|
||||
// ... unless the buffer is too small, in which case we'll
|
||||
// allocate a fresh one.
|
||||
free(temp_data);
|
||||
temp_data = malloc(32768);
|
||||
temp_size = 32768;
|
||||
}
|
||||
|
||||
// now the deflate stream
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = uncompressed_target_size;
|
||||
strm.next_in = uncompressed_target_data;
|
||||
ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
|
||||
do {
|
||||
strm.avail_out = temp_size;
|
||||
strm.next_out = temp_data;
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
ssize_t have = temp_size - strm.avail_out;
|
||||
|
||||
if (sink(temp_data, have, token) != have) {
|
||||
printf("failed to write %ld compressed bytes to output\n",
|
||||
(long)have);
|
||||
return -1;
|
||||
}
|
||||
SHA_update(ctx, temp_data, have);
|
||||
} while (ret != Z_STREAM_END);
|
||||
deflateEnd(&strm);
|
||||
|
||||
free(temp_data);
|
||||
free(uncompressed_target_data);
|
||||
} else {
|
||||
printf("patch chunk %d is unknown type %d\n", i, type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
195
applypatch/main.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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 <unistd.h>
|
||||
|
||||
#include "applypatch.h"
|
||||
#include "edify/expr.h"
|
||||
#include "mincrypt/sha.h"
|
||||
|
||||
int CheckMode(int argc, char** argv) {
|
||||
if (argc < 3) {
|
||||
return 2;
|
||||
}
|
||||
return applypatch_check(argv[2], argc-3, argv+3);
|
||||
}
|
||||
|
||||
int SpaceMode(int argc, char** argv) {
|
||||
if (argc != 3) {
|
||||
return 2;
|
||||
}
|
||||
char* endptr;
|
||||
size_t bytes = strtol(argv[2], &endptr, 10);
|
||||
if (bytes == 0 && endptr == argv[2]) {
|
||||
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
return CacheSizeCheck(bytes);
|
||||
}
|
||||
|
||||
// Parse arguments (which should be of the form "<sha1>" or
|
||||
// "<sha1>:<filename>" into the new parallel arrays *sha1s and
|
||||
// *patches (loading file contents into the patches). Returns 0 on
|
||||
// success.
|
||||
static int ParsePatchArgs(int argc, char** argv,
|
||||
char*** sha1s, Value*** patches, int* num_patches) {
|
||||
*num_patches = argc;
|
||||
*sha1s = malloc(*num_patches * sizeof(char*));
|
||||
*patches = malloc(*num_patches * sizeof(Value*));
|
||||
memset(*patches, 0, *num_patches * sizeof(Value*));
|
||||
|
||||
uint8_t digest[SHA_DIGEST_SIZE];
|
||||
|
||||
int i;
|
||||
for (i = 0; i < *num_patches; ++i) {
|
||||
char* colon = strchr(argv[i], ':');
|
||||
if (colon != NULL) {
|
||||
*colon = '\0';
|
||||
++colon;
|
||||
}
|
||||
|
||||
if (ParseSha1(argv[i], digest) != 0) {
|
||||
printf("failed to parse sha1 \"%s\"\n", argv[i]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*sha1s)[i] = argv[i];
|
||||
if (colon == NULL) {
|
||||
(*patches)[i] = NULL;
|
||||
} else {
|
||||
FileContents fc;
|
||||
if (LoadFileContents(colon, &fc) != 0) {
|
||||
goto abort;
|
||||
}
|
||||
(*patches)[i] = malloc(sizeof(Value));
|
||||
(*patches)[i]->type = VAL_BLOB;
|
||||
(*patches)[i]->size = fc.size;
|
||||
(*patches)[i]->data = (char*)fc.data;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
abort:
|
||||
for (i = 0; i < *num_patches; ++i) {
|
||||
Value* p = (*patches)[i];
|
||||
if (p != NULL) {
|
||||
free(p->data);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
free(*sha1s);
|
||||
free(*patches);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PatchMode(int argc, char** argv) {
|
||||
if (argc < 6) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
char* endptr;
|
||||
size_t target_size = strtol(argv[4], &endptr, 10);
|
||||
if (target_size == 0 && endptr == argv[4]) {
|
||||
printf("can't parse \"%s\" as byte count\n\n", argv[4]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char** sha1s;
|
||||
Value** patches;
|
||||
int num_patches;
|
||||
if (ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches) != 0) {
|
||||
printf("failed to parse patch args\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = applypatch(argv[1], argv[2], argv[3], target_size,
|
||||
num_patches, sha1s, patches);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < num_patches; ++i) {
|
||||
Value* p = patches[i];
|
||||
if (p != NULL) {
|
||||
free(p->data);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
free(sha1s);
|
||||
free(patches);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// This program applies binary patches to files in a way that is safe
|
||||
// (the original file is not touched until we have the desired
|
||||
// replacement for it) and idempotent (it's okay to run this program
|
||||
// multiple times).
|
||||
//
|
||||
// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
|
||||
// successfully.
|
||||
//
|
||||
// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
|
||||
// bsdiff <patch> to <src-file> to produce a new file (the type of patch
|
||||
// is automatically detected from the file header). If that new
|
||||
// file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
|
||||
// exits successfully. Note that if <src-file> and <tgt-file> are
|
||||
// not the same, <src-file> is NOT deleted on success. <tgt-file>
|
||||
// may be the string "-" to mean "the same as src-file".
|
||||
//
|
||||
// - otherwise, or if any error is encountered, exits with non-zero
|
||||
// status.
|
||||
//
|
||||
// <src-file> (or <file> in check mode) may refer to an MTD partition
|
||||
// to read the source data. See the comments for the
|
||||
// LoadMTDContents() function above for the format of such a filename.
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
usage:
|
||||
printf(
|
||||
"usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
|
||||
"[<src-sha1>:<patch> ...]\n"
|
||||
" or %s -c <file> [<sha1> ...]\n"
|
||||
" or %s -s <bytes>\n"
|
||||
" or %s -l\n"
|
||||
"\n"
|
||||
"Filenames may be of the form\n"
|
||||
" MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
|
||||
"to specify reading from or writing to an MTD partition.\n\n",
|
||||
argv[0], argv[0], argv[0], argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
if (strncmp(argv[1], "-l", 3) == 0) {
|
||||
result = ShowLicenses();
|
||||
} else if (strncmp(argv[1], "-c", 3) == 0) {
|
||||
result = CheckMode(argc, argv);
|
||||
} else if (strncmp(argv[1], "-s", 3) == 0) {
|
||||
result = SpaceMode(argc, argv);
|
||||
} else {
|
||||
result = PatchMode(argc, argv);
|
||||
}
|
||||
|
||||
if (result == 2) {
|
||||
goto usage;
|
||||
}
|
||||
return result;
|
||||
}
|
BIN
applypatch/testdata/new.file
vendored
Normal file
BIN
applypatch/testdata/old.file
vendored
Normal file
BIN
applypatch/testdata/patch.bsdiff
vendored
Normal file
65
applypatch/utils.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 "utils.h"
|
||||
|
||||
/** Write a 4-byte value to f in little-endian order. */
|
||||
void Write4(int value, FILE* f) {
|
||||
fputc(value & 0xff, f);
|
||||
fputc((value >> 8) & 0xff, f);
|
||||
fputc((value >> 16) & 0xff, f);
|
||||
fputc((value >> 24) & 0xff, f);
|
||||
}
|
||||
|
||||
/** Write an 8-byte value to f in little-endian order. */
|
||||
void Write8(long long value, FILE* f) {
|
||||
fputc(value & 0xff, f);
|
||||
fputc((value >> 8) & 0xff, f);
|
||||
fputc((value >> 16) & 0xff, f);
|
||||
fputc((value >> 24) & 0xff, f);
|
||||
fputc((value >> 32) & 0xff, f);
|
||||
fputc((value >> 40) & 0xff, f);
|
||||
fputc((value >> 48) & 0xff, f);
|
||||
fputc((value >> 56) & 0xff, f);
|
||||
}
|
||||
|
||||
int Read2(void* pv) {
|
||||
unsigned char* p = pv;
|
||||
return (int)(((unsigned int)p[1] << 8) |
|
||||
(unsigned int)p[0]);
|
||||
}
|
||||
|
||||
int Read4(void* pv) {
|
||||
unsigned char* p = pv;
|
||||
return (int)(((unsigned int)p[3] << 24) |
|
||||
((unsigned int)p[2] << 16) |
|
||||
((unsigned int)p[1] << 8) |
|
||||
(unsigned int)p[0]);
|
||||
}
|
||||
|
||||
long long Read8(void* pv) {
|
||||
unsigned char* p = pv;
|
||||
return (long long)(((unsigned long long)p[7] << 56) |
|
||||
((unsigned long long)p[6] << 48) |
|
||||
((unsigned long long)p[5] << 40) |
|
||||
((unsigned long long)p[4] << 32) |
|
||||
((unsigned long long)p[3] << 24) |
|
||||
((unsigned long long)p[2] << 16) |
|
||||
((unsigned long long)p[1] << 8) |
|
||||
(unsigned long long)p[0]);
|
||||
}
|
30
applypatch/utils.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
||||
#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Read and write little-endian values of various sizes.
|
||||
|
||||
void Write4(int value, FILE* f);
|
||||
void Write8(long long value, FILE* f);
|
||||
int Read2(void* p);
|
||||
int Read4(void* p);
|
||||
long long Read8(void* p);
|
||||
|
||||
#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
7
bmlutils/Android.mk
Normal file
@ -0,0 +1,7 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CFLAGS += -DBOARD_BOOT_DEVICE=\"$(BOARD_BOOT_DEVICE)\"
|
||||
LOCAL_SRC_FILES := bmlutils.c
|
||||
LOCAL_MODULE := libbmlutils
|
||||
include $(BUILD_STATIC_LIBRARY)
|
58
bmlutils/bmlutils.c
Normal file
@ -0,0 +1,58 @@
|
||||
#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>
|
||||
|
||||
extern int __system(const char *command);
|
||||
|
||||
int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
sprintf("dd if=%s of=/dev/block/bml7 bs=4096", filename);
|
||||
return __system(tmp);
|
||||
}
|
||||
|
||||
int cmd_bml_backup_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
sprintf("dd of=%s if=/dev/block/bml7 bs=4096", filename);
|
||||
return __system(tmp);
|
||||
}
|
||||
|
||||
int cmd_bml_erase_raw_partition(const char *partition)
|
||||
{
|
||||
// TODO: implement raw wipe
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_bml_erase_partition(const char *partition, const char *filesystem)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cmd_bml_get_partition_device(const char *partition, char *device)
|
||||
{
|
||||
return -1;
|
||||
}
|
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;
|
||||
|
214
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 BOARD_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;
|
||||
}
|
||||
@ -636,6 +653,7 @@ static int
|
||||
cmd_write_raw_image(const char *name, void *cookie,
|
||||
int argc, const char *argv[], PermissionRequestList *permissions)
|
||||
{
|
||||
#ifdef BOARD_USES_MTDUTILS
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
//xxx permissions
|
||||
@ -721,6 +739,10 @@ cmd_write_raw_image(const char *name, void *cookie,
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
LOGE("Board does not support mtd utils.");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* mark <resource> dirty|clean
|
||||
@ -753,6 +775,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 +1251,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
|
||||
|
100
default_recovery_ui.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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_recovery_start() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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())
|
507
edify/expr.c
Normal file
@ -0,0 +1,507 @@
|
||||
/*
|
||||
* 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) {
|
||||
Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);
|
||||
if (v == NULL) return NULL;
|
||||
if (v->type != VAL_STRING) {
|
||||
ErrorAbort(state, "expecting string, got value type %d", v->type);
|
||||
FreeValue(v);
|
||||
return NULL;
|
||||
}
|
||||
char* result = v->data;
|
||||
free(v);
|
||||
return result;
|
||||
}
|
||||
|
||||
Value* EvaluateValue(State* state, Expr* expr) {
|
||||
return expr->fn(expr->name, state, expr->argc, expr->argv);
|
||||
}
|
||||
|
||||
Value* StringValue(char* str) {
|
||||
if (str == NULL) return NULL;
|
||||
Value* v = malloc(sizeof(Value));
|
||||
v->type = VAL_STRING;
|
||||
v->size = strlen(str);
|
||||
v->data = str;
|
||||
return v;
|
||||
}
|
||||
|
||||
void FreeValue(Value* v) {
|
||||
if (v == NULL) return;
|
||||
free(v->data);
|
||||
free(v);
|
||||
}
|
||||
|
||||
Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc == 0) {
|
||||
return StringValue(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]);
|
||||
}
|
||||
free(strings);
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* 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 EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
if (argc == 3) {
|
||||
free(cond);
|
||||
return EvaluateValue(state, argv[2]);
|
||||
} else {
|
||||
return StringValue(cond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value* 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;
|
||||
}
|
||||
|
||||
Value* 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 StringValue(strdup(""));
|
||||
}
|
||||
|
||||
Value* 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 StringValue(val);
|
||||
}
|
||||
|
||||
Value* 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 StringValue(strdup(""));
|
||||
}
|
||||
|
||||
Value* 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 EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return StringValue(left);
|
||||
}
|
||||
}
|
||||
|
||||
Value* 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 EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return StringValue(left);
|
||||
}
|
||||
}
|
||||
|
||||
Value* 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);
|
||||
return StringValue(strdup(bv ? "" : "t"));
|
||||
}
|
||||
|
||||
Value* 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 StringValue(result);
|
||||
}
|
||||
|
||||
Value* 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 StringValue(result);
|
||||
}
|
||||
|
||||
Value* 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 StringValue(result);
|
||||
}
|
||||
|
||||
Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
Value* left = EvaluateValue(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
FreeValue(left);
|
||||
return EvaluateValue(state, argv[1]);
|
||||
}
|
||||
|
||||
Value* 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 StringValue(strdup(result ? "t" : ""));
|
||||
}
|
||||
|
||||
Value* 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);
|
||||
}
|
||||
|
||||
Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return StringValue(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]);
|
||||
}
|
||||
free(args);
|
||||
return -1;
|
||||
}
|
||||
*(va_arg(v, char**)) = args[i];
|
||||
}
|
||||
va_end(v);
|
||||
free(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' Value* (the ... is
|
||||
// zero or more Value** to put them in). If any expression evaluates
|
||||
// to NULL, free the rest and return -1. Return 0 on success.
|
||||
int ReadValueArgs(State* state, Expr* argv[], int count, ...) {
|
||||
Value** args = malloc(count * sizeof(Value*));
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
args[i] = EvaluateValue(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
va_end(v);
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
FreeValue(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return -1;
|
||||
}
|
||||
*(va_arg(v, Value**)) = args[i];
|
||||
}
|
||||
va_end(v);
|
||||
free(args);
|
||||
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;
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of Value*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// Values it contains.
|
||||
Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) {
|
||||
Value** args = (Value**)malloc(argc * sizeof(Value*));
|
||||
int i = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
args[i] = EvaluateValue(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
FreeValue(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns NULL.
|
||||
Value* 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;
|
||||
}
|
163
edify/expr.h
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 <unistd.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;
|
||||
|
||||
#define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null
|
||||
#define VAL_BLOB 2
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
ssize_t size;
|
||||
char* data;
|
||||
} Value;
|
||||
|
||||
typedef Value* (*Function)(const char* name, State* state,
|
||||
int argc, Expr* argv[]);
|
||||
|
||||
struct Expr {
|
||||
Function fn;
|
||||
char* name;
|
||||
int argc;
|
||||
Expr** argv;
|
||||
int start, end;
|
||||
};
|
||||
|
||||
// Take one of the Expr*s passed to the function as an argument,
|
||||
// evaluate it, return the resulting Value. The caller takes
|
||||
// ownership of the returned Value.
|
||||
Value* EvaluateValue(State* state, Expr* expr);
|
||||
|
||||
// Take one of the Expr*s passed to the function as an argument,
|
||||
// evaluate it, assert that it is a string, and return the resulting
|
||||
// char*. The caller takes ownership of the returned char*. This is
|
||||
// a convenience function for older functions that want to deal only
|
||||
// with strings.
|
||||
char* Evaluate(State* state, Expr* expr);
|
||||
|
||||
// Glue to make an Expr out of a literal.
|
||||
Value* 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.)
|
||||
Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* 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().
|
||||
Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* 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, giving 'count' Value* (the ... is
|
||||
// zero or more Value** to put them in). If any expression evaluates
|
||||
// to NULL, free the rest and return -1. Return 0 on success.
|
||||
int ReadValueArgs(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[]);
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of Value*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// Values it contains.
|
||||
Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]);
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns NULL.
|
||||
Value* ErrorAbort(State* state, char* format, ...);
|
||||
|
||||
// Wrap a string into a Value, taking ownership of the string.
|
||||
Value* StringValue(char* str);
|
||||
|
||||
// Free a Value object.
|
||||
void FreeValue(Value* v);
|
||||
|
||||
#endif // _EXPRESSION_H
|
112
edify/lexer.l
Normal file
@ -0,0 +1,112 @@
|
||||
%{
|
||||
/*
|
||||
* 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 "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;
|
218
edify/main.c
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 = strdup(expr_str);
|
||||
state.errmsg = NULL;
|
||||
|
||||
result = Evaluate(&state, e);
|
||||
free(state.errmsg);
|
||||
free(state.script);
|
||||
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");
|
||||
if (f == NULL) {
|
||||
printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
|
||||
return 1;
|
||||
}
|
||||
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;
|
||||
}
|
38
edify/yydefs.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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)
|
||||
|
||||
int yylex();
|
||||
|
||||
#endif
|
922
extendedcommands.c
Normal file
@ -0,0 +1,922 @@
|
||||
#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 "../../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);
|
||||
}
|
||||
|
||||
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", BOARD_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_unknown_device(const char* root)
|
||||
{
|
||||
// if this is SDEXT:, don't worry about it.
|
||||
if (0 == strcmp(root, "SDEXT:"))
|
||||
{
|
||||
struct stat st;
|
||||
if (0 != stat(BOARD_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_unknown_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",
|
||||
"2048M",
|
||||
"4096M",
|
||||
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;
|
||||
}
|
||||
char device[PATH_MAX];
|
||||
int ret = get_root_partition_device(root_path, device);
|
||||
if (ret == 0)
|
||||
{
|
||||
fprintf(file, "%s ", device);
|
||||
}
|
||||
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 BOARD_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_unknown_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...
|
||||
|
91
flashutils/Android.mk
Normal file
@ -0,0 +1,91 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := flashutils.c
|
||||
LOCAL_MODULE := libflashutils
|
||||
LOCAL_C_INCLUDES += bootable/recovery
|
||||
LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := flash_image.c
|
||||
LOCAL_MODULE := flash_image
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
#LOCAL_STATIC_LIBRARIES += $(BOARD_FLASH_LIBRARY)
|
||||
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
|
||||
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 := libflashutils libmtdutils libmmcutils libbmlutils
|
||||
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 := libflashutils libmtdutils libmmcutils libbmlutils
|
||||
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 := libflashutils libmtdutils libmmcutils libbmlutils 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 := libflashutils libmtdutils libmmcutils libbmlutils 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 := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
150
flashutils/dump_image.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 "flashutils.h"
|
||||
|
||||
#ifdef LOG_TAG
|
||||
#undef LOG_TAG
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return backup_raw_partition(argv[1], argv[2]);
|
||||
}
|
103
flashutils/erase_image.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 "cutils/log.h"
|
||||
#include "flashutils.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#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]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s partition\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return erase_raw_partition(argv[1]);
|
||||
}
|
@ -22,8 +22,8 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cutils/log.h"
|
||||
#include "mtdutils.h"
|
||||
|
||||
#if 0
|
||||
#define LOG_TAG "flash_image"
|
||||
|
||||
#define HEADER_SIZE 2048 // size of header to compare for equality
|
||||
@ -138,3 +138,14 @@ int main(int argc, char **argv) {
|
||||
if (mtd_write_close(out)) die("error closing %s", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return restore_raw_partition(argv[1], argv[2]);
|
||||
}
|
168
flashutils/flashutils.c
Normal file
@ -0,0 +1,168 @@
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "flashutils/flashutils.h"
|
||||
|
||||
enum flash_type {
|
||||
UNSUPPORTED = -1,
|
||||
UNKNOWN = 0,
|
||||
MTD = 1,
|
||||
MMC = 2,
|
||||
BML = 3
|
||||
};
|
||||
|
||||
int the_flash_type = UNKNOWN;
|
||||
|
||||
int device_flash_type()
|
||||
{
|
||||
if (the_flash_type == UNKNOWN) {
|
||||
if (access("/dev/block/bml1", F_OK) == 0) {
|
||||
the_flash_type = BML;
|
||||
} else if (access("/proc/emmc", F_OK) == 0) {
|
||||
the_flash_type = MMC;
|
||||
} else if (access("/proc/mtd", F_OK) == 0) {
|
||||
the_flash_type = MTD;
|
||||
} else {
|
||||
the_flash_type = UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
return the_flash_type;
|
||||
}
|
||||
|
||||
char* get_default_filesystem()
|
||||
{
|
||||
return device_flash_type() == MMC ? "ext3" : "yaffs2";
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
int restore_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
int type = device_flash_type();
|
||||
switch (type) {
|
||||
case MTD:
|
||||
return cmd_mtd_restore_raw_partition(partition, filename);
|
||||
case MMC:
|
||||
return cmd_mmc_restore_raw_partition(partition, filename);
|
||||
case BML:
|
||||
return cmd_bml_restore_raw_partition(partition, filename);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int backup_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
int type = device_flash_type();
|
||||
switch (type) {
|
||||
case MTD:
|
||||
return cmd_mtd_backup_raw_partition(partition, filename);
|
||||
case MMC:
|
||||
return cmd_mmc_backup_raw_partition(partition, filename);
|
||||
case BML:
|
||||
return cmd_bml_backup_raw_partition(partition, filename);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int erase_raw_partition(const char *partition)
|
||||
{
|
||||
int type = device_flash_type();
|
||||
switch (type) {
|
||||
case MTD:
|
||||
return cmd_mtd_erase_raw_partition(partition);
|
||||
case MMC:
|
||||
return cmd_mmc_erase_raw_partition(partition);
|
||||
case BML:
|
||||
return cmd_bml_erase_raw_partition(partition);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int erase_partition(const char *partition, const char *filesystem)
|
||||
{
|
||||
int type = device_flash_type();
|
||||
switch (type) {
|
||||
case MTD:
|
||||
return cmd_mtd_erase_partition(partition, filesystem);
|
||||
case MMC:
|
||||
return cmd_mmc_erase_partition(partition, filesystem);
|
||||
case BML:
|
||||
return cmd_bml_erase_partition(partition, filesystem);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
|
||||
{
|
||||
int type = device_flash_type();
|
||||
switch (type) {
|
||||
case MTD:
|
||||
return cmd_mtd_mount_partition(partition, mount_point, filesystem, read_only);
|
||||
case MMC:
|
||||
return cmd_mmc_mount_partition(partition, mount_point, filesystem, read_only);
|
||||
case BML:
|
||||
return cmd_bml_mount_partition(partition, mount_point, filesystem, read_only);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int get_partition_device(const char *partition, char *device)
|
||||
{
|
||||
int type = device_flash_type();
|
||||
switch (type) {
|
||||
case MTD:
|
||||
return cmd_mtd_get_partition_device(partition, device);
|
||||
case MMC:
|
||||
return cmd_mmc_get_partition_device(partition, device);
|
||||
case BML:
|
||||
return cmd_bml_get_partition_device(partition, device);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
38
flashutils/flashutils.h
Normal file
@ -0,0 +1,38 @@
|
||||
int restore_raw_partition(const char *partition, const char *filename);
|
||||
int backup_raw_partition(const char *partition, const char *filename);
|
||||
int erase_raw_partition(const char *partition);
|
||||
int erase_partition(const char *partition, const char *filesystem);
|
||||
int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
|
||||
int get_partition_device(const char *partition, char *device);
|
||||
|
||||
#define FLASH_MTD 0
|
||||
#define FLASH_MMC 1
|
||||
#define FLASH_BML 2
|
||||
|
||||
int is_mtd_device();
|
||||
char* get_default_filesystem();
|
||||
|
||||
int __system(const char *command);
|
||||
|
||||
extern int cmd_mtd_restore_raw_partition(const char *partition, const char *filename);
|
||||
extern int cmd_mtd_backup_raw_partition(const char *partition, const char *filename);
|
||||
extern int cmd_mtd_erase_raw_partition(const char *partition);
|
||||
extern int cmd_mtd_erase_partition(const char *partition, const char *filesystem);
|
||||
extern int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
|
||||
extern int cmd_mtd_get_partition_device(const char *partition, char *device);
|
||||
|
||||
extern int cmd_mmc_restore_raw_partition(const char *partition, const char *filename);
|
||||
extern int cmd_mmc_backup_raw_partition(const char *partition, const char *filename);
|
||||
extern int cmd_mmc_erase_raw_partition(const char *partition);
|
||||
extern int cmd_mmc_erase_partition(const char *partition, const char *filesystem);
|
||||
extern int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
|
||||
extern int cmd_mmc_get_partition_device(const char *partition, char *device);
|
||||
|
||||
extern int cmd_bml_restore_raw_partition(const char *partition, const char *filename);
|
||||
extern int cmd_bml_backup_raw_partition(const char *partition, const char *filename);
|
||||
extern int cmd_bml_erase_raw_partition(const char *partition);
|
||||
extern int cmd_bml_erase_partition(const char *partition, const char *filesystem);
|
||||
extern int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
|
||||
extern int cmd_bml_get_partition_device(const char *partition, char *device);
|
||||
|
||||
|
415
install.c
@ -14,137 +14,336 @@
|
||||
* 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"
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/SysUtil.h"
|
||||
#include "minzip/Zip.h"
|
||||
#include "mtdutils/mounts.h"
|
||||
#include "mounts.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "roots.h"
|
||||
#include "verifier.h"
|
||||
|
||||
/* List of public keys */
|
||||
static const RSAPublicKey keys[] = {
|
||||
#include "keys.inc"
|
||||
};
|
||||
#include "firmware.h"
|
||||
#include "legacy.h"
|
||||
|
||||
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
|
||||
#include "extendedcommands.h"
|
||||
|
||||
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.)
|
||||
//
|
||||
// (API v3: this command no longer exists.)
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
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 , 0x%x , { %u",
|
||||
&(key->len), &(key->n0inv), &(key->n[0])) != 3) {
|
||||
goto exit;
|
||||
}
|
||||
if (key->len != RSANUMWORDS) {
|
||||
LOGE("key length (%d) does not match expected size\n", key->len);
|
||||
goto exit;
|
||||
}
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
|
||||
}
|
||||
if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
|
||||
}
|
||||
fscanf(f, " } } ");
|
||||
|
||||
// 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 +368,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
|
123
legacy.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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/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 "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);
|
@ -7,6 +7,10 @@ LOCAL_C_INCLUDES +=\
|
||||
external/libpng\
|
||||
external/zlib
|
||||
|
||||
ifneq ($(BOARD_LDPI_RECOVERY),)
|
||||
LOCAL_CFLAGS += -DBOARD_LDPI_RECOVERY='"$(BOARD_LDPI_RECOVERY)"'
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := libminui
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
283
minui/events.c
@ -19,16 +19,161 @@
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/poll.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#include "minui.h"
|
||||
|
||||
#define MAX_DEVICES 16
|
||||
|
||||
#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable"
|
||||
#define VIBRATOR_TIME_MS 50
|
||||
|
||||
#define PRESS_THRESHHOLD 10
|
||||
|
||||
struct virtualkey {
|
||||
int scancode;
|
||||
int centerx, centery;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
struct position {
|
||||
int x, y;
|
||||
int pressed;
|
||||
struct input_absinfo xi, yi;
|
||||
};
|
||||
|
||||
struct ev {
|
||||
struct pollfd *fd;
|
||||
|
||||
struct virtualkey *vks;
|
||||
int vk_count;
|
||||
|
||||
struct position p, mt_p;
|
||||
int sent, mt_idx;
|
||||
};
|
||||
|
||||
static struct pollfd ev_fds[MAX_DEVICES];
|
||||
static struct ev evs[MAX_DEVICES];
|
||||
static unsigned ev_count = 0;
|
||||
|
||||
static inline int ABS(int x) {
|
||||
return x<0?-x:x;
|
||||
}
|
||||
|
||||
int vibrate(int timeout_ms)
|
||||
{
|
||||
char str[20];
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
ret = snprintf(str, sizeof(str), "%d", timeout_ms);
|
||||
ret = write(fd, str, ret);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns empty tokens */
|
||||
static char *vk_strtok_r(char *str, const char *delim, char **save_str)
|
||||
{
|
||||
if(!str) {
|
||||
if(!*save_str) return NULL;
|
||||
str = (*save_str) + 1;
|
||||
}
|
||||
*save_str = strpbrk(str, delim);
|
||||
if(*save_str) **save_str = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static int vk_init(struct ev *e)
|
||||
{
|
||||
char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys.";
|
||||
char vks[2048], *ts;
|
||||
ssize_t len;
|
||||
int vk_fd;
|
||||
int i;
|
||||
|
||||
e->vk_count = 0;
|
||||
|
||||
len = strlen(vk_path);
|
||||
len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(vk_path) - len), vk_path + len);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
|
||||
vk_fd = open(vk_path, O_RDONLY);
|
||||
if (vk_fd < 0)
|
||||
return -1;
|
||||
|
||||
len = read(vk_fd, vks, sizeof(vks)-1);
|
||||
close(vk_fd);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
|
||||
vks[len] = '\0';
|
||||
|
||||
/* Parse a line like:
|
||||
keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:...
|
||||
*/
|
||||
for (ts = vks, e->vk_count = 1; *ts; ++ts) {
|
||||
if (*ts == ':')
|
||||
++e->vk_count;
|
||||
}
|
||||
|
||||
if (e->vk_count % 6) {
|
||||
LOGW("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6);
|
||||
}
|
||||
e->vk_count /= 6;
|
||||
if (e->vk_count <= 0)
|
||||
return -1;
|
||||
|
||||
e->sent = 0;
|
||||
e->mt_idx = 0;
|
||||
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi);
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi);
|
||||
e->p.pressed = 0;
|
||||
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi);
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi);
|
||||
e->mt_p.pressed = 0;
|
||||
|
||||
e->vks = malloc(sizeof(*e->vks) * e->vk_count);
|
||||
|
||||
for (i = 0; i < e->vk_count; ++i) {
|
||||
char *token[6];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 6; ++j) {
|
||||
token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts);
|
||||
}
|
||||
|
||||
if (strcmp(token[0], "0x01") != 0) {
|
||||
/* Java does string compare, so we do too. */
|
||||
LOGW("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
e->vks[i].scancode = strtol(token[1], NULL, 0);
|
||||
e->vks[i].centerx = strtol(token[2], NULL, 0);
|
||||
e->vks[i].centery = strtol(token[3], NULL, 0);
|
||||
e->vks[i].width = strtol(token[4], NULL, 0);
|
||||
e->vks[i].height = strtol(token[5], NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ev_init(void)
|
||||
{
|
||||
DIR *dir;
|
||||
@ -45,6 +190,11 @@ int ev_init(void)
|
||||
|
||||
ev_fds[ev_count].fd = fd;
|
||||
ev_fds[ev_count].events = POLLIN;
|
||||
evs[ev_count].fd = &ev_fds[ev_count];
|
||||
|
||||
/* Load virtualkeys if there are any */
|
||||
vk_init(&evs[ev_count]);
|
||||
|
||||
ev_count++;
|
||||
if(ev_count == MAX_DEVICES) break;
|
||||
}
|
||||
@ -55,11 +205,135 @@ int ev_init(void)
|
||||
|
||||
void ev_exit(void)
|
||||
{
|
||||
while (ev_count > 0) {
|
||||
close(ev_fds[--ev_count].fd);
|
||||
while (ev_count-- > 0) {
|
||||
if (evs[ev_count].vk_count) {
|
||||
free(evs[ev_count].vks);
|
||||
evs[ev_count].vk_count = 0;
|
||||
}
|
||||
close(ev_fds[ev_count].fd);
|
||||
}
|
||||
}
|
||||
|
||||
static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size)
|
||||
{
|
||||
int screen_pos;
|
||||
|
||||
if (info->minimum == info->maximum)
|
||||
return 0;
|
||||
|
||||
screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum);
|
||||
return (screen_pos >= 0 && screen_pos < screen_size);
|
||||
}
|
||||
|
||||
static int vk_tp_to_screen(struct position *p, int *x, int *y)
|
||||
{
|
||||
if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum)
|
||||
return 0;
|
||||
|
||||
*x = (p->x - p->xi.minimum) * (gr_fb_width() - 1) / (p->xi.maximum - p->xi.minimum);
|
||||
*y = (p->y - p->yi.minimum) * (gr_fb_height() - 1) / (p->yi.maximum - p->yi.minimum);
|
||||
|
||||
if (*x >= 0 && *x < gr_fb_width() &&
|
||||
*y >= 0 && *y < gr_fb_height()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Translate a virtual key in to a real key event, if needed */
|
||||
/* Returns non-zero when the event should be consumed */
|
||||
static int vk_modify(struct ev *e, struct input_event *ev)
|
||||
{
|
||||
int i;
|
||||
int x, y;
|
||||
|
||||
if (ev->type == EV_KEY) {
|
||||
if (ev->code == BTN_TOUCH)
|
||||
e->p.pressed = ev->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ev->type == EV_ABS) {
|
||||
switch (ev->code) {
|
||||
case ABS_X:
|
||||
e->p.x = ev->value;
|
||||
return !vk_inside_display(e->p.x, &e->p.xi, gr_fb_width());
|
||||
case ABS_Y:
|
||||
e->p.y = ev->value;
|
||||
return !vk_inside_display(e->p.y, &e->p.yi, gr_fb_height());
|
||||
case ABS_MT_POSITION_X:
|
||||
if (e->mt_idx) return 1;
|
||||
e->mt_p.x = ev->value;
|
||||
return !vk_inside_display(e->mt_p.x, &e->mt_p.xi, gr_fb_width());
|
||||
case ABS_MT_POSITION_Y:
|
||||
if (e->mt_idx) return 1;
|
||||
e->mt_p.y = ev->value;
|
||||
return !vk_inside_display(e->mt_p.y, &e->mt_p.yi, gr_fb_height());
|
||||
case ABS_MT_TOUCH_MAJOR:
|
||||
if (e->mt_idx) return 1;
|
||||
if (e->sent)
|
||||
e->mt_p.pressed = (ev->value > 0);
|
||||
else
|
||||
e->mt_p.pressed = (ev->value > PRESS_THRESHHOLD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ev->type != EV_SYN)
|
||||
return 0;
|
||||
|
||||
if (ev->code == SYN_MT_REPORT) {
|
||||
/* Ignore the rest of the points */
|
||||
++e->mt_idx;
|
||||
return 1;
|
||||
}
|
||||
if (ev->code != SYN_REPORT)
|
||||
return 0;
|
||||
|
||||
/* Report complete */
|
||||
|
||||
e->mt_idx = 0;
|
||||
|
||||
if (!e->p.pressed && !e->mt_p.pressed) {
|
||||
/* No touch */
|
||||
e->sent = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(e->p.pressed && vk_tp_to_screen(&e->p, &x, &y)) &&
|
||||
!(e->mt_p.pressed && vk_tp_to_screen(&e->mt_p, &x, &y))) {
|
||||
/* No touch inside vk area */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (e->sent) {
|
||||
/* We've already sent a fake key for this touch */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The screen is being touched on the vk area */
|
||||
e->sent = 1;
|
||||
|
||||
for (i = 0; i < e->vk_count; ++i) {
|
||||
int xd = ABS(e->vks[i].centerx - x);
|
||||
int yd = ABS(e->vks[i].centery - y);
|
||||
if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) {
|
||||
/* Fake a key event */
|
||||
ev->type = EV_KEY;
|
||||
ev->code = e->vks[i].scancode;
|
||||
ev->value = 1;
|
||||
|
||||
vibrate(VIBRATOR_TIME_MS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ev_get(struct input_event *ev, unsigned dont_wait)
|
||||
{
|
||||
int r;
|
||||
@ -72,7 +346,10 @@ int ev_get(struct input_event *ev, unsigned dont_wait)
|
||||
for(n = 0; n < ev_count; n++) {
|
||||
if(ev_fds[n].revents & POLLIN) {
|
||||
r = read(ev_fds[n].fd, ev, sizeof(*ev));
|
||||
if(r == sizeof(*ev)) return 0;
|
||||
if(r == sizeof(*ev)) {
|
||||
if (!vk_modify(&evs[n], ev))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
minui/font_7x16.h
Normal file
@ -29,7 +29,12 @@
|
||||
|
||||
#include <pixelflinger/pixelflinger.h>
|
||||
|
||||
#include "font_10x18.h"
|
||||
#ifndef BOARD_LDPI_RECOVERY
|
||||
#include "font_10x18.h"
|
||||
#else
|
||||
#include "font_7x16.h"
|
||||
#endif
|
||||
|
||||
#include "minui.h"
|
||||
|
||||
typedef struct {
|
||||
@ -115,6 +120,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,85 @@
|
||||
|
||||
#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 && color_type == PNG_COLOR_TYPE_RGB) ||
|
||||
(channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
|
||||
(channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE)))) {
|
||||
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 +116,47 @@ 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;
|
||||
}
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
int y;
|
||||
if (channels == 3) {
|
||||
for (y = 0; y < (int)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 < (int)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) {
|
||||
|
75
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;
|
||||
@ -796,6 +810,43 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned char* buffer;
|
||||
long len;
|
||||
} BufferExtractCookie;
|
||||
|
||||
static bool bufferProcessFunction(const unsigned char *data, int dataLen,
|
||||
void *cookie) {
|
||||
BufferExtractCookie *bec = (BufferExtractCookie*)cookie;
|
||||
|
||||
memmove(bec->buffer, data, dataLen);
|
||||
bec->buffer += dataLen;
|
||||
bec->len -= dataLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uncompress "pEntry" in "pArchive" to buffer, which must be large
|
||||
* enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
|
||||
*/
|
||||
bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
|
||||
const ZipEntry *pEntry, unsigned char *buffer)
|
||||
{
|
||||
BufferExtractCookie bec;
|
||||
bec.buffer = buffer;
|
||||
bec.len = mzGetZipEntryUncompLen(pEntry);
|
||||
|
||||
bool ret = mzProcessZipEntryContents(pArchive, pEntry,
|
||||
bufferProcessFunction, (void*)&bec);
|
||||
if (!ret || bec.len != 0) {
|
||||
LOGE("Can't extract entry to memory buffer.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Helper state to make path translation easier and less malloc-happy.
|
||||
*/
|
||||
typedef struct {
|
||||
|
@ -168,6 +168,13 @@ bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry);
|
||||
bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
|
||||
const ZipEntry *pEntry, int fd);
|
||||
|
||||
/*
|
||||
* Inflate and write an entry to a memory buffer, which must be long
|
||||
* enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
|
||||
*/
|
||||
bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
|
||||
const ZipEntry *pEntry, unsigned char* buffer);
|
||||
|
||||
/*
|
||||
* Inflate all entries under zipDir to the directory specified by
|
||||
* targetDir, which must exist and be a writable directory.
|
||||
|
15
mmcutils/Android.mk
Normal file
@ -0,0 +1,15 @@
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
mmcutils.c
|
||||
|
||||
LOCAL_MODULE := libmmcutils
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
591
mmcutils/mmcutils.c
Normal file
@ -0,0 +1,591 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h> // for _IOW, _IOR, mount()
|
||||
|
||||
#include "mmcutils.h"
|
||||
|
||||
unsigned ext3_count = 0;
|
||||
char *ext3_partitions[] = {"system", "userdata", "cache", "NONE"};
|
||||
|
||||
unsigned vfat_count = 0;
|
||||
char *vfat_partitions[] = {"modem", "NONE"};
|
||||
|
||||
struct MmcPartition {
|
||||
char *device_index;
|
||||
char *filesystem;
|
||||
char *name;
|
||||
unsigned dstatus;
|
||||
unsigned dtype ;
|
||||
unsigned dfirstsec;
|
||||
unsigned dsize;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
MmcPartition *partitions;
|
||||
int partitions_allocd;
|
||||
int partition_count;
|
||||
} MmcState;
|
||||
|
||||
static MmcState g_mmc_state = {
|
||||
NULL, // partitions
|
||||
0, // partitions_allocd
|
||||
-1 // partition_count
|
||||
};
|
||||
|
||||
#define MMC_DEVICENAME "/dev/block/mmcblk0"
|
||||
|
||||
static void
|
||||
mmc_partition_name (MmcPartition *mbr, unsigned int type) {
|
||||
switch(type)
|
||||
{
|
||||
char name[64];
|
||||
case MMC_BOOT_TYPE:
|
||||
sprintf(name,"boot");
|
||||
mbr->name = strdup(name);
|
||||
break;
|
||||
case MMC_RECOVERY_TYPE:
|
||||
sprintf(name,"recovery");
|
||||
mbr->name = strdup(name);
|
||||
break;
|
||||
case MMC_EXT3_TYPE:
|
||||
if (strcmp("NONE", ext3_partitions[ext3_count])) {
|
||||
strcpy((char *)name,(const char *)ext3_partitions[ext3_count]);
|
||||
mbr->name = strdup(name);
|
||||
ext3_count++;
|
||||
}
|
||||
mbr->filesystem = strdup("ext3");
|
||||
break;
|
||||
case MMC_VFAT_TYPE:
|
||||
if (strcmp("NONE", vfat_partitions[vfat_count])) {
|
||||
strcpy((char *)name,(const char *)vfat_partitions[vfat_count]);
|
||||
mbr->name = strdup(name);
|
||||
vfat_count++;
|
||||
}
|
||||
mbr->filesystem = strdup("vfat");
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static int
|
||||
mmc_read_mbr (const char *device, MmcPartition *mbr) {
|
||||
FILE *fd;
|
||||
unsigned char buffer[512];
|
||||
int idx, i;
|
||||
unsigned mmc_partition_count = 0;
|
||||
unsigned int dtype;
|
||||
unsigned int dfirstsec;
|
||||
unsigned int EBR_first_sec;
|
||||
unsigned int EBR_current_sec;
|
||||
int ret = -1;
|
||||
|
||||
fd = fopen(device, "r");
|
||||
if(fd == NULL)
|
||||
{
|
||||
printf("Can't open device: \"%s\"\n", device);
|
||||
goto ERROR2;
|
||||
}
|
||||
if ((fread(buffer, 512, 1, fd)) != 1)
|
||||
{
|
||||
printf("Can't read device: \"%s\"\n", device);
|
||||
goto ERROR1;
|
||||
}
|
||||
/* Check to see if signature exists */
|
||||
if ((buffer[TABLE_SIGNATURE] != 0x55) || \
|
||||
(buffer[TABLE_SIGNATURE + 1] != 0xAA))
|
||||
{
|
||||
printf("Incorrect mbr signatures!\n");
|
||||
goto ERROR1;
|
||||
}
|
||||
idx = TABLE_ENTRY_0;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
char device_index[128];
|
||||
|
||||
mbr[mmc_partition_count].dstatus = \
|
||||
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
|
||||
mbr[mmc_partition_count].dtype = \
|
||||
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
|
||||
mbr[mmc_partition_count].dfirstsec = \
|
||||
GET_LWORD_FROM_BYTE(&buffer[idx + \
|
||||
i * TABLE_ENTRY_SIZE + \
|
||||
OFFSET_FIRST_SEC]);
|
||||
mbr[mmc_partition_count].dsize = \
|
||||
GET_LWORD_FROM_BYTE(&buffer[idx + \
|
||||
i * TABLE_ENTRY_SIZE + \
|
||||
OFFSET_SIZE]);
|
||||
dtype = mbr[mmc_partition_count].dtype;
|
||||
dfirstsec = mbr[mmc_partition_count].dfirstsec;
|
||||
mmc_partition_name(&mbr[mmc_partition_count], \
|
||||
mbr[mmc_partition_count].dtype);
|
||||
|
||||
sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
|
||||
mbr[mmc_partition_count].device_index = strdup(device_index);
|
||||
|
||||
mmc_partition_count++;
|
||||
if (mmc_partition_count == MAX_PARTITIONS)
|
||||
goto SUCCESS;
|
||||
}
|
||||
|
||||
/* See if the last partition is EBR, if not, parsing is done */
|
||||
if (dtype != 0x05)
|
||||
{
|
||||
goto SUCCESS;
|
||||
}
|
||||
|
||||
EBR_first_sec = dfirstsec;
|
||||
EBR_current_sec = dfirstsec;
|
||||
|
||||
fseek (fd, (EBR_first_sec * 512), SEEK_SET);
|
||||
if ((fread(buffer, 512, 1, fd)) != 1)
|
||||
goto ERROR1;
|
||||
|
||||
/* Loop to parse the EBR */
|
||||
for (i = 0;; i++)
|
||||
{
|
||||
char device_index[128];
|
||||
|
||||
if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA))
|
||||
{
|
||||
break;
|
||||
}
|
||||
mbr[mmc_partition_count].dstatus = \
|
||||
buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
|
||||
mbr[mmc_partition_count].dtype = \
|
||||
buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
|
||||
mbr[mmc_partition_count].dfirstsec = \
|
||||
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
|
||||
OFFSET_FIRST_SEC]) + \
|
||||
EBR_current_sec;
|
||||
mbr[mmc_partition_count].dsize = \
|
||||
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
|
||||
OFFSET_SIZE]);
|
||||
mmc_partition_name(&mbr[mmc_partition_count], \
|
||||
mbr[mmc_partition_count].dtype);
|
||||
|
||||
sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
|
||||
mbr[mmc_partition_count].device_index = strdup(device_index);
|
||||
|
||||
mmc_partition_count++;
|
||||
if (mmc_partition_count == MAX_PARTITIONS)
|
||||
goto SUCCESS;
|
||||
|
||||
dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
|
||||
if(dfirstsec == 0)
|
||||
{
|
||||
/* Getting to the end of the EBR tables */
|
||||
break;
|
||||
}
|
||||
/* More EBR to follow - read in the next EBR sector */
|
||||
fseek (fd, ((EBR_first_sec + dfirstsec) * 512), SEEK_SET);
|
||||
if ((fread(buffer, 512, 1, fd)) != 1)
|
||||
goto ERROR1;
|
||||
|
||||
EBR_current_sec = EBR_first_sec + dfirstsec;
|
||||
}
|
||||
|
||||
SUCCESS:
|
||||
ret = mmc_partition_count;
|
||||
ERROR1:
|
||||
fclose(fd);
|
||||
ERROR2:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
mmc_scan_partitions() {
|
||||
int i;
|
||||
ssize_t nbytes;
|
||||
|
||||
if (g_mmc_state.partitions == NULL) {
|
||||
const int nump = MAX_PARTITIONS;
|
||||
MmcPartition *partitions = malloc(nump * sizeof(*partitions));
|
||||
if (partitions == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
g_mmc_state.partitions = partitions;
|
||||
g_mmc_state.partitions_allocd = nump;
|
||||
memset(partitions, 0, nump * sizeof(*partitions));
|
||||
}
|
||||
g_mmc_state.partition_count = 0;
|
||||
ext3_count = 0;
|
||||
vfat_count = 0;
|
||||
|
||||
/* Initialize all of the entries to make things easier later.
|
||||
* (Lets us handle sparsely-numbered partitions, which
|
||||
* may not even be possible.)
|
||||
*/
|
||||
for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
|
||||
MmcPartition *p = &g_mmc_state.partitions[i];
|
||||
if (p->device_index != NULL) {
|
||||
free(p->device_index);
|
||||
p->device_index = NULL;
|
||||
}
|
||||
if (p->name != NULL) {
|
||||
free(p->name);
|
||||
p->name = NULL;
|
||||
}
|
||||
if (p->filesystem != NULL) {
|
||||
free(p->filesystem);
|
||||
p->filesystem = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
g_mmc_state.partition_count = mmc_read_mbr(MMC_DEVICENAME, g_mmc_state.partitions);
|
||||
if(g_mmc_state.partition_count == -1)
|
||||
{
|
||||
printf("Error in reading mbr!\n");
|
||||
// keep "partitions" around so we can free the names on a rescan.
|
||||
g_mmc_state.partition_count = -1;
|
||||
}
|
||||
return g_mmc_state.partition_count;
|
||||
}
|
||||
|
||||
const MmcPartition *
|
||||
mmc_find_partition_by_name(const char *name)
|
||||
{
|
||||
if (g_mmc_state.partitions != NULL) {
|
||||
int i;
|
||||
for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
|
||||
MmcPartition *p = &g_mmc_state.partitions[i];
|
||||
if (p->device_index !=NULL && p->name != NULL) {
|
||||
if (strcmp(p->name, name) == 0) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MKE2FS_BIN "/sbin/mke2fs"
|
||||
#define TUNE2FS_BIN "/sbin/tune2fs"
|
||||
#define E2FSCK_BIN "/sbin/e2fsck"
|
||||
|
||||
static int
|
||||
run_exec_process ( char **argv) {
|
||||
pid_t pid;
|
||||
int status;
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
execv(argv[0], argv);
|
||||
fprintf(stderr, "E:Can't run (%s)\n",strerror(errno));
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
waitpid(pid, &status, 0);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mmc_format_ext3 (MmcPartition *partition) {
|
||||
char device[128];
|
||||
strcpy(device, partition->device_index);
|
||||
// Run mke2fs
|
||||
char *const mke2fs[] = {MKE2FS_BIN, "-j", device, NULL};
|
||||
if(run_exec_process(mke2fs))
|
||||
return -1;
|
||||
|
||||
// Run tune2fs
|
||||
char *const tune2fs[] = {TUNE2FS_BIN, "-j", "-C", "1", device, NULL};
|
||||
if(run_exec_process(tune2fs))
|
||||
return -1;
|
||||
|
||||
// Run e2fsck
|
||||
char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL};
|
||||
if(run_exec_process(e2fsck))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mmc_mount_partition(const MmcPartition *partition, const char *mount_point,
|
||||
int read_only)
|
||||
{
|
||||
const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
|
||||
char devname[128];
|
||||
int rv = -1;
|
||||
strcpy(devname, partition->device_index);
|
||||
if (partition->filesystem == NULL) {
|
||||
printf("Null filesystem!\n");
|
||||
return rv;
|
||||
}
|
||||
if (!read_only) {
|
||||
rv = mount(devname, mount_point, partition->filesystem, flags, NULL);
|
||||
}
|
||||
if (read_only || rv < 0) {
|
||||
rv = mount(devname, mount_point, partition->filesystem, flags | MS_RDONLY, 0);
|
||||
if (rv < 0) {
|
||||
printf("Failed to mount %s on %s: %s\n",
|
||||
devname, mount_point, strerror(errno));
|
||||
} else {
|
||||
printf("Mount %s on %s read-only\n", devname, mount_point);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
mmc_raw_copy (const MmcPartition *partition, char *in_file) {
|
||||
int ch;
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
int val = 0;
|
||||
char buf[512];
|
||||
unsigned sz = 0;
|
||||
unsigned i;
|
||||
int ret = -1;
|
||||
char *out_file = partition->device_index;
|
||||
|
||||
in = fopen ( in_file, "r" );
|
||||
if (in == NULL)
|
||||
goto ERROR3;
|
||||
|
||||
out = fopen ( out_file, "w" );
|
||||
if (out == NULL)
|
||||
goto ERROR2;
|
||||
|
||||
fseek(in, 0L, SEEK_END);
|
||||
sz = ftell(in);
|
||||
fseek(in, 0L, SEEK_SET);
|
||||
|
||||
if (sz % 512)
|
||||
{
|
||||
while ( ( ch = fgetc ( in ) ) != EOF )
|
||||
fputc ( ch, out );
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i< (sz/512); i++)
|
||||
{
|
||||
if ((fread(buf, 512, 1, in)) != 1)
|
||||
goto ERROR1;
|
||||
if ((fwrite(buf, 512, 1, out)) != 1)
|
||||
goto ERROR1;
|
||||
}
|
||||
}
|
||||
|
||||
fsync(out);
|
||||
ret = 0;
|
||||
ERROR1:
|
||||
fclose ( out );
|
||||
ERROR2:
|
||||
fclose ( in );
|
||||
ERROR3:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TODO: refactor this to not be a giant copy paste mess
|
||||
int
|
||||
mmc_raw_dump (const MmcPartition *partition, char *out_file) {
|
||||
int ch;
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
int val = 0;
|
||||
char buf[512];
|
||||
unsigned sz = 0;
|
||||
unsigned i;
|
||||
int ret = -1;
|
||||
char *in_file = partition->device_index;
|
||||
|
||||
in = fopen ( in_file, "r" );
|
||||
if (in == NULL)
|
||||
goto ERROR3;
|
||||
|
||||
out = fopen ( out_file, "w" );
|
||||
if (out == NULL)
|
||||
goto ERROR2;
|
||||
|
||||
fseek(in, 0L, SEEK_END);
|
||||
sz = ftell(in);
|
||||
fseek(in, 0L, SEEK_SET);
|
||||
|
||||
if (sz % 512)
|
||||
{
|
||||
while ( ( ch = fgetc ( in ) ) != EOF )
|
||||
fputc ( ch, out );
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i< (sz/512); i++)
|
||||
{
|
||||
if ((fread(buf, 512, 1, in)) != 1)
|
||||
goto ERROR1;
|
||||
if ((fwrite(buf, 512, 1, out)) != 1)
|
||||
goto ERROR1;
|
||||
}
|
||||
}
|
||||
|
||||
fsync(out);
|
||||
ret = 0;
|
||||
ERROR1:
|
||||
fclose ( out );
|
||||
ERROR2:
|
||||
fclose ( in );
|
||||
ERROR3:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mmc_raw_read (const MmcPartition *partition, char *data, int data_size) {
|
||||
int ch;
|
||||
FILE *in;
|
||||
int val = 0;
|
||||
char buf[512];
|
||||
unsigned sz = 0;
|
||||
unsigned i;
|
||||
int ret = -1;
|
||||
char *in_file = partition->device_index;
|
||||
|
||||
in = fopen ( in_file, "r" );
|
||||
if (in == NULL)
|
||||
goto ERROR3;
|
||||
|
||||
fseek(in, 0L, SEEK_END);
|
||||
sz = ftell(in);
|
||||
fseek(in, 0L, SEEK_SET);
|
||||
|
||||
fread(data, data_size, 1, in);
|
||||
|
||||
ret = 0;
|
||||
ERROR1:
|
||||
ERROR2:
|
||||
fclose ( in );
|
||||
ERROR3:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
mmc_raw_write (const MmcPartition *partition, char *data, int data_size) {
|
||||
int ch;
|
||||
FILE *out;
|
||||
int val = 0;
|
||||
char buf[512];
|
||||
unsigned sz = 0;
|
||||
unsigned i;
|
||||
int ret = -1;
|
||||
char *out_file = partition->device_index;
|
||||
|
||||
out = fopen ( out_file, "w" );
|
||||
if (out == NULL)
|
||||
goto ERROR3;
|
||||
|
||||
fwrite(data, data_size, 1, out);
|
||||
|
||||
ret = 0;
|
||||
ERROR1:
|
||||
ERROR2:
|
||||
fclose ( out );
|
||||
ERROR3:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int cmd_mmc_restore_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
mmc_scan_partitions();
|
||||
const MmcPartition *p;
|
||||
p = mmc_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
return mmc_raw_copy(p, filename);
|
||||
}
|
||||
|
||||
int cmd_mmc_backup_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
mmc_scan_partitions();
|
||||
const MmcPartition *p;
|
||||
p = mmc_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
return mmc_raw_dump(p, filename);
|
||||
}
|
||||
|
||||
int cmd_mmc_erase_raw_partition(const char *partition)
|
||||
{
|
||||
mmc_scan_partitions();
|
||||
const MmcPartition *p;
|
||||
p = mmc_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
|
||||
// TODO: implement raw wipe
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_mmc_erase_partition(const char *partition, const char *filesystem)
|
||||
{
|
||||
mmc_scan_partitions();
|
||||
const MmcPartition *p;
|
||||
p = mmc_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
return mmc_format_ext3 (p);
|
||||
}
|
||||
|
||||
int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
|
||||
{
|
||||
mmc_scan_partitions();
|
||||
const MmcPartition *p;
|
||||
p = mmc_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
return mmc_mount_partition(p, mount_point, read_only);
|
||||
}
|
||||
|
||||
int cmd_mmc_get_partition_device(const char *partition, char *device)
|
||||
{
|
||||
mmc_scan_partitions();
|
||||
const MmcPartition *p;
|
||||
p = mmc_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
strcpy(device, p->device_index);
|
||||
return 0;
|
||||
}
|
88
mmcutils/mmcutils.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MMCUTILS_H_
|
||||
#define MMCUTILS_H_
|
||||
|
||||
/* Some useful define used to access the MBR/EBR table */
|
||||
#define BLOCK_SIZE 0x200
|
||||
#define TABLE_ENTRY_0 0x1BE
|
||||
#define TABLE_ENTRY_1 0x1CE
|
||||
#define TABLE_ENTRY_2 0x1DE
|
||||
#define TABLE_ENTRY_3 0x1EE
|
||||
#define TABLE_SIGNATURE 0x1FE
|
||||
#define TABLE_ENTRY_SIZE 0x010
|
||||
|
||||
#define OFFSET_STATUS 0x00
|
||||
#define OFFSET_TYPE 0x04
|
||||
#define OFFSET_FIRST_SEC 0x08
|
||||
#define OFFSET_SIZE 0x0C
|
||||
#define COPYBUFF_SIZE (1024 * 16)
|
||||
#define BINARY_IN_TABLE_SIZE (16 * 512)
|
||||
#define MAX_FILE_ENTRIES 20
|
||||
|
||||
#define MMC_BOOT_TYPE 0x48
|
||||
#define MMC_SYSTEM_TYPE 0x82
|
||||
#define MMC_USERDATA_TYPE 0x83
|
||||
#define MMC_RECOVERY_TYPE 0x71
|
||||
|
||||
#define MMC_RCA 2
|
||||
|
||||
#define MAX_PARTITIONS 64
|
||||
|
||||
#define GET_LWORD_FROM_BYTE(x) ((unsigned)*(x) | \
|
||||
((unsigned)*((x)+1) << 8) | \
|
||||
((unsigned)*((x)+2) << 16) | \
|
||||
((unsigned)*((x)+3) << 24))
|
||||
|
||||
#define PUT_LWORD_TO_BYTE(x, y) do{*(x) = (y) & 0xff; \
|
||||
*((x)+1) = ((y) >> 8) & 0xff; \
|
||||
*((x)+2) = ((y) >> 16) & 0xff; \
|
||||
*((x)+3) = ((y) >> 24) & 0xff; }while(0)
|
||||
|
||||
#define GET_PAR_NUM_FROM_POS(x) ((((x) & 0x0000FF00) >> 8) + ((x) & 0x000000FF))
|
||||
|
||||
#define MMC_BOOT_TYPE 0x48
|
||||
#define MMC_EXT3_TYPE 0x83
|
||||
#define MMC_VFAT_TYPE 0xC
|
||||
typedef struct MmcPartition MmcPartition;
|
||||
|
||||
/* Functions */
|
||||
int mmc_scan_partitions();
|
||||
const MmcPartition *mmc_find_partition_by_name(const char *name);
|
||||
int mmc_format_ext3 (MmcPartition *partition);
|
||||
int mmc_mount_partition(const MmcPartition *partition, const char *mount_point, \
|
||||
int read_only);
|
||||
int mmc_raw_copy (const MmcPartition *partition, char *in_file);
|
||||
int mmc_raw_read (const MmcPartition *partition, char *data, int data_size);
|
||||
int mmc_raw_write (const MmcPartition *partition, char *data, int data_size);
|
||||
|
||||
#endif // MMCUTILS_H_
|
||||
|
||||
|
@ -5,19 +5,12 @@ LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
mtdutils.c \
|
||||
mounts.c
|
||||
mtdutils.c
|
||||
|
||||
LOCAL_MODULE := libmtdutils
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := flash_image.c
|
||||
LOCAL_MODULE := flash_image
|
||||
LOCAL_STATIC_LIBRARIES := libmtdutils
|
||||
LOCAL_SHARED_LIBRARIES := libcutils libc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
@ -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 >= (int)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,315 @@ 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;
|
||||
}
|
||||
|
||||
#define BLOCK_SIZE 2048
|
||||
#define SPARE_SIZE (BLOCK_SIZE >> 5)
|
||||
#define HEADER_SIZE 2048
|
||||
|
||||
int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
|
||||
{
|
||||
const MtdPartition *ptn;
|
||||
MtdWriteContext *write;
|
||||
void *data;
|
||||
unsigned sz;
|
||||
|
||||
if (mtd_scan_partitions() <= 0)
|
||||
{
|
||||
printf("error scanning partitions");
|
||||
return -1;
|
||||
}
|
||||
const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
|
||||
if (partition == NULL)
|
||||
{
|
||||
printf("can't find %s partition", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the first part of the file matches the partition, skip writing
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
printf("error opening %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char header[HEADER_SIZE];
|
||||
int headerlen = read(fd, header, sizeof(header));
|
||||
if (headerlen <= 0)
|
||||
{
|
||||
printf("error reading %s header", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MtdReadContext *in = mtd_read_partition(partition);
|
||||
if (in == NULL) {
|
||||
printf("error opening %s: %s\n", partition, strerror(errno));
|
||||
// just assume it needs re-writing
|
||||
} else {
|
||||
char check[HEADER_SIZE];
|
||||
int checklen = mtd_read_data(in, check, sizeof(check));
|
||||
if (checklen <= 0) {
|
||||
printf("error reading %s: %s\n", partition_name, strerror(errno));
|
||||
// just assume it needs re-writing
|
||||
} else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
|
||||
printf("header is the same, not flashing %s\n", partition_name);
|
||||
return 0;
|
||||
}
|
||||
mtd_read_close(in);
|
||||
}
|
||||
|
||||
// Skip the header (we'll come back to it), write everything else
|
||||
printf("flashing %s from %s\n", partition_name, filename);
|
||||
|
||||
MtdWriteContext *out = mtd_write_partition(partition);
|
||||
if (out == NULL)
|
||||
{
|
||||
printf("error writing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[HEADER_SIZE];
|
||||
memset(buf, 0, headerlen);
|
||||
int wrote = mtd_write_data(out, buf, headerlen);
|
||||
if (wrote != headerlen)
|
||||
{
|
||||
printf("error writing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int len;
|
||||
while ((len = read(fd, buf, sizeof(buf))) > 0) {
|
||||
wrote = mtd_write_data(out, buf, len);
|
||||
if (wrote != len)
|
||||
{
|
||||
printf("error writing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (len < 0)
|
||||
{
|
||||
printf("error reading %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mtd_write_close(out))
|
||||
{
|
||||
printf("error closing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now come back and write the header last
|
||||
|
||||
out = mtd_write_partition(partition);
|
||||
if (out == NULL)
|
||||
{
|
||||
printf("error re-opening %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wrote = mtd_write_data(out, header, headerlen);
|
||||
if (wrote != headerlen)
|
||||
{
|
||||
printf("error re-writing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Need to write a complete block, so write the rest of the first block
|
||||
size_t block_size;
|
||||
if (mtd_partition_info(partition, NULL, &block_size, NULL))
|
||||
{
|
||||
printf("error getting %s block size", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lseek(fd, headerlen, SEEK_SET) != headerlen)
|
||||
{
|
||||
printf("error rewinding %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int left = block_size - headerlen;
|
||||
while (left < 0) left += block_size;
|
||||
while (left > 0) {
|
||||
len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
|
||||
if (len <= 0){
|
||||
printf("error reading %s", filename);
|
||||
return -1;
|
||||
}
|
||||
if (mtd_write_data(out, buf, len) != len)
|
||||
{
|
||||
printf("error writing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
left -= len;
|
||||
}
|
||||
|
||||
if (mtd_write_close(out))
|
||||
{
|
||||
printf("error closing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
|
||||
{
|
||||
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)
|
||||
{
|
||||
printf("error scanning partitions");
|
||||
return -1;
|
||||
}
|
||||
|
||||
partition = mtd_find_partition_by_name(partition_name);
|
||||
if (partition == NULL)
|
||||
{
|
||||
printf("can't find %s partition", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
|
||||
printf("can't get info of partition %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(filename, "-")) {
|
||||
fd = fileno(stdout);
|
||||
}
|
||||
else {
|
||||
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
printf("error opening %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
in = mtd_read_partition(partition);
|
||||
if (in == NULL) {
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
printf("error opening %s: %s\n", partition_name, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
|
||||
wrote = write(fd, buf, len);
|
||||
if (wrote != len) {
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
printf("error writing %s", filename);
|
||||
return -1;
|
||||
}
|
||||
total += BLOCK_SIZE;
|
||||
}
|
||||
|
||||
mtd_read_close(in);
|
||||
|
||||
if (close(fd)) {
|
||||
unlink(filename);
|
||||
printf("error closing %s", filename);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_mtd_erase_raw_partition(const char *partition_name)
|
||||
{
|
||||
MtdWriteContext *out;
|
||||
size_t erased;
|
||||
size_t total_size;
|
||||
size_t erase_size;
|
||||
|
||||
if (mtd_scan_partitions() <= 0)
|
||||
{
|
||||
printf("error scanning partitions");
|
||||
return -1;
|
||||
}
|
||||
const MtdPartition *p = mtd_find_partition_by_name(partition_name);
|
||||
if (p == NULL)
|
||||
{
|
||||
printf("can't find %s partition", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
out = mtd_write_partition(p);
|
||||
if (out == NULL)
|
||||
{
|
||||
printf("could not estabilish write context for %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// do the actual erase, -1 = full partition erase
|
||||
erased = mtd_erase_blocks(out, -1);
|
||||
|
||||
// erased = bytes erased, if zero, something borked
|
||||
if (!erased)
|
||||
{
|
||||
printf("error erasing %s", partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
|
||||
{
|
||||
return cmd_mtd_erase_raw_partition(partition);
|
||||
}
|
||||
|
||||
|
||||
int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
|
||||
{
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition *p;
|
||||
p = mtd_find_partition_by_name(partition);
|
||||
if (p == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return mtd_mount_partition(p, mount_point, filesystem, read_only);
|
||||
}
|
||||
|
||||
int cmd_mtd_get_partition_device(const char *partition, char *device)
|
||||
{
|
||||
mtd_scan_partitions();
|
||||
MtdPartition *p = mtd_find_partition_by_name(partition);
|
||||
if (p == NULL)
|
||||
return -1;
|
||||
sprintf(device, "/dev/block/mtdblock%d", p->device_index);
|
||||
return 0;
|
||||
}
|
||||
|
@ -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 $?
|
367
nandroid.c
Normal file
@ -0,0 +1,367 @@
|
||||
#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 "../../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 = backup_raw_partition("boot", tmp);
|
||||
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 = backup_raw_partition("recovery", tmp);
|
||||
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 BOARD_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(BOARD_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, "%s/boot.img", backup_path);
|
||||
ui_print("Restoring boot image...\n");
|
||||
if (0 != (ret = restore_raw_partition("boot", 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 BOARD_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;
|
||||
}
|
430
recovery.c
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -27,24 +28,30 @@
|
||||
#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"
|
||||
#include "install.h"
|
||||
#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";
|
||||
@ -62,6 +69,7 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
|
||||
* --update_package=root:path - verify install an OTA package file
|
||||
* --wipe_data - erase user data (and cache), then reboot
|
||||
* --wipe_cache - wipe cache (but not user data), then reboot
|
||||
* --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
|
||||
*
|
||||
* After completing, we remove /cache/recovery/command and reboot.
|
||||
* Arguments may also be supplied in the bootloader control block (BCB).
|
||||
@ -106,6 +114,26 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
|
||||
* 8g. finish_recovery() erases BCB
|
||||
* -- after this, rebooting will (try to) restart the main system --
|
||||
* 9. main() calls reboot() to boot main system
|
||||
*
|
||||
* ENCRYPTED FILE SYSTEMS ENABLE/DISABLE
|
||||
* 1. user selects "enable encrypted file systems"
|
||||
* 2. main system writes "--set_encrypted_filesystem=on|off" to
|
||||
* /cache/recovery/command
|
||||
* 3. main system reboots into recovery
|
||||
* 4. get_args() writes BCB with "boot-recovery" and
|
||||
* "--set_encrypted_filesystems=on|off"
|
||||
* -- after this, rebooting will restart the transition --
|
||||
* 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data
|
||||
* Settings include: property to specify the Encrypted FS istatus and
|
||||
* FS encryption key if enabled (not yet implemented)
|
||||
* 6. erase_root() reformats /data
|
||||
* 7. erase_root() reformats /cache
|
||||
* 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data
|
||||
* Settings include: property to specify the Encrypted FS status and
|
||||
* FS encryption key if enabled (not yet implemented)
|
||||
* 9. finish_recovery() erases BCB
|
||||
* -- after this, rebooting will restart the main system --
|
||||
* 10. main() calls reboot() to boot main system
|
||||
*/
|
||||
|
||||
static const int MAX_ARG_LENGTH = 4096;
|
||||
@ -130,7 +158,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 +178,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 +190,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,21 +237,34 @@ 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
|
||||
// record any intent we were asked to communicate back to the system.
|
||||
// this function is idempotent: call it as many times as you like.
|
||||
static void
|
||||
finish_recovery(const char *send_intent)
|
||||
{
|
||||
finish_recovery(const char *send_intent) {
|
||||
// By this point, we're ready to return to the main system...
|
||||
if (send_intent != NULL) {
|
||||
FILE *fp = fopen_root_path(INTENT_FILE, "w");
|
||||
if (fp != NULL) {
|
||||
if (fp == NULL) {
|
||||
LOGE("Can't open %s\n", INTENT_FILE);
|
||||
} else {
|
||||
fputs(send_intent, fp);
|
||||
check_and_fclose(fp, INTENT_FILE);
|
||||
}
|
||||
@ -227,7 +272,9 @@ finish_recovery(const char *send_intent)
|
||||
|
||||
// Copy logs to cache so the system can find out what happened.
|
||||
FILE *log = fopen_root_path(LOG_FILE, "a");
|
||||
if (log != NULL) {
|
||||
if (log == NULL) {
|
||||
LOGE("Can't open %s\n", LOG_FILE);
|
||||
} else {
|
||||
FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
|
||||
if (tmplog == NULL) {
|
||||
LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
|
||||
@ -242,10 +289,12 @@ finish_recovery(const char *send_intent)
|
||||
check_and_fclose(log, LOG_FILE);
|
||||
}
|
||||
|
||||
// Reset the bootloader message to revert to a normal main system boot.
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
// Reset to mormal system boot so recovery won't cycle indefinitely.
|
||||
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,107 +307,195 @@ 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)
|
||||
{
|
||||
erase_root(const char *root) {
|
||||
ui_set_background(BACKGROUND_ICON_INSTALLING);
|
||||
ui_show_indeterminate_progress();
|
||||
ui_print("Formatting %s...\n", 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 BOARD_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,36 +503,68 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_property(const char *key, const char *name, void *cookie)
|
||||
{
|
||||
print_property(const char *key, const char *name, void *cookie) {
|
||||
fprintf(stderr, "%s=%s\n", key, name);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
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 +573,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;
|
||||
@ -425,6 +595,8 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
device_recovery_start();
|
||||
|
||||
fprintf(stderr, "Command:");
|
||||
for (arg = 0; arg < argc; arg++) {
|
||||
fprintf(stderr, " \"%s\"", argv[arg]);
|
||||
@ -434,33 +606,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 +665,7 @@ main(int argc, char **argv)
|
||||
reboot(RB_AUTOBOOT);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int get_allow_toggle_display() {
|
||||
return allow_display_toggle;
|
||||
}
|
||||
|
90
recovery_ui.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 when recovery starts up. Returns 0.
|
||||
extern int device_recovery_start();
|
||||
|
||||
// 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 |
Before Width: | Height: | Size: 89 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: 1.9 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate2.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate3.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate4.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate5.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
res/images/indeterminate6.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 294 B |
BIN
res/images/progress_empty.png
Normal file
After Width: | Height: | Size: 361 B |
BIN
res/images/progress_fill.png
Normal file
After Width: | Height: | Size: 286 B |
148
roots.c
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,44 +22,44 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "mtdutils/mounts.h"
|
||||
#include <limits.h>
|
||||
|
||||
#include "flashutils/flashutils.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 "mounts.h"
|
||||
#include "extendedcommands.h"
|
||||
|
||||
/* Canonical pointers.
|
||||
xxx may just want to use enums
|
||||
*/
|
||||
static const char g_mtd_device[] = "@\0g_mtd_device";
|
||||
static const char g_default_device[] = "@\0g_default_device";
|
||||
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_default_device, NULL, "boot", NULL, g_raw, NULL },
|
||||
{ "CACHE:", BOARD_CACHE_DEVICE, NULL, "cache", "/cache", BOARD_CACHE_FILESYSTEM, BOARD_CACHE_FILESYSTEM_OPTIONS },
|
||||
{ "DATA:", BOARD_DATA_DEVICE, NULL, "userdata", "/data", BOARD_DATA_FILESYSTEM, BOARD_DATA_FILESYSTEM_OPTIONS },
|
||||
#ifdef BOARD_HAS_DATADATA
|
||||
{ "DATADATA:", BOARD_DATADATA_DEVICE, NULL, "datadata", "/datadata", BOARD_DATADATA_FILESYSTEM, BOARD_DATADATA_FILESYSTEM_OPTIONS },
|
||||
#endif
|
||||
{ "MISC:", g_default_device, NULL, "misc", NULL, g_raw, NULL },
|
||||
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file, NULL },
|
||||
{ "RECOVERY:", g_default_device, NULL, "recovery", "/", g_raw, NULL },
|
||||
{ "SDCARD:", BOARD_SDCARD_DEVICE_PRIMARY, BOARD_SDCARD_DEVICE_SECONDARY, NULL, "/sdcard", "vfat", NULL },
|
||||
{ "SDEXT:", BOARD_SDEXT_DEVICE, NULL, NULL, "/sd-ext", BOARD_SDEXT_FILESYSTEM, NULL },
|
||||
{ "SYSTEM:", BOARD_SYSTEM_DEVICE, NULL, "system", "/system", BOARD_SYSTEM_FILESYSTEM, BOARD_SYSTEM_FILESYSTEM_OPTIONS },
|
||||
{ "MBM:", g_default_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 +208,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)
|
||||
{
|
||||
@ -224,21 +238,6 @@ ensure_root_path_mounted(const char *root_path)
|
||||
|
||||
/* It's not mounted.
|
||||
*/
|
||||
if (info->device == g_mtd_device) {
|
||||
if (info->partition_name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
//TODO: make the mtd stuff scan once when it needs to
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition *partition;
|
||||
partition = mtd_find_partition_by_name(info->partition_name);
|
||||
if (partition == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return mtd_mount_partition(partition, info->mount_point,
|
||||
info->filesystem, 0);
|
||||
}
|
||||
|
||||
if (info->device == NULL || info->mount_point == NULL ||
|
||||
info->filesystem == NULL ||
|
||||
info->filesystem == g_raw ||
|
||||
@ -246,9 +245,15 @@ ensure_root_path_mounted(const char *root_path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (info->device == g_default_device) {
|
||||
if (info->partition_name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return mount_partition(info->partition_name, info->mount_point, info->filesystem, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -293,18 +298,38 @@ ensure_root_path_unmounted(const char *root_path)
|
||||
return unmount_mounted_volume(volume);
|
||||
}
|
||||
|
||||
int
|
||||
get_root_partition_device(const char *root_path, char *device)
|
||||
{
|
||||
const RootInfo *info = get_root_info_for_path(root_path);
|
||||
if (info == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (info->device == g_default_device)
|
||||
return get_partition_device(info->partition_name, device);
|
||||
return info->device;
|
||||
}
|
||||
|
||||
#ifndef BOARD_HAS_NO_MISC_PARTITION
|
||||
const MtdPartition *
|
||||
get_root_mtd_partition(const char *root_path)
|
||||
{
|
||||
const RootInfo *info = get_root_info_for_path(root_path);
|
||||
if (info == NULL || info->device != g_mtd_device ||
|
||||
if (info == NULL || info->device != g_default_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);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
format_root_device(const char *root)
|
||||
@ -316,17 +341,20 @@ 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_default_device) {
|
||||
/* Don't try to format a mounted device.
|
||||
*/
|
||||
int ret = ensure_root_path_unmounted(root);
|
||||
@ -338,33 +366,17 @@ format_root_device(const char *root)
|
||||
|
||||
/* Format the device.
|
||||
*/
|
||||
if (info->device == g_mtd_device) {
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition *partition;
|
||||
partition = mtd_find_partition_by_name(info->partition_name);
|
||||
if (partition == NULL) {
|
||||
LOGW("format_root_device: can't find mtd partition \"%s\"\n",
|
||||
info->partition_name);
|
||||
return -1;
|
||||
}
|
||||
if (info->filesystem == g_raw || !strcmp(info->filesystem, "yaffs2")) {
|
||||
MtdWriteContext *write = mtd_write_partition(partition);
|
||||
if (write == NULL) {
|
||||
LOGW("format_root_device: can't open \"%s\"\n", root);
|
||||
return -1;
|
||||
} else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
|
||||
LOGW("format_root_device: can't erase \"%s\"\n", root);
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
} else if (mtd_write_close(write)) {
|
||||
LOGW("format_root_device: can't close \"%s\"\n", root);
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (info->device == g_default_device) {
|
||||
int ret = 0;
|
||||
if (info->filesystem == g_raw)
|
||||
ret = erase_raw_partition(info->partition_name);
|
||||
else
|
||||
ret = erase_partition(info->partition_name, info->filesystem);
|
||||
|
||||
if (ret != 0)
|
||||
LOGE("Error erasing device %s\n", info->device);
|
||||
return ret;
|
||||
}
|
||||
//TODO: handle other device types (sdcard, etc.)
|
||||
LOGW("format_root_device: can't handle non-mtd device \"%s\"\n", root);
|
||||
return -1;
|
||||
|
||||
return format_unknown_device(root);
|
||||
}
|
||||
|
85
roots.h
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,7 +19,80 @@
|
||||
#define RECOVERY_ROOTS_H_
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include "flashutils/flashutils.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "mmcutils/mmcutils.h"
|
||||
|
||||
#ifndef BOARD_USES_MMCUTILS
|
||||
#define DEFAULT_FILESYSTEM "yaffs2"
|
||||
#else
|
||||
#define DEFAULT_FILESYSTEM "ext3"
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SDCARD_DEVICE_PRIMARY
|
||||
#define BOARD_SDCARD_DEVICE_PRIMARY "/dev/block/mmcblk0p1"
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SDCARD_DEVICE_SECONDARY
|
||||
#define BOARD_SDCARD_DEVICE_SECONDARY "/dev/block/mmcblk0"
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SDEXT_DEVICE
|
||||
#define BOARD_SDEXT_DEVICE "/dev/block/mmcblk0p2"
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SDEXT_FILESYSTEM
|
||||
#define BOARD_SDEXT_FILESYSTEM "auto"
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_DATA_DEVICE
|
||||
#define BOARD_DATA_DEVICE g_default_device
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_DATA_FILESYSTEM
|
||||
#define BOARD_DATA_FILESYSTEM DEFAULT_FILESYSTEM
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_DATADATA_DEVICE
|
||||
#define BOARD_DATADATA_DEVICE g_default_device
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_DATADATA_FILESYSTEM
|
||||
#define BOARD_DATADATA_FILESYSTEM DEFAULT_FILESYSTEM
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_CACHE_DEVICE
|
||||
#define BOARD_CACHE_DEVICE g_default_device
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_CACHE_FILESYSTEM
|
||||
#define BOARD_CACHE_FILESYSTEM DEFAULT_FILESYSTEM
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SYSTEM_DEVICE
|
||||
#define BOARD_SYSTEM_DEVICE g_default_device
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SYSTEM_FILESYSTEM
|
||||
#define BOARD_SYSTEM_FILESYSTEM DEFAULT_FILESYSTEM
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_DATA_FILESYSTEM_OPTIONS
|
||||
#define BOARD_DATA_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_CACHE_FILESYSTEM_OPTIONS
|
||||
#define BOARD_CACHE_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_DATADATA_FILESYSTEM_OPTIONS
|
||||
#define BOARD_DATADATA_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_SYSTEM_FILESYSTEM_OPTIONS
|
||||
#define BOARD_SYSTEM_FILESYSTEM_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
|
||||
/* Any of the "root_path" arguments can be paths with relative
|
||||
* components, like "SYSTEM:a/b/c".
|
||||
@ -54,10 +128,21 @@ int ensure_root_path_mounted(const char *root_path);
|
||||
int ensure_root_path_unmounted(const char *root_path);
|
||||
|
||||
const MtdPartition *get_root_mtd_partition(const char *root_path);
|
||||
int get_root_partition_device(const char *root_path, char *device);
|
||||
|
||||
/* "root" must be the exact name of the root; no relative path is permitted.
|
||||
* If the named root is mounted, this will attempt to unmount it first.
|
||||
*/
|
||||
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_
|
||||
|