173 Commits

Author SHA1 Message Date
8df69c0660 minui: useless code is useless
Change-Id: I288c82a647c4145a2569d5dec206b8ca89693e7c
2010-11-27 23:02:01 -06:00
229a543bb3 minui: clean up some stuff, and avoid multiple key events on N1
Change-Id: I7d26a406eb200c866b2fbc841424ac698f6d3bf3
2010-11-27 22:52:39 -06:00
517a16b98f Merge "minui: Add haptic feedback to virtual keys" into froyo 2010-11-28 03:54:03 +00:00
9397e322c3 Merge "minui: add support for virtual keys" into froyo 2010-11-28 03:53:44 +00:00
ddc2e39dcf fix fstab generation
Change-Id: I223a07fd0ce3c0d48b7f18f5c42ae204affe087a
2010-11-27 17:53:50 -08:00
d90ad5dedf fix no select button
Change-Id: I142d48156e166f9e5e7f3ad7c89c1829dbe2009a
2010-11-27 14:41:07 -08:00
3847a2ddd9 add missing file
Change-Id: I11948c0b4aece99e5cbdc32b39ba5b76d1b0ffb2
2010-11-27 13:36:17 -08:00
44bd4947d2 minui: Add haptic feedback to virtual keys
Change-Id: Ibd9298513626092297bff83f4914b436c4ffbb3a
2010-11-27 13:36:23 -06:00
ac731c88e3 minui: add support for virtual keys
Change-Id: Ie0e75a2fbccd42993a2f7c3d42ba38afea2d7679
2010-11-26 23:32:26 -06:00
9acb022ab8 recovery: Support board-specified font sizes and font-files
This, for example, is for an LDPI device:
BOARD_RECOVERY_FONT_INCLUDE_HEADER := ../../device/geeksphone/one/font_7x16.h
BOARD_RECOVERY_FONT_WIDTH := 7
BOARD_RECOVERY_FONT_HEIGHT := 16

Change-Id: I71d2f420b9668b5767c9b0b5d90e3e523c56e873
2010-11-27 04:08:06 +00:00
37d96f7414 2.5.1.3
Change-Id: Iae5d458edaedb90dd2422175a185a9b5725dbc74
2010-11-26 18:45:06 -08:00
0b23331f23 Added larger DATA size values for SD partitioning.
Used for systems that use internal SD partitioning for DATA storage.

Change-Id: I3274c196a7143143fccb806cff1665a6f40c1ce7
2010-11-19 10:28:56 -05:00
4123b58299 recovery: Autodetection of device flash type
Detect flash type at runtime rather than requiring this to be set in the
device configuration. The detection is based on the existence of /proc/mtd,
/proc/emmc, or /dev/block/bml1.

Change-Id: I464962a567022c5862c249f06d36c2d1cddeacba
2010-11-14 22:40:30 -05:00
158d25cae4 Supress "warning: comparison between signed and unsigned integer expressions" in minui/resources.c and mtdutils/mtdutils.c 2010-11-13 05:23:33 +08:00
7c50645a6c always build
Change-Id: I007eb15230e948074a1aad420ef0c3c6a2ae102e
2010-11-11 01:20:29 -08:00
e5c7e0eaca implement bmlutils
Change-Id: I8ec38a3b5e20e39b02c393d13e89142038ae3e82
2010-11-11 01:19:10 -08:00
12154200a7 fix up implementation of int get_partition_device
Change-Id: I142b15228322790892dd07b10d6a3f31440badc7
2010-11-11 01:16:14 -08:00
d771acbed0 Modify apply_patch to fail if trying to patch mtd on non-mtd devices.
Change-Id: I09c5bb5d12d838fdc4c4c6eb380021c9b3f3e33e
2010-11-11 00:00:45 -08:00
1b86754eaa Fix up updater to use the new refactored block device support.
Change-Id: I597f3d83ef14d2640192e0026a0fe1a93023bfc6
2010-11-10 23:52:09 -08:00
19447c0550 Refactor recovery's block device handling to work across variant hardware in a cleaner fashion.
Re add firmware update

Change-Id: I699ad22390ed14e597d17a7bcb32ad1b1af00b4b

support mmc misc

Change-Id: Iff02f8d03db6835f501d052140cebeefee521305

fix compile errors

Change-Id: I032edbd157a8a15f561bb83330c715ebaa008d18

fix compile errors

Change-Id: Idff3449be3376f22fceefc2c35637527f8df8f3f

Initial work to clean up the block devices.

Change-Id: I4be20ac124864a281be9cd116e211a2618404a27

all done

Change-Id: I0338f62f6a045556ebe90b0200685be113178319

fix up nandroid

Change-Id: I886f00271183e6d2921c080b0939341f2cf12a4d
2010-11-10 23:31:34 -08:00
fef77c0253 Changes to support Vision recovery. Fixing up a lot of bugs related to the CodeAurora mmc commit.
Change-Id: I9b71070fe41559a5d93d3c35efc3a511b7088e8e
2010-11-09 20:03:42 -08:00
7a77aec362 fix mmcutils usage
Change-Id: I04db586599af21f64174b0e656201c724bf82050
2010-11-09 09:23:15 -08:00
487821abe5 Merge remote branch 'github/eclair' into froyo 2010-11-06 17:59:05 -07:00
9456641236 2.5.1.1 2010-11-05 12:47:48 -07:00
1805cfe02e missing file
Change-Id: Ie87219d798f62128906fc6050ced7be5b4801426
2010-10-12 18:46:41 -07:00
815ca5d230 [recovery]: Add support for OTA upgrade on mmc devices.
Change-Id: I8f230dfa5be4e9f142765797d949e10434e1fdeb
2010-10-12 18:46:30 -07:00
0209a62c7d merge from froyo
Change-Id: Ic80ad93cd3b05d55ba8b4e16edaa59ee58f08f05
2010-10-12 11:39:35 -07:00
adf906d034 custom keymapping support 2010-09-26 15:58:35 -07:00
7f4ff5cd3a favor primary device always for fstab 2010-09-20 19:21:52 -07:00
cb71c2f334 merge from eclair 2010-09-17 14:19:52 -07:00
e17a78ddda allow the parted and fix permissions tools to be optional 2010-09-17 14:17:57 -07:00
4e10b135cc Lie and say it is version 2 for backwards compatibility.
Change-Id: I5b0627fd49d3ee9f562c67bcac48783b00198ab0
2010-09-13 15:14:07 -07:00
56c1b3b576 add support for bml dumping
Change-Id: I9dd7fd8552d4efce533c23541f3612c01e3bd90b
2010-09-13 15:08:49 -07:00
68df48c28f implement redbend flashing
Change-Id: Icad93103ed2321ad8b6aecbbf2e0f2f8fe139c0f
2010-09-13 15:04:54 -07:00
28a41b4df2 initial support for bml backup and restore
Change-Id: I7e6f1a76371c0d1c1f4cfac0413ba6fa7743f17a
2010-09-13 14:55:17 -07:00
5a9fe1c964 missing
Change-Id: I836d8c28c9efce034032fa3bd2e0e15cac1ad23f
2010-09-13 14:33:50 -07:00
b5a36a0e20 add bmlutils
Change-Id: Ia402c4fcd2881cfb7cf7318718933ed64a19ec6b
2010-09-13 14:33:15 -07:00
e5678e9249 initial support for flashing samsung kernels via redbend_ua
Change-Id: I9033146899d596c6d0a4ba8a5fad080d799d96ae
2010-09-13 13:25:11 -07:00
4233c7fb3c Use the primary device.
Change-Id: Id45fcedcb5b3cacd753346a117423a462a00efb2
2010-09-03 22:16:57 -07:00
23ce6b14ae allow the parted and fix permissions tools to be optional 2010-08-29 12:35:10 -07:00
91e9e830e2 new fix permissions 2010-08-26 10:41:08 -07:00
fad25ab75b merge with eclair recovery 2010-08-26 10:40:57 -07:00
56606a2da3 fix missing break statements in switch... 2010-08-23 16:15:33 -07:00
ceddcd59ac add fix permissions and partition sd card to the recovery. TODO: Restrict to devicess that support large recovery images. 2010-08-23 16:13:14 -07:00
54a284568b 2507 2010-08-20 11:35:15 -07:00
3b4135826d remove hijac 2010-08-20 09:56:15 -07:00
38e8b2b6a9 cleanups 2010-08-17 22:21:53 -07:00
f0e31b89b9 fixes and stuff for droid x 2010-08-17 16:55:38 -07:00
d632c0def4 hijack executable 2010-08-16 18:05:55 -07:00
2098707b02 droid x test crap 2010-08-16 01:21:10 -07:00
6c7745d928 rename some menus. wipe more locations for dalvik cache. 2010-08-07 12:17:13 -07:00
4e625e8cd2 missing files 2010-07-24 11:21:12 -07:00
852bb420e7 Do not unmount on wipe unless needed. Add reboot binary. Restart recovery process for Galaxy S. 2010-07-24 11:18:00 -07:00
d4060c3eb6 Galaxy S initial support. Fix firmware flash on inc. May need to fix other phones. 2010-07-22 20:14:44 -07:00
e074d8d543 need to remove that line about the recovery process start.... this will break other phones. or check the process list. 2010-07-20 16:39:23 -07:00
5aaa8238f7 galaxys 2010-07-20 16:23:18 -07:00
ef83d3ea87 fix
Change-Id: I9bc8cfde27b89d9a548c47c5727530c604d36ee0
2010-07-19 08:54:23 -07:00
702294d5b8 merge
Change-Id: I25a675887320afd2984523efbc3b7506231ebe4b
2010-07-19 08:43:05 -07:00
f146bc1b74 removal of the recovery checkpoint needs to happen AFTER sdcard is mounted. 2010-07-19 08:41:40 -07:00
d61e56ab36 version 2.5.0.0 2010-07-19 08:41:40 -07:00
9565424508 add confirmations to anything that would change the system. 2010-07-19 08:41:40 -07:00
17bba907ac Make generic confirmation function. 2010-07-19 08:40:58 -07:00
4ca9b4c3a0 allow toggling of software back menu item. add recovery checkpoint file. mount auto now uses busybox mount. 2010-07-19 08:40:58 -07:00
efa6530dbd up the version 2010-07-19 08:40:58 -07:00
b41b4589c2 2.0.2.0 2010-07-19 08:40:58 -07:00
fdda0d66d3 fail 2010-07-19 08:40:57 -07:00
30e5b7f6dc change where the recovery version is spit 2010-07-19 08:40:57 -07:00
6e5851647a command line nandroid 2010-07-19 08:40:57 -07:00
92077c15d6 removal of the recovery checkpoint needs to happen AFTER sdcard is mounted. 2010-07-15 00:10:08 -07:00
fae335e159 version 2.5.0.0 2010-07-14 21:01:44 -07:00
d63eaef179 add confirmations to anything that would change the system. 2010-07-14 21:01:21 -07:00
ecd32fa2e8 Make generic confirmation function. 2010-07-14 18:38:08 -07:00
1bf4f695d4 allow toggling of software back menu item. add recovery checkpoint file. mount auto now uses busybox mount. 2010-07-14 18:37:33 -07:00
37186b19bc up the version 2010-07-07 19:10:09 -07:00
d8038e15f8 2.0.2.0 2010-07-03 16:38:34 -07:00
82c2ca262e Backup of Froyo apps on external storage. 2010-07-03 13:57:28 -07:00
d3cc60b036 nandroid executable
Change-Id: I697530a804be443260059b24e231d77dfe5bc6c4
2010-07-03 13:56:45 -07:00
062d6b0bb3 Backup of Froyo apps on external storage. 2010-07-03 13:54:32 -07:00
6440ed585f fail 2010-07-01 12:52:34 -07:00
b9c595c654 change where the recovery version is spit 2010-07-01 12:39:15 -07:00
d823d5f327 fix build
Change-Id: I284e852856ffa7f6588b243a628c35e5f6d73fc7
2010-07-01 08:21:02 -07:00
1e8aabad34 alphabetical sort
Change-Id: I4b1bb6787a5cbe1e99e3d8b0cc5bf37d7167398d
2010-07-01 00:23:45 -07:00
789ab6bed2 alphabetical sort
Change-Id: I4b1bb6787a5cbe1e99e3d8b0cc5bf37d7167398d
2010-07-01 00:23:25 -07:00
916f5538f9 fail
Change-Id: I93fc7a7a717e5a7eceafd7175e22c32b2588742a
2010-06-30 23:31:35 -07:00
f2954b5b64 Always use the default recovery ui.
Change-Id: Ia73aeb932498d39e508bf232c0070910aa204ec5
2010-06-30 23:27:26 -07:00
107629b02d forward port excluded bootloader stuff from eclair
Change-Id: I03fb0d4dc982a3718a616c6204e70a3e11cff8f8
2010-06-30 23:17:53 -07:00
0837091e8d command line nandroid 2010-06-26 12:25:02 -07:00
d634bb2d2b 2.0.1.4 2010-06-25 12:23:35 -07:00
0eb14b30e0 Merge from Froyo. 2010-06-23 17:38:05 -07:00
cd44ab973e dont fail if nonmtd format fails... create mtab 2010-06-23 00:02:14 -07:00
49af23cbce dalvik cache wip support 2010-06-21 13:45:51 -07:00
8ec9418782 fix fstab for /sdcard 2010-06-21 12:27:43 -07:00
52d3f205b5 fix sdext backup bug 2010-06-21 12:11:13 -07:00
5cd94a9a73 2.0.0.8 2010-06-21 08:53:41 -07:00
e25908bd87 logging 2010-06-21 08:16:19 -07:00
a6522b3584 add recovery.log reporting 2010-06-20 13:16:06 -07:00
598cfc7e9d Remove any references to mmcblk0. Add recovery.log failure hook into ROM Manager. 2010-06-20 09:42:47 -07:00
c290861d4b fix copy and paste typo that was messing up sd-ext backup 2010-06-19 08:42:59 -07:00
e51e47d814 fix up the default SDCARD devices 2010-06-18 15:57:18 -07:00
63e0476b6e Wipe DATADATA as well when doing factory resets or wiping DATA. 2010-06-15 12:56:17 -07:00
8b5e1856b3 support DATADATA 2010-06-14 22:04:22 -07:00
14239d291a begin to abstract out the file system and mount information. known issue: create_fstab causes segfault on incredible. and thus subsequent crash loop of recovery. 2010-06-14 15:02:48 -07:00
f8b21c2b4d hacking away 2010-06-14 12:49:47 -07:00
59bcc7eb3f support ext3 and ext4 partitions 2010-06-11 15:48:49 -07:00
2654f5aae1 DO NOT MERGE
Removing unused recovey options.
Please refer to Bug#2502219 for more info.

Change-Id: I2fe3cdb0c8b93ed7e1cc4093824fbe181f5f0aea
2010-03-26 16:03:44 -07:00
dcc38b3c15 Add an empty CleanSpec.mk
Change-Id: Icd177bd26120e0c8929faa8d1007f6c5bd446cb8
2010-03-08 18:04:03 -08:00
5b695f393e make StringValue wrapper okay to call on NULL
The docs say "don't do this", but it's trivial to make safe.  Make
StringValue(NULL) return NULL instead of crashing.

Change-Id: I2221bcb4c98d8adb4e25c764d7bdcfa787822bcf
2010-02-24 15:05:07 -08:00
c4351c7910 refactor applypatch and friends
Change the applypatch function to take meaningful arguments instead of
argc and argv.  Move all the parsing of arguments into main.c (for the
standalone binary) and into install.c (for the updater function).
applypatch() takes patches as Value objects, so we can pass in blobs
extracted from the package without ever writing them to temp files.

The patching code is changed to read the patch from memory instead of
a file.

A bunch of compiler warnings (mostly about signed vs unsigned types)
are fixed.

Support for the IMGDIFF1 format is dropped.  (We've been generating
IMGDIFF2 packages for some time now.)

Change-Id: I217563c500012750f27110db821928a06211323f
2010-02-22 15:30:33 -08:00
583fc12c3d add missing includes to fix mac build (maybe)
Change-Id: Id2712940c4929f3a8b3ba5d4e9e03bb8034747ee
2010-02-19 16:07:57 -08:00
512536a54a relocate applypatch; add type system and new functions to edify
- Move applypatch to this package (from build).

- Add a rudimentary type system to edify:  instead of just returning a
  char*, functions now return a Value*, which is a struct that can
  carry different types of value (currently just STRING and BLOB).
  Convert all functions to this new scheme.

- Change the one-argument form of package_extract_file to return a
  Value of the new BLOB type.

- Add read_file() to load a local file and return a blob, and
  sha1_check() to test a blob (or string) against a set of possible
  sha1s.  read_file() uses the file-loading code from applypatch so it
  can read MTD partitions as well.

This is the start of better integration between applypatch and the
rest of edify.

b/2361316 - VZW Issue PP628: Continuous reset to Droid logo:
            framework-res.apk update failed (CR LIBtt59130)

Change-Id: Ibd038074749a4d515de1f115c498c6c589ee91e5
2010-02-18 14:22:12 -08:00
21854ccdb2 Filename check and free allocated strings
Make sure file is valid before we try to read it. Also free all the
strings we allocate in various functions so we don't leak memory.

Change-Id: Ica3c8dae992e73718c79c12ff5d7e315c290caea
2010-02-17 18:33:44 -08:00
2e068dc330 am da846fcf: am 4c382b13: reconcile main tree with open-source eclair
Merge commit 'da846fcf1b6a7bbd2f9f30c965b25f084568ef75'

* commit 'da846fcf1b6a7bbd2f9f30c965b25f084568ef75':
  android-2.1_r1 snapshot
2010-02-05 14:09:04 -08:00
da846fcf1b am 4c382b13: reconcile main tree with open-source eclair
Merge commit '4c382b13657be5d949e7dfc9ef46a66f2eb496e0' into eclair-plus-aosp

* commit '4c382b13657be5d949e7dfc9ef46a66f2eb496e0':
  android-2.1_r1 snapshot
2010-02-05 08:22:49 -08:00
4c382b1365 reconcile main tree with open-source eclair 2010-02-05 08:09:31 -08:00
e08991e02a bump updater API version to 3; deprecate firmware update command
Remove support for the HTC-specific "firmware" update command and the
corresponding edify function write_firmware_update().  This
functionality is now done by an edify extension library that lives in
vendor/htc.

Change-Id: I80858951ff10ed8dfff98aefb796bef009e05efb
2010-02-03 09:20:07 -08:00
93dbe07ff6 Merge "change log recovery to generic device_recovery_start function" 2010-02-02 08:54:10 -08:00
efa1bab94c change log recovery to generic device_recovery_start function
Remove (or at least stop calling) the HTC-specific mechanism for
preserving the recovery log from before a radio or hboot update.
Replace it with a generic device_recovery_start() function which each
device's code can implement to do whatever it wants on recovery
startup.

Change-Id: If3cca4b498c0b1cf0565236404ecf56a1fc46123
2010-02-01 15:59:12 -08:00
6aece33b3f add a one-argument version of package_extract_file
Add a version of package_extract_file that returns the file data as
its return value (to be consumed by some other edify function that
expects to receive a bunch of binary data as an argument).  Lets us
avoid having two copies of a big file in memory (extracting it into
/tmp, which is a ramdisk, and then having something load it into
memory) when doing things like radio updates.

Change-Id: Ie26ece5fbae457eb0ddcd8a13d74d78a769fbc70
2010-02-01 14:40:12 -08:00
b551724ceb reconcile android-2.1_r1 snapshot 2010-01-29 14:07:31 -08:00
aa062531aa fix parsing of dumpkeys output
%i can't be used to read unsigned ints (though it happens to work with
bionic).  Change to %x and %u as appropriate.

Change-Id: I8ea9ca16a939501757cf70fc5953abee26c8231c
http://b/2402231 - Parser for /res/keys interprets n0inv as a signed int
2010-01-28 16:51:00 -08:00
687bc12ccf save the recovery log from before HTC firmware updates
When doing a firmware (radio or hboot) update on HTC devices, save the
recovery log in block 1 of the cache partition, before the firmware
image and the UI bitmaps.  When we boot back into recovery after the
firmware update to reformat the cache partition, copy that log out of
cache before reformatting it and dump it into the current invocation's
log.

The practical upshot of all this is that we can see the log output
from radio and hboot updates.

Change-Id: Ie0e89566754c88f4bed6a90d8a0aa04047b01a27
2010-01-21 12:50:04 -08:00
883b4c8be5 am 4e9332cb: am 22d79a5c: make offsets in firmware update header not point to bad blocks
Merge commit '4e9332cb0bb84df4c08bbb469e59a54eab2a9df0'

* commit '4e9332cb0bb84df4c08bbb469e59a54eab2a9df0':
  make offsets in firmware update header not point to bad blocks
2010-01-13 12:17:11 -08:00
4e9332cb0b am 22d79a5c: make offsets in firmware update header not point to bad blocks
Merge commit '22d79a5c5eab9c1e86ff2af210bb072689e2d630' into eclair-plus-aosp

* commit '22d79a5c5eab9c1e86ff2af210bb072689e2d630':
  make offsets in firmware update header not point to bad blocks
2010-01-13 11:24:42 -08:00
4c5f9f3416 make offsets in firmware update header not point to bad blocks
hboot will apparently fail to install if the first block of the image
(the one pointed to by the offset in the block 0 header) is a bad
block.  (Hopefully it handles subsequent bad blocks.)

This change makes the MTD write code keep track of the bad blocks it
has skipped over, so that the offset in the header can be adjusted to
be the address of the first successfully written block.

Change-Id: I45d58e32a36d0c1dbc0a7f871bd5985b6c8ff524
http://b/2358012 - passion: failure to flash hboot (bad blocks?)
2010-01-13 09:21:25 -08:00
b765729081 android-2.1_r1 snapshot 2010-01-12 15:18:06 -08:00
be47155f75 am 158657bc: merge from open-source master
Merge commit '158657bc5ce438d3cf1f601255896b854fd49103'

* commit '158657bc5ce438d3cf1f601255896b854fd49103':
  Security: Fix typo in recovery EOCD detection.
2009-12-21 15:47:17 -08:00
158657bc5c merge from open-source master 2009-12-21 15:31:49 -08:00
9b514530a6 am d36308c2: am 17a47098: use MEMGETBADBLOCK to look for bad blocks when reading MTD partitions
Merge commit 'd36308c26d3f2947f4ff49f2ecc22cbb659fdf37'

* commit 'd36308c26d3f2947f4ff49f2ecc22cbb659fdf37':
  use MEMGETBADBLOCK to look for bad blocks when reading MTD partitions
2009-12-15 15:04:32 -08:00
d36308c26d am 17a47098: use MEMGETBADBLOCK to look for bad blocks when reading MTD partitions
Merge commit '17a47098d2a4214397f8b30e2692c9487d7ab5ff' into eclair-plus-aosp

* commit '17a47098d2a4214397f8b30e2692c9487d7ab5ff':
  use MEMGETBADBLOCK to look for bad blocks when reading MTD partitions
2009-12-15 07:42:36 -08:00
8fae8279fa Merge commit 'goog/eclair-plus-aosp' 2009-12-15 00:44:04 -08:00
25215285c4 am 9b430e11: am 73ae31ce: add a simple unit test for the OTA package verifier
Merge commit '9b430e11d6c4fb907d0aa96667142e2c00585e09'

* commit '9b430e11d6c4fb907d0aa96667142e2c00585e09':
  add a simple unit test for the OTA package verifier
2009-12-10 15:52:09 -08:00
9b430e11d6 am 73ae31ce: add a simple unit test for the OTA package verifier
Merge commit '73ae31ce0ac09c0e45924d817644261c87ab1a60' into eclair-mr2-plus-aosp

* commit '73ae31ce0ac09c0e45924d817644261c87ab1a60':
  add a simple unit test for the OTA package verifier
2009-12-10 15:35:23 -08:00
bd6181ad58 Merge change I117fdea9
* changes:
  Recovery changes for Encrypted File Systems. This change enables/disables the Encrypted file systems feature. It reads some properties form the data partition, wipes the partition out, and then rewrites the proper properties again into the data partition to signal that encrypted FS are enabled.
2009-12-10 14:51:10 -08:00
0523156775 Recovery changes for Encrypted File Systems.
This change enables/disables the Encrypted file systems feature. It reads some properties form the data partition, wipes the partition out, and then rewrites the proper properties again into the data partition to signal that encrypted FS are enabled.
2009-12-10 14:49:04 -08:00
002c9dfb80 am 2278a04a: am 9acf28a3: am c652e41d: fix cut-and-paste error in verifier
Merge commit '2278a04a0921007d726e9e1ec4b668860f961f88'

* commit '2278a04a0921007d726e9e1ec4b668860f961f88':
  fix cut-and-paste error in verifier
2009-12-10 14:37:57 -08:00
73ae31ce0a add a simple unit test for the OTA package verifier 2009-12-09 17:01:45 -08:00
2278a04a09 am 9acf28a3: am c652e41d: fix cut-and-paste error in verifier
Merge commit '9acf28a390aab3e0f394c701bc3cda6cbc9393b3' into eclair-mr2-plus-aosp

* commit '9acf28a390aab3e0f394c701bc3cda6cbc9393b3':
  fix cut-and-paste error in verifier
2009-12-09 15:54:14 -08:00
3b0f484776 Security: Fix typo in recovery EOCD detection.
This issue results in the ability to modify the contents of a signed
OTA recovery image.
2009-12-09 01:31:06 -05:00
9acf28a390 am c652e41d: fix cut-and-paste error in verifier
Merge commit 'c652e41d9173e299a8e1805ae1b2bba75a34ae12' into eclair-mr2

* commit 'c652e41d9173e299a8e1805ae1b2bba75a34ae12':
  fix cut-and-paste error in verifier
2009-12-08 15:54:50 -08:00
6149073651 am c652e41d: fix cut-and-paste error in verifier
Merge commit 'c652e41d9173e299a8e1805ae1b2bba75a34ae12' into eclair-plus-aosp

* commit 'c652e41d9173e299a8e1805ae1b2bba75a34ae12':
  fix cut-and-paste error in verifier
2009-12-08 15:54:32 -08:00
b8f506fb37 am 6e5be9b2: merge from open-source master
Merge commit '6e5be9b24c232be4cfc22b7cbabb0fdf6d869f7c'

* commit '6e5be9b24c232be4cfc22b7cbabb0fdf6d869f7c':
  eclair snapshot
2009-11-15 15:00:59 -08:00
6e5be9b24c merge from open-source master 2009-11-15 14:55:00 -08:00
052acd61c8 merge from open-source master
Merge commit 'goog/stage-korg-master' into HEAD
2009-11-15 14:05:55 -08:00
b2ce982d43 merge from eclair 2009-11-15 12:05:33 -08:00
a43c44f31f eclair snapshot 2009-11-12 18:45:15 -08:00
f88cea6ded am 4011770f: merge from open-source master
Merge commit '4011770f2d06fcb743abc91a01a531c7ae2d9175'

* commit '4011770f2d06fcb743abc91a01a531c7ae2d9175':
2009-10-14 16:11:42 -07:00
4011770f2d merge from open-source master 2009-10-14 16:02:08 -07:00
31f0fc2235 am d93a2545: simplify construction of the recovery progress bar
Merge commit 'd93a25459cdefba940f254b4c5f54fd7d9cdaf11'

* commit 'd93a25459cdefba940f254b4c5f54fd7d9cdaf11':
  simplify construction of the recovery progress bar
2009-10-13 11:59:45 -07:00
2ec8a1929f am 54ec81fe: Merge change I1c8ca2e4 into eclair
Merge commit '54ec81fe86225d5f3ee3ab16e3266ae88cd639fa'

* commit '54ec81fe86225d5f3ee3ab16e3266ae88cd639fa':
  replace generic recovery icons
2009-10-12 12:40:12 -07:00
022229c47e am 988500b6: add terminator to recovery\'s getopt_long options array
Merge commit '988500b615de24455e1fee69e72055bb1dca9c86'

* commit '988500b615de24455e1fee69e72055bb1dca9c86':
  add terminator to recovery's getopt_long options array
2009-10-12 11:45:31 -07:00
d93a25459c simplify construction of the recovery progress bar
Instead of six separate images for the left end, right end, and tiled
center portion of the full and empty progress bars, just use two
images:  a full bar and an empty bar.  Draw the left side of the full
bar and the right side of the empty one, moving the boundary rightward
to "fill" the bar.  This makes recovery trivially smaller, and allows
fancier images to be used as progress bars.

Support paletted PNG images as resources.
2009-10-08 16:32:58 -07:00
d641a0e141 am 54ec81fe: Merge change I1c8ca2e4 into eclair
Merge commit '54ec81fe86225d5f3ee3ab16e3266ae88cd639fa' into eclair-plus-aosp

* commit '54ec81fe86225d5f3ee3ab16e3266ae88cd639fa':
  replace generic recovery icons
2009-10-07 17:17:41 -07:00
42ab176195 am 988500b6: add terminator to recovery\'s getopt_long options array
Merge commit '988500b615de24455e1fee69e72055bb1dca9c86' into eclair-plus-aosp

* commit '988500b615de24455e1fee69e72055bb1dca9c86':
  add terminator to recovery's getopt_long options array
2009-10-07 11:57:04 -07:00
4526d4fe62 am 8f8bc4cb: am f93d8166: confirm before wiping user data in recovery
Merge commit '8f8bc4cb487e0f853c97cb5ff1481d707ac6b66d'

* commit '8f8bc4cb487e0f853c97cb5ff1481d707ac6b66d':
  confirm before wiping user data in recovery
2009-09-25 08:45:47 -07:00
8f8bc4cb48 am f93d8166: confirm before wiping user data in recovery
Merge commit 'f93d8166ef4c06f6ad71293ffa8a4ce469df4fa5' into eclair-plus-aosp

* commit 'f93d8166ef4c06f6ad71293ffa8a4ce469df4fa5':
  confirm before wiping user data in recovery
2009-09-22 18:28:19 -07:00
83a25d7380 am 486aa290: am fd8fb0c4: reduce fraction of progress bar for verification
Merge commit '486aa290635dbf6f60b3435694951fed3470ffdf'

* commit '486aa290635dbf6f60b3435694951fed3470ffdf':
  reduce fraction of progress bar for verification
2009-09-20 14:40:27 -07:00
486aa29063 am fd8fb0c4: reduce fraction of progress bar for verification
Merge commit 'fd8fb0c49242af5147708f1a93ea3acba546555b' into eclair-plus-aosp

* commit 'fd8fb0c49242af5147708f1a93ea3acba546555b':
  reduce fraction of progress bar for verification
2009-09-20 14:30:37 -07:00
66c76bcfcf am d16fb221: am 60babf8b: delete files before symlinking; log error messages
Merge commit 'd16fb221cd27abea8a954bd5f6554fa116366519'

* commit 'd16fb221cd27abea8a954bd5f6554fa116366519':
  delete files before symlinking; log error messages
2009-09-18 18:15:46 -07:00
d16fb221cd am 60babf8b: delete files before symlinking; log error messages
Merge commit '60babf8ba766662cc0932e8271b67daa69cddd5f' into eclair-plus-aosp

* commit '60babf8ba766662cc0932e8271b67daa69cddd5f':
  delete files before symlinking; log error messages
2009-09-18 15:49:37 -07:00
b9ad6dfd81 am 2f4fc561: am 196c25c7: don\'t complain if recovery icon is short
Merge commit '2f4fc56183f3fe2edb5e3cd5e12329871e02518f'

* commit '2f4fc56183f3fe2edb5e3cd5e12329871e02518f':
  don't complain if recovery icon is short
2009-09-17 06:38:28 -07:00
2f4fc56183 am 196c25c7: don\'t complain if recovery icon is short
Merge commit '196c25c777daedbe2fe5a45171fb42e43ceed9af' into eclair-plus-aosp

* commit '196c25c777daedbe2fe5a45171fb42e43ceed9af':
  don't complain if recovery icon is short
2009-09-15 13:46:00 -07:00
49c82ce553 am b5d542cd: am a3f89eab: add a run_program() function to edify
Merge commit 'b5d542cd40360867bc00cdb9266c0abf26448c55'

* commit 'b5d542cd40360867bc00cdb9266c0abf26448c55':
  add a run_program() function to edify
2009-09-10 14:41:05 -07:00
b5d542cd40 am a3f89eab: add a run_program() function to edify
Merge commit 'a3f89eabb7ddcf44add8ce3b321ceab6d35289cb' into eclair-plus-aosp

* commit 'a3f89eabb7ddcf44add8ce3b321ceab6d35289cb':
  add a run_program() function to edify
2009-09-10 14:30:43 -07:00
af42fa0a7d am 9a77b613: merge from open-source master
Merge commit '9a77b613f3fdf6340b0f5f24d1c725fd0001e4a9'

* commit '9a77b613f3fdf6340b0f5f24d1c725fd0001e4a9':
2009-09-04 11:05:32 -07:00
9a77b613f3 merge from open-source master 2009-09-04 07:50:41 -07:00
65a56909a3 merge from donut 2009-09-03 14:12:45 -07:00
2f39bf9a9f am cbf90380: merge from open-source master
Merge commit 'cbf903803850deb4f70490d97a1603e7b8679cc2'

* commit 'cbf903803850deb4f70490d97a1603e7b8679cc2':
  Not all failures to fopen_root_path() are serious.
2009-09-01 12:14:02 -07:00
cbf9038038 merge from open-source master 2009-09-01 08:27:43 -07:00
619ec2f3aa Not all failures to fopen_root_path() are serious.
Example: E:Can't open /cache/recovery/command.
2009-08-25 12:53:40 -07:00
6785c2534a am 34c98df7: (-s ours) do not merge: cherry-pick of c2d666bd4f from master
Merge commit '34c98df78a80881698f63ce0815f3e16823d85e0'

* commit '34c98df78a80881698f63ce0815f3e16823d85e0':
  do not merge: cherry-pick of c2d666bd4f from master
2009-08-18 12:13:35 -07:00
c2d666bd4f Recovery: When updating from SD card, update can't resume automatically
after a power loss

Submitted on behalf of Hong-Bin Wang <hong-binwang@motorola.com>
Signed-off-by: Jared Suttles <jared.suttles@motorola.com>
2009-08-17 17:39:54 -07:00
50a8a71f0b am 54e2e86c: (-s ours) do not merge: cherry-picked 60151a295c from master branch
Merge commit '54e2e86c5740a2c7a02e95e94c4aff362a5502cf'

* commit '54e2e86c5740a2c7a02e95e94c4aff362a5502cf':
  do not merge: cherry-picked 60151a295c from master branch
2009-08-17 14:11:31 -07:00
60151a295c verify whole-file signature instead of jarsigner signatures
In recovery, verify a signature that covers the entire zip file,
instead of using the jarsigner format to verify individual files.

Bug: 1328985
2009-08-14 17:26:33 -07:00
103 changed files with 8437 additions and 802 deletions

View File

@ -8,32 +8,55 @@ commands_recovery_local_path := $(LOCAL_PATH)
# LOCAL_CPP_EXTENSION := .c
LOCAL_SRC_FILES := \
mounts.c \
extendedcommands.c \
nandroid.c \
legacy.c \
commands.c \
recovery.c \
bootloader.c \
firmware.c \
install.c \
roots.c \
ui.c \
verifier.c
LOCAL_SRC_FILES += \
reboot.c \
setprop.c
ifndef BOARD_HAS_NO_MISC_PARTITION
LOCAL_SRC_FILES += \
firmware.c \
bootloader.c
else
LOCAL_CFLAGS += -DBOARD_HAS_NO_MISC_PARTITION
endif
ifdef BOARD_RECOVERY_IGNORE_BOOTABLES
LOCAL_CFLAGS += -DBOARD_RECOVERY_IGNORE_BOOTABLES
endif
ifdef BOARD_HIJACK_RECOVERY_PATH
LOCAL_CFLAGS += -DBOARD_HIJACK_RECOVERY_PATH=\"$(BOARD_HIJACK_RECOVERY_PATH)\"
endif
LOCAL_SRC_FILES += test_roots.c
LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
RECOVERY_VERSION := ClockworkMod Recovery v1.8.1.8
LOCAL_CFLAGS := -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
RECOVERY_VERSION := ClockworkMod Recovery v2.5.1.3
LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
RECOVERY_API_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
ifeq ($(BOARD_HAS_NO_SELECT_BUTTON),true)
LOCAL_CFLAGS += -DKEY_POWER_IS_SELECT_ITEM
endif
BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_SDCARD_DEVICE_PRIMARY BOARD_SDCARD_DEVICE_SECONDARY BOARD_SDEXT_DEVICE BOARD_SDEXT_FILESYSTEM BOARD_DATA_DEVICE BOARD_DATA_FILESYSTEM BOARD_DATADATA_DEVICE BOARD_DATADATA_FILESYSTEM BOARD_CACHE_DEVICE BOARD_CACHE_FILESYSTEM BOARD_SYSTEM_DEVICE BOARD_SYSTEM_FILESYSTEM BOARD_HAS_DATADATA BOARD_DATA_FILESYSTEM_OPTIONS BOARD_DATADATA_FILESYSTEM_OPTIONS BOARD_CACHE_FILESYSTEM_OPTIONS BOARD_SYSTEM_FILESYSTEM_OPTIONS BOARD_HAS_MTD_CACHE BOARD_USES_BMLUTILS BOARD_USES_MMCUTILS BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY
$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
$(if $($(board_define)), \
$(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
) \
)
# This binary is in the recovery ramdisk, which is otherwise a copy of root.
# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses
@ -43,20 +66,24 @@ endif
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES :=
ifeq ($(TARGET_RECOVERY_UI_LIB),)
ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
LOCAL_SRC_FILES += default_recovery_ui.c
else
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
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 += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
LOCAL_STATIC_LIBRARIES += libamend
LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt
LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
LOCAL_STATIC_LIBRARIES += libstdc++ libc
include $(BUILD_EXECUTABLE)
RECOVERY_LINKS := amend busybox flash_image dump_image mkyaffs2image unyaffs erase_image
RECOVERY_LINKS := amend busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
# nc is provided by external/netcat
SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
$(SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
@ -88,14 +115,6 @@ LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := nandroid-md5.sh
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := mkfstab.sh
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := mkfstab.sh
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := killrecovery.sh
LOCAL_MODULE_TAGS := eng
@ -104,15 +123,35 @@ LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := killrecovery.sh
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := verifier_test.c verifier.c
LOCAL_MODULE := verifier_test
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
include $(commands_recovery_local_path)/amend/Android.mk
include $(commands_recovery_local_path)/bmlutils/Android.mk
include $(commands_recovery_local_path)/flashutils/Android.mk
include $(commands_recovery_local_path)/minui/Android.mk
include $(commands_recovery_local_path)/minzip/Android.mk
include $(commands_recovery_local_path)/mtdutils/Android.mk
include $(commands_recovery_local_path)/mmcutils/Android.mk
include $(commands_recovery_local_path)/tools/Android.mk
include $(commands_recovery_local_path)/edify/Android.mk
include $(commands_recovery_local_path)/updater/Android.mk
include $(commands_recovery_local_path)/applypatch/Android.mk
include $(commands_recovery_local_path)/utilities/Android.mk
commands_recovery_local_path :=
endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR
endif # !TARGET_SIMULATOR

49
CleanSpec.mk Normal file
View 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
# ************************************************

61
applypatch/Android.mk Normal file
View File

@ -0,0 +1,61 @@
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
ifneq ($(TARGET_SIMULATOR),true)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c
LOCAL_MODULE := libapplypatch
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch_static
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c
LOCAL_MODULE := imgdiff
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += external/zlib external/bzip2
LOCAL_STATIC_LIBRARIES += libz libbz
include $(BUILD_HOST_EXECUTABLE)
endif # !TARGET_SIMULATOR

826
applypatch/applypatch.c Normal file
View File

@ -0,0 +1,826 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "mincrypt/sha.h"
#include "applypatch.h"
#include "mtdutils/mtdutils.h"
#include "edify/expr.h"
int SaveFileContents(const char* filename, FileContents file);
int LoadMTDContents(const char* filename, FileContents* file);
int ParseSha1(const char* str, uint8_t* digest);
ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
static int mtd_partitions_scanned = 0;
// Read a file into memory; store it and its associated metadata in
// *file. Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file) {
file->data = NULL;
// A special 'filename' beginning with "MTD:" means to load the
// contents of an MTD partition.
if (strncmp(filename, "MTD:", 4) == 0) {
return LoadMTDContents(filename, file);
}
if (stat(filename, &file->st) != 0) {
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
return -1;
}
file->size = file->st.st_size;
file->data = malloc(file->size);
FILE* f = fopen(filename, "rb");
if (f == NULL) {
printf("failed to open \"%s\": %s\n", filename, strerror(errno));
free(file->data);
file->data = NULL;
return -1;
}
ssize_t bytes_read = fread(file->data, 1, file->size, f);
if (bytes_read != file->size) {
printf("short read of \"%s\" (%ld bytes of %ld)\n",
filename, (long)bytes_read, (long)file->size);
free(file->data);
file->data = NULL;
return -1;
}
fclose(f);
SHA(file->data, file->size, file->sha1);
return 0;
}
static size_t* size_array;
// comparison function for qsort()ing an int array of indexes into
// size_array[].
static int compare_size_indices(const void* a, const void* b) {
int aa = *(int*)a;
int bb = *(int*)b;
if (size_array[aa] < size_array[bb]) {
return -1;
} else if (size_array[aa] > size_array[bb]) {
return 1;
} else {
return 0;
}
}
void FreeFileContents(FileContents* file) {
if (file) free(file->data);
free(file);
}
// Load the contents of an MTD partition into the provided
// FileContents. filename should be a string of the form
// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:...".
// The smallest size_n bytes for which that prefix of the mtd contents
// has the corresponding sha1 hash will be loaded. It is acceptable
// for a size value to be repeated with different sha1s. Will return
// 0 on success.
//
// This complexity is needed because if an OTA installation is
// interrupted, the partition might contain either the source or the
// target data, which might be of different lengths. We need to know
// the length in order to read from MTD (there is no "end-of-file"
// marker), so the caller must specify the possible lengths and the
// hash of the data, and we'll do the load expecting to find one of
// those hashes.
int LoadMTDContents(const char* filename, FileContents* file) {
#ifdef BOARD_USES_MTDUTILS
char* copy = strdup(filename);
const char* magic = strtok(copy, ":");
if (strcmp(magic, "MTD") != 0) {
printf("LoadMTDContents called with bad filename (%s)\n",
filename);
return -1;
}
const char* partition = strtok(NULL, ":");
int i;
int colons = 0;
for (i = 0; filename[i] != '\0'; ++i) {
if (filename[i] == ':') {
++colons;
}
}
if (colons < 3 || colons%2 == 0) {
printf("LoadMTDContents called with bad filename (%s)\n",
filename);
}
int pairs = (colons-1)/2; // # of (size,sha1) pairs in filename
int* index = malloc(pairs * sizeof(int));
size_t* size = malloc(pairs * sizeof(size_t));
char** sha1sum = malloc(pairs * sizeof(char*));
for (i = 0; i < pairs; ++i) {
const char* size_str = strtok(NULL, ":");
size[i] = strtol(size_str, NULL, 10);
if (size[i] == 0) {
printf("LoadMTDContents called with bad size (%s)\n", filename);
return -1;
}
sha1sum[i] = strtok(NULL, ":");
index[i] = i;
}
// sort the index[] array so it indexes the pairs in order of
// increasing size.
size_array = size;
qsort(index, pairs, sizeof(int), compare_size_indices);
if (!mtd_partitions_scanned) {
mtd_scan_partitions();
mtd_partitions_scanned = 1;
}
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
if (mtd == NULL) {
printf("mtd partition \"%s\" not found (loading %s)\n",
partition, filename);
return -1;
}
MtdReadContext* ctx = mtd_read_partition(mtd);
if (ctx == NULL) {
printf("failed to initialize read of mtd partition \"%s\"\n",
partition);
return -1;
}
SHA_CTX sha_ctx;
SHA_init(&sha_ctx);
uint8_t parsed_sha[SHA_DIGEST_SIZE];
// allocate enough memory to hold the largest size.
file->data = malloc(size[index[pairs-1]]);
char* p = (char*)file->data;
file->size = 0; // # bytes read so far
for (i = 0; i < pairs; ++i) {
// Read enough additional bytes to get us up to the next size
// (again, we're trying the possibilities in order of increasing
// size).
size_t next = size[index[i]] - file->size;
size_t read = 0;
if (next > 0) {
read = mtd_read_data(ctx, p, next);
if (next != read) {
printf("short read (%d bytes of %d) for partition \"%s\"\n",
read, next, partition);
free(file->data);
file->data = NULL;
return -1;
}
SHA_update(&sha_ctx, p, read);
file->size += read;
}
// Duplicate the SHA context and finalize the duplicate so we can
// check it against this pair's expected hash.
SHA_CTX temp_ctx;
memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
const uint8_t* sha_so_far = SHA_final(&temp_ctx);
if (ParseSha1(sha1sum[index[i]], parsed_sha) != 0) {
printf("failed to parse sha1 %s in %s\n",
sha1sum[index[i]], filename);
free(file->data);
file->data = NULL;
return -1;
}
if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
// we have a match. stop reading the partition; we'll return
// the data we've read so far.
printf("mtd read matched size %d sha %s\n",
size[index[i]], sha1sum[index[i]]);
break;
}
p += read;
}
mtd_read_close(ctx);
if (i == pairs) {
// Ran off the end of the list of (size,sha1) pairs without
// finding a match.
printf("contents of MTD partition \"%s\" didn't match %s\n",
partition, filename);
free(file->data);
file->data = NULL;
return -1;
}
const uint8_t* sha_final = SHA_final(&sha_ctx);
for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
file->sha1[i] = sha_final[i];
}
// Fake some stat() info.
file->st.st_mode = 0644;
file->st.st_uid = 0;
file->st.st_gid = 0;
free(copy);
free(index);
free(size);
free(sha1sum);
return 0;
#else
printf("mtd utils not supported.\n");
return -1;
#endif
}
// Save the contents of the given FileContents object under the given
// filename. Return 0 on success.
int SaveFileContents(const char* filename, FileContents file) {
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) {
printf("failed to open \"%s\" for write: %s\n",
filename, strerror(errno));
return -1;
}
ssize_t bytes_written = FileSink(file.data, file.size, &fd);
if (bytes_written != file.size) {
printf("short write of \"%s\" (%ld bytes of %ld) (%s)\n",
filename, (long)bytes_written, (long)file.size,
strerror(errno));
close(fd);
return -1;
}
fsync(fd);
close(fd);
if (chmod(filename, file.st.st_mode) != 0) {
printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
return -1;
}
if (chown(filename, file.st.st_uid, file.st.st_gid) != 0) {
printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
return -1;
}
return 0;
}
// Write a memory buffer to target_mtd partition, a string of the form
// "MTD:<partition>[:...]". Return 0 on success.
int WriteToMTDPartition(unsigned char* data, size_t len,
const char* target_mtd) {
#ifdef BOARD_USES_MTDUTILS
char* partition = strchr(target_mtd, ':');
if (partition == NULL) {
printf("bad MTD target name \"%s\"\n", target_mtd);
return -1;
}
++partition;
// Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
// We want just the partition name "boot".
partition = strdup(partition);
char* end = strchr(partition, ':');
if (end != NULL)
*end = '\0';
if (!mtd_partitions_scanned) {
mtd_scan_partitions();
mtd_partitions_scanned = 1;
}
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
if (mtd == NULL) {
printf("mtd partition \"%s\" not found for writing\n", partition);
return -1;
}
MtdWriteContext* ctx = mtd_write_partition(mtd);
if (ctx == NULL) {
printf("failed to init mtd partition \"%s\" for writing\n",
partition);
return -1;
}
size_t written = mtd_write_data(ctx, (char*)data, len);
if (written != len) {
printf("only wrote %d of %d bytes to MTD %s\n",
written, len, partition);
mtd_write_close(ctx);
return -1;
}
if (mtd_erase_blocks(ctx, -1) < 0) {
printf("error finishing mtd write of %s\n", partition);
mtd_write_close(ctx);
return -1;
}
if (mtd_write_close(ctx)) {
printf("error closing mtd write of %s\n", partition);
return -1;
}
free(partition);
return 0;
#else
printf("mtd utils not supported.\n");
return -1;
#endif
}
// Take a string 'str' of 40 hex digits and parse it into the 20
// byte array 'digest'. 'str' may contain only the digest or be of
// the form "<digest>:<anything>". Return 0 on success, -1 on any
// error.
int ParseSha1(const char* str, uint8_t* digest) {
int i;
const char* ps = str;
uint8_t* pd = digest;
for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
int digit;
if (*ps >= '0' && *ps <= '9') {
digit = *ps - '0';
} else if (*ps >= 'a' && *ps <= 'f') {
digit = *ps - 'a' + 10;
} else if (*ps >= 'A' && *ps <= 'F') {
digit = *ps - 'A' + 10;
} else {
return -1;
}
if (i % 2 == 0) {
*pd = digit << 4;
} else {
*pd |= digit;
++pd;
}
}
if (*ps != '\0') return -1;
return 0;
}
// Search an array of sha1 strings for one matching the given sha1.
// Return the index of the match on success, or -1 if no match is
// found.
int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
int num_patches) {
int i;
uint8_t patch_sha1[SHA_DIGEST_SIZE];
for (i = 0; i < num_patches; ++i) {
if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) {
return i;
}
}
return -1;
}
// Returns 0 if the contents of the file (argv[2]) or the cached file
// match any of the sha1's on the command line (argv[3:]). Returns
// nonzero otherwise.
int applypatch_check(const char* filename,
int num_patches, char** const patch_sha1_str) {
FileContents file;
file.data = NULL;
// It's okay to specify no sha1s; the check will pass if the
// LoadFileContents is successful. (Useful for reading MTD
// partitions, where the filename encodes the sha1s; no need to
// check them twice.)
if (LoadFileContents(filename, &file) != 0 ||
(num_patches > 0 &&
FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
printf("file \"%s\" doesn't have any of expected "
"sha1 sums; checking cache\n", filename);
free(file.data);
// If the source file is missing or corrupted, it might be because
// we were killed in the middle of patching it. A copy of it
// should have been made in CACHE_TEMP_SOURCE. If that file
// exists and matches the sha1 we're looking for, the check still
// passes.
if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
printf("failed to load cache file\n");
return 1;
}
if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
printf("cache bits don't match any sha1 for \"%s\"\n", filename);
free(file.data);
return 1;
}
}
free(file.data);
return 0;
}
int ShowLicenses() {
ShowBSDiffLicense();
return 0;
}
ssize_t FileSink(unsigned char* data, ssize_t len, void* token) {
int fd = *(int *)token;
ssize_t done = 0;
ssize_t wrote;
while (done < (ssize_t) len) {
wrote = write(fd, data+done, len-done);
if (wrote <= 0) {
printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
return done;
}
done += wrote;
}
return done;
}
typedef struct {
unsigned char* buffer;
ssize_t size;
ssize_t pos;
} MemorySinkInfo;
ssize_t MemorySink(unsigned char* data, ssize_t len, void* token) {
MemorySinkInfo* msi = (MemorySinkInfo*)token;
if (msi->size - msi->pos < len) {
return -1;
}
memcpy(msi->buffer + msi->pos, data, len);
msi->pos += len;
return len;
}
// Return the amount of free space (in bytes) on the filesystem
// containing filename. filename must exist. Return -1 on error.
size_t FreeSpaceForFile(const char* filename) {
struct statfs sf;
if (statfs(filename, &sf) != 0) {
printf("failed to statfs %s: %s\n", filename, strerror(errno));
return -1;
}
return sf.f_bsize * sf.f_bfree;
}
int CacheSizeCheck(size_t bytes) {
if (MakeFreeSpaceOnCache(bytes) < 0) {
printf("unable to make %ld bytes available on /cache\n", (long)bytes);
return 1;
} else {
return 0;
}
}
// This function applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
// - if the sha1 hash of <target_filename> is <target_sha1_string>,
// does nothing and exits successfully.
//
// - otherwise, if the sha1 hash of <source_filename> is one of the
// entries in <patch_sha1_str>, the corresponding patch from
// <patch_data> (which must be a VAL_BLOB) is applied to produce a
// new file (the type of patch is automatically detected from the
// blob daat). If that new file has sha1 hash <target_sha1_str>,
// moves it to replace <target_filename>, and exits successfully.
// Note that if <source_filename> and <target_filename> are not the
// same, <source_filename> is NOT deleted on success.
// <target_filename> may be the string "-" to mean "the same as
// source_filename".
//
// - otherwise, or if any error is encountered, exits with non-zero
// status.
//
// <source_filename> may refer to an MTD partition to read the source
// data. See the comments for the LoadMTDContents() function above
// for the format of such a filename.
int applypatch(const char* source_filename,
const char* target_filename,
const char* target_sha1_str,
size_t target_size,
int num_patches,
char** const patch_sha1_str,
Value** patch_data) {
printf("\napplying patch to %s\n", source_filename);
if (target_filename[0] == '-' &&
target_filename[1] == '\0') {
target_filename = source_filename;
}
uint8_t target_sha1[SHA_DIGEST_SIZE];
if (ParseSha1(target_sha1_str, target_sha1) != 0) {
printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
return 1;
}
FileContents copy_file;
FileContents source_file;
const Value* source_patch_value = NULL;
const Value* copy_patch_value = NULL;
int made_copy = 0;
// We try to load the target file into the source_file object.
if (LoadFileContents(target_filename, &source_file) == 0) {
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
// The early-exit case: the patch was already applied, this file
// has the desired hash, nothing for us to do.
printf("\"%s\" is already target; no patch needed\n",
target_filename);
return 0;
}
}
if (source_file.data == NULL ||
(target_filename != source_filename &&
strcmp(target_filename, source_filename) != 0)) {
// Need to load the source file: either we failed to load the
// target file, or we did but it's different from the source file.
free(source_file.data);
LoadFileContents(source_filename, &source_file);
}
if (source_file.data != NULL) {
int to_use = FindMatchingPatch(source_file.sha1,
patch_sha1_str, num_patches);
if (to_use >= 0) {
source_patch_value = patch_data[to_use];
}
}
if (source_patch_value == NULL) {
free(source_file.data);
printf("source file is bad; trying copy\n");
if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
// fail.
printf("failed to read copy file\n");
return 1;
}
int to_use = FindMatchingPatch(copy_file.sha1,
patch_sha1_str, num_patches);
if (to_use > 0) {
copy_patch_value = patch_data[to_use];
}
if (copy_patch_value == NULL) {
// fail.
printf("copy file doesn't match source SHA-1s either\n");
return 1;
}
}
int retry = 1;
SHA_CTX ctx;
int output;
MemorySinkInfo msi;
FileContents* source_to_use;
char* outname;
// assume that target_filename (eg "/system/app/Foo.apk") is located
// on the same filesystem as its top-level directory ("/system").
// We need something that exists for calling statfs().
char target_fs[strlen(target_filename)+1];
char* slash = strchr(target_filename+1, '/');
if (slash != NULL) {
int count = slash - target_filename;
strncpy(target_fs, target_filename, count);
target_fs[count] = '\0';
} else {
strcpy(target_fs, target_filename);
}
do {
// Is there enough room in the target filesystem to hold the patched
// file?
if (strncmp(target_filename, "MTD:", 4) == 0) {
// If the target is an MTD partition, we're actually going to
// write the output to /tmp and then copy it to the partition.
// statfs() always returns 0 blocks free for /tmp, so instead
// we'll just assume that /tmp has enough space to hold the file.
// We still write the original source to cache, in case the MTD
// write is interrupted.
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
printf("not enough free space on /cache\n");
return 1;
}
if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
printf("failed to back up source file\n");
return 1;
}
made_copy = 1;
retry = 0;
} else {
int enough_space = 0;
if (retry > 0) {
size_t free_space = FreeSpaceForFile(target_fs);
int enough_space =
(free_space > (target_size * 3 / 2)); // 50% margin of error
printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n",
(long)target_size, (long)free_space, retry, enough_space);
}
if (!enough_space) {
retry = 0;
}
if (!enough_space && source_patch_value != NULL) {
// Using the original source, but not enough free space. First
// copy the source file to cache, then delete it from the original
// location.
if (strncmp(source_filename, "MTD:", 4) == 0) {
// It's impossible to free space on the target filesystem by
// deleting the source if the source is an MTD partition. If
// we're ever in a state where we need to do this, fail.
printf("not enough free space for target but source is MTD\n");
return 1;
}
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
printf("not enough free space on /cache\n");
return 1;
}
if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
printf("failed to back up source file\n");
return 1;
}
made_copy = 1;
unlink(source_filename);
size_t free_space = FreeSpaceForFile(target_fs);
printf("(now %ld bytes free for target)\n", (long)free_space);
}
}
const Value* patch;
if (source_patch_value != NULL) {
source_to_use = &source_file;
patch = source_patch_value;
} else {
source_to_use = &copy_file;
patch = copy_patch_value;
}
if (patch->type != VAL_BLOB) {
printf("patch is not a blob\n");
return 1;
}
SinkFn sink = NULL;
void* token = NULL;
output = -1;
outname = NULL;
if (strncmp(target_filename, "MTD:", 4) == 0) {
// We store the decoded output in memory.
msi.buffer = malloc(target_size);
if (msi.buffer == NULL) {
printf("failed to alloc %ld bytes for output\n",
(long)target_size);
return 1;
}
msi.pos = 0;
msi.size = target_size;
sink = MemorySink;
token = &msi;
} else {
// We write the decoded output to "<tgt-file>.patch".
outname = (char*)malloc(strlen(target_filename) + 10);
strcpy(outname, target_filename);
strcat(outname, ".patch");
output = open(outname, O_WRONLY | O_CREAT | O_TRUNC);
if (output < 0) {
printf("failed to open output file %s: %s\n",
outname, strerror(errno));
return 1;
}
sink = FileSink;
token = &output;
}
char* header = patch->data;
ssize_t header_bytes_read = patch->size;
SHA_init(&ctx);
int result;
if (header_bytes_read >= 8 &&
memcmp(header, "BSDIFF40", 8) == 0) {
result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
patch, 0, sink, token, &ctx);
} else if (header_bytes_read >= 8 &&
memcmp(header, "IMGDIFF2", 8) == 0) {
result = ApplyImagePatch(source_to_use->data, source_to_use->size,
patch, sink, token, &ctx);
} else {
printf("Unknown patch file format\n");
return 1;
}
if (output >= 0) {
fsync(output);
close(output);
}
if (result != 0) {
if (retry == 0) {
printf("applying patch failed\n");
return result != 0;
} else {
printf("applying patch failed; retrying\n");
}
if (outname != NULL) {
unlink(outname);
}
} else {
// succeeded; no need to retry
break;
}
} while (retry-- > 0);
const uint8_t* current_target_sha1 = SHA_final(&ctx);
if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
printf("patch did not produce expected sha1\n");
return 1;
}
if (output < 0) {
// Copy the temp file to the MTD partition.
if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
printf("write of patched data to %s failed\n", target_filename);
return 1;
}
free(msi.buffer);
} else {
// Give the .patch file the same owner, group, and mode of the
// original source file.
if (chmod(outname, source_to_use->st.st_mode) != 0) {
printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno));
return 1;
}
if (chown(outname, source_to_use->st.st_uid,
source_to_use->st.st_gid) != 0) {
printf("chown of \"%s\" failed: %s\n", outname, strerror(errno));
return 1;
}
// Finally, rename the .patch file to replace the target file.
if (rename(outname, target_filename) != 0) {
printf("rename of .patch to \"%s\" failed: %s\n",
target_filename, strerror(errno));
return 1;
}
}
// If this run of applypatch created the copy, and we're here, we
// can delete it.
if (made_copy) unlink(CACHE_TEMP_SOURCE);
// Success!
return 0;
}

84
applypatch/applypatch.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _APPLYPATCH_H
#define _APPLYPATCH_H
#include <sys/stat.h>
#include "mincrypt/sha.h"
#include "edify/expr.h"
typedef struct _Patch {
uint8_t sha1[SHA_DIGEST_SIZE];
const char* patch_filename;
} Patch;
typedef struct _FileContents {
uint8_t sha1[SHA_DIGEST_SIZE];
unsigned char* data;
ssize_t size;
struct stat st;
} FileContents;
// When there isn't enough room on the target filesystem to hold the
// patched version of the file, we copy the original here and delete
// it to free up space. If the expected source file doesn't exist, or
// is corrupted, we look to see if this file contains the bits we want
// and use it as the source instead.
#define CACHE_TEMP_SOURCE "/cache/saved.file"
typedef ssize_t (*SinkFn)(unsigned char*, ssize_t, void*);
// applypatch.c
int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
int CacheSizeCheck(size_t bytes);
int ParseSha1(const char* str, uint8_t* digest);
int applypatch(const char* source_filename,
const char* target_filename,
const char* target_sha1_str,
size_t target_size,
int num_patches,
char** const patch_sha1_str,
Value** patch_data);
int applypatch_check(const char* filename,
int num_patches,
char** const patch_sha1_str);
// Read a file into memory; store it and its associated metadata in
// *file. Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file);
void FreeFileContents(FileContents* file);
// bsdiff.c
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx);
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
unsigned char** new_data, ssize_t* new_size);
// imgpatch.c
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch,
SinkFn sink, void* token, SHA_CTX* ctx);
// freecache.c
int MakeFreeSpaceOnCache(size_t bytes_needed);
#endif

350
applypatch/applypatch.sh Executable file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

30
applypatch/imgdiff.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Image patch chunk types
#define CHUNK_NORMAL 0
#define CHUNK_GZIP 1 // version 1 only
#define CHUNK_DEFLATE 2 // version 2 only
#define CHUNK_RAW 3 // version 2 only
// The gzip header size is actually variable, but we currently don't
// support gzipped data with any of the optional fields, so for now it
// will always be ten bytes. See RFC 1952 for the definition of the
// gzip format.
#define GZIP_HEADER_LEN 10
// The gzip footer size really is fixed.
#define GZIP_FOOTER_LEN 8

118
applypatch/imgdiff_test.sh Executable file
View 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
View 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
View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "applypatch.h"
#include "edify/expr.h"
#include "mincrypt/sha.h"
int CheckMode(int argc, char** argv) {
if (argc < 3) {
return 2;
}
return applypatch_check(argv[2], argc-3, argv+3);
}
int SpaceMode(int argc, char** argv) {
if (argc != 3) {
return 2;
}
char* endptr;
size_t bytes = strtol(argv[2], &endptr, 10);
if (bytes == 0 && endptr == argv[2]) {
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
return 1;
}
return CacheSizeCheck(bytes);
}
// Parse arguments (which should be of the form "<sha1>" or
// "<sha1>:<filename>" into the new parallel arrays *sha1s and
// *patches (loading file contents into the patches). Returns 0 on
// success.
static int ParsePatchArgs(int argc, char** argv,
char*** sha1s, Value*** patches, int* num_patches) {
*num_patches = argc;
*sha1s = malloc(*num_patches * sizeof(char*));
*patches = malloc(*num_patches * sizeof(Value*));
memset(*patches, 0, *num_patches * sizeof(Value*));
uint8_t digest[SHA_DIGEST_SIZE];
int i;
for (i = 0; i < *num_patches; ++i) {
char* colon = strchr(argv[i], ':');
if (colon != NULL) {
*colon = '\0';
++colon;
}
if (ParseSha1(argv[i], digest) != 0) {
printf("failed to parse sha1 \"%s\"\n", argv[i]);
return -1;
}
(*sha1s)[i] = argv[i];
if (colon == NULL) {
(*patches)[i] = NULL;
} else {
FileContents fc;
if (LoadFileContents(colon, &fc) != 0) {
goto abort;
}
(*patches)[i] = malloc(sizeof(Value));
(*patches)[i]->type = VAL_BLOB;
(*patches)[i]->size = fc.size;
(*patches)[i]->data = (char*)fc.data;
}
}
return 0;
abort:
for (i = 0; i < *num_patches; ++i) {
Value* p = (*patches)[i];
if (p != NULL) {
free(p->data);
free(p);
}
}
free(*sha1s);
free(*patches);
return -1;
}
int PatchMode(int argc, char** argv) {
if (argc < 6) {
return 2;
}
char* endptr;
size_t target_size = strtol(argv[4], &endptr, 10);
if (target_size == 0 && endptr == argv[4]) {
printf("can't parse \"%s\" as byte count\n\n", argv[4]);
return 1;
}
char** sha1s;
Value** patches;
int num_patches;
if (ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches) != 0) {
printf("failed to parse patch args\n");
return 1;
}
int result = applypatch(argv[1], argv[2], argv[3], target_size,
num_patches, sha1s, patches);
int i;
for (i = 0; i < num_patches; ++i) {
Value* p = patches[i];
if (p != NULL) {
free(p->data);
free(p);
}
}
free(sha1s);
free(patches);
return result;
}
// This program applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
// successfully.
//
// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
// bsdiff <patch> to <src-file> to produce a new file (the type of patch
// is automatically detected from the file header). If that new
// file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
// exits successfully. Note that if <src-file> and <tgt-file> are
// not the same, <src-file> is NOT deleted on success. <tgt-file>
// may be the string "-" to mean "the same as src-file".
//
// - otherwise, or if any error is encountered, exits with non-zero
// status.
//
// <src-file> (or <file> in check mode) may refer to an MTD partition
// to read the source data. See the comments for the
// LoadMTDContents() function above for the format of such a filename.
int main(int argc, char** argv) {
if (argc < 2) {
usage:
printf(
"usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
"[<src-sha1>:<patch> ...]\n"
" or %s -c <file> [<sha1> ...]\n"
" or %s -s <bytes>\n"
" or %s -l\n"
"\n"
"Filenames may be of the form\n"
" MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
"to specify reading from or writing to an MTD partition.\n\n",
argv[0], argv[0], argv[0], argv[0]);
return 2;
}
int result;
if (strncmp(argv[1], "-l", 3) == 0) {
result = ShowLicenses();
} else if (strncmp(argv[1], "-c", 3) == 0) {
result = CheckMode(argc, argv);
} else if (strncmp(argv[1], "-s", 3) == 0) {
result = SpaceMode(argc, argv);
} else {
result = PatchMode(argc, argv);
}
if (result == 2) {
goto usage;
}
return result;
}

BIN
applypatch/testdata/new.file vendored Normal file

Binary file not shown.

BIN
applypatch/testdata/old.file vendored Normal file

Binary file not shown.

BIN
applypatch/testdata/patch.bsdiff vendored Normal file

Binary file not shown.

65
applypatch/utils.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include "utils.h"
/** Write a 4-byte value to f in little-endian order. */
void Write4(int value, FILE* f) {
fputc(value & 0xff, f);
fputc((value >> 8) & 0xff, f);
fputc((value >> 16) & 0xff, f);
fputc((value >> 24) & 0xff, f);
}
/** Write an 8-byte value to f in little-endian order. */
void Write8(long long value, FILE* f) {
fputc(value & 0xff, f);
fputc((value >> 8) & 0xff, f);
fputc((value >> 16) & 0xff, f);
fputc((value >> 24) & 0xff, f);
fputc((value >> 32) & 0xff, f);
fputc((value >> 40) & 0xff, f);
fputc((value >> 48) & 0xff, f);
fputc((value >> 56) & 0xff, f);
}
int Read2(void* pv) {
unsigned char* p = pv;
return (int)(((unsigned int)p[1] << 8) |
(unsigned int)p[0]);
}
int Read4(void* pv) {
unsigned char* p = pv;
return (int)(((unsigned int)p[3] << 24) |
((unsigned int)p[2] << 16) |
((unsigned int)p[1] << 8) |
(unsigned int)p[0]);
}
long long Read8(void* pv) {
unsigned char* p = pv;
return (long long)(((unsigned long long)p[7] << 56) |
((unsigned long long)p[6] << 48) |
((unsigned long long)p[5] << 40) |
((unsigned long long)p[4] << 32) |
((unsigned long long)p[3] << 24) |
((unsigned long long)p[2] << 16) |
((unsigned long long)p[1] << 8) |
(unsigned long long)p[0]);
}

30
applypatch/utils.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H
#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
#include <stdio.h>
// Read and write little-endian values of various sizes.
void Write4(int value, FILE* f);
void Write8(long long value, FILE* f);
int Read2(void* p);
int Read4(void* p);
long long Read8(void* p);
#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H

7
bmlutils/Android.mk Normal file
View File

@ -0,0 +1,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DBOARD_BOOT_DEVICE=\"$(BOARD_BOOT_DEVICE)\"
LOCAL_SRC_FILES := bmlutils.c
LOCAL_MODULE := libbmlutils
include $(BUILD_STATIC_LIBRARY)

58
bmlutils/bmlutils.c Normal file
View File

@ -0,0 +1,58 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>
extern int __system(const char *command);
int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
{
char tmp[PATH_MAX];
sprintf("dd if=%s of=/dev/block/bml7 bs=4096", filename);
return __system(tmp);
}
int cmd_bml_backup_raw_partition(const char *partition, const char *filename)
{
char tmp[PATH_MAX];
sprintf("dd of=%s if=/dev/block/bml7 bs=4096", filename);
return __system(tmp);
}
int cmd_bml_erase_raw_partition(const char *partition)
{
// TODO: implement raw wipe
return 0;
}
int cmd_bml_erase_partition(const char *partition, const char *filesystem)
{
return -1;
}
int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
{
return -1;
}
int cmd_bml_get_partition_device(const char *partition, char *device)
{
return -1;
}

View File

@ -145,7 +145,15 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[],
LOGE("Can't format %s\n", root);
return 1;
}
#ifdef BOARD_HAS_DATADATA
if (0 == strcmp(root, "DATA:")) {
ret = format_root_device("DATADATA:");
if (ret != 0) {
LOGE("Can't format %s\n", root);
return 1;
}
}
#endif
return 0;
}
@ -618,11 +626,13 @@ cmd_write_firmware_image(const char *name, void *cookie,
return 1;
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (remember_firmware_update(type, context.data, context.total_bytes)) {
LOGE("Can't store %s image\n", type);
free(context.data);
return 1;
}
#endif
return 0;
}
@ -643,6 +653,7 @@ static int
cmd_write_raw_image(const char *name, void *cookie,
int argc, const char *argv[], PermissionRequestList *permissions)
{
#ifdef BOARD_USES_MTDUTILS
UNUSED(cookie);
CHECK_WORDS();
//xxx permissions
@ -728,6 +739,10 @@ cmd_write_raw_image(const char *name, void *cookie,
return -1;
}
return 0;
#else
LOGE("Board does not support mtd utils.");
return -1;
#endif
}
/* mark <resource> dirty|clean
@ -774,18 +789,7 @@ cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
case 0:
{
char backup_path[PATH_MAX];
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
if (tmp == NULL)
{
struct timeval tp;
gettimeofday(&tp, NULL);
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
}
else
{
strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
}
nandroid_generate_timestamp_path(backup_path);
backup_name = backup_path;
}
break;

View File

@ -47,6 +47,9 @@ int ui_menu_select(int sel);
// statements will be displayed.
void ui_end_menu();
int ui_get_showing_back_button();
void ui_set_showing_back_button(int showBackButton);
// Set the icon (normally the only thing visible besides the progress bar).
enum {
BACKGROUND_ICON_NONE,

View File

@ -27,21 +27,24 @@ char* MENU_ITEMS[] = { "reboot system now",
"wipe data/factory reset",
"wipe cache partition",
"install zip from sdcard",
"nandroid",
"partitions menu",
"backup and restore",
"mounts and storage",
"advanced",
NULL };
int device_recovery_start() {
return 0;
}
int device_toggle_display(volatile char* key_pressed, int key_code) {
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
if (alt && key_code == KEY_L)
return 1;
// allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
#ifdef KEY_POWER_IS_SELECT_ITEM
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
#else
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);
#endif
}
int device_reboot_now(volatile char* key_pressed, int key_code) {
@ -51,17 +54,24 @@ int device_reboot_now(volatile char* key_pressed, int key_code) {
int device_handle_key(int key_code, int visible) {
if (visible) {
switch (key_code) {
case KEY_UP:
case KEY_VOLUMEUP:
return HIGHLIGHT_DOWN;
case KEY_CAPSLOCK:
case KEY_DOWN:
case KEY_VOLUMEDOWN:
return HIGHLIGHT_DOWN;
case KEY_LEFTSHIFT:
case KEY_UP:
case KEY_VOLUMEUP:
return HIGHLIGHT_UP;
#ifdef KEY_POWER_IS_SELECT_ITEM
case KEY_POWER:
#endif
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:
@ -70,9 +80,6 @@ int device_handle_key(int key_code, int visible) {
case KEY_SEND:
return SELECT_ITEM;
#ifndef KEY_POWER_IS_SELECT_ITEM
case KEY_POWER:
#endif
case KEY_END:
case KEY_BACKSPACE:
case KEY_BACK:

View File

@ -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);

View File

@ -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

View File

@ -15,6 +15,8 @@
* limitations under the License.
*/
#include <string.h>
#include "expr.h"
#include "yydefs.h"
#include "parser.h"

View File

@ -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);

View File

@ -33,4 +33,6 @@ typedef struct {
} \
} while (0)
int yylex();
#endif

View File

@ -33,7 +33,6 @@
#include "commands.h"
#include "amend/amend.h"
#include "mtdutils/dump_image.h"
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
@ -60,17 +59,21 @@ void toggle_script_asserts()
int install_zip(const char* packagefilepath)
{
ui_print("\n-- Installing: %s\n", packagefilepath);
#ifndef BOARD_HAS_NO_MISC_PARTITION
set_sdcard_update_bootloader_message();
#endif
int status = install_package(packagefilepath);
ui_reset_progress();
if (status != INSTALL_SUCCESS) {
ui_set_background(BACKGROUND_ICON_ERROR);
ui_print("Installation aborted.\n");
return 1;
}
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (firmware_update_pending()) {
ui_print("\nReboot via menu to complete\ninstallation.\n");
}
#endif
ui_set_background(BACKGROUND_ICON_NONE);
ui_print("\nInstall from sdcard complete.\n");
return 0;
@ -90,7 +93,7 @@ void show_install_update_menu()
{
static char* headers[] = { "Apply update from .zip file on SD card",
"",
NULL
NULL
};
for (;;)
{
@ -104,15 +107,18 @@ void show_install_update_menu()
toggle_signature_check();
break;
case ITEM_APPLY_SDCARD:
install_zip(SDCARD_PACKAGE_FILE);
{
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
install_zip(SDCARD_PACKAGE_FILE);
break;
}
case ITEM_CHOOSE_ZIP:
show_choose_zip_menu();
break;
default:
return;
}
}
}
@ -147,11 +153,11 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory,
ui_print("Couldn't open directory.\n");
return NULL;
}
int extension_length = 0;
if (fileExtensionOrDirectory != NULL)
extension_length = strlen(fileExtensionOrDirectory);
int isCounting = 1;
i = 0;
for (pass = 0; pass < 2; pass++) {
@ -159,7 +165,7 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory,
// skip hidden files
if (de->d_name[0] == '.')
continue;
// NULL means that we are gathering directories, so skip this
if (fileExtensionOrDirectory != NULL)
{
@ -181,13 +187,13 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory,
if (!(S_ISDIR(info.st_mode)))
continue;
}
if (pass == 0)
{
total++;
continue;
}
files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2);
strcpy(files[i], directory);
strcat(files[i], de->d_name);
@ -213,6 +219,21 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory,
return NULL;
}
// sort the result
if (files != NULL) {
for (i = 0; i < total; i++) {
int curMax = -1;
int j;
for (j = 0; j < total - i; j++) {
if (curMax == -1 || strcmp(files[curMax], files[j]) < 0)
curMax = j;
}
char* temp = files[curMax];
files[curMax] = files[total - i - 1];
files[total - i - 1] = temp;
}
}
return files;
}
@ -269,7 +290,7 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto
break;
}
continue;
}
}
strcpy(ret, files[chosen_item - numDirs]);
return_value = ret;
break;
@ -291,57 +312,20 @@ void show_choose_zip_menu()
static char* headers[] = { "Choose a zip to apply",
"",
NULL
NULL
};
char* file = choose_file_menu("/sdcard/", ".zip", headers);
if (file == NULL)
return;
char sdcard_package_file[1024];
strcpy(sdcard_package_file, "SDCARD:");
strcat(sdcard_package_file, file + strlen("/sdcard/"));
install_zip(sdcard_package_file);
}
// This was pulled from bionic: The default system command always looks
// for shell in /system/bin/sh. This is bad.
#define _PATH_BSHELL "/sbin/sh"
extern char **environ;
int
__system(const char *command)
{
pid_t pid;
sig_t intsave, quitsave;
sigset_t mask, omask;
int pstat;
char *argp[] = {"sh", "-c", NULL, NULL};
if (!command) /* just checking... */
return(1);
argp[2] = (char *)command;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &omask);
switch (pid = vfork()) {
case -1: /* error */
sigprocmask(SIG_SETMASK, &omask, NULL);
return(-1);
case 0: /* child */
sigprocmask(SIG_SETMASK, &omask, NULL);
execve(_PATH_BSHELL, argp, environ);
_exit(127);
}
intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN);
quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
pid = waitpid(pid, (int *)&pstat, 0);
sigprocmask(SIG_SETMASK, &omask, NULL);
(void)bsd_signal(SIGINT, intsave);
(void)bsd_signal(SIGQUIT, quitsave);
return (pid == -1 ? -1 : pstat);
static char* confirm_install = "Confirm install?";
static char confirm[PATH_MAX];
sprintf(confirm, "Yes - Install %s", basename(file));
if (confirm_selection(confirm_install, confirm))
install_zip(sdcard_package_file);
}
void show_nandroid_restore_menu()
@ -350,73 +334,76 @@ void show_nandroid_restore_menu()
LOGE ("Can't mount /sdcard\n");
return;
}
static char* headers[] = { "Choose an image to restore",
"",
NULL
NULL
};
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers);
if (file == NULL)
return;
nandroid_restore(file, 1, 1, 1, 1, 1);
if (confirm_selection("Confirm restore?", "Yes - Restore"))
nandroid_restore(file, 1, 1, 1, 1, 1);
}
void show_mount_usb_storage_menu()
{
__system("echo /dev/block/mmcblk0 > /sys/devices/platform/usb_mass_storage/lun0/file");
char command[PATH_MAX];
sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", BOARD_SDCARD_DEVICE_PRIMARY);
__system(command);
static char* headers[] = { "USB Mass Storage device",
"Leaving this menu unmount",
"your SD card from your PC.",
"",
NULL
NULL
};
static char* list[] = { "Unmount", NULL };
for (;;)
{
int chosen_item = get_menu_selection(headers, list, 0);
if (chosen_item == GO_BACK || chosen_item == 0)
break;
}
__system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file");
__system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable");
}
int confirm_format()
int confirm_selection(const char* title, const char* confirm)
{
static char* title_headers[] = { "Confirm format?",
" THIS CAN NOT BE UNDONE.",
"",
NULL,
};
struct stat info;
if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info))
return 1;
char* items[] = { " No",
" No",
" No",
" No",
" No",
" No",
" No",
" Yes -- wipe partition", // [7]
" No",
" No",
" No",
char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL };
char* items[] = { "No",
"No",
"No",
"No",
"No",
"No",
"No",
confirm, //" Yes -- wipe partition", // [7
"No",
"No",
"No",
NULL };
int chosen_item = get_menu_selection(title_headers, items, 0);
int chosen_item = get_menu_selection(confirm_headers, items, 0);
return chosen_item == 7;
}
int format_non_mtd_device(const char* root)
int format_unknown_device(const char* root)
{
// if this is SDEXT:, don't worry about it.
if (0 == strcmp(root, "SDEXT:"))
{
struct stat st;
if (0 != stat("/dev/block/mmcblk0p2", &st))
if (0 != stat(BOARD_SDEXT_DEVICE, &st))
{
ui_print("No app2sd partition found. Skipping format of /sd-ext.\n");
return 0;
@ -428,7 +415,8 @@ int format_non_mtd_device(const char* root)
if (0 != ensure_root_path_mounted(root))
{
ui_print("Error mounting %s!\n", path);
return 1;
ui_print("Skipping format...\n");
return 0;
}
static char tmp[PATH_MAX];
@ -436,7 +424,7 @@ int format_non_mtd_device(const char* root)
__system(tmp);
sprintf(tmp, "rm -rf %s/.*", path);
__system(tmp);
ensure_root_path_unmounted(root);
return 0;
}
@ -447,32 +435,35 @@ int format_non_mtd_device(const char* root)
void show_partition_menu()
{
static char* headers[] = { "Mount and unmount partitions",
static char* headers[] = { "Mounts and Storage Menu",
"",
NULL
NULL
};
typedef char* string;
string mounts[MOUNTABLE_COUNT][3] = {
string mounts[MOUNTABLE_COUNT][3] = {
{ "mount /system", "unmount /system", "SYSTEM:" },
{ "mount /data", "unmount /data", "DATA:" },
{ "mount /cache", "unmount /cache", "CACHE:" },
{ "mount /sdcard", "unmount /sdcard", "SDCARD:" },
{ "mount /sd-ext", "unmount /sd-ext", "SDEXT:" }
};
string mtds[MTD_COUNT][2] = {
{ "format boot", "BOOT:" },
{ "format system", "SYSTEM:" },
{ "format data", "DATA:" },
{ "format cache", "CACHE:" },
};
string mmcs[MMC_COUNT][3] = {
{ "format sdcard", "SDCARD:" },
{ "format sd-ext", "SDEXT:" }
{ "format sd-ext", "SDEXT:" }
};
static char* confirm_format = "Confirm format?";
static char* confirm = "Yes - Format";
for (;;)
{
int ismounted[MOUNTABLE_COUNT];
@ -483,20 +474,20 @@ void show_partition_menu()
ismounted[i] = is_root_path_mounted(mounts[i][2]);
options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0];
}
for (i = 0; i < MTD_COUNT; i++)
{
options[MOUNTABLE_COUNT + i] = mtds[i][0];
}
for (i = 0; i < MMC_COUNT; i++)
{
options[MOUNTABLE_COUNT + MTD_COUNT + i] = mmcs[i][0];
}
options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT] = "mount USB storage";
options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1] = NULL;
int chosen_item = get_menu_selection(headers, options, 0);
if (chosen_item == GO_BACK)
break;
@ -520,7 +511,7 @@ void show_partition_menu()
else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT)
{
chosen_item = chosen_item - MOUNTABLE_COUNT;
if (!confirm_format())
if (!confirm_selection(confirm_format, confirm))
continue;
ui_print("Formatting %s...\n", mtds[chosen_item][1]);
if (0 != format_root_device(mtds[chosen_item][1]))
@ -531,10 +522,10 @@ void show_partition_menu()
else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
{
chosen_item = chosen_item - MOUNTABLE_COUNT - MTD_COUNT;
if (!confirm_format())
if (!confirm_selection(confirm_format, confirm))
continue;
ui_print("Formatting %s...\n", mmcs[chosen_item][1]);
if (0 != format_non_mtd_device(mmcs[chosen_item][1]))
if (0 != format_unknown_device(mmcs[chosen_item][1]))
ui_print("Error formatting %s!\n", mmcs[chosen_item][1]);
else
ui_print("Done.\n");
@ -575,8 +566,8 @@ int run_script_from_buffer(char* script_data, int script_len, char* filename)
}
printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
return 1;
}
}
return 0;
}
@ -587,7 +578,7 @@ int run_script(char* filename)
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");
@ -595,6 +586,8 @@ int run_script(char* filename)
// 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);
@ -616,17 +609,18 @@ int run_and_remove_extendedcommand()
}
sleep(1);
}
remove("/sdcard/clockworkmod/.recoverycheckpoint");
if (i == 0) {
ui_print("Timed out waiting for SD card... continuing anyways.");
}
sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
return run_script(tmp);
}
int amend_main(int argc, char** argv)
{
if (argc != 2)
if (argc != 2)
{
printf("Usage: amend <script>\n");
return 0;
@ -672,23 +666,31 @@ void show_nandroid_advanced_restore_menu()
NULL
};
static char* confirm_restore = "Confirm restore?";
int chosen_item = get_menu_selection(headers, list, 0);
switch (chosen_item)
{
case 0:
nandroid_restore(file, 1, 0, 0, 0, 0);
if (confirm_selection(confirm_restore, "Yes - Restore boot"))
nandroid_restore(file, 1, 0, 0, 0, 0);
break;
case 1:
nandroid_restore(file, 0, 1, 0, 0, 0);
if (confirm_selection(confirm_restore, "Yes - Restore system"))
nandroid_restore(file, 0, 1, 0, 0, 0);
break;
case 2:
nandroid_restore(file, 0, 0, 1, 0, 0);
if (confirm_selection(confirm_restore, "Yes - Restore data"))
nandroid_restore(file, 0, 0, 1, 0, 0);
break;
case 3:
nandroid_restore(file, 0, 0, 0, 1, 0);
if (confirm_selection(confirm_restore, "Yes - Restore cache"))
nandroid_restore(file, 0, 0, 0, 1, 0);
break;
case 4:
nandroid_restore(file, 0, 0, 0, 0, 1);
if (confirm_selection(confirm_restore, "Yes - Restore sd-ext"))
nandroid_restore(file, 0, 0, 0, 0, 1);
break;
}
}
@ -697,10 +699,10 @@ void show_nandroid_menu()
{
static char* headers[] = { "Nandroid",
"",
NULL
NULL
};
static char* list[] = { "Backup",
static char* list[] = { "Backup",
"Restore",
"Advanced Restore",
NULL
@ -751,8 +753,14 @@ void show_advanced_menu()
};
static char* list[] = { "Reboot Recovery",
"Wipe Dalvik Cache",
"Wipe Battery Stats",
"Report Error",
"Key Test",
#ifndef BOARD_HAS_SMALL_RECOVERY
"Partition SD Card",
"Fix Permissions",
#endif
NULL
};
@ -767,9 +775,30 @@ void show_advanced_menu()
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery");
break;
case 1:
wipe_battery_stats();
{
if (0 != ensure_root_path_mounted("DATA:"))
break;
ensure_root_path_mounted("SDEXT:");
ensure_root_path_mounted("CACHE:");
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) {
__system("rm -r /data/dalvik-cache");
__system("rm -r /cache/dalvik-cache");
__system("rm -r /sd-ext/dalvik-cache");
}
ensure_root_path_unmounted("DATA:");
ui_print("Dalvik Cache wiped.\n");
break;
}
case 2:
{
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Battery Stats"))
wipe_battery_stats();
break;
}
case 3:
handle_failure(1);
break;
case 4:
{
ui_print("Outputting key codes.\n");
ui_print("Go back to end debugging.\n");
@ -782,7 +811,112 @@ void show_advanced_menu()
ui_print("Key: %d\n", key);
}
while (action != GO_BACK);
break;
}
case 5:
{
static char* ext_sizes[] = { "128M",
"256M",
"512M",
"1024M",
"2048M",
"4096M",
NULL };
static char* swap_sizes[] = { "0M",
"32M",
"64M",
"128M",
"256M",
NULL };
static char* ext_headers[] = { "Ext Size", "", NULL };
static char* swap_headers[] = { "Swap Size", "", NULL };
int ext_size = get_menu_selection(ext_headers, ext_sizes, 0);
if (ext_size == GO_BACK)
continue;
int swap_size = get_menu_selection(swap_headers, swap_sizes, 0);
if (swap_size == GO_BACK)
continue;
char sddevice[256];
const RootInfo *ri = get_root_info_for_path("SDCARD:");
strcpy(sddevice, ri->device);
// we only want the mmcblk, not the partition
sddevice[strlen("/dev/block/mmcblkX")] = NULL;
char cmd[PATH_MAX];
setenv("SDPATH", sddevice, 1);
sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
ui_print("Partitioning SD Card... please wait...\n");
if (0 == __system(cmd))
ui_print("Done!\n");
else
ui_print("An error occured while partitioning your SD Card. Please see /tmp/recovery.log for more details.\n");
break;
}
case 6:
{
ensure_root_path_mounted("SYSTEM:");
ensure_root_path_mounted("DATA:");
ui_print("Fixing permissions...\n");
__system("fix_permissions");
ui_print("Done!\n");
break;
}
}
}
}
}
void write_fstab_root(char *root_path, FILE *file)
{
RootInfo *info = get_root_info_for_path(root_path);
if (info == NULL) {
LOGW("Unable to get root info for %s during fstab generation!", root_path);
return;
}
char device[PATH_MAX];
int ret = get_root_partition_device(root_path, device);
if (ret == 0)
{
fprintf(file, "%s ", device);
}
else
{
fprintf(file, "%s ", info->device);
}
fprintf(file, "%s ", info->mount_point);
fprintf(file, "%s %s\n", info->filesystem, info->filesystem_options == NULL ? "rw" : info->filesystem_options);
}
void create_fstab()
{
__system("touch /etc/mtab");
FILE *file = fopen("/etc/fstab", "w");
if (file == NULL) {
LOGW("Unable to create /etc/fstab!");
return;
}
write_fstab_root("CACHE:", file);
write_fstab_root("DATA:", file);
#ifdef BOARD_HAS_DATADATA
write_fstab_root("DATADATA:", file);
#endif
write_fstab_root("SYSTEM:", file);
write_fstab_root("SDCARD:", file);
write_fstab_root("SDEXT:", file);
fclose(file);
}
void handle_failure(int ret)
{
if (ret == 0)
return;
if (0 != ensure_root_path_mounted("SDCARD:"))
return;
mkdir("/sdcard/clockworkmod", S_IRWXU);
__system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log");
ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n");
}

View File

@ -38,8 +38,9 @@ void
show_advanced_menu();
int
format_non_mtd_device(const char* root);
format_unknown_device(const char* root);
void
wipe_battery_stats();
void create_fstab();

91
flashutils/Android.mk Normal file
View File

@ -0,0 +1,91 @@
LOCAL_PATH := $(call my-dir)
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_ARCH),arm)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flashutils.c
LOCAL_MODULE := libflashutils
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := flash_image
LOCAL_MODULE_TAGS := eng
#LOCAL_STATIC_LIBRARIES += $(BOARD_FLASH_LIBRARY)
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := dump_image.c
LOCAL_MODULE := dump_image
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := erase_image.c
LOCAL_MODULE := erase_image
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := libflash_image
LOCAL_CFLAGS += -Dmain=flash_image_main
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := dump_image.c
LOCAL_MODULE := libdump_image
LOCAL_CFLAGS += -Dmain=dump_image_main
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := erase_image.c
LOCAL_MODULE := liberase_image
LOCAL_CFLAGS += -Dmain=erase_image_main
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := dump_image.c
LOCAL_MODULE := utility_dump_image
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := dump_image
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := utility_flash_image
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := flash_image
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := erase_image.c
LOCAL_MODULE := utility_erase_image
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := erase_image
LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR

View File

@ -23,13 +23,14 @@
#include <sys/ioctl.h>
#include "cutils/log.h"
#include "mtdutils.h"
#include "dump_image.h"
#include "flashutils.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#if 0
#define LOG_TAG "dump_image"
#define BLOCK_SIZE 2048
@ -135,3 +136,15 @@ int main(int argc, char **argv)
return dump_image(argv[1], argv[2], NULL);
}
#endif
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
return 2;
}
return backup_raw_partition(argv[1], argv[2]);
}

View File

@ -23,15 +23,17 @@
#include <unistd.h>
#include <fcntl.h>
#include <mtd/mtd-user.h>
#include "cutils/log.h"
#include "mtdutils.h"
#include "flashutils.h"
#if 0
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "erase_image"
static int die(const char *msg, ...) {
@ -86,3 +88,16 @@ int main(int argc, char **argv) {
return erase_image(argv[1]);
}
#endif
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "usage: %s partition\n", argv[0]);
return 2;
}
return erase_raw_partition(argv[1]);
}

View File

@ -22,8 +22,8 @@
#include <unistd.h>
#include "cutils/log.h"
#include "mtdutils.h"
#if 0
#define LOG_TAG "flash_image"
#define HEADER_SIZE 2048 // size of header to compare for equality
@ -138,3 +138,14 @@ int main(int argc, char **argv) {
if (mtd_write_close(out)) die("error closing %s", argv[1]);
return 0;
}
#endif
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
return 2;
}
return restore_raw_partition(argv[1], argv[2]);
}

168
flashutils/flashutils.c Normal file
View File

@ -0,0 +1,168 @@
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include "flashutils/flashutils.h"
enum flash_type {
UNSUPPORTED = -1,
UNKNOWN = 0,
MTD = 1,
MMC = 2,
BML = 3
};
int the_flash_type = UNKNOWN;
int device_flash_type()
{
if (the_flash_type == UNKNOWN) {
if (access("/dev/block/bml1", F_OK) == 0) {
the_flash_type = BML;
} else if (access("/proc/emmc", F_OK) == 0) {
the_flash_type = MMC;
} else if (access("/proc/mtd", F_OK) == 0) {
the_flash_type = MTD;
} else {
the_flash_type = UNSUPPORTED;
}
}
return the_flash_type;
}
char* get_default_filesystem()
{
return device_flash_type() == MMC ? "ext3" : "yaffs2";
}
// This was pulled from bionic: The default system command always looks
// for shell in /system/bin/sh. This is bad.
#define _PATH_BSHELL "/sbin/sh"
extern char **environ;
int
__system(const char *command)
{
pid_t pid;
sig_t intsave, quitsave;
sigset_t mask, omask;
int pstat;
char *argp[] = {"sh", "-c", NULL, NULL};
if (!command) /* just checking... */
return(1);
argp[2] = (char *)command;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &omask);
switch (pid = vfork()) {
case -1: /* error */
sigprocmask(SIG_SETMASK, &omask, NULL);
return(-1);
case 0: /* child */
sigprocmask(SIG_SETMASK, &omask, NULL);
execve(_PATH_BSHELL, argp, environ);
_exit(127);
}
intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN);
quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
pid = waitpid(pid, (int *)&pstat, 0);
sigprocmask(SIG_SETMASK, &omask, NULL);
(void)bsd_signal(SIGINT, intsave);
(void)bsd_signal(SIGQUIT, quitsave);
return (pid == -1 ? -1 : pstat);
}
int restore_raw_partition(const char *partition, const char *filename)
{
int type = device_flash_type();
switch (type) {
case MTD:
return cmd_mtd_restore_raw_partition(partition, filename);
case MMC:
return cmd_mmc_restore_raw_partition(partition, filename);
case BML:
return cmd_bml_restore_raw_partition(partition, filename);
default:
return -1;
}
}
int backup_raw_partition(const char *partition, const char *filename)
{
int type = device_flash_type();
switch (type) {
case MTD:
return cmd_mtd_backup_raw_partition(partition, filename);
case MMC:
return cmd_mmc_backup_raw_partition(partition, filename);
case BML:
return cmd_bml_backup_raw_partition(partition, filename);
default:
return -1;
}
}
int erase_raw_partition(const char *partition)
{
int type = device_flash_type();
switch (type) {
case MTD:
return cmd_mtd_erase_raw_partition(partition);
case MMC:
return cmd_mmc_erase_raw_partition(partition);
case BML:
return cmd_bml_erase_raw_partition(partition);
default:
return -1;
}
}
int erase_partition(const char *partition, const char *filesystem)
{
int type = device_flash_type();
switch (type) {
case MTD:
return cmd_mtd_erase_partition(partition, filesystem);
case MMC:
return cmd_mmc_erase_partition(partition, filesystem);
case BML:
return cmd_bml_erase_partition(partition, filesystem);
default:
return -1;
}
}
int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
{
int type = device_flash_type();
switch (type) {
case MTD:
return cmd_mtd_mount_partition(partition, mount_point, filesystem, read_only);
case MMC:
return cmd_mmc_mount_partition(partition, mount_point, filesystem, read_only);
case BML:
return cmd_bml_mount_partition(partition, mount_point, filesystem, read_only);
default:
return -1;
}
}
int get_partition_device(const char *partition, char *device)
{
int type = device_flash_type();
switch (type) {
case MTD:
return cmd_mtd_get_partition_device(partition, device);
case MMC:
return cmd_mmc_get_partition_device(partition, device);
case BML:
return cmd_bml_get_partition_device(partition, device);
default:
return -1;
}
}

38
flashutils/flashutils.h Normal file
View File

@ -0,0 +1,38 @@
int restore_raw_partition(const char *partition, const char *filename);
int backup_raw_partition(const char *partition, const char *filename);
int erase_raw_partition(const char *partition);
int erase_partition(const char *partition, const char *filesystem);
int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
int get_partition_device(const char *partition, char *device);
#define FLASH_MTD 0
#define FLASH_MMC 1
#define FLASH_BML 2
int is_mtd_device();
char* get_default_filesystem();
int __system(const char *command);
extern int cmd_mtd_restore_raw_partition(const char *partition, const char *filename);
extern int cmd_mtd_backup_raw_partition(const char *partition, const char *filename);
extern int cmd_mtd_erase_raw_partition(const char *partition);
extern int cmd_mtd_erase_partition(const char *partition, const char *filesystem);
extern int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
extern int cmd_mtd_get_partition_device(const char *partition, char *device);
extern int cmd_mmc_restore_raw_partition(const char *partition, const char *filename);
extern int cmd_mmc_backup_raw_partition(const char *partition, const char *filename);
extern int cmd_mmc_erase_raw_partition(const char *partition);
extern int cmd_mmc_erase_partition(const char *partition, const char *filesystem);
extern int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
extern int cmd_mmc_get_partition_device(const char *partition, char *device);
extern int cmd_bml_restore_raw_partition(const char *partition, const char *filename);
extern int cmd_bml_backup_raw_partition(const char *partition, const char *filename);
extern int cmd_bml_erase_raw_partition(const char *partition);
extern int cmd_bml_erase_partition(const char *partition, const char *filesystem);
extern int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
extern int cmd_bml_get_partition_device(const char *partition, char *device);

View File

@ -28,15 +28,17 @@
#include "minui/minui.h"
#include "minzip/SysUtil.h"
#include "minzip/Zip.h"
#include "mtdutils/mounts.h"
#include "mounts.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
#include "firmware.h"
#include "legacy.h"
#include "extendedcommands.h"
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
#define PUBLIC_KEYS_FILE "/res/keys"
@ -90,11 +92,13 @@ handle_firmware_update(char* type, char* filename, ZipArchive* zip) {
fclose(f);
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (remember_firmware_update(type, data, data_size)) {
LOGE("Can't store %s image\n", type);
free(data);
return INSTALL_ERROR;
}
#endif
free(filename);
return INSTALL_SUCCESS;
@ -148,9 +152,12 @@ try_update_binary(const char *path, ZipArchive *zip) {
//
// firmware <"hboot"|"radio"> <filename>
// arrange to install the contents of <filename> in the
// given partition on reboot. (API v2: <filename> may
// start with "PACKAGE:" to indicate taking a file from
// the OTA package.)
// given partition on reboot.
//
// (API v2: <filename> may start with "PACKAGE:" to
// indicate taking a file from the OTA package.)
//
// (API v3: this command no longer exists.)
//
// ui_print <string>
// display <string> on the screen.
@ -234,6 +241,7 @@ try_update_binary(const char *path, ZipArchive *zip) {
} else {
return INSTALL_SUCCESS;
}
return INSTALL_SUCCESS;
}
static int
@ -295,7 +303,7 @@ load_keys(const char* filename, int* numKeys) {
++*numKeys;
out = realloc(out, *numKeys * sizeof(RSAPublicKey));
RSAPublicKey* key = out + (*numKeys - 1);
if (fscanf(f, " { %i , %i , { %i",
if (fscanf(f, " { %i , 0x%x , { %u",
&(key->len), &(key->n0inv), &(key->n[0])) != 3) {
goto exit;
}
@ -304,11 +312,11 @@ load_keys(const char* filename, int* numKeys) {
goto exit;
}
for (i = 1; i < key->len; ++i) {
if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit;
if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
}
if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit;
if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
for (i = 1; i < key->len; ++i) {
if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit;
if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
}
fscanf(f, " } } ");

View File

@ -5,4 +5,18 @@ rm /cache/update.zip
touch /tmp/.ignorebootmessage
kill $(ps | grep /sbin/adbd)
kill $(ps | grep /sbin/recovery)
exit 1
# On the Galaxy S, the recovery comes test signed, but the
# recovery is not automatically restarted.
if [ -f /init.smdkc110.rc ]
then
/sbin/recovery &
fi
# Droid X
if [ -f /init.mapphone_cdma.rc ]
then
/sbin/recovery &
fi
exit 1

View File

@ -28,7 +28,6 @@
#include "minui/minui.h"
#include "minzip/SysUtil.h"
#include "minzip/Zip.h"
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
@ -41,7 +40,7 @@
#include "minui/minui.h"
#include "minzip/SysUtil.h"
#include "minzip/Zip.h"
#include "mtdutils/mounts.h"
#include "mounts.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"

View File

@ -7,6 +7,10 @@ LOCAL_C_INCLUDES +=\
external/libpng\
external/zlib
ifneq ($(BOARD_LDPI_RECOVERY),)
LOCAL_CFLAGS += -DBOARD_LDPI_RECOVERY='"$(BOARD_LDPI_RECOVERY)"'
endif
LOCAL_MODULE := libminui
include $(BUILD_STATIC_LIBRARY)

View File

@ -19,16 +19,161 @@
#include <fcntl.h>
#include <dirent.h>
#include <sys/poll.h>
#include <limits.h>
#include <linux/input.h>
#include "../common.h"
#include "minui.h"
#define MAX_DEVICES 16
#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable"
#define VIBRATOR_TIME_MS 50
#define PRESS_THRESHHOLD 10
struct virtualkey {
int scancode;
int centerx, centery;
int width, height;
};
struct position {
int x, y;
int pressed;
struct input_absinfo xi, yi;
};
struct ev {
struct pollfd *fd;
struct virtualkey *vks;
int vk_count;
struct position p, mt_p;
int sent, mt_idx;
};
static struct pollfd ev_fds[MAX_DEVICES];
static struct ev evs[MAX_DEVICES];
static unsigned ev_count = 0;
static inline int ABS(int x) {
return x<0?-x:x;
}
int vibrate(int timeout_ms)
{
char str[20];
int fd;
int ret;
fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY);
if (fd < 0)
return -1;
ret = snprintf(str, sizeof(str), "%d", timeout_ms);
ret = write(fd, str, ret);
close(fd);
if (ret < 0)
return -1;
return 0;
}
/* Returns empty tokens */
static char *vk_strtok_r(char *str, const char *delim, char **save_str)
{
if(!str) {
if(!*save_str) return NULL;
str = (*save_str) + 1;
}
*save_str = strpbrk(str, delim);
if(*save_str) **save_str = '\0';
return str;
}
static int vk_init(struct ev *e)
{
char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys.";
char vks[2048], *ts;
ssize_t len;
int vk_fd;
int i;
e->vk_count = 0;
len = strlen(vk_path);
len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(vk_path) - len), vk_path + len);
if (len <= 0)
return -1;
vk_fd = open(vk_path, O_RDONLY);
if (vk_fd < 0)
return -1;
len = read(vk_fd, vks, sizeof(vks)-1);
close(vk_fd);
if (len <= 0)
return -1;
vks[len] = '\0';
/* Parse a line like:
keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:...
*/
for (ts = vks, e->vk_count = 1; *ts; ++ts) {
if (*ts == ':')
++e->vk_count;
}
if (e->vk_count % 6) {
LOGW("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6);
}
e->vk_count /= 6;
if (e->vk_count <= 0)
return -1;
e->sent = 0;
e->mt_idx = 0;
ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi);
ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi);
e->p.pressed = 0;
ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi);
ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi);
e->mt_p.pressed = 0;
e->vks = malloc(sizeof(*e->vks) * e->vk_count);
for (i = 0; i < e->vk_count; ++i) {
char *token[6];
int j;
for (j = 0; j < 6; ++j) {
token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts);
}
if (strcmp(token[0], "0x01") != 0) {
/* Java does string compare, so we do too. */
LOGW("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]);
continue;
}
e->vks[i].scancode = strtol(token[1], NULL, 0);
e->vks[i].centerx = strtol(token[2], NULL, 0);
e->vks[i].centery = strtol(token[3], NULL, 0);
e->vks[i].width = strtol(token[4], NULL, 0);
e->vks[i].height = strtol(token[5], NULL, 0);
}
return 0;
}
int ev_init(void)
{
DIR *dir;
@ -45,6 +190,11 @@ int ev_init(void)
ev_fds[ev_count].fd = fd;
ev_fds[ev_count].events = POLLIN;
evs[ev_count].fd = &ev_fds[ev_count];
/* Load virtualkeys if there are any */
vk_init(&evs[ev_count]);
ev_count++;
if(ev_count == MAX_DEVICES) break;
}
@ -55,11 +205,135 @@ int ev_init(void)
void ev_exit(void)
{
while (ev_count > 0) {
close(ev_fds[--ev_count].fd);
while (ev_count-- > 0) {
if (evs[ev_count].vk_count) {
free(evs[ev_count].vks);
evs[ev_count].vk_count = 0;
}
close(ev_fds[ev_count].fd);
}
}
static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size)
{
int screen_pos;
if (info->minimum == info->maximum)
return 0;
screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum);
return (screen_pos >= 0 && screen_pos < screen_size);
}
static int vk_tp_to_screen(struct position *p, int *x, int *y)
{
if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum)
return 0;
*x = (p->x - p->xi.minimum) * (gr_fb_width() - 1) / (p->xi.maximum - p->xi.minimum);
*y = (p->y - p->yi.minimum) * (gr_fb_height() - 1) / (p->yi.maximum - p->yi.minimum);
if (*x >= 0 && *x < gr_fb_width() &&
*y >= 0 && *y < gr_fb_height()) {
return 0;
}
return 1;
}
/* Translate a virtual key in to a real key event, if needed */
/* Returns non-zero when the event should be consumed */
static int vk_modify(struct ev *e, struct input_event *ev)
{
int i;
int x, y;
if (ev->type == EV_KEY) {
if (ev->code == BTN_TOUCH)
e->p.pressed = ev->value;
return 0;
}
if (ev->type == EV_ABS) {
switch (ev->code) {
case ABS_X:
e->p.x = ev->value;
return !vk_inside_display(e->p.x, &e->p.xi, gr_fb_width());
case ABS_Y:
e->p.y = ev->value;
return !vk_inside_display(e->p.y, &e->p.yi, gr_fb_height());
case ABS_MT_POSITION_X:
if (e->mt_idx) return 1;
e->mt_p.x = ev->value;
return !vk_inside_display(e->mt_p.x, &e->mt_p.xi, gr_fb_width());
case ABS_MT_POSITION_Y:
if (e->mt_idx) return 1;
e->mt_p.y = ev->value;
return !vk_inside_display(e->mt_p.y, &e->mt_p.yi, gr_fb_height());
case ABS_MT_TOUCH_MAJOR:
if (e->mt_idx) return 1;
if (e->sent)
e->mt_p.pressed = (ev->value > 0);
else
e->mt_p.pressed = (ev->value > PRESS_THRESHHOLD);
return 0;
}
return 0;
}
if (ev->type != EV_SYN)
return 0;
if (ev->code == SYN_MT_REPORT) {
/* Ignore the rest of the points */
++e->mt_idx;
return 1;
}
if (ev->code != SYN_REPORT)
return 0;
/* Report complete */
e->mt_idx = 0;
if (!e->p.pressed && !e->mt_p.pressed) {
/* No touch */
e->sent = 0;
return 0;
}
if (!(e->p.pressed && vk_tp_to_screen(&e->p, &x, &y)) &&
!(e->mt_p.pressed && vk_tp_to_screen(&e->mt_p, &x, &y))) {
/* No touch inside vk area */
return 0;
}
if (e->sent) {
/* We've already sent a fake key for this touch */
return 1;
}
/* The screen is being touched on the vk area */
e->sent = 1;
for (i = 0; i < e->vk_count; ++i) {
int xd = ABS(e->vks[i].centerx - x);
int yd = ABS(e->vks[i].centery - y);
if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) {
/* Fake a key event */
ev->type = EV_KEY;
ev->code = e->vks[i].scancode;
ev->value = 1;
vibrate(VIBRATOR_TIME_MS);
return 0;
}
}
return 1;
}
int ev_get(struct input_event *ev, unsigned dont_wait)
{
int r;
@ -72,7 +346,10 @@ int ev_get(struct input_event *ev, unsigned dont_wait)
for(n = 0; n < ev_count; n++) {
if(ev_fds[n].revents & POLLIN) {
r = read(ev_fds[n].fd, ev, sizeof(*ev));
if(r == sizeof(*ev)) return 0;
if(r == sizeof(*ev)) {
if (!vk_modify(&evs[n], ev))
return 0;
}
}
}
}

15
minui/font_7x16.h Normal file

File diff suppressed because one or more lines are too long

View File

@ -29,7 +29,12 @@
#include <pixelflinger/pixelflinger.h>
#include "font_10x18.h"
#ifndef BOARD_LDPI_RECOVERY
#include "font_10x18.h"
#else
#include "font_7x16.h"
#endif
#include "minui.h"
typedef struct {

View File

@ -97,9 +97,10 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
int color_type = info_ptr->color_type;
int bit_depth = info_ptr->bit_depth;
int channels = info_ptr->channels;
if (bit_depth != 8 || (channels != 3 && channels != 4) ||
(color_type != PNG_COLOR_TYPE_RGB &&
color_type != PNG_COLOR_TYPE_RGBA)) {
if (!(bit_depth == 8 &&
((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
(channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
(channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE)))) {
return -7;
goto exit;
}
@ -118,9 +119,13 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
surface->format = (channels == 3) ?
GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
}
int y;
if (channels == 3) {
for (y = 0; y < height; ++y) {
for (y = 0; y < (int)height; ++y) {
unsigned char* pRow = pData + y * stride;
png_read_row(png_ptr, pRow, NULL);
@ -139,7 +144,7 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
}
}
} else {
for (y = 0; y < height; ++y) {
for (y = 0; y < (int)height; ++y) {
unsigned char* pRow = pData + y * stride;
png_read_row(png_ptr, pRow, NULL);
}

View File

@ -810,6 +810,43 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
return true;
}
typedef struct {
unsigned char* buffer;
long len;
} BufferExtractCookie;
static bool bufferProcessFunction(const unsigned char *data, int dataLen,
void *cookie) {
BufferExtractCookie *bec = (BufferExtractCookie*)cookie;
memmove(bec->buffer, data, dataLen);
bec->buffer += dataLen;
bec->len -= dataLen;
return true;
}
/*
* Uncompress "pEntry" in "pArchive" to buffer, which must be large
* enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
*/
bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
const ZipEntry *pEntry, unsigned char *buffer)
{
BufferExtractCookie bec;
bec.buffer = buffer;
bec.len = mzGetZipEntryUncompLen(pEntry);
bool ret = mzProcessZipEntryContents(pArchive, pEntry,
bufferProcessFunction, (void*)&bec);
if (!ret || bec.len != 0) {
LOGE("Can't extract entry to memory buffer.\n");
return false;
}
return true;
}
/* Helper state to make path translation easier and less malloc-happy.
*/
typedef struct {

View File

@ -168,6 +168,13 @@ bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry);
bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
const ZipEntry *pEntry, int fd);
/*
* Inflate and write an entry to a memory buffer, which must be long
* enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
*/
bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
const ZipEntry *pEntry, unsigned char* buffer);
/*
* Inflate all entries under zipDir to the directory specified by
* targetDir, which must exist and be a writable directory.

View File

@ -1,37 +0,0 @@
#!/sbin/sh
rm -f /etc/fstab
cat /proc/mtd | while read mtdentry
do
mtd=$(echo $mtdentry | awk '{print $1}')
mtd=$(echo $mtd | sed s/mtd//)
mtd=$(echo $mtd | sed s/://)
exist=$(ls -l /dev/block/mtdblock$mtd) 2> /dev/null
if [ -z "$exist" ]
then
continue
fi
partition=$(echo $mtdentry | awk '{print $4}')
partition=$(echo $partition | sed s/\"//g)
mount=$partition
type=
if [ "$partition" = "system" ]
then
type=yaffs2
elif [ "$partition" = "userdata" ]
then
type=yaffs2
mount=firstboot
elif [ "$partition" == "cache" ]
then
type=yaffs2
else
continue
fi
echo "/dev/block/mtdblock$mtd /$mount $type rw" >> /etc/fstab
done
echo "/dev/block/mmcblk1p1" /sdcard vfat rw >> /etc/fstab
echo "/dev/block/mmcblk1p2" /sd-ext auto rw >> /etc/fstab
echo "/dev/block/innersd0p5" /cache ext3 rw >> /etc/fstab
echo "/dev/block/innersd0p6" /data ext3 rw >> /etc/fstab

15
mmcutils/Android.mk Normal file
View File

@ -0,0 +1,15 @@
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_ARCH),arm)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
mmcutils.c
LOCAL_MODULE := libmmcutils
include $(BUILD_STATIC_LIBRARY)
endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR

591
mmcutils/mmcutils.c Normal file
View File

@ -0,0 +1,591 @@
/*
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h> // for _IOW, _IOR, mount()
#include "mmcutils.h"
unsigned ext3_count = 0;
char *ext3_partitions[] = {"system", "userdata", "cache", "NONE"};
unsigned vfat_count = 0;
char *vfat_partitions[] = {"modem", "NONE"};
struct MmcPartition {
char *device_index;
char *filesystem;
char *name;
unsigned dstatus;
unsigned dtype ;
unsigned dfirstsec;
unsigned dsize;
};
typedef struct {
MmcPartition *partitions;
int partitions_allocd;
int partition_count;
} MmcState;
static MmcState g_mmc_state = {
NULL, // partitions
0, // partitions_allocd
-1 // partition_count
};
#define MMC_DEVICENAME "/dev/block/mmcblk0"
static void
mmc_partition_name (MmcPartition *mbr, unsigned int type) {
switch(type)
{
char name[64];
case MMC_BOOT_TYPE:
sprintf(name,"boot");
mbr->name = strdup(name);
break;
case MMC_RECOVERY_TYPE:
sprintf(name,"recovery");
mbr->name = strdup(name);
break;
case MMC_EXT3_TYPE:
if (strcmp("NONE", ext3_partitions[ext3_count])) {
strcpy((char *)name,(const char *)ext3_partitions[ext3_count]);
mbr->name = strdup(name);
ext3_count++;
}
mbr->filesystem = strdup("ext3");
break;
case MMC_VFAT_TYPE:
if (strcmp("NONE", vfat_partitions[vfat_count])) {
strcpy((char *)name,(const char *)vfat_partitions[vfat_count]);
mbr->name = strdup(name);
vfat_count++;
}
mbr->filesystem = strdup("vfat");
break;
};
}
static int
mmc_read_mbr (const char *device, MmcPartition *mbr) {
FILE *fd;
unsigned char buffer[512];
int idx, i;
unsigned mmc_partition_count = 0;
unsigned int dtype;
unsigned int dfirstsec;
unsigned int EBR_first_sec;
unsigned int EBR_current_sec;
int ret = -1;
fd = fopen(device, "r");
if(fd == NULL)
{
printf("Can't open device: \"%s\"\n", device);
goto ERROR2;
}
if ((fread(buffer, 512, 1, fd)) != 1)
{
printf("Can't read device: \"%s\"\n", device);
goto ERROR1;
}
/* Check to see if signature exists */
if ((buffer[TABLE_SIGNATURE] != 0x55) || \
(buffer[TABLE_SIGNATURE + 1] != 0xAA))
{
printf("Incorrect mbr signatures!\n");
goto ERROR1;
}
idx = TABLE_ENTRY_0;
for (i = 0; i < 4; i++)
{
char device_index[128];
mbr[mmc_partition_count].dstatus = \
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
mbr[mmc_partition_count].dtype = \
buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
mbr[mmc_partition_count].dfirstsec = \
GET_LWORD_FROM_BYTE(&buffer[idx + \
i * TABLE_ENTRY_SIZE + \
OFFSET_FIRST_SEC]);
mbr[mmc_partition_count].dsize = \
GET_LWORD_FROM_BYTE(&buffer[idx + \
i * TABLE_ENTRY_SIZE + \
OFFSET_SIZE]);
dtype = mbr[mmc_partition_count].dtype;
dfirstsec = mbr[mmc_partition_count].dfirstsec;
mmc_partition_name(&mbr[mmc_partition_count], \
mbr[mmc_partition_count].dtype);
sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
mbr[mmc_partition_count].device_index = strdup(device_index);
mmc_partition_count++;
if (mmc_partition_count == MAX_PARTITIONS)
goto SUCCESS;
}
/* See if the last partition is EBR, if not, parsing is done */
if (dtype != 0x05)
{
goto SUCCESS;
}
EBR_first_sec = dfirstsec;
EBR_current_sec = dfirstsec;
fseek (fd, (EBR_first_sec * 512), SEEK_SET);
if ((fread(buffer, 512, 1, fd)) != 1)
goto ERROR1;
/* Loop to parse the EBR */
for (i = 0;; i++)
{
char device_index[128];
if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA))
{
break;
}
mbr[mmc_partition_count].dstatus = \
buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
mbr[mmc_partition_count].dtype = \
buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
mbr[mmc_partition_count].dfirstsec = \
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
OFFSET_FIRST_SEC]) + \
EBR_current_sec;
mbr[mmc_partition_count].dsize = \
GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
OFFSET_SIZE]);
mmc_partition_name(&mbr[mmc_partition_count], \
mbr[mmc_partition_count].dtype);
sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
mbr[mmc_partition_count].device_index = strdup(device_index);
mmc_partition_count++;
if (mmc_partition_count == MAX_PARTITIONS)
goto SUCCESS;
dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
if(dfirstsec == 0)
{
/* Getting to the end of the EBR tables */
break;
}
/* More EBR to follow - read in the next EBR sector */
fseek (fd, ((EBR_first_sec + dfirstsec) * 512), SEEK_SET);
if ((fread(buffer, 512, 1, fd)) != 1)
goto ERROR1;
EBR_current_sec = EBR_first_sec + dfirstsec;
}
SUCCESS:
ret = mmc_partition_count;
ERROR1:
fclose(fd);
ERROR2:
return ret;
}
int
mmc_scan_partitions() {
int i;
ssize_t nbytes;
if (g_mmc_state.partitions == NULL) {
const int nump = MAX_PARTITIONS;
MmcPartition *partitions = malloc(nump * sizeof(*partitions));
if (partitions == NULL) {
errno = ENOMEM;
return -1;
}
g_mmc_state.partitions = partitions;
g_mmc_state.partitions_allocd = nump;
memset(partitions, 0, nump * sizeof(*partitions));
}
g_mmc_state.partition_count = 0;
ext3_count = 0;
vfat_count = 0;
/* Initialize all of the entries to make things easier later.
* (Lets us handle sparsely-numbered partitions, which
* may not even be possible.)
*/
for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
MmcPartition *p = &g_mmc_state.partitions[i];
if (p->device_index != NULL) {
free(p->device_index);
p->device_index = NULL;
}
if (p->name != NULL) {
free(p->name);
p->name = NULL;
}
if (p->filesystem != NULL) {
free(p->filesystem);
p->filesystem = NULL;
}
}
g_mmc_state.partition_count = mmc_read_mbr(MMC_DEVICENAME, g_mmc_state.partitions);
if(g_mmc_state.partition_count == -1)
{
printf("Error in reading mbr!\n");
// keep "partitions" around so we can free the names on a rescan.
g_mmc_state.partition_count = -1;
}
return g_mmc_state.partition_count;
}
const MmcPartition *
mmc_find_partition_by_name(const char *name)
{
if (g_mmc_state.partitions != NULL) {
int i;
for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
MmcPartition *p = &g_mmc_state.partitions[i];
if (p->device_index !=NULL && p->name != NULL) {
if (strcmp(p->name, name) == 0) {
return p;
}
}
}
}
return NULL;
}
#define MKE2FS_BIN "/sbin/mke2fs"
#define TUNE2FS_BIN "/sbin/tune2fs"
#define E2FSCK_BIN "/sbin/e2fsck"
static int
run_exec_process ( char **argv) {
pid_t pid;
int status;
pid = fork();
if (pid == 0) {
execv(argv[0], argv);
fprintf(stderr, "E:Can't run (%s)\n",strerror(errno));
_exit(-1);
}
waitpid(pid, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
return 1;
}
return 0;
}
int
mmc_format_ext3 (MmcPartition *partition) {
char device[128];
strcpy(device, partition->device_index);
// Run mke2fs
char *const mke2fs[] = {MKE2FS_BIN, "-j", device, NULL};
if(run_exec_process(mke2fs))
return -1;
// Run tune2fs
char *const tune2fs[] = {TUNE2FS_BIN, "-j", "-C", "1", device, NULL};
if(run_exec_process(tune2fs))
return -1;
// Run e2fsck
char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL};
if(run_exec_process(e2fsck))
return -1;
return 0;
}
int
mmc_mount_partition(const MmcPartition *partition, const char *mount_point,
int read_only)
{
const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
char devname[128];
int rv = -1;
strcpy(devname, partition->device_index);
if (partition->filesystem == NULL) {
printf("Null filesystem!\n");
return rv;
}
if (!read_only) {
rv = mount(devname, mount_point, partition->filesystem, flags, NULL);
}
if (read_only || rv < 0) {
rv = mount(devname, mount_point, partition->filesystem, flags | MS_RDONLY, 0);
if (rv < 0) {
printf("Failed to mount %s on %s: %s\n",
devname, mount_point, strerror(errno));
} else {
printf("Mount %s on %s read-only\n", devname, mount_point);
}
}
return rv;
}
int
mmc_raw_copy (const MmcPartition *partition, char *in_file) {
int ch;
FILE *in;
FILE *out;
int val = 0;
char buf[512];
unsigned sz = 0;
unsigned i;
int ret = -1;
char *out_file = partition->device_index;
in = fopen ( in_file, "r" );
if (in == NULL)
goto ERROR3;
out = fopen ( out_file, "w" );
if (out == NULL)
goto ERROR2;
fseek(in, 0L, SEEK_END);
sz = ftell(in);
fseek(in, 0L, SEEK_SET);
if (sz % 512)
{
while ( ( ch = fgetc ( in ) ) != EOF )
fputc ( ch, out );
}
else
{
for (i=0; i< (sz/512); i++)
{
if ((fread(buf, 512, 1, in)) != 1)
goto ERROR1;
if ((fwrite(buf, 512, 1, out)) != 1)
goto ERROR1;
}
}
fsync(out);
ret = 0;
ERROR1:
fclose ( out );
ERROR2:
fclose ( in );
ERROR3:
return ret;
}
// TODO: refactor this to not be a giant copy paste mess
int
mmc_raw_dump (const MmcPartition *partition, char *out_file) {
int ch;
FILE *in;
FILE *out;
int val = 0;
char buf[512];
unsigned sz = 0;
unsigned i;
int ret = -1;
char *in_file = partition->device_index;
in = fopen ( in_file, "r" );
if (in == NULL)
goto ERROR3;
out = fopen ( out_file, "w" );
if (out == NULL)
goto ERROR2;
fseek(in, 0L, SEEK_END);
sz = ftell(in);
fseek(in, 0L, SEEK_SET);
if (sz % 512)
{
while ( ( ch = fgetc ( in ) ) != EOF )
fputc ( ch, out );
}
else
{
for (i=0; i< (sz/512); i++)
{
if ((fread(buf, 512, 1, in)) != 1)
goto ERROR1;
if ((fwrite(buf, 512, 1, out)) != 1)
goto ERROR1;
}
}
fsync(out);
ret = 0;
ERROR1:
fclose ( out );
ERROR2:
fclose ( in );
ERROR3:
return ret;
}
int
mmc_raw_read (const MmcPartition *partition, char *data, int data_size) {
int ch;
FILE *in;
int val = 0;
char buf[512];
unsigned sz = 0;
unsigned i;
int ret = -1;
char *in_file = partition->device_index;
in = fopen ( in_file, "r" );
if (in == NULL)
goto ERROR3;
fseek(in, 0L, SEEK_END);
sz = ftell(in);
fseek(in, 0L, SEEK_SET);
fread(data, data_size, 1, in);
ret = 0;
ERROR1:
ERROR2:
fclose ( in );
ERROR3:
return ret;
}
int
mmc_raw_write (const MmcPartition *partition, char *data, int data_size) {
int ch;
FILE *out;
int val = 0;
char buf[512];
unsigned sz = 0;
unsigned i;
int ret = -1;
char *out_file = partition->device_index;
out = fopen ( out_file, "w" );
if (out == NULL)
goto ERROR3;
fwrite(data, data_size, 1, out);
ret = 0;
ERROR1:
ERROR2:
fclose ( out );
ERROR3:
return ret;
}
int cmd_mmc_restore_raw_partition(const char *partition, const char *filename)
{
mmc_scan_partitions();
const MmcPartition *p;
p = mmc_find_partition_by_name(partition);
if (p == NULL)
return -1;
return mmc_raw_copy(p, filename);
}
int cmd_mmc_backup_raw_partition(const char *partition, const char *filename)
{
mmc_scan_partitions();
const MmcPartition *p;
p = mmc_find_partition_by_name(partition);
if (p == NULL)
return -1;
return mmc_raw_dump(p, filename);
}
int cmd_mmc_erase_raw_partition(const char *partition)
{
mmc_scan_partitions();
const MmcPartition *p;
p = mmc_find_partition_by_name(partition);
if (p == NULL)
return -1;
// TODO: implement raw wipe
return 0;
}
int cmd_mmc_erase_partition(const char *partition, const char *filesystem)
{
mmc_scan_partitions();
const MmcPartition *p;
p = mmc_find_partition_by_name(partition);
if (p == NULL)
return -1;
return mmc_format_ext3 (p);
}
int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
{
mmc_scan_partitions();
const MmcPartition *p;
p = mmc_find_partition_by_name(partition);
if (p == NULL)
return -1;
return mmc_mount_partition(p, mount_point, read_only);
}
int cmd_mmc_get_partition_device(const char *partition, char *device)
{
mmc_scan_partitions();
const MmcPartition *p;
p = mmc_find_partition_by_name(partition);
if (p == NULL)
return -1;
strcpy(device, p->device_index);
return 0;
}

88
mmcutils/mmcutils.h Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MMCUTILS_H_
#define MMCUTILS_H_
/* Some useful define used to access the MBR/EBR table */
#define BLOCK_SIZE 0x200
#define TABLE_ENTRY_0 0x1BE
#define TABLE_ENTRY_1 0x1CE
#define TABLE_ENTRY_2 0x1DE
#define TABLE_ENTRY_3 0x1EE
#define TABLE_SIGNATURE 0x1FE
#define TABLE_ENTRY_SIZE 0x010
#define OFFSET_STATUS 0x00
#define OFFSET_TYPE 0x04
#define OFFSET_FIRST_SEC 0x08
#define OFFSET_SIZE 0x0C
#define COPYBUFF_SIZE (1024 * 16)
#define BINARY_IN_TABLE_SIZE (16 * 512)
#define MAX_FILE_ENTRIES 20
#define MMC_BOOT_TYPE 0x48
#define MMC_SYSTEM_TYPE 0x82
#define MMC_USERDATA_TYPE 0x83
#define MMC_RECOVERY_TYPE 0x71
#define MMC_RCA 2
#define MAX_PARTITIONS 64
#define GET_LWORD_FROM_BYTE(x) ((unsigned)*(x) | \
((unsigned)*((x)+1) << 8) | \
((unsigned)*((x)+2) << 16) | \
((unsigned)*((x)+3) << 24))
#define PUT_LWORD_TO_BYTE(x, y) do{*(x) = (y) & 0xff; \
*((x)+1) = ((y) >> 8) & 0xff; \
*((x)+2) = ((y) >> 16) & 0xff; \
*((x)+3) = ((y) >> 24) & 0xff; }while(0)
#define GET_PAR_NUM_FROM_POS(x) ((((x) & 0x0000FF00) >> 8) + ((x) & 0x000000FF))
#define MMC_BOOT_TYPE 0x48
#define MMC_EXT3_TYPE 0x83
#define MMC_VFAT_TYPE 0xC
typedef struct MmcPartition MmcPartition;
/* Functions */
int mmc_scan_partitions();
const MmcPartition *mmc_find_partition_by_name(const char *name);
int mmc_format_ext3 (MmcPartition *partition);
int mmc_mount_partition(const MmcPartition *partition, const char *mount_point, \
int read_only);
int mmc_raw_copy (const MmcPartition *partition, char *in_file);
int mmc_raw_read (const MmcPartition *partition, char *data, int data_size);
int mmc_raw_write (const MmcPartition *partition, char *data, int data_size);
#endif // MMCUTILS_H_

View File

@ -5,88 +5,12 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
mtdutils.c \
mounts.c
mtdutils.c
LOCAL_MODULE := libmtdutils
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := flash_image
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libmtdutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := dump_image.c
LOCAL_MODULE := dump_image
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libmtdutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := erase_image.c
LOCAL_MODULE := erase_image
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libmtdutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := libflash_image
LOCAL_CFLAGS += -Dmain=flash_image_main
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := dump_image.c
LOCAL_MODULE := libdump_image
LOCAL_CFLAGS += -Dmain=dump_image_main
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := erase_image.c
LOCAL_MODULE := liberase_image
LOCAL_CFLAGS += -Dmain=erase_image_main
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := dump_image.c
LOCAL_MODULE := utility_dump_image
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := dump_image
LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := utility_flash_image
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := flash_image
LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := erase_image.c
LOCAL_MODULE := utility_erase_image
LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := erase_image
LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR

View File

@ -1,17 +0,0 @@
#undef main
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
if (strstr(argv[0], "flash_image") != NULL)
return flash_image_main(argc, argv);
if (strstr(argv[0], "dump_image") != NULL)
return dump_image_main(argc, argv);
if (strstr(argv[0], "mkyaffs2image") != NULL)
return mkyaffs2image_main(argc, argv);
if (strstr(argv[0], "unyaffs") != NULL)
return unyaffs_main(argc, argv);
return 0;
}

View File

@ -1,8 +0,0 @@
#ifndef DUMP_IMAGE_H
#define DUMP_IMAGE_H
typedef void (*dump_image_callback) (int partition_dumped, int partition_size);
int dump_image(char* partition_name, char* filename, dump_image_callback callback);
#endif

View File

@ -28,13 +28,6 @@
#include "mtdutils.h"
struct MtdPartition {
int device_index;
unsigned int size;
unsigned int erase_size;
char *name;
};
struct MtdReadContext {
const MtdPartition *partition;
char *buffer;
@ -344,7 +337,7 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
read += ctx->partition->erase_size;
}
if (read >= len) {
if (read >= (int)len) {
return read;
}
@ -564,3 +557,295 @@ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
}
return pos;
}
#define BLOCK_SIZE 2048
#define SPARE_SIZE (BLOCK_SIZE >> 5)
#define HEADER_SIZE 2048
int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
{
const MtdPartition *ptn;
MtdWriteContext *write;
void *data;
unsigned sz;
if (mtd_scan_partitions() <= 0)
{
printf("error scanning partitions");
return -1;
}
const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
if (partition == NULL)
{
printf("can't find %s partition", partition_name);
return -1;
}
// If the first part of the file matches the partition, skip writing
int fd = open(filename, O_RDONLY);
if (fd < 0)
{
printf("error opening %s", filename);
return -1;
}
char header[HEADER_SIZE];
int headerlen = read(fd, header, sizeof(header));
if (headerlen <= 0)
{
printf("error reading %s header", filename);
return -1;
}
MtdReadContext *in = mtd_read_partition(partition);
if (in == NULL) {
printf("error opening %s: %s\n", partition, strerror(errno));
// just assume it needs re-writing
} else {
char check[HEADER_SIZE];
int checklen = mtd_read_data(in, check, sizeof(check));
if (checklen <= 0) {
printf("error reading %s: %s\n", partition_name, strerror(errno));
// just assume it needs re-writing
} else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
printf("header is the same, not flashing %s\n", partition_name);
return 0;
}
mtd_read_close(in);
}
// Skip the header (we'll come back to it), write everything else
printf("flashing %s from %s\n", partition_name, filename);
MtdWriteContext *out = mtd_write_partition(partition);
if (out == NULL)
{
printf("error writing %s", partition_name);
return -1;
}
char buf[HEADER_SIZE];
memset(buf, 0, headerlen);
int wrote = mtd_write_data(out, buf, headerlen);
if (wrote != headerlen)
{
printf("error writing %s", partition_name);
return -1;
}
int len;
while ((len = read(fd, buf, sizeof(buf))) > 0) {
wrote = mtd_write_data(out, buf, len);
if (wrote != len)
{
printf("error writing %s", partition_name);
return -1;
}
}
if (len < 0)
{
printf("error reading %s", filename);
return -1;
}
if (mtd_write_close(out))
{
printf("error closing %s", partition_name);
return -1;
}
// Now come back and write the header last
out = mtd_write_partition(partition);
if (out == NULL)
{
printf("error re-opening %s", partition_name);
return -1;
}
wrote = mtd_write_data(out, header, headerlen);
if (wrote != headerlen)
{
printf("error re-writing %s", partition_name);
return -1;
}
// Need to write a complete block, so write the rest of the first block
size_t block_size;
if (mtd_partition_info(partition, NULL, &block_size, NULL))
{
printf("error getting %s block size", partition_name);
return -1;
}
if (lseek(fd, headerlen, SEEK_SET) != headerlen)
{
printf("error rewinding %s", filename);
return -1;
}
int left = block_size - headerlen;
while (left < 0) left += block_size;
while (left > 0) {
len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
if (len <= 0){
printf("error reading %s", filename);
return -1;
}
if (mtd_write_data(out, buf, len) != len)
{
printf("error writing %s", partition_name);
return -1;
}
left -= len;
}
if (mtd_write_close(out))
{
printf("error closing %s", partition_name);
return -1;
}
return 0;
}
int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
{
MtdReadContext *in;
const MtdPartition *partition;
char buf[BLOCK_SIZE + SPARE_SIZE];
size_t partition_size;
size_t read_size;
size_t total;
int fd;
int wrote;
int len;
if (mtd_scan_partitions() <= 0)
{
printf("error scanning partitions");
return -1;
}
partition = mtd_find_partition_by_name(partition_name);
if (partition == NULL)
{
printf("can't find %s partition", partition_name);
return -1;
}
if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
printf("can't get info of partition %s", partition_name);
return -1;
}
if (!strcmp(filename, "-")) {
fd = fileno(stdout);
}
else {
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
}
if (fd < 0)
{
printf("error opening %s", filename);
return -1;
}
in = mtd_read_partition(partition);
if (in == NULL) {
close(fd);
unlink(filename);
printf("error opening %s: %s\n", partition_name, strerror(errno));
return -1;
}
total = 0;
while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
wrote = write(fd, buf, len);
if (wrote != len) {
close(fd);
unlink(filename);
printf("error writing %s", filename);
return -1;
}
total += BLOCK_SIZE;
}
mtd_read_close(in);
if (close(fd)) {
unlink(filename);
printf("error closing %s", filename);
return -1;
}
return 0;
}
int cmd_mtd_erase_raw_partition(const char *partition_name)
{
MtdWriteContext *out;
size_t erased;
size_t total_size;
size_t erase_size;
if (mtd_scan_partitions() <= 0)
{
printf("error scanning partitions");
return -1;
}
const MtdPartition *p = mtd_find_partition_by_name(partition_name);
if (p == NULL)
{
printf("can't find %s partition", partition_name);
return -1;
}
out = mtd_write_partition(p);
if (out == NULL)
{
printf("could not estabilish write context for %s", partition_name);
return -1;
}
// do the actual erase, -1 = full partition erase
erased = mtd_erase_blocks(out, -1);
// erased = bytes erased, if zero, something borked
if (!erased)
{
printf("error erasing %s", partition_name);
return -1;
}
return 0;
}
int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
{
return cmd_mtd_erase_raw_partition(partition);
}
int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
{
mtd_scan_partitions();
const MtdPartition *p;
p = mtd_find_partition_by_name(partition);
if (p == NULL) {
return -1;
}
return mtd_mount_partition(p, mount_point, filesystem, read_only);
}
int cmd_mtd_get_partition_device(const char *partition, char *device)
{
mtd_scan_partitions();
MtdPartition *p = mtd_find_partition_by_name(partition);
if (p == NULL)
return -1;
sprintf(device, "/dev/block/mtdblock%d", p->device_index);
return 0;
}

View File

@ -52,4 +52,11 @@ off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */
off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
int mtd_write_close(MtdWriteContext *);
struct MtdPartition {
int device_index;
unsigned int size;
unsigned int erase_size;
char *name;
};
#endif // MTDUTILS_H_

View File

@ -33,7 +33,6 @@
#include "commands.h"
#include "amend/amend.h"
#include "mtdutils/dump_image.h"
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
@ -75,7 +74,7 @@ void compute_directory_stats(char* directory)
ui_show_progress(1, 0);
}
int nandroid_backup_partition(const char* backup_path, char* root) {
int nandroid_backup_partition_extended(const char* backup_path, char* root, int umount_when_finished) {
int ret = 0;
char mount_point[PATH_MAX];
translate_root_path(root, mount_point, PATH_MAX);
@ -96,7 +95,9 @@ int nandroid_backup_partition(const char* backup_path, char* root) {
char tmp[PATH_MAX];
sprintf(tmp, "%s/%s.img", backup_path, name);
ret = mkyaffs2image(mount_point, tmp, 0, callback);
ensure_root_path_unmounted(root);
if (umount_when_finished) {
ensure_root_path_unmounted(root);
}
if (0 != ret) {
ui_print("Error while making a yaffs2 image of %s!\n", mount_point);
return ret;
@ -104,6 +105,10 @@ int nandroid_backup_partition(const char* backup_path, char* root) {
return 0;
}
int nandroid_backup_partition(const char* backup_path, char* root) {
return nandroid_backup_partition_extended(backup_path, root, 1);
}
int nandroid_backup(const char* backup_path)
{
ui_set_background(BACKGROUND_ICON_INSTALLING);
@ -127,17 +132,19 @@ int nandroid_backup(const char* backup_path)
sprintf(tmp, "mkdir -p %s", backup_path);
__system(tmp);
#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
ui_print("Backing up boot...\n");
sprintf(tmp, "%s/%s", backup_path, "boot.img");
ret = dump_image("boot", tmp, NULL);
ret = backup_raw_partition("boot", tmp);
if (0 != ret)
return print_and_error("Error while dumping boot image!\n");
ui_print("Backing up recovery...\n");
sprintf(tmp, "%s/%s", backup_path, "recovery.img");
ret = dump_image("recovery", tmp, NULL);
ret = backup_raw_partition("recovery", tmp);
if (0 != ret)
return print_and_error("Error while dumping boot image!\n");
return print_and_error("Error while dumping recovery image!\n");
#endif
if (0 != (ret = nandroid_backup_partition(backup_path, "SYSTEM:")))
return ret;
@ -145,22 +152,37 @@ int nandroid_backup(const char* backup_path)
if (0 != (ret = nandroid_backup_partition(backup_path, "DATA:")))
return ret;
if (0 != (ret = nandroid_backup_partition(backup_path, "CACHE:")))
#ifdef BOARD_HAS_DATADATA
if (0 != (ret = nandroid_backup_partition(backup_path, "DATADATA:")))
return ret;
#endif
struct stat st;
if (0 != stat("/dev/block/mmcblk0p2", &st))
if (0 != stat("/sdcard/.android_secure", &st))
{
ui_print("No /sdcard/.android_secure found. Skipping backup of applications on external storage.\n");
}
else
{
if (0 != (ret = nandroid_backup_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
return ret;
}
if (0 != (ret = nandroid_backup_partition_extended(backup_path, "CACHE:", 0)))
return ret;
if (0 != stat(BOARD_SDEXT_DEVICE, &st))
{
ui_print("No sd-ext found. Skipping backup of sd-ext.\n");
}
else
{
if (0 != ensure_root_path_mounted("SDEXT:"))
ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.");
ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.\n");
else if (0 != (ret = nandroid_backup_partition(backup_path, "SDEXT:")))
return ret;
}
ui_print("Generating md5 sum...\n");
sprintf(tmp, "nandroid-md5.sh %s", backup_path);
if (0 != (ret = __system(tmp))) {
@ -177,23 +199,40 @@ int nandroid_backup(const char* backup_path)
typedef int (*format_function)(char* root);
int nandroid_restore_partition(const char* backup_path, const char* root) {
static void ensure_directory(const char* dir) {
char tmp[PATH_MAX];
sprintf(tmp, "mkdir -p %s", dir);
__system(tmp);
}
int nandroid_restore_partition_extended(const char* backup_path, const char* root, int umount_when_finished) {
int ret = 0;
char mount_point[PATH_MAX];
translate_root_path(root, mount_point, PATH_MAX);
char* name = basename(mount_point);
char tmp[PATH_MAX];
sprintf(tmp, "%s/%s.img", backup_path, name);
struct stat file_info;
if (0 != (ret = statfs(tmp, &file_info))) {
ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point);
return 0;
}
ensure_directory(mount_point);
unyaffs_callback callback = NULL;
if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
callback = yaffs_callback;
}
ui_print("Restoring %s...\n", name);
/*
if (0 != (ret = ensure_root_path_unmounted(root))) {
ui_print("Can't unmount %s!\n", mount_point);
return ret;
}
*/
if (0 != (ret = format_root_device(root))) {
ui_print("Error while formatting %s!\n", root);
return ret;
@ -204,19 +243,22 @@ int nandroid_restore_partition(const char* backup_path, const char* root) {
return ret;
}
char tmp[PATH_MAX];
sprintf(tmp, "%s/%s.img", backup_path, name);
if (0 != (ret = unyaffs(tmp, mount_point, callback))) {
ui_print("Error while restoring %s!\n", mount_point);
return ret;
}
if (0 != strcmp(root, "CACHE")) {
if (umount_when_finished) {
ensure_root_path_unmounted(root);
}
return 0;
}
int nandroid_restore_partition(const char* backup_path, const char* root) {
return nandroid_restore_partition_extended(backup_path, root, 1);
}
int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext)
{
ui_set_background(BACKGROUND_ICON_INSTALLING);
@ -234,37 +276,40 @@ int nandroid_restore(const char* backup_path, int restore_boot, int restore_syst
return print_and_error("MD5 mismatch!\n");
int ret;
#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
if (restore_boot)
{
ui_print("Erasing boot before restore...\n");
if (0 != (ret = format_root_device("BOOT:")))
return print_and_error("Error while formatting BOOT:!\n");
sprintf(tmp, "flash_image boot %s/boot.img", backup_path);
sprintf(tmp, "%s/boot.img", backup_path);
ui_print("Restoring boot image...\n");
if (0 != (ret = __system(tmp))) {
if (0 != (ret = restore_raw_partition("boot", tmp))) {
ui_print("Error while flashing boot image!");
return ret;
}
}
#endif
if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "SYSTEM:")))
return ret;
if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATA:")))
return ret;
#ifdef BOARD_HAS_DATADATA
if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATADATA:")))
return ret;
#endif
if (restore_cache && 0 != (ret = nandroid_restore_partition(backup_path, "CACHE:")))
if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
return ret;
if (restore_sdext)
{
struct statfs s;
sprintf(tmp, "%s/sd-ext.img", backup_path);
if (0 != (ret = statfs(tmp, &s)))
ui_print("sd-ext.img not found. Skipping restore of /sd-ext.");
else if (0 != (ret = nandroid_restore_partition(backup_path, "SDEXT:")))
return ret;
}
if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "CACHE:", 0)))
return ret;
if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "SDEXT:")))
return ret;
sync();
ui_set_background(BACKGROUND_ICON_NONE);
@ -272,3 +317,51 @@ int nandroid_restore(const char* backup_path, int restore_boot, int restore_syst
ui_print("\nRestore complete!\n");
return 0;
}
void nandroid_generate_timestamp_path(char* backup_path)
{
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
if (tmp == NULL)
{
struct timeval tp;
gettimeofday(&tp, NULL);
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
}
else
{
strftime(backup_path, PATH_MAX, "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
}
}
int nandroid_usage()
{
printf("Usage: nandroid backup\n");
printf("Usage: nandroid restore <directory>\n");
return 1;
}
int nandroid_main(int argc, char** argv)
{
if (argc > 3 || argc < 2)
return nandroid_usage();
if (strcmp("backup", argv[1]) == 0)
{
if (argc != 2)
return nandroid_usage();
char backup_path[PATH_MAX];
nandroid_generate_timestamp_path(backup_path);
return nandroid_backup(backup_path);
}
if (strcmp("restore", argv[1]) == 0)
{
if (argc != 3)
return nandroid_usage();
return nandroid_restore(argv[2], 1, 1, 1, 1, 1);
}
return nandroid_usage();
}

View File

@ -4,5 +4,6 @@
int nandroid_main(int argc, char** argv);
int nandroid_backup(const char* backup_path);
int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext);
void nandroid_generate_timestamp_path(char* backup_path);
#endif

72
reboot.c Normal file
View File

@ -0,0 +1,72 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/reboot.h>
#include <unistd.h>
#include <cutils/properties.h>
int reboot_main(int argc, char *argv[])
{
int ret;
int nosync = 0;
int poweroff = 0;
int force = 0;
opterr = 0;
do {
int c;
c = getopt(argc, argv, "npf");
if (c == EOF) {
break;
}
switch (c) {
case 'n':
nosync = 1;
break;
case 'p':
poweroff = 1;
break;
case 'f':
force = 1;
break;
case '?':
fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
exit(EXIT_FAILURE);
}
} while (1);
if(argc > optind + 1) {
fprintf(stderr, "%s: too many arguments\n", argv[0]);
exit(EXIT_FAILURE);
}
if(!nosync)
sync();
if(force || argc > optind) {
if(poweroff)
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
else if(argc > optind)
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]);
else
ret = reboot(RB_AUTOBOOT);
} else {
if(poweroff) {
property_set("ctl.start", "poweroff");
ret = 0;
} else {
property_set("ctl.start", "reboot");
ret = 0;
}
}
if(ret < 0) {
perror("reboot");
exit(EXIT_FAILURE);
}
fprintf(stderr, "reboot returned\n");
return 0;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +33,6 @@
#include "bootloader.h"
#include "common.h"
#include "cutils/properties.h"
#include "firmware.h"
#include "install.h"
#include "minui/minui.h"
#include "minzip/DirUtil.h"
@ -69,6 +69,7 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
* --update_package=root:path - verify install an OTA package file
* --wipe_data - erase user data (and cache), then reboot
* --wipe_cache - wipe cache (but not user data), then reboot
* --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
*
* After completing, we remove /cache/recovery/command and reboot.
* Arguments may also be supplied in the bootloader control block (BCB).
@ -113,6 +114,26 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
* 8g. finish_recovery() erases BCB
* -- after this, rebooting will (try to) restart the main system --
* 9. main() calls reboot() to boot main system
*
* ENCRYPTED FILE SYSTEMS ENABLE/DISABLE
* 1. user selects "enable encrypted file systems"
* 2. main system writes "--set_encrypted_filesystem=on|off" to
* /cache/recovery/command
* 3. main system reboots into recovery
* 4. get_args() writes BCB with "boot-recovery" and
* "--set_encrypted_filesystems=on|off"
* -- after this, rebooting will restart the transition --
* 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data
* Settings include: property to specify the Encrypted FS istatus and
* FS encryption key if enabled (not yet implemented)
* 6. erase_root() reformats /data
* 7. erase_root() reformats /cache
* 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data
* Settings include: property to specify the Encrypted FS status and
* FS encryption key if enabled (not yet implemented)
* 9. finish_recovery() erases BCB
* -- after this, rebooting will restart the main system --
* 10. main() calls reboot() to boot main system
*/
static const int MAX_ARG_LENGTH = 4096;
@ -157,7 +178,9 @@ static void
get_args(int *argc, char ***argv) {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
#ifndef BOARD_HAS_NO_MISC_PARTITION
get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
#endif
if (boot.command[0] != 0 && boot.command[0] != 255) {
LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
@ -214,30 +237,34 @@ get_args(int *argc, char ***argv) {
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
set_bootloader_message(&boot);
#endif
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
void
set_sdcard_update_bootloader_message()
{
set_sdcard_update_bootloader_message() {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
set_bootloader_message(&boot);
}
#endif
// clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read), and
// record any intent we were asked to communicate back to the system.
// this function is idempotent: call it as many times as you like.
static void
finish_recovery(const char *send_intent)
{
finish_recovery(const char *send_intent) {
// By this point, we're ready to return to the main system...
if (send_intent != NULL) {
FILE *fp = fopen_root_path(INTENT_FILE, "w");
if (fp != NULL) {
if (fp == NULL) {
LOGE("Can't open %s\n", INTENT_FILE);
} else {
fputs(send_intent, fp);
check_and_fclose(fp, INTENT_FILE);
}
@ -245,7 +272,9 @@ finish_recovery(const char *send_intent)
// Copy logs to cache so the system can find out what happened.
FILE *log = fopen_root_path(LOG_FILE, "a");
if (log != NULL) {
if (log == NULL) {
LOGE("Can't open %s\n", LOG_FILE);
} else {
FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
if (tmplog == NULL) {
LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
@ -260,10 +289,12 @@ finish_recovery(const char *send_intent)
check_and_fclose(log, LOG_FILE);
}
// Reset the bootloader message to revert to a normal main system boot.
#ifndef BOARD_HAS_NO_MISC_PARTITION
// Reset to mormal system boot so recovery won't cycle indefinitely.
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
set_bootloader_message(&boot);
#endif
// Remove the command file, so recovery won't repeat indefinitely.
char path[PATH_MAX] = "";
@ -277,8 +308,7 @@ finish_recovery(const char *send_intent)
}
static int
erase_root(const char *root)
{
erase_root(const char *root) {
ui_set_background(BACKGROUND_ICON_INSTALLING);
ui_show_indeterminate_progress();
ui_print("Formatting %s...\n", root);
@ -317,12 +347,19 @@ get_menu_selection(char** headers, char** items, int menu_only) {
int selected = 0;
int chosen_item = -1;
// Some users with dead enter keys need a way to turn on power to select.
// Jiggering across the wrapping menu is one "secret" way to enable it.
// We can't rely on /cache or /sdcard since they may not be available.
int wrap_count = 0;
while (chosen_item < 0 && chosen_item != GO_BACK) {
int key = ui_wait_key();
int visible = ui_text_visible();
int action = device_handle_key(key, visible);
int old_selected = selected;
if (action < 0) {
switch (action) {
case HIGHLIGHT_UP:
@ -335,11 +372,11 @@ get_menu_selection(char** headers, char** items, int menu_only) {
break;
case SELECT_ITEM:
chosen_item = selected;
#ifdef KEY_POWER_IS_SELECT_ITEM
if (chosen_item == item_count) {
chosen_item = GO_BACK;
if (ui_get_showing_back_button()) {
if (chosen_item == item_count) {
chosen_item = GO_BACK;
}
}
#endif
break;
case NO_ACTION:
break;
@ -350,6 +387,21 @@ get_menu_selection(char** headers, char** items, int menu_only) {
} else if (!menu_only) {
chosen_item = action;
}
if (abs(selected - old_selected) > 1) {
wrap_count++;
if (wrap_count == 3) {
wrap_count = 0;
if (ui_get_showing_back_button()) {
ui_print("Back menu button disabled.\n");
ui_set_showing_back_button(0);
}
else {
ui_print("Back menu button enabled.\n");
ui_set_showing_back_button(1);
}
}
}
}
ui_end_menu();
@ -392,16 +444,18 @@ wipe_data(int confirm) {
ui_print("\n-- Wiping data...\n");
device_wipe_data();
erase_root("DATA:");
#ifdef BOARD_HAS_DATADATA
erase_root("DATADATA:");
#endif
erase_root("CACHE:");
erase_root("SDEXT:");
erase_root("SDCARD:/.android_secure");
ui_print("Data wipe complete.\n");
}
static void
prompt_and_wait()
{
prompt_and_wait() {
char** headers = prepend_title(MENU_HEADERS);
ui_print(EXPAND(RECOVERY_VERSION)"\n");
for (;;) {
finish_recovery(NULL);
@ -426,27 +480,39 @@ prompt_and_wait()
break;
case ITEM_WIPE_CACHE:
ui_print("\n-- Wiping cache...\n");
erase_root("CACHE:");
ui_print("Cache wipe complete.\n");
if (!ui_text_visible()) return;
if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache"))
{
ui_print("\n-- Wiping cache...\n");
erase_root("CACHE:");
ui_print("Cache wipe complete.\n");
if (!ui_text_visible()) return;
}
break;
case ITEM_APPLY_SDCARD:
ui_print("\n-- Install from sdcard...\n");
set_sdcard_update_bootloader_message();
int status = install_package(SDCARD_PACKAGE_FILE);
if (status != INSTALL_SUCCESS) {
ui_set_background(BACKGROUND_ICON_ERROR);
ui_print("Installation aborted.\n");
} else if (!ui_text_visible()) {
return; // reboot if logs aren't visible
} else {
if (firmware_update_pending()) {
ui_print("\nReboot via menu to complete\n"
"installation.\n");
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
{
ui_print("\n-- Install from sdcard...\n");
#ifndef BOARD_HAS_NO_MISC_PARTITION
set_sdcard_update_bootloader_message();
#endif
int status = install_package(SDCARD_PACKAGE_FILE);
if (status != INSTALL_SUCCESS) {
ui_set_background(BACKGROUND_ICON_ERROR);
ui_print("Installation aborted.\n");
} else if (!ui_text_visible()) {
return; // reboot if logs aren't visible
} else {
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (firmware_update_pending()) {
ui_print("\nReboot via menu to complete\n"
"installation.\n");
} else {
ui_print("\nInstall from sdcard complete.\n");
}
#else
ui_print("\nInstall from sdcard complete.\n");
#endif
}
}
break;
@ -467,14 +533,12 @@ prompt_and_wait()
}
static void
print_property(const char *key, const char *name, void *cookie)
{
print_property(const char *key, const char *name, void *cookie) {
fprintf(stderr, "%s=%s\n", key, name);
}
int
main(int argc, char **argv)
{
main(int argc, char **argv) {
if (strstr(argv[0], "recovery") == NULL)
{
if (strstr(argv[0], "flash_image") != NULL)
@ -489,9 +553,16 @@ main(int argc, char **argv)
return unyaffs_main(argc, argv);
if (strstr(argv[0], "amend"))
return amend_main(argc, argv);
if (strstr(argv[0], "nandroid"))
return nandroid_main(argc, argv);
if (strstr(argv[0], "reboot"))
return reboot_main(argc, argv);
if (strstr(argv[0], "setprop"))
return setprop_main(argc, argv);
return busybox_driver(argc, argv);
}
__system("/sbin/mkfstab.sh");
__system("/sbin/postrecoveryboot.sh");
create_fstab();
int is_user_initiated_recovery = 0;
time_t start = time(NULL);
@ -502,6 +573,7 @@ main(int argc, char **argv)
fprintf(stderr, "Starting recovery on %s", ctime(&start));
ui_init();
ui_print(EXPAND(RECOVERY_VERSION)"\n");
get_args(&argc, &argv);
int previous_runs = 0;
@ -523,6 +595,8 @@ main(int argc, char **argv)
}
}
device_recovery_start();
fprintf(stderr, "Command:");
for (arg = 0; arg < argc; arg++) {
fprintf(stderr, " \"%s\"", argv[arg]);
@ -563,10 +637,14 @@ main(int argc, char **argv)
if (extendedcommand_file_exists()) {
LOGI("Running extendedcommand...\n");
if (0 == run_and_remove_extendedcommand()) {
int ret;
if (0 == (ret = run_and_remove_extendedcommand())) {
status = INSTALL_SUCCESS;
ui_set_show_text(0);
}
else {
handle_failure(ret);
}
} else {
LOGI("Skipping execution of extendedcommand, file not found...\n");
}
@ -575,8 +653,10 @@ main(int argc, char **argv)
if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR);
if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait();
#ifndef BOARD_HAS_NO_MISC_PARTITION
// If there is a radio image pending, reboot now to install it.
maybe_install_firmware_update(send_intent);
#endif
// Otherwise, get ready to boot the main system...
finish_recovery(send_intent);

View File

@ -17,6 +17,9 @@
#ifndef _RECOVERY_UI_H
#define _RECOVERY_UI_H
// Called when recovery starts up. Returns 0.
extern int device_recovery_start();
// Called in the input thread when a new key (key_code) is pressed.
// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
// keys are already pressed. Return true if the text display should

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

147
roots.c
View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,48 +22,44 @@
#include <sys/types.h>
#include <unistd.h>
#include "mtdutils/mtdutils.h"
#include "mtdutils/mounts.h"
#include <limits.h>
#include "flashutils/flashutils.h"
#include "minzip/Zip.h"
#include "roots.h"
#include "common.h"
#include "mounts.h"
#include "extendedcommands.h"
typedef struct {
const char *name;
const char *device;
const char *device2; // If the first one doesn't work (may be NULL)
const char *partition_name;
const char *mount_point;
const char *filesystem;
} RootInfo;
/* Canonical pointers.
xxx may just want to use enums
*/
static const char g_mtd_device[] = "@\0g_mtd_device";
static const char g_default_device[] = "@\0g_default_device";
static const char g_raw[] = "@\0g_raw";
static const char g_package_file[] = "@\0g_package_file";
static RootInfo g_roots[] = {
{ "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw },
{ "CACHE:", "/dev/block/innersd0p5", NULL, "cache", "/cache", "ext3" },
{ "DATA:", "/dev/block/innersd0p6", NULL, "userdata", "/data", "ext3" },
{ "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw },
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file },
{ "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw },
{ "SDCARD:", "/dev/block/mmcblk1p1", "/dev/block/mmcblk0", NULL, "/sdcard", "vfat" },
{ "SDEXT:", "/dev/block/mmcblk1p2", NULL, NULL, "/sd-ext", "ext3" },
{ "SYSTEM:", g_mtd_device, NULL, "system", "/system", "yaffs2" },
{ "MBM:", g_mtd_device, NULL, "mbm", NULL, g_raw },
{ "TMP:", NULL, NULL, NULL, "/tmp", NULL },
{ "BOOT:", g_default_device, NULL, "boot", NULL, g_raw, NULL },
{ "CACHE:", BOARD_CACHE_DEVICE, NULL, "cache", "/cache", BOARD_CACHE_FILESYSTEM, BOARD_CACHE_FILESYSTEM_OPTIONS },
{ "DATA:", BOARD_DATA_DEVICE, NULL, "userdata", "/data", BOARD_DATA_FILESYSTEM, BOARD_DATA_FILESYSTEM_OPTIONS },
#ifdef BOARD_HAS_DATADATA
{ "DATADATA:", BOARD_DATADATA_DEVICE, NULL, "datadata", "/datadata", BOARD_DATADATA_FILESYSTEM, BOARD_DATADATA_FILESYSTEM_OPTIONS },
#endif
{ "MISC:", g_default_device, NULL, "misc", NULL, g_raw, NULL },
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file, NULL },
{ "RECOVERY:", g_default_device, NULL, "recovery", "/", g_raw, NULL },
{ "SDCARD:", BOARD_SDCARD_DEVICE_PRIMARY, BOARD_SDCARD_DEVICE_SECONDARY, NULL, "/sdcard", "vfat", NULL },
{ "SDEXT:", BOARD_SDEXT_DEVICE, NULL, NULL, "/sd-ext", BOARD_SDEXT_FILESYSTEM, NULL },
{ "SYSTEM:", BOARD_SYSTEM_DEVICE, NULL, "system", "/system", BOARD_SYSTEM_FILESYSTEM, BOARD_SYSTEM_FILESYSTEM_OPTIONS },
{ "MBM:", g_default_device, NULL, "mbm", NULL, g_raw, NULL },
{ "TMP:", NULL, NULL, NULL, "/tmp", NULL, NULL },
};
#define NUM_ROOTS (sizeof(g_roots) / sizeof(g_roots[0]))
// TODO: for SDCARD:, try /dev/block/mmcblk0 if mmcblk0p1 fails
static const RootInfo *
const RootInfo *
get_root_info_for_path(const char *root_path)
{
const char *c;
@ -211,6 +208,19 @@ is_root_path_mounted(const char *root_path)
return internal_root_mounted(info) >= 0;
}
static int mount_internal(const char* device, const char* mount_point, const char* filesystem, const char* filesystem_options)
{
if (strcmp(filesystem, "auto") != 0 && filesystem_options == NULL) {
return mount(device, mount_point, filesystem, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
}
else {
char mount_cmd[PATH_MAX];
const char* options = filesystem_options == NULL ? "noatime,nodiratime,nodev" : filesystem_options;
sprintf(mount_cmd, "mount -t %s -o%s %s %s", filesystem, options, device, mount_point);
return __system(mount_cmd);
}
}
int
ensure_root_path_mounted(const char *root_path)
{
@ -228,21 +238,6 @@ ensure_root_path_mounted(const char *root_path)
/* It's not mounted.
*/
if (info->device == g_mtd_device) {
if (info->partition_name == NULL) {
return -1;
}
//TODO: make the mtd stuff scan once when it needs to
mtd_scan_partitions();
const MtdPartition *partition;
partition = mtd_find_partition_by_name(info->partition_name);
if (partition == NULL) {
return -1;
}
return mtd_mount_partition(partition, info->mount_point,
info->filesystem, 0);
}
if (info->device == NULL || info->mount_point == NULL ||
info->filesystem == NULL ||
info->filesystem == g_raw ||
@ -250,9 +245,15 @@ ensure_root_path_mounted(const char *root_path)
return -1;
}
if (info->device == g_default_device) {
if (info->partition_name == NULL) {
return -1;
}
return mount_partition(info->partition_name, info->mount_point, info->filesystem, 0);
}
mkdir(info->mount_point, 0755); // in case it doesn't already exist
if (mount(info->device, info->mount_point, info->filesystem,
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
if (mount_internal(info->device, info->mount_point, info->filesystem, info->filesystem_options)) {
if (info->device2 == NULL) {
LOGE("Can't mount %s\n(%s)\n", info->device, strerror(errno));
return -1;
@ -297,18 +298,38 @@ ensure_root_path_unmounted(const char *root_path)
return unmount_mounted_volume(volume);
}
int
get_root_partition_device(const char *root_path, char *device)
{
const RootInfo *info = get_root_info_for_path(root_path);
if (info == NULL)
{
return NULL;
}
if (info->device == g_default_device)
return get_partition_device(info->partition_name, device);
return info->device;
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
const MtdPartition *
get_root_mtd_partition(const char *root_path)
{
const RootInfo *info = get_root_info_for_path(root_path);
if (info == NULL || info->device != g_mtd_device ||
if (info == NULL || info->device != g_default_device ||
info->partition_name == NULL)
{
#ifdef BOARD_HAS_MTD_CACHE
if (strcmp(root_path, "CACHE:") != 0)
return NULL;
#else
return NULL;
#endif
}
mtd_scan_partitions();
return mtd_find_partition_by_name(info->partition_name);
}
#endif
int
format_root_device(const char *root)
@ -320,17 +341,20 @@ format_root_device(const char *root)
while (*c != '\0' && *c != ':') {
c++;
}
/*
if (c[0] != ':' || c[1] != '\0') {
LOGW("format_root_device: bad root name \"%s\"\n", root);
return -1;
}
*/
const RootInfo *info = get_root_info_for_path(root);
if (info == NULL || info->device == NULL) {
LOGW("format_root_device: can't resolve \"%s\"\n", root);
return -1;
}
if (info->mount_point != NULL) {
if (info->mount_point != NULL && info->device == g_default_device) {
/* Don't try to format a mounted device.
*/
int ret = ensure_root_path_unmounted(root);
@ -342,32 +366,17 @@ format_root_device(const char *root)
/* Format the device.
*/
if (info->device == g_mtd_device) {
mtd_scan_partitions();
const MtdPartition *partition;
partition = mtd_find_partition_by_name(info->partition_name);
if (partition == NULL) {
LOGW("format_root_device: can't find mtd partition \"%s\"\n",
info->partition_name);
return -1;
}
if (info->filesystem == g_raw || !strcmp(info->filesystem, "yaffs2")) {
MtdWriteContext *write = mtd_write_partition(partition);
if (write == NULL) {
LOGW("format_root_device: can't open \"%s\"\n", root);
return -1;
} else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
LOGW("format_root_device: can't erase \"%s\"\n", root);
mtd_write_close(write);
return -1;
} else if (mtd_write_close(write)) {
LOGW("format_root_device: can't close \"%s\"\n", root);
return -1;
} else {
return 0;
}
}
if (info->device == g_default_device) {
int ret = 0;
if (info->filesystem == g_raw)
ret = erase_raw_partition(info->partition_name);
else
ret = erase_partition(info->partition_name, info->filesystem);
if (ret != 0)
LOGE("Error erasing device %s\n", info->device);
return ret;
}
return format_non_mtd_device(root);
return format_unknown_device(root);
}

85
roots.h
View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,7 +19,80 @@
#define RECOVERY_ROOTS_H_
#include "minzip/Zip.h"
#include "flashutils/flashutils.h"
#include "mtdutils/mtdutils.h"
#include "mmcutils/mmcutils.h"
#ifndef BOARD_USES_MMCUTILS
#define DEFAULT_FILESYSTEM "yaffs2"
#else
#define DEFAULT_FILESYSTEM "ext3"
#endif
#ifndef BOARD_SDCARD_DEVICE_PRIMARY
#define BOARD_SDCARD_DEVICE_PRIMARY "/dev/block/mmcblk0p1"
#endif
#ifndef BOARD_SDCARD_DEVICE_SECONDARY
#define BOARD_SDCARD_DEVICE_SECONDARY "/dev/block/mmcblk0"
#endif
#ifndef BOARD_SDEXT_DEVICE
#define BOARD_SDEXT_DEVICE "/dev/block/mmcblk0p2"
#endif
#ifndef BOARD_SDEXT_FILESYSTEM
#define BOARD_SDEXT_FILESYSTEM "auto"
#endif
#ifndef BOARD_DATA_DEVICE
#define BOARD_DATA_DEVICE g_default_device
#endif
#ifndef BOARD_DATA_FILESYSTEM
#define BOARD_DATA_FILESYSTEM DEFAULT_FILESYSTEM
#endif
#ifndef BOARD_DATADATA_DEVICE
#define BOARD_DATADATA_DEVICE g_default_device
#endif
#ifndef BOARD_DATADATA_FILESYSTEM
#define BOARD_DATADATA_FILESYSTEM DEFAULT_FILESYSTEM
#endif
#ifndef BOARD_CACHE_DEVICE
#define BOARD_CACHE_DEVICE g_default_device
#endif
#ifndef BOARD_CACHE_FILESYSTEM
#define BOARD_CACHE_FILESYSTEM DEFAULT_FILESYSTEM
#endif
#ifndef BOARD_SYSTEM_DEVICE
#define BOARD_SYSTEM_DEVICE g_default_device
#endif
#ifndef BOARD_SYSTEM_FILESYSTEM
#define BOARD_SYSTEM_FILESYSTEM DEFAULT_FILESYSTEM
#endif
#ifndef BOARD_DATA_FILESYSTEM_OPTIONS
#define BOARD_DATA_FILESYSTEM_OPTIONS NULL
#endif
#ifndef BOARD_CACHE_FILESYSTEM_OPTIONS
#define BOARD_CACHE_FILESYSTEM_OPTIONS NULL
#endif
#ifndef BOARD_DATADATA_FILESYSTEM_OPTIONS
#define BOARD_DATADATA_FILESYSTEM_OPTIONS NULL
#endif
#ifndef BOARD_SYSTEM_FILESYSTEM_OPTIONS
#define BOARD_SYSTEM_FILESYSTEM_OPTIONS NULL
#endif
/* Any of the "root_path" arguments can be paths with relative
* components, like "SYSTEM:a/b/c".
@ -54,10 +128,21 @@ int ensure_root_path_mounted(const char *root_path);
int ensure_root_path_unmounted(const char *root_path);
const MtdPartition *get_root_mtd_partition(const char *root_path);
int get_root_partition_device(const char *root_path, char *device);
/* "root" must be the exact name of the root; no relative path is permitted.
* If the named root is mounted, this will attempt to unmount it first.
*/
int format_root_device(const char *root);
typedef struct {
const char *name;
const char *device;
const char *device2; // If the first one doesn't work (may be NULL)
const char *partition_name;
const char *mount_point;
const char *filesystem;
const char *filesystem_options;
} RootInfo;
#endif // RECOVERY_ROOTS_H_

18
setprop.c Normal file
View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include <cutils/properties.h>
int setprop_main(int argc, char *argv[])
{
if(argc != 3) {
fprintf(stderr,"usage: setprop <key> <value>\n");
return 1;
}
if(property_set(argv[1], argv[2])){
fprintf(stderr,"could not set property\n");
return 1;
}
return 0;
}

BIN
testdata/alter-footer.zip vendored Normal file

Binary file not shown.

BIN
testdata/alter-metadata.zip vendored Normal file

Binary file not shown.

BIN
testdata/fake-eocd.zip vendored Normal file

Binary file not shown.

BIN
testdata/jarsigned.zip vendored Normal file

Binary file not shown.

BIN
testdata/otasigned.zip vendored Normal file

Binary file not shown.

BIN
testdata/random.zip vendored Normal file

Binary file not shown.

BIN
testdata/unsigned.zip vendored Normal file

Binary file not shown.

82
ui.c
View File

@ -29,45 +29,47 @@
#include "minui/minui.h"
#include "recovery_ui.h"
#ifdef BOARD_HAS_NO_SELECT_BUTTON
static int gShowBackButton = 1;
#else
static int gShowBackButton = 0;
#endif
#define MAX_COLS 64
#define MAX_ROWS 32
#define MENU_MAX_COLS 64
#define MENU_MAX_ROWS 250
#define CHAR_WIDTH 10
#define CHAR_HEIGHT 18
#ifndef BOARD_LDPI_RECOVERY
#define CHAR_WIDTH 10
#define CHAR_HEIGHT 18
#else
#define CHAR_WIDTH 7
#define CHAR_HEIGHT 16
#endif
#define PROGRESSBAR_INDETERMINATE_STATES 6
#define PROGRESSBAR_INDETERMINATE_FPS 15
enum { LEFT_SIDE, CENTER_TILE, RIGHT_SIDE, NUM_SIDES };
static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER;
static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
static gr_surface gProgressBarEmpty[NUM_SIDES];
static gr_surface gProgressBarFill[NUM_SIDES];
static gr_surface gProgressBarEmpty;
static gr_surface gProgressBarFill;
static int ui_has_initialized = 0;
static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
{ &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
{ &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" },
{ &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING],
"icon_firmware_install" },
{ &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_ERROR],
"icon_firmware_error" },
{ &gProgressBarIndeterminate[0], "indeterminate1" },
{ &gProgressBarIndeterminate[1], "indeterminate2" },
{ &gProgressBarIndeterminate[2], "indeterminate3" },
{ &gProgressBarIndeterminate[3], "indeterminate4" },
{ &gProgressBarIndeterminate[4], "indeterminate5" },
{ &gProgressBarIndeterminate[5], "indeterminate6" },
{ &gProgressBarEmpty[LEFT_SIDE], "progress_bar_empty_left_round" },
{ &gProgressBarEmpty[CENTER_TILE], "progress_bar_empty" },
{ &gProgressBarEmpty[RIGHT_SIDE], "progress_bar_empty_right_round" },
{ &gProgressBarFill[LEFT_SIDE], "progress_bar_left_round" },
{ &gProgressBarFill[CENTER_TILE], "progress_bar_fill" },
{ &gProgressBarFill[RIGHT_SIDE], "progress_bar_right_round" },
{ &gProgressBarEmpty, "progress_empty" },
{ &gProgressBarFill, "progress_fill" },
{ NULL, NULL },
};
@ -127,8 +129,8 @@ static void draw_progress_locked()
if (gProgressBarType == PROGRESSBAR_TYPE_NONE) return;
int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
int width = gr_get_width(gProgressBarIndeterminate[0]);
int height = gr_get_height(gProgressBarIndeterminate[0]);
int width = gr_get_width(gProgressBarEmpty);
int height = gr_get_height(gProgressBarEmpty);
int dx = (gr_fb_width() - width)/2;
int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
@ -141,18 +143,12 @@ static void draw_progress_locked()
float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
int pos = (int) (progress * width);
gr_surface s = (pos ? gProgressBarFill : gProgressBarEmpty)[LEFT_SIDE];
gr_blit(s, 0, 0, gr_get_width(s), gr_get_height(s), dx, dy);
int x = gr_get_width(s);
while (x + (int) gr_get_width(gProgressBarEmpty[RIGHT_SIDE]) < width) {
s = (pos > x ? gProgressBarFill : gProgressBarEmpty)[CENTER_TILE];
gr_blit(s, 0, 0, gr_get_width(s), gr_get_height(s), dx + x, dy);
x += gr_get_width(s);
if (pos > 0) {
gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
}
if (pos < width-1) {
gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
}
s = (pos > x ? gProgressBarFill : gProgressBarEmpty)[RIGHT_SIDE];
gr_blit(s, 0, 0, gr_get_width(s), gr_get_height(s), dx + x, dy);
}
if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
@ -176,6 +172,7 @@ static void draw_text_line(int row, const char* t) {
// Should only be called with gUpdateMutex locked.
static void draw_screen_locked(void)
{
if (!ui_has_initialized) return;
draw_background_locked(gCurrentIcon);
draw_progress_locked();
@ -229,6 +226,7 @@ static void draw_screen_locked(void)
// Should only be called with gUpdateMutex locked.
static void update_screen_locked(void)
{
if (!ui_has_initialized) return;
draw_screen_locked();
gr_flip();
}
@ -237,6 +235,7 @@ static void update_screen_locked(void)
// Should only be called with gUpdateMutex locked.
static void update_progress_locked(void)
{
if (!ui_has_initialized) return;
if (show_text || !gPagesIdentical) {
draw_screen_locked(); // Must redraw the whole screen
gPagesIdentical = 1;
@ -346,6 +345,7 @@ static void *input_thread(void *cookie)
void ui_init(void)
{
ui_has_initialized = 1;
gr_init();
ev_init();
@ -506,10 +506,11 @@ int ui_start_menu(char** headers, char** items) {
strncpy(menu[i] + MENU_ITEM_HEADER_LENGTH, items[i-menu_top], text_cols-1 - MENU_ITEM_HEADER_LENGTH);
menu[i][text_cols-1] = '\0';
}
#ifdef KEY_POWER_IS_SELECT_ITEM
strcpy(menu[i], " - +++++Go Back+++++");
++i;
#endif
if (gShowBackButton) {
strcpy(menu[i], " - +++++Go Back+++++");
++i;
}
menu_items = i - menu_top;
show_menu = 1;
@ -517,11 +518,10 @@ int ui_start_menu(char** headers, char** items) {
update_screen_locked();
}
pthread_mutex_unlock(&gUpdateMutex);
#ifdef KEY_POWER_IS_SELECT_ITEM
return menu_items - 1;
#else
if (gShowBackButton) {
return menu_items - 1;
}
return menu_items;
#endif
}
int ui_menu_select(int sel) {
@ -597,3 +597,11 @@ void ui_clear_key_queue() {
void ui_set_show_text(int value) {
show_text = value;
}
void ui_set_showing_back_button(int showBackButton) {
gShowBackButton = showBackButton;
}
int ui_get_showing_back_button() {
return gShowBackButton;
}

View File

@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir)
updater_src_files := \
install.c \
../mounts.c \
updater.c
#
@ -18,8 +19,10 @@ LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES := $(updater_src_files)
LOCAL_STATIC_LIBRARIES := $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libminzip libz
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 The Android Open Source Project
* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,17 +30,20 @@
#include "cutils/misc.h"
#include "cutils/properties.h"
#include "edify/expr.h"
#include "mincrypt/sha.h"
#include "minzip/DirUtil.h"
#include "mtdutils/mounts.h"
#include "mounts.h"
#include "flashutils/flashutils.h"
#include "mtdutils/mtdutils.h"
#include "mmcutils/mmcutils.h"
#include "updater.h"
#include "applypatch/applypatch.h"
// mount(type, location, mount_point)
//
// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
// type="vfat" location="/dev/block/<whatever>" to mount a device
char* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 3) {
return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
@ -66,23 +70,11 @@ char* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
mkdir(mount_point, 0755);
if (strcmp(type, "MTD") == 0) {
mtd_scan_partitions();
const MtdPartition* mtd;
mtd = mtd_find_partition_by_name(location);
if (mtd == NULL) {
fprintf(stderr, "%s: no mtd partition named \"%s\"",
name, location);
if (strcmp(type, "MTD") == 0 || strcmp(type, "MMC") == 0) {
if (0 == mount_partition(location, mount_point, get_default_filesystem(), 0))
result = mount_point;
else
result = strdup("");
goto done;
}
if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) {
fprintf(stderr, "mtd mount of %s failed: %s\n",
location, strerror(errno));
result = strdup("");
goto done;
}
result = mount_point;
} else {
if (mount(location, mount_point, type,
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
@ -98,12 +90,12 @@ done:
free(type);
free(location);
if (result != mount_point) free(mount_point);
return result;
return StringValue(result);
}
// is_mounted(mount_point)
char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
@ -127,11 +119,11 @@ char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
done:
if (result != mount_point) free(mount_point);
return result;
return StringValue(result);
}
char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
@ -157,14 +149,14 @@ char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
done:
if (result != mount_point) free(mount_point);
return result;
return StringValue(result);
}
// format(type, location)
//
// type="MTD" location=partition
char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
@ -184,45 +176,23 @@ char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
if (strcmp(type, "MTD") == 0) {
mtd_scan_partitions();
const MtdPartition* mtd = mtd_find_partition_by_name(location);
if (mtd == NULL) {
fprintf(stderr, "%s: no mtd partition named \"%s\"",
name, location);
if (strcmp(type, "MTD") == 0 || strcmp(type, "MMC") == 0) {
if (0 != erase_partition(location, NULL)) {
result = strdup("");
goto done;
}
MtdWriteContext* ctx = mtd_write_partition(mtd);
if (ctx == NULL) {
fprintf(stderr, "%s: can't write \"%s\"", name, location);
result = strdup("");
goto done;
}
if (mtd_erase_blocks(ctx, -1) == -1) {
mtd_write_close(ctx);
fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
result = strdup("");
goto done;
}
if (mtd_write_close(ctx) != 0) {
fprintf(stderr, "%s: failed to close \"%s\"", name, location);
result = strdup("");
goto done;
}
result = location;
} else {
fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
}
result = location;
done:
free(type);
if (result != location) free(location);
return result;
return StringValue(result);
}
char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
char** paths = malloc(argc * sizeof(char*));
int i;
for (i = 0; i < argc; ++i) {
@ -249,11 +219,11 @@ char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
char buffer[10];
sprintf(buffer, "%d", success);
return strdup(buffer);
return StringValue(strdup(buffer));
}
char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
}
@ -270,10 +240,10 @@ char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
free(sec_str);
return frac_str;
return StringValue(frac_str);
}
char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
@ -287,11 +257,11 @@ char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
return frac_str;
return StringValue(frac_str);
}
// package_extract_dir(package_path, destination_path)
char* PackageExtractDirFn(const char* name, State* state,
Value* PackageExtractDirFn(const char* name, State* state,
int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
@ -310,48 +280,94 @@ char* PackageExtractDirFn(const char* name, State* state,
NULL, NULL);
free(zip_path);
free(dest_path);
return strdup(success ? "t" : "");
return StringValue(strdup(success ? "t" : ""));
}
// package_extract_file(package_path, destination_path)
char* PackageExtractFileFn(const char* name, State* state,
// or
// package_extract_file(package_path)
// to return the entire contents of the file as the result of this
// function (the char* returned is actually a FileContents*).
Value* PackageExtractFileFn(const char* name, State* state,
int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
if (argc != 1 && argc != 2) {
return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
name, argc);
}
char* zip_path;
char* dest_path;
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
bool success = false;
if (argc == 2) {
// The two-argument version extracts to a file.
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
fprintf(stderr, "%s: no %s in package\n", name, zip_path);
goto done;
char* zip_path;
char* dest_path;
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
fprintf(stderr, "%s: no %s in package\n", name, zip_path);
goto done2;
}
FILE* f = fopen(dest_path, "wb");
if (f == NULL) {
fprintf(stderr, "%s: can't open %s for write: %s\n",
name, dest_path, strerror(errno));
goto done2;
}
success = mzExtractZipEntryToFile(za, entry, fileno(f));
fclose(f);
done2:
free(zip_path);
free(dest_path);
return StringValue(strdup(success ? "t" : ""));
} else {
// The one-argument version returns the contents of the file
// as the result.
char* zip_path;
Value* v = malloc(sizeof(Value));
v->type = VAL_BLOB;
v->size = -1;
v->data = NULL;
if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
fprintf(stderr, "%s: no %s in package\n", name, zip_path);
goto done1;
}
v->size = mzGetZipEntryUncompLen(entry);
v->data = malloc(v->size);
if (v->data == NULL) {
fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n",
name, (long)v->size, zip_path);
goto done1;
}
success = mzExtractZipEntryToBuffer(za, entry,
(unsigned char *)v->data);
done1:
free(zip_path);
if (!success) {
free(v->data);
v->data = NULL;
v->size = -1;
}
return v;
}
FILE* f = fopen(dest_path, "wb");
if (f == NULL) {
fprintf(stderr, "%s: can't open %s for write: %s\n",
name, dest_path, strerror(errno));
goto done;
}
success = mzExtractZipEntryToFile(za, entry, fileno(f));
fclose(f);
done:
free(zip_path);
free(dest_path);
return strdup(success ? "t" : "");
}
// symlink target src1 src2 ...
// unlinks any previously existing src1, src2, etc before creating symlinks.
char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc == 0) {
return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
}
@ -380,11 +396,11 @@ char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
free(srcs[i]);
}
free(srcs);
return strdup("");
return StringValue(strdup(""));
}
char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
bool recursive = (strcmp(name, "set_perm_recursive") == 0);
@ -454,11 +470,11 @@ done:
}
free(args);
return result;
return StringValue(result);
}
char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
@ -470,7 +486,7 @@ char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
property_get(key, value, "");
free(key);
return strdup(value);
return StringValue(strdup(value));
}
@ -479,7 +495,7 @@ char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
// interprets 'file' as a getprop-style file (key=value pairs, one
// per line, # comment lines and blank lines okay), and returns the value
// for 'key' (or "" if it isn't defined).
char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* buffer = NULL;
char* filename;
@ -569,7 +585,7 @@ char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
free(filename);
free(key);
free(buffer);
return result;
return StringValue(result);
}
@ -582,7 +598,7 @@ static bool write_raw_image_cb(const unsigned char* data,
}
// write_raw_image(file, partition)
char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* partition;
@ -600,153 +616,138 @@ char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
mtd_scan_partitions();
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
if (mtd == NULL) {
fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
if (0 == restore_raw_partition(partition, filename))
result = strdup(partition);
else
result = strdup("");
goto done;
}
MtdWriteContext* ctx = mtd_write_partition(mtd);
if (ctx == NULL) {
fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
name, partition);
result = strdup("");
goto done;
}
bool success;
FILE* f = fopen(filename, "rb");
if (f == NULL) {
fprintf(stderr, "%s: can't open %s: %s\n",
name, filename, strerror(errno));
result = strdup("");
goto done;
}
success = true;
char* buffer = malloc(BUFSIZ);
int read;
while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
int wrote = mtd_write_data(ctx, buffer, read);
success = success && (wrote == read);
if (!success) {
fprintf(stderr, "mtd_write_data to %s failed: %s\n",
partition, strerror(errno));
}
}
free(buffer);
fclose(f);
if (mtd_erase_blocks(ctx, -1) == -1) {
fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
}
if (mtd_write_close(ctx) != 0) {
fprintf(stderr, "%s: error closing write of %s\n", name, partition);
}
printf("%s %s partition from %s\n",
success ? "wrote" : "failed to write", partition, filename);
result = success ? partition : strdup("");
done:
if (result != partition) free(partition);
free(filename);
return result;
return StringValue(result);
}
// write_firmware_image(file, partition)
//
// partition is "radio" or "hboot"
// file is not used until after updater exits
//
// TODO: this should live in some HTC-specific library
char* WriteFirmwareImageFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* result = NULL;
char* partition;
char* filename;
if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
return NULL;
}
if (strlen(partition) == 0) {
ErrorAbort(state, "partition argument to %s can't be empty", name);
goto done;
}
if (strlen(filename) == 0) {
ErrorAbort(state, "file argument to %s can't be empty", name);
goto done;
}
FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe;
fprintf(cmd, "firmware %s %s\n", partition, filename);
printf("will write %s firmware from %s\n", partition, filename);
result = partition;
done:
if (result != partition) free(partition);
free(filename);
return result;
}
extern int applypatch(int argc, char** argv);
// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...)
// apply_patch_check(file, sha1, ...)
// apply_patch_space(bytes)
char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
printf("in applypatchfn (%s)\n", name);
char* prepend = NULL;
if (strstr(name, "check") != NULL) {
prepend = "-c";
} else if (strstr(name, "space") != NULL) {
prepend = "-s";
Value* ApplyPatchSpaceFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* bytes_str;
if (ReadArgs(state, argv, 1, &bytes_str) < 0) {
return NULL;
}
char** args = ReadVarArgs(state, argc, argv);
if (args == NULL) return NULL;
// insert the "program name" argv[0] and a copy of the "prepend"
// string (if any) at the start of the args.
int extra = 1 + (prepend != NULL ? 1 : 0);
char** temp = malloc((argc+extra) * sizeof(char*));
memcpy(temp+extra, args, argc * sizeof(char*));
temp[0] = strdup("updater");
if (prepend) {
temp[1] = strdup(prepend);
char* endptr;
size_t bytes = strtol(bytes_str, &endptr, 10);
if (bytes == 0 && endptr == bytes_str) {
ErrorAbort(state, "%s(): can't parse \"%s\" as byte count\n\n",
name, bytes_str);
free(bytes_str);
return NULL;
}
free(args);
args = temp;
argc += extra;
printf("calling applypatch\n");
fflush(stdout);
int result = applypatch(argc, args);
printf("applypatch returned %d\n", result);
return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
}
// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 6 || (argc % 2) == 1) {
return ErrorAbort(state, "%s(): expected at least 6 args and an "
"even number, got %d",
name, argc);
}
char* source_filename;
char* target_filename;
char* target_sha1;
char* target_size_str;
if (ReadArgs(state, argv, 4, &source_filename, &target_filename,
&target_sha1, &target_size_str) < 0) {
return NULL;
}
char* endptr;
size_t target_size = strtol(target_size_str, &endptr, 10);
if (target_size == 0 && endptr == target_size_str) {
ErrorAbort(state, "%s(): can't parse \"%s\" as byte count",
name, target_size_str);
free(source_filename);
free(target_filename);
free(target_sha1);
free(target_size_str);
return NULL;
}
int patchcount = (argc-4) / 2;
Value** patches = ReadValueVarArgs(state, argc-4, argv+4);
int i;
for (i = 0; i < argc; ++i) {
free(args[i]);
for (i = 0; i < patchcount; ++i) {
if (patches[i*2]->type != VAL_STRING) {
ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i);
break;
}
if (patches[i*2+1]->type != VAL_BLOB) {
ErrorAbort(state, "%s(): patch #%d is not blob", name, i);
break;
}
}
if (i != patchcount) {
for (i = 0; i < patchcount*2; ++i) {
FreeValue(patches[i]);
}
free(patches);
return NULL;
}
free(args);
switch (result) {
case 0: return strdup("t");
case 1: return strdup("");
default: return ErrorAbort(state, "applypatch couldn't parse args");
char** patch_sha_str = malloc(patchcount * sizeof(char*));
for (i = 0; i < patchcount; ++i) {
patch_sha_str[i] = patches[i*2]->data;
patches[i*2]->data = NULL;
FreeValue(patches[i*2]);
patches[i] = patches[i*2+1];
}
int result = applypatch(source_filename, target_filename,
target_sha1, target_size,
patchcount, patch_sha_str, patches);
for (i = 0; i < patchcount; ++i) {
FreeValue(patches[i]);
}
free(patch_sha_str);
free(patches);
return StringValue(strdup(result == 0 ? "t" : ""));
}
char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
// apply_patch_check(file, [sha1_1, ...])
Value* ApplyPatchCheckFn(const char* name, State* state,
int argc, Expr* argv[]) {
if (argc < 1) {
return ErrorAbort(state, "%s(): expected at least 1 arg, got %d",
name, argc);
}
char* filename;
if (ReadArgs(state, argv, 1, &filename) < 0) {
return NULL;
}
int patchcount = argc-1;
char** sha1s = ReadVarArgs(state, argc-1, argv+1);
int result = applypatch_check(filename, patchcount, sha1s);
int i;
for (i = 0; i < patchcount; ++i) {
free(sha1s[i]);
}
free(sha1s);
return StringValue(strdup(result == 0 ? "t" : ""));
}
Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
char** args = ReadVarArgs(state, argc, argv);
if (args == NULL) {
return NULL;
@ -775,10 +776,10 @@ char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
}
fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
return buffer;
return StringValue(buffer);
}
char* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 1) {
return ErrorAbort(state, "%s() expects at least 1 arg", name);
}
@ -821,9 +822,108 @@ char* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
char buffer[20];
sprintf(buffer, "%d", status);
return strdup(buffer);
return StringValue(strdup(buffer));
}
// Take a sha-1 digest and return it as a newly-allocated hex string.
static char* PrintSha1(uint8_t* digest) {
char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
int i;
const char* alphabet = "0123456789abcdef";
for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
buffer[i*2+1] = alphabet[digest[i] & 0xf];
}
buffer[i*2] = '\0';
return buffer;
}
// sha1_check(data)
// to return the sha1 of the data (given in the format returned by
// read_file).
//
// sha1_check(data, sha1_hex, [sha1_hex, ...])
// returns the sha1 of the file if it matches any of the hex
// strings passed, or "" if it does not equal any of them.
//
Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 1) {
return ErrorAbort(state, "%s() expects at least 1 arg", name);
}
Value** args = ReadValueVarArgs(state, argc, argv);
if (args == NULL) {
return NULL;
}
if (args[0]->size < 0) {
fprintf(stderr, "%s(): no file contents received", name);
return StringValue(strdup(""));
}
uint8_t digest[SHA_DIGEST_SIZE];
SHA(args[0]->data, args[0]->size, digest);
FreeValue(args[0]);
if (argc == 1) {
return StringValue(PrintSha1(digest));
}
int i;
uint8_t* arg_digest = malloc(SHA_DIGEST_SIZE);
for (i = 1; i < argc; ++i) {
if (args[i]->type != VAL_STRING) {
fprintf(stderr, "%s(): arg %d is not a string; skipping",
name, i);
} else if (ParseSha1(args[i]->data, arg_digest) != 0) {
// Warn about bad args and skip them.
fprintf(stderr, "%s(): error parsing \"%s\" as sha-1; skipping",
name, args[i]->data);
} else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) {
break;
}
FreeValue(args[i]);
}
if (i >= argc) {
// Didn't match any of the hex strings; return false.
return StringValue(strdup(""));
}
// Found a match; free all the remaining arguments and return the
// matched one.
int j;
for (j = i+1; j < argc; ++j) {
FreeValue(args[j]);
}
return args[i];
}
// Read a local file and return its contents (the char* returned
// is actually a FileContents*).
Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
char* filename;
if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
Value* v = malloc(sizeof(Value));
v->type = VAL_BLOB;
FileContents fc;
if (LoadFileContents(filename, &fc) != 0) {
ErrorAbort(state, "%s() loading \"%s\" failed: %s",
name, filename, strerror(errno));
free(filename);
free(v);
free(fc.data);
return NULL;
}
v->size = fc.size;
v->data = (char*)fc.data;
free(filename);
return v;
}
void RegisterInstallFunctions() {
RegisterFunction("mount", MountFn);
@ -843,11 +943,13 @@ void RegisterInstallFunctions() {
RegisterFunction("getprop", GetPropFn);
RegisterFunction("file_getprop", FileGetPropFn);
RegisterFunction("write_raw_image", WriteRawImageFn);
RegisterFunction("write_firmware_image", WriteFirmwareImageFn);
RegisterFunction("apply_patch", ApplyPatchFn);
RegisterFunction("apply_patch_check", ApplyPatchFn);
RegisterFunction("apply_patch_space", ApplyPatchFn);
RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
RegisterFunction("read_file", ReadFileFn);
RegisterFunction("sha1_check", Sha1CheckFn);
RegisterFunction("ui_print", UIPrintFn);

View File

@ -33,15 +33,23 @@
#define SCRIPT_NAME "META-INF/com/google/android/updater-script"
int main(int argc, char** argv) {
// Various things log information to stdout or stderr more or less
// at random. The log file makes more sense if buffering is
// turned off so things appear in the right order.
setbuf(stdout, NULL);
setbuf(stderr, NULL);
if (argc != 4) {
fprintf(stderr, "unexpected number of arguments (%d)\n", argc);
return 1;
}
char* version = argv[1];
if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') {
// We support version "1" or "2".
fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n",
if ((version[0] != '1' && version[0] != '2' && version[0] != '3') ||
version[1] != '\0') {
// We support version 1, 2, or 3.
fprintf(stderr, "wrong updater binary API; expected 1, 2, or 3; "
"got %s\n",
argv[1]);
return 2;
}
@ -100,6 +108,7 @@ int main(int argc, char** argv) {
UpdaterInfo updater_info;
updater_info.cmd_pipe = cmd_pipe;
updater_info.package_zip = &za;
updater_info.version = atoi(version);
State state;
state.cookie = &updater_info;

View File

@ -23,6 +23,7 @@
typedef struct {
FILE* cmd_pipe;
ZipArchive* package_zip;
int version;
} UpdaterInfo;
#endif

45
utilities/Android.mk Normal file
View File

@ -0,0 +1,45 @@
LOCAL_PATH := $(call my-dir)
ifndef BOARD_HAS_SMALL_RECOVERY
include $(CLEAR_VARS)
LOCAL_MODULE := e2fsck
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := fix_permissions
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := parted
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := sdparted
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := tune2fs
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
endif

BIN
utilities/e2fsck Normal file

Binary file not shown.

484
utilities/fix_permissions Normal file
View File

@ -0,0 +1,484 @@
#! /system/bin/sh
#
# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh
#
# fix_permissions - fixes permissions on Android data directories after upgrade
# shade@chemlab.org
#
# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/
# implementation by: Cyanogen
# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro
#
# v1.1-v1.31r3 - many improvements and concepts from XDA developers.
# v1.34 through v2.00 - A lot of frustration [by Kastro]
# v2.01 - Completely rewrote the script for SPEED, thanks for the input farmatito
# /data/data depth recursion is tweaked;
# fixed single mode;
# functions created for modularity;
# logging can be disabled via CLI for more speed;
# runtime computation added to end (Runtime: mins secs);
# progress (current # of total) added to screen;
# fixed CLI argument parsing, now you can have more than one option!;
# debug cli option;
# verbosity can be disabled via CLI option for less noise;;
# [by Kastro, (XDA: k4str0), twitter;mattcarver]
# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups,
# fix help text, implement simulated run (-s) [farmatito]
# v2.03 - fixed chown group ownership output [Kastro]
# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat]
VERSION="2.04"
# Defaults
DEBUG=0 # Debug off by default
LOGGING=1 # Logging on by default
VERBOSE=1 # Verbose on by default
# Messages
UID_MSG="Changing user ownership for:"
GID_MSG="Changing group ownership for:"
PERM_MSG="Changing permissions for:"
# Programs needed
ECHO="busybox echo"
GREP="busybox grep"
EGREP="busybox egrep"
CAT="busybox cat"
CHOWN="busybox chown"
CHMOD="busybox chmod"
MOUNT="busybox mount"
UMOUNT="busybox umount"
CUT="busybox cut"
FIND="busybox find"
LS="busybox ls"
TR="busybox tr"
TEE="busybox tee"
TEST="busybox test"
SED="busybox sed"
RM="busybox rm"
WC="busybox wc"
EXPR="busybox expr"
DATE="busybox date"
# Initialise vars
CODEPATH=""
UID=""
GID=""
PACKAGE=""
REMOVE=0
NOSYSTEM=0
ONLY_ONE=""
SIMULATE=0
SYSREMOUNT=0
SYSMOUNT=0
DATAMOUNT=0
SYSSDMOUNT=0
FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
FP_STARTEPOCH=$( $DATE +%s )
if $TEST "$SD_EXT_DIRECTORY" = ""; then
#check for mount point, /system/sd included in tests for backward compatibility
for MP in /sd-ext /system/sd;do
if $TEST -d $MP; then
SD_EXT_DIRECTORY=$MP
break
fi
done
fi
fp_usage()
{
$ECHO "Usage $0 [OPTIONS] [APK_PATH]"
$ECHO " -d turn on debug"
$ECHO " -f fix only package APK_PATH"
$ECHO " -l disable logging for this run (faster)"
$ECHO " -r remove stale data directories"
$ECHO " of uninstalled packages while fixing permissions"
$ECHO " -s simulate only"
$ECHO " -u check only non-system directories"
$ECHO " -v disable verbosity for this run (less output)"
$ECHO " -V print version"
$ECHO " -h this help"
}
fp_parseargs()
{
# Parse options
while $TEST $# -ne 0; do
case "$1" in
-d)
DEBUG=1
;;
-f)
if $TEST $# -lt 2; then
$ECHO "$0: missing argument for option $1"
exit 1
else
if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then
ONLY_ONE=$2
shift;
else
$ECHO "$0: missing argument for option $1"
exit 1
fi
fi
;;
-r)
REMOVE=1
;;
-s)
SIMULATE=1
;;
-l)
if $TEST $LOGGING -eq 0; then
LOGGING=1
else
LOGGING=0
fi
;;
-v)
if $TEST $VERBOSE -eq 0; then
VERBOSE=1
else
VERBOSE=0
fi
;;
-u)
NOSYSTEM=1
;;
-V)
$ECHO "$0 $VERSION"
exit 0
;;
-h)
fp_usage
exit 0
;;
-*)
$ECHO "$0: unknown option $1"
$ECHO
fp_usage
exit 1
;;
esac
shift;
done
}
fp_print()
{
MSG=$@
if $TEST $LOGGING -eq 1; then
$ECHO $MSG | $TEE -a $LOG_FILE
else
$ECHO $MSG
fi
}
fp_start()
{
if $TEST $SIMULATE -eq 0 ; then
if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then
DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 )
if $TEST $DEBUG -eq 1; then
fp_print "/system mounted on $DEVICE"
fi
if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then
$MOUNT -o remount,rw $DEVICE /system
SYSREMOUNT=1
fi
else
$MOUNT /system > /dev/null 2>&1
SYSMOUNT=1
fi
if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then
$MOUNT /data > /dev/null 2>&1
DATAMOUNT=1
fi
if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then
$MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
SYSSDMOUNT=1
fi
fi
if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then
LOG_FILE="/data/fix_permissions.log"
else
LOG_FILE="/sdcard/fix_permissions.log"
fi
if $TEST ! -e "$LOG_FILE"; then
> $LOG_FILE
fi
fp_print "$0 $VERSION started at $FP_STARTTIME"
}
fp_chown_uid()
{
FP_OLDUID=$1
FP_UID=$2
FP_FILE=$3
#if user ownership doesn't equal then change them
if $TEST "$FP_OLDUID" != "$FP_UID"; then
if $TEST $VERBOSE -ne 0; then
fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'"
fi
if $TEST $SIMULATE -eq 0; then
$CHOWN $FP_UID "$FP_FILE"
fi
fi
}
fp_chown_gid()
{
FP_OLDGID=$1
FP_GID=$2
FP_FILE=$3
#if group ownership doesn't equal then change them
if $TEST "$FP_OLDGID" != "$FP_GID"; then
if $TEST $VERBOSE -ne 0; then
fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'"
fi
if $TEST $SIMULATE -eq 0; then
$CHOWN :$FP_GID "$FP_FILE"
fi
fi
}
fp_chmod()
{
FP_OLDPER=$1
FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 )
FP_PERSTR=$2
FP_PERNUM=$3
FP_FILE=$4
#if the permissions are not equal
if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then
if $TEST $VERBOSE -ne 0; then
fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)"
fi
#change the permissions
if $TEST $SIMULATE -eq 0; then
$CHMOD $FP_PERNUM "$FP_FILE"
fi
fi
}
fp_all()
{
FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $WC -l )
I=0
$CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | while read all_line; do
I=$( $EXPR $I + 1 )
fp_package "$all_line" $I $FP_NUMS
done
}
fp_single()
{
FP_SFOUND=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE | wc -l )
if $TEST $FP_SFOUND -gt 1; then
fp_print "Cannot perform single operation on $FP_SFOUND matched package(s)."
elif $TEST $FP_SFOUND = "" -o $FP_SFOUND -eq 0; then
fp_print "Could not find the package you specified in the packages.xml file."
else
FP_SPKG=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE )
fp_package "${FP_SPKG}" 1 1
fi
}
fp_package()
{
pkgline=$1
curnum=$2
endnum=$3
CODEPATH=$( $ECHO $pkgline | $SED 's%.* codePath="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
PACKAGE=$( $ECHO $pkgline | $SED 's%.* name="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
UID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
GID=$UID
APPDIR=$( $ECHO $CODEPATH | $SED 's%^\(.*\)/.*%\1%' )
APK=$( $ECHO $CODEPATH | $SED 's%^.*/\(.*\..*\)$%\1%' )
#debug
if $TEST $DEBUG -eq 1; then
fp_print "CODEPATH: $CODEPATH APPDIR: $APPDIR APK:$APK UID/GID:$UID:$GID"
fi
#check for existence of apk
if $TEST -e $CODEPATH; then
fp_print "Processing ($curnum of $endnum): $PACKAGE..."
#lets get existing permissions of CODEPATH
OLD_UGD=$( $LS -ln "$CODEPATH" )
OLD_PER=$( $ECHO $OLD_UGD | $CUT -d ' ' -f1 )
OLD_UID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f3 )
OLD_GID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f4 )
#apk source dirs
if $TEST "$APPDIR" = "/system/app"; then
#skip system apps if set
if $TEST "$NOSYSTEM" = "1"; then
fp_print "***SKIPPING SYSTEM APP ($PACKAGE)!"
return
fi
fp_chown_uid $OLD_UID 0 "$CODEPATH"
fp_chown_gid $OLD_GID 0 "$CODEPATH"
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
elif $TEST "$APPDIR" = "/data/app" || $TEST "$APPDIR" = "/sd-ext/app"; then
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
fp_chown_gid $OLD_GID 1000 "$CODEPATH"
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
elif $TEST "$APPDIR" = "/data/app-private" || $TEST "$APPDIR" = "/sd-ext/app-private"; then
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
fp_chown_gid $OLD_GID $GID "$CODEPATH"
fp_chmod $OLD_PER "rw-r-----" 640 "$CODEPATH"
fi
else
fp_print "$CODEPATH does not exist ($curnum of $endnum). Reinstall..."
if $TEST $REMOVE -eq 1; then
if $TEST -d /data/data/$PACKAGE ; then
fp_print "Removing stale dir /data/data/$PACKAGE"
if $TEST $SIMULATE -eq 0 ; then
$RM -R /data/data/$PACKAGE
fi
fi
fi
fi
#the data/data for the package
if $TEST -d "/data/data/$PACKAGE"; then
#find all directories in /data/data/$PACKAGE
$FIND /data/data/$PACKAGE -type d -exec $LS -ldn {} \; | while read dataline; do
#get existing permissions of that directory
OLD_PER=$( $ECHO $dataline | $CUT -d ' ' -f1 )
OLD_UID=$( $ECHO $dataline | $CUT -d ' ' -f3 )
OLD_GID=$( $ECHO $dataline | $CUT -d ' ' -f4 )
FILEDIR=$( $ECHO $dataline | $CUT -d ' ' -f9 )
FOURDIR=$( $ECHO $FILEDIR | $CUT -d '/' -f5 )
#set defaults for iteration
ISLIB=0
REVPERM=755
REVPSTR="rwxr-xr-x"
REVUID=$UID
REVGID=$GID
if $TEST "$FOURDIR" = ""; then
#package directory, perms:755 owner:$UID:$GID
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
elif $TEST "$FOURDIR" = "lib"; then
#lib directory, perms:755 owner:1000:1000
#lib files, perms:755 owner:1000:1000
ISLIB=1
REVPERM=755
REVPSTR="rwxr-xr-x"
REVUID=1000
REVGID=1000
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
elif $TEST "$FOURDIR" = "shared_prefs"; then
#shared_prefs directories, perms:771 owner:$UID:$GID
#shared_prefs files, perms:660 owner:$UID:$GID
REVPERM=660
REVPSTR="rw-rw----"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
elif $TEST "$FOURDIR" = "databases"; then
#databases directories, perms:771 owner:$UID:$GID
#databases files, perms:660 owner:$UID:$GID
REVPERM=660
REVPSTR="rw-rw----"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
elif $TEST "$FOURDIR" = "cache"; then
#cache directories, perms:771 owner:$UID:$GID
#cache files, perms:600 owner:$UID:GID
REVPERM=600
REVPSTR="rw-------"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
else
#other directories, perms:771 owner:$UID:$GID
REVPERM=771
REVPSTR="rwxrwx--x"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
fi
#change ownership of directories matched
if $TEST "$ISLIB" = "1"; then
fp_chown_uid $OLD_UID 1000 "$FILEDIR"
fp_chown_gid $OLD_GID 1000 "$FILEDIR"
else
fp_chown_uid $OLD_UID $UID "$FILEDIR"
fp_chown_gid $OLD_GID $GID "$FILEDIR"
fi
#if any files exist in directory with improper permissions reset them
$FIND $FILEDIR -type f -maxdepth 1 ! -perm $REVPERM -exec $LS -ln {} \; | while read subline; do
OLD_PER=$( $ECHO $subline | $CUT -d ' ' -f1 )
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
fp_chmod $OLD_PER $REVPSTR $REVPERM "$SUBFILE"
done
#if any files exist in directory with improper user reset them
$FIND $FILEDIR -type f -maxdepth 1 ! -user $REVUID -exec $LS -ln {} \; | while read subline; do
OLD_UID=$( $ECHO $subline | $CUT -d ' ' -f3 )
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
fp_chown_uid $OLD_UID $REVUID "$SUBFILE"
done
#if any files exist in directory with improper group reset them
$FIND $FILEDIR -type f -maxdepth 1 ! -group $REVGID -exec $LS -ln {} \; | while read subline; do
OLD_GID=$( $ECHO $subline | $CUT -d ' ' -f4 )
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
fp_chown_gid $OLD_GID $REVGID "$SUBFILE"
done
done
fi
}
date_diff()
{
if $TEST $# -ne 2; then
FP_DDM="E"
FP_DDS="E"
return
fi
FP_DDD=$( $EXPR $2 - $1 )
FP_DDM=$( $EXPR $FP_DDD / 60 )
FP_DDS=$( $EXPR $FP_DDD % 60 )
}
fp_end()
{
if $TEST $SYSREMOUNT -eq 1; then
$MOUNT -o remount,ro $DEVICE /system > /dev/null 2>&1
fi
if $TEST $SYSSDMOUNT -eq 1; then
$UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
fi
if $TEST $SYSMOUNT -eq 1; then
$UMOUNT /system > /dev/null 2>&1
fi
if $TEST $DATAMOUNT -eq 1; then
$UMOUNT /data > /dev/null 2>&1
fi
FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
FP_ENDEPOCH=$( $DATE +%s )
date_diff $FP_STARTEPOCH $FP_ENDEPOCH
fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)"
}
#MAIN SCRIPT
fp_parseargs $@
fp_start
if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then
fp_single "$ONLY_ONE"
else
fp_all
fi
fp_end

BIN
utilities/parted Normal file

Binary file not shown.

637
utilities/sdparted Normal file
View File

@ -0,0 +1,637 @@
#!/sbin/sh
# do logging, if not excluded with -x
LOGFILE="/data/sdparted.log"
[ "$1" != "-x" ] && echo "$0" "$@" >> "$LOGFILE" && "$0" -x "$@" 2>&1 | tee -a "$LOGFILE" && exit
shift
ShowError() { echo ; echo " err: $1" ; echo ; exit 1 ; }
ShowMessage() { echo ; echo " msg: $1" ; }
ShowHelp() {
cat <<DONEHELP
$SCRIPTNAME v$SCRIPTREV created by $MYNAME
if you use this script in your work, please give some credit. thanks.
requirements: cm-recovery-v1.4
usage: $SCRIPTNAME [options]
options:
--fatsize|-fs SIZE[MG] set the size of the fat32 partition to <SIZE>.
default=total sdcard size - (ext + swap)
--extsize|-es SIZE[MG] set the size of the ext partition to <SIZE>.
default=$EXTSIZE
--swapsize|-ss SIZE[MG] set the size of the swap partition to <SIZE>.
if set to 0, no swap partition will be created.
default=$SWAPSIZE
--extfs|-efs TYPE set the filesystem of ext partition to <TYPE>.
valid types=ext2, ext3, ext4
default=$EXTFS
--upgradefs|-ufs TYPE upgrades existing ext partition to <TYPE>.
this operation will NOT wipe your sdcard and
cannot be used with any partition creation options.
valid types=ext3, ext4
--downgradefs|-dfs TYPE downgrades existing ext partition to <TYPE>.
this operation will NOT wipe your sdcard and
cannot be used with any partition creation options.
valid types=ext2
--interactive|-i interactive mode
--help|-h display this help
--printonly|-po display sdcard information
--silent|-s do not prompt user, not even initial warning.
examples:
$SCRIPTNAME creates swap=$SWAPSIZE ext2=$EXTSIZE fat32=remaining free space
$SCRIPTNAME -efs ext4 creates swap=$SWAPSIZE ext4=$EXTSIZE fat32=remaining free space
$SCRIPTNAME -fs 1.5G -efs ext3 creates swap=$SWAPSIZE ext3=$EXTSIZE fat32=1536
$SCRIPTNAME -es 256M -ss 0 creates no swap ext2=256 fat32=remaining free space
$SCRIPTNAME -ufs ext4 upgrades ext partition to ext4
DONEHELP
}
UserAbort() {
WHILEEXIT=
while [ -z "$WHILEEXIT" ]
do
echo -n "do you want to continue? (Y/n) "
read response
echo
[ "$response" = "Y" ] || [ "$response" = "n" ] || [ "$response" = "N" ] && WHILEEXIT="$response"
done
echo "$response" > /dev/null 2>&1 >>"$LOGFILE"
[ "$response" != "Y" ]
}
UnmountAll () {
# unmount all partitions so we can work with $SDPATH
# i'm assuming no more than 3 partitions
# maybe make a little more elegant later
echo -n "unmounting all partitions..."
umount "$FATPATH" > /dev/null 2>&1 >>"$LOGFILE"
umount "$EXTPATH" > /dev/null 2>&1 >>"$LOGFILE"
umount "$SWAPPATH" > /dev/null 2>&1 >>"$LOGFILE"
echo "done"
echo
}
CheckReqs() {
echo -n "checking script requirements..."
# check for valid sdcard
[ -e $SDPATH ] || ShowError "$SDPATH does not exist!"
# look for necessary programs
[ -e $CMPARTED ] || ShowError "$CMPARTED does not exist!"
[ -e $CMTUNE2FS ] || ShowError "$CMTUNE2FS does not exist!"
[ -e $CME2FSCK ] || ShowError "$CME2FSCK does not exist!"
# verify cm-v1.4
PARTEDREV=`"$CMPARTED" "$SDPATH" version | grep Parted | cut -d" " -f3`
[ "$PARTEDREV" == "1.8.8.1.179-aef3" ] || ShowError "you are not using parted v1.8.8.1.179-aef3!"
echo "done"
echo
}
CheckTableType() {
TABLETYPE=`"$CMPARTED" "$SDPATH" print | grep Table: | cut -d" " -f3`
[ "$TABLETYPE" == "loop" -o "$TABLETYPE" == "msdos" ] && TTISOK=1 || TTISOK=0
[ "$TABLETYPE" == "loop" ] && TTISLOOP=1 || TTISLOOP=0
[ "$TABLETYPE" == "msdos" ] && TTISMSDOS=1 || TTISMOSDOS=0
}
ValidateExtArg() {
FUNC_RET="nonzero"
# validating argument
[ "$1" != "ext2" ] && [ "$1" != "ext3" ] && [ "$1" != "ext4" ] && FUNC_RET=
[ -z "$FUNC_RET" ] && [ -z "$IMODE" ] && ShowError "$1 is not a valid filesystem."
[ -z "$FUNC_RET" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid filesystem."
# return valid argument
[ -n "$FUNC_RET" ] && FUNC_RET="$1"
}
ValidateSizeArg() {
# check for zero-length arg to protect expr length
[ -z "$1" ] && ShowError "zero-length argument passed to size-validator"
SIZEMB=
ARGLEN=`expr length $1`
SIZELEN=$(($ARGLEN-1))
SIZEARG=`expr substr $1 1 $SIZELEN`
SIZEUNIT=`expr substr $1 $ARGLEN 1`
# check if SIZEARG is an integer
if [ $SIZEARG -eq $SIZEARG 2> /dev/null ] ; then
# look for G
[ "$SIZEUNIT" == "G" ] && SIZEMB=$(($SIZEARG * 1024))
# look for M
[ "$SIZEUNIT" == "M" ] && SIZEMB=$SIZEARG
# no units on arg AND prevents using bogus size units
[ -z "$SIZEMB" ] && [ $SIZEUNIT -eq $SIZEUNIT 2> /dev/null ] && SIZEMB=$1
# check if SIZEARG is a floating point number, GB only
elif [ `expr index "$SIZEARG" .` != 0 ] && [ "$SIZEUNIT" == "G" ] ; then
INT=`echo "$SIZEARG" | cut -d"." -f1`
FRAC=`echo "$SIZEARG" | cut -d"." -f2`
SIGDIGITS=`expr length $FRAC`
[ -z "$INT" ] && INT=0
INTMB=$(($INT * 1024))
FRACMB=$((($FRAC * 1024) / (10**$SIGDIGITS)))
SIZEMB=$(($INTMB + $FRACMB))
# it's not a valid size
else
[ -z "$IMODE" ] && ShowError "$1 is not a valid size"
fi
[ -z "$SIZEMB" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid size"
# return valid argument in MB
FUNC_RET=$SIZEMB
}
CalculatePartitions() {
# get size of sdcard in MB & do some math
SDSIZEMB=`"$CMPARTED" "$SDPATH" unit MB print | grep $SDPATH | cut -d" " -f3`
SDSIZE=${SDSIZEMB%MB}
[ -n "$FATSIZE" ] || FATSIZE=$(($SDSIZE - $EXTSIZE - $SWAPSIZE))
EXTEND=$(($FATSIZE + $EXTSIZE))
SWAPEND=$(($EXTEND + $SWAPSIZE))
# check for fatsize of 0
[ $FATSIZE -le 0 ] && ShowError "must have a fat32 partition greater than 0MB"
# check for zero-length sdsize...
# indicative of parted not reporting length
# correctly b/c of error on sdcard
[ -z "$SDSIZE" ] && ShowError "zero-length argument passed to partition-calculator"
# make sure we're not being asked to do the impossible
[ $(($FATSIZE + $EXTSIZE + $SWAPSIZE)) -gt $SDSIZE ] && [ -z "$IMODE" ] && ShowError "sum of requested partitions is greater than sdcard size"
}
UpgradeDowngradeOnly() {
if [ -n "$UEXTFSONLY" ] ; then
echo
[ -n "$CREATEPART" ] && ShowError "cannot use upgrade option when creating partitions, use -efs instead"
[ -n "$DEXTFSONLY" ] && ShowError "cannot upgrade AND downgrade, it just doesn't make sense"
echo "you have chosen to upgrade $EXTPATH to $UEXTFSONLY."
echo "this action will NOT delete any data from sdcard."
echo
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
echo
UpgradeExt "$UEXTFSONLY"
ShowCardInfo
elif [ -n "$DEXTFSONLY" ] ; then
echo
[ -n "$CREATEPART" ] && ShowError "cannot use downgrade option when creating partitions."
[ -n "$UEXTFSONLY" ] && ShowError "cannot downgrade AND upgrade, it just doesn't make sense."
echo "you have chosen to downgrade $EXTPATH to $DEXTFSONLY."
echo "this action will NOT delete any data from sdcard."
echo
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
echo
DowngradeExt "$DEXTFSONLY"
ShowCardInfo
fi
}
PrepareSdCard() {
echo
if [ $TTISOK -eq 0 ] ; then
echo "partition 1 may not be aligned to cylinder boundaries."
echo "to continue, this must be corrected."
elif [ $TTISLOOP -gt 0 ] ; then
echo "your sdcard's partition table type is $TABLETYPE."
echo "to continue, partition table must be set to 'msdos'."
elif [ $TTISMSDOS -gt 0 ] ; then
# just a reminder..in a later version,
# i may implement resizing of partitions,
# so this will be unnecessary. but until then...
echo "to continue, all existing partitions must be removed."
else
# this is not good, and should never happen
# if it does, there is a serious problem
ShowError "sdcard failed table type check."
fi
echo
echo "this action will remove all data from your sdcard."
echo
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
[ $TTISOK -eq 0 ] && echo -n "correcting cylinder boundaries..."
[ $TTISLOOP -gt 0 ] && echo -n "setting partition table to msdos..."
[ $TTISMSDOS -gt 0 ] && echo -n "removing all partitions..."
"$CMPARTED" -s "$SDPATH" mklabel msdos 2>&1 >>"$LOGFILE"
echo "done"
echo
}
ShowActions() {
echo
echo "total size of sdcard=$SDSIZEMB"
echo
echo "the following actions will be performed:"
echo " -create $FATSIZE""MB fat32 partition"
[ $EXTSIZE -gt 0 ] && echo " -create $EXTSIZE""MB ext2 partition"
[ $SWAPSIZE -gt 0 ] && echo " -create $SWAPSIZE""MB swap partition"
[ "$EXTFS" != "ext2" ] && echo " -ext2 partition will be upgraded to $EXTFS"
echo
[ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user"
echo
}
ShowCardInfo() {
CheckTableType
echo
echo "retrieving current sdcard information..."
if [ $TTISOK -gt 0 ] ; then
echo
parted "$SDPATH" print
echo
echo "script log is located @ /data/sdparted.log"
exit 0
else
echo
echo "partition 1 may not be aligned to cylinder boundaries."
ShowError "cannot complete print operation."
fi
echo
}
PartitionSdCard() {
echo "performing selected actions..."
echo
if [ $FATSIZE -gt 0 ] ; then
echo -n "creating fat32 partition..."
"$CMPARTED" -s "$SDPATH" mkpartfs primary fat32 0 "$FATSIZE"MB 2>&1 >>"$LOGFILE"
echo "done"
fi
if [ $EXTSIZE -gt 0 ] ; then
echo -n "creating ext2 partition..."
"$CMPARTED" -s "$SDPATH" mkpartfs primary ext2 "$FATSIZE"MB "$EXTEND"MB 2>&1 >>"$LOGFILE"
echo "done"
fi
if [ $SWAPSIZE -gt 0 ] ; then
echo -n "creating swap partition..."
"$CMPARTED" -s "$SDPATH" mkpartfs primary linux-swap "$EXTEND"MB "$SWAPEND"MB 2>&1 >>"$LOGFILE"
echo "done"
fi
echo
}
UpgradeExt() {
# check for no upgrade
[ "$1" == "ext2" ] && return
# check for ext partition
[ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist"
# have to use -m switch for this check b/c parted incorrectly
# reports all ext partitions as ext2 when running print <number>
CHECKEXTFS=`"$CMPARTED" -m "$SDPATH" print | grep ext | cut -d":" -f5`
[ "$CHECKEXTFS" == "$1" ] && ShowError "$EXTPATH is already $1"
# grabbed the code bits for ext3 from upgrade_fs(credit:cyanogen)
# check for ext2...must upgrade to ext3 first b4 ext4
if [ "$1" == "ext3" -o "$1" == "ext4" ] ; then
echo -n "adding journaling to $EXTPATH..."
umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
"$CME2FSCK" -p "$EXTPATH" 2>&1 >>"$LOGFILE"
"$CMTUNE2FS" -c0 -i0 -j "$EXTPATH" 2>&1 >>"$LOGFILE"
echo "done"
fi
# and got convert to ext4 from xda-forum(credit:Denkai)
if [ "$1" == "ext4" ] ; then
echo -n "converting $EXTPATH to ext4 filesystem..."
umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
"$CMTUNE2FS" -O extents,uninit_bg,dir_index "$EXTPATH" 2>&1 >>"$LOGFILE"
"$CME2FSCK" -fpDC0 "$EXTPATH" 2>&1 >>"$LOGFILE"
echo "done"
fi
echo
}
DowngradeExt() {
# check for ext partition
[ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist"
# have to use print for this check b/c parted incorrectly
# reports all ext partitions as ext2 when running print <number>
CHECKEXTFS=`"$CMPARTED" -m "$SDPATH" print | grep ext | cut -d":" -f5`
[ "$CHECKEXTFS" == "$1" ] && ShowError "$EXTPATH is already $1"
if [ "$CHECKEXTFS" == "ext4" -o "$1" == "ext3" ] ; then
# interweb says downgrading from ext4 is not possible
# without a backup/restore procedure.
# if i figure it out, i'll implement it.
ShowError "downgrading from ext4 is not currently supported"
fi
if [ "$1" == "ext2" ] ; then
echo -n "removing journaling from $EXTPATH..."
umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
"$CMTUNE2FS" -O ^has_journal "$EXTPATH" 2>&1 >>"$LOGFILE"
"$CME2FSCK" -fp "$EXTPATH" 2>&1 >>"$LOGFILE"
echo "done"
fi
echo
}
Interactive() {
cat <<DONEINSTRUCT
sdparted interactive mode
rules:
1. no answer means you accept default value
2. enter '0' for no partition
DONEINSTRUCT
UserAbort && ShowError "script canceled by user"
CalculatePartitions
GetSwapSize
FATSIZE=
CalculatePartitions
GetExtSize
GetExtType
FATSIZE=
CalculatePartitions
GetFatSize
}
GetSwapSize() {
SWAPTEST=
while [ -z "$SWAPTEST" ]
do
echo
echo -n "swap partition size [default=$SWAPSIZE]: "
read SWAPRESP
[ -z "$SWAPRESP" ] && SWAPRESP="$SWAPSIZE"
echo "$SWAPRESP" > /dev/null 2>&1 >>"$LOGFILE"
ValidateSizeArg "$SWAPRESP"
SWAPTEST="$FUNC_RET"
[ -n "$SWAPTEST" ] && [ $SWAPTEST -gt $SDSIZE ] && ShowMessage "$SWAPRESP > available space($(($SDSIZE))M)." && SWAPTEST=
done
SWAPSIZE=$SWAPTEST
}
GetExtSize() {
EXTTEST=
while [ -z "$EXTTEST" ]
do
echo
echo -n "ext partition size [default=$EXTSIZE]: "
read EXTRESP
[ -z "$EXTRESP" ] && EXTRESP="$EXTSIZE"
echo "$EXTRESP" > /dev/null 2>&1 >>"$LOGFILE"
ValidateSizeArg "$EXTRESP"
EXTTEST="$FUNC_RET"
[ -n "$EXTTEST" ] && [ $EXTTEST -gt $(($SDSIZE - $SWAPSIZE)) ] && ShowMessage "$EXTRESP > available space($(($SDSIZE - $SWAPSIZE))M)." && EXTTEST=
done
EXTSIZE=$EXTTEST
}
GetExtType() {
FSTEST=
while [ -z "$FSTEST" ]
do
echo
echo -n "ext partition type [default=$EXTFS]: "
read FSRESP
[ -z "$FSRESP" ] && FSRESP="$EXTFS"
echo "$FSRESP" > /dev/null 2>&1 >>"$LOGFILE"
ValidateExtArg "$FSRESP"
FSTEST="$FUNC_RET"
done
EXTFS="$FSTEST"
}
GetFatSize() {
FATTEST=
while [ -z "$FATTEST" ]
do
echo
echo -n "fat partition size [default=$FATSIZE]: "
read FATRESP
[ -z "$FATRESP" ] && FATRESP="$FATSIZE"
echo "$FATRESP" > /dev/null 2>&1 >>"$LOGFILE"
ValidateSizeArg "$FATRESP"
FATTEST="$FUNC_RET"
[ -n "$FATTEST" ] && [ $FATTEST -gt $FATSIZE ] && ShowMessage "$FATRESP > available space($(($SDSIZE - $SWAPSIZE - $EXTSIZE))M)." && FATTEST=
[ -n "$FATTEST" ] && [ $FATTEST -le 0 ] && ShowMessage "must have a fat32 partition greater than 0MB" && FATTEST=
done
FATSIZE=$FATTEST
}
SCRIPTNAME="sdparted"
SCRIPTREV="0.6"
MYNAME="51dusty"
IMODE=
SILENTRUN=
CREATEPART=
FUNC_RET=
UEXTFSONLY=
DEXTFSONLY=
TTISOK=
TTISLOOP=
TTISMSDOS=
SDSIZE=
SDSIZEMB=
if [ -z "$SDPATH" ]
then
SDPATH="/dev/block/mmcblk0"
else
echo Found SDPATH=$SDPATH
fi
FATSIZE=
FATTYPE="fat32"
FATPATH=$SDPATH"p1"
EXTSIZE=512
EXTFS="ext2"
EXTPATH=$SDPATH"p2"
EXTEND=
SWAPSIZE=32
SWAPTYPE="linux-swap"
SWAPPATH=$SDPATH"p3"
SWAPEND=
CMPARTED="/sbin/parted"
CMTUNE2FS="/sbin/tune2fs"
CME2FSCK="/sbin/e2fsck"
# give the output some breathing room
echo "$SCRIPTREV" >> "$LOGFILE"
echo
# check for arguments
while [ $# -gt 0 ] ; do
case "$1" in
-h|--help) ShowHelp ; exit 0 ;;
-fs|--fatsize) shift ; ValidateSizeArg "$1" ; FATSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
-es|--extsize) shift ; ValidateSizeArg "$1" ; EXTSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
-ss|--swapsize) shift ; ValidateSizeArg "$1" ; SWAPSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
-efs|--extfs) shift ; ValidateExtArg "$1" ; EXTFS="$FUNC_RET" ; CREATEPART="$1" ;;
-ufs|--upgradefs) shift ; ValidateExtArg "$1" ; UEXTFSONLY="$FUNC_RET" ;;
-dfs|--downgradefs) shift ; ValidateExtArg "$1" ; DEXTFSONLY="$FUNC_RET" ;;
-i|--interactive) IMODE="$1" ;;
-s|--silent) SILENTRUN="$1" ;;
-po|--printonly) ShowCardInfo ;;
*) ShowHelp ; ShowError "unknown argument '$1'" ;;
esac
shift
done
# can't do silent when in interactive mode
[ -n "$IMODE" ] && SILENTRUN=
# make sure sdcard exists and all needed files are here
CheckReqs
# unmount all
UnmountAll
# upgrade only? downgrade only?
UpgradeDowngradeOnly
# check table
CheckTableType
# prep card
PrepareSdCard
# check for interactive mode
[ -n "$IMODE" ] && Interactive
# do some math
CalculatePartitions
# last chance to cancel
ShowActions
# partition card
PartitionSdCard
# upgrade fs if necessary
UpgradeExt "$EXTFS"
# say goodbye and show print output
ShowCardInfo

BIN
utilities/tune2fs Executable file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More