Compare commits
624 Commits
Author | SHA1 | Date | |
---|---|---|---|
1e2da51172 | |||
8ba9a6335b | |||
9a7615326e | |||
08bedb21d8 | |||
7ba8a30150 | |||
21b8a12300 | |||
d87b8df56d | |||
1c50ff29e6 | |||
a465962667 | |||
e2a66586a3 | |||
10034aabc9 | |||
638103922f | |||
67e73e1559 | |||
3cdfd138fa | |||
ccc1fdb386 | |||
18e3f62ad2 | |||
b20a4812ba | |||
5617056cb8 | |||
aa3c3d429b | |||
cd3705e4ab | |||
4f78176329 | |||
de338e56b3 | |||
5a44ceb692 | |||
4c8e2482a6 | |||
059db9bfb6 | |||
e510810c71 | |||
4e50004a0e | |||
d43f5a01bf | |||
1cb1ce343b | |||
9487d2965f | |||
d4c04cb00b | |||
e9039792c9 | |||
f0c389ddcd | |||
357a2f5109 | |||
db524c28b4 | |||
a64c697333 | |||
1659814576 | |||
a9017dabc5 | |||
ddc1241a36 | |||
edae7a5b0f | |||
d2eecca360 | |||
b062713ee0 | |||
d280f6e0d8 | |||
b0a63d857f | |||
ff316eb739 | |||
21ad8d04dd | |||
0e7d88613a | |||
0df56f4db4 | |||
070dafc6fe | |||
63fb0eedbd | |||
88a233d5fa | |||
2291705c41 | |||
7ef88155e0 | |||
a8f265dd6f | |||
e0bbe565af | |||
890b951a3d | |||
c971f76e45 | |||
c9ea117b56 | |||
b4812b5b34 | |||
4fcd523ded | |||
7691202e89 | |||
89ed0b7355 | |||
46ba04b01b | |||
f0e81c4643 | |||
24ba583ec2 | |||
85850a553b | |||
6cf153cd50 | |||
04fae9d67e | |||
17fd32d0a2 | |||
441031dadc | |||
9996a5cd15 | |||
2e85422f89 | |||
f908e5bdbe | |||
a87bb5f57b | |||
f6abd409bb | |||
b1c19c1334 | |||
95fb821c19 | |||
0917a7aedf | |||
3681b91861 | |||
81a61516c9 | |||
fdfb636336 | |||
7f13e150cf | |||
30a937a954 | |||
88e0899617 | |||
365836736c | |||
bc29063bf4 | |||
cf2de074f6 | |||
bf5a60a034 | |||
5268131918 | |||
d6bf694ff0 | |||
4daf48a10b | |||
3c7f00ede6 | |||
c2ddaea83a | |||
7f33591677 | |||
01ba8bc552 | |||
337dcdd011 | |||
b3122e51a0 | |||
6afbcdc651 | |||
97c7df2f33 | |||
5e922a4bf4 | |||
5ee0380c67 | |||
04a211f372 | |||
7b2697f66f | |||
0d651b1b70 | |||
d41c2c8e89 | |||
1a873797f7 | |||
7e70b6be7f | |||
45df699a5e | |||
81a1ffea20 | |||
c915937ebd | |||
137c9f41c4 | |||
d07ffd72cd | |||
eb557e4233 | |||
55e6e7dc4f | |||
0bf56815b7 | |||
d5d1fcdc96 | |||
6242a8bc9b | |||
cd264e7f94 | |||
8918673cf7 | |||
b2ceb696d0 | |||
5c69bdd735 | |||
94a4babac7 | |||
e87f9e695e | |||
6057bade5c | |||
eb3aa6ff32 | |||
1115c8f325 | |||
8d92ae052e | |||
70f25bead8 | |||
32ac1666e2 | |||
7e6067e36c | |||
b0462e6ae2 | |||
491f4847f6 | |||
80cd7707c0 | |||
24c802b05e | |||
7f3a8c90b0 | |||
e8bdefda33 | |||
de7025e43c | |||
fd6a28be5d | |||
4f1c5e37f4 | |||
67700e791a | |||
3f2e286747 | |||
5e4f258ca2 | |||
bc9dc6fc48 | |||
a2bc7245f7 | |||
1c2238b3af | |||
128f131e41 | |||
aaad77fc80 | |||
9154b8cfd2 | |||
933487868f | |||
e9ab999990 | |||
a75c067df4 | |||
50732e3c67 | |||
78f44e8bdf | |||
0b06fc0696 | |||
67d358cf4e | |||
718a2f7872 | |||
ca0fc46095 | |||
b4261d9ddd | |||
47591ce0d0 | |||
31c3ccf531 | |||
6c88ec2e8b | |||
eed82c0f81 | |||
9e08693670 | |||
7305f7d956 | |||
5ab3281dbe | |||
b986fe24b4 | |||
aee5f85b29 | |||
40fd4565d7 | |||
a42d2b1e24 | |||
9759ede732 | |||
9d2629c1c4 | |||
3dbe66b71d | |||
fb04b87002 | |||
9d1bcdf7b8 | |||
7e58b65efb | |||
eb1ecab9bb | |||
11cff33332 | |||
fb3bd71038 | |||
e909c69169 | |||
7905c80940 | |||
fcb87af735 | |||
55e5e7b59a | |||
16cfb3c72f | |||
7126ef404c | |||
2457adcdf7 | |||
0e961cd47a | |||
5583712d6f | |||
4fbfc4610e | |||
38a921435a | |||
94caefedb7 | |||
2e0964bed1 | |||
4eff651c83 | |||
1f40b86175 | |||
14d9e75577 | |||
4d8c033203 | |||
0e6b3f22ca | |||
967e15c44d | |||
5082299146 | |||
5a4e03d120 | |||
7f72ad7934 | |||
f84b25243d | |||
a94d954862 | |||
5f77b7d715 | |||
ebe7a1dcf6 | |||
c56768cfb1 | |||
cdcb773cea | |||
ab0f3f79d6 | |||
b3aac3c367 | |||
6ebedf0053 | |||
82da01d6a3 | |||
1117c69fe4 | |||
7172eb7b62 | |||
8d2e1defb8 | |||
01143a5630 | |||
b2d30187d6 | |||
88efbfb82b | |||
195e06be59 | |||
aff130d843 | |||
cf5195aa79 | |||
ef5994fe29 | |||
30712302e0 | |||
4187347329 | |||
0bf1c6e598 | |||
7adeadce47 | |||
e62132bb6d | |||
8a6bc77423 | |||
d3e66fed8b | |||
d79b7541f3 | |||
b7b7b37457 | |||
28a08efe15 | |||
0b4eac9e01 | |||
666053ed26 | |||
63605f1d45 | |||
20b516a408 | |||
49553c7561 | |||
9ad8ba21b3 | |||
4d5109cfa8 | |||
e44b41a770 | |||
c2a158db95 | |||
54815c9dc9 | |||
c636b7300b | |||
90588820b7 | |||
f4bb554ee9 | |||
cea6eba92d | |||
cde585e3de | |||
df3004bd16 | |||
9c4c8e2c66 | |||
6a97396572 | |||
7364e82a5f | |||
1f1a274ecc | |||
803896f62c | |||
f573510b50 | |||
f1bde36572 | |||
cfd256a332 | |||
907ed71a5c | |||
eee085415f | |||
fdd4766c1e | |||
ed7d296dfa | |||
179b2d9895 | |||
469243e536 | |||
3a7dc29b24 | |||
bab66b40f7 | |||
6944713e92 | |||
7a52627720 | |||
0d87851037 | |||
814a934c8f | |||
d2375719d5 | |||
3bf881539c | |||
ee9ab1969a | |||
5615b2d46a | |||
3f26366993 | |||
faa477ec99 | |||
6d19f22c85 | |||
67fa0c375f | |||
3ed8e18721 | |||
4762633cf8 | |||
201aa88cba | |||
90415aca67 | |||
6e4472abbd | |||
f66088d2ce | |||
6ce4a326ef | |||
4cc533dd1c | |||
b70fe3f76a | |||
92796ec949 | |||
59d86b4cf7 | |||
68189f2994 | |||
7e69e2cccc | |||
73bb6f749f | |||
b7c91766da | |||
5ee98b20d0 | |||
65ad19c943 | |||
13ac7a473f | |||
e734dad68c | |||
be6d4d1052 | |||
fa265db3a3 | |||
c5ab2a34f9 | |||
e2b929ded1 | |||
6809c51f8d | |||
a1f43bfd27 | |||
978a0e25c4 | |||
ab1f8b836e | |||
47d86a35bc | |||
7161f35736 | |||
33e37f3e5d | |||
d9fbe2f1ce | |||
e8bc2c808c | |||
33fd0d0a70 | |||
c007b961d7 | |||
ee458bbf9b | |||
8d43d94065 | |||
2c273f004e | |||
75b2fa023b | |||
2810ceda34 | |||
a9f0e7fdfe | |||
c7ef9af5d7 | |||
49ee9a8b09 | |||
64d79c6531 | |||
4c05d95112 | |||
28fee5a1b2 | |||
6d0604bf34 | |||
6da1abbc38 | |||
5df22d03d2 | |||
5cae445e43 | |||
80abd51dbc | |||
da993fcf26 | |||
1966aaf4e6 | |||
8d58c95703 | |||
8f132ed870 | |||
862c83bb31 | |||
b5d0bd024c | |||
540d57f25a | |||
066a1027a4 | |||
a25cf5ec4f | |||
e06e539196 | |||
be3e6f13b8 | |||
d646a6fa1d | |||
49396b79b5 | |||
53c9bd8b10 | |||
d8e21c3712 | |||
b4c5fd6305 | |||
9f52e5f23b | |||
67c381a6fa | |||
949f3b34ee | |||
f1cb0d5b35 | |||
ad10e56863 | |||
7aa8ef9abc | |||
a25deae3ef | |||
f12df71ffb | |||
3affbec593 | |||
2e45449ef3 | |||
1375df75ff | |||
a8708c6044 | |||
da32b54f85 | |||
29a7891204 | |||
10a91b0bf2 | |||
5a3f1d8af5 | |||
05d4a09aba | |||
15b06184cd | |||
ebc5aab351 | |||
ca889ec844 | |||
5b7f34a2d8 | |||
1f76a5da6b | |||
264f549b7d | |||
7501798fab | |||
c8ff793bf4 | |||
1e6d772f35 | |||
03cf72a4f5 | |||
cdb433af66 | |||
745b5ff987 | |||
31b741174c | |||
bec0995afe | |||
33491accea | |||
4196e0fa5e | |||
d0fff5633c | |||
5460f0c746 | |||
5d80817f2b | |||
4995114d38 | |||
9765a037a1 | |||
7c1bffefeb | |||
bf4444f1fb | |||
b643e4f3d0 | |||
8637c73757 | |||
df1e406782 | |||
0b7bbf29d5 | |||
d352233ab6 | |||
16c0ace22b | |||
134ead7537 | |||
03a4f5ba0d | |||
cd8af06d64 | |||
02c360501b | |||
8df69c0660 | |||
229a543bb3 | |||
517a16b98f | |||
9397e322c3 | |||
ddc2e39dcf | |||
d90ad5dedf | |||
3847a2ddd9 | |||
44bd4947d2 | |||
ac731c88e3 | |||
9acb022ab8 | |||
37d96f7414 | |||
0b23331f23 | |||
4123b58299 | |||
158d25cae4 | |||
7c50645a6c | |||
e5c7e0eaca | |||
12154200a7 | |||
d771acbed0 | |||
1b86754eaa | |||
19447c0550 | |||
fef77c0253 | |||
7a77aec362 | |||
487821abe5 | |||
9456641236 | |||
70f7e8dbf0 | |||
c5ebf1fba2 | |||
5d6309e77f | |||
02971af254 | |||
8521ef5965 | |||
51266d1397 | |||
1805cfe02e | |||
815ca5d230 | |||
0209a62c7d | |||
7c3ee270ce | |||
2d87023e77 | |||
2c3539e4d8 | |||
1a32732db2 | |||
704fa75024 | |||
9b125b04c6 | |||
6b26c882a8 | |||
af78591c9a | |||
2c3c5c15d1 | |||
c18eeb874b | |||
3d798835a3 | |||
8147ba850b | |||
93ca4fc694 | |||
cc8cd3f3ca | |||
0f1ad110f8 | |||
3dba40da1e | |||
d4208f9f9d | |||
c9ebb58623 | |||
cb71c2f334 | |||
e17a78ddda | |||
40f0d3b4e0 | |||
d7d4208976 | |||
97e54ef573 | |||
1dcd2fd0c7 | |||
dc49ce453e | |||
792b00712b | |||
6c3e03b3c5 | |||
a66c32ab1d | |||
d7693c4c8e | |||
6b1ff61f62 | |||
4f69afdb63 | |||
85bcf776e1 | |||
9f89b0e467 | |||
76445f3aef | |||
8a8e6cc33c | |||
56c5105bd7 | |||
8674a726ff | |||
858f0a763d | |||
8e5e4dada7 | |||
bd4bc088e2 | |||
d12560aa21 | |||
4e10b135cc | |||
56c1b3b576 | |||
68df48c28f | |||
28a41b4df2 | |||
5a9fe1c964 | |||
b5a36a0e20 | |||
e5678e9249 | |||
6a84721c76 | |||
f94a3575d7 | |||
61ba7a83ef | |||
0b519b25c3 | |||
a2c9a1e8a2 | |||
aaf3f56c44 | |||
4233c7fb3c | |||
b56db27d08 | |||
28cae98c83 | |||
4bc980626c | |||
5200114fe8 | |||
3e115b81a4 | |||
4c7c2f73af | |||
17b44ee3d8 | |||
532c86002b | |||
21f0f97eba | |||
91e9e830e2 | |||
fad25ab75b | |||
37d58711f6 | |||
979002871d | |||
965f9dc453 | |||
baf6e35473 | |||
8101125ee5 | |||
dff87121ad | |||
beecac49da | |||
c080bc549a | |||
fbd7ae7a1c | |||
201cd46680 | |||
80dcee145f | |||
c4e3200578 | |||
17986e6b87 | |||
8cd9e4f3d4 | |||
04611da55b | |||
db314d69f0 | |||
f635d2e910 | |||
dc9e87c44b | |||
b4277c25c6 | |||
ef83d3ea87 | |||
702294d5b8 | |||
f146bc1b74 | |||
d61e56ab36 | |||
9565424508 | |||
17bba907ac | |||
4ca9b4c3a0 | |||
efa6530dbd | |||
b41b4589c2 | |||
fdda0d66d3 | |||
30e5b7f6dc | |||
6e5851647a | |||
050d0f7fec | |||
c02c37a1e6 | |||
23ceeea85e | |||
f291d858f8 | |||
82c2ca262e | |||
d3cc60b036 | |||
3d177d055c | |||
b442b45bdd | |||
d823d5f327 | |||
789ab6bed2 | |||
916f5538f9 | |||
f2954b5b64 | |||
107629b02d | |||
42cfc2cc05 | |||
60faafcf01 | |||
49c73a76a3 | |||
ba634dc9f7 | |||
4fef81a5a9 | |||
c78a9698dd | |||
ecc76ba551 | |||
9e6513f145 | |||
21b97ed569 | |||
0eb14b30e0 | |||
b3d9a15b66 | |||
dd6a0412b5 | |||
eb681fd491 | |||
be42930f0b | |||
97ca48e7f4 | |||
be598881d0 | |||
6102227b0b | |||
52219a68a8 | |||
f33e24645b | |||
be17b1c0d7 | |||
51282faa8c | |||
2654f5aae1 | |||
31f6ee88ce | |||
dcc38b3c15 | |||
5b695f393e | |||
c4351c7910 | |||
583fc12c3d | |||
512536a54a | |||
21854ccdb2 | |||
2e068dc330 | |||
da846fcf1b | |||
4c382b1365 | |||
e08991e02a | |||
93dbe07ff6 | |||
efa1bab94c | |||
6aece33b3f | |||
b551724ceb | |||
aa062531aa | |||
687bc12ccf | |||
883b4c8be5 | |||
4e9332cb0b | |||
4c5f9f3416 | |||
b765729081 | |||
be47155f75 | |||
158657bc5c | |||
9b514530a6 | |||
d36308c26d | |||
8fae8279fa | |||
25215285c4 | |||
9b430e11d6 | |||
bd6181ad58 | |||
0523156775 | |||
002c9dfb80 | |||
73ae31ce0a | |||
2278a04a09 | |||
3b0f484776 | |||
9acf28a390 | |||
6149073651 | |||
b8f506fb37 | |||
6e5be9b24c | |||
052acd61c8 | |||
b2ce982d43 | |||
a43c44f31f | |||
f88cea6ded | |||
4011770f2d | |||
31f0fc2235 | |||
2ec8a1929f | |||
022229c47e | |||
d93a25459c | |||
d641a0e141 | |||
42ab176195 | |||
4526d4fe62 | |||
8f8bc4cb48 | |||
83a25d7380 | |||
486aa29063 | |||
66c76bcfcf | |||
d16fb221cd | |||
b9ad6dfd81 | |||
2f4fc56183 | |||
49c82ce553 | |||
b5d542cd40 | |||
af42fa0a7d | |||
9a77b613f3 | |||
65a56909a3 | |||
2f39bf9a9f | |||
cbf9038038 | |||
619ec2f3aa | |||
6785c2534a | |||
c2d666bd4f | |||
50a8a71f0b | |||
60151a295c |
218
Android.mk
218
Android.mk
@ -1,6 +1,3 @@
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
@ -8,126 +5,63 @@ commands_recovery_local_path := $(LOCAL_PATH)
|
||||
# LOCAL_CPP_EXTENSION := .c
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
extendedcommands.c \
|
||||
nandroid.c \
|
||||
legacy.c \
|
||||
commands.c \
|
||||
recovery.c \
|
||||
install.c \
|
||||
roots.c \
|
||||
ui.c \
|
||||
verifier.c
|
||||
recovery.c \
|
||||
bootloader.c \
|
||||
install.c \
|
||||
roots.c \
|
||||
ui.c \
|
||||
mounts.c \
|
||||
extendedcommands.c \
|
||||
nandroid.c \
|
||||
../../system/core/toolbox/reboot.c \
|
||||
firmware.c \
|
||||
edifyscripting.c \
|
||||
setprop.c \
|
||||
default_recovery_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
|
||||
ADDITIONAL_RECOVERY_FILES := $(shell echo $$ADDITIONAL_RECOVERY_FILES)
|
||||
LOCAL_SRC_FILES += $(ADDITIONAL_RECOVERY_FILES)
|
||||
|
||||
LOCAL_MODULE := recovery
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
RECOVERY_VERSION := ClockworkMod Recovery v2.5.1.0
|
||||
ifdef I_AM_KOUSH
|
||||
RECOVERY_NAME := ClockworkMod Recovery
|
||||
LOCAL_CFLAGS += -DI_AM_KOUSH
|
||||
else
|
||||
RECOVERY_NAME := CWM-based Recovery
|
||||
endif
|
||||
|
||||
RECOVERY_VERSION := $(RECOVERY_NAME) v5.5.0.4
|
||||
|
||||
LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
|
||||
RECOVERY_API_VERSION := 2
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
|
||||
ifeq ($(BOARD_HAS_NO_SELECT_BUTTON),true)
|
||||
LOCAL_CFLAGS += -DKEY_POWER_IS_SELECT_ITEM
|
||||
ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),)
|
||||
BOARD_RECOVERY_CHAR_WIDTH := $(shell echo $(BOARD_USE_CUSTOM_RECOVERY_FONT) | cut -d _ -f 2 | cut -d . -f 1 | cut -d x -f 1)
|
||||
BOARD_RECOVERY_CHAR_HEIGHT := $(shell echo $(BOARD_USE_CUSTOM_RECOVERY_FONT) | cut -d _ -f 2 | cut -d . -f 1 | cut -d x -f 2)
|
||||
else
|
||||
BOARD_RECOVERY_CHAR_WIDTH := 10
|
||||
BOARD_RECOVERY_CHAR_HEIGHT := 18
|
||||
endif
|
||||
LOCAL_CFLAGS += -DBOARD_RECOVERY_CHAR_WIDTH=$(BOARD_RECOVERY_CHAR_WIDTH) -DBOARD_RECOVERY_CHAR_HEIGHT=$(BOARD_RECOVERY_CHAR_HEIGHT)
|
||||
|
||||
ifdef BOARD_SDCARD_DEVICE_PRIMARY
|
||||
LOCAL_CFLAGS += -DSDCARD_DEVICE_PRIMARY=\"$(BOARD_SDCARD_DEVICE_PRIMARY)\"
|
||||
endif
|
||||
BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_UMS_LUNFILE BOARD_RECOVERY_ALWAYS_WIPES BOARD_RECOVERY_HANDLES_MOUNT BOARD_TOUCH_RECOVERY
|
||||
|
||||
ifdef BOARD_SDCARD_DEVICE_SECONDARY
|
||||
LOCAL_CFLAGS += -DSDCARD_DEVICE_SECONDARY=\"$(BOARD_SDCARD_DEVICE_SECONDARY)\"
|
||||
endif
|
||||
$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
|
||||
$(if $($(board_define)), \
|
||||
$(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
|
||||
) \
|
||||
)
|
||||
|
||||
ifdef BOARD_SDEXT_DEVICE
|
||||
LOCAL_CFLAGS += -DSDEXT_DEVICE=\"$(BOARD_SDEXT_DEVICE)\"
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
ifdef BOARD_SDEXT_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DSDEXT_FILESYSTEM=\"$(BOARD_SDEXT_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATA_DEVICE
|
||||
LOCAL_CFLAGS += -DDATA_DEVICE=\"$(BOARD_DATA_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATA_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DDATA_FILESYSTEM=\"$(BOARD_DATA_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATADATA_DEVICE
|
||||
LOCAL_CFLAGS += -DDATADATA_DEVICE=\"$(BOARD_DATADATA_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATADATA_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DDATADATA_FILESYSTEM=\"$(BOARD_DATADATA_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_CACHE_DEVICE
|
||||
LOCAL_CFLAGS += -DCACHE_DEVICE=\"$(BOARD_CACHE_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_CACHE_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DCACHE_FILESYSTEM=\"$(BOARD_CACHE_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SYSTEM_DEVICE
|
||||
LOCAL_CFLAGS += -DSYSTEM_DEVICE=\"$(BOARD_SYSTEM_DEVICE)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SYSTEM_FILESYSTEM
|
||||
LOCAL_CFLAGS += -DSYSTEM_FILESYSTEM=\"$(BOARD_SYSTEM_FILESYSTEM)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_HAS_DATADATA
|
||||
LOCAL_CFLAGS += -DHAS_DATADATA
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATA_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DDATA_FILESYSTEM_OPTIONS=\"$(BOARD_DATA_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_DATADATA_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DDATADATA_FILESYSTEM_OPTIONS=\"$(BOARD_DATADATA_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_CACHE_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DCACHE_FILESYSTEM_OPTIONS=\"$(BOARD_CACHE_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_SYSTEM_FILESYSTEM_OPTIONS
|
||||
LOCAL_CFLAGS += -DSYSTEM_FILESYSTEM_OPTIONS=\"$(BOARD_SYSTEM_FILESYSTEM_OPTIONS)\"
|
||||
endif
|
||||
|
||||
ifdef BOARD_HAS_MTD_CACHE
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_MTD_CACHE
|
||||
endif
|
||||
|
||||
ifdef BOARD_HAS_SMALL_RECOVERY
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_SMALL_RECOVERY
|
||||
endif
|
||||
LOCAL_CFLAGS += -DUSE_EXT4
|
||||
LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
LOCAL_STATIC_LIBRARIES += libext4_utils libz
|
||||
|
||||
# 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
|
||||
@ -136,48 +70,59 @@ endif
|
||||
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
|
||||
LOCAL_SRC_FILES += default_recovery_ui.c
|
||||
LOCAL_SRC_FILES += default_recovery_keys.c
|
||||
else
|
||||
LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING)
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image libmtdutils
|
||||
LOCAL_STATIC_LIBRARIES += libamend
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libext4_utils libz
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libedify libbusybox libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libcrecovery libflashutils libmtdutils libmmcutils libbmlutils
|
||||
|
||||
ifeq ($(BOARD_USES_BML_OVER_MTD),true)
|
||||
LOCAL_STATIC_LIBRARIES += libbml_over_mtd
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
|
||||
LOCAL_STATIC_LIBRARIES += libstdc++ libc
|
||||
|
||||
LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
RECOVERY_LINKS := amend busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
|
||||
RECOVERY_LINKS := edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop
|
||||
|
||||
# nc is provided by external/netcat
|
||||
SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
|
||||
$(SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
|
||||
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
|
||||
RECOVERY_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
|
||||
$(RECOVERY_SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
|
||||
$(RECOVERY_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
|
||||
@echo "Symlink: $@ -> $(RECOVERY_BINARY)"
|
||||
@mkdir -p $(dir $@)
|
||||
@rm -rf $@
|
||||
$(hide) ln -sf $(RECOVERY_BINARY) $@
|
||||
|
||||
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
|
||||
ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_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)
|
||||
exclude := tune2fs mke2fs
|
||||
RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
|
||||
$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox
|
||||
$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
|
||||
@echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
|
||||
@mkdir -p $(dir $@)
|
||||
@rm -rf $@
|
||||
$(hide) ln -sf $(BUSYBOX_BINARY) $@
|
||||
|
||||
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
|
||||
ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := nandroid-md5.sh
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := nandroid-md5.sh
|
||||
@ -185,22 +130,37 @@ include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := killrecovery.sh
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := killrecovery.sh
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(commands_recovery_local_path)/amend/Android.mk
|
||||
include $(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)
|
||||
|
||||
include $(commands_recovery_local_path)/bmlutils/Android.mk
|
||||
include $(commands_recovery_local_path)/flashutils/Android.mk
|
||||
include $(commands_recovery_local_path)/libcrecovery/Android.mk
|
||||
include $(commands_recovery_local_path)/minui/Android.mk
|
||||
include $(commands_recovery_local_path)/minelf/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
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
|
||||
# ************************************************
|
@ -1,53 +0,0 @@
|
||||
# Copyright 2007 The Android Open Source Project
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
amend_src_files := \
|
||||
amend.c \
|
||||
lexer.l \
|
||||
parser_y.y \
|
||||
ast.c \
|
||||
symtab.c \
|
||||
commands.c \
|
||||
permissions.c \
|
||||
execute.c
|
||||
|
||||
amend_test_files := \
|
||||
test_symtab.c \
|
||||
test_commands.c \
|
||||
test_permissions.c
|
||||
|
||||
# "-x c" forces the lex/yacc files to be compiled as c;
|
||||
# the build system otherwise forces them to be c++.
|
||||
amend_cflags := -Wall -x c
|
||||
|
||||
#
|
||||
# Build the host-side command line tool
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(amend_src_files) \
|
||||
$(amend_test_files) \
|
||||
register.c \
|
||||
main.c
|
||||
|
||||
LOCAL_CFLAGS := $(amend_cflags) -g -O0
|
||||
LOCAL_MODULE := amend
|
||||
LOCAL_YACCFLAGS := -v
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
#
|
||||
# Build the device-side library
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(amend_src_files)
|
||||
LOCAL_SRC_FILES += $(amend_test_files)
|
||||
|
||||
LOCAL_CFLAGS := $(amend_cflags)
|
||||
LOCAL_MODULE := libamend
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "amend.h"
|
||||
#include "lexer.h"
|
||||
|
||||
extern const AmCommandList *gCommands;
|
||||
|
||||
const AmCommandList *
|
||||
parseAmendScript(const char *buf, size_t bufLen)
|
||||
{
|
||||
setLexerInputBuffer(buf, bufLen);
|
||||
int ret = yyparse();
|
||||
if (ret != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return gCommands;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_H_
|
||||
#define AMEND_H_
|
||||
|
||||
#include "ast.h"
|
||||
#include "execute.h"
|
||||
|
||||
const AmCommandList *parseAmendScript(const char *buf, size_t bufLen);
|
||||
|
||||
#endif // AMEND_H_
|
198
amend/ast.c
198
amend/ast.c
@ -1,198 +0,0 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include "ast.h"
|
||||
|
||||
static const char gSpaces[] =
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" ";
|
||||
const int gSpacesMax = sizeof(gSpaces) - 1;
|
||||
|
||||
static const char *
|
||||
pad(int level)
|
||||
{
|
||||
level *= 4;
|
||||
if (level > gSpacesMax) {
|
||||
level = gSpacesMax;
|
||||
}
|
||||
return gSpaces + gSpacesMax - level;
|
||||
}
|
||||
|
||||
void dumpBooleanValue(int level, const AmBooleanValue *booleanValue);
|
||||
void dumpStringValue(int level, const AmStringValue *stringValue);
|
||||
|
||||
void
|
||||
dumpBooleanExpression(int level, const AmBooleanExpression *booleanExpression)
|
||||
{
|
||||
const char *op;
|
||||
bool unary = false;
|
||||
|
||||
switch (booleanExpression->op) {
|
||||
case AM_BOP_NOT:
|
||||
op = "NOT";
|
||||
unary = true;
|
||||
break;
|
||||
case AM_BOP_EQ:
|
||||
op = "EQ";
|
||||
break;
|
||||
case AM_BOP_NE:
|
||||
op = "NE";
|
||||
break;
|
||||
case AM_BOP_AND:
|
||||
op = "AND";
|
||||
break;
|
||||
case AM_BOP_OR:
|
||||
op = "OR";
|
||||
break;
|
||||
default:
|
||||
op = "??";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%sBOOLEAN %s {\n", pad(level), op);
|
||||
dumpBooleanValue(level + 1, booleanExpression->arg1);
|
||||
if (!unary) {
|
||||
dumpBooleanValue(level + 1, booleanExpression->arg2);
|
||||
}
|
||||
printf("%s}\n", pad(level));
|
||||
}
|
||||
|
||||
void
|
||||
dumpFunctionArguments(int level, const AmFunctionArguments *functionArguments)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < functionArguments->argc; i++) {
|
||||
dumpStringValue(level, &functionArguments->argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dumpFunctionCall(int level, const AmFunctionCall *functionCall)
|
||||
{
|
||||
printf("%sFUNCTION %s (\n", pad(level), functionCall->name);
|
||||
dumpFunctionArguments(level + 1, functionCall->args);
|
||||
printf("%s)\n", pad(level));
|
||||
}
|
||||
|
||||
void
|
||||
dumpStringValue(int level, const AmStringValue *stringValue)
|
||||
{
|
||||
switch (stringValue->type) {
|
||||
case AM_SVAL_LITERAL:
|
||||
printf("%s\"%s\"\n", pad(level), stringValue->u.literal);
|
||||
break;
|
||||
case AM_SVAL_FUNCTION:
|
||||
dumpFunctionCall(level, stringValue->u.function);
|
||||
break;
|
||||
default:
|
||||
printf("%s<UNKNOWN SVAL TYPE %d>\n", pad(level), stringValue->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dumpStringComparisonExpression(int level,
|
||||
const AmStringComparisonExpression *stringComparisonExpression)
|
||||
{
|
||||
const char *op;
|
||||
|
||||
switch (stringComparisonExpression->op) {
|
||||
case AM_SOP_LT:
|
||||
op = "LT";
|
||||
break;
|
||||
case AM_SOP_LE:
|
||||
op = "LE";
|
||||
break;
|
||||
case AM_SOP_GT:
|
||||
op = "GT";
|
||||
break;
|
||||
case AM_SOP_GE:
|
||||
op = "GE";
|
||||
break;
|
||||
case AM_SOP_EQ:
|
||||
op = "EQ";
|
||||
break;
|
||||
case AM_SOP_NE:
|
||||
op = "NE";
|
||||
break;
|
||||
default:
|
||||
op = "??";
|
||||
break;
|
||||
}
|
||||
printf("%sSTRING %s {\n", pad(level), op);
|
||||
dumpStringValue(level + 1, stringComparisonExpression->arg1);
|
||||
dumpStringValue(level + 1, stringComparisonExpression->arg2);
|
||||
printf("%s}\n", pad(level));
|
||||
}
|
||||
|
||||
void
|
||||
dumpBooleanValue(int level, const AmBooleanValue *booleanValue)
|
||||
{
|
||||
switch (booleanValue->type) {
|
||||
case AM_BVAL_EXPRESSION:
|
||||
dumpBooleanExpression(level, &booleanValue->u.expression);
|
||||
break;
|
||||
case AM_BVAL_STRING_COMPARISON:
|
||||
dumpStringComparisonExpression(level,
|
||||
&booleanValue->u.stringComparison);
|
||||
break;
|
||||
default:
|
||||
printf("%s<UNKNOWN BVAL TYPE %d>\n", pad(1), booleanValue->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dumpWordList(const AmWordList *wordList)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < wordList->argc; i++) {
|
||||
printf("%s\"%s\"\n", pad(1), wordList->argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dumpCommandArguments(const AmCommandArguments *commandArguments)
|
||||
{
|
||||
if (commandArguments->booleanArgs) {
|
||||
dumpBooleanValue(1, commandArguments->u.b);
|
||||
} else {
|
||||
dumpWordList(commandArguments->u.w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dumpCommand(const AmCommand *command)
|
||||
{
|
||||
printf("command \"%s\" {\n", command->name);
|
||||
dumpCommandArguments(command->args);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
void
|
||||
dumpCommandList(const AmCommandList *commandList)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < commandList->commandCount; i++) {
|
||||
dumpCommand(commandList->commands[i]);
|
||||
}
|
||||
}
|
165
amend/ast.h
165
amend/ast.h
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_AST_H_
|
||||
#define AMEND_AST_H_
|
||||
|
||||
#include "commands.h"
|
||||
|
||||
typedef struct AmStringValue AmStringValue;
|
||||
|
||||
typedef struct {
|
||||
int argc;
|
||||
AmStringValue *argv;
|
||||
} AmFunctionArguments;
|
||||
|
||||
/* An internal structure used only by the parser;
|
||||
* will not appear in the output AST.
|
||||
xxx try to move this into parser.h
|
||||
*/
|
||||
typedef struct AmFunctionArgumentBuilder AmFunctionArgumentBuilder;
|
||||
struct AmFunctionArgumentBuilder {
|
||||
AmFunctionArgumentBuilder *next;
|
||||
AmStringValue *arg;
|
||||
int argCount;
|
||||
};
|
||||
|
||||
typedef struct AmWordListBuilder AmWordListBuilder;
|
||||
struct AmWordListBuilder {
|
||||
AmWordListBuilder *next;
|
||||
const char *word;
|
||||
int wordCount;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
Function *fn;
|
||||
AmFunctionArguments *args;
|
||||
} AmFunctionCall;
|
||||
|
||||
|
||||
/* <string-value> ::=
|
||||
* <literal-string> |
|
||||
* <function-call>
|
||||
*/
|
||||
struct AmStringValue {
|
||||
unsigned int line;
|
||||
|
||||
enum {
|
||||
AM_SVAL_LITERAL,
|
||||
AM_SVAL_FUNCTION,
|
||||
} type;
|
||||
union {
|
||||
const char *literal;
|
||||
//xxx inline instead of using pointers
|
||||
AmFunctionCall *function;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
/* <string-comparison-expression> ::=
|
||||
* <string-value> <string-comparison-operator> <string-value>
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int line;
|
||||
|
||||
enum {
|
||||
AM_SOP_LT,
|
||||
AM_SOP_LE,
|
||||
AM_SOP_GT,
|
||||
AM_SOP_GE,
|
||||
AM_SOP_EQ,
|
||||
AM_SOP_NE,
|
||||
} op;
|
||||
AmStringValue *arg1;
|
||||
AmStringValue *arg2;
|
||||
} AmStringComparisonExpression;
|
||||
|
||||
|
||||
/* <boolean-expression> ::=
|
||||
* ! <boolean-value> |
|
||||
* <boolean-value> <binary-boolean-operator> <boolean-value>
|
||||
*/
|
||||
typedef struct AmBooleanValue AmBooleanValue;
|
||||
typedef struct {
|
||||
unsigned int line;
|
||||
|
||||
enum {
|
||||
AM_BOP_NOT,
|
||||
|
||||
AM_BOP_EQ,
|
||||
AM_BOP_NE,
|
||||
|
||||
AM_BOP_AND,
|
||||
|
||||
AM_BOP_OR,
|
||||
} op;
|
||||
AmBooleanValue *arg1;
|
||||
AmBooleanValue *arg2;
|
||||
} AmBooleanExpression;
|
||||
|
||||
|
||||
/* <boolean-value> ::=
|
||||
* <boolean-expression> |
|
||||
* <string-comparison-expression>
|
||||
*/
|
||||
struct AmBooleanValue {
|
||||
unsigned int line;
|
||||
|
||||
enum {
|
||||
AM_BVAL_EXPRESSION,
|
||||
AM_BVAL_STRING_COMPARISON,
|
||||
} type;
|
||||
union {
|
||||
AmBooleanExpression expression;
|
||||
AmStringComparisonExpression stringComparison;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned int line;
|
||||
|
||||
int argc;
|
||||
const char **argv;
|
||||
} AmWordList;
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool booleanArgs;
|
||||
union {
|
||||
AmWordList *w;
|
||||
AmBooleanValue *b;
|
||||
} u;
|
||||
} AmCommandArguments;
|
||||
|
||||
typedef struct {
|
||||
unsigned int line;
|
||||
|
||||
const char *name;
|
||||
Command *cmd;
|
||||
AmCommandArguments *args;
|
||||
} AmCommand;
|
||||
|
||||
typedef struct {
|
||||
AmCommand **commands;
|
||||
int commandCount;
|
||||
int arraySize;
|
||||
} AmCommandList;
|
||||
|
||||
void dumpCommandList(const AmCommandList *commandList);
|
||||
|
||||
#endif // AMEND_AST_H_
|
273
amend/commands.c
273
amend/commands.c
@ -1,273 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "symtab.h"
|
||||
#include "commands.h"
|
||||
|
||||
#if 1
|
||||
#define TRACE(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define TRACE(...) /**/
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CMD_TYPE_UNKNOWN = -1,
|
||||
CMD_TYPE_COMMAND = 0,
|
||||
CMD_TYPE_FUNCTION
|
||||
} CommandType;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
void *cookie;
|
||||
CommandType type;
|
||||
CommandArgumentType argType;
|
||||
CommandHook hook;
|
||||
} CommandEntry;
|
||||
|
||||
static struct {
|
||||
SymbolTable *symbolTable;
|
||||
bool commandStateInitialized;
|
||||
} gCommandState;
|
||||
|
||||
int
|
||||
commandInit()
|
||||
{
|
||||
if (gCommandState.commandStateInitialized) {
|
||||
return -1;
|
||||
}
|
||||
gCommandState.symbolTable = createSymbolTable();
|
||||
if (gCommandState.symbolTable == NULL) {
|
||||
return -1;
|
||||
}
|
||||
gCommandState.commandStateInitialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
commandCleanup()
|
||||
{
|
||||
if (gCommandState.commandStateInitialized) {
|
||||
gCommandState.commandStateInitialized = false;
|
||||
deleteSymbolTable(gCommandState.symbolTable);
|
||||
gCommandState.symbolTable = NULL;
|
||||
//xxx need to free the entries and names in the symbol table
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
registerCommandInternal(const char *name, CommandType type,
|
||||
CommandArgumentType argType, CommandHook hook, void *cookie)
|
||||
{
|
||||
CommandEntry *entry;
|
||||
|
||||
if (!gCommandState.commandStateInitialized) {
|
||||
return -1;
|
||||
}
|
||||
if (name == NULL || hook == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (type != CMD_TYPE_COMMAND && type != CMD_TYPE_FUNCTION) {
|
||||
return -1;
|
||||
}
|
||||
if (argType != CMD_ARGS_BOOLEAN && argType != CMD_ARGS_WORDS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry = (CommandEntry *)malloc(sizeof(CommandEntry));
|
||||
if (entry != NULL) {
|
||||
entry->name = strdup(name);
|
||||
if (entry->name != NULL) {
|
||||
int ret;
|
||||
|
||||
entry->cookie = cookie;
|
||||
entry->type = type;
|
||||
entry->argType = argType;
|
||||
entry->hook = hook;
|
||||
ret = addToSymbolTable(gCommandState.symbolTable,
|
||||
entry->name, entry->type, entry);
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
registerCommand(const char *name,
|
||||
CommandArgumentType argType, CommandHook hook, void *cookie)
|
||||
{
|
||||
return registerCommandInternal(name,
|
||||
CMD_TYPE_COMMAND, argType, hook, cookie);
|
||||
}
|
||||
|
||||
int
|
||||
registerFunction(const char *name, FunctionHook hook, void *cookie)
|
||||
{
|
||||
return registerCommandInternal(name,
|
||||
CMD_TYPE_FUNCTION, CMD_ARGS_WORDS, (CommandHook)hook, cookie);
|
||||
}
|
||||
|
||||
Command *
|
||||
findCommand(const char *name)
|
||||
{
|
||||
return (Command *)findInSymbolTable(gCommandState.symbolTable,
|
||||
name, CMD_TYPE_COMMAND);
|
||||
}
|
||||
|
||||
Function *
|
||||
findFunction(const char *name)
|
||||
{
|
||||
return (Function *)findInSymbolTable(gCommandState.symbolTable,
|
||||
name, CMD_TYPE_FUNCTION);
|
||||
}
|
||||
|
||||
CommandArgumentType
|
||||
getCommandArgumentType(Command *cmd)
|
||||
{
|
||||
CommandEntry *entry = (CommandEntry *)cmd;
|
||||
|
||||
if (entry != NULL) {
|
||||
return entry->argType;
|
||||
}
|
||||
return CMD_ARGS_UNKNOWN;
|
||||
}
|
||||
|
||||
static int
|
||||
callCommandInternal(CommandEntry *entry, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
|
||||
(argc == 0 || (argc > 0 && argv != NULL)))
|
||||
{
|
||||
if (permissions == NULL) {
|
||||
int i;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (argv[i] == NULL) {
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("calling command %s\n", entry->name);
|
||||
return entry->hook(entry->name, entry->cookie, argc, argv, permissions);
|
||||
//xxx if permissions, make sure the entry has added at least one element.
|
||||
}
|
||||
bail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
callBooleanCommandInternal(CommandEntry *entry, bool arg,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
if (entry != NULL && entry->argType == CMD_ARGS_BOOLEAN) {
|
||||
TRACE("calling boolean command %s\n", entry->name);
|
||||
return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL,
|
||||
permissions);
|
||||
//xxx if permissions, make sure the entry has added at least one element.
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
callCommand(Command *cmd, int argc, const char *argv[])
|
||||
{
|
||||
return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
callBooleanCommand(Command *cmd, bool arg)
|
||||
{
|
||||
return callBooleanCommandInternal((CommandEntry *)cmd, arg, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
getCommandPermissions(Command *cmd, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
if (permissions != NULL) {
|
||||
return callCommandInternal((CommandEntry *)cmd, argc, argv,
|
||||
permissions);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
getBooleanCommandPermissions(Command *cmd, bool arg,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
if (permissions != NULL) {
|
||||
return callBooleanCommandInternal((CommandEntry *)cmd, arg,
|
||||
permissions);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
callFunctionInternal(CommandEntry *entry, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen, PermissionRequestList *permissions)
|
||||
{
|
||||
if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
|
||||
(argc == 0 || (argc > 0 && argv != NULL)))
|
||||
{
|
||||
if ((permissions == NULL && result != NULL) ||
|
||||
(permissions != NULL && result == NULL))
|
||||
{
|
||||
if (permissions == NULL) {
|
||||
/* This is the actual invocation of the function,
|
||||
* which means that none of the arguments are allowed
|
||||
* to be NULL.
|
||||
*/
|
||||
int i;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (argv[i] == NULL) {
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("calling function %s\n", entry->name);
|
||||
return ((FunctionHook)entry->hook)(entry->name, entry->cookie,
|
||||
argc, argv, result, resultLen, permissions);
|
||||
//xxx if permissions, make sure the entry has added at least one element.
|
||||
}
|
||||
}
|
||||
bail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
callFunction(Function *fn, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen)
|
||||
{
|
||||
return callFunctionInternal((CommandEntry *)fn, argc, argv,
|
||||
result, resultLen, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
getFunctionPermissions(Function *fn, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
if (permissions != NULL) {
|
||||
return callFunctionInternal((CommandEntry *)fn, argc, argv,
|
||||
NULL, NULL, permissions);
|
||||
}
|
||||
return -1;
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_COMMANDS_H_
|
||||
#define AMEND_COMMANDS_H_
|
||||
|
||||
#include "permissions.h"
|
||||
|
||||
/* Invoke or dry-run a command. If "permissions" is non-NULL,
|
||||
* the hook should fill it out with the list of files and operations that
|
||||
* it would need to complete its operation. If "permissions" is NULL,
|
||||
* the hook should do the actual work specified by its arguments.
|
||||
*
|
||||
* When a command is called with non-NULL "permissions", some arguments
|
||||
* may be NULL. A NULL argument indicates that the argument is actually
|
||||
* the output of another function, so is not known at permissions time.
|
||||
* The permissions of leaf-node functions (those that have only literal
|
||||
* strings as arguments) will get appended to the permissions of the
|
||||
* functions that call them. However, to be completely safe, functions
|
||||
* that receive a NULL argument should request the broadest-possible
|
||||
* permissions for the range of the input argument.
|
||||
*
|
||||
* When a boolean command is called, "argc" is the boolean value and
|
||||
* "argv" is NULL.
|
||||
*/
|
||||
typedef int (*CommandHook)(const char *name, void *cookie,
|
||||
int argc, const char *argv[],
|
||||
PermissionRequestList *permissions);
|
||||
|
||||
int commandInit(void);
|
||||
void commandCleanup(void);
|
||||
|
||||
/*
|
||||
* Command management
|
||||
*/
|
||||
|
||||
struct Command;
|
||||
typedef struct Command Command;
|
||||
|
||||
typedef enum {
|
||||
CMD_ARGS_UNKNOWN = -1,
|
||||
CMD_ARGS_BOOLEAN = 0,
|
||||
CMD_ARGS_WORDS
|
||||
} CommandArgumentType;
|
||||
|
||||
int registerCommand(const char *name,
|
||||
CommandArgumentType argType, CommandHook hook, void *cookie);
|
||||
|
||||
Command *findCommand(const char *name);
|
||||
|
||||
CommandArgumentType getCommandArgumentType(Command *cmd);
|
||||
|
||||
int callCommand(Command *cmd, int argc, const char *argv[]);
|
||||
int callBooleanCommand(Command *cmd, bool arg);
|
||||
|
||||
int getCommandPermissions(Command *cmd, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions);
|
||||
int getBooleanCommandPermissions(Command *cmd, bool arg,
|
||||
PermissionRequestList *permissions);
|
||||
|
||||
/*
|
||||
* Function management
|
||||
*/
|
||||
|
||||
typedef int (*FunctionHook)(const char *name, void *cookie,
|
||||
int argc, const char *argv[],
|
||||
char **result, size_t *resultLen,
|
||||
PermissionRequestList *permissions);
|
||||
|
||||
struct Function;
|
||||
typedef struct Function Function;
|
||||
|
||||
int registerFunction(const char *name, FunctionHook hook, void *cookie);
|
||||
|
||||
Function *findFunction(const char *name);
|
||||
|
||||
int callFunction(Function *fn, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen);
|
||||
|
||||
int getFunctionPermissions(Function *fn, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions);
|
||||
|
||||
#endif // AMEND_COMMANDS_H_
|
315
amend/execute.c
315
amend/execute.c
@ -1,315 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include "ast.h"
|
||||
#include "execute.h"
|
||||
|
||||
typedef struct {
|
||||
int c;
|
||||
const char **v;
|
||||
} StringList;
|
||||
|
||||
static int execBooleanValue(ExecContext *ctx,
|
||||
const AmBooleanValue *booleanValue, bool *result);
|
||||
static int execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
|
||||
const char **result);
|
||||
|
||||
static int
|
||||
execBooleanExpression(ExecContext *ctx,
|
||||
const AmBooleanExpression *booleanExpression, bool *result)
|
||||
{
|
||||
int ret;
|
||||
bool arg1, arg2;
|
||||
bool unary;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(booleanExpression != NULL);
|
||||
assert(result != NULL);
|
||||
if (ctx == NULL || booleanExpression == NULL || result == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
if (booleanExpression->op == AM_BOP_NOT) {
|
||||
unary = true;
|
||||
} else {
|
||||
unary = false;
|
||||
}
|
||||
|
||||
ret = execBooleanValue(ctx, booleanExpression->arg1, &arg1);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
if (!unary) {
|
||||
ret = execBooleanValue(ctx, booleanExpression->arg2, &arg2);
|
||||
if (ret != 0) return ret;
|
||||
} else {
|
||||
arg2 = false;
|
||||
}
|
||||
|
||||
switch (booleanExpression->op) {
|
||||
case AM_BOP_NOT:
|
||||
*result = !arg1;
|
||||
break;
|
||||
case AM_BOP_EQ:
|
||||
*result = (arg1 == arg2);
|
||||
break;
|
||||
case AM_BOP_NE:
|
||||
*result = (arg1 != arg2);
|
||||
break;
|
||||
case AM_BOP_AND:
|
||||
*result = (arg1 && arg2);
|
||||
break;
|
||||
case AM_BOP_OR:
|
||||
*result = (arg1 || arg2);
|
||||
break;
|
||||
default:
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
execFunctionArguments(ExecContext *ctx,
|
||||
const AmFunctionArguments *functionArguments, StringList *result)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(functionArguments != NULL);
|
||||
assert(result != NULL);
|
||||
if (ctx == NULL || functionArguments == NULL || result == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
result->c = functionArguments->argc;
|
||||
result->v = (const char **)malloc(result->c * sizeof(const char *));
|
||||
if (result->v == NULL) {
|
||||
result->c = 0;
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < functionArguments->argc; i++) {
|
||||
ret = execStringValue(ctx, &functionArguments->argv[i], &result->v[i]);
|
||||
if (ret != 0) {
|
||||
result->c = 0;
|
||||
free(result->v);
|
||||
//TODO: free the individual args, if we're responsible for them.
|
||||
result->v = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
execFunctionCall(ExecContext *ctx, const AmFunctionCall *functionCall,
|
||||
const char **result)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(functionCall != NULL);
|
||||
assert(result != NULL);
|
||||
if (ctx == NULL || functionCall == NULL || result == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
StringList args;
|
||||
ret = execFunctionArguments(ctx, functionCall->args, &args);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = callFunction(functionCall->fn, args.c, args.v, (char **)result, NULL);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
//TODO: clean up args
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
|
||||
const char **result)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(stringValue != NULL);
|
||||
assert(result != NULL);
|
||||
if (ctx == NULL || stringValue == NULL || result == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
switch (stringValue->type) {
|
||||
case AM_SVAL_LITERAL:
|
||||
*result = strdup(stringValue->u.literal);
|
||||
break;
|
||||
case AM_SVAL_FUNCTION:
|
||||
ret = execFunctionCall(ctx, stringValue->u.function, result);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
execStringComparisonExpression(ExecContext *ctx,
|
||||
const AmStringComparisonExpression *stringComparisonExpression,
|
||||
bool *result)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(stringComparisonExpression != NULL);
|
||||
assert(result != NULL);
|
||||
if (ctx == NULL || stringComparisonExpression == NULL || result == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
const char *arg1, *arg2;
|
||||
ret = execStringValue(ctx, stringComparisonExpression->arg1, &arg1);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = execStringValue(ctx, stringComparisonExpression->arg2, &arg2);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmp = strcmp(arg1, arg2);
|
||||
|
||||
switch (stringComparisonExpression->op) {
|
||||
case AM_SOP_LT:
|
||||
*result = (cmp < 0);
|
||||
break;
|
||||
case AM_SOP_LE:
|
||||
*result = (cmp <= 0);
|
||||
break;
|
||||
case AM_SOP_GT:
|
||||
*result = (cmp > 0);
|
||||
break;
|
||||
case AM_SOP_GE:
|
||||
*result = (cmp >= 0);
|
||||
break;
|
||||
case AM_SOP_EQ:
|
||||
*result = (cmp == 0);
|
||||
break;
|
||||
case AM_SOP_NE:
|
||||
*result = (cmp != 0);
|
||||
break;
|
||||
default:
|
||||
return -__LINE__;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
execBooleanValue(ExecContext *ctx, const AmBooleanValue *booleanValue,
|
||||
bool *result)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(booleanValue != NULL);
|
||||
assert(result != NULL);
|
||||
if (ctx == NULL || booleanValue == NULL || result == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
switch (booleanValue->type) {
|
||||
case AM_BVAL_EXPRESSION:
|
||||
ret = execBooleanExpression(ctx, &booleanValue->u.expression, result);
|
||||
break;
|
||||
case AM_BVAL_STRING_COMPARISON:
|
||||
ret = execStringComparisonExpression(ctx,
|
||||
&booleanValue->u.stringComparison, result);
|
||||
break;
|
||||
default:
|
||||
ret = -__LINE__;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
execCommand(ExecContext *ctx, const AmCommand *command)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(ctx != NULL);
|
||||
assert(command != NULL);
|
||||
if (ctx == NULL || command == NULL) {
|
||||
return -__LINE__;
|
||||
}
|
||||
|
||||
CommandArgumentType argType;
|
||||
argType = getCommandArgumentType(command->cmd);
|
||||
switch (argType) {
|
||||
case CMD_ARGS_BOOLEAN:
|
||||
{
|
||||
bool bVal;
|
||||
ret = execBooleanValue(ctx, command->args->u.b, &bVal);
|
||||
if (ret == 0) {
|
||||
ret = callBooleanCommand(command->cmd, bVal);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CMD_ARGS_WORDS:
|
||||
{
|
||||
AmWordList *words = command->args->u.w;
|
||||
ret = callCommand(command->cmd, words->argc, words->argv);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -__LINE__;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
execCommandList(ExecContext *ctx, const AmCommandList *commandList)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < commandList->commandCount; i++) {
|
||||
int ret = execCommand(ctx, commandList->commands[i]);
|
||||
if (ret != 0) {
|
||||
int line = commandList->commands[i]->line;
|
||||
return line > 0 ? line : ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_EXECUTE_H_
|
||||
#define AMEND_EXECUTE_H_
|
||||
|
||||
typedef struct ExecContext ExecContext;
|
||||
|
||||
/* Returns 0 on success, otherwise the line number that failed. */
|
||||
int execCommandList(ExecContext *ctx, const AmCommandList *commandList);
|
||||
|
||||
#endif // AMEND_EXECUTE_H_
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_LEXER_H_
|
||||
#define AMEND_LEXER_H_
|
||||
|
||||
#define AMEND_LEXER_BUFFER_INPUT 1
|
||||
|
||||
void yyerror(const char *msg);
|
||||
int yylex(void);
|
||||
|
||||
#if AMEND_LEXER_BUFFER_INPUT
|
||||
void setLexerInputBuffer(const char *buf, size_t buflen);
|
||||
#else
|
||||
#include <stdio.h>
|
||||
void yyset_in(FILE *in_str);
|
||||
#endif
|
||||
|
||||
const char *tokenToString(int token);
|
||||
|
||||
typedef enum {
|
||||
AM_UNKNOWN_ARGS,
|
||||
AM_WORD_ARGS,
|
||||
AM_BOOLEAN_ARGS,
|
||||
} AmArgumentType;
|
||||
|
||||
void setLexerArgumentType(AmArgumentType type);
|
||||
int getLexerLineNumber(void);
|
||||
|
||||
#endif // AMEND_LEXER_H_
|
299
amend/lexer.l
299
amend/lexer.l
@ -1,299 +0,0 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "ast.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
|
||||
const char *tokenToString(int token)
|
||||
{
|
||||
static char scratch[128];
|
||||
|
||||
switch (token) {
|
||||
case TOK_AND:
|
||||
return "&&";
|
||||
case TOK_OR:
|
||||
return "||";
|
||||
case TOK_EQ:
|
||||
return "==";
|
||||
case TOK_NE:
|
||||
return "!=";
|
||||
case TOK_GE:
|
||||
return ">=";
|
||||
case TOK_LE:
|
||||
return "<=";
|
||||
case TOK_EOF:
|
||||
return "EOF";
|
||||
case TOK_EOL:
|
||||
return "EOL\n";
|
||||
case TOK_STRING:
|
||||
snprintf(scratch, sizeof(scratch),
|
||||
"STRING<%s>", yylval.literalString);
|
||||
return scratch;
|
||||
case TOK_IDENTIFIER:
|
||||
snprintf(scratch, sizeof(scratch), "IDENTIFIER<%s>",
|
||||
yylval.literalString);
|
||||
return scratch;
|
||||
case TOK_WORD:
|
||||
snprintf(scratch, sizeof(scratch), "WORD<%s>",
|
||||
yylval.literalString);
|
||||
return scratch;
|
||||
default:
|
||||
if (token > ' ' && token <= '~') {
|
||||
scratch[0] = (char)token;
|
||||
scratch[1] = '\0';
|
||||
} else {
|
||||
snprintf(scratch, sizeof(scratch), "??? <%d>", token);
|
||||
}
|
||||
return scratch;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *value;
|
||||
char *nextc;
|
||||
unsigned int alloc_size;
|
||||
} AmString;
|
||||
|
||||
static int addCharToString(AmString *str, char c)
|
||||
{
|
||||
if ((unsigned int)(str->nextc - str->value) >= str->alloc_size) {
|
||||
char *new_value;
|
||||
unsigned int new_size;
|
||||
|
||||
new_size = (str->alloc_size + 1) * 2;
|
||||
if (new_size < 64) {
|
||||
new_size = 64;
|
||||
}
|
||||
|
||||
new_value = (char *)realloc(str->value, new_size);
|
||||
if (new_value == NULL) {
|
||||
yyerror("out of memory");
|
||||
return -1;
|
||||
}
|
||||
str->nextc = str->nextc - str->value + new_value;
|
||||
str->value = new_value;
|
||||
str->alloc_size = new_size;
|
||||
}
|
||||
*str->nextc++ = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setString(AmString *str, const char *p)
|
||||
{
|
||||
str->nextc = str->value;
|
||||
while (*p != '\0') {
|
||||
//TODO: add the whole string at once
|
||||
addCharToString(str, *p++);
|
||||
}
|
||||
return addCharToString(str, '\0');
|
||||
}
|
||||
|
||||
static AmString gStr = { NULL, NULL, 0 };
|
||||
static int gLineNumber = 1;
|
||||
static AmArgumentType gArgumentType = AM_UNKNOWN_ARGS;
|
||||
static const char *gErrorMessage = NULL;
|
||||
|
||||
#if AMEND_LEXER_BUFFER_INPUT
|
||||
static const char *gInputBuffer;
|
||||
static const char *gInputBufferNext;
|
||||
static const char *gInputBufferEnd;
|
||||
|
||||
# define YY_INPUT(buf, result, max_size) \
|
||||
do { \
|
||||
int nbytes = gInputBufferEnd - gInputBufferNext; \
|
||||
if (nbytes > 0) { \
|
||||
if (nbytes > max_size) { \
|
||||
nbytes = max_size; \
|
||||
} \
|
||||
memcpy(buf, gInputBufferNext, nbytes); \
|
||||
gInputBufferNext += nbytes; \
|
||||
result = nbytes; \
|
||||
} else { \
|
||||
result = YY_NULL; \
|
||||
} \
|
||||
} while (false)
|
||||
#endif // AMEND_LEXER_BUFFER_INPUT
|
||||
|
||||
%}
|
||||
|
||||
%option noyywrap
|
||||
|
||||
%x QUOTED_STRING BOOLEAN WORDS
|
||||
|
||||
ident [a-zA-Z_][a-zA-Z_0-9]*
|
||||
word [^ \t\r\n"]+
|
||||
|
||||
%%
|
||||
/* This happens at the beginning of each call to yylex().
|
||||
*/
|
||||
if (gArgumentType == AM_WORD_ARGS) {
|
||||
BEGIN(WORDS);
|
||||
} else if (gArgumentType == AM_BOOLEAN_ARGS) {
|
||||
BEGIN(BOOLEAN);
|
||||
}
|
||||
|
||||
/*xxx require everything to be 7-bit-clean, printable characters */
|
||||
<INITIAL>{
|
||||
{ident}/[ \t\r\n] {
|
||||
/* The only token we recognize in the initial
|
||||
* state is an identifier followed by whitespace.
|
||||
*/
|
||||
setString(&gStr, yytext);
|
||||
yylval.literalString = gStr.value;
|
||||
return TOK_IDENTIFIER;
|
||||
}
|
||||
}
|
||||
|
||||
<BOOLEAN>{
|
||||
{ident} {
|
||||
/* Non-quoted identifier-style string */
|
||||
setString(&gStr, yytext);
|
||||
yylval.literalString = gStr.value;
|
||||
return TOK_IDENTIFIER;
|
||||
}
|
||||
"&&" return TOK_AND;
|
||||
"||" return TOK_OR;
|
||||
"==" return TOK_EQ;
|
||||
"!=" return TOK_NE;
|
||||
">=" return TOK_GE;
|
||||
"<=" return TOK_LE;
|
||||
[<>()!,] return yytext[0];
|
||||
}
|
||||
|
||||
/* Double-quoted string handling */
|
||||
|
||||
<WORDS,BOOLEAN>\" {
|
||||
/* Initial quote */
|
||||
gStr.nextc = gStr.value;
|
||||
BEGIN(QUOTED_STRING);
|
||||
}
|
||||
|
||||
<QUOTED_STRING>{
|
||||
\" {
|
||||
/* Closing quote */
|
||||
BEGIN(INITIAL);
|
||||
addCharToString(&gStr, '\0');
|
||||
yylval.literalString = gStr.value;
|
||||
if (gArgumentType == AM_WORD_ARGS) {
|
||||
return TOK_WORD;
|
||||
} else {
|
||||
return TOK_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
<<EOF>> |
|
||||
\n {
|
||||
/* Unterminated string */
|
||||
yyerror("unterminated string");
|
||||
return TOK_ERROR;
|
||||
}
|
||||
|
||||
\\\" {
|
||||
/* Escaped quote */
|
||||
addCharToString(&gStr, '"');
|
||||
}
|
||||
|
||||
\\\\ {
|
||||
/* Escaped backslash */
|
||||
addCharToString(&gStr, '\\');
|
||||
}
|
||||
|
||||
\\. {
|
||||
/* No other escapes allowed. */
|
||||
gErrorMessage = "illegal escape";
|
||||
return TOK_ERROR;
|
||||
}
|
||||
|
||||
[^\\\n\"]+ {
|
||||
/* String contents */
|
||||
char *p = yytext;
|
||||
while (*p != '\0') {
|
||||
/* TODO: add the whole string at once */
|
||||
addCharToString(&gStr, *p++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<WORDS>{
|
||||
/*xxx look out for backslashes; escape backslashes and quotes */
|
||||
/*xxx if a quote is right against a char, we should append */
|
||||
{word} {
|
||||
/* Whitespace-separated word */
|
||||
setString(&gStr, yytext);
|
||||
yylval.literalString = gStr.value;
|
||||
return TOK_WORD;
|
||||
}
|
||||
}
|
||||
|
||||
<INITIAL,WORDS,BOOLEAN>{
|
||||
\n {
|
||||
/* Count lines */
|
||||
gLineNumber++;
|
||||
gArgumentType = AM_UNKNOWN_ARGS;
|
||||
BEGIN(INITIAL);
|
||||
return TOK_EOL;
|
||||
}
|
||||
|
||||
/*xxx backslashes to extend lines? */
|
||||
/* Skip whitespace and comments.
|
||||
*/
|
||||
[ \t\r]+ ;
|
||||
#.* ;
|
||||
|
||||
. {
|
||||
/* Fail on anything we didn't expect. */
|
||||
gErrorMessage = "unexpected character";
|
||||
return TOK_ERROR;
|
||||
}
|
||||
}
|
||||
%%
|
||||
|
||||
void
|
||||
yyerror(const char *msg)
|
||||
{
|
||||
if (!strcmp(msg, "syntax error") && gErrorMessage != NULL) {
|
||||
msg = gErrorMessage;
|
||||
gErrorMessage = NULL;
|
||||
}
|
||||
fprintf(stderr, "line %d: %s at '%s'\n", gLineNumber, msg, yytext);
|
||||
}
|
||||
|
||||
#if AMEND_LEXER_BUFFER_INPUT
|
||||
void
|
||||
setLexerInputBuffer(const char *buf, size_t buflen)
|
||||
{
|
||||
gLineNumber = 1;
|
||||
gInputBuffer = buf;
|
||||
gInputBufferNext = gInputBuffer;
|
||||
gInputBufferEnd = gInputBuffer + buflen;
|
||||
}
|
||||
#endif // AMEND_LEXER_BUFFER_INPUT
|
||||
|
||||
void
|
||||
setLexerArgumentType(AmArgumentType type)
|
||||
{
|
||||
gArgumentType = type;
|
||||
}
|
||||
|
||||
int
|
||||
getLexerLineNumber(void)
|
||||
{
|
||||
return gLineNumber;
|
||||
}
|
195
amend/main.c
195
amend/main.c
@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ast.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "register.h"
|
||||
#include "execute.h"
|
||||
|
||||
void
|
||||
lexTest()
|
||||
{
|
||||
int token;
|
||||
do {
|
||||
token = yylex();
|
||||
if (token == 0) {
|
||||
printf(" EOF");
|
||||
fflush(stdout);
|
||||
break;
|
||||
} else {
|
||||
printf(" %s", tokenToString(token));
|
||||
fflush(stdout);
|
||||
if (token == TOK_IDENTIFIER) {
|
||||
if (strcmp(yylval.literalString, "assert") == 0) {
|
||||
setLexerArgumentType(AM_BOOLEAN_ARGS);
|
||||
} else {
|
||||
setLexerArgumentType(AM_WORD_ARGS);
|
||||
}
|
||||
do {
|
||||
token = yylex();
|
||||
printf(" %s", tokenToString(token));
|
||||
fflush(stdout);
|
||||
} while (token != TOK_EOL && token != TOK_EOF && token != 0);
|
||||
} else if (token != TOK_EOL) {
|
||||
fprintf(stderr, "syntax error: expected identifier\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (token != 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
printf("usage: amend [--debug-lex|--debug-ast] [<filename>]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
extern const AmCommandList *gCommands;
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
FILE *inputFile = NULL;
|
||||
bool debugLex = false;
|
||||
bool debugAst = false;
|
||||
const char *fileName = NULL;
|
||||
int err;
|
||||
|
||||
#if 1
|
||||
extern int test_symtab(void);
|
||||
int ret = test_symtab();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_symtab() failed: %d\n", ret);
|
||||
exit(ret);
|
||||
}
|
||||
extern int test_cmd_fn(void);
|
||||
ret = test_cmd_fn();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_cmd_fn() failed: %d\n", ret);
|
||||
exit(ret);
|
||||
}
|
||||
extern int test_permissions(void);
|
||||
ret = test_permissions();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_permissions() failed: %d\n", ret);
|
||||
exit(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
while (argc > 0) {
|
||||
if (strcmp("--debug-lex", argv[0]) == 0) {
|
||||
debugLex = true;
|
||||
} else if (strcmp("--debug-ast", argv[0]) == 0) {
|
||||
debugAst = true;
|
||||
} else if (argv[0][0] == '-') {
|
||||
fprintf(stderr, "amend: Unknown option \"%s\"\n", argv[0]);
|
||||
usage();
|
||||
} else {
|
||||
fileName = argv[0];
|
||||
}
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (fileName != NULL) {
|
||||
inputFile = fopen(fileName, "r");
|
||||
if (inputFile == NULL) {
|
||||
fprintf(stderr, "amend: Can't open input file '%s'\n", fileName);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
commandInit();
|
||||
//xxx clean up
|
||||
|
||||
err = registerUpdateCommands();
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "amend: Error registering commands: %d\n", err);
|
||||
exit(-err);
|
||||
}
|
||||
err = registerUpdateFunctions();
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "amend: Error registering functions: %d\n", err);
|
||||
exit(-err);
|
||||
}
|
||||
|
||||
#if AMEND_LEXER_BUFFER_INPUT
|
||||
if (inputFile == NULL) {
|
||||
fprintf(stderr, "amend: No input file\n");
|
||||
usage();
|
||||
}
|
||||
char *fileData;
|
||||
int fileDataLen;
|
||||
fseek(inputFile, 0, SEEK_END);
|
||||
fileDataLen = ftell(inputFile);
|
||||
rewind(inputFile);
|
||||
if (fileDataLen < 0) {
|
||||
fprintf(stderr, "amend: Can't get file length\n");
|
||||
exit(2);
|
||||
} else if (fileDataLen == 0) {
|
||||
printf("amend: Empty input file\n");
|
||||
exit(0);
|
||||
}
|
||||
fileData = (char *)malloc(fileDataLen + 1);
|
||||
if (fileData == NULL) {
|
||||
fprintf(stderr, "amend: Can't allocate %d bytes\n", fileDataLen + 1);
|
||||
exit(2);
|
||||
}
|
||||
size_t nread = fread(fileData, 1, fileDataLen, inputFile);
|
||||
if (nread != (size_t)fileDataLen) {
|
||||
fprintf(stderr, "amend: Didn't read %d bytes, only %zd\n", fileDataLen,
|
||||
nread);
|
||||
exit(2);
|
||||
}
|
||||
fileData[fileDataLen] = '\0';
|
||||
setLexerInputBuffer(fileData, fileDataLen);
|
||||
#else
|
||||
if (inputFile == NULL) {
|
||||
inputFile = stdin;
|
||||
}
|
||||
yyset_in(inputFile);
|
||||
#endif
|
||||
|
||||
if (debugLex) {
|
||||
lexTest();
|
||||
} else {
|
||||
int ret = yyparse();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "amend: Parse failed (%d)\n", ret);
|
||||
exit(2);
|
||||
} else {
|
||||
if (debugAst) {
|
||||
dumpCommandList(gCommands);
|
||||
}
|
||||
printf("amend: Parse successful.\n");
|
||||
ret = execCommandList((ExecContext *)1, gCommands);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "amend: Execution failed (%d)\n", ret);
|
||||
exit(3);
|
||||
}
|
||||
printf("amend: Execution successful.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
430
amend/parser_y.y
430
amend/parser_y.y
@ -1,430 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
%{
|
||||
#undef NDEBUG
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "ast.h"
|
||||
#include "lexer.h"
|
||||
#include "commands.h"
|
||||
|
||||
void yyerror(const char *msg);
|
||||
int yylex(void);
|
||||
|
||||
#define STRING_COMPARISON(out, a1, sop, a2) \
|
||||
do { \
|
||||
out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
|
||||
if (out == NULL) { \
|
||||
YYABORT; \
|
||||
} \
|
||||
out->type = AM_BVAL_STRING_COMPARISON; \
|
||||
out->u.stringComparison.op = sop; \
|
||||
out->u.stringComparison.arg1 = a1; \
|
||||
out->u.stringComparison.arg2 = a2; \
|
||||
} while (false)
|
||||
|
||||
#define BOOLEAN_EXPRESSION(out, a1, bop, a2) \
|
||||
do { \
|
||||
out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
|
||||
if (out == NULL) { \
|
||||
YYABORT; \
|
||||
} \
|
||||
out->type = AM_BVAL_EXPRESSION; \
|
||||
out->u.expression.op = bop; \
|
||||
out->u.expression.arg1 = a1; \
|
||||
out->u.expression.arg2 = a2; \
|
||||
} while (false)
|
||||
|
||||
AmCommandList *gCommands = NULL;
|
||||
%}
|
||||
|
||||
%start lines
|
||||
|
||||
%union {
|
||||
char *literalString;
|
||||
AmFunctionArgumentBuilder *functionArgumentBuilder;
|
||||
AmFunctionArguments *functionArguments;
|
||||
AmFunctionCall *functionCall;
|
||||
AmStringValue *stringValue;
|
||||
AmBooleanValue *booleanValue;
|
||||
AmWordListBuilder *wordListBuilder;
|
||||
AmCommandArguments *commandArguments;
|
||||
AmCommand *command;
|
||||
AmCommandList *commandList;
|
||||
}
|
||||
|
||||
%token TOK_AND TOK_OR TOK_EQ TOK_NE TOK_GE TOK_LE TOK_EOF TOK_EOL TOK_ERROR
|
||||
%token <literalString> TOK_STRING TOK_IDENTIFIER TOK_WORD
|
||||
|
||||
%type <commandList> lines
|
||||
%type <command> command line
|
||||
%type <functionArgumentBuilder> function_arguments
|
||||
%type <functionArguments> function_arguments_or_empty
|
||||
%type <functionCall> function_call
|
||||
%type <literalString> function_name
|
||||
%type <stringValue> string_value
|
||||
%type <booleanValue> boolean_expression
|
||||
%type <wordListBuilder> word_list
|
||||
%type <commandArguments> arguments
|
||||
|
||||
/* Operator precedence, weakest to strongest.
|
||||
* Same as C/Java precedence.
|
||||
*/
|
||||
|
||||
%left TOK_OR
|
||||
%left TOK_AND
|
||||
%left TOK_EQ TOK_NE
|
||||
%left '<' '>' TOK_LE TOK_GE
|
||||
%right '!'
|
||||
|
||||
%%
|
||||
|
||||
lines : /* empty */
|
||||
{
|
||||
$$ = (AmCommandList *)malloc(sizeof(AmCommandList));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
gCommands = $$;
|
||||
$$->arraySize = 64;
|
||||
$$->commandCount = 0;
|
||||
$$->commands = (AmCommand **)malloc(
|
||||
sizeof(AmCommand *) * $$->arraySize);
|
||||
if ($$->commands == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
| lines line
|
||||
{
|
||||
if ($2 != NULL) {
|
||||
if ($1->commandCount >= $1->arraySize) {
|
||||
AmCommand **newArray;
|
||||
newArray = (AmCommand **)realloc($$->commands,
|
||||
sizeof(AmCommand *) * $$->arraySize * 2);
|
||||
if (newArray == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->commands = newArray;
|
||||
$$->arraySize *= 2;
|
||||
}
|
||||
$1->commands[$1->commandCount++] = $2;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
line : line_ending
|
||||
{
|
||||
$$ = NULL; /* ignore blank lines */
|
||||
}
|
||||
| command arguments line_ending
|
||||
{
|
||||
$$ = $1;
|
||||
$$->args = $2;
|
||||
setLexerArgumentType(AM_UNKNOWN_ARGS);
|
||||
}
|
||||
;
|
||||
|
||||
command : TOK_IDENTIFIER
|
||||
{
|
||||
Command *cmd = findCommand($1);
|
||||
if (cmd == NULL) {
|
||||
fprintf(stderr, "Unknown command \"%s\"\n", $1);
|
||||
YYABORT;
|
||||
}
|
||||
$$ = (AmCommand *)malloc(sizeof(AmCommand));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->line = getLexerLineNumber();
|
||||
$$->name = strdup($1);
|
||||
if ($$->name == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->args = NULL;
|
||||
CommandArgumentType argType = getCommandArgumentType(cmd);
|
||||
if (argType == CMD_ARGS_BOOLEAN) {
|
||||
setLexerArgumentType(AM_BOOLEAN_ARGS);
|
||||
} else {
|
||||
setLexerArgumentType(AM_WORD_ARGS);
|
||||
}
|
||||
$$->cmd = cmd;
|
||||
}
|
||||
;
|
||||
|
||||
line_ending :
|
||||
TOK_EOL
|
||||
| TOK_EOF
|
||||
;
|
||||
|
||||
arguments : boolean_expression
|
||||
{
|
||||
$$ = (AmCommandArguments *)malloc(
|
||||
sizeof(AmCommandArguments));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->booleanArgs = true;
|
||||
$$->u.b = $1;
|
||||
}
|
||||
| word_list
|
||||
{
|
||||
/* Convert the builder list into an array.
|
||||
* Do it in reverse order; the words were pushed
|
||||
* onto the list in LIFO order.
|
||||
*/
|
||||
AmWordList *w = (AmWordList *)malloc(sizeof(AmWordList));
|
||||
if (w == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
if ($1 != NULL) {
|
||||
AmWordListBuilder *words = $1;
|
||||
|
||||
w->argc = words->wordCount;
|
||||
w->argv = (const char **)malloc(w->argc *
|
||||
sizeof(char *));
|
||||
if (w->argv == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
int i;
|
||||
for (i = w->argc; words != NULL && i > 0; --i) {
|
||||
AmWordListBuilder *f = words;
|
||||
w->argv[i-1] = words->word;
|
||||
words = words->next;
|
||||
free(f);
|
||||
}
|
||||
assert(i == 0);
|
||||
assert(words == NULL);
|
||||
} else {
|
||||
w->argc = 0;
|
||||
w->argv = NULL;
|
||||
}
|
||||
$$ = (AmCommandArguments *)malloc(
|
||||
sizeof(AmCommandArguments));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->booleanArgs = false;
|
||||
$$->u.w = w;
|
||||
}
|
||||
;
|
||||
|
||||
word_list : /* empty */
|
||||
{ $$ = NULL; }
|
||||
| word_list TOK_WORD
|
||||
{
|
||||
if ($1 == NULL) {
|
||||
$$ = (AmWordListBuilder *)malloc(
|
||||
sizeof(AmWordListBuilder));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->next = NULL;
|
||||
$$->wordCount = 1;
|
||||
} else {
|
||||
$$ = (AmWordListBuilder *)malloc(
|
||||
sizeof(AmWordListBuilder));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->next = $1;
|
||||
$$->wordCount = $$->next->wordCount + 1;
|
||||
}
|
||||
$$->word = strdup($2);
|
||||
if ($$->word == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
boolean_expression :
|
||||
'!' boolean_expression
|
||||
{
|
||||
$$ = (AmBooleanValue *)malloc(sizeof(AmBooleanValue));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->type = AM_BVAL_EXPRESSION;
|
||||
$$->u.expression.op = AM_BOP_NOT;
|
||||
$$->u.expression.arg1 = $2;
|
||||
$$->u.expression.arg2 = NULL;
|
||||
}
|
||||
/* TODO: if both expressions are literals, evaluate now */
|
||||
| boolean_expression TOK_AND boolean_expression
|
||||
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_AND, $3); }
|
||||
| boolean_expression TOK_OR boolean_expression
|
||||
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_OR, $3); }
|
||||
| boolean_expression TOK_EQ boolean_expression
|
||||
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_EQ, $3); }
|
||||
| boolean_expression TOK_NE boolean_expression
|
||||
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_NE, $3); }
|
||||
| '(' boolean_expression ')'
|
||||
{ $$ = $2; }
|
||||
/* TODO: if both strings are literals, evaluate now */
|
||||
| string_value '<' string_value
|
||||
{ STRING_COMPARISON($$, $1, AM_SOP_LT, $3); }
|
||||
| string_value '>' string_value
|
||||
{ STRING_COMPARISON($$, $1, AM_SOP_GT, $3); }
|
||||
| string_value TOK_EQ string_value
|
||||
{ STRING_COMPARISON($$, $1, AM_SOP_EQ, $3); }
|
||||
| string_value TOK_NE string_value
|
||||
{ STRING_COMPARISON($$, $1, AM_SOP_NE, $3); }
|
||||
| string_value TOK_LE string_value
|
||||
{ STRING_COMPARISON($$, $1, AM_SOP_LE, $3); }
|
||||
| string_value TOK_GE string_value
|
||||
{ STRING_COMPARISON($$, $1, AM_SOP_GE, $3); }
|
||||
;
|
||||
|
||||
string_value :
|
||||
TOK_IDENTIFIER
|
||||
{
|
||||
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->type = AM_SVAL_LITERAL;
|
||||
$$->u.literal = strdup($1);
|
||||
if ($$->u.literal == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
| TOK_STRING
|
||||
{
|
||||
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->type = AM_SVAL_LITERAL;
|
||||
$$->u.literal = strdup($1);
|
||||
if ($$->u.literal == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
}
|
||||
| function_call
|
||||
{
|
||||
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->type = AM_SVAL_FUNCTION;
|
||||
$$->u.function = $1;
|
||||
}
|
||||
;
|
||||
|
||||
/* We can't just say
|
||||
* TOK_IDENTIFIER '(' function_arguments_or_empty ')'
|
||||
* because parsing function_arguments_or_empty will clobber
|
||||
* the underlying string that yylval.literalString points to.
|
||||
*/
|
||||
function_call :
|
||||
function_name '(' function_arguments_or_empty ')'
|
||||
{
|
||||
Function *fn = findFunction($1);
|
||||
if (fn == NULL) {
|
||||
fprintf(stderr, "Unknown function \"%s\"\n", $1);
|
||||
YYABORT;
|
||||
}
|
||||
$$ = (AmFunctionCall *)malloc(sizeof(AmFunctionCall));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->name = $1;
|
||||
if ($$->name == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->fn = fn;
|
||||
$$->args = $3;
|
||||
}
|
||||
;
|
||||
|
||||
function_name :
|
||||
TOK_IDENTIFIER
|
||||
{
|
||||
$$ = strdup($1);
|
||||
}
|
||||
;
|
||||
|
||||
function_arguments_or_empty :
|
||||
/* empty */
|
||||
{
|
||||
$$ = (AmFunctionArguments *)malloc(
|
||||
sizeof(AmFunctionArguments));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->argc = 0;
|
||||
$$->argv = NULL;
|
||||
}
|
||||
| function_arguments
|
||||
{
|
||||
AmFunctionArgumentBuilder *args = $1;
|
||||
assert(args != NULL);
|
||||
|
||||
/* Convert the builder list into an array.
|
||||
* Do it in reverse order; the args were pushed
|
||||
* onto the list in LIFO order.
|
||||
*/
|
||||
$$ = (AmFunctionArguments *)malloc(
|
||||
sizeof(AmFunctionArguments));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->argc = args->argCount;
|
||||
$$->argv = (AmStringValue *)malloc(
|
||||
$$->argc * sizeof(AmStringValue));
|
||||
if ($$->argv == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
int i;
|
||||
for (i = $$->argc; args != NULL && i > 0; --i) {
|
||||
AmFunctionArgumentBuilder *f = args;
|
||||
$$->argv[i-1] = *args->arg;
|
||||
args = args->next;
|
||||
free(f->arg);
|
||||
free(f);
|
||||
}
|
||||
assert(i == 0);
|
||||
assert(args == NULL);
|
||||
}
|
||||
;
|
||||
|
||||
function_arguments :
|
||||
string_value
|
||||
{
|
||||
$$ = (AmFunctionArgumentBuilder *)malloc(
|
||||
sizeof(AmFunctionArgumentBuilder));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->next = NULL;
|
||||
$$->argCount = 1;
|
||||
$$->arg = $1;
|
||||
}
|
||||
| function_arguments ',' string_value
|
||||
{
|
||||
$$ = (AmFunctionArgumentBuilder *)malloc(
|
||||
sizeof(AmFunctionArgumentBuilder));
|
||||
if ($$ == NULL) {
|
||||
YYABORT;
|
||||
}
|
||||
$$->next = $1;
|
||||
$$->argCount = $$->next->argCount + 1;
|
||||
$$->arg = $3;
|
||||
}
|
||||
;
|
||||
/* xxx this whole tool needs to be hardened */
|
@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "permissions.h"
|
||||
|
||||
int
|
||||
initPermissionRequestList(PermissionRequestList *list)
|
||||
{
|
||||
if (list != NULL) {
|
||||
list->requests = NULL;
|
||||
list->numRequests = 0;
|
||||
list->requestsAllocated = 0;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
addPermissionRequestToList(PermissionRequestList *list,
|
||||
const char *path, bool recursive, unsigned int permissions)
|
||||
{
|
||||
if (list == NULL || list->numRequests < 0 ||
|
||||
list->requestsAllocated < list->numRequests || path == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (list->numRequests == list->requestsAllocated) {
|
||||
int newSize;
|
||||
PermissionRequest *newRequests;
|
||||
|
||||
newSize = list->requestsAllocated * 2;
|
||||
if (newSize < 16) {
|
||||
newSize = 16;
|
||||
}
|
||||
newRequests = (PermissionRequest *)realloc(list->requests,
|
||||
newSize * sizeof(PermissionRequest));
|
||||
if (newRequests == NULL) {
|
||||
return -2;
|
||||
}
|
||||
list->requests = newRequests;
|
||||
list->requestsAllocated = newSize;
|
||||
}
|
||||
|
||||
PermissionRequest *req;
|
||||
req = &list->requests[list->numRequests++];
|
||||
req->path = strdup(path);
|
||||
if (req->path == NULL) {
|
||||
list->numRequests--;
|
||||
return -3;
|
||||
}
|
||||
req->recursive = recursive;
|
||||
req->requested = permissions;
|
||||
req->allowed = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
freePermissionRequestListElements(PermissionRequestList *list)
|
||||
{
|
||||
if (list != NULL && list->numRequests >= 0 &&
|
||||
list->requestsAllocated >= list->numRequests)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < list->numRequests; i++) {
|
||||
free((void *)list->requests[i].path);
|
||||
}
|
||||
free(list->requests);
|
||||
initPermissionRequestList(list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Global permission table
|
||||
*/
|
||||
|
||||
static struct {
|
||||
Permission *permissions;
|
||||
int numPermissionEntries;
|
||||
int allocatedPermissionEntries;
|
||||
bool permissionStateInitialized;
|
||||
} gPermissionState = {
|
||||
#if 1
|
||||
NULL, 0, 0, false
|
||||
#else
|
||||
.permissions = NULL,
|
||||
.numPermissionEntries = 0,
|
||||
.allocatedPermissionEntries = 0,
|
||||
.permissionStateInitialized = false
|
||||
#endif
|
||||
};
|
||||
|
||||
int
|
||||
permissionInit()
|
||||
{
|
||||
if (gPermissionState.permissionStateInitialized) {
|
||||
return -1;
|
||||
}
|
||||
gPermissionState.permissions = NULL;
|
||||
gPermissionState.numPermissionEntries = 0;
|
||||
gPermissionState.allocatedPermissionEntries = 0;
|
||||
gPermissionState.permissionStateInitialized = true;
|
||||
//xxx maybe add an "namespace root gets no permissions" fallback by default
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
permissionCleanup()
|
||||
{
|
||||
if (gPermissionState.permissionStateInitialized) {
|
||||
gPermissionState.permissionStateInitialized = false;
|
||||
if (gPermissionState.permissions != NULL) {
|
||||
int i;
|
||||
for (i = 0; i < gPermissionState.numPermissionEntries; i++) {
|
||||
free((void *)gPermissionState.permissions[i].path);
|
||||
}
|
||||
free(gPermissionState.permissions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
getPermissionCount()
|
||||
{
|
||||
if (gPermissionState.permissionStateInitialized) {
|
||||
return gPermissionState.numPermissionEntries;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const Permission *
|
||||
getPermissionAt(int index)
|
||||
{
|
||||
if (!gPermissionState.permissionStateInitialized) {
|
||||
return NULL;
|
||||
}
|
||||
if (index < 0 || index >= gPermissionState.numPermissionEntries) {
|
||||
return NULL;
|
||||
}
|
||||
return &gPermissionState.permissions[index];
|
||||
}
|
||||
|
||||
int
|
||||
getAllowedPermissions(const char *path, bool recursive,
|
||||
unsigned int *outAllowed)
|
||||
{
|
||||
if (!gPermissionState.permissionStateInitialized) {
|
||||
return -2;
|
||||
}
|
||||
if (outAllowed == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*outAllowed = 0;
|
||||
if (path == NULL) {
|
||||
return -1;
|
||||
}
|
||||
//TODO: implement this for real.
|
||||
recursive = false;
|
||||
*outAllowed = PERMSET_ALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed)
|
||||
{
|
||||
if (!gPermissionState.permissionStateInitialized) {
|
||||
return -2;
|
||||
}
|
||||
if (requests == NULL || requests->requests == NULL ||
|
||||
requests->numRequests < 0 ||
|
||||
requests->requestsAllocated < requests->numRequests)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int conflicts = 0;
|
||||
int i;
|
||||
for (i = 0; i < requests->numRequests; i++) {
|
||||
PermissionRequest *req;
|
||||
unsigned int allowed;
|
||||
int ret;
|
||||
|
||||
req = &requests->requests[i];
|
||||
ret = getAllowedPermissions(req->path, req->recursive, &allowed);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((req->requested & ~allowed) != 0) {
|
||||
conflicts++;
|
||||
}
|
||||
if (updateAllowed) {
|
||||
req->allowed = allowed;
|
||||
}
|
||||
}
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
int
|
||||
registerPermissionSet(int count, Permission *set)
|
||||
{
|
||||
if (!gPermissionState.permissionStateInitialized) {
|
||||
return -2;
|
||||
}
|
||||
if (count < 0 || (count > 0 && set == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (gPermissionState.numPermissionEntries + count >=
|
||||
gPermissionState.allocatedPermissionEntries)
|
||||
{
|
||||
Permission *newList;
|
||||
int newSize;
|
||||
|
||||
newSize = (gPermissionState.allocatedPermissionEntries + count) * 2;
|
||||
if (newSize < 16) {
|
||||
newSize = 16;
|
||||
}
|
||||
newList = (Permission *)realloc(gPermissionState.permissions,
|
||||
newSize * sizeof(Permission));
|
||||
if (newList == NULL) {
|
||||
return -3;
|
||||
}
|
||||
gPermissionState.permissions = newList;
|
||||
gPermissionState.allocatedPermissionEntries = newSize;
|
||||
}
|
||||
|
||||
Permission *p = &gPermissionState.permissions[
|
||||
gPermissionState.numPermissionEntries];
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
*p = set[i];
|
||||
//TODO: cache the strlen of the path
|
||||
//TODO: normalize; strip off trailing /
|
||||
p->path = strdup(p->path);
|
||||
if (p->path == NULL) {
|
||||
/* If we can't add all of the entries, we don't
|
||||
* add any of them.
|
||||
*/
|
||||
Permission *pp = &gPermissionState.permissions[
|
||||
gPermissionState.numPermissionEntries];
|
||||
while (pp != p) {
|
||||
free((void *)pp->path);
|
||||
pp++;
|
||||
}
|
||||
return -4;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
gPermissionState.numPermissionEntries += count;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_PERMISSIONS_H_
|
||||
#define AMEND_PERMISSIONS_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define PERM_NONE (0)
|
||||
#define PERM_STAT (1<<0)
|
||||
#define PERM_READ (1<<1)
|
||||
#define PERM_WRITE (1<<2) // including create, delete, mkdir, rmdir
|
||||
#define PERM_CHMOD (1<<3)
|
||||
#define PERM_CHOWN (1<<4)
|
||||
#define PERM_CHGRP (1<<5)
|
||||
#define PERM_SETUID (1<<6)
|
||||
#define PERM_SETGID (1<<7)
|
||||
|
||||
#define PERMSET_READ (PERM_STAT | PERM_READ)
|
||||
#define PERMSET_WRITE (PERMSET_READ | PERM_WRITE)
|
||||
|
||||
#define PERMSET_ALL \
|
||||
(PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \
|
||||
PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID)
|
||||
|
||||
typedef struct {
|
||||
unsigned int requested;
|
||||
unsigned int allowed;
|
||||
const char *path;
|
||||
bool recursive;
|
||||
} PermissionRequest;
|
||||
|
||||
typedef struct {
|
||||
PermissionRequest *requests;
|
||||
int numRequests;
|
||||
int requestsAllocated;
|
||||
} PermissionRequestList;
|
||||
|
||||
/* Properly clear out a PermissionRequestList.
|
||||
*
|
||||
* @return 0 if list is non-NULL, negative otherwise.
|
||||
*/
|
||||
int initPermissionRequestList(PermissionRequestList *list);
|
||||
|
||||
/* Add a permission request to the list, allocating more space
|
||||
* if necessary.
|
||||
*
|
||||
* @return 0 on success or a negative value on failure.
|
||||
*/
|
||||
int addPermissionRequestToList(PermissionRequestList *list,
|
||||
const char *path, bool recursive, unsigned int permissions);
|
||||
|
||||
/* Free anything allocated by addPermissionRequestToList(). The caller
|
||||
* is responsible for freeing the actual PermissionRequestList.
|
||||
*/
|
||||
void freePermissionRequestListElements(PermissionRequestList *list);
|
||||
|
||||
|
||||
/*
|
||||
* Global permission table
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
const char *path;
|
||||
unsigned int allowed;
|
||||
} Permission;
|
||||
|
||||
int permissionInit(void);
|
||||
void permissionCleanup(void);
|
||||
|
||||
/* Returns the allowed permissions for the path in "outAllowed".
|
||||
* Returns 0 if successful, negative if a parameter or global state
|
||||
* is bad.
|
||||
*/
|
||||
int getAllowedPermissions(const char *path, bool recursive,
|
||||
unsigned int *outAllowed);
|
||||
|
||||
/* More-recently-registered permissions override older permissions.
|
||||
*/
|
||||
int registerPermissionSet(int count, Permission *set);
|
||||
|
||||
/* Check to make sure that each request is allowed.
|
||||
*
|
||||
* @param requests The list of permission requests
|
||||
* @param updateAllowed If true, update the "allowed" field in each
|
||||
* element of the list
|
||||
* @return the number of requests that were denied, or negative if
|
||||
* an error occurred.
|
||||
*/
|
||||
int countPermissionConflicts(PermissionRequestList *requests,
|
||||
bool updateAllowed);
|
||||
|
||||
/* Inspection/testing/debugging functions
|
||||
*/
|
||||
int getPermissionCount(void);
|
||||
const Permission *getPermissionAt(int index);
|
||||
|
||||
#endif // AMEND_PERMISSIONS_H_
|
410
amend/register.c
410
amend/register.c
@ -1,410 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include "commands.h"
|
||||
|
||||
#include "register.h"
|
||||
|
||||
#define UNUSED(p) ((void)(p))
|
||||
|
||||
#define CHECK_BOOL() \
|
||||
do { \
|
||||
assert(argv == NULL); \
|
||||
if (argv != NULL) return -1; \
|
||||
assert(argc == true || argc == false); \
|
||||
if (argc != true && argc != false) return -1; \
|
||||
} while (false)
|
||||
|
||||
#define CHECK_WORDS() \
|
||||
do { \
|
||||
assert(argc >= 0); \
|
||||
if (argc < 0) return -1; \
|
||||
assert(argc == 0 || argv != NULL); \
|
||||
if (argc != 0 && argv == NULL) return -1; \
|
||||
if (permissions != NULL) { \
|
||||
int CW_I_; \
|
||||
for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
|
||||
assert(argv[CW_I_] != NULL); \
|
||||
if (argv[CW_I_] == NULL) return -1; \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define CHECK_FN() \
|
||||
do { \
|
||||
CHECK_WORDS(); \
|
||||
if (permissions != NULL) { \
|
||||
assert(result == NULL); \
|
||||
if (result != NULL) return -1; \
|
||||
} else { \
|
||||
assert(result != NULL); \
|
||||
if (result == NULL) return -1; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define NO_PERMS(perms) \
|
||||
do { \
|
||||
PermissionRequestList *NP_PRL_ = (perms); \
|
||||
if (NP_PRL_ != NULL) { \
|
||||
int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
|
||||
"", false, PERM_NONE); \
|
||||
if (NP_RET_ < 0) { \
|
||||
/* Returns from the calling function. \
|
||||
*/ \
|
||||
return NP_RET_; \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
/*
|
||||
* Command definitions
|
||||
*/
|
||||
|
||||
/* assert <boolexpr>
|
||||
*/
|
||||
static int
|
||||
cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_BOOL();
|
||||
NO_PERMS(permissions);
|
||||
|
||||
/* If our argument is false, return non-zero (failure)
|
||||
* If our argument is true, return zero (success)
|
||||
*/
|
||||
if (argc) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* format <root>
|
||||
*/
|
||||
static int
|
||||
cmd_format(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
//xxx
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy_dir <srcdir> <dstdir>
|
||||
*/
|
||||
static int
|
||||
cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
//xxx
|
||||
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
|
||||
cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
//xxx when marking, save the top-level hash at the mark point
|
||||
// so we can retry on failure. Otherwise the hashes won't match,
|
||||
// or someone could intentionally dirty the FS to force a downgrade
|
||||
//xxx
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* done
|
||||
*/
|
||||
static int
|
||||
cmd_done(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_WORDS();
|
||||
//xxx
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
registerUpdateCommands()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
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;
|
||||
|
||||
ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
//xxx some way to fix permissions
|
||||
//xxx could have "installperms" commands that build the fs_config list
|
||||
//xxx along with a "commitperms", and any copy_dir etc. needs to see
|
||||
// a commitperms before it will work
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/* update_forced()
|
||||
*
|
||||
* Returns "true" if some system setting has determined that
|
||||
* the update should happen no matter what.
|
||||
*/
|
||||
static int
|
||||
fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_FN();
|
||||
NO_PERMS(permissions);
|
||||
|
||||
if (argc != 0) {
|
||||
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
|
||||
name, argc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//xxx check some global or property
|
||||
bool force = true;
|
||||
if (force) {
|
||||
*result = strdup("true");
|
||||
} else {
|
||||
*result = strdup("");
|
||||
}
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = strlen(*result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get_mark(<resource>)
|
||||
*
|
||||
* Returns the current mark associated with the provided resource.
|
||||
*/
|
||||
static int
|
||||
fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_FN();
|
||||
NO_PERMS(permissions);
|
||||
|
||||
if (argc != 1) {
|
||||
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
|
||||
name, argc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//xxx look up the value
|
||||
*result = strdup("");
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = strlen(*result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hash_dir(<path-to-directory>)
|
||||
*/
|
||||
static int
|
||||
fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_FN();
|
||||
|
||||
const char *dir;
|
||||
if (argc != 1) {
|
||||
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
|
||||
name, argc);
|
||||
return 1;
|
||||
} else {
|
||||
dir = argv[0];
|
||||
}
|
||||
|
||||
if (permissions != NULL) {
|
||||
if (dir == NULL) {
|
||||
/* The argument is the result of another function.
|
||||
* Assume the worst case, where the function returns
|
||||
* the root.
|
||||
*/
|
||||
dir = "/";
|
||||
}
|
||||
ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
|
||||
} else {
|
||||
//xxx build and return the string
|
||||
*result = strdup("hashvalue");
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = strlen(*result);
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* matches(<str>, <str1> [, <strN>...])
|
||||
* If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
|
||||
* otherwise returns "".
|
||||
*
|
||||
* E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
|
||||
*/
|
||||
static int
|
||||
fn_matches(const char *name, void *cookie, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_FN();
|
||||
NO_PERMS(permissions);
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
|
||||
name, argc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[0], argv[i]) == 0) {
|
||||
*result = strdup(argv[0]);
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = strlen(*result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
*result = strdup("");
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* concat(<str>, <str1> [, <strN>...])
|
||||
* Returns the concatenation of all strings.
|
||||
*/
|
||||
static int
|
||||
fn_concat(const char *name, void *cookie, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen,
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
UNUSED(name);
|
||||
UNUSED(cookie);
|
||||
CHECK_FN();
|
||||
NO_PERMS(permissions);
|
||||
|
||||
size_t totalLen = 0;
|
||||
int i;
|
||||
for (i = 0; i < argc; i++) {
|
||||
totalLen += strlen(argv[i]);
|
||||
}
|
||||
|
||||
char *s = (char *)malloc(totalLen + 1);
|
||||
if (s == NULL) {
|
||||
return -1;
|
||||
}
|
||||
s[totalLen] = '\0';
|
||||
for (i = 0; i < argc; i++) {
|
||||
//TODO: keep track of the end to avoid walking the string each time
|
||||
strcat(s, argv[i]);
|
||||
}
|
||||
*result = s;
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = strlen(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
registerUpdateFunctions()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = registerFunction("update_forced", fn_update_forced, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerFunction("get_mark", fn_get_mark, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerFunction("hash_dir", fn_hash_dir, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerFunction("matches", fn_matches, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
ret = registerFunction("concat", fn_concat, NULL);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_REGISTER_H_
|
||||
#define AMEND_REGISTER_H_
|
||||
|
||||
int registerUpdateCommands(void);
|
||||
int registerUpdateFunctions(void);
|
||||
|
||||
#endif // AMEND_REGISTER_H_
|
132
amend/symtab.c
132
amend/symtab.c
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "symtab.h"
|
||||
|
||||
#define DEFAULT_TABLE_SIZE 16
|
||||
|
||||
typedef struct {
|
||||
char *symbol;
|
||||
const void *cookie;
|
||||
unsigned int flags;
|
||||
} SymbolTableEntry;
|
||||
|
||||
struct SymbolTable {
|
||||
SymbolTableEntry *table;
|
||||
int numEntries;
|
||||
int maxSize;
|
||||
};
|
||||
|
||||
SymbolTable *
|
||||
createSymbolTable()
|
||||
{
|
||||
SymbolTable *tab;
|
||||
|
||||
tab = (SymbolTable *)malloc(sizeof(SymbolTable));
|
||||
if (tab != NULL) {
|
||||
tab->numEntries = 0;
|
||||
tab->maxSize = DEFAULT_TABLE_SIZE;
|
||||
tab->table = (SymbolTableEntry *)malloc(
|
||||
tab->maxSize * sizeof(SymbolTableEntry));
|
||||
if (tab->table == NULL) {
|
||||
free(tab);
|
||||
tab = NULL;
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
void
|
||||
deleteSymbolTable(SymbolTable *tab)
|
||||
{
|
||||
if (tab != NULL) {
|
||||
while (tab->numEntries > 0) {
|
||||
free(tab->table[--tab->numEntries].symbol);
|
||||
}
|
||||
free(tab->table);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
findInSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (tab == NULL || symbol == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO: Sort the table and binary search
|
||||
for (i = 0; i < tab->numEntries; i++) {
|
||||
if (strcmp(tab->table[i].symbol, symbol) == 0 &&
|
||||
tab->table[i].flags == flags)
|
||||
{
|
||||
return (void *)tab->table[i].cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
|
||||
const void *cookie)
|
||||
{
|
||||
if (tab == NULL || symbol == NULL || cookie == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make sure that this symbol isn't already in the table.
|
||||
*/
|
||||
if (findInSymbolTable(tab, symbol, flags) != NULL) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Make sure there's enough space for the new entry.
|
||||
*/
|
||||
if (tab->numEntries == tab->maxSize) {
|
||||
SymbolTableEntry *newTable;
|
||||
int newSize;
|
||||
|
||||
newSize = tab->numEntries * 2;
|
||||
if (newSize < DEFAULT_TABLE_SIZE) {
|
||||
newSize = DEFAULT_TABLE_SIZE;
|
||||
}
|
||||
newTable = (SymbolTableEntry *)realloc(tab->table,
|
||||
newSize * sizeof(SymbolTableEntry));
|
||||
if (newTable == NULL) {
|
||||
return -1;
|
||||
}
|
||||
tab->maxSize = newSize;
|
||||
tab->table = newTable;
|
||||
}
|
||||
|
||||
/* Insert the new entry.
|
||||
*/
|
||||
symbol = strdup(symbol);
|
||||
if (symbol == NULL) {
|
||||
return -1;
|
||||
}
|
||||
// TODO: Sort the table
|
||||
tab->table[tab->numEntries].symbol = (char *)symbol;
|
||||
tab->table[tab->numEntries].cookie = cookie;
|
||||
tab->table[tab->numEntries].flags = flags;
|
||||
tab->numEntries++;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_SYMTAB_H_
|
||||
#define AMEND_SYMTAB_H_
|
||||
|
||||
typedef struct SymbolTable SymbolTable;
|
||||
|
||||
SymbolTable *createSymbolTable(void);
|
||||
|
||||
void deleteSymbolTable(SymbolTable *tab);
|
||||
|
||||
/* symbol and cookie must be non-NULL.
|
||||
*/
|
||||
int addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
|
||||
const void *cookie);
|
||||
|
||||
void *findInSymbolTable(SymbolTable *tab, const char *symbol,
|
||||
unsigned int flags);
|
||||
|
||||
#endif // AMEND_SYMTAB_H_
|
@ -1,538 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include "commands.h"
|
||||
|
||||
static struct {
|
||||
bool called;
|
||||
const char *name;
|
||||
void *cookie;
|
||||
int argc;
|
||||
const char **argv;
|
||||
PermissionRequestList *permissions;
|
||||
int returnValue;
|
||||
char *functionResult;
|
||||
} gTestCommandState;
|
||||
|
||||
static int
|
||||
testCommand(const char *name, void *cookie, int argc, const char *argv[],
|
||||
PermissionRequestList *permissions)
|
||||
{
|
||||
gTestCommandState.called = true;
|
||||
gTestCommandState.name = name;
|
||||
gTestCommandState.cookie = cookie;
|
||||
gTestCommandState.argc = argc;
|
||||
gTestCommandState.argv = argv;
|
||||
gTestCommandState.permissions = permissions;
|
||||
return gTestCommandState.returnValue;
|
||||
}
|
||||
|
||||
static int
|
||||
testFunction(const char *name, void *cookie, int argc, const char *argv[],
|
||||
char **result, size_t *resultLen, PermissionRequestList *permissions)
|
||||
{
|
||||
gTestCommandState.called = true;
|
||||
gTestCommandState.name = name;
|
||||
gTestCommandState.cookie = cookie;
|
||||
gTestCommandState.argc = argc;
|
||||
gTestCommandState.argv = argv;
|
||||
gTestCommandState.permissions = permissions;
|
||||
if (result != NULL) {
|
||||
*result = gTestCommandState.functionResult;
|
||||
if (resultLen != NULL) {
|
||||
*resultLen = strlen(*result);
|
||||
}
|
||||
}
|
||||
return gTestCommandState.returnValue;
|
||||
}
|
||||
|
||||
static int
|
||||
test_commands()
|
||||
{
|
||||
Command *cmd;
|
||||
int ret;
|
||||
CommandArgumentType argType;
|
||||
|
||||
ret = commandInit();
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure we can't initialize twice.
|
||||
*/
|
||||
ret = commandInit();
|
||||
assert(ret < 0);
|
||||
|
||||
/* Try calling with some bad values.
|
||||
*/
|
||||
ret = registerCommand(NULL, CMD_ARGS_UNKNOWN, NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = registerCommand("hello", CMD_ARGS_UNKNOWN, NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = registerCommand("hello", CMD_ARGS_WORDS, NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
cmd = findCommand(NULL);
|
||||
assert(cmd == NULL);
|
||||
|
||||
argType = getCommandArgumentType(NULL);
|
||||
assert((int)argType < 0);
|
||||
|
||||
ret = callCommand(NULL, -1, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = callBooleanCommand(NULL, false);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Register some commands.
|
||||
*/
|
||||
ret = registerCommand("one", CMD_ARGS_WORDS, testCommand,
|
||||
&gTestCommandState);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = registerCommand("two", CMD_ARGS_WORDS, testCommand,
|
||||
&gTestCommandState);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = registerCommand("bool", CMD_ARGS_BOOLEAN, testCommand,
|
||||
&gTestCommandState);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure that all of those commands exist and that their
|
||||
* argument types are correct.
|
||||
*/
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
argType = getCommandArgumentType(cmd);
|
||||
assert(argType == CMD_ARGS_WORDS);
|
||||
|
||||
cmd = findCommand("two");
|
||||
assert(cmd != NULL);
|
||||
argType = getCommandArgumentType(cmd);
|
||||
assert(argType == CMD_ARGS_WORDS);
|
||||
|
||||
cmd = findCommand("bool");
|
||||
assert(cmd != NULL);
|
||||
argType = getCommandArgumentType(cmd);
|
||||
assert(argType == CMD_ARGS_BOOLEAN);
|
||||
|
||||
/* Make sure that no similar commands exist.
|
||||
*/
|
||||
cmd = findCommand("on");
|
||||
assert(cmd == NULL);
|
||||
|
||||
cmd = findCommand("onee");
|
||||
assert(cmd == NULL);
|
||||
|
||||
/* Make sure that a double insertion fails.
|
||||
*/
|
||||
ret = registerCommand("one", CMD_ARGS_WORDS, testCommand,
|
||||
&gTestCommandState);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Make sure that bad args fail.
|
||||
*/
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
|
||||
ret = callCommand(cmd, -1, NULL); // argc must be non-negative
|
||||
assert(ret < 0);
|
||||
|
||||
ret = callCommand(cmd, 1, NULL); // argv can't be NULL if argc > 0
|
||||
assert(ret < 0);
|
||||
|
||||
/* Make sure that you can't make a boolean call on a regular command.
|
||||
*/
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
|
||||
ret = callBooleanCommand(cmd, false);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Make sure that you can't make a regular call on a boolean command.
|
||||
*/
|
||||
cmd = findCommand("bool");
|
||||
assert(cmd != NULL);
|
||||
|
||||
ret = callCommand(cmd, 0, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Set up some arguments.
|
||||
*/
|
||||
int argc = 4;
|
||||
const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
|
||||
|
||||
/* Make a call and make sure that it occurred.
|
||||
*/
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 25;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
ret = callCommand(cmd, argc, argv);
|
||||
//xxx also try calling with a null argv element (should fail)
|
||||
assert(ret == 25);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "one") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == argc);
|
||||
assert(gTestCommandState.argv == argv);
|
||||
assert(gTestCommandState.permissions == NULL);
|
||||
|
||||
/* Make a boolean call and make sure that it occurred.
|
||||
*/
|
||||
cmd = findCommand("bool");
|
||||
assert(cmd != NULL);
|
||||
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 12;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
ret = callBooleanCommand(cmd, false);
|
||||
assert(ret == 12);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "bool") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == 0);
|
||||
assert(gTestCommandState.argv == NULL);
|
||||
assert(gTestCommandState.permissions == NULL);
|
||||
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 13;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
ret = callBooleanCommand(cmd, true);
|
||||
assert(ret == 13);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "bool") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == 1);
|
||||
assert(gTestCommandState.argv == NULL);
|
||||
assert(gTestCommandState.permissions == NULL);
|
||||
|
||||
/* Try looking up permissions.
|
||||
*/
|
||||
PermissionRequestList permissions;
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 27;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
argv[1] = NULL; // null out an arg, which should be ok
|
||||
ret = getCommandPermissions(cmd, argc, argv, &permissions);
|
||||
assert(ret == 27);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "one") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == argc);
|
||||
assert(gTestCommandState.argv == argv);
|
||||
assert(gTestCommandState.permissions == &permissions);
|
||||
|
||||
/* Boolean command permissions
|
||||
*/
|
||||
cmd = findCommand("bool");
|
||||
assert(cmd != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 55;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
// argv[1] is still NULL
|
||||
ret = getBooleanCommandPermissions(cmd, true, &permissions);
|
||||
assert(ret == 55);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "bool") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == 1);
|
||||
assert(gTestCommandState.argv == NULL);
|
||||
assert(gTestCommandState.permissions == &permissions);
|
||||
|
||||
|
||||
/* Smoke test commandCleanup().
|
||||
*/
|
||||
commandCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_functions()
|
||||
{
|
||||
Function *fn;
|
||||
int ret;
|
||||
|
||||
ret = commandInit();
|
||||
assert(ret == 0);
|
||||
|
||||
/* Try calling with some bad values.
|
||||
*/
|
||||
ret = registerFunction(NULL, NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = registerFunction("hello", NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
fn = findFunction(NULL);
|
||||
assert(fn == NULL);
|
||||
|
||||
ret = callFunction(NULL, -1, NULL, NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Register some functions.
|
||||
*/
|
||||
ret = registerFunction("one", testFunction, &gTestCommandState);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = registerFunction("two", testFunction, &gTestCommandState);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = registerFunction("three", testFunction, &gTestCommandState);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure that all of those functions exist.
|
||||
* argument types are correct.
|
||||
*/
|
||||
fn = findFunction("one");
|
||||
assert(fn != NULL);
|
||||
|
||||
fn = findFunction("two");
|
||||
assert(fn != NULL);
|
||||
|
||||
fn = findFunction("three");
|
||||
assert(fn != NULL);
|
||||
|
||||
/* Make sure that no similar functions exist.
|
||||
*/
|
||||
fn = findFunction("on");
|
||||
assert(fn == NULL);
|
||||
|
||||
fn = findFunction("onee");
|
||||
assert(fn == NULL);
|
||||
|
||||
/* Make sure that a double insertion fails.
|
||||
*/
|
||||
ret = registerFunction("one", testFunction, &gTestCommandState);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Make sure that bad args fail.
|
||||
*/
|
||||
fn = findFunction("one");
|
||||
assert(fn != NULL);
|
||||
|
||||
// argc must be non-negative
|
||||
ret = callFunction(fn, -1, NULL, (char **)1, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
// argv can't be NULL if argc > 0
|
||||
ret = callFunction(fn, 1, NULL, (char **)1, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
// result can't be NULL
|
||||
ret = callFunction(fn, 0, NULL, NULL, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Set up some arguments.
|
||||
*/
|
||||
int argc = 4;
|
||||
const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
|
||||
|
||||
/* Make a call and make sure that it occurred.
|
||||
*/
|
||||
char *functionResult;
|
||||
size_t functionResultLen;
|
||||
fn = findFunction("one");
|
||||
assert(fn != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 25;
|
||||
gTestCommandState.functionResult = "1234";
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
functionResult = NULL;
|
||||
functionResultLen = 55;
|
||||
ret = callFunction(fn, argc, argv,
|
||||
&functionResult, &functionResultLen);
|
||||
//xxx also try calling with a null resultLen arg (should succeed)
|
||||
//xxx also try calling with a null argv element (should fail)
|
||||
assert(ret == 25);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "one") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == argc);
|
||||
assert(gTestCommandState.argv == argv);
|
||||
assert(gTestCommandState.permissions == NULL);
|
||||
assert(strcmp(functionResult, "1234") == 0);
|
||||
assert(functionResultLen == strlen(functionResult));
|
||||
|
||||
/* Try looking up permissions.
|
||||
*/
|
||||
PermissionRequestList permissions;
|
||||
fn = findFunction("one");
|
||||
assert(fn != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 27;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
argv[1] = NULL; // null out an arg, which should be ok
|
||||
ret = getFunctionPermissions(fn, argc, argv, &permissions);
|
||||
assert(ret == 27);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "one") == 0);
|
||||
assert(gTestCommandState.cookie == &gTestCommandState);
|
||||
assert(gTestCommandState.argc == argc);
|
||||
assert(gTestCommandState.argv == argv);
|
||||
assert(gTestCommandState.permissions == &permissions);
|
||||
|
||||
/* Smoke test commandCleanup().
|
||||
*/
|
||||
commandCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_interaction()
|
||||
{
|
||||
Command *cmd;
|
||||
Function *fn;
|
||||
int ret;
|
||||
|
||||
ret = commandInit();
|
||||
assert(ret == 0);
|
||||
|
||||
/* Register some commands.
|
||||
*/
|
||||
ret = registerCommand("one", CMD_ARGS_WORDS, testCommand, (void *)0xc1);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = registerCommand("two", CMD_ARGS_WORDS, testCommand, (void *)0xc2);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Register some functions, one of which shares a name with a command.
|
||||
*/
|
||||
ret = registerFunction("one", testFunction, (void *)0xf1);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = registerFunction("three", testFunction, (void *)0xf3);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Look up each of the commands, and make sure no command exists
|
||||
* with the name used only by our function.
|
||||
*/
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
|
||||
cmd = findCommand("two");
|
||||
assert(cmd != NULL);
|
||||
|
||||
cmd = findCommand("three");
|
||||
assert(cmd == NULL);
|
||||
|
||||
/* Look up each of the functions, and make sure no function exists
|
||||
* with the name used only by our command.
|
||||
*/
|
||||
fn = findFunction("one");
|
||||
assert(fn != NULL);
|
||||
|
||||
fn = findFunction("two");
|
||||
assert(fn == NULL);
|
||||
|
||||
fn = findFunction("three");
|
||||
assert(fn != NULL);
|
||||
|
||||
/* Set up some arguments.
|
||||
*/
|
||||
int argc = 4;
|
||||
const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
|
||||
|
||||
/* Call the overlapping command and make sure that the cookie is correct.
|
||||
*/
|
||||
cmd = findCommand("one");
|
||||
assert(cmd != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 123;
|
||||
gTestCommandState.permissions = (PermissionRequestList *)1;
|
||||
ret = callCommand(cmd, argc, argv);
|
||||
assert(ret == 123);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "one") == 0);
|
||||
assert((int)gTestCommandState.cookie == 0xc1);
|
||||
assert(gTestCommandState.argc == argc);
|
||||
assert(gTestCommandState.argv == argv);
|
||||
assert(gTestCommandState.permissions == NULL);
|
||||
|
||||
/* Call the overlapping function and make sure that the cookie is correct.
|
||||
*/
|
||||
char *functionResult;
|
||||
size_t functionResultLen;
|
||||
fn = findFunction("one");
|
||||
assert(fn != NULL);
|
||||
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
|
||||
gTestCommandState.called = false;
|
||||
gTestCommandState.returnValue = 125;
|
||||
gTestCommandState.functionResult = "5678";
|
||||
gTestCommandState.permissions = (PermissionRequestList *)2;
|
||||
functionResult = NULL;
|
||||
functionResultLen = 66;
|
||||
ret = callFunction(fn, argc, argv, &functionResult, &functionResultLen);
|
||||
assert(ret == 125);
|
||||
assert(gTestCommandState.called);
|
||||
assert(strcmp(gTestCommandState.name, "one") == 0);
|
||||
assert((int)gTestCommandState.cookie == 0xf1);
|
||||
assert(gTestCommandState.argc == argc);
|
||||
assert(gTestCommandState.argv == argv);
|
||||
assert(gTestCommandState.permissions == NULL);
|
||||
assert(strcmp(functionResult, "5678") == 0);
|
||||
assert(functionResultLen == strlen(functionResult));
|
||||
|
||||
/* Clean up.
|
||||
*/
|
||||
commandCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
test_cmd_fn()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = test_commands();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_commands() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = test_functions();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_functions() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = test_interaction();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_interaction() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include "permissions.h"
|
||||
|
||||
static int
|
||||
test_permission_list()
|
||||
{
|
||||
PermissionRequestList list;
|
||||
int ret;
|
||||
int numRequests;
|
||||
|
||||
/* Bad parameter
|
||||
*/
|
||||
ret = initPermissionRequestList(NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Good parameter
|
||||
*/
|
||||
ret = initPermissionRequestList(&list);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Bad parameters
|
||||
*/
|
||||
ret = addPermissionRequestToList(NULL, NULL, false, 0);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = addPermissionRequestToList(&list, NULL, false, 0);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Good parameters
|
||||
*/
|
||||
numRequests = 0;
|
||||
|
||||
ret = addPermissionRequestToList(&list, "one", false, 1);
|
||||
assert(ret == 0);
|
||||
numRequests++;
|
||||
|
||||
ret = addPermissionRequestToList(&list, "two", false, 2);
|
||||
assert(ret == 0);
|
||||
numRequests++;
|
||||
|
||||
ret = addPermissionRequestToList(&list, "three", false, 3);
|
||||
assert(ret == 0);
|
||||
numRequests++;
|
||||
|
||||
ret = addPermissionRequestToList(&list, "recursive", true, 55);
|
||||
assert(ret == 0);
|
||||
numRequests++;
|
||||
|
||||
/* Validate the list
|
||||
*/
|
||||
assert(list.requests != NULL);
|
||||
assert(list.numRequests == numRequests);
|
||||
assert(list.numRequests <= list.requestsAllocated);
|
||||
bool sawOne = false;
|
||||
bool sawTwo = false;
|
||||
bool sawThree = false;
|
||||
bool sawRecursive = false;
|
||||
int i;
|
||||
for (i = 0; i < list.numRequests; i++) {
|
||||
PermissionRequest *req = &list.requests[i];
|
||||
assert(req->allowed == 0);
|
||||
|
||||
/* Order isn't guaranteed, so we have to switch every time.
|
||||
*/
|
||||
if (strcmp(req->path, "one") == 0) {
|
||||
assert(!sawOne);
|
||||
assert(req->requested == 1);
|
||||
assert(!req->recursive);
|
||||
sawOne = true;
|
||||
} else if (strcmp(req->path, "two") == 0) {
|
||||
assert(!sawTwo);
|
||||
assert(req->requested == 2);
|
||||
assert(!req->recursive);
|
||||
sawTwo = true;
|
||||
} else if (strcmp(req->path, "three") == 0) {
|
||||
assert(!sawThree);
|
||||
assert(req->requested == 3);
|
||||
assert(!req->recursive);
|
||||
sawThree = true;
|
||||
} else if (strcmp(req->path, "recursive") == 0) {
|
||||
assert(!sawRecursive);
|
||||
assert(req->requested == 55);
|
||||
assert(req->recursive);
|
||||
sawRecursive = true;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
assert(sawOne);
|
||||
assert(sawTwo);
|
||||
assert(sawThree);
|
||||
assert(sawRecursive);
|
||||
|
||||
/* Smoke test the teardown
|
||||
*/
|
||||
freePermissionRequestListElements(&list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_permission_table()
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Test the global permissions table.
|
||||
* Try calling functions without initializing first.
|
||||
*/
|
||||
ret = registerPermissionSet(0, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = countPermissionConflicts((PermissionRequestList *)16, false);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = getPermissionCount();
|
||||
assert(ret < 0);
|
||||
|
||||
const Permission *p;
|
||||
p = getPermissionAt(0);
|
||||
assert(p == NULL);
|
||||
|
||||
/* Initialize.
|
||||
*/
|
||||
ret = permissionInit();
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure we can't initialize twice.
|
||||
*/
|
||||
ret = permissionInit();
|
||||
assert(ret < 0);
|
||||
|
||||
/* Test the inspection functions.
|
||||
*/
|
||||
ret = getPermissionCount();
|
||||
assert(ret == 0);
|
||||
|
||||
p = getPermissionAt(-1);
|
||||
assert(p == NULL);
|
||||
|
||||
p = getPermissionAt(0);
|
||||
assert(p == NULL);
|
||||
|
||||
p = getPermissionAt(1);
|
||||
assert(p == NULL);
|
||||
|
||||
/* Test registerPermissionSet().
|
||||
* Try some bad parameter values.
|
||||
*/
|
||||
ret = registerPermissionSet(-1, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = registerPermissionSet(1, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Register some permissions.
|
||||
*/
|
||||
Permission p1;
|
||||
p1.path = "one";
|
||||
p1.allowed = 1;
|
||||
ret = registerPermissionSet(1, &p1);
|
||||
assert(ret == 0);
|
||||
ret = getPermissionCount();
|
||||
assert(ret == 1);
|
||||
|
||||
Permission p2[2];
|
||||
p2[0].path = "two";
|
||||
p2[0].allowed = 2;
|
||||
p2[1].path = "three";
|
||||
p2[1].allowed = 3;
|
||||
ret = registerPermissionSet(2, p2);
|
||||
assert(ret == 0);
|
||||
ret = getPermissionCount();
|
||||
assert(ret == 3);
|
||||
|
||||
ret = registerPermissionSet(0, NULL);
|
||||
assert(ret == 0);
|
||||
ret = getPermissionCount();
|
||||
assert(ret == 3);
|
||||
|
||||
p1.path = "four";
|
||||
p1.allowed = 4;
|
||||
ret = registerPermissionSet(1, &p1);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure the table looks correct.
|
||||
* Order is important; more-recent additions
|
||||
* should appear at higher indices.
|
||||
*/
|
||||
ret = getPermissionCount();
|
||||
assert(ret == 4);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ret; i++) {
|
||||
const Permission *p;
|
||||
p = getPermissionAt(i);
|
||||
assert(p != NULL);
|
||||
assert(p->allowed == (unsigned int)(i + 1));
|
||||
switch (i) {
|
||||
case 0:
|
||||
assert(strcmp(p->path, "one") == 0);
|
||||
break;
|
||||
case 1:
|
||||
assert(strcmp(p->path, "two") == 0);
|
||||
break;
|
||||
case 2:
|
||||
assert(strcmp(p->path, "three") == 0);
|
||||
break;
|
||||
case 3:
|
||||
assert(strcmp(p->path, "four") == 0);
|
||||
break;
|
||||
default:
|
||||
assert(!"internal error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
p = getPermissionAt(ret);
|
||||
assert(p == NULL);
|
||||
|
||||
/* Smoke test the teardown
|
||||
*/
|
||||
permissionCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_allowed_permissions()
|
||||
{
|
||||
int ret;
|
||||
int numPerms;
|
||||
|
||||
/* Make sure these fail before initialization.
|
||||
*/
|
||||
ret = countPermissionConflicts((PermissionRequestList *)1, false);
|
||||
assert(ret < 0);
|
||||
|
||||
ret = getAllowedPermissions((const char *)1, false, (unsigned int *)1);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Initialize.
|
||||
*/
|
||||
ret = permissionInit();
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure countPermissionConflicts() fails with bad parameters.
|
||||
*/
|
||||
ret = countPermissionConflicts(NULL, false);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Register a set of permissions.
|
||||
*/
|
||||
Permission perms[] = {
|
||||
{ "/", PERM_NONE },
|
||||
{ "/stat", PERM_STAT },
|
||||
{ "/read", PERMSET_READ },
|
||||
{ "/write", PERMSET_WRITE },
|
||||
{ "/.stat", PERM_STAT },
|
||||
{ "/.stat/.read", PERMSET_READ },
|
||||
{ "/.stat/.read/.write", PERMSET_WRITE },
|
||||
{ "/.stat/.write", PERMSET_WRITE },
|
||||
};
|
||||
numPerms = sizeof(perms) / sizeof(perms[0]);
|
||||
ret = registerPermissionSet(numPerms, perms);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Build a permission request list.
|
||||
*/
|
||||
PermissionRequestList list;
|
||||
ret = initPermissionRequestList(&list);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = addPermissionRequestToList(&list, "/stat", false, PERM_STAT);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = addPermissionRequestToList(&list, "/read", false, PERM_READ);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = addPermissionRequestToList(&list, "/write", false, PERM_WRITE);
|
||||
assert(ret == 0);
|
||||
|
||||
//TODO: cover more cases once the permission stuff has been implemented
|
||||
|
||||
/* All of the requests in the list should be allowed.
|
||||
*/
|
||||
ret = countPermissionConflicts(&list, false);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Add a request that will be denied.
|
||||
*/
|
||||
ret = addPermissionRequestToList(&list, "/stat", false, 1<<31 | PERM_STAT);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = countPermissionConflicts(&list, false);
|
||||
assert(ret == 1);
|
||||
|
||||
//TODO: more tests
|
||||
|
||||
permissionCleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
test_permissions()
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = test_permission_list();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_permission_list() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = test_permission_table();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_permission_table() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = test_allowed_permissions();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "test_permission_table() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include "symtab.h"
|
||||
|
||||
int
|
||||
test_symtab()
|
||||
{
|
||||
SymbolTable *tab;
|
||||
void *cookie;
|
||||
int ret;
|
||||
|
||||
/* Test creation */
|
||||
tab = createSymbolTable();
|
||||
assert(tab != NULL);
|
||||
|
||||
/* Smoke-test deletion */
|
||||
deleteSymbolTable(tab);
|
||||
|
||||
|
||||
tab = createSymbolTable();
|
||||
assert(tab != NULL);
|
||||
|
||||
|
||||
/* table parameter must be non-NULL. */
|
||||
ret = addToSymbolTable(NULL, NULL, 0, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* symbol parameter must be non-NULL. */
|
||||
ret = addToSymbolTable(tab, NULL, 0, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
/* cookie parameter must be non-NULL. */
|
||||
ret = addToSymbolTable(tab, "null", 0, NULL);
|
||||
assert(ret < 0);
|
||||
|
||||
|
||||
/* table parameter must be non-NULL. */
|
||||
cookie = findInSymbolTable(NULL, NULL, 0);
|
||||
assert(cookie == NULL);
|
||||
|
||||
/* symbol parameter must be non-NULL. */
|
||||
cookie = findInSymbolTable(tab, NULL, 0);
|
||||
assert(cookie == NULL);
|
||||
|
||||
|
||||
/* Try some actual inserts.
|
||||
*/
|
||||
ret = addToSymbolTable(tab, "one", 0, (void *)1);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = addToSymbolTable(tab, "two", 0, (void *)2);
|
||||
assert(ret == 0);
|
||||
|
||||
ret = addToSymbolTable(tab, "three", 0, (void *)3);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Try some lookups.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "one", 0);
|
||||
assert((int)cookie == 1);
|
||||
|
||||
cookie = findInSymbolTable(tab, "two", 0);
|
||||
assert((int)cookie == 2);
|
||||
|
||||
cookie = findInSymbolTable(tab, "three", 0);
|
||||
assert((int)cookie == 3);
|
||||
|
||||
/* Try to insert something that's already there.
|
||||
*/
|
||||
ret = addToSymbolTable(tab, "one", 0, (void *)1111);
|
||||
assert(ret < 0);
|
||||
|
||||
/* Make sure that the failed duplicate insert didn't
|
||||
* clobber the original cookie value.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "one", 0);
|
||||
assert((int)cookie == 1);
|
||||
|
||||
/* Try looking up something that isn't there.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "FOUR", 0);
|
||||
assert(cookie == NULL);
|
||||
|
||||
/* Try looking up something that's similar to an existing entry.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "on", 0);
|
||||
assert(cookie == NULL);
|
||||
|
||||
cookie = findInSymbolTable(tab, "onee", 0);
|
||||
assert(cookie == NULL);
|
||||
|
||||
/* Test flags.
|
||||
* Try inserting something with a different flag.
|
||||
*/
|
||||
ret = addToSymbolTable(tab, "ten", 333, (void *)10);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure it's there.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "ten", 333);
|
||||
assert((int)cookie == 10);
|
||||
|
||||
/* Make sure it's not there when looked up with a different flag.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "ten", 0);
|
||||
assert(cookie == NULL);
|
||||
|
||||
/* Try inserting something that has the same name as something
|
||||
* with a different flag.
|
||||
*/
|
||||
ret = addToSymbolTable(tab, "one", 333, (void *)11);
|
||||
assert(ret == 0);
|
||||
|
||||
/* Make sure the new entry exists.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "one", 333);
|
||||
assert((int)cookie == 11);
|
||||
|
||||
/* Make sure the old entry still has the right value.
|
||||
*/
|
||||
cookie = findInSymbolTable(tab, "one", 0);
|
||||
assert((int)cookie == 1);
|
||||
|
||||
/* Try deleting again, now that there's stuff in the table.
|
||||
*/
|
||||
deleteSymbolTable(tab);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
I am a jelly donut.
|
@ -1,2 +0,0 @@
|
||||
This is a sample no-op test, which does at least serve to verify that the
|
||||
test harness is working.
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
echo 'I am a jelly donut.'
|
@ -1 +0,0 @@
|
||||
EOF
|
@ -1 +0,0 @@
|
||||
Test to make sure that an empty file is accepted properly.
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
amend --debug-lex input
|
@ -1,13 +0,0 @@
|
||||
IDENTIFIER<this_identifier_is_not_assert> EOL
|
||||
IDENTIFIER<NEITHER_IS_THIS_123> EOL
|
||||
IDENTIFIER<but_the_next_one_is> EOL
|
||||
IDENTIFIER<assert> EOL
|
||||
IDENTIFIER<next_one_is_not_an_identifier> EOL
|
||||
line 6: unexpected character at '1'
|
||||
EOF
|
||||
line 1: unexpected character at '"'
|
||||
EOF
|
||||
line 1: unexpected character at '='
|
||||
EOF
|
||||
line 1: unexpected character at '9'
|
||||
EOF
|
@ -1 +0,0 @@
|
||||
Test to make sure that simple command names are tokenized properly.
|
@ -1,6 +0,0 @@
|
||||
this_identifier_is_not_assert
|
||||
NEITHER_IS_THIS_123
|
||||
but_the_next_one_is
|
||||
assert
|
||||
next_one_is_not_an_identifier
|
||||
12not_an_identifier
|
@ -1 +0,0 @@
|
||||
"quoted"
|
@ -1 +0,0 @@
|
||||
==
|
@ -1 +0,0 @@
|
||||
99
|
@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
amend --debug-lex input
|
||||
amend --debug-lex input2
|
||||
amend --debug-lex input3
|
||||
amend --debug-lex input4
|
@ -1,5 +0,0 @@
|
||||
IDENTIFIER<comment_on_this_line> EOL
|
||||
IDENTIFIER<none_on_this_one> EOL
|
||||
EOL
|
||||
EOL
|
||||
EOF
|
@ -1 +0,0 @@
|
||||
Test to make sure that comments are stripped out.
|
@ -1,4 +0,0 @@
|
||||
comment_on_this_line # this is a "comment" (with / a bunch) # \\ of stuff \
|
||||
none_on_this_one
|
||||
# beginning of line
|
||||
# preceded by whitespace
|
@ -1,13 +0,0 @@
|
||||
IDENTIFIER<test> WORD<string> EOL
|
||||
IDENTIFIER<test> WORD<string with spaces> EOL
|
||||
IDENTIFIER<test> WORD<string with "escaped" quotes> EOL
|
||||
IDENTIFIER<test> WORD<string with \escaped\ backslashes> EOL
|
||||
IDENTIFIER<test> WORD<string with # a comment character> EOL
|
||||
EOF
|
||||
EOL
|
||||
IDENTIFIER<test1>line 2: unterminated string at '
|
||||
'
|
||||
??? <0>
|
||||
EOL
|
||||
IDENTIFIER<test1>line 2: illegal escape at '\n'
|
||||
??? <0>
|
@ -1 +0,0 @@
|
||||
Test to make sure that quoted strings are tokenized properly.
|
@ -1,5 +0,0 @@
|
||||
test "string"
|
||||
test "string with spaces"
|
||||
test "string with \"escaped\" quotes"
|
||||
test "string with \\escaped\\ backslashes"
|
||||
test "string with # a comment character"
|
@ -1,2 +0,0 @@
|
||||
# This should fail
|
||||
test1 "unterminated string
|
@ -1,2 +0,0 @@
|
||||
# This should fail
|
||||
test1 "string with illegal escape \n in the middle"
|
@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
amend --debug-lex input
|
||||
amend --debug-lex input2
|
||||
amend --debug-lex input3
|
@ -1,6 +0,0 @@
|
||||
IDENTIFIER<test> WORD<this> WORD<has> WORD<a> WORD<bunch> WORD<of> WORD<BARE> WORD<ALPHA> WORD<WORDS> EOL
|
||||
IDENTIFIER<test> WORD<12> WORD<this> WORD<has(some> WORD<)> WORD<ALPHANUMER1C> WORD<and> WORD<\\> WORD<whatever> WORD<characters> EOL
|
||||
IDENTIFIER<test> WORD<this> WORD<has> WORD<mixed> WORD<bare> WORD<and quoted> WORD<words> EOL
|
||||
IDENTIFIER<test> WORD<what> WORD<about> WORD<quotesin the middle?> EOL
|
||||
IDENTIFIER<test> WORD<"""shouldn't> WORD<be> WORD<a> WORD<quoted> WORD<string> EOL
|
||||
EOF
|
@ -1 +0,0 @@
|
||||
Test to make sure that argument words are tokenized properly.
|
@ -1,5 +0,0 @@
|
||||
test this has a bunch of BARE ALPHA WORDS
|
||||
test 12 this has(some ) ALPHANUMER1C and \\ whatever characters
|
||||
test this has mixed bare "and quoted" words
|
||||
test what about quotes"in the middle?"
|
||||
test \"\"\"shouldn't be a quoted string
|
@ -1,2 +0,0 @@
|
||||
# This should fail
|
||||
test1 "unterminated string
|
@ -1,2 +0,0 @@
|
||||
# This should fail
|
||||
test1 "string with illegal escape \n in the middle"
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
amend --debug-lex input
|
@ -1,11 +0,0 @@
|
||||
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<112345oldhashvalue1234123> EOL
|
||||
IDENTIFIER<mark> WORD<SYS:> WORD<dirty> EOL
|
||||
IDENTIFIER<copy_dir> WORD<PKG:android-files> WORD<SYS:> EOL
|
||||
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL
|
||||
IDENTIFIER<mark> WORD<SYS:> WORD<clean> EOL
|
||||
IDENTIFIER<done> EOL
|
||||
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> , STRING<blah> ) == STRING<112345oldhashvalue1234123> EOL
|
||||
IDENTIFIER<assert> STRING<true> == STRING<false> EOL
|
||||
IDENTIFIER<assert> IDENTIFIER<one> ( STRING<abc> , IDENTIFIER<two> ( STRING<def> ) ) == STRING<five> EOL
|
||||
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> || IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL
|
||||
EOF
|
@ -1 +0,0 @@
|
||||
An input script similar to one that will actually be used in practice.
|
@ -1,10 +0,0 @@
|
||||
assert hash_dir("SYS:") == "112345oldhashvalue1234123"
|
||||
mark SYS: dirty
|
||||
copy_dir "PKG:android-files" SYS:
|
||||
assert hash_dir("SYS:") == "667890newhashvalue6678909"
|
||||
mark SYS: clean
|
||||
done
|
||||
assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123"
|
||||
assert "true" == "false"
|
||||
assert one("abc", two("def")) == "five"
|
||||
assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "667890newhashvalue6678909"
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
amend --debug-lex input
|
@ -1,74 +0,0 @@
|
||||
command "assert" {
|
||||
STRING EQ {
|
||||
FUNCTION hash_dir (
|
||||
"SYS:"
|
||||
)
|
||||
"112345oldhashvalue1234123"
|
||||
}
|
||||
}
|
||||
command "mark" {
|
||||
"SYS:"
|
||||
"dirty"
|
||||
}
|
||||
command "copy_dir" {
|
||||
"PKG:android-files"
|
||||
"SYS:"
|
||||
}
|
||||
command "assert" {
|
||||
STRING EQ {
|
||||
FUNCTION hash_dir (
|
||||
"SYS:"
|
||||
)
|
||||
"667890newhashvalue6678909"
|
||||
}
|
||||
}
|
||||
command "mark" {
|
||||
"SYS:"
|
||||
"clean"
|
||||
}
|
||||
command "done" {
|
||||
}
|
||||
command "assert" {
|
||||
STRING EQ {
|
||||
FUNCTION hash_dir (
|
||||
"SYS:"
|
||||
"blah"
|
||||
)
|
||||
"112345oldhashvalue1234123"
|
||||
}
|
||||
}
|
||||
command "assert" {
|
||||
STRING EQ {
|
||||
"true"
|
||||
"false"
|
||||
}
|
||||
}
|
||||
command "assert" {
|
||||
STRING NE {
|
||||
FUNCTION matches (
|
||||
FUNCTION hash_dir (
|
||||
"SYS:"
|
||||
)
|
||||
"667890newhashvalue6678909"
|
||||
"999999newhashvalue6678909"
|
||||
)
|
||||
""
|
||||
}
|
||||
}
|
||||
command "assert" {
|
||||
BOOLEAN OR {
|
||||
STRING EQ {
|
||||
FUNCTION hash_dir (
|
||||
"SYS:"
|
||||
)
|
||||
"667890newhashvalue6678909"
|
||||
}
|
||||
STRING EQ {
|
||||
FUNCTION hash_dir (
|
||||
"SYS:"
|
||||
)
|
||||
"999999newhashvalue6678909"
|
||||
}
|
||||
}
|
||||
}
|
||||
amend: Parse successful.
|
@ -1 +0,0 @@
|
||||
An input script similar to one that will actually be used in practice.
|
@ -1,10 +0,0 @@
|
||||
assert hash_dir("SYS:") == "112345oldhashvalue1234123"
|
||||
mark SYS: dirty
|
||||
copy_dir "PKG:android-files" SYS:
|
||||
assert hash_dir("SYS:") == "667890newhashvalue6678909"
|
||||
mark SYS: clean
|
||||
done
|
||||
assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123"
|
||||
assert "true" == "false"
|
||||
assert matches(hash_dir("SYS:"), "667890newhashvalue6678909", "999999newhashvalue6678909") != ""
|
||||
assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "999999newhashvalue6678909"
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
amend --debug-ast input
|
@ -1,150 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Set up prog to be the path of this script, including following symlinks,
|
||||
# and set up progdir to be the fully-qualified pathname of its directory.
|
||||
prog="$0"
|
||||
while [ -h "${prog}" ]; do
|
||||
newProg=`/bin/ls -ld "${prog}"`
|
||||
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
|
||||
if expr "x${newProg}" : 'x/' >/dev/null; then
|
||||
prog="${newProg}"
|
||||
else
|
||||
progdir=`dirname "${prog}"`
|
||||
prog="${progdir}/${newProg}"
|
||||
fi
|
||||
done
|
||||
oldwd=`pwd`
|
||||
progdir=`dirname "${prog}"`
|
||||
cd "${progdir}"
|
||||
progdir=`pwd`
|
||||
prog="${progdir}"/`basename "${prog}"`
|
||||
|
||||
info="info.txt"
|
||||
run="run"
|
||||
expected="expected.txt"
|
||||
output="out.txt"
|
||||
skip="SKIP"
|
||||
|
||||
dev_mode="no"
|
||||
if [ "x$1" = "x--dev" ]; then
|
||||
dev_mode="yes"
|
||||
shift
|
||||
fi
|
||||
|
||||
update_mode="no"
|
||||
if [ "x$1" = "x--update" ]; then
|
||||
update_mode="yes"
|
||||
shift
|
||||
fi
|
||||
|
||||
usage="no"
|
||||
if [ "x$1" = "x--help" ]; then
|
||||
usage="yes"
|
||||
else
|
||||
if [ "x$1" = "x" ]; then
|
||||
testdir=`basename "$oldwd"`
|
||||
else
|
||||
testdir="$1"
|
||||
fi
|
||||
|
||||
if [ '!' -d "$testdir" ]; then
|
||||
td2=`echo ${testdir}-*`
|
||||
if [ '!' -d "$td2" ]; then
|
||||
echo "${testdir}: no such test directory" 1>&2
|
||||
usage="yes"
|
||||
fi
|
||||
testdir="$td2"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$usage" = "yes" ]; then
|
||||
prog=`basename $prog`
|
||||
(
|
||||
echo "usage:"
|
||||
echo " $prog --help Print this message."
|
||||
echo " $prog testname Run test normally."
|
||||
echo " $prog --dev testname Development mode (dump to stdout)."
|
||||
echo " $prog --update testname Update mode (replace expected.txt)."
|
||||
echo " Omitting the test name uses the current directory as the test."
|
||||
) 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
td_info="$testdir"/"$info"
|
||||
td_run="$testdir"/"$run"
|
||||
td_expected="$testdir"/"$expected"
|
||||
td_skip="$testdir"/"$skip"
|
||||
|
||||
if [ -r "$td_skip" ]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
tmpdir=/tmp/test-$$
|
||||
|
||||
if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then
|
||||
echo "${testdir}: missing files" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# copy the test to a temp dir and run it
|
||||
|
||||
echo "${testdir}: running..." 1>&2
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
cp -Rp "$testdir" "$tmpdir"
|
||||
cd "$tmpdir"
|
||||
chmod 755 "$run"
|
||||
|
||||
#PATH="${progdir}/../build/bin:${PATH}"
|
||||
|
||||
good="no"
|
||||
if [ "$dev_mode" = "yes" ]; then
|
||||
"./$run" 2>&1
|
||||
echo "exit status: $?" 1>&2
|
||||
good="yes"
|
||||
elif [ "$update_mode" = "yes" ]; then
|
||||
"./$run" >"${progdir}/$td_expected" 2>&1
|
||||
good="yes"
|
||||
else
|
||||
"./$run" >"$output" 2>&1
|
||||
cmp -s "$expected" "$output"
|
||||
if [ "$?" = "0" ]; then
|
||||
# output == expected
|
||||
good="yes"
|
||||
echo "$testdir"': succeeded!' 1>&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$good" = "yes" ]; then
|
||||
cd "$oldwd"
|
||||
rm -rf "$tmpdir"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
(
|
||||
echo "${testdir}: FAILED!"
|
||||
echo ' '
|
||||
echo '#################### info'
|
||||
cat "$info" | sed 's/^/# /g'
|
||||
echo '#################### diffs'
|
||||
diff -u "$expected" "$output"
|
||||
echo '####################'
|
||||
echo ' '
|
||||
echo "files left in $tmpdir"
|
||||
) 1>&2
|
||||
|
||||
exit 1
|
@ -1,69 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Set up prog to be the path of this script, including following symlinks,
|
||||
# and set up progdir to be the fully-qualified pathname of its directory.
|
||||
prog="$0"
|
||||
while [ -h "${prog}" ]; do
|
||||
newProg=`/bin/ls -ld "${prog}"`
|
||||
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
|
||||
if expr "x${newProg}" : 'x/' >/dev/null; then
|
||||
prog="${newProg}"
|
||||
else
|
||||
progdir=`dirname "${prog}"`
|
||||
prog="${progdir}/${newProg}"
|
||||
fi
|
||||
done
|
||||
oldwd=`pwd`
|
||||
progdir=`dirname "${prog}"`
|
||||
cd "${progdir}"
|
||||
progdir=`pwd`
|
||||
prog="${progdir}"/`basename "${prog}"`
|
||||
|
||||
passed=0
|
||||
skipped=0
|
||||
skipNames=""
|
||||
failed=0
|
||||
failNames=""
|
||||
|
||||
for i in *; do
|
||||
if [ -d "$i" -a -r "$i" ]; then
|
||||
./one-test "$i"
|
||||
status=$?
|
||||
if [ "$status" = "0" ]; then
|
||||
((passed += 1))
|
||||
elif [ "$status" = "2" ]; then
|
||||
((skipped += 1))
|
||||
skipNames="$skipNames $i"
|
||||
else
|
||||
((failed += 1))
|
||||
failNames="$failNames $i"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "passed: $passed test(s)"
|
||||
echo "skipped: $skipped test(s)"
|
||||
|
||||
for i in $skipNames; do
|
||||
echo "skipped: $i"
|
||||
done
|
||||
|
||||
echo "failed: $failed test(s)"
|
||||
|
||||
for i in $failNames; do
|
||||
echo "failed: $i"
|
||||
done
|
57
applypatch/Android.mk
Normal file
57
applypatch/Android.mk
Normal file
@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
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 libminelf
|
||||
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 libminelf
|
||||
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)
|
911
applypatch/applypatch.c
Normal file
911
applypatch/applypatch.c
Normal file
@ -0,0 +1,911 @@
|
||||
/*
|
||||
* 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);
|
||||
static int LoadPartitionContents(const char* filename, FileContents* file);
|
||||
int ParseSha1(const char* str, uint8_t* digest);
|
||||
static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
|
||||
|
||||
static int mtd_partitions_scanned = 0;
|
||||
|
||||
// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
|
||||
// the retouched entries back to their original value (such that SHA-1 checks
|
||||
// don't fail due to randomization); store the file contents and associated
|
||||
// metadata in *file.
|
||||
//
|
||||
// Return 0 on success.
|
||||
int LoadFileContents(const char* filename, FileContents* file,
|
||||
int retouch_flag) {
|
||||
file->data = NULL;
|
||||
|
||||
// A special 'filename' beginning with "MTD:" or "EMMC:" means to
|
||||
// load the contents of a partition.
|
||||
if (strncmp(filename, "MTD:", 4) == 0 ||
|
||||
strncmp(filename, "EMMC:", 5) == 0) {
|
||||
return LoadPartitionContents(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);
|
||||
|
||||
// apply_patch[_check] functions are blind to randomization. Randomization
|
||||
// is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
|
||||
// within a file, this means the file is assumed "corrupt" for simplicity.
|
||||
if (retouch_flag) {
|
||||
int32_t desired_offset = 0;
|
||||
if (retouch_mask_data(file->data, file->size,
|
||||
&desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
|
||||
printf("error trying to mask retouch entries\n");
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SHA(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 or EMMC partition into the provided
|
||||
// FileContents. filename should be a string of the form
|
||||
// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:..." (or
|
||||
// "EMMC:<partition_device>:..."). The smallest size_n bytes for
|
||||
// which that prefix of the partition 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 a partition (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.
|
||||
enum PartitionType { MTD, EMMC };
|
||||
|
||||
static int LoadPartitionContents(const char* filename, FileContents* file) {
|
||||
char* copy = strdup(filename);
|
||||
const char* magic = strtok(copy, ":");
|
||||
|
||||
enum PartitionType type;
|
||||
|
||||
if (strcmp(magic, "MTD") == 0) {
|
||||
type = MTD;
|
||||
} else if (strcmp(magic, "EMMC") == 0) {
|
||||
type = EMMC;
|
||||
} else {
|
||||
printf("LoadPartitionContents 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("LoadPartitionContents 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("LoadPartitionContents 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);
|
||||
|
||||
MtdReadContext* ctx = NULL;
|
||||
FILE* dev = NULL;
|
||||
|
||||
switch (type) {
|
||||
case MTD:
|
||||
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;
|
||||
}
|
||||
|
||||
ctx = mtd_read_partition(mtd);
|
||||
if (ctx == NULL) {
|
||||
printf("failed to initialize read of mtd partition \"%s\"\n",
|
||||
partition);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case EMMC:
|
||||
dev = fopen(partition, "rb");
|
||||
if (dev == NULL) {
|
||||
printf("failed to open emmc partition \"%s\": %s\n",
|
||||
partition, strerror(errno));
|
||||
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) {
|
||||
switch (type) {
|
||||
case MTD:
|
||||
read = mtd_read_data(ctx, p, next);
|
||||
break;
|
||||
|
||||
case EMMC:
|
||||
read = fread(p, 1, next, dev);
|
||||
break;
|
||||
}
|
||||
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("partition read matched size %d sha %s\n",
|
||||
size[index[i]], sha1sum[index[i]]);
|
||||
break;
|
||||
}
|
||||
|
||||
p += read;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MTD:
|
||||
mtd_read_close(ctx);
|
||||
break;
|
||||
|
||||
case EMMC:
|
||||
fclose(dev);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (i == pairs) {
|
||||
// Ran off the end of the list of (size,sha1) pairs without
|
||||
// finding a match.
|
||||
printf("contents of 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;
|
||||
}
|
||||
|
||||
|
||||
// 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' partition, a string of the form
|
||||
// "MTD:<partition>[:...]" or "EMMC:<partition_device>:". Return 0 on
|
||||
// success.
|
||||
int WriteToPartition(unsigned char* data, size_t len,
|
||||
const char* target) {
|
||||
char* copy = strdup(target);
|
||||
const char* magic = strtok(copy, ":");
|
||||
|
||||
enum PartitionType type;
|
||||
if (strcmp(magic, "MTD") == 0) {
|
||||
type = MTD;
|
||||
} else if (strcmp(magic, "EMMC") == 0) {
|
||||
type = EMMC;
|
||||
} else {
|
||||
printf("WriteToPartition called with bad target (%s)\n", target);
|
||||
return -1;
|
||||
}
|
||||
const char* partition = strtok(NULL, ":");
|
||||
|
||||
if (partition == NULL) {
|
||||
printf("bad partition target name \"%s\"\n", target);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MTD:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
case EMMC:
|
||||
;
|
||||
FILE* f = fopen(partition, "wb");
|
||||
if (fwrite(data, 1, len, f) != len) {
|
||||
printf("short write writing to %s (%s)\n",
|
||||
partition, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (fclose(f) != 0) {
|
||||
printf("error closing %s (%s)\n", partition, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
free(copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// partitions, where the filename encodes the sha1s; no need to
|
||||
// check them twice.)
|
||||
if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 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, RETOUCH_DO_MASK) != 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 a partition to read the source data.
|
||||
// See the comments for the LoadPartition Contents() 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,
|
||||
RETOUCH_DO_MASK) == 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,
|
||||
RETOUCH_DO_MASK);
|
||||
}
|
||||
|
||||
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,
|
||||
RETOUCH_DO_MASK) < 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 ||
|
||||
strncmp(target_filename, "EMMC:", 5) == 0) {
|
||||
// If the target is a 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 partition 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);
|
||||
enough_space =
|
||||
(free_space > (256 << 10)) && // 256k (two-block) minimum
|
||||
(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 ||
|
||||
strncmp(source_filename, "EMMC:", 5) == 0) {
|
||||
// It's impossible to free space on the target filesystem by
|
||||
// deleting the source if the source is a 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 partition\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 ||
|
||||
strncmp(target_filename, "EMMC:", 5) == 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 partition.
|
||||
if (WriteToPartition(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;
|
||||
}
|
87
applypatch/applypatch.h
Normal file
87
applypatch/applypatch.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 "minelf/Retouch.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);
|
||||
|
||||
int LoadFileContents(const char* filename, FileContents* file,
|
||||
int retouch_flag);
|
||||
int SaveFileContents(const char* filename, FileContents file);
|
||||
void FreeFileContents(FileContents* file);
|
||||
int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
|
||||
int num_patches);
|
||||
|
||||
// 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
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
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
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
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
1010
applypatch/imgdiff.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* 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.
|
||||
@ -14,11 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef AMEND_PARSER_H_
|
||||
#define AMEND_PARSER_H_
|
||||
// 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
|
||||
|
||||
#include "parser_y.h"
|
||||
// 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
|
||||
|
||||
int yyparse(void);
|
||||
|
||||
#endif // AMEND_PARSER_H_
|
||||
// The gzip footer size really is fixed.
|
||||
#define GZIP_FOOTER_LEN 8
|
118
applypatch/imgdiff_test.sh
Executable file
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
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
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, RETOUCH_DONT_MASK) != 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/new.file
vendored
Normal file
Binary file not shown.
BIN
applypatch/testdata/old.file
vendored
Normal file
BIN
applypatch/testdata/old.file
vendored
Normal file
Binary file not shown.
BIN
applypatch/testdata/patch.bsdiff
vendored
Normal file
BIN
applypatch/testdata/patch.bsdiff
vendored
Normal file
Binary file not shown.
65
applypatch/utils.c
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]);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* 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.
|
||||
@ -14,15 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef RECOVERY_COMMANDS_H_
|
||||
#define RECOVERY_COMMANDS_H_
|
||||
#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
||||
#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
ZipArchive *package;
|
||||
} RecoveryCommandContext;
|
||||
// Read and write little-endian values of various sizes.
|
||||
|
||||
int register_update_commands(RecoveryCommandContext *ctx);
|
||||
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 // RECOVERY_COMMANDS_H_
|
||||
#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
19
bmlutils/Android.mk
Normal file
19
bmlutils/Android.mk
Normal file
@ -0,0 +1,19 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
|
||||
|
||||
$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
|
||||
$(if $($(board_define)), \
|
||||
$(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
|
||||
) \
|
||||
)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libcrecovery
|
||||
LOCAL_C_INCLUDES := bootable/recovery/libcrecovery
|
||||
|
||||
LOCAL_SRC_FILES := bmlutils.c
|
||||
LOCAL_MODULE := libbmlutils
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
include $(BUILD_STATIC_LIBRARY)
|
206
bmlutils/bmlutils.c
Normal file
206
bmlutils/bmlutils.c
Normal file
@ -0,0 +1,206 @@
|
||||
#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 <common.h>
|
||||
|
||||
#define BML_UNLOCK_ALL 0x8A29 ///< unlock all partition RO -> RW
|
||||
|
||||
#ifndef BOARD_BML_BOOT
|
||||
#define BOARD_BML_BOOT "/dev/block/bml7"
|
||||
#endif
|
||||
|
||||
#ifndef BOARD_BML_RECOVERY
|
||||
#define BOARD_BML_RECOVERY "/dev/block/bml8"
|
||||
#endif
|
||||
|
||||
static int restore_internal(const char* bml, const char* filename)
|
||||
{
|
||||
char buf[4096];
|
||||
int dstfd, srcfd, bytes_read, bytes_written, total_read = 0;
|
||||
if (filename == NULL)
|
||||
srcfd = 0;
|
||||
else {
|
||||
srcfd = open(filename, O_RDONLY | O_LARGEFILE);
|
||||
if (srcfd < 0)
|
||||
return 2;
|
||||
}
|
||||
dstfd = open(bml, O_RDWR | O_LARGEFILE);
|
||||
if (dstfd < 0)
|
||||
return 3;
|
||||
if (ioctl(dstfd, BML_UNLOCK_ALL, 0))
|
||||
return 4;
|
||||
do {
|
||||
total_read += bytes_read = read(srcfd, buf, 4096);
|
||||
if (!bytes_read)
|
||||
break;
|
||||
if (bytes_read < 4096)
|
||||
memset(&buf[bytes_read], 0, 4096 - bytes_read);
|
||||
if (write(dstfd, buf, 4096) < 4096)
|
||||
return 5;
|
||||
} while(bytes_read == 4096);
|
||||
|
||||
close(dstfd);
|
||||
close(srcfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
|
||||
{
|
||||
if (strcmp(partition, "boot") != 0 && strcmp(partition, "recovery") != 0 && strcmp(partition, "recoveryonly") != 0 && partition[0] != '/')
|
||||
return 6;
|
||||
|
||||
int ret = -1;
|
||||
if (strcmp(partition, "recoveryonly") != 0) {
|
||||
// always restore boot, regardless of whether recovery or boot is flashed.
|
||||
// this is because boot and recovery are the same on some samsung phones.
|
||||
// unless of course, recoveryonly is explictly chosen (bml8)
|
||||
ret = restore_internal(BOARD_BML_BOOT, filename);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (strcmp(partition, "recovery") == 0 || strcmp(partition, "recoveryonly") == 0)
|
||||
ret = restore_internal(BOARD_BML_RECOVERY, filename);
|
||||
|
||||
// support explicitly provided device paths
|
||||
if (partition[0] == '/')
|
||||
ret = restore_internal(partition, filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_bml_backup_raw_partition(const char *partition, const char *out_file)
|
||||
{
|
||||
char* bml;
|
||||
if (strcmp("boot", partition) == 0)
|
||||
bml = BOARD_BML_BOOT;
|
||||
else if (strcmp("recovery", partition) == 0)
|
||||
bml = BOARD_BML_RECOVERY;
|
||||
else if (partition[0] == '/') {
|
||||
// support explicitly provided device paths
|
||||
bml = partition;
|
||||
}
|
||||
else {
|
||||
printf("Invalid partition.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ch;
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
int val = 0;
|
||||
char buf[512];
|
||||
unsigned sz = 0;
|
||||
unsigned i;
|
||||
int ret = -1;
|
||||
char *in_file = bml;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
int format_rfs_device (const char *device, const char *path) {
|
||||
const char *fatsize = "32";
|
||||
const char *sectorsize = "1";
|
||||
|
||||
if (strcmp(path, "/datadata") == 0 || strcmp(path, "/cache") == 0) {
|
||||
fatsize = "16";
|
||||
}
|
||||
|
||||
// Just in case /data sector size needs to be altered
|
||||
else if (strcmp(path, "/data") == 0 ) {
|
||||
sectorsize = "1";
|
||||
}
|
||||
|
||||
// dump 10KB of zeros to partition before format due to fat.format bug
|
||||
char cmd[PATH_MAX];
|
||||
|
||||
sprintf(cmd, "/sbin/dd if=/dev/zero of=%s bs=4096 count=10", device);
|
||||
if(__system(cmd)) {
|
||||
printf("failure while zeroing rfs partition.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Run fat.format
|
||||
sprintf(cmd, "/sbin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device);
|
||||
if(__system(cmd)) {
|
||||
printf("failure while running fat.format\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
6
bmlutils/bmlutils.h
Normal file
6
bmlutils/bmlutils.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef BMLUTILS_H_
|
||||
#define BMLUTILS_H_
|
||||
|
||||
int format_rfs_device (const char *device, const char *path);
|
||||
|
||||
#endif // BMLUTILS_H_
|
241
bootloader.c
241
bootloader.c
@ -22,97 +22,114 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
|
||||
static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
|
||||
static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v);
|
||||
static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
|
||||
|
||||
int get_bootloader_message(struct bootloader_message *out) {
|
||||
Volume* v = volume_for_path("/misc");
|
||||
if(v)
|
||||
{
|
||||
if (strcmp(v->fs_type, "mtd") == 0) {
|
||||
return get_bootloader_message_mtd(out, v);
|
||||
} else if (strcmp(v->fs_type, "emmc") == 0) {
|
||||
return get_bootloader_message_block(out, v);
|
||||
}
|
||||
LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int set_bootloader_message(const struct bootloader_message *in) {
|
||||
Volume* v = volume_for_path("/misc");
|
||||
if(v)
|
||||
{
|
||||
if (strcmp(v->fs_type, "mtd") == 0) {
|
||||
return set_bootloader_message_mtd(in, v);
|
||||
} else if (strcmp(v->fs_type, "emmc") == 0) {
|
||||
return set_bootloader_message_block(in, v);
|
||||
}
|
||||
LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// for misc partitions on MTD
|
||||
// ------------------------------
|
||||
|
||||
static const char *CACHE_NAME = "CACHE:";
|
||||
static const char *MISC_NAME = "MISC:";
|
||||
static const int MISC_PAGES = 3; // number of pages to save
|
||||
static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
|
||||
|
||||
#ifdef LOG_VERBOSE
|
||||
static void dump_data(const char *data, int len) {
|
||||
int pos;
|
||||
for (pos = 0; pos < len; ) {
|
||||
printf("%05x: %02x", pos, data[pos]);
|
||||
for (++pos; pos < len && (pos % 24) != 0; ++pos) {
|
||||
printf(" %02x", data[pos]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int get_bootloader_message(struct bootloader_message *out) {
|
||||
static int get_bootloader_message_mtd(struct bootloader_message *out,
|
||||
const Volume* v) {
|
||||
size_t write_size;
|
||||
const MtdPartition *part = get_root_mtd_partition(MISC_NAME);
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition *part = mtd_find_partition_by_name(v->device);
|
||||
if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
|
||||
LOGE("Can't find %s\n", MISC_NAME);
|
||||
LOGE("Can't find %s\n", v->device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MtdReadContext *read = mtd_read_partition(part);
|
||||
if (read == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const ssize_t size = write_size * MISC_PAGES;
|
||||
char data[size];
|
||||
ssize_t r = mtd_read_data(read, data, size);
|
||||
if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
|
||||
mtd_read_close(read);
|
||||
if (r != size) return -1;
|
||||
|
||||
#ifdef LOG_VERBOSE
|
||||
printf("\n--- get_bootloader_message ---\n");
|
||||
dump_data(data, size);
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_bootloader_message(const struct bootloader_message *in) {
|
||||
static int set_bootloader_message_mtd(const struct bootloader_message *in,
|
||||
const Volume* v) {
|
||||
size_t write_size;
|
||||
const MtdPartition *part = get_root_mtd_partition(MISC_NAME);
|
||||
mtd_scan_partitions();
|
||||
const MtdPartition *part = mtd_find_partition_by_name(v->device);
|
||||
if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
|
||||
LOGE("Can't find %s\n", MISC_NAME);
|
||||
LOGE("Can't find %s\n", v->device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MtdReadContext *read = mtd_read_partition(part);
|
||||
if (read == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t size = write_size * MISC_PAGES;
|
||||
char data[size];
|
||||
ssize_t r = mtd_read_data(read, data, size);
|
||||
if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
|
||||
mtd_read_close(read);
|
||||
if (r != size) return -1;
|
||||
|
||||
memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
|
||||
|
||||
#ifdef LOG_VERBOSE
|
||||
printf("\n--- set_bootloader_message ---\n");
|
||||
dump_data(data, size);
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
MtdWriteContext *write = mtd_write_partition(part);
|
||||
if (write == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (mtd_write_data(write, data, size) != size) {
|
||||
LOGE("Can't write %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
LOGE("Can't write %s\n(%s)\n", v->device, strerror(errno));
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
if (mtd_write_close(write)) {
|
||||
LOGE("Can't finish %s\n(%s)\n", MISC_NAME, strerror(errno));
|
||||
LOGE("Can't finish %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -120,6 +137,70 @@ int set_bootloader_message(const struct bootloader_message *in) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------
|
||||
// for misc partitions on block devices
|
||||
// ------------------------------------
|
||||
|
||||
static void wait_for_device(const char* fn) {
|
||||
int tries = 0;
|
||||
int ret;
|
||||
struct stat buf;
|
||||
do {
|
||||
++tries;
|
||||
ret = stat(fn, &buf);
|
||||
if (ret) {
|
||||
printf("stat %s try %d: %s\n", fn, tries, strerror(errno));
|
||||
sleep(1);
|
||||
}
|
||||
} while (ret && tries < 10);
|
||||
if (ret) {
|
||||
printf("failed to stat %s\n", fn);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_bootloader_message_block(struct bootloader_message *out,
|
||||
const Volume* v) {
|
||||
wait_for_device(v->device);
|
||||
FILE* f = fopen(v->device, "rb");
|
||||
if (f == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
struct bootloader_message temp;
|
||||
int count = fread(&temp, sizeof(temp), 1, f);
|
||||
if (count != 1) {
|
||||
LOGE("Failed reading %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (fclose(f) != 0) {
|
||||
LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
memcpy(out, &temp, sizeof(temp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_bootloader_message_block(const struct bootloader_message *in,
|
||||
const Volume* v) {
|
||||
wait_for_device(v->device);
|
||||
FILE* f = fopen(v->device, "wb");
|
||||
if (f == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
int count = fwrite(in, sizeof(*in), 1, f);
|
||||
if (count != 1) {
|
||||
LOGE("Failed writing %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (fclose(f) != 0) {
|
||||
LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update Image
|
||||
*
|
||||
* - will be stored in the "cache" partition
|
||||
@ -130,27 +211,27 @@ int set_bootloader_message(const struct bootloader_message *in) {
|
||||
* - two raw bitmaps will be included, the "busy" and "fail" bitmaps
|
||||
* - for dream, the bitmaps will be 320x480x16bpp RGB565
|
||||
*/
|
||||
|
||||
|
||||
#define UPDATE_MAGIC "MSM-RADIO-UPDATE"
|
||||
#define UPDATE_MAGIC_SIZE 16
|
||||
#define UPDATE_VERSION 0x00010000
|
||||
|
||||
|
||||
struct update_header {
|
||||
unsigned char MAGIC[UPDATE_MAGIC_SIZE];
|
||||
|
||||
|
||||
unsigned version;
|
||||
unsigned size;
|
||||
|
||||
|
||||
unsigned image_offset;
|
||||
unsigned image_length;
|
||||
|
||||
|
||||
unsigned bitmap_width;
|
||||
unsigned bitmap_height;
|
||||
unsigned bitmap_bpp;
|
||||
|
||||
|
||||
unsigned busy_bitmap_offset;
|
||||
unsigned busy_bitmap_length;
|
||||
|
||||
|
||||
unsigned fail_bitmap_offset;
|
||||
unsigned fail_bitmap_length;
|
||||
};
|
||||
@ -159,113 +240,113 @@ int write_update_for_bootloader(
|
||||
const char *update, int update_length,
|
||||
int bitmap_width, int bitmap_height, int bitmap_bpp,
|
||||
const char *busy_bitmap, const char *fail_bitmap) {
|
||||
if (ensure_root_path_unmounted(CACHE_NAME)) {
|
||||
LOGE("Can't unmount %s\n", CACHE_NAME);
|
||||
if (ensure_path_unmounted("/cache")) {
|
||||
LOGE("Can't unmount /cache\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const MtdPartition *part = get_root_mtd_partition(CACHE_NAME);
|
||||
|
||||
const MtdPartition *part = mtd_find_partition_by_name("cache");
|
||||
if (part == NULL) {
|
||||
LOGE("Can't find %s\n", CACHE_NAME);
|
||||
LOGE("Can't find cache\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
MtdWriteContext *write = mtd_write_partition(part);
|
||||
if (write == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't open cache\n(%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Write an invalid (zero) header first, to disable any previous
|
||||
* update and any other structured contents (like a filesystem),
|
||||
* and as a placeholder for the amount of space required.
|
||||
*/
|
||||
|
||||
|
||||
struct update_header header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
const ssize_t header_size = sizeof(header);
|
||||
if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
|
||||
LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't write header to cache\n(%s)\n", strerror(errno));
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Write each section individually block-aligned, so we can write
|
||||
* each block independently without complicated buffering.
|
||||
*/
|
||||
|
||||
|
||||
memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE);
|
||||
header.version = UPDATE_VERSION;
|
||||
header.size = header_size;
|
||||
|
||||
|
||||
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) {
|
||||
LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't write update to cache\n(%s)\n", strerror(errno));
|
||||
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;
|
||||
header.bitmap_bpp = bitmap_bpp;
|
||||
|
||||
|
||||
int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
|
||||
|
||||
|
||||
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) {
|
||||
LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't write bitmap to cache\n(%s)\n", strerror(errno));
|
||||
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_length = fail_bitmap != NULL ? bitmap_length : 0;
|
||||
if ((int) header.fail_bitmap_offset == -1 ||
|
||||
mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
|
||||
LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't write bitmap to cache\n(%s)\n", strerror(errno));
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
if (mtd_write_close(write)) {
|
||||
LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't finish writing cache\n(%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
write = mtd_write_partition(part);
|
||||
if (write == NULL) {
|
||||
LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't reopen cache\n(%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
|
||||
LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't rewrite header to cache\n(%s)\n", strerror(errno));
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (mtd_erase_blocks(write, 0) != image_start_pos) {
|
||||
LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Misalignment rewriting cache\n(%s)\n", strerror(errno));
|
||||
mtd_write_close(write);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (mtd_write_close(write)) {
|
||||
LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno));
|
||||
LOGE("Can't finish header of cache\n(%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
1293
commands.c
1293
commands.c
File diff suppressed because it is too large
Load Diff
63
common.h
63
common.h
@ -26,12 +26,15 @@ void ui_init();
|
||||
int ui_wait_key(); // waits for a key/button press, returns the code
|
||||
int ui_key_pressed(int key); // returns >0 if the code is currently pressed
|
||||
int ui_text_visible(); // returns >0 if text log is currently visible
|
||||
int ui_text_ever_visible(); // returns >0 if text log was ever visible
|
||||
void ui_show_text(int visible);
|
||||
void ui_clear_key_queue();
|
||||
|
||||
// Write a message to the on-screen log shown with Alt-L (also to stderr).
|
||||
// The screen is small, and users may need to report these messages to support,
|
||||
// so keep the output short and not too cryptic.
|
||||
void ui_print(const char *fmt, ...);
|
||||
void ui_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void ui_printlogtail(int nb_lines);
|
||||
|
||||
void ui_reset_text_col();
|
||||
void ui_set_show_text(int value);
|
||||
@ -39,7 +42,7 @@ 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).
|
||||
int ui_start_menu(char** headers, char** items);
|
||||
int ui_start_menu(char** headers, char** items, int initial_selection);
|
||||
// Set the menu highlight to the given index, and return it (capped to
|
||||
// the range [0..numitems).
|
||||
int ui_menu_select(int sel);
|
||||
@ -55,6 +58,7 @@ enum {
|
||||
BACKGROUND_ICON_NONE,
|
||||
BACKGROUND_ICON_INSTALLING,
|
||||
BACKGROUND_ICON_ERROR,
|
||||
BACKGROUND_ICON_CLOCKWORK,
|
||||
BACKGROUND_ICON_FIRMWARE_INSTALLING,
|
||||
BACKGROUND_ICON_FIRMWARE_ERROR,
|
||||
NUM_BACKGROUND_ICONS
|
||||
@ -85,12 +89,12 @@ void ui_show_indeterminate_progress();
|
||||
void ui_reset_progress();
|
||||
|
||||
#define LOGE(...) ui_print("E:" __VA_ARGS__)
|
||||
#define LOGW(...) fprintf(stderr, "W:" __VA_ARGS__)
|
||||
#define LOGI(...) fprintf(stderr, "I:" __VA_ARGS__)
|
||||
#define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
|
||||
#define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
|
||||
|
||||
#if 0
|
||||
#define LOGV(...) fprintf(stderr, "V:" __VA_ARGS__)
|
||||
#define LOGD(...) fprintf(stderr, "D:" __VA_ARGS__)
|
||||
#define LOGV(...) fprintf(stdout, "V:" __VA_ARGS__)
|
||||
#define LOGD(...) fprintf(stdout, "D:" __VA_ARGS__)
|
||||
#else
|
||||
#define LOGV(...) do {} while (0)
|
||||
#define LOGD(...) do {} while (0)
|
||||
@ -99,4 +103,51 @@ void ui_reset_progress();
|
||||
#define STRINGIFY(x) #x
|
||||
#define EXPAND(x) STRINGIFY(x)
|
||||
|
||||
typedef struct {
|
||||
const char* mount_point; // eg. "/cache". must live in the root directory.
|
||||
|
||||
const char* fs_type; // "yaffs2" or "ext4" or "vfat"
|
||||
|
||||
const char* device; // MTD partition name if fs_type == "yaffs"
|
||||
// block device if fs_type == "ext4" or "vfat"
|
||||
|
||||
const char* device2; // alternative device to try if fs_type
|
||||
// == "ext4" or "vfat" and mounting
|
||||
// 'device' fails
|
||||
|
||||
long long length; // (ext4 partition only) when
|
||||
// formatting, size to use for the
|
||||
// partition. 0 or negative number
|
||||
// means to format all but the last
|
||||
// (that much).
|
||||
|
||||
const char* fs_type2;
|
||||
|
||||
const char* fs_options;
|
||||
|
||||
const char* fs_options2;
|
||||
} Volume;
|
||||
|
||||
typedef struct {
|
||||
// number of frames in indeterminate progress bar animation
|
||||
int indeterminate_frames;
|
||||
|
||||
// number of frames per second to try to maintain when animating
|
||||
int update_fps;
|
||||
|
||||
// number of frames in installing animation. may be zero for a
|
||||
// static installation icon.
|
||||
int installing_frames;
|
||||
|
||||
// the install icon is animated by drawing images containing the
|
||||
// changing part over the base icon. These specify the
|
||||
// coordinates of the upper-left corner.
|
||||
int install_overlay_offset_x;
|
||||
int install_overlay_offset_y;
|
||||
|
||||
} UIParameters;
|
||||
|
||||
// fopen a file, mounting volumes and making parent dirs as necessary.
|
||||
FILE* fopen_path(const char *path, const char *mode);
|
||||
|
||||
#endif // RECOVERY_COMMON_H
|
||||
|
68
default_recovery_keys.c
Normal file
68
default_recovery_keys.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "recovery_ui.h"
|
||||
#include "common.h"
|
||||
#include "extendedcommands.h"
|
||||
|
||||
|
||||
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 0;
|
||||
//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_handle_key(int key_code, int visible) {
|
||||
if (visible) {
|
||||
switch (key_code) {
|
||||
case KEY_CAPSLOCK:
|
||||
case KEY_DOWN:
|
||||
case KEY_VOLUMEDOWN:
|
||||
case KEY_MENU:
|
||||
return HIGHLIGHT_DOWN;
|
||||
|
||||
case KEY_LEFTSHIFT:
|
||||
case KEY_UP:
|
||||
case KEY_VOLUMEUP:
|
||||
case KEY_HOME:
|
||||
return HIGHLIGHT_UP;
|
||||
|
||||
case KEY_POWER:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display() && ui_menu_level > 0) {
|
||||
return GO_BACK;
|
||||
}
|
||||
break;
|
||||
case KEY_LEFTBRACE:
|
||||
case KEY_ENTER:
|
||||
case BTN_MOUSE:
|
||||
case KEY_CAMERA:
|
||||
case KEY_F21:
|
||||
case KEY_SEND:
|
||||
return SELECT_ITEM;
|
||||
|
||||
case KEY_END:
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_SEARCH:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display() && ui_menu_level > 0) {
|
||||
return GO_BACK;
|
||||
}
|
||||
case KEY_BACK:
|
||||
if (ui_menu_level > 0) {
|
||||
return GO_BACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ACTION;
|
||||
}
|
@ -23,70 +23,25 @@
|
||||
char* MENU_HEADERS[] = { NULL };
|
||||
|
||||
char* MENU_ITEMS[] = { "reboot system now",
|
||||
"apply sdcard:update.zip",
|
||||
"install zip from sdcard",
|
||||
"wipe data/factory reset",
|
||||
"wipe cache partition",
|
||||
"install zip from sdcard",
|
||||
"backup and restore",
|
||||
"mounts and storage",
|
||||
"advanced",
|
||||
NULL };
|
||||
|
||||
int device_toggle_display(volatile char* key_pressed, int key_code) {
|
||||
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
|
||||
if (alt && key_code == KEY_L)
|
||||
return 1;
|
||||
// allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
|
||||
if (ui_get_showing_back_button()) {
|
||||
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
|
||||
}
|
||||
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END);
|
||||
void device_ui_init(UIParameters* ui_parameters) {
|
||||
}
|
||||
|
||||
int device_recovery_start() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
165
edify/expr.c
165
edify/expr.c
@ -33,12 +33,40 @@ int BooleanString(const char* s) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char* ConcatFn(const char* name, State* state, int 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 strdup("");
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
char** strings = malloc(argc * sizeof(char*));
|
||||
int i;
|
||||
@ -67,10 +95,11 @@ char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(strings[i]);
|
||||
}
|
||||
return result;
|
||||
free(strings);
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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");
|
||||
@ -83,18 +112,18 @@ char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
|
||||
if (BooleanString(cond) == true) {
|
||||
free(cond);
|
||||
return Evaluate(state, argv[1]);
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
if (argc == 3) {
|
||||
free(cond);
|
||||
return Evaluate(state, argv[2]);
|
||||
return EvaluateValue(state, argv[2]);
|
||||
} else {
|
||||
return cond;
|
||||
return StringValue(cond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* msg = NULL;
|
||||
if (argc > 0) {
|
||||
msg = Evaluate(state, argv[0]);
|
||||
@ -108,7 +137,7 @@ char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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]);
|
||||
@ -130,20 +159,20 @@ char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return strdup("");
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
|
||||
char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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 val;
|
||||
return StringValue(val);
|
||||
}
|
||||
|
||||
char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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]);
|
||||
@ -153,48 +182,44 @@ char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
fputs(v, stdout);
|
||||
free(v);
|
||||
}
|
||||
return strdup("");
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
|
||||
char* LogicalAndFn(const char* name, State* state,
|
||||
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 Evaluate(state, argv[1]);
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return left;
|
||||
return StringValue(left);
|
||||
}
|
||||
}
|
||||
|
||||
char* LogicalOrFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
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 Evaluate(state, argv[1]);
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return left;
|
||||
return StringValue(left);
|
||||
}
|
||||
}
|
||||
|
||||
char* LogicalNotFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
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);
|
||||
if (bv) {
|
||||
return strdup("");
|
||||
} else {
|
||||
return strdup("t");
|
||||
}
|
||||
return StringValue(strdup(bv ? "" : "t"));
|
||||
}
|
||||
|
||||
char* SubstringFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
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]);
|
||||
@ -206,10 +231,10 @@ char* SubstringFn(const char* name, State* state,
|
||||
char* result = strdup(strstr(haystack, needle) ? "t" : "");
|
||||
free(needle);
|
||||
free(haystack);
|
||||
return result;
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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]);
|
||||
@ -221,10 +246,10 @@ char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
|
||||
free(left);
|
||||
free(right);
|
||||
return result;
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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]);
|
||||
@ -236,17 +261,17 @@ char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
|
||||
free(left);
|
||||
free(right);
|
||||
return result;
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
Value* left = EvaluateValue(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
free(left);
|
||||
return Evaluate(state, argv[1]);
|
||||
FreeValue(left);
|
||||
return EvaluateValue(state, argv[1]);
|
||||
}
|
||||
|
||||
char* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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");
|
||||
@ -277,10 +302,11 @@ char* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
done:
|
||||
free(left);
|
||||
free(right);
|
||||
return strdup(result ? "t" : "");
|
||||
return StringValue(strdup(result ? "t" : ""));
|
||||
}
|
||||
|
||||
char* GreaterThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
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");
|
||||
@ -294,8 +320,8 @@ char* GreaterThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return LessThanIntFn(name, state, 2, temp);
|
||||
}
|
||||
|
||||
char* Literal(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return strdup(name);
|
||||
Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return StringValue(strdup(name));
|
||||
}
|
||||
|
||||
Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
|
||||
@ -389,11 +415,39 @@ int ReadArgs(State* state, Expr* argv[], int count, ...) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -418,9 +472,30 @@ char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
|
||||
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.
|
||||
char* ErrorAbort(State* state, char* format, ...) {
|
||||
Value* ErrorAbort(State* state, char* format, ...) {
|
||||
char* buffer = malloc(4096);
|
||||
va_list v;
|
||||
va_start(v, format);
|
||||
|
67
edify/expr.h
67
edify/expr.h
@ -17,6 +17,8 @@
|
||||
#ifndef _EXPRESSION_H
|
||||
#define _EXPRESSION_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "yydefs.h"
|
||||
|
||||
#define MAX_STRING_LEN 1024
|
||||
@ -39,8 +41,17 @@ typedef struct {
|
||||
char* errmsg;
|
||||
} State;
|
||||
|
||||
typedef char* (*Function)(const char* name, State* state,
|
||||
int argc, Expr* argv[]);
|
||||
#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;
|
||||
@ -50,31 +61,41 @@ struct Expr {
|
||||
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.
|
||||
char* Literal(const char* name, State* state, int argc, Expr* argv[]);
|
||||
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.)
|
||||
char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
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().
|
||||
char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
char* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
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
|
||||
@ -112,15 +133,31 @@ Function FindFunction(const char* name);
|
||||
// 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.
|
||||
char* ErrorAbort(State* state, char* format, ...);
|
||||
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
|
||||
|
@ -15,6 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "expr.h"
|
||||
#include "yydefs.h"
|
||||
#include "parser.h"
|
||||
|
@ -42,11 +42,12 @@ int expect(const char* expr_str, const char* expected, int* errors) {
|
||||
|
||||
State state;
|
||||
state.cookie = NULL;
|
||||
state.script = expr_str;
|
||||
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;
|
||||
@ -181,6 +182,10 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -33,4 +33,6 @@ typedef struct {
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int yylex();
|
||||
|
||||
#endif
|
||||
|
441
edifyscripting.c
Normal file
441
edifyscripting.c
Normal file
@ -0,0 +1,441 @@
|
||||
/*
|
||||
* 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 <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 "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
|
||||
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
|
||||
|
||||
#include "extendedcommands.h"
|
||||
#include "nandroid.h"
|
||||
#include "mounts.h"
|
||||
#include "flashutils/flashutils.h"
|
||||
#include "edify/expr.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "mmcutils/mmcutils.h"
|
||||
//#include "edify/parser.h"
|
||||
|
||||
Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
size += strlen(args[i]);
|
||||
}
|
||||
char* buffer = malloc(size+1);
|
||||
size = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strcpy(buffer+size, args[i]);
|
||||
size += strlen(args[i]);
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
buffer[size] = '\0';
|
||||
|
||||
char* line = strtok(buffer, "\n");
|
||||
while (line) {
|
||||
ui_print("%s\n", line);
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
return StringValue(buffer);
|
||||
}
|
||||
|
||||
Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc < 1) {
|
||||
return ErrorAbort(state, "%s() expects at least 1 arg", name);
|
||||
}
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char** args2 = malloc(sizeof(char*) * (argc+1));
|
||||
memcpy(args2, args, sizeof(char*) * argc);
|
||||
args2[argc] = NULL;
|
||||
|
||||
fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
execv(args2[0], args2);
|
||||
fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
fprintf(stderr, "run_program: child exited with status %d\n",
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(stderr, "run_program: child terminated by signal %d\n",
|
||||
WTERMSIG(status));
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
free(args2);
|
||||
|
||||
char buffer[20];
|
||||
sprintf(buffer, "%d", status);
|
||||
|
||||
return StringValue(strdup(buffer));
|
||||
}
|
||||
|
||||
Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
|
||||
}
|
||||
|
||||
char *path;
|
||||
if (ReadArgs(state, argv, 1, &path) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ui_print("Formatting %s...\n", path);
|
||||
if (0 != format_volume(path)) {
|
||||
free(path);
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
|
||||
if (strcmp(path, "/data") == 0 && has_datadata()) {
|
||||
ui_print("Formatting /datadata...\n", path);
|
||||
if (0 != format_volume("/datadata")) {
|
||||
free(path);
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
if (0 != format_volume("/sdcard/.android_secure")) {
|
||||
free(path);
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return StringValue(strdup(path));
|
||||
}
|
||||
|
||||
Value* BackupFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
|
||||
}
|
||||
char* path;
|
||||
if (ReadArgs(state, argv, 1, &path) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (0 != nandroid_backup(path))
|
||||
return StringValue(strdup(""));
|
||||
|
||||
return StringValue(strdup(path));
|
||||
}
|
||||
|
||||
Value* RestoreFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc < 1) {
|
||||
return ErrorAbort(state, "%s() expects at least 1 arg", name);
|
||||
}
|
||||
char** args = ReadVarArgs(state, argc, argv);
|
||||
if (args == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char** args2 = malloc(sizeof(char*) * (argc+1));
|
||||
memcpy(args2, args, sizeof(char*) * argc);
|
||||
args2[argc] = NULL;
|
||||
|
||||
char* path = strdup(args2[0]);
|
||||
int restoreboot = 1;
|
||||
int restoresystem = 1;
|
||||
int restoredata = 1;
|
||||
int restorecache = 1;
|
||||
int restoresdext = 1;
|
||||
int i;
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (args2[i] == NULL)
|
||||
continue;
|
||||
if (strcmp(args2[i], "noboot") == 0)
|
||||
restoreboot = 0;
|
||||
else if (strcmp(args2[i], "nosystem") == 0)
|
||||
restoresystem = 0;
|
||||
else if (strcmp(args2[i], "nodata") == 0)
|
||||
restoredata = 0;
|
||||
else if (strcmp(args2[i], "nocache") == 0)
|
||||
restorecache = 0;
|
||||
else if (strcmp(args2[i], "nosd-ext") == 0)
|
||||
restoresdext = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(args[i]);
|
||||
}
|
||||
free(args);
|
||||
free(args2);
|
||||
|
||||
if (0 != nandroid_restore(path, restoreboot, restoresystem, restoredata, restorecache, restoresdext, 0)) {
|
||||
free(path);
|
||||
return StringValue(strdup(""));
|
||||
}
|
||||
|
||||
return StringValue(path);
|
||||
}
|
||||
|
||||
Value* InstallZipFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
|
||||
}
|
||||
char* path;
|
||||
if (ReadArgs(state, argv, 1, &path) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (0 != install_zip(path))
|
||||
return StringValue(strdup(""));
|
||||
|
||||
return StringValue(strdup(path));
|
||||
}
|
||||
|
||||
Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 1) {
|
||||
return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
|
||||
}
|
||||
char* path;
|
||||
if (ReadArgs(state, argv, 1, &path) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (0 != ensure_path_mounted(path))
|
||||
return StringValue(strdup(""));
|
||||
|
||||
return StringValue(strdup(path));
|
||||
}
|
||||
|
||||
void RegisterRecoveryHooks() {
|
||||
RegisterFunction("mount", MountFn);
|
||||
RegisterFunction("format", FormatFn);
|
||||
RegisterFunction("ui_print", UIPrintFn);
|
||||
RegisterFunction("run_program", RunProgramFn);
|
||||
RegisterFunction("backup_rom", BackupFn);
|
||||
RegisterFunction("restore_rom", RestoreFn);
|
||||
RegisterFunction("install_zip", InstallZipFn);
|
||||
}
|
||||
|
||||
static int hasInitializedEdify = 0;
|
||||
int run_script_from_buffer(char* script_data, int script_len, char* filename)
|
||||
{
|
||||
if (!hasInitializedEdify) {
|
||||
RegisterBuiltins();
|
||||
RegisterRecoveryHooks();
|
||||
FinishRegistration();
|
||||
hasInitializedEdify = 1;
|
||||
}
|
||||
|
||||
Expr* root;
|
||||
int error_count = 0;
|
||||
yy_scan_bytes(script_data, script_len);
|
||||
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 = script_data;
|
||||
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);
|
||||
return -1;
|
||||
} else {
|
||||
printf("result is [%s]\n", result);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand"
|
||||
|
||||
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_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.");
|
||||
}
|
||||
|
||||
ui_print("Verifying SD Card marker...\n");
|
||||
struct stat st;
|
||||
if (stat("/sdcard/clockworkmod/.salted_hash", &st) != 0) {
|
||||
ui_print("SD Card marker not found...\n");
|
||||
if (volume_for_path("/emmc") != NULL) {
|
||||
ui_print("Checking Internal SD Card marker...\n");
|
||||
ensure_path_unmounted("/sdcard");
|
||||
if (ensure_path_mounted_at_mount_point("/emmc", "/sdcard") != 0) {
|
||||
ui_print("Internal SD Card marker not found... continuing anyways.\n");
|
||||
// unmount everything, and remount as normal
|
||||
ensure_path_unmounted("/emmc");
|
||||
ensure_path_unmounted("/sdcard");
|
||||
|
||||
ensure_path_mounted("/sdcard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
|
||||
int ret;
|
||||
#ifdef I_AM_KOUSH
|
||||
if (0 != (ret = before_run_script(tmp))) {
|
||||
ui_print("Error processing ROM Manager script. Please verify that you are performing the backup, restore, or ROM installation from ROM Manager v4.4.0.0 or higher.\n");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
return run_script(tmp);
|
||||
}
|
||||
|
||||
int extendedcommand_file_exists()
|
||||
{
|
||||
struct stat file_info;
|
||||
return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
|
||||
}
|
||||
|
||||
int edify_main(int argc, char** argv) {
|
||||
load_volume_table();
|
||||
process_volumes();
|
||||
RegisterBuiltins();
|
||||
RegisterRecoveryHooks();
|
||||
FinishRegistration();
|
||||
|
||||
if (argc != 2) {
|
||||
printf("edify <filename>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
31
etc/init.rc
31
etc/init.rc
@ -1,3 +1,5 @@
|
||||
on early-init
|
||||
start ueventd
|
||||
|
||||
on init
|
||||
export PATH /sbin
|
||||
@ -7,7 +9,11 @@ on init
|
||||
|
||||
symlink /system/etc /etc
|
||||
|
||||
mkdir /boot
|
||||
mkdir /sdcard
|
||||
mkdir /sd-ext
|
||||
mkdir /datadata
|
||||
mkdir /emmc
|
||||
mkdir /system
|
||||
mkdir /data
|
||||
mkdir /cache
|
||||
@ -21,13 +27,30 @@ on boot
|
||||
|
||||
class_start default
|
||||
|
||||
service ueventd /sbin/ueventd
|
||||
critical
|
||||
|
||||
service recovery /sbin/recovery
|
||||
|
||||
service adbd /sbin/adbd recovery
|
||||
disabled
|
||||
|
||||
on property:persist.service.adb.enable=1
|
||||
start adbd
|
||||
# Always start adbd on userdebug and eng builds
|
||||
# In recovery, always run adbd as root.
|
||||
on property:ro.debuggable=1
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
write /sys/class/android_usb/android0/idVendor 18D1
|
||||
write /sys/class/android_usb/android0/idProduct D001
|
||||
write /sys/class/android_usb/android0/functions adb
|
||||
#write /sys/class/android_usb/android0/enable 1
|
||||
write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer
|
||||
write /sys/class/android_usb/android0/iProduct $ro.product.model
|
||||
write /sys/class/android_usb/android0/iSerial $ro.serialno
|
||||
#start adbd
|
||||
setprop service.adb.root 1
|
||||
|
||||
on property:persist.service.adb.enable=0
|
||||
stop adbd
|
||||
# Restart adbd so it can run as root
|
||||
on property:service.adb.root=1
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
restart adbd
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
|
1009
extendedcommands.c
1009
extendedcommands.c
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user