267 Commits

Author SHA1 Message Date
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
23ce6b14ae allow the parted and fix permissions tools to be optional 2010-08-29 12:35:10 -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
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
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
1e8aabad34 alphabetical sort
Change-Id: I4b1bb6787a5cbe1e99e3d8b0cc5bf37d7167398d
2010-07-01 00:23:45 -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
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
1741dcdb26 1.8.1.8 2010-06-11 00:49:49 -07:00
cdb63355c1 1.8.1.7 2010-06-09 15:49:44 -07:00
58131e760c add optional hiding of the progress bar, for speed. 2010-06-09 12:41:20 -07:00
387080a40b syntax error 2010-06-09 12:23:32 -07:00
f4e3a67a38 support or devices that do not have a trackball or enter button. 2010-06-09 12:19:41 -07:00
cd9e3ce55b revert change from zinx 2010-05-25 13:14:50 -07:00
77c452689d Merge branch 'eclair' of git://github.com/koush/android_bootable_recovery into eclair 2010-05-24 22:34:46 -07:00
4206851070 1.8.1.5 2010-05-24 22:34:44 -07:00
917c38af20 adjust menu start position: zinx 2010-05-24 16:43:39 -07:00
2c3cc50356 add a utility erase_image. possible fix for d/s flashing bug from zinx 2010-05-21 10:11:05 -07:00
bca9243339 add a static flash_image to the utilities 2010-05-06 11:50:32 -07:00
a2f6c69e7f fix sd-ext in fstab 2010-05-04 09:03:29 -07:00
ada3f3ba33 publish update-binary as a utility 2010-05-01 21:30:55 -07:00
fd1579b7c0 add Wipe Battery stats. Generate fstab dynamically. 2010-05-01 12:46:55 -07:00
1e9f60a967 1.8.1.2 2010-04-19 01:26:35 -07:00
8430655931 format BOOT on nandroid restore. 2010-04-18 21:21:58 -07:00
f3534d08eb tabs vs spaces 2010-04-18 18:06:24 -07:00
b7538c721e 1.8.1.0 2010-04-18 16:01:17 -07:00
2f73e58ef8 Unmount partitions after restore (except for CACHE:).
Wipe sd-ext on data wipe.
Fix bug where wiping SDEXT: did not work.
2010-04-18 16:00:21 -07:00
7fe4d7bbcb because Chris Soyars doesnt like adb reboot recovery. whaaat!? 2010-04-06 23:04:52 -07:00
6d32ac029c do not create a /tmp/.installscript. Let the install script do that. 2010-04-06 22:20:04 -07:00
8fa54d19f1 Fix the module class fail. 2010-04-06 13:23:35 -04:00
59be31ed31 Fix the build. 2010-04-06 13:12:55 -04:00
5552aa38f5 Create a static arm dump_image utility. 2010-04-05 22:25:16 -07:00
a5f64e66dd Add Hero CDMA support. Fix fail where formatting sd-ext would actually rm -rf /... wow epic fail. Spaces in paths are not your friend. 2010-04-05 15:44:57 -07:00
6771aca715 copy extended command and run it, rather than load and delete. 2010-04-03 23:28:39 -07:00
7c17e8828c 1.8.0.5 2010-04-02 22:18:10 -07:00
71ef11f2d5 sd-ext should not show an error when trying to back it up and it is not there 2010-04-02 22:17:56 -07:00
1d53c4ea63 Real men clean up their mallocs 2010-04-02 16:46:21 -07:00
dec83e2551 1.8.0.4 2010-04-02 14:25:56 -07:00
b4bb02bdfb 1.8.0.3 2010-04-02 13:16:06 -07:00
d6380d2cb4 up the recovery version 2010-04-01 17:01:16 -07:00
24a0124b7c missing file 2010-04-01 12:21:10 -07:00
68b0190858 Nandroid restore of sd-ext. 2010-04-01 12:20:39 -07:00
3f99539c4d Add formatting options for SDCARD and SDEXT. Nandroid backup now supports SDEXT if it is mountable. TODO: Nandroid Restore of SDEXT. 2010-03-30 23:29:43 -07:00
32f5c7ec94 1.7.8.9 2010-03-29 23:10:47 -07:00
a958ac458d support run_program from non package root 2010-03-29 23:10:32 -07:00
d221668138 1.7.8.8 2010-03-29 16:14:07 -07:00
5a3caa1cfe Merge branch 'eclair' of git://github.com/koush/android_bootable_recovery into eclair 2010-03-29 15:20:36 -07:00
24cecb1d08 use busybox-minimal.links 2010-03-29 15:20:07 -07:00
6923cc3223 Allow selective restore from ROM Manager. Dump recovery image on Nandroid. 2010-03-29 14:46:00 -07:00
6a26e7c23f friendlier backup names. Remove option to format anything but boot. 1.7.8.4 2010-03-27 15:26:11 -07:00
c0970934e2 dont fail on lack of free space. just warn. figure out issue later. allow KEY_BACK 2010-03-25 23:19:19 -07:00
04159f738f epic fail on my part. integer overflow. the s_bfree and such are all long values 2010-03-25 19:04:08 -07:00
fe84a7ff84 allow selective restore 2010-03-25 18:19:23 -07:00
78686294eb add free space check 2010-03-25 17:58:45 -07:00
d717892353 put parenthesis around the md5sum... apparently there was a strange issue of the sum not being generated. 1.7.7.3 2010-03-24 14:38:40 -07:00
f953555bde 1.7.7.1 2010-03-24 10:36:05 -07:00
b0a2503e02 check for md5 creation failure 2010-03-24 10:34:38 -07:00
1c61c29837 use libbusybox which has a minimum config 2010-03-23 23:52:38 -07:00
622e82ace9 fix version to have a revision 2010-03-23 15:50:18 -07:00
da3a58289f touch /tmp/.installscript so we dont run backuptool 2010-03-23 15:11:16 -07:00
707fa6d289 null terminate script. wrapping menus. 1.7.7 2010-03-23 11:44:33 -07:00
581bd861d1 run update-binary first. revert recovery API version to 2. use RECOVERY_VERSION for the actual display version. 2010-03-20 01:08:55 -07:00
a496b51a83 working advanced menus 2010-03-19 14:51:45 -07:00
16f0b49bea working erase_image and options in recovery now 2010-03-19 14:37:11 -07:00
75b930f396 added erase_flash, utility to erase mtd partition
primarily for platforms without engineering SPL or native fastboot support
2010-03-19 13:35:20 -07:00
5899ac9ca0 the beginnings of adding a format option, 2010-03-19 13:34:36 -07:00
d3243fe047 1.7.3 2010-03-17 12:59:57 -07:00
6f406aab03 Move the symlink creation into recovery, rather than in the hacky build/core/Makefile 2010-03-16 23:46:13 -07:00
f721594cd0 Fix mount USB storage bug. Fix bug where boot image was not being restored by Nandroid. 2010-03-16 13:34:51 -07:00
23116c95c7 readd /sdcard to mount menu 2010-03-16 11:56:01 -07:00
3fb8d302f4 use the cached basename, rather than call it again. 2010-03-15 17:05:27 -07:00
4b249cd022 refactor backup and restore
fix bugs
2010-03-15 16:41:09 -07:00
7e0a72c8b3 need to specify a backup directory if none is given 2010-03-15 00:54:34 -07:00
b9546a8047 Camera and center button now select. Add more mount and unmount options. 1.7.0 2010-03-14 22:42:30 -07:00
face588f64 do the md5 sum and check in the directory of the backup. otherwise it uses absolute paths, which doesnt allow you to rename the backup or move it. 2010-03-13 10:15:55 -08:00
6da075c92b 1.6.7 2010-03-12 23:50:00 -08:00
3f38f328cf sync memory and filesystem 2010-03-12 23:45:25 -08:00
ee57bbc1b6 nandroid in C now has a progress bar 2010-03-12 23:21:12 -08:00
928d605435 remove old nandroid script 2010-03-12 17:47:59 -08:00
54305a8497 Nandroid fully implemented in C... needs some testing. 2010-03-12 17:43:26 -08:00
a85d7cc65a nandroid backup is now implemented in C 2010-03-12 17:00:58 -08:00
4a8e7d2a09 tabs vs spaces. 2010-03-12 14:30:56 -08:00
3836f72fbf delete script before running it, otherwise you have potential recovery loops 2010-03-11 22:17:43 -08:00
6bd595cc32 1.6.4 2010-03-11 22:17:11 -08:00
3a25cf553d extended command script waits for the sd card device 2010-03-08 19:22:41 -08:00
85c2a5386b 1.6.1 2010-03-08 14:21:08 -08:00
8c866dc252 fix backup 2010-03-08 14:20:37 -08:00
5306db57e5 fix backup bug 2010-03-08 14:09:35 -08:00
8bbbbd4445 menus can now be bigger than the screen, scoll support in menus 2010-03-08 16:09:49 +01:00
ea46fe29d9 install_zip command 2010-03-08 02:58:04 -08:00
19459f31fa 1.5.0 2010-03-07 22:31:40 -08:00
60d7ee07ba add sleep and print commands. 2010-03-07 22:16:55 -08:00
a9483087a0 add backup_rom and restore_rom to amend scripts 2010-03-07 21:47:41 -08:00
13d8fccf50 need to hide the UI to reboot 2010-03-07 14:15:14 -08:00
32e4111df5 reboot upon extendedcommand success 2010-03-07 14:11:56 -08:00
72a1db6358 run an extended command script on boot if it exists 2010-03-07 14:10:26 -08:00
f68aaaf20c run amend scripts from the command line 2010-03-07 13:39:21 -08:00
2bda3e9fa0 disable script asserts by default for user usage 2010-03-06 16:40:52 -08:00
73098ab18a version 1.4.2. minor bug fixes. 2010-03-06 16:37:01 -08:00
99fb6fef11 merge busybox and other tools into recovery. 2010-03-03 00:42:58 -08:00
e81cb750fd not every implementation has the "enable" file 2010-02-26 17:44:33 -08:00
81d8ee59bb up the version 2010-02-26 14:18:55 -08:00
0317378bf6 usb mass storage support. user initiated recovery is now user friendly. 2010-02-26 14:14:23 -08:00
ea2f2428a1 Merge branch 'eclair' of git@github.com:ctso/android_bootable_recovery into eclair 2010-02-26 02:49:57 -05:00
a1749d93bc Added ability to wipe_data and update_package at the same time.
This will be needed for a future project I am working on,
OpenUpdater.
2010-02-26 02:49:30 -05:00
db8dedf12f Fixed a bug that would never allow update_package + wipe_data to be called. 2010-02-26 02:48:08 -05:00
185baeeb79 Added ability to wipe_data and update_package at the same time.
This will be needed for a future project I am working on,
OpenUpdater.
2010-02-26 02:45:55 -05:00
261dde9f48 make it pretty 2010-02-25 16:51:45 -08:00
001c5b508f clean up tabs vs spaces 2010-02-25 14:53:57 -08:00
49f5689b42 allow choosing of subdirectories when searching for a zip file to install 2010-02-25 14:51:05 -08:00
79ce82cab3 successful installation should hide the android installing icon 2010-02-25 12:03:17 -08:00
33370db187 better error codes to allow for diagnosing problems in recovery mode. fix system looking for sh in /system/bin/sh 2010-02-25 11:39:07 -08:00
01098d245c nandroid backup supports a directory now 2010-02-25 11:07:19 -08:00
36d02893cd fix build 2010-02-24 22:46:28 -08:00
ff32e8c7e5 clean up 2010-02-24 13:16:43 -08:00
a37e9b1f19 Patch from Magnus to fix issues with bad blocks and dumping a boot image. Need to clean up the patch a bit. Also reverted mtdutils.s to korg/eclair-release 2010-02-24 13:13:34 -08:00
5740b042b5 change the nandroid backup directory to something recovery specific, to reo prevent possible collisions 2010-02-23 22:41:36 -08:00
69b75410de do not check the results of a run_program if script asserts are disabled 2010-02-23 18:07:31 -08:00
007439bef6 never restore recovery 2010-02-22 19:01:54 -08:00
7400dc2700 missing file 2010-02-22 08:53:47 -08:00
981b0cd1e1 roll recovery tools into a single binary. fix up nandroid to work without deviceid requirement 2010-02-22 08:53:34 -08:00
225c6b4673 do error checking of nandroid operations 2010-02-21 22:02:24 -08:00
1fa52ecdbf add mount sdcard option 2010-02-21 21:29:10 -08:00
bcdd00359c Functional Nandroid 2010-02-21 21:10:25 -08:00
466e67a582 generate fstab using mkfstab.sh. implement restore of system data and cache in nandroid 2010-02-21 19:29:32 -08:00
1a7ee5384d add mkfstab.sh script to create an fstab on any device. clean up dump_image to use libmtdutils 2010-02-21 17:52:30 -08:00
7e5a661064 move unyaffs into external/yaffs2 2010-02-21 01:09:52 -08:00
f9476fbfe8 fix build break 2010-02-21 01:02:30 -08:00
c788c26397 more fixes 2010-02-20 17:25:03 -08:00
8ce0be4956 nearly working nandroid, built against libc 2010-02-20 15:59:06 -08:00
3ab130fa9e add missing files 2010-02-19 16:50:36 -08:00
bf055bb1be intetrate nandroid into recovery 2010-02-19 16:47:53 -08:00
a3c2f735d7 fix up back button and menu toggling 2010-02-19 14:17:22 -08:00
841b2bf352 Add static flash_image for recovery 2010-02-15 01:45:38 -08:00
3a976a79bf power button toggles menu 2010-02-13 15:33:49 -08:00
e923487ff6 working recovery image! 2010-02-12 00:43:24 -08:00
1f14c9a1f1 Need to call register_package_root prior to installing the package. The subsequent unregistering made me think that it was being registered at some point, but it was removed in the transition to eclair. The unregistration was dead code. It is being used now though. 2010-02-11 23:08:23 -08:00
6060e5c6df update.zip somewhat working now... 2010-02-11 22:27:06 -08:00
4c1eed2573 Initial reintegration of legacy update process that used update-script 2010-02-11 18:59:58 -08:00
0d4ff2fcc6 allow alt-l or menu to bring up the recovery menu 2010-02-10 12:10:02 -08:00
89d385c4d9 allow mouse/trackball click for enter on touch only devices. 2010-02-10 12:10:01 -08:00
22d79a5c5e make offsets in firmware update header not point to bad blocks
(This is being cherry-picked from master.)

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.

http://b/2358012 - passion: failure to flash hboot (bad blocks?)
2010-01-13 10:07:28 -08:00
17a47098d2 use MEMGETBADBLOCK to look for bad blocks when reading MTD partitions 2009-12-14 18:27:03 -08:00
c652e41d91 fix cut-and-paste error in verifier
Oops.
2009-12-08 15:30:09 -08:00
54ec81fe86 Merge change I1c8ca2e4 into eclair
* changes:
  replace generic recovery icons
2009-10-07 20:09:16 -04:00
988500b615 add terminator to recovery's getopt_long options array
http://b/2170691 - recovery argument parsing is broken
2009-10-06 14:44:54 -07:00
1c8ca2e40b replace generic recovery icons 2009-10-02 15:42:31 -07:00
f93d8166ef confirm before wiping user data in recovery
When using the hidden menu to wipe data in recovery, confirm before
starting the wipe.  (This does not affect booting with the --wipe_data
flag, or using Alt+W on dream with the menu hidden -- those still wipe
data immediately.)
2009-09-22 18:16:56 -07:00
fd8fb0c492 reduce fraction of progress bar for verification
Reduce the fraction of the progress bar used for package verification
from 50% to 25%:
  - verification is faster than before due to sha1 improvements
  - in eclair we're now verifying the compressed data rather than
    decompressing it
  - incremental packages (which is what most installs use) write more
    data than is contained in the package.
2009-09-20 14:10:27 -07:00
60babf8ba7 delete files before symlinking; log error messages
The symlink() function should remove existing files before creating
symlinks, so scripts are idempotent.  Log messages when various system
calls fail (but don't make the whole script fail).
2009-09-18 15:11:24 -07:00
196c25c777 don't complain if recovery icon is short
If the a recovery icon file is so short that we can't even read the
8-byte header, put a message in the log but not on the device screen.
We intentionally have zero-length files for some icons on some devices,
if they're never shown (eg, the firmware installation icons are only
used on HTC devices).
2009-09-15 08:50:04 -07:00
a3f89eabb7 add a run_program() function to edify
Handy for producing debugging OTA packages (eg, running sqlite3 or
whatever in recovery).
2009-09-10 14:20:53 -07:00
34c98df78a do not merge: cherry-pick of c2d666bd4f from master 2009-08-18 12:05:45 -07:00
54e2e86c57 do not merge: cherry-picked 60151a295c from master branch 2009-08-17 13:21:04 -07:00
0cf0e33b5f merge from open-source master 2009-08-04 09:00:15 -07:00
b9955b8373 am 20697b96: only build flash_image for eng
Merge commit '20697b965cd923211daeea2037f60218358b9659'

* commit '20697b965cd923211daeea2037f60218358b9659':
  only build flash_image for eng
2009-08-04 08:44:43 -07:00
34109de24e merge from donut 2009-07-29 14:56:48 -07:00
eb1ac27bf0 reconcile korg/master into goog/master 2009-07-26 11:43:53 -07:00
7bd5c66075 Merge korg/donut into korg/master 2009-07-25 17:48:00 -07:00
20697b965c only build flash_image for eng
With the recovery image being installed by applypatch, the flash_image
tool isn't needed any more.  Continue to build it for eng just in case
it's handy for debugging.
2009-07-23 15:17:00 -07:00
23412e6f14 fix compile warnings in recovery, change images
gcc 4.4 complains about some of the recovery ui functions not being
declared.  To include the header, we have to fix the 'volatile'
declaration (otherwise there's a compiler error).

Move the dream-specific images to vendor/htc/dream, make the default
images a generic phone.
2009-07-23 10:36:43 -07:00
608fa02e1a resolved conflicts for merge of 64893ccc to master 2009-07-15 18:10:28 -07:00
64893ccc09 remove amend
Yank all the code to install OTA packages out of the recovery binary
itself.  Now packages are installed by a binary included in the
package (run as a child of recovery), so we can make improvements in
the installation process without waiting for a new release to use
them.
2009-07-14 16:58:42 -07:00
c457ff6436 am bec02d57: skip over all-zero blocks when reading MTD partition
Merge commit 'bec02d57fb85cc7dd0196a54b0e9530e306623ac'

* commit 'bec02d57fb85cc7dd0196a54b0e9530e306623ac':
  skip over all-zero blocks when reading MTD partition
2009-07-02 15:24:19 -07:00
bec02d57fb skip over all-zero blocks when reading MTD partition
We fail to detect certain bad blocks (marked in the factory as bad, I
think?) when reading mtd partitions.  These come back as a block of
all zeros.  Since it's fairly unlikely a legitimate boot or recovery
block will contain 128k of zeros, change mtdutils to skip over such
blocks.

Arve says https://review.source.android.com/10535 may be a long-term
fix for this, but he isn't yet sure.
2009-07-01 12:09:29 -07:00
fc382dfc75 am d8f7c9b8: Merge change 5545 into donut
Merge commit 'd8f7c9b85e25fab93fef2221a84b60edc2e7b837'

* commit 'd8f7c9b85e25fab93fef2221a84b60edc2e7b837':
  remove updater from the user system image
2009-06-29 16:39:15 -07:00
d8f7c9b85e Merge change 5545 into donut
* changes:
  remove updater from the user system image
2009-06-26 14:42:37 -07:00
ad3db099d5 remove updater from the user system image
updater (which is only needed in OTA packages) is getting included in
/system/bin, where it just takes up (quite a bit of) space.  Use the
hack of including it only in eng builds so it's not there for user
builds.
2009-06-26 13:38:55 -07:00
898ef399d3 am 0bbfe3d9: fix off-by-one error in set_perm()
Merge commit '0bbfe3d901885c1f0ab006e8d4cc1029c44a7376'

* commit '0bbfe3d901885c1f0ab006e8d4cc1029c44a7376':
  fix off-by-one error in set_perm()
2009-06-25 14:34:03 -07:00
0bbfe3d901 fix off-by-one error in set_perm()
We were inadvertently skipping over the first filename in the list of
arguments.
2009-06-25 13:37:31 -07:00
4275c3cfc8 am fbf3c10e: improve updater progress bar
Merge commit 'fbf3c10e45c20f8fe6bd1ac49ffe220035b9c454'

* commit 'fbf3c10e45c20f8fe6bd1ac49ffe220035b9c454':
  improve updater progress bar
2009-06-24 17:39:16 -07:00
fbf3c10e45 improve updater progress bar
Let recovery accept set_progress commands to control progress over the
'current segment' of the bar.  Add a set_progress() builtin to the
updater binary.
2009-06-24 09:36:20 -07:00
2b0fdc6571 add device extension mechanism to updater
Allow devices (in BoardConfig.mk) to define additional static
libraries to be linked in to updater, to make device-specific
functions available in edify scripts.  Modify the updater makefile to
arrange for device libraries to register their edify functions.
2009-06-22 14:00:44 -07:00
b128f54d0d add function for device-specific wipe data features
Some devices want to do special things when recovery wipes data (eg,
wipe data in their baseband processor as well).  Add a hook in the
device-specific recovery library that gets called when data is wiped.

Also add an amend root for the "mbm" partition.
2009-06-18 15:07:14 -07:00
f8aaf0a77f am 47cace98: add file_getprop() to updater
Merge commit '47cace98369f60df2351a65801c8065bb7f9dbf0'

* commit '47cace98369f60df2351a65801c8065bb7f9dbf0':
  add file_getprop() to updater
2009-06-18 12:57:25 -07:00
47cace9836 add file_getprop() to updater
Add a function to read a property from a ".prop"-formatted file
(key=value pairs, one per line, ignore # comment lines and blank
lines).  Move ErrorAbort to the core of edify; it's not specific to
updater now that errors aren't stored in the app cookie.
2009-06-18 10:11:50 -07:00
d683785ec9 resolved conflicts for merge of fb2e3af3 to master 2009-06-17 22:07:13 -07:00
fb2e3af3f9 let the "firmware" command take the file straight from the package
To do a firmware-install-on-reboot, the update binary tells recovery
what file to install before rebooting.  Let this file be specified as
"PACKAGE:<foo>" to indicate taking the file out of the OTA package,
avoiding an extra copy to /tmp.  Bump the API version number to
reflect this change.
2009-06-17 18:12:16 -07:00
e77e091522 am e3da02e7: add less_than_int, greater_than_int to edify
Merge commit 'e3da02e7bcfd85c543419e7590a3c86f64d8cc8a'

* commit 'e3da02e7bcfd85c543419e7590a3c86f64d8cc8a':
  add less_than_int, greater_than_int to edify
2009-06-15 14:28:17 -07:00
84cbfb6cb4 am d9c9d10d: fixes to edify and updater script
Merge commit 'd9c9d10d9da76f067d3955bea71f7bb39e859fa5'

* commit 'd9c9d10d9da76f067d3955bea71f7bb39e859fa5':
  fixes to edify and updater script
2009-06-14 21:12:26 -07:00
cf2b2a2e8f am 8edb00c9: edify extensions for OTA package installation, part 2
Merge commit '8edb00c990e563e6f91b278a212f2edf877cf763'

* commit '8edb00c990e563e6f91b278a212f2edf877cf763':
  edify extensions for OTA package installation, part 2
2009-06-14 20:49:32 -07:00
e3da02e7bc add less_than_int, greater_than_int to edify
Add functions less_than_int() and greater_than_int() that interpret
their args as ints and do the comparison.  ("<" and ">" operators, if
implemented, should do string comparison.)  This lets us do the build
time check currently done by the check_prereq binary.
2009-06-12 16:13:52 -07:00
d9c9d10d9d fixes to edify and updater script
A few more changes to edify:

  - fix write_raw_image(); my last change neglected to close the write
    context, so the written image was corrupt.

  - each expression tracks the span of the source code from which it
    was compiled, so that assert()'s error message can include the
    source of the expression that failed.

  - the 'cookie' argument to each Function is replaced with a State
    object, which contains the cookie, the source script (for use with
    the above spans), and the current error message (replacing the
    global variables that were used for this purpose).

  - in the recovery image, a new command "ui_print" can be sent back
    through the command pipe to cause text to appear on the screen.
    Add a new ui_print() function to print things from scripts.
    Rename existing "print" function to "stdout".
2009-06-12 14:05:03 -07:00
8edb00c990 edify extensions for OTA package installation, part 2
Adds more edify functions for OTAs:

  is_mounted getprop apply_patch apply_patch_check apply_patch_space
  write_raw_image write_firmware_image package_extract_file

This allows us to install radios, hboots, boot images, and install
incremental OTA packages.

Fixes a couple of dumb bugs in edify itself:

  - we were doubling the size of the function table each time it was
    *not* full, rather than each time it was full

  - "no such function" errors weren't visible to the parser, so they
    didn't prevent execution of the script.
2009-06-12 09:40:37 -07:00
32eb0a8c87 am 9dbc027b: fix sim build in donut, too
Merge commit '9dbc027b5f540bcf23c968398f8a70e92abd56cd'

* commit '9dbc027b5f540bcf23c968398f8a70e92abd56cd':
  fix sim build in donut, too
2009-06-12 08:25:49 -07:00
6c301e244d am 9931f7f3: edify extensions for OTA package installation, part 1
Merge commit '9931f7f3c1288171319e9ff7d053ebaad07db720'

* commit '9931f7f3c1288171319e9ff7d053ebaad07db720':
  edify extensions for OTA package installation, part 1
2009-06-12 08:25:38 -07:00
9dbc027b5f fix sim build in donut, too 2009-06-11 17:32:55 -07:00
c3885fabda fix simulator build by excluding more of recovery 2009-06-11 17:05:58 -07:00
9931f7f3c1 edify extensions for OTA package installation, part 1
Adds the following edify functions:

  mount unmount format show_progress delete delete_recursive
  package_extract symlink set_perm set_perm_recursive

This set is enough to extract and install the system part of a (full)
OTA package.

Adds the updater binary that extracts an edify script from the OTA
package and then executes it.  Minor changes to the edify core (adds a
sleep() builtin for debugging, adds "." to the set of characters that
can appear in an unquoted string).
2009-06-11 16:25:29 -07:00
cbb9129345 fix error from change 3606
When I changed the definition of device_perform_action() in response
to a reviewer comment, I forgot to change this implementation.
2009-06-11 14:57:53 -07:00
ddd6a2865d split out device-specific recovery UI code into vendor directories
Take some device-specific details of the recovery UI (eg, what keys to
press to bring up the interface and perform actions, exact text of the
menu, etc.) and split them out into separate C functions.  Arrange to
take implementations of those functions from the appropriate vendor
directory at build time.  Provide a default implementation in case no
vendor-specific one is available.
2009-06-11 14:50:33 -07:00
d9d9d1785a am 9b9c2114: Merge change 3514 into donut
Merge commit '9b9c2114bd7d02200ce43cb9ec513473079dfad9'

* commit '9b9c2114bd7d02200ce43cb9ec513473079dfad9':
  core of edify, an eventual replacement for amend
2009-06-10 08:12:32 -07:00
9b9c2114bd Merge change 3514 into donut
* changes:
  core of edify, an eventual replacement for amend
2009-06-10 08:10:29 -07:00
37bee62aef core of edify, an eventual replacement for amend
Edify is a simple scripting language for OTA installation, to be used
when we move to OTAs being installed via binaries shipped with the
package.
2009-06-09 17:37:19 -07:00
573fd7b68b Force the fb into 16 bpp mode in case the hw has some other default.
Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
2009-06-05 16:58:36 -07:00
825915dc6c am b2ee9201: allow OTA package to provide binary instead of script
Merge commit 'b2ee9201be583b17ddbf0eaa69a37545f992b565'

* commit 'b2ee9201be583b17ddbf0eaa69a37545f992b565':
  allow OTA package to provide binary instead of script
2009-06-05 00:29:47 -07:00
b2ee9201be allow OTA package to provide binary instead of script
Allow installation of OTA packages which do not contain an
update-script, but instead contain an update-binary.
2009-06-04 16:45:32 -07:00
8caf81fa24 am f28c916e: remove unused permissions scheme from amend
Merge commit 'f28c916e73ee9f643c67c70d059c70381d774cb0'

* commit 'f28c916e73ee9f643c67c70d059c70381d774cb0':
  remove unused permissions scheme from amend
2009-06-03 08:39:37 -07:00
f28c916e73 remove unused permissions scheme from amend
Amend (aka the recovery command language) had a half-implemented
scheme of limiting which commands OTA packages were allowed to
execute.  It's not clear what this was ever supposed to be good for.
Remove it.
2009-06-02 15:41:00 -07:00
9d5be8488f am 07e1dca7: don\'t say "install complete" when it really isn\'t
Merge commit '07e1dca7068284c4f3013550335029eb72b39b82'

* commit '07e1dca7068284c4f3013550335029eb72b39b82':
  don't say "install complete" when it really isn't
2009-06-01 14:35:16 -07:00
07e1dca706 don't say "install complete" when it really isn't
Change the recovery UI so that when there is a hboot or radio update
pending (which the user most do a home+back reboot to actually
install), the UI tells them so, instead of saying "Install from sdcard
complete."
2009-05-28 19:02:45 -07:00
985d95f9f4 am 1c4ceae: undo temporary alignment hack
Merge commit '1c4ceae38f3fd7eb1e451d430acb5d99f257b0f9'

* commit '1c4ceae38f3fd7eb1e451d430acb5d99f257b0f9':
  undo temporary alignment hack
2009-05-08 12:22:51 -07:00
1c4ceae38f undo temporary alignment hack
Remove the memory alignment that mysteriously made OTA installs work,
in anticipation of a kernel that fixes the actual problem.  Handle
EINTR properly.
2009-05-08 09:43:28 -07:00
1c10ff34b5 am 683c462: align data passed to write() on 32k boundaries
Merge commit '683c4628039a8cb6dad1a086fae23a7d71438414'

* commit '683c4628039a8cb6dad1a086fae23a7d71438414':
  align data passed to write() on 32k boundaries
2009-05-06 11:31:43 -07:00
683c462803 align data passed to write() on 32k boundaries
In donut, OTA installation often encounters the write() system call
doing short writes -- which is legal but unexpected -- or failing with
ENOSPC when plenty of space is available.  Passing aligned memory
buffers to write() appears to prevent (or at least reduce the
frequency) of these problems.  b/1833052 has been filed to look at the
underlying problem, but this change aligns buffers we use with write()
so we can OTA for now (or see if this problem still occurs).
2009-05-06 08:40:28 -07:00
0f03d1408d am 596271f: handle short writes when unzipping files
Merge commit '596271fa71d79e3eec03c7cf6ac76cb026dd8578'

* commit '596271fa71d79e3eec03c7cf6ac76cb026dd8578':
  handle short writes when unzipping files
2009-04-29 21:20:20 -07:00
596271fa71 handle short writes when unzipping files
minzip fails if write() doesn't write all the data in one call.
Apparently this was good enough before, but it causes OTAs to fail all
the time now (maybe due to the recently-submitted kernel)?  Change
code to attempt continuing after short writes.
2009-04-29 17:35:34 -07:00
7fce23fbb5 Merge donut into master 2009-04-22 18:33:11 -07:00
f554ceb050 merge cupcake into donut 2009-04-22 17:56:50 -07:00
29fcea1564 Merge donut into master 2009-04-22 17:12:09 -07:00
275acbe70a Merge branch 'readonly-p4-master' 2009-04-02 10:13:29 -07:00
796901d3b0 AI 144132: am: CL 144130 Don't build OTA package keys into the recovery binary; read
them from an external file in the recovery image.  Use the
  test-keys for all builds.
  Original author: dougz
  Merged from: //branches/donutburger/...

Automated import of CL 144132
2009-04-02 10:12:24 -07:00
d5ebb7b06d Merge branch 'readonly-p4-master' 2009-04-02 09:52:18 -07:00
0555388b8c AI 144105: am: CL 144082 Remove the unused "unpacking" recovery icon.
Original author: dougz
  Merged from: //branches/donutburger/...

Automated import of CL 144105
2009-04-02 09:51:09 -07:00
5d30026ddb Merge branch 'readonly-p4-master' 2009-04-02 09:41:51 -07:00
fc3ada0c11 AI 144101: am: CL 144070 Add an option to wipe cache (only) to the recovery menu.
Original author: dougz
  Merged from: //branches/donutburger/...

Automated import of CL 144101
2009-04-02 09:40:40 -07:00
d1b19b9c98 AI 144130: Don't build OTA package keys into the recovery binary; read
them from an external file in the recovery image.  Use the
  test-keys for all builds.

Automated import of CL 144130
2009-04-01 15:48:46 -07:00
49283858fb AI 144082: Remove the unused "unpacking" recovery icon.
Automated import of CL 144082
2009-04-01 14:39:15 -07:00
1066d2c319 AI 144070: Add an option to wipe cache (only) to the recovery menu.
Automated import of CL 144070
2009-04-01 13:57:40 -07:00
36a9c1e530 Merge branch 'open_source_no_contributions' into google_internal 2009-03-29 08:55:03 -07:00
19faefad05 AI 143289: am: CL 143128 Use PNG instead of BMP for recovery image icons. This saves
about 60k from the recovery and system images.
  Original author: dougz
  Merged from: //branches/donutburger/...

Automated import of CL 143289
2009-03-27 17:06:24 -07:00
b2b467c757 Merge commit 'korg/cupcake' 2009-03-27 15:30:35 -07:00
58bde316e2 AI 143128: Use PNG instead of BMP for recovery image icons. This saves
about 60k from the recovery and system images.

Automated import of CL 143128
2009-03-27 13:25:30 -07:00
e6faba0580 Automated import from //branches/master/...@142142,142142 2009-03-24 22:17:05 -07:00
bc012de46e Automated import from //branches/donutburger/...@142141,142141 2009-03-24 21:30:32 -07:00
cf5b17055b Automated import from //branches/donutburger/...@140818,140818 2009-03-24 18:36:43 -07:00
97c618f378 Automated import from //branches/master/...@140824,140824 2009-03-24 18:36:42 -07:00
c5c389f8f2 Merge commit 'remotes/korg/cupcake' into cupcake_to_master 2009-03-18 16:57:16 -07:00
fac53c1677 Remove obsolete OTA tools
Resolves http://code.google.com/p/android/issues/detail?id=2077
2009-03-02 12:33:57 -08:00
6d12e0d6f8 Merge branch 'cupcake' 2009-01-09 18:03:37 -08:00
92 changed files with 7114 additions and 1018 deletions

View File

@ -1,27 +1,134 @@
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_ARCH),arm)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
commands_recovery_local_path := $(LOCAL_PATH)
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_ARCH),arm)
# LOCAL_CPP_EXTENSION := .c
LOCAL_SRC_FILES := \
recovery.c \
bootloader.c \
extendedcommands.c \
nandroid.c \
legacy.c \
commands.c \
firmware.c \
recovery.c \
install.c \
roots.c \
ui.c \
verifier.c
LOCAL_SRC_FILES += \
reboot.c \
setprop.c
ifndef BOARD_HAS_NO_MISC_PARTITION
LOCAL_SRC_FILES += \
firmware.c \
bootloader.c
else
LOCAL_CFLAGS += -DBOARD_HAS_NO_MISC_PARTITION
endif
ifdef BOARD_RECOVERY_IGNORE_BOOTABLES
LOCAL_CFLAGS += -DBOARD_RECOVERY_IGNORE_BOOTABLES
endif
ifdef BOARD_HIJACK_RECOVERY_PATH
LOCAL_CFLAGS += -DBOARD_HIJACK_RECOVERY_PATH=\"$(BOARD_HIJACK_RECOVERY_PATH)\"
endif
LOCAL_SRC_FILES += test_roots.c
LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
RECOVERY_VERSION := ClockworkMod Recovery v2.5.1.0
LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
RECOVERY_API_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
ifeq ($(BOARD_HAS_NO_SELECT_BUTTON),true)
LOCAL_CFLAGS += -DKEY_POWER_IS_SELECT_ITEM
endif
ifdef BOARD_SDCARD_DEVICE_PRIMARY
LOCAL_CFLAGS += -DSDCARD_DEVICE_PRIMARY=\"$(BOARD_SDCARD_DEVICE_PRIMARY)\"
endif
ifdef BOARD_SDCARD_DEVICE_SECONDARY
LOCAL_CFLAGS += -DSDCARD_DEVICE_SECONDARY=\"$(BOARD_SDCARD_DEVICE_SECONDARY)\"
endif
ifdef BOARD_SDEXT_DEVICE
LOCAL_CFLAGS += -DSDEXT_DEVICE=\"$(BOARD_SDEXT_DEVICE)\"
endif
ifdef BOARD_SDEXT_FILESYSTEM
LOCAL_CFLAGS += -DSDEXT_FILESYSTEM=\"$(BOARD_SDEXT_FILESYSTEM)\"
endif
ifdef BOARD_DATA_DEVICE
LOCAL_CFLAGS += -DDATA_DEVICE=\"$(BOARD_DATA_DEVICE)\"
endif
ifdef BOARD_DATA_FILESYSTEM
LOCAL_CFLAGS += -DDATA_FILESYSTEM=\"$(BOARD_DATA_FILESYSTEM)\"
endif
ifdef BOARD_DATADATA_DEVICE
LOCAL_CFLAGS += -DDATADATA_DEVICE=\"$(BOARD_DATADATA_DEVICE)\"
endif
ifdef BOARD_DATADATA_FILESYSTEM
LOCAL_CFLAGS += -DDATADATA_FILESYSTEM=\"$(BOARD_DATADATA_FILESYSTEM)\"
endif
ifdef BOARD_CACHE_DEVICE
LOCAL_CFLAGS += -DCACHE_DEVICE=\"$(BOARD_CACHE_DEVICE)\"
endif
ifdef BOARD_CACHE_FILESYSTEM
LOCAL_CFLAGS += -DCACHE_FILESYSTEM=\"$(BOARD_CACHE_FILESYSTEM)\"
endif
ifdef BOARD_SYSTEM_DEVICE
LOCAL_CFLAGS += -DSYSTEM_DEVICE=\"$(BOARD_SYSTEM_DEVICE)\"
endif
ifdef BOARD_SYSTEM_FILESYSTEM
LOCAL_CFLAGS += -DSYSTEM_FILESYSTEM=\"$(BOARD_SYSTEM_FILESYSTEM)\"
endif
ifdef BOARD_HAS_DATADATA
LOCAL_CFLAGS += -DHAS_DATADATA
endif
ifdef BOARD_DATA_FILESYSTEM_OPTIONS
LOCAL_CFLAGS += -DDATA_FILESYSTEM_OPTIONS=\"$(BOARD_DATA_FILESYSTEM_OPTIONS)\"
endif
ifdef BOARD_DATADATA_FILESYSTEM_OPTIONS
LOCAL_CFLAGS += -DDATADATA_FILESYSTEM_OPTIONS=\"$(BOARD_DATADATA_FILESYSTEM_OPTIONS)\"
endif
ifdef BOARD_CACHE_FILESYSTEM_OPTIONS
LOCAL_CFLAGS += -DCACHE_FILESYSTEM_OPTIONS=\"$(BOARD_CACHE_FILESYSTEM_OPTIONS)\"
endif
ifdef BOARD_SYSTEM_FILESYSTEM_OPTIONS
LOCAL_CFLAGS += -DSYSTEM_FILESYSTEM_OPTIONS=\"$(BOARD_SYSTEM_FILESYSTEM_OPTIONS)\"
endif
ifdef BOARD_HAS_MTD_CACHE
LOCAL_CFLAGS += -DBOARD_HAS_MTD_CACHE
endif
ifdef BOARD_HAS_SMALL_RECOVERY
LOCAL_CFLAGS += -DBOARD_HAS_SMALL_RECOVERY
endif
# This binary is in the recovery ramdisk, which is otherwise a copy of root.
# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses
# a (redundant) copy of the binary in /system/bin for user builds.
@ -29,32 +136,71 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libcutils
LOCAL_STATIC_LIBRARIES :=
ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
LOCAL_SRC_FILES += default_recovery_ui.c
else
LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING)
endif
LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image libmtdutils
LOCAL_STATIC_LIBRARIES += libamend
LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
LOCAL_STATIC_LIBRARIES += libstdc++ libc
# Specify a C-includable file containing the OTA public keys.
# This is built in config/Makefile.
# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT
# PRODUCTS/BUILD TYPES. ***
# TODO: make recovery read the keys from an external file.
RECOVERY_INSTALL_OTA_KEYS_INC := \
$(call intermediates-dir-for,PACKAGING,ota_keys_inc)/keys.inc
# Let install.c say #include "keys.inc"
LOCAL_C_INCLUDES += $(dir $(RECOVERY_INSTALL_OTA_KEYS_INC))
include $(BUILD_EXECUTABLE)
# Depend on the generated keys.inc containing the OTA public keys.
$(intermediates)/install.o: $(RECOVERY_INSTALL_OTA_KEYS_INC)
RECOVERY_LINKS := amend busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
# nc is provided by external/netcat
SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
$(SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
@echo "Symlink: $@ -> $(RECOVERY_BINARY)"
@mkdir -p $(dir $@)
@rm -rf $@
$(hide) ln -sf $(RECOVERY_BINARY) $@
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
# Now let's do recovery symlinks
BUSYBOX_LINKS := $(shell cat external/busybox/busybox-minimal.links)
SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
$(SYMLINKS): BUSYBOX_BINARY := busybox
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
@echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
@mkdir -p $(dir $@)
@rm -rf $@
$(hide) ln -sf $(BUSYBOX_BINARY) $@
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
include $(CLEAR_VARS)
LOCAL_MODULE := nandroid-md5.sh
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := nandroid-md5.sh
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := killrecovery.sh
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
LOCAL_SRC_FILES := killrecovery.sh
include $(BUILD_PREBUILT)
include $(commands_recovery_local_path)/amend/Android.mk
include $(commands_recovery_local_path)/minui/Android.mk
include $(commands_recovery_local_path)/minzip/Android.mk
include $(commands_recovery_local_path)/mtdutils/Android.mk
include $(commands_recovery_local_path)/tools/Android.mk
include $(commands_recovery_local_path)/edify/Android.mk
include $(commands_recovery_local_path)/updater/Android.mk
include $(commands_recovery_local_path)/utilities/Android.mk
commands_recovery_local_path :=
endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR
include $(commands_recovery_local_path)/amend/Android.mk
include $(commands_recovery_local_path)/minzip/Android.mk
include $(commands_recovery_local_path)/mtdutils/Android.mk
include $(commands_recovery_local_path)/tools/Android.mk
commands_recovery_local_path :=

View File

@ -125,6 +125,19 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
return -1;
}
/* delete <srcdir> <dstdir>
*/
static int
cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
/* mark <resource> dirty|clean
*/
static int
@ -165,6 +178,9 @@ registerUpdateCommands()
ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
if (ret < 0) return ret;
ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, NULL);
if (ret < 0) return ret;
ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
if (ret < 0) return ret;

View File

@ -198,7 +198,7 @@ int write_update_for_bootloader(
header.version = UPDATE_VERSION;
header.size = header_size;
header.image_offset = mtd_erase_blocks(write, 0);
off_t image_start_pos = mtd_erase_blocks(write, 0);
header.image_length = update_length;
if ((int) header.image_offset == -1 ||
mtd_write_data(write, update, update_length) != update_length) {
@ -206,6 +206,8 @@ int write_update_for_bootloader(
mtd_write_close(write);
return -1;
}
off_t busy_start_pos = mtd_erase_blocks(write, 0);
header.image_offset = mtd_find_write_start(write, image_start_pos);
header.bitmap_width = bitmap_width;
header.bitmap_height = bitmap_height;
@ -213,7 +215,6 @@ int write_update_for_bootloader(
int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
header.busy_bitmap_offset = mtd_erase_blocks(write, 0);
header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
if ((int) header.busy_bitmap_offset == -1 ||
mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
@ -221,8 +222,9 @@ int write_update_for_bootloader(
mtd_write_close(write);
return -1;
}
off_t fail_start_pos = mtd_erase_blocks(write, 0);
header.busy_bitmap_offset = mtd_find_write_start(write, busy_start_pos);
header.fail_bitmap_offset = mtd_erase_blocks(write, 0);
header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
if ((int) header.fail_bitmap_offset == -1 ||
mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
@ -230,6 +232,8 @@ int write_update_for_bootloader(
mtd_write_close(write);
return -1;
}
mtd_erase_blocks(write, 0);
header.fail_bitmap_offset = mtd_find_write_start(write, fail_start_pos);
/* Write the header last, after all the blocks it refers to, so that
* when the magic number is installed everything is valid.
@ -252,7 +256,7 @@ int write_update_for_bootloader(
return -1;
}
if (mtd_erase_blocks(write, 0) != (off_t) header.image_offset) {
if (mtd_erase_blocks(write, 0) != image_start_pos) {
LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;

View File

@ -39,6 +39,8 @@
#include "minzip/Zip.h"
#include "roots.h"
#include "extendedcommands.h"
static int gDidShowProgress = 0;
#define UNUSED(p) ((void)(p))
@ -107,6 +109,10 @@ cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
CHECK_BOOL();
NO_PERMS(permissions);
if (!script_assert_enabled) {
return 0;
}
/* If our argument is false, return non-zero (failure)
* If our argument is true, return zero (success)
*/
@ -139,7 +145,15 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[],
LOGE("Can't format %s\n", root);
return 1;
}
#ifdef HAS_DATADATA
if (0 == strcmp(root, "DATA:")) {
ret = format_root_device("DATADATA:");
if (ret != 0) {
LOGE("Can't format %s\n", root);
return 1;
}
}
#endif
return 0;
}
@ -342,39 +356,40 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
return 1;
}
// Copy the program file to temporary storage.
if (!is_package_root_path(argv[0])) {
LOGE("Command %s: non-package program file \"%s\" not supported\n",
name, argv[0]);
return 1;
}
char path[PATH_MAX];
const ZipArchive *package;
if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
return 1;
}
const ZipEntry *entry = mzFindZipEntry(package, path);
if (entry == NULL) {
LOGE("Can't find %s\n", path);
return 1;
}
static const char *binary = "/tmp/run_program_binary";
unlink(binary); // just to be sure
int fd = creat(binary, 0755);
if (fd < 0) {
LOGE("Can't make %s\n", binary);
return 1;
char path[PATH_MAX];
if (!is_package_root_path(argv[0])) {
// Copy the program file to temporary storage.
binary = argv[0];
strcpy(path, binary);
}
bool ok = mzExtractZipEntryToFile(package, entry, fd);
close(fd);
else
{
const ZipArchive *package;
if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
return 1;
}
if (!ok) {
LOGE("Can't copy %s\n", path);
return 1;
const ZipEntry *entry = mzFindZipEntry(package, path);
if (entry == NULL) {
LOGE("Can't find %s\n", path);
return 1;
}
unlink(binary); // just to be sure
int fd = creat(binary, 0755);
if (fd < 0) {
LOGE("Can't make %s\n", binary);
return 1;
}
bool ok = mzExtractZipEntryToFile(package, entry, fd);
close(fd);
if (!ok) {
LOGE("Can't copy %s\n", path);
return 1;
}
}
// Create a copy of argv to NULL-terminate it, as execv requires
@ -391,7 +406,7 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) {
return 0;
} else {
LOGE("Error in %s\n(Status %d)\n", path, status);
@ -611,11 +626,13 @@ cmd_write_firmware_image(const char *name, void *cookie,
return 1;
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (remember_firmware_update(type, context.data, context.total_bytes)) {
LOGE("Can't store %s image\n", type);
free(context.data);
return 1;
}
#endif
return 0;
}
@ -753,6 +770,119 @@ cmd_done(const char *name, void *cookie, int argc, const char *argv[],
}
static int
cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(cookie);
CHECK_WORDS();
char* backup_name = NULL;
char backup_path[PATH_MAX];
switch(argc)
{
case 0:
{
char backup_path[PATH_MAX];
nandroid_generate_timestamp_path(backup_path);
backup_name = backup_path;
}
break;
case 1:
backup_name = argv[0];
break;
default:
LOGE("Command %s requires zero or one argument\n", name);
return 1;
}
return nandroid_backup(backup_name);
}
static int
cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(cookie);
CHECK_WORDS();
int restoreboot = 1;
int restoresystem = 1;
int restoredata = 1;
int restorecache = 1;
int restoresdext = 1;
int i;
for (i = 0; i < argc; i++)
{
if (strcmp(argv[i], "noboot") == 0)
restoreboot = 0;
else if (strcmp(argv[i], "nosystem") == 0)
restoresystem = 0;
else if (strcmp(argv[i], "nodata") == 0)
restoredata = 0;
else if (strcmp(argv[i], "nocache") == 0)
restorecache = 0;
else if (strcmp(argv[i], "nosd-ext") == 0)
restorecache = 0;
}
return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache, restoresdext);
}
static int
cmd_sleep(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(cookie);
CHECK_WORDS();
if (argc != 1) {
LOGE("Command %s requires exactly one argument\n", name);
return 1;
}
int seconds = atoi(argv[0]);
sleep(seconds);
return 0;
}
static int
cmd_print(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(cookie);
CHECK_WORDS();
char message[1024];
message[0] = NULL;
int i;
for (i = 0; i < argc; i++)
{
strcat(message, argv[i]);
strcat(message, " ");
}
strcat(message, "\n");
ui_print(message);
return 0;
}
static int
cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(cookie);
CHECK_WORDS();
if (argc != 1) {
LOGE("Command %s requires exactly one argument\n", name);
return 1;
}
return install_zip(argv[0]);
}
/*
* Function definitions
*/
@ -1116,6 +1246,21 @@ register_update_commands(RecoveryCommandContext *ctx)
ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
if (ret < 0) return ret;
ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx);
if (ret < 0) return ret;
ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx);
if (ret < 0) return ret;
ret = registerCommand("sleep", CMD_ARGS_WORDS, cmd_sleep, (void *)ctx);
if (ret < 0) return ret;
ret = registerCommand("print", CMD_ARGS_WORDS, cmd_print, (void *)ctx);
if (ret < 0) return ret;
ret = registerCommand("install_zip", CMD_ARGS_WORDS, cmd_install_zip, (void *)ctx);
if (ret < 0) return ret;
/*
* Functions
*/

View File

@ -33,10 +33,13 @@ void ui_clear_key_queue();
// so keep the output short and not too cryptic.
void ui_print(const char *fmt, ...);
void ui_reset_text_col();
void ui_set_show_text(int value);
// Display some header text followed by a menu of items, which appears
// at the top of the screen (in place of any scrolling ui_print()
// output, if necessary).
void ui_start_menu(char** headers, char** items);
int ui_start_menu(char** headers, char** items);
// Set the menu highlight to the given index, and return it (capped to
// the range [0..numitems).
int ui_menu_select(int sel);
@ -44,10 +47,12 @@ int ui_menu_select(int sel);
// statements will be displayed.
void ui_end_menu();
int ui_get_showing_back_button();
void ui_set_showing_back_button(int showBackButton);
// Set the icon (normally the only thing visible besides the progress bar).
enum {
BACKGROUND_ICON_NONE,
BACKGROUND_ICON_UNPACKING,
BACKGROUND_ICON_INSTALLING,
BACKGROUND_ICON_ERROR,
BACKGROUND_ICON_FIRMWARE_INSTALLING,
@ -69,7 +74,7 @@ void ui_set_progress(float fraction); // 0.0 - 1.0 within the defined scope
// Default allocation of progress bar segments to operations
static const int VERIFICATION_PROGRESS_TIME = 60;
static const float VERIFICATION_PROGRESS_FRACTION = 0.5;
static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
@ -91,4 +96,7 @@ void ui_reset_progress();
#define LOGD(...) do {} while (0)
#endif
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
#endif // RECOVERY_COMMON_H

96
default_recovery_ui.c Normal file
View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <linux/input.h>
#include "recovery_ui.h"
#include "common.h"
#include "extendedcommands.h"
char* MENU_HEADERS[] = { NULL };
char* MENU_ITEMS[] = { "reboot system now",
"apply sdcard:update.zip",
"wipe data/factory reset",
"wipe cache partition",
"install zip from sdcard",
"backup and restore",
"mounts and storage",
"advanced",
NULL };
int device_toggle_display(volatile char* key_pressed, int key_code) {
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
if (alt && key_code == KEY_L)
return 1;
// allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
if (ui_get_showing_back_button()) {
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
}
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END);
}
int device_reboot_now(volatile char* key_pressed, int key_code) {
return 0;
}
int device_handle_key(int key_code, int visible) {
if (visible) {
switch (key_code) {
case KEY_CAPSLOCK:
case KEY_DOWN:
case KEY_VOLUMEDOWN:
return HIGHLIGHT_DOWN;
case KEY_LEFTSHIFT:
case KEY_UP:
case KEY_VOLUMEUP:
return HIGHLIGHT_UP;
case KEY_POWER:
if (ui_get_showing_back_button()) {
return SELECT_ITEM;
}
if (!get_allow_toggle_display())
return GO_BACK;
break;
case KEY_LEFTBRACE:
case KEY_ENTER:
case BTN_MOUSE:
case KEY_CENTER:
case KEY_CAMERA:
case KEY_F21:
case KEY_SEND:
return SELECT_ITEM;
case KEY_END:
case KEY_BACKSPACE:
case KEY_BACK:
if (!get_allow_toggle_display())
return GO_BACK;
}
}
return NO_ACTION;
}
int device_perform_action(int which) {
return which;
}
int device_wipe_data() {
return 0;
}

39
edify/Android.mk Normal file
View File

@ -0,0 +1,39 @@
# Copyright 2009 The Android Open Source Project
LOCAL_PATH := $(call my-dir)
edify_src_files := \
lexer.l \
parser.y \
expr.c
# "-x c" forces the lex/yacc files to be compiled as c;
# the build system otherwise forces them to be c++.
edify_cflags := -x c
#
# Build the host-side command line tool
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(edify_src_files) \
main.c
LOCAL_CFLAGS := $(edify_cflags) -g -O0
LOCAL_MODULE := edify
LOCAL_YACCFLAGS := -v
include $(BUILD_HOST_EXECUTABLE)
#
# Build the device-side library
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(edify_src_files)
LOCAL_CFLAGS := $(edify_cflags)
LOCAL_MODULE := libedify
include $(BUILD_STATIC_LIBRARY)

108
edify/README Normal file
View File

@ -0,0 +1,108 @@
Update scripts (from donut onwards) are written in a new little
scripting language ("edify") that is superficially somewhat similar to
the old one ("amend"). This is a brief overview of the new language.
- The entire script is a single expression.
- All expressions are string-valued.
- String literals appear in double quotes. \n, \t, \", and \\ are
understood, as are hexadecimal escapes like \x4a.
- String literals consisting of only letters, numbers, colons,
underscores, slashes, and periods don't need to be in double quotes.
- The following words are reserved:
if then else endif
They have special meaning when unquoted. (In quotes, they are just
string literals.)
- When used as a boolean, the empty string is "false" and all other
strings are "true".
- All functions are actually macros (in the Lisp sense); the body of
the function can control which (if any) of the arguments are
evaluated. This means that functions can act as control
structures.
- Operators (like "&&" and "||") are just syntactic sugar for builtin
functions, so they can act as control structures as well.
- ";" is a binary operator; evaluating it just means to first evaluate
the left side, then the right. It can also appear after any
expression.
- Comments start with "#" and run to the end of the line.
Some examples:
- There's no distinction between quoted and unquoted strings; the
quotes are only needed if you want characters like whitespace to
appear in the string. The following expressions all evaluate to the
same string.
"a b"
a + " " + b
"a" + " " + "b"
"a\x20b"
a + "\x20b"
concat(a, " ", "b")
"concat"(a, " ", "b")
As shown in the last example, function names are just strings,
too. They must be string *literals*, however. This is not legal:
("con" + "cat")(a, " ", b) # syntax error!
- The ifelse() builtin takes three arguments: it evaluates exactly
one of the second and third, depending on whether the first one is
true. There is also some syntactic sugar to make expressions that
look like if/else statements:
# these are all equivalent
ifelse(something(), "yes", "no")
if something() then yes else no endif
if something() then "yes" else "no" endif
The else part is optional.
if something() then "yes" endif # if something() is false,
# evaluates to false
ifelse(condition(), "", abort()) # abort() only called if
# condition() is false
The last example is equivalent to:
assert(condition())
- The && and || operators can be used similarly; they evaluate their
second argument only if it's needed to determine the truth of the
expression. Their value is the value of the last-evaluated
argument:
file_exists("/data/system/bad") && delete("/data/system/bad")
file_exists("/data/system/missing") || create("/data/system/missing")
get_it() || "xxx" # returns value of get_it() if that value is
# true, otherwise returns "xxx"
- The purpose of ";" is to simulate imperative statements, of course,
but the operator can be used anywhere. Its value is the value of
its right side:
concat(a;b;c, d, e;f) # evaluates to "cdf"
A more useful example might be something like:
ifelse(condition(),
(first_step(); second_step();), # second ; is optional
alternative_procedure())

432
edify/expr.c Normal file
View File

@ -0,0 +1,432 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include "expr.h"
// Functions should:
//
// - return a malloc()'d string
// - if Evaluate() on any argument returns NULL, return NULL.
int BooleanString(const char* s) {
return s[0] != '\0';
}
char* Evaluate(State* state, Expr* expr) {
return expr->fn(expr->name, state, expr->argc, expr->argv);
}
char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc == 0) {
return strdup("");
}
char** strings = malloc(argc * sizeof(char*));
int i;
for (i = 0; i < argc; ++i) {
strings[i] = NULL;
}
char* result = NULL;
int length = 0;
for (i = 0; i < argc; ++i) {
strings[i] = Evaluate(state, argv[i]);
if (strings[i] == NULL) {
goto done;
}
length += strlen(strings[i]);
}
result = malloc(length+1);
int p = 0;
for (i = 0; i < argc; ++i) {
strcpy(result+p, strings[i]);
p += strlen(strings[i]);
}
result[p] = '\0';
done:
for (i = 0; i < argc; ++i) {
free(strings[i]);
}
return result;
}
char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2 && argc != 3) {
free(state->errmsg);
state->errmsg = strdup("ifelse expects 2 or 3 arguments");
return NULL;
}
char* cond = Evaluate(state, argv[0]);
if (cond == NULL) {
return NULL;
}
if (BooleanString(cond) == true) {
free(cond);
return Evaluate(state, argv[1]);
} else {
if (argc == 3) {
free(cond);
return Evaluate(state, argv[2]);
} else {
return cond;
}
}
}
char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
char* msg = NULL;
if (argc > 0) {
msg = Evaluate(state, argv[0]);
}
free(state->errmsg);
if (msg) {
state->errmsg = msg;
} else {
state->errmsg = strdup("called abort()");
}
return NULL;
}
char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
int i;
for (i = 0; i < argc; ++i) {
char* v = Evaluate(state, argv[i]);
if (v == NULL) {
return NULL;
}
int b = BooleanString(v);
free(v);
if (!b) {
int prefix_len;
int len = argv[i]->end - argv[i]->start;
char* err_src = malloc(len + 20);
strcpy(err_src, "assert failed: ");
prefix_len = strlen(err_src);
memcpy(err_src + prefix_len, state->script + argv[i]->start, len);
err_src[prefix_len + len] = '\0';
free(state->errmsg);
state->errmsg = err_src;
return NULL;
}
}
return strdup("");
}
char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
char* val = Evaluate(state, argv[0]);
if (val == NULL) {
return NULL;
}
int v = strtol(val, NULL, 10);
sleep(v);
return val;
}
char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
int i;
for (i = 0; i < argc; ++i) {
char* v = Evaluate(state, argv[i]);
if (v == NULL) {
return NULL;
}
fputs(v, stdout);
free(v);
}
return strdup("");
}
char* LogicalAndFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* left = Evaluate(state, argv[0]);
if (left == NULL) return NULL;
if (BooleanString(left) == true) {
free(left);
return Evaluate(state, argv[1]);
} else {
return left;
}
}
char* LogicalOrFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* left = Evaluate(state, argv[0]);
if (left == NULL) return NULL;
if (BooleanString(left) == false) {
free(left);
return Evaluate(state, argv[1]);
} else {
return left;
}
}
char* LogicalNotFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* val = Evaluate(state, argv[0]);
if (val == NULL) return NULL;
bool bv = BooleanString(val);
free(val);
if (bv) {
return strdup("");
} else {
return strdup("t");
}
}
char* SubstringFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* needle = Evaluate(state, argv[0]);
if (needle == NULL) return NULL;
char* haystack = Evaluate(state, argv[1]);
if (haystack == NULL) {
free(needle);
return NULL;
}
char* result = strdup(strstr(haystack, needle) ? "t" : "");
free(needle);
free(haystack);
return result;
}
char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
char* left = Evaluate(state, argv[0]);
if (left == NULL) return NULL;
char* right = Evaluate(state, argv[1]);
if (right == NULL) {
free(left);
return NULL;
}
char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
free(left);
free(right);
return result;
}
char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
char* left = Evaluate(state, argv[0]);
if (left == NULL) return NULL;
char* right = Evaluate(state, argv[1]);
if (right == NULL) {
free(left);
return NULL;
}
char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
free(left);
free(right);
return result;
}
char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
char* left = Evaluate(state, argv[0]);
if (left == NULL) return NULL;
free(left);
return Evaluate(state, argv[1]);
}
char* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2) {
free(state->errmsg);
state->errmsg = strdup("less_than_int expects 2 arguments");
return NULL;
}
char* left;
char* right;
if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL;
bool result = false;
char* end;
long l_int = strtol(left, &end, 10);
if (left[0] == '\0' || *end != '\0') {
fprintf(stderr, "[%s] is not an int\n", left);
goto done;
}
long r_int = strtol(right, &end, 10);
if (right[0] == '\0' || *end != '\0') {
fprintf(stderr, "[%s] is not an int\n", right);
goto done;
}
result = l_int < r_int;
done:
free(left);
free(right);
return strdup(result ? "t" : "");
}
char* GreaterThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2) {
free(state->errmsg);
state->errmsg = strdup("greater_than_int expects 2 arguments");
return NULL;
}
Expr* temp[2];
temp[0] = argv[1];
temp[1] = argv[0];
return LessThanIntFn(name, state, 2, temp);
}
char* Literal(const char* name, State* state, int argc, Expr* argv[]) {
return strdup(name);
}
Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
va_list v;
va_start(v, count);
Expr* e = malloc(sizeof(Expr));
e->fn = fn;
e->name = "(operator)";
e->argc = count;
e->argv = malloc(count * sizeof(Expr*));
int i;
for (i = 0; i < count; ++i) {
e->argv[i] = va_arg(v, Expr*);
}
va_end(v);
e->start = loc.start;
e->end = loc.end;
return e;
}
// -----------------------------------------------------------------
// the function table
// -----------------------------------------------------------------
static int fn_entries = 0;
static int fn_size = 0;
NamedFunction* fn_table = NULL;
void RegisterFunction(const char* name, Function fn) {
if (fn_entries >= fn_size) {
fn_size = fn_size*2 + 1;
fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
}
fn_table[fn_entries].name = name;
fn_table[fn_entries].fn = fn;
++fn_entries;
}
static int fn_entry_compare(const void* a, const void* b) {
const char* na = ((const NamedFunction*)a)->name;
const char* nb = ((const NamedFunction*)b)->name;
return strcmp(na, nb);
}
void FinishRegistration() {
qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
}
Function FindFunction(const char* name) {
NamedFunction key;
key.name = name;
NamedFunction* nf = bsearch(&key, fn_table, fn_entries,
sizeof(NamedFunction), fn_entry_compare);
if (nf == NULL) {
return NULL;
}
return nf->fn;
}
void RegisterBuiltins() {
RegisterFunction("ifelse", IfElseFn);
RegisterFunction("abort", AbortFn);
RegisterFunction("assert", AssertFn);
RegisterFunction("concat", ConcatFn);
RegisterFunction("is_substring", SubstringFn);
RegisterFunction("stdout", StdoutFn);
RegisterFunction("sleep", SleepFn);
RegisterFunction("less_than_int", LessThanIntFn);
RegisterFunction("greater_than_int", GreaterThanIntFn);
}
// -----------------------------------------------------------------
// convenience methods for functions
// -----------------------------------------------------------------
// Evaluate the expressions in argv, giving 'count' char* (the ... is
// zero or more char** to put them in). If any expression evaluates
// to NULL, free the rest and return -1. Return 0 on success.
int ReadArgs(State* state, Expr* argv[], int count, ...) {
char** args = malloc(count * sizeof(char*));
va_list v;
va_start(v, count);
int i;
for (i = 0; i < count; ++i) {
args[i] = Evaluate(state, argv[i]);
if (args[i] == NULL) {
va_end(v);
int j;
for (j = 0; j < i; ++j) {
free(args[j]);
}
return -1;
}
*(va_arg(v, char**)) = args[i];
}
va_end(v);
return 0;
}
// Evaluate the expressions in argv, returning an array of char*
// results. If any evaluate to NULL, free the rest and return NULL.
// The caller is responsible for freeing the returned array and the
// strings it contains.
char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
char** args = (char**)malloc(argc * sizeof(char*));
int i = 0;
for (i = 0; i < argc; ++i) {
args[i] = Evaluate(state, argv[i]);
if (args[i] == NULL) {
int j;
for (j = 0; j < i; ++j) {
free(args[j]);
}
free(args);
return NULL;
}
}
return args;
}
// Use printf-style arguments to compose an error message to put into
// *state. Returns NULL.
char* ErrorAbort(State* state, char* format, ...) {
char* buffer = malloc(4096);
va_list v;
va_start(v, format);
vsnprintf(buffer, 4096, format, v);
va_end(v);
free(state->errmsg);
state->errmsg = buffer;
return NULL;
}

126
edify/expr.h Normal file
View File

@ -0,0 +1,126 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _EXPRESSION_H
#define _EXPRESSION_H
#include "yydefs.h"
#define MAX_STRING_LEN 1024
typedef struct Expr Expr;
typedef struct {
// Optional pointer to app-specific data; the core of edify never
// uses this value.
void* cookie;
// The source of the original script. Must be NULL-terminated,
// and in writable memory (Evaluate may make temporary changes to
// it but will restore it when done).
char* script;
// The error message (if any) returned if the evaluation aborts.
// Should be NULL initially, will be either NULL or a malloc'd
// pointer after Evaluate() returns.
char* errmsg;
} State;
typedef char* (*Function)(const char* name, State* state,
int argc, Expr* argv[]);
struct Expr {
Function fn;
char* name;
int argc;
Expr** argv;
int start, end;
};
char* Evaluate(State* state, Expr* expr);
// Glue to make an Expr out of a literal.
char* Literal(const char* name, State* state, int argc, Expr* argv[]);
// Functions corresponding to various syntactic sugar operators.
// ("concat" is also available as a builtin function, to concatenate
// more than two strings.)
char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
// Convenience function for building expressions with a fixed number
// of arguments.
Expr* Build(Function fn, YYLTYPE loc, int count, ...);
// Global builtins, registered by RegisterBuiltins().
char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
char* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
char* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
// For setting and getting the global error string (when returning
// NULL from a function).
void SetError(const char* message); // makes a copy
const char* GetError(); // retains ownership
void ClearError();
typedef struct {
const char* name;
Function fn;
} NamedFunction;
// Register a new function. The same Function may be registered under
// multiple names, but a given name should only be used once.
void RegisterFunction(const char* name, Function fn);
// Register all the builtins.
void RegisterBuiltins();
// Call this after all calls to RegisterFunction() but before parsing
// any scripts to finish building the function table.
void FinishRegistration();
// Find the Function for a given name; return NULL if no such function
// exists.
Function FindFunction(const char* name);
// --- convenience functions for use in functions ---
// Evaluate the expressions in argv, giving 'count' char* (the ... is
// zero or more char** to put them in). If any expression evaluates
// to NULL, free the rest and return -1. Return 0 on success.
int ReadArgs(State* state, Expr* argv[], int count, ...);
// Evaluate the expressions in argv, returning an array of char*
// results. If any evaluate to NULL, free the rest and return NULL.
// The caller is responsible for freeing the returned array and the
// strings it contains.
char** ReadVarArgs(State* state, int argc, Expr* argv[]);
// Use printf-style arguments to compose an error message to put into
// *state. Returns NULL.
char* ErrorAbort(State* state, char* format, ...);
#endif // _EXPRESSION_H

110
edify/lexer.l Normal file
View File

@ -0,0 +1,110 @@
%{
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "expr.h"
#include "yydefs.h"
#include "parser.h"
int gLine = 1;
int gColumn = 1;
int gPos = 0;
// TODO: enforce MAX_STRING_LEN during lexing
char string_buffer[MAX_STRING_LEN];
char* string_pos;
#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \
gColumn+=yyleng; gPos+=yyleng;} while(0)
%}
%x STR
%option noyywrap
%%
\" {
BEGIN(STR);
string_pos = string_buffer;
yylloc.start = gPos;
++gColumn;
++gPos;
}
<STR>{
\" {
++gColumn;
++gPos;
BEGIN(INITIAL);
*string_pos = '\0';
yylval.str = strdup(string_buffer);
yylloc.end = gPos;
return STRING;
}
\\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; }
\\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; }
\\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; }
\\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; }
\\x[0-9a-fA-F]{2} {
gColumn += yyleng;
gPos += yyleng;
int val;
sscanf(yytext+2, "%x", &val);
*string_pos++ = val;
}
\n {
++gLine;
++gPos;
gColumn = 1;
*string_pos++ = yytext[0];
}
. {
++gColumn;
++gPos;
*string_pos++ = yytext[0];
}
}
if ADVANCE; return IF;
then ADVANCE; return THEN;
else ADVANCE; return ELSE;
endif ADVANCE; return ENDIF;
[a-zA-Z0-9_:/.]+ {
ADVANCE;
yylval.str = strdup(yytext);
return STRING;
}
\&\& ADVANCE; return AND;
\|\| ADVANCE; return OR;
== ADVANCE; return EQ;
!= ADVANCE; return NE;
[+(),!;] ADVANCE; return yytext[0];
[ \t]+ ADVANCE;
(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1;
. return BAD;

213
edify/main.c Normal file
View File

@ -0,0 +1,213 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "expr.h"
#include "parser.h"
extern int yyparse(Expr** root, int* error_count);
int expect(const char* expr_str, const char* expected, int* errors) {
Expr* e;
int error;
char* result;
printf(".");
yy_scan_string(expr_str);
int error_count = 0;
error = yyparse(&e, &error_count);
if (error > 0 || error_count > 0) {
fprintf(stderr, "error parsing \"%s\" (%d errors)\n",
expr_str, error_count);
++*errors;
return 0;
}
State state;
state.cookie = NULL;
state.script = expr_str;
state.errmsg = NULL;
result = Evaluate(&state, e);
free(state.errmsg);
if (result == NULL && expected != NULL) {
fprintf(stderr, "error evaluating \"%s\"\n", expr_str);
++*errors;
return 0;
}
if (result == NULL && expected == NULL) {
return 1;
}
if (strcmp(result, expected) != 0) {
fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n",
expr_str, expected, result);
++*errors;
free(result);
return 0;
}
free(result);
return 1;
}
int test() {
int errors = 0;
expect("a", "a", &errors);
expect("\"a\"", "a", &errors);
expect("\"\\x61\"", "a", &errors);
expect("# this is a comment\n"
" a\n"
" \n",
"a", &errors);
// sequence operator
expect("a; b; c", "c", &errors);
// string concat operator
expect("a + b", "ab", &errors);
expect("a + \n \"b\"", "ab", &errors);
expect("a + b +\nc\n", "abc", &errors);
// string concat function
expect("concat(a, b)", "ab", &errors);
expect("concat(a,\n \"b\")", "ab", &errors);
expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
// logical and
expect("a && b", "b", &errors);
expect("a && \"\"", "", &errors);
expect("\"\" && b", "", &errors);
expect("\"\" && \"\"", "", &errors);
expect("\"\" && abort()", "", &errors); // test short-circuiting
expect("t && abort()", NULL, &errors);
// logical or
expect("a || b", "a", &errors);
expect("a || \"\"", "a", &errors);
expect("\"\" || b", "b", &errors);
expect("\"\" || \"\"", "", &errors);
expect("a || abort()", "a", &errors); // test short-circuiting
expect("\"\" || abort()", NULL, &errors);
// logical not
expect("!a", "", &errors);
expect("! \"\"", "t", &errors);
expect("!!a", "t", &errors);
// precedence
expect("\"\" == \"\" && b", "b", &errors);
expect("a + b == ab", "t", &errors);
expect("ab == a + b", "t", &errors);
expect("a + (b == ab)", "a", &errors);
expect("(ab == a) + b", "b", &errors);
// substring function
expect("is_substring(cad, abracadabra)", "t", &errors);
expect("is_substring(abrac, abracadabra)", "t", &errors);
expect("is_substring(dabra, abracadabra)", "t", &errors);
expect("is_substring(cad, abracxadabra)", "", &errors);
expect("is_substring(abrac, axbracadabra)", "", &errors);
expect("is_substring(dabra, abracadabrxa)", "", &errors);
// ifelse function
expect("ifelse(t, yes, no)", "yes", &errors);
expect("ifelse(!t, yes, no)", "no", &errors);
expect("ifelse(t, yes, abort())", "yes", &errors);
expect("ifelse(!t, abort(), no)", "no", &errors);
// if "statements"
expect("if t then yes else no endif", "yes", &errors);
expect("if \"\" then yes else no endif", "no", &errors);
expect("if \"\" then yes endif", "", &errors);
expect("if \"\"; t then yes endif", "yes", &errors);
// numeric comparisons
expect("less_than_int(3, 14)", "t", &errors);
expect("less_than_int(14, 3)", "", &errors);
expect("less_than_int(x, 3)", "", &errors);
expect("less_than_int(3, x)", "", &errors);
expect("greater_than_int(3, 14)", "", &errors);
expect("greater_than_int(14, 3)", "t", &errors);
expect("greater_than_int(x, 3)", "", &errors);
expect("greater_than_int(3, x)", "", &errors);
printf("\n");
return errors;
}
void ExprDump(int depth, Expr* n, char* script) {
printf("%*s", depth*2, "");
char temp = script[n->end];
script[n->end] = '\0';
printf("%s %p (%d-%d) \"%s\"\n",
n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
script+n->start);
script[n->end] = temp;
int i;
for (i = 0; i < n->argc; ++i) {
ExprDump(depth+1, n->argv[i], script);
}
}
int main(int argc, char** argv) {
RegisterBuiltins();
FinishRegistration();
if (argc == 1) {
return test() != 0;
}
FILE* f = fopen(argv[1], "r");
char buffer[8192];
int size = fread(buffer, 1, 8191, f);
fclose(f);
buffer[size] = '\0';
Expr* root;
int error_count = 0;
yy_scan_bytes(buffer, size);
int error = yyparse(&root, &error_count);
printf("parse returned %d; %d errors encountered\n", error, error_count);
if (error == 0 || error_count > 0) {
ExprDump(0, root, buffer);
State state;
state.cookie = NULL;
state.script = buffer;
state.errmsg = NULL;
char* result = Evaluate(&state, root);
if (result == NULL) {
printf("result was NULL, message is: %s\n",
(state.errmsg == NULL ? "(NULL)" : state.errmsg));
free(state.errmsg);
} else {
printf("result is [%s]\n", result);
}
}
return 0;
}

130
edify/parser.y Normal file
View File

@ -0,0 +1,130 @@
%{
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "expr.h"
#include "yydefs.h"
#include "parser.h"
extern int gLine;
extern int gColumn;
void yyerror(Expr** root, int* error_count, const char* s);
int yyparse(Expr** root, int* error_count);
%}
%locations
%union {
char* str;
Expr* expr;
struct {
int argc;
Expr** argv;
} args;
}
%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
%token <str> STRING BAD
%type <expr> expr
%type <args> arglist
%parse-param {Expr** root}
%parse-param {int* error_count}
%error-verbose
/* declarations in increasing order of precedence */
%left ';'
%left ','
%left OR
%left AND
%left EQ NE
%left '+'
%right '!'
%%
input: expr { *root = $1; }
;
expr: STRING {
$$ = malloc(sizeof(Expr));
$$->fn = Literal;
$$->name = $1;
$$->argc = 0;
$$->argv = NULL;
$$->start = @$.start;
$$->end = @$.end;
}
| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); }
| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; }
| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); }
| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); }
| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); }
| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); }
| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); }
| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); }
| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
| STRING '(' arglist ')' {
$$ = malloc(sizeof(Expr));
$$->fn = FindFunction($1);
if ($$->fn == NULL) {
char buffer[256];
snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
yyerror(root, error_count, buffer);
YYERROR;
}
$$->name = $1;
$$->argc = $3.argc;
$$->argv = $3.argv;
$$->start = @$.start;
$$->end = @$.end;
}
;
arglist: /* empty */ {
$$.argc = 0;
$$.argv = NULL;
}
| expr {
$$.argc = 1;
$$.argv = malloc(sizeof(Expr*));
$$.argv[0] = $1;
}
| arglist ',' expr {
$$.argc = $1.argc + 1;
$$.argv = realloc($$.argv, $$.argc * sizeof(Expr*));
$$.argv[$$.argc-1] = $3;
}
;
%%
void yyerror(Expr** root, int* error_count, const char* s) {
if (strlen(s) == 0) {
s = "syntax error";
}
printf("line %d col %d: %s\n", gLine, gColumn, s);
++*error_count;
}

36
edify/yydefs.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _YYDEFS_H_
#define _YYDEFS_H_
#define YYLTYPE YYLTYPE
typedef struct {
int start, end;
} YYLTYPE;
#define YYLLOC_DEFAULT(Current, Rhs, N) \
do { \
if (N) { \
(Current).start = YYRHSLOC(Rhs, 1).start; \
(Current).end = YYRHSLOC(Rhs, N).end; \
} else { \
(Current).start = YYRHSLOC(Rhs, 0).start; \
(Current).end = YYRHSLOC(Rhs, 0).end; \
} \
} while (0)
#endif

962
extendedcommands.c Normal file
View File

@ -0,0 +1,962 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>
#include "bootloader.h"
#include "common.h"
#include "cutils/properties.h"
#include "firmware.h"
#include "install.h"
#include "minui/minui.h"
#include "minzip/DirUtil.h"
#include "roots.h"
#include "recovery_ui.h"
#include "commands.h"
#include "amend/amend.h"
#include "mtdutils/mtdutils.h"
#include "mtdutils/dump_image.h"
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
#include "extendedcommands.h"
#include "nandroid.h"
int signature_check_enabled = 1;
int script_assert_enabled = 1;
static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
void
toggle_signature_check()
{
signature_check_enabled = !signature_check_enabled;
ui_print("Signature Check: %s\n", signature_check_enabled ? "Enabled" : "Disabled");
}
void toggle_script_asserts()
{
script_assert_enabled = !script_assert_enabled;
ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled");
}
int install_zip(const char* packagefilepath)
{
ui_print("\n-- Installing: %s\n", packagefilepath);
#ifndef BOARD_HAS_NO_MISC_PARTITION
set_sdcard_update_bootloader_message();
#endif
int status = install_package(packagefilepath);
ui_reset_progress();
if (status != INSTALL_SUCCESS) {
ui_set_background(BACKGROUND_ICON_ERROR);
ui_print("Installation aborted.\n");
return 1;
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (firmware_update_pending()) {
ui_print("\nReboot via menu to complete\ninstallation.\n");
}
#endif
ui_set_background(BACKGROUND_ICON_NONE);
ui_print("\nInstall from sdcard complete.\n");
return 0;
}
char* INSTALL_MENU_ITEMS[] = { "apply sdcard:update.zip",
"choose zip from sdcard",
"toggle signature verification",
"toggle script asserts",
NULL };
#define ITEM_APPLY_SDCARD 0
#define ITEM_CHOOSE_ZIP 1
#define ITEM_SIG_CHECK 2
#define ITEM_ASSERTS 3
void show_install_update_menu()
{
static char* headers[] = { "Apply update from .zip file on SD card",
"",
NULL
};
for (;;)
{
int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0);
switch (chosen_item)
{
case ITEM_ASSERTS:
toggle_script_asserts();
break;
case ITEM_SIG_CHECK:
toggle_signature_check();
break;
case ITEM_APPLY_SDCARD:
{
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
install_zip(SDCARD_PACKAGE_FILE);
break;
}
case ITEM_CHOOSE_ZIP:
show_choose_zip_menu();
break;
default:
return;
}
}
}
void free_string_array(char** array)
{
if (array == NULL)
return;
char* cursor = array[0];
int i = 0;
while (cursor != NULL)
{
free(cursor);
cursor = array[++i];
}
free(array);
}
char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int* numFiles)
{
char path[PATH_MAX] = "";
DIR *dir;
struct dirent *de;
int total = 0;
int i;
char** files = NULL;
int pass;
*numFiles = 0;
int dirLen = strlen(directory);
dir = opendir(directory);
if (dir == NULL) {
ui_print("Couldn't open directory.\n");
return NULL;
}
int extension_length = 0;
if (fileExtensionOrDirectory != NULL)
extension_length = strlen(fileExtensionOrDirectory);
int isCounting = 1;
i = 0;
for (pass = 0; pass < 2; pass++) {
while ((de=readdir(dir)) != NULL) {
// skip hidden files
if (de->d_name[0] == '.')
continue;
// NULL means that we are gathering directories, so skip this
if (fileExtensionOrDirectory != NULL)
{
// make sure that we can have the desired extension (prevent seg fault)
if (strlen(de->d_name) < extension_length)
continue;
// compare the extension
if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0)
continue;
}
else
{
struct stat info;
char fullFileName[PATH_MAX];
strcpy(fullFileName, directory);
strcat(fullFileName, de->d_name);
stat(fullFileName, &info);
// make sure it is a directory
if (!(S_ISDIR(info.st_mode)))
continue;
}
if (pass == 0)
{
total++;
continue;
}
files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2);
strcpy(files[i], directory);
strcat(files[i], de->d_name);
if (fileExtensionOrDirectory == NULL)
strcat(files[i], "/");
i++;
}
if (pass == 1)
break;
if (total == 0)
break;
rewinddir(dir);
*numFiles = total;
files = (char**) malloc((total+1)*sizeof(char*));
files[total]=NULL;
}
if(closedir(dir) < 0) {
LOGE("Failed to close directory.");
}
if (total==0) {
return NULL;
}
// sort the result
if (files != NULL) {
for (i = 0; i < total; i++) {
int curMax = -1;
int j;
for (j = 0; j < total - i; j++) {
if (curMax == -1 || strcmp(files[curMax], files[j]) < 0)
curMax = j;
}
char* temp = files[curMax];
files[curMax] = files[total - i - 1];
files[total - i - 1] = temp;
}
}
return files;
}
// pass in NULL for fileExtensionOrDirectory and you will get a directory chooser
char* choose_file_menu(const char* directory, const char* fileExtensionOrDirectory, const char* headers[])
{
char path[PATH_MAX] = "";
DIR *dir;
struct dirent *de;
int numFiles = 0;
int numDirs = 0;
int i;
char* return_value = NULL;
int dir_len = strlen(directory);
char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles);
char** dirs = NULL;
if (fileExtensionOrDirectory != NULL)
dirs = gather_files(directory, NULL, &numDirs);
int total = numDirs + numFiles;
if (total == 0)
{
ui_print("No files found.\n");
}
else
{
char** list = (char**) malloc((total + 1) * sizeof(char*));
list[total] = NULL;
for (i = 0 ; i < numDirs; i++)
{
list[i] = strdup(dirs[i] + dir_len);
}
for (i = 0 ; i < numFiles; i++)
{
list[numDirs + i] = strdup(files[i] + dir_len);
}
for (;;)
{
int chosen_item = get_menu_selection(headers, list, 0);
if (chosen_item == GO_BACK)
break;
static char ret[PATH_MAX];
if (chosen_item < numDirs)
{
char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers);
if (subret != NULL)
{
strcpy(ret, subret);
return_value = ret;
break;
}
continue;
}
strcpy(ret, files[chosen_item - numDirs]);
return_value = ret;
break;
}
free_string_array(list);
}
free_string_array(files);
free_string_array(dirs);
return return_value;
}
void show_choose_zip_menu()
{
if (ensure_root_path_mounted("SDCARD:") != 0) {
LOGE ("Can't mount /sdcard\n");
return;
}
static char* headers[] = { "Choose a zip to apply",
"",
NULL
};
char* file = choose_file_menu("/sdcard/", ".zip", headers);
if (file == NULL)
return;
char sdcard_package_file[1024];
strcpy(sdcard_package_file, "SDCARD:");
strcat(sdcard_package_file, file + strlen("/sdcard/"));
static char* confirm_install = "Confirm install?";
static char confirm[PATH_MAX];
sprintf(confirm, "Yes - Install %s", basename(file));
if (confirm_selection(confirm_install, confirm))
install_zip(sdcard_package_file);
}
// This was pulled from bionic: The default system command always looks
// for shell in /system/bin/sh. This is bad.
#define _PATH_BSHELL "/sbin/sh"
extern char **environ;
int
__system(const char *command)
{
pid_t pid;
sig_t intsave, quitsave;
sigset_t mask, omask;
int pstat;
char *argp[] = {"sh", "-c", NULL, NULL};
if (!command) /* just checking... */
return(1);
argp[2] = (char *)command;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &omask);
switch (pid = vfork()) {
case -1: /* error */
sigprocmask(SIG_SETMASK, &omask, NULL);
return(-1);
case 0: /* child */
sigprocmask(SIG_SETMASK, &omask, NULL);
execve(_PATH_BSHELL, argp, environ);
_exit(127);
}
intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN);
quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
pid = waitpid(pid, (int *)&pstat, 0);
sigprocmask(SIG_SETMASK, &omask, NULL);
(void)bsd_signal(SIGINT, intsave);
(void)bsd_signal(SIGQUIT, quitsave);
return (pid == -1 ? -1 : pstat);
}
void show_nandroid_restore_menu()
{
if (ensure_root_path_mounted("SDCARD:") != 0) {
LOGE ("Can't mount /sdcard\n");
return;
}
static char* headers[] = { "Choose an image to restore",
"",
NULL
};
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers);
if (file == NULL)
return;
if (confirm_selection("Confirm restore?", "Yes - Restore"))
nandroid_restore(file, 1, 1, 1, 1, 1);
}
void show_mount_usb_storage_menu()
{
char command[PATH_MAX];
sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", SDCARD_DEVICE_PRIMARY);
__system(command);
static char* headers[] = { "USB Mass Storage device",
"Leaving this menu unmount",
"your SD card from your PC.",
"",
NULL
};
static char* list[] = { "Unmount", NULL };
for (;;)
{
int chosen_item = get_menu_selection(headers, list, 0);
if (chosen_item == GO_BACK || chosen_item == 0)
break;
}
__system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file");
__system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable");
}
int confirm_selection(const char* title, const char* confirm)
{
struct stat info;
if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info))
return 1;
char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL };
char* items[] = { "No",
"No",
"No",
"No",
"No",
"No",
"No",
confirm, //" Yes -- wipe partition", // [7
"No",
"No",
"No",
NULL };
int chosen_item = get_menu_selection(confirm_headers, items, 0);
return chosen_item == 7;
}
int format_non_mtd_device(const char* root)
{
// if this is SDEXT:, don't worry about it.
if (0 == strcmp(root, "SDEXT:"))
{
struct stat st;
if (0 != stat(SDEXT_DEVICE, &st))
{
ui_print("No app2sd partition found. Skipping format of /sd-ext.\n");
return 0;
}
}
char path[PATH_MAX];
translate_root_path(root, path, PATH_MAX);
if (0 != ensure_root_path_mounted(root))
{
ui_print("Error mounting %s!\n", path);
ui_print("Skipping format...\n");
return 0;
}
static char tmp[PATH_MAX];
sprintf(tmp, "rm -rf %s/*", path);
__system(tmp);
sprintf(tmp, "rm -rf %s/.*", path);
__system(tmp);
ensure_root_path_unmounted(root);
return 0;
}
#define MOUNTABLE_COUNT 5
#define MTD_COUNT 4
#define MMC_COUNT 2
void show_partition_menu()
{
static char* headers[] = { "Mounts and Storage Menu",
"",
NULL
};
typedef char* string;
string mounts[MOUNTABLE_COUNT][3] = {
{ "mount /system", "unmount /system", "SYSTEM:" },
{ "mount /data", "unmount /data", "DATA:" },
{ "mount /cache", "unmount /cache", "CACHE:" },
{ "mount /sdcard", "unmount /sdcard", "SDCARD:" },
{ "mount /sd-ext", "unmount /sd-ext", "SDEXT:" }
};
string mtds[MTD_COUNT][2] = {
{ "format boot", "BOOT:" },
{ "format system", "SYSTEM:" },
{ "format data", "DATA:" },
{ "format cache", "CACHE:" },
};
string mmcs[MMC_COUNT][3] = {
{ "format sdcard", "SDCARD:" },
{ "format sd-ext", "SDEXT:" }
};
static char* confirm_format = "Confirm format?";
static char* confirm = "Yes - Format";
for (;;)
{
int ismounted[MOUNTABLE_COUNT];
int i;
static string options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null
for (i = 0; i < MOUNTABLE_COUNT; i++)
{
ismounted[i] = is_root_path_mounted(mounts[i][2]);
options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0];
}
for (i = 0; i < MTD_COUNT; i++)
{
options[MOUNTABLE_COUNT + i] = mtds[i][0];
}
for (i = 0; i < MMC_COUNT; i++)
{
options[MOUNTABLE_COUNT + MTD_COUNT + i] = mmcs[i][0];
}
options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT] = "mount USB storage";
options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1] = NULL;
int chosen_item = get_menu_selection(headers, options, 0);
if (chosen_item == GO_BACK)
break;
if (chosen_item == MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
{
show_mount_usb_storage_menu();
}
else if (chosen_item < MOUNTABLE_COUNT)
{
if (ismounted[chosen_item])
{
if (0 != ensure_root_path_unmounted(mounts[chosen_item][2]))
ui_print("Error unmounting %s!\n", mounts[chosen_item][2]);
}
else
{
if (0 != ensure_root_path_mounted(mounts[chosen_item][2]))
ui_print("Error mounting %s!\n", mounts[chosen_item][2]);
}
}
else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT)
{
chosen_item = chosen_item - MOUNTABLE_COUNT;
if (!confirm_selection(confirm_format, confirm))
continue;
ui_print("Formatting %s...\n", mtds[chosen_item][1]);
if (0 != format_root_device(mtds[chosen_item][1]))
ui_print("Error formatting %s!\n", mtds[chosen_item][1]);
else
ui_print("Done.\n");
}
else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
{
chosen_item = chosen_item - MOUNTABLE_COUNT - MTD_COUNT;
if (!confirm_selection(confirm_format, confirm))
continue;
ui_print("Formatting %s...\n", mmcs[chosen_item][1]);
if (0 != format_non_mtd_device(mmcs[chosen_item][1]))
ui_print("Error formatting %s!\n", mmcs[chosen_item][1]);
else
ui_print("Done.\n");
}
}
}
#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand"
int extendedcommand_file_exists()
{
struct stat file_info;
return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
}
int run_script_from_buffer(char* script_data, int script_len, char* filename)
{
/* Parse the script. Note that the script and parse tree are never freed.
*/
const AmCommandList *commands = parseAmendScript(script_data, script_len);
if (commands == NULL) {
printf("Syntax error in update script\n");
return 1;
} else {
printf("Parsed %.*s\n", script_len, filename);
}
/* Execute the script.
*/
int ret = execCommandList((ExecContext *)1, commands);
if (ret != 0) {
int num = ret;
char *line = NULL, *next = script_data;
while (next != NULL && ret-- > 0) {
line = next;
next = memchr(line, '\n', script_data + script_len - line);
if (next != NULL) *next++ = '\0';
}
printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
return 1;
}
return 0;
}
int run_script(char* filename)
{
struct stat file_info;
if (0 != stat(filename, &file_info)) {
printf("Error executing stat on file: %s\n", filename);
return 1;
}
int script_len = file_info.st_size;
char* script_data = (char*)malloc(script_len + 1);
FILE *file = fopen(filename, "rb");
fread(script_data, script_len, 1, file);
// supposedly not necessary, but let's be safe.
script_data[script_len] = '\0';
fclose(file);
LOGI("Running script:\n");
LOGI("\n%s\n", script_data);
int ret = run_script_from_buffer(script_data, script_len, filename);
free(script_data);
return ret;
}
int run_and_remove_extendedcommand()
{
char tmp[PATH_MAX];
sprintf(tmp, "cp %s /tmp/%s", EXTENDEDCOMMAND_SCRIPT, basename(EXTENDEDCOMMAND_SCRIPT));
__system(tmp);
remove(EXTENDEDCOMMAND_SCRIPT);
int i = 0;
for (i = 20; i > 0; i--) {
ui_print("Waiting for SD Card to mount (%ds)\n", i);
if (ensure_root_path_mounted("SDCARD:") == 0) {
ui_print("SD Card mounted...\n");
break;
}
sleep(1);
}
remove("/sdcard/clockworkmod/.recoverycheckpoint");
if (i == 0) {
ui_print("Timed out waiting for SD card... continuing anyways.");
}
sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
return run_script(tmp);
}
int amend_main(int argc, char** argv)
{
if (argc != 2)
{
printf("Usage: amend <script>\n");
return 0;
}
RecoveryCommandContext ctx = { NULL };
if (register_update_commands(&ctx)) {
LOGE("Can't install update commands\n");
}
return run_script(argv[1]);
}
void show_nandroid_advanced_restore_menu()
{
if (ensure_root_path_mounted("SDCARD:") != 0) {
LOGE ("Can't mount /sdcard\n");
return;
}
static char* advancedheaders[] = { "Choose an image to restore",
"",
"Choose an image to restore",
"first. The next menu will",
"you more options.",
"",
NULL
};
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, advancedheaders);
if (file == NULL)
return;
static char* headers[] = { "Nandroid Advanced Restore",
"",
NULL
};
static char* list[] = { "Restore boot",
"Restore system",
"Restore data",
"Restore cache",
"Restore sd-ext",
NULL
};
static char* confirm_restore = "Confirm restore?";
int chosen_item = get_menu_selection(headers, list, 0);
switch (chosen_item)
{
case 0:
if (confirm_selection(confirm_restore, "Yes - Restore boot"))
nandroid_restore(file, 1, 0, 0, 0, 0);
break;
case 1:
if (confirm_selection(confirm_restore, "Yes - Restore system"))
nandroid_restore(file, 0, 1, 0, 0, 0);
break;
case 2:
if (confirm_selection(confirm_restore, "Yes - Restore data"))
nandroid_restore(file, 0, 0, 1, 0, 0);
break;
case 3:
if (confirm_selection(confirm_restore, "Yes - Restore cache"))
nandroid_restore(file, 0, 0, 0, 1, 0);
break;
case 4:
if (confirm_selection(confirm_restore, "Yes - Restore sd-ext"))
nandroid_restore(file, 0, 0, 0, 0, 1);
break;
}
}
void show_nandroid_menu()
{
static char* headers[] = { "Nandroid",
"",
NULL
};
static char* list[] = { "Backup",
"Restore",
"Advanced Restore",
NULL
};
int chosen_item = get_menu_selection(headers, list, 0);
switch (chosen_item)
{
case 0:
{
char backup_path[PATH_MAX];
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
if (tmp == NULL)
{
struct timeval tp;
gettimeofday(&tp, NULL);
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
}
else
{
strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
}
nandroid_backup(backup_path);
}
break;
case 1:
show_nandroid_restore_menu();
break;
case 2:
show_nandroid_advanced_restore_menu();
break;
}
}
void wipe_battery_stats()
{
ensure_root_path_mounted("DATA:");
remove("/data/system/batterystats.bin");
ensure_root_path_unmounted("DATA:");
}
void show_advanced_menu()
{
static char* headers[] = { "Advanced and Debugging Menu",
"",
NULL
};
static char* list[] = { "Reboot Recovery",
"Wipe Dalvik Cache",
"Wipe Battery Stats",
"Report Error",
"Key Test",
#ifndef BOARD_HAS_SMALL_RECOVERY
"Partition SD Card",
"Fix Permissions",
#endif
NULL
};
for (;;)
{
int chosen_item = get_menu_selection(headers, list, 0);
if (chosen_item == GO_BACK)
break;
switch (chosen_item)
{
case 0:
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery");
break;
case 1:
{
if (0 != ensure_root_path_mounted("DATA:"))
break;
ensure_root_path_mounted("SDEXT:");
ensure_root_path_mounted("CACHE:");
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) {
__system("rm -r /data/dalvik-cache");
__system("rm -r /cache/dalvik-cache");
__system("rm -r /sd-ext/dalvik-cache");
}
ensure_root_path_unmounted("DATA:");
ui_print("Dalvik Cache wiped.\n");
break;
}
case 2:
{
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Battery Stats"))
wipe_battery_stats();
break;
}
case 3:
handle_failure(1);
break;
case 4:
{
ui_print("Outputting key codes.\n");
ui_print("Go back to end debugging.\n");
int key;
int action;
do
{
key = ui_wait_key();
action = device_handle_key(key, 1);
ui_print("Key: %d\n", key);
}
while (action != GO_BACK);
break;
}
case 5:
{
static char* ext_sizes[] = { "128M",
"256M",
"512M",
"1024M",
NULL };
static char* swap_sizes[] = { "0M",
"32M",
"64M",
"128M",
"256M",
NULL };
static char* ext_headers[] = { "Ext Size", "", NULL };
static char* swap_headers[] = { "Swap Size", "", NULL };
int ext_size = get_menu_selection(ext_headers, ext_sizes, 0);
if (ext_size == GO_BACK)
continue;
int swap_size = get_menu_selection(swap_headers, swap_sizes, 0);
if (swap_size == GO_BACK)
continue;
char sddevice[256];
const RootInfo *ri = get_root_info_for_path("SDCARD:");
strcpy(sddevice, ri->device);
// we only want the mmcblk, not the partition
sddevice[strlen("/dev/block/mmcblkX")] = NULL;
char cmd[PATH_MAX];
setenv("SDPATH", sddevice, 1);
sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
ui_print("Partitioning SD Card... please wait...\n");
if (0 == __system(cmd))
ui_print("Done!\n");
else
ui_print("An error occured while partitioning your SD Card. Please see /tmp/recovery.log for more details.\n");
break;
}
case 6:
{
ensure_root_path_mounted("SYSTEM:");
ensure_root_path_mounted("DATA:");
ui_print("Fixing permissions...\n");
__system("fix_permissions");
ui_print("Done!\n");
break;
}
}
}
}
void write_fstab_root(char *root_path, FILE *file)
{
RootInfo *info = get_root_info_for_path(root_path);
if (info == NULL) {
LOGW("Unable to get root info for %s during fstab generation!", root_path);
return;
}
MtdPartition *mtd = get_root_mtd_partition(root_path);
if (mtd != NULL)
{
fprintf(file, "/dev/block/mtdblock%d ", mtd->device_index);
}
else
{
fprintf(file, "%s ", info->device);
}
fprintf(file, "%s ", info->mount_point);
fprintf(file, "%s %s\n", info->filesystem, info->filesystem_options == NULL ? "rw" : info->filesystem_options);
}
void create_fstab()
{
__system("touch /etc/mtab");
FILE *file = fopen("/etc/fstab", "w");
if (file == NULL) {
LOGW("Unable to create /etc/fstab!");
return;
}
write_fstab_root("CACHE:", file);
write_fstab_root("DATA:", file);
#ifdef HAS_DATADATA
write_fstab_root("DATADATA:", file);
#endif
write_fstab_root("SYSTEM:", file);
write_fstab_root("SDCARD:", file);
write_fstab_root("SDEXT:", file);
fclose(file);
}
void handle_failure(int ret)
{
if (ret == 0)
return;
if (0 != ensure_root_path_mounted("SDCARD:"))
return;
mkdir("/sdcard/clockworkmod", S_IRWXU);
__system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log");
ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n");
}

46
extendedcommands.h Normal file
View File

@ -0,0 +1,46 @@
extern int signature_check_enabled;
extern int script_assert_enabled;
void
toggle_signature_check();
void
toggle_script_asserts();
void
show_choose_zip_menu();
int
do_nandroid_backup(const char* backup_name);
int
do_nandroid_restore();
void
show_nandroid_restore_menu();
void
show_nandroid_menu();
void
show_partition_menu();
void
show_choose_zip_menu();
int
install_zip(const char* packagefilepath);
int
__system(const char *command);
void
show_advanced_menu();
int
format_non_mtd_device(const char* root);
void
wipe_battery_stats();
void create_fstab();

View File

@ -39,6 +39,10 @@ int remember_firmware_update(const char *type, const char *data, int length) {
return 0;
}
// Return true if there is a firmware update pending.
int firmware_update_pending() {
return update_data != NULL && update_length > 0;
}
/* Bootloader / Recovery Flow
*

View File

@ -23,6 +23,9 @@
*/
int remember_firmware_update(const char *type, const char *data, int length);
/* Returns true if a firmware update has been saved. */
int firmware_update_pending();
/* If an update was saved, reboot into the bootloader now to install it.
* Returns 0 if no radio image was defined, nonzero on error,
* doesn't return at all on success...

411
install.c
View File

@ -14,12 +14,14 @@
* limitations under the License.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "amend/amend.h"
#include "common.h"
#include "install.h"
#include "mincrypt/rsa.h"
@ -30,121 +32,312 @@
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
#include "firmware.h"
#include "legacy.h"
/* List of public keys */
static const RSAPublicKey keys[] = {
#include "keys.inc"
};
#include "extendedcommands.h"
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
static const ZipEntry *
find_update_script(ZipArchive *zip)
{
//TODO: Get the location of this script from the MANIFEST.MF file
return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
}
static int read_data(ZipArchive *zip, const ZipEntry *entry,
char** ppData, int* pLength) {
int len = (int)mzGetZipEntryUncompLen(entry);
if (len <= 0) {
LOGE("Bad data length %d\n", len);
return -1;
}
char *data = malloc(len + 1);
if (data == NULL) {
LOGE("Can't allocate %d bytes for data\n", len + 1);
return -2;
}
bool ok = mzReadZipEntry(zip, entry, data, len);
if (!ok) {
LOGE("Error while reading data\n");
free(data);
return -3;
}
data[len] = '\0'; // not necessary, but just to be safe
*ppData = data;
if (pLength) {
*pLength = len;
}
return 0;
}
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
#define PUBLIC_KEYS_FILE "/res/keys"
// The update binary ask us to install a firmware file on reboot. Set
// that up. Takes ownership of type and filename.
static int
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
{
/* Read the entire script into a buffer.
*/
int script_len;
char* script_data;
if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) {
LOGE("Can't read update script\n");
return INSTALL_ERROR;
}
handle_firmware_update(char* type, char* filename, ZipArchive* zip) {
unsigned int data_size;
const ZipEntry* entry = NULL;
/* Parse the script. Note that the script and parse tree are never freed.
*/
const AmCommandList *commands = parseAmendScript(script_data, script_len);
if (commands == NULL) {
LOGE("Syntax error in update script\n");
return INSTALL_ERROR;
} else {
UnterminatedString name = mzGetZipEntryFileName(update_script_entry);
LOGI("Parsed %.*s\n", name.len, name.str);
}
/* Execute the script.
*/
int ret = execCommandList((ExecContext *)1, commands);
if (ret != 0) {
int num = ret;
char *line, *next = script_data;
while (next != NULL && ret-- > 0) {
line = next;
next = memchr(line, '\n', script_data + script_len - line);
if (next != NULL) *next++ = '\0';
if (strncmp(filename, "PACKAGE:", 8) == 0) {
entry = mzFindZipEntry(zip, filename+8);
if (entry == NULL) {
LOGE("Failed to find \"%s\" in package", filename+8);
return INSTALL_ERROR;
}
LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
data_size = entry->uncompLen;
} else {
struct stat st_data;
if (stat(filename, &st_data) < 0) {
LOGE("Error stat'ing %s: %s\n", filename, strerror(errno));
return INSTALL_ERROR;
}
data_size = st_data.st_size;
}
LOGI("type is %s; size is %d; file is %s\n",
type, data_size, filename);
char* data = malloc(data_size);
if (data == NULL) {
LOGI("Can't allocate %d bytes for firmware data\n", data_size);
return INSTALL_ERROR;
}
ui_print("Installation complete.\n");
if (entry) {
if (mzReadZipEntry(zip, entry, data, data_size) == false) {
LOGE("Failed to read \"%s\" from package", filename+8);
return INSTALL_ERROR;
}
} else {
FILE* f = fopen(filename, "rb");
if (f == NULL) {
LOGE("Failed to open %s: %s\n", filename, strerror(errno));
return INSTALL_ERROR;
}
if (fread(data, 1, data_size, f) != data_size) {
LOGE("Failed to read firmware data: %s\n", strerror(errno));
return INSTALL_ERROR;
}
fclose(f);
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (remember_firmware_update(type, data, data_size)) {
LOGE("Can't store %s image\n", type);
free(data);
return INSTALL_ERROR;
}
#endif
free(filename);
return INSTALL_SUCCESS;
}
// If the package contains an update binary, extract it and run it.
static int
try_update_binary(const char *path, ZipArchive *zip) {
const ZipEntry* binary_entry =
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
if (binary_entry == NULL) {
return INSTALL_UPDATE_BINARY_MISSING;
}
char* binary = "/tmp/update_binary";
unlink(binary);
int fd = creat(binary, 0755);
if (fd < 0) {
LOGE("Can't make %s\n", binary);
return 1;
}
bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
close(fd);
if (!ok) {
LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
return 1;
}
int pipefd[2];
pipe(pipefd);
// When executing the update binary contained in the package, the
// arguments passed are:
//
// - the version number for this interface
//
// - an fd to which the program can write in order to update the
// progress bar. The program can write single-line commands:
//
// progress <frac> <secs>
// fill up the next <frac> part of of the progress bar
// over <secs> seconds. If <secs> is zero, use
// set_progress commands to manually control the
// progress of this segment of the bar
//
// set_progress <frac>
// <frac> should be between 0.0 and 1.0; sets the
// progress bar within the segment defined by the most
// recent progress command.
//
// firmware <"hboot"|"radio"> <filename>
// arrange to install the contents of <filename> in the
// given partition on reboot. (API v2: <filename> may
// start with "PACKAGE:" to indicate taking a file from
// the OTA package.)
//
// ui_print <string>
// display <string> on the screen.
//
// - the name of the package zip file.
//
char** args = malloc(sizeof(char*) * 5);
args[0] = binary;
args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
args[2] = malloc(10);
sprintf(args[2], "%d", pipefd[1]);
args[3] = (char*)path;
args[4] = NULL;
pid_t pid = fork();
if (pid == 0) {
close(pipefd[0]);
execv(binary, args);
fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno));
_exit(-1);
}
close(pipefd[1]);
char* firmware_type = NULL;
char* firmware_filename = NULL;
char buffer[1024];
FILE* from_child = fdopen(pipefd[0], "r");
while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
char* command = strtok(buffer, " \n");
if (command == NULL) {
continue;
} else if (strcmp(command, "progress") == 0) {
char* fraction_s = strtok(NULL, " \n");
char* seconds_s = strtok(NULL, " \n");
float fraction = strtof(fraction_s, NULL);
int seconds = strtol(seconds_s, NULL, 10);
ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),
seconds);
} else if (strcmp(command, "set_progress") == 0) {
char* fraction_s = strtok(NULL, " \n");
float fraction = strtof(fraction_s, NULL);
ui_set_progress(fraction);
} else if (strcmp(command, "firmware") == 0) {
char* type = strtok(NULL, " \n");
char* filename = strtok(NULL, " \n");
if (type != NULL && filename != NULL) {
if (firmware_type != NULL) {
LOGE("ignoring attempt to do multiple firmware updates");
} else {
firmware_type = strdup(type);
firmware_filename = strdup(filename);
}
}
} else if (strcmp(command, "ui_print") == 0) {
char* str = strtok(NULL, "\n");
if (str) {
ui_print(str);
} else {
ui_print("\n");
}
} else {
LOGE("unknown command [%s]\n", command);
}
}
fclose(from_child);
int status;
waitpid(pid, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
return INSTALL_ERROR;
}
if (firmware_type != NULL) {
return handle_firmware_update(firmware_type, firmware_filename, zip);
} else {
return INSTALL_SUCCESS;
}
}
static int
handle_update_package(const char *path, ZipArchive *zip)
{
// Give verification half the progress bar...
ui_print("Verifying update package...\n");
ui_show_progress(
VERIFICATION_PROGRESS_FRACTION,
VERIFICATION_PROGRESS_TIME);
if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) {
LOGE("Verification failed\n");
return INSTALL_CORRUPT;
}
// Update should take the rest of the progress bar.
ui_print("Installing update...\n");
const ZipEntry *script_entry;
script_entry = find_update_script(zip);
if (script_entry == NULL) {
LOGE("Can't find update script\n");
return INSTALL_CORRUPT;
}
LOGI("Trying update-binary.\n");
int result = try_update_binary(path, zip);
if (register_package_root(zip, path) < 0) {
LOGE("Can't register package root\n");
return INSTALL_ERROR;
if (result == INSTALL_UPDATE_BINARY_MISSING)
{
register_package_root(NULL, NULL); // Unregister package root
if (register_package_root(zip, path) < 0) {
LOGE("Can't register package root\n");
return INSTALL_ERROR;
}
const ZipEntry *script_entry;
script_entry = find_update_script(zip);
LOGI("Trying update-script.\n");
result = handle_update_script(zip, script_entry);
if (result == INSTALL_UPDATE_SCRIPT_MISSING)
result = INSTALL_ERROR;
}
int ret = handle_update_script(zip, script_entry);
register_package_root(NULL, NULL); // Unregister package root
return ret;
return result;
}
// Reads a file containing one or more public keys as produced by
// DumpPublicKey: this is an RSAPublicKey struct as it would appear
// as a C source literal, eg:
//
// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
//
// (Note that the braces and commas in this example are actual
// characters the parser expects to find in the file; the ellipses
// indicate more numbers omitted from this example.)
//
// The file may contain multiple keys in this format, separated by
// commas. The last key must not be followed by a comma.
//
// Returns NULL if the file failed to parse, or if it contain zero keys.
static RSAPublicKey*
load_keys(const char* filename, int* numKeys) {
RSAPublicKey* out = NULL;
*numKeys = 0;
FILE* f = fopen(filename, "r");
if (f == NULL) {
LOGE("opening %s: %s\n", filename, strerror(errno));
goto exit;
}
int i;
bool done = false;
while (!done) {
++*numKeys;
out = realloc(out, *numKeys * sizeof(RSAPublicKey));
RSAPublicKey* key = out + (*numKeys - 1);
if (fscanf(f, " { %i , %i , { %i",
&(key->len), &(key->n0inv), &(key->n[0])) != 3) {
goto exit;
}
if (key->len != RSANUMWORDS) {
LOGE("key length (%d) does not match expected size\n", key->len);
goto exit;
}
for (i = 1; i < key->len; ++i) {
if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit;
}
if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit;
for (i = 1; i < key->len; ++i) {
if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit;
}
fscanf(f, " } } ");
// if the line ends in a comma, this file has more keys.
switch (fgetc(f)) {
case ',':
// more keys to come.
break;
case EOF:
done = true;
break;
default:
LOGE("unexpected character between keys\n");
goto exit;
}
}
fclose(f);
return out;
exit:
if (f) fclose(f);
free(out);
*numKeys = 0;
return NULL;
}
int
@ -169,10 +362,36 @@ install_package(const char *root_path)
ui_print("Opening update package...\n");
LOGI("Update file path: %s\n", path);
int err;
if (signature_check_enabled) {
int numKeys;
RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
if (loadedKeys == NULL) {
LOGE("Failed to load keys\n");
return INSTALL_CORRUPT;
}
LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
// Give verification half the progress bar...
ui_print("Verifying update package...\n");
ui_show_progress(
VERIFICATION_PROGRESS_FRACTION,
VERIFICATION_PROGRESS_TIME);
err = verify_file(path, loadedKeys, numKeys);
free(loadedKeys);
LOGI("verify_file returned %d\n", err);
if (err != VERIFY_SUCCESS) {
LOGE("signature verification failed\n");
return INSTALL_CORRUPT;
}
}
/* Try to open the package.
*/
ZipArchive zip;
int err = mzOpenZipArchive(path, &zip);
err = mzOpenZipArchive(path, &zip);
if (err != 0) {
LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
return INSTALL_CORRUPT;

View File

@ -19,7 +19,7 @@
#include "common.h"
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_SCRIPT_MISSING, INSTALL_UPDATE_BINARY_MISSING };
int install_package(const char *root_path);
#endif // RECOVERY_INSTALL_H_

22
killrecovery.sh Executable file
View File

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

124
legacy.c Normal file
View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "common.h"
#include "install.h"
#include "mincrypt/rsa.h"
#include "minui/minui.h"
#include "minzip/SysUtil.h"
#include "minzip/Zip.h"
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
#include "firmware.h"
#include "amend/amend.h"
#include "common.h"
#include "install.h"
#include "mincrypt/rsa.h"
#include "minui/minui.h"
#include "minzip/SysUtil.h"
#include "minzip/Zip.h"
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
static int read_data(ZipArchive *zip, const ZipEntry *entry,
char** ppData, int* pLength) {
int len = (int)mzGetZipEntryUncompLen(entry);
if (len <= 0) {
LOGE("Bad data length %d\n", len);
return -1;
}
char *data = malloc(len + 1);
if (data == NULL) {
LOGE("Can't allocate %d bytes for data\n", len + 1);
return -2;
}
bool ok = mzReadZipEntry(zip, entry, data, len);
if (!ok) {
LOGE("Error while reading data\n");
free(data);
return -3;
}
data[len] = '\0'; // not necessary, but just to be safe
*ppData = data;
if (pLength) {
*pLength = len;
}
return 0;
}
int
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
{
/* Read the entire script into a buffer.
*/
int script_len;
char* script_data;
if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) {
LOGE("Can't read update script\n");
return INSTALL_UPDATE_SCRIPT_MISSING;
}
/* Parse the script. Note that the script and parse tree are never freed.
*/
const AmCommandList *commands = parseAmendScript(script_data, script_len);
if (commands == NULL) {
LOGE("Syntax error in update script\n");
return INSTALL_ERROR;
} else {
UnterminatedString name = mzGetZipEntryFileName(update_script_entry);
LOGI("Parsed %.*s\n", name.len, name.str);
}
/* Execute the script.
*/
int ret = execCommandList((ExecContext *)1, commands);
if (ret != 0) {
int num = ret;
char *line, *next = script_data;
while (next != NULL && ret-- > 0) {
line = next;
next = memchr(line, '\n', script_data + script_len - line);
if (next != NULL) *next++ = '\0';
}
LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
return INSTALL_ERROR;
}
ui_print("Installation complete.\n");
return INSTALL_SUCCESS;
}
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
const ZipEntry *
find_update_script(ZipArchive *zip)
{
//TODO: Get the location of this script from the MANIFEST.MF file
return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
}

5
legacy.h Normal file
View File

@ -0,0 +1,5 @@
int
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry);
const ZipEntry *
find_update_script(ZipArchive *zip);

View File

@ -115,6 +115,7 @@ static void set_active_framebuffer(unsigned n)
if (n > 1) return;
vi.yres_virtual = vi.yres * 2;
vi.yoffset = n * vi.yres;
vi.bits_per_pixel = 16;
if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
perror("active fb swap failed");
}

View File

@ -41,22 +41,6 @@ unsigned int gr_get_height(gr_surface surface);
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
struct input_event;
// Dream-specific key codes
#define KEY_DREAM_HOME 102 // = KEY_HOME
#define KEY_DREAM_RED 107 // = KEY_END
#define KEY_DREAM_VOLUMEDOWN 114 // = KEY_VOLUMEDOWN
#define KEY_DREAM_VOLUMEUP 115 // = KEY_VOLUMEUP
#define KEY_DREAM_SYM 127 // = KEY_COMPOSE
#define KEY_DREAM_MENU 139 // = KEY_MENU
#define KEY_DREAM_BACK 158 // = KEY_BACK
#define KEY_DREAM_FOCUS 211 // = KEY_HP (light touch on camera)
#define KEY_DREAM_CAMERA 212 // = KEY_CAMERA
#define KEY_DREAM_AT 215 // = KEY_EMAIL
#define KEY_DREAM_GREEN 231
#define KEY_DREAM_FATTOUCH 258 // = BTN_2 ???
#define KEY_DREAM_BALL 272 // = BTN_MOUSE
#define KEY_DREAM_TOUCH 330 // = BTN_TOUCH
int ev_init(void);
void ev_exit(void);
int ev_get(struct input_event *ev, unsigned dont_wait);

View File

@ -29,97 +29,84 @@
#include <pixelflinger/pixelflinger.h>
#include <png.h>
#include "minui.h"
// File signature for BMP files.
// The letters 'BM' as a little-endian unsigned short.
#define BMP_SIGNATURE 0x4d42
typedef struct {
// constant, value should equal BMP_SIGNATURE
unsigned short bfType;
// size of the file in bytes.
unsigned long bfSize;
// must always be set to zero.
unsigned short bfReserved1;
// must always be set to zero.
unsigned short bfReserved2;
// offset from the beginning of the file to the bitmap data.
unsigned long bfOffBits;
// The BITMAPINFOHEADER:
// size of the BITMAPINFOHEADER structure, in bytes.
unsigned long biSize;
// width of the image, in pixels.
unsigned long biWidth;
// height of the image, in pixels.
unsigned long biHeight;
// number of planes of the target device, must be set to 1.
unsigned short biPlanes;
// number of bits per pixel.
unsigned short biBitCount;
// type of compression, zero means no compression.
unsigned long biCompression;
// size of the image data, in bytes. If there is no compression,
// it is valid to set this member to zero.
unsigned long biSizeImage;
// horizontal pixels per meter on the designated targer device,
// usually set to zero.
unsigned long biXPelsPerMeter;
// vertical pixels per meter on the designated targer device,
// usually set to zero.
unsigned long biYPelsPerMeter;
// number of colors used in the bitmap, if set to zero the
// number of colors is calculated using the biBitCount member.
unsigned long biClrUsed;
// number of color that are 'important' for the bitmap,
// if set to zero, all colors are important.
unsigned long biClrImportant;
} __attribute__((packed)) BitMapFileHeader;
// libpng gives "undefined reference to 'pow'" errors, and I have no
// idea how to convince the build system to link with -lm. We don't
// need this functionality (it's used for gamma adjustment) so provide
// a dummy implementation to satisfy the linker.
double pow(double x, double y) {
return x;
}
int res_create_surface(const char* name, gr_surface* pSurface) {
char resPath[256];
BitMapFileHeader header;
GGLSurface* surface = NULL;
int result = 0;
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.bmp", name);
unsigned char header[8];
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
resPath[sizeof(resPath)-1] = '\0';
int fd = open(resPath, O_RDONLY);
if (fd == -1) {
FILE* fp = fopen(resPath, "rb");
if (fp == NULL) {
result = -1;
goto exit;
}
size_t bytesRead = read(fd, &header, sizeof(header));
size_t bytesRead = fread(header, 1, sizeof(header), fp);
if (bytesRead != sizeof(header)) {
result = -2;
goto exit;
}
if (header.bfType != BMP_SIGNATURE) {
result = -3; // Not a legal header
if (png_sig_cmp(header, 0, sizeof(header))) {
result = -3;
goto exit;
}
if (header.biPlanes != 1) {
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
result = -4;
goto exit;
}
if (!(header.biBitCount == 24 || header.biBitCount == 32)) {
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
result = -5;
goto exit;
}
if (header.biCompression != 0) {
if (setjmp(png_jmpbuf(png_ptr))) {
result = -6;
goto exit;
}
size_t width = header.biWidth;
size_t height = header.biHeight;
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, sizeof(header));
png_read_info(png_ptr, info_ptr);
size_t width = info_ptr->width;
size_t height = info_ptr->height;
size_t stride = 4 * width;
size_t pixelSize = stride * height;
int color_type = info_ptr->color_type;
int bit_depth = info_ptr->bit_depth;
int channels = info_ptr->channels;
if (bit_depth != 8 || (channels != 3 && channels != 4) ||
(color_type != PNG_COLOR_TYPE_RGB &&
color_type != PNG_COLOR_TYPE_RGBA)) {
return -7;
goto exit;
}
surface = malloc(sizeof(GGLSurface) + pixelSize);
if (surface == NULL) {
result = -7;
result = -8;
goto exit;
}
unsigned char* pData = (unsigned char*) (surface + 1);
@ -128,63 +115,43 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
surface->height = height;
surface->stride = width; /* Yes, pixels, not bytes */
surface->data = pData;
surface->format = (header.biBitCount == 24) ?
surface->format = (channels == 3) ?
GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
// Source pixel bytes are stored B G R {A}
lseek(fd, header.bfOffBits, SEEK_SET);
size_t y;
if (header.biBitCount == 24) { // RGB
size_t inputStride = (((3 * width + 3) >> 2) << 2);
for (y = 0; y < height; y++) {
unsigned char* pRow = pData + (height - (y + 1)) * stride;
bytesRead = read(fd, pRow, inputStride);
if (bytesRead != inputStride) {
result = -8;
goto exit;
}
int y;
if (channels == 3) {
for (y = 0; y < height; ++y) {
unsigned char* pRow = pData + y * stride;
png_read_row(png_ptr, pRow, NULL);
int x;
for(x = width - 1; x >= 0; x--) {
int sx = x * 3;
int dx = x * 4;
unsigned char b = pRow[sx];
unsigned char r = pRow[sx];
unsigned char g = pRow[sx + 1];
unsigned char r = pRow[sx + 2];
unsigned char b = pRow[sx + 2];
unsigned char a = 0xff;
pRow[dx ] = r; // r
pRow[dx + 1] = g; // g
pRow[dx + 2] = b; // b;
pRow[dx + 2] = b; // b
pRow[dx + 3] = a;
}
}
} else { // RGBA
for (y = 0; y < height; y++) {
unsigned char* pRow = pData + (height - (y + 1)) * stride;
bytesRead = read(fd, pRow, stride);
if (bytesRead != stride) {
result = -9;
goto exit;
}
size_t x;
for(x = 0; x < width; x++) {
size_t xx = x * 4;
unsigned char b = pRow[xx];
unsigned char g = pRow[xx + 1];
unsigned char r = pRow[xx + 2];
unsigned char a = pRow[xx + 3];
pRow[xx ] = r;
pRow[xx + 1] = g;
pRow[xx + 2] = b;
pRow[xx + 3] = a;
}
} else {
for (y = 0; y < height; ++y) {
unsigned char* pRow = pData + y * stride;
png_read_row(png_ptr, pRow, NULL);
}
}
*pSurface = (gr_surface) surface;
exit:
if (fd >= 0) {
close(fd);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if (fp != NULL) {
fclose(fp);
}
if (result < 0) {
if (surface) {

View File

@ -41,7 +41,7 @@ enum {
CENSIZ = 20,
CENLEN = 24,
CENNAM = 28,
CENEXT = 30,
CENEXT = 30,
CENCOM = 32,
CENDSK = 34,
CENATT = 36,
@ -66,13 +66,13 @@ enum {
LOCSIG = 0x04034b50, // PK34
LOCHDR = 30,
LOCVER = 4,
LOCFLG = 6,
LOCHOW = 8,
LOCTIM = 10,
LOCCRC = 14,
LOCSIZ = 18,
LOCSIZ = 18,
LOCLEN = 22,
LOCNAM = 26,
LOCEXT = 28,
@ -757,7 +757,7 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
{
CopyProcessArgs args;
bool ret;
args.buf = buf;
args.bufLen = bufLen;
ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction,
@ -770,15 +770,29 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
}
static bool writeProcessFunction(const unsigned char *data, int dataLen,
void *fd)
void *cookie)
{
ssize_t n = write((int)fd, data, dataLen);
if (n != dataLen) {
LOGE("Can't write %d bytes (only %ld) from zip file: %s\n",
dataLen, n, strerror(errno));
return false;
int fd = (int)cookie;
ssize_t soFar = 0;
while (true) {
ssize_t n = write(fd, data+soFar, dataLen-soFar);
if (n <= 0) {
LOGE("Error writing %ld bytes from zip file from %p: %s\n",
dataLen-soFar, data+soFar, strerror(errno));
if (errno != EINTR) {
return false;
}
} else if (n > 0) {
soFar += n;
if (soFar == dataLen) return true;
if (soFar > dataLen) {
LOGE("write overrun? (%ld bytes instead of %d)\n",
soFar, dataLen);
return false;
}
}
}
return true;
}
/*
@ -788,7 +802,7 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
const ZipEntry *pEntry, int fd)
{
bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
(void *)fd);
(void*)fd);
if (!ret) {
LOGE("Can't extract entry to file.\n");
return false;

View File

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

17
mtdutils/driver.c Normal file
View File

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

137
mtdutils/dump_image.c Normal file
View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "cutils/log.h"
#include "mtdutils.h"
#include "dump_image.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "dump_image"
#define BLOCK_SIZE 2048
#define SPARE_SIZE (BLOCK_SIZE >> 5)
static int die(const char *msg, ...) {
int err = errno;
va_list args;
va_start(args, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
if (err != 0) {
strlcat(buf, ": ", sizeof(buf));
strlcat(buf, strerror(err), sizeof(buf));
}
fprintf(stderr, "%s\n", buf);
return 1;
}
/* Read a flash partition and write it to an image file. */
int dump_image(char* partition_name, char* filename, dump_image_callback callback) {
MtdReadContext *in;
const MtdPartition *partition;
char buf[BLOCK_SIZE + SPARE_SIZE];
size_t partition_size;
size_t read_size;
size_t total;
int fd;
int wrote;
int len;
if (mtd_scan_partitions() <= 0)
return die("error scanning partitions");
partition = mtd_find_partition_by_name(partition_name);
if (partition == NULL)
return die("can't find %s partition", partition_name);
if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
return die("can't get info of partition %s", partition_name);
}
if (!strcmp(filename, "-")) {
fd = fileno(stdout);
}
else {
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
}
if (fd < 0)
return die("error opening %s", filename);
in = mtd_read_partition(partition);
if (in == NULL) {
close(fd);
unlink(filename);
return die("error opening %s: %s\n", partition_name, strerror(errno));
}
total = 0;
while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
wrote = write(fd, buf, len);
if (wrote != len) {
close(fd);
unlink(filename);
return die("error writing %s", filename);
}
total += BLOCK_SIZE;
if (callback != NULL)
callback(total, partition_size);
}
mtd_read_close(in);
if (close(fd)) {
unlink(filename);
return die("error closing %s", filename);
}
return 0;
}
int main(int argc, char **argv)
{
ssize_t (*read_func) (MtdReadContext *, char *, size_t);
MtdReadContext *in;
const MtdPartition *partition;
char buf[BLOCK_SIZE + SPARE_SIZE];
size_t partition_size;
size_t read_size;
size_t total;
int fd;
int wrote;
int len;
if (argc != 3) {
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
return 2;
}
return dump_image(argv[1], argv[2], NULL);
}

8
mtdutils/dump_image.h Normal file
View File

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

88
mtdutils/erase_image.c Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Portions Copyright (C) 2010 Magnus Eriksson <packetlss@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <mtd/mtd-user.h>
#include "cutils/log.h"
#include "mtdutils.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "erase_image"
static int die(const char *msg, ...) {
int err = errno;
va_list args;
va_start(args, msg);
char buf[1024];
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
if (err != 0) {
strlcat(buf, ": ", sizeof(buf));
strlcat(buf, strerror(err), sizeof(buf));
}
fprintf(stderr, "%s\n", buf);
LOGE("%s\n", buf);
return 3;
}
int erase_image(char* partition_name) {
MtdWriteContext *out;
size_t erased;
size_t total_size;
size_t erase_size;
if (mtd_scan_partitions() <= 0) die("error scanning partitions");
const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
if (partition == NULL) return die("can't find %s partition", partition_name);
out = mtd_write_partition(partition);
if (out == NULL) return die("could not estabilish write context for %s", partition_name);
// do the actual erase, -1 = full partition erase
erased = mtd_erase_blocks(out, -1);
// erased = bytes erased, if zero, something borked
if (!erased) return die("error erasing %s", partition_name);
return 0;
}
/* Erase a mtd partition */
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <partition>\n", argv[0]);
return 2;
}
return erase_image(argv[1]);
}

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;
@ -47,6 +40,10 @@ struct MtdWriteContext {
char *buffer;
size_t stored;
int fd;
off_t* bad_block_offsets;
int bad_block_alloc;
int bad_block_count;
};
typedef struct {
@ -283,21 +280,35 @@ static int read_block(const MtdPartition *partition, int fd, char *data)
return -1;
}
off_t pos = lseek(fd, 0, SEEK_CUR);
loff_t pos = lseek64(fd, 0, SEEK_CUR);
ssize_t size = partition->erase_size;
int mgbb;
while (pos + size <= (int) partition->size) {
if (lseek(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
fprintf(stderr, "mtd: read error at 0x%08lx (%s)\n",
if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
fprintf(stderr, "mtd: read error at 0x%08llx (%s)\n",
pos, strerror(errno));
} else if (ioctl(fd, ECCGETSTATS, &after)) {
fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
return -1;
} else if (after.failed != before.failed) {
fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08lx\n",
fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
after.corrected - before.corrected,
after.failed - before.failed, pos);
} else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
fprintf(stderr,
"mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
mgbb, pos, errno);
} else {
return 0; // Success!
int i;
for (i = 0; i < size; ++i) {
if (data[i] != 0) {
return 0; // Success!
}
}
fprintf(stderr, "mtd: read all-zero block at 0x%08llx; skipping\n",
pos);
}
pos += partition->erase_size;
@ -326,6 +337,10 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
read += ctx->partition->erase_size;
}
if (read >= len) {
return read;
}
// Read the next block into the buffer
if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
@ -348,6 +363,10 @@ MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
if (ctx == NULL) return NULL;
ctx->bad_block_offsets = NULL;
ctx->bad_block_alloc = 0;
ctx->bad_block_count = 0;
ctx->buffer = malloc(partition->erase_size);
if (ctx->buffer == NULL) {
free(ctx);
@ -368,8 +387,20 @@ MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
return ctx;
}
static int write_block(const MtdPartition *partition, int fd, const char *data)
static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
ctx->bad_block_alloc * sizeof(off_t));
}
ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
}
static int write_block(MtdWriteContext *ctx, const char *data)
{
const MtdPartition *partition = ctx->partition;
int fd = ctx->fd;
off_t pos = lseek(fd, 0, SEEK_CUR);
if (pos == (off_t) -1) return 1;
@ -377,6 +408,7 @@ static int write_block(const MtdPartition *partition, int fd, const char *data)
while (pos + size <= (int) partition->size) {
loff_t bpos = pos;
if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) {
add_bad_block_offset(ctx, pos);
fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos);
pos += partition->erase_size;
continue; // Don't try to erase known factory-bad blocks.
@ -418,6 +450,7 @@ static int write_block(const MtdPartition *partition, int fd, const char *data)
}
// Try to erase it once more as we give up on this block
add_bad_block_offset(ctx, pos);
fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
ioctl(fd, MEMERASE, &erase_info);
pos += partition->erase_size;
@ -443,13 +476,13 @@ ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
// If a complete block was accumulated, write it
if (ctx->stored == ctx->partition->erase_size) {
if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
if (write_block(ctx, ctx->buffer)) return -1;
ctx->stored = 0;
}
// Write complete blocks directly from the user's buffer
while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
if (write_block(ctx->partition, ctx->fd, data + wrote)) return -1;
if (write_block(ctx, data + wrote)) return -1;
wrote += ctx->partition->erase_size;
}
}
@ -463,7 +496,7 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
if (ctx->stored > 0) {
size_t zero = ctx->partition->erase_size - ctx->stored;
memset(ctx->buffer + ctx->stored, 0, zero);
if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
if (write_block(ctx, ctx->buffer)) return -1;
ctx->stored = 0;
}
@ -504,7 +537,23 @@ int mtd_write_close(MtdWriteContext *ctx)
// Make sure any pending data gets written
if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
if (close(ctx->fd)) r = -1;
free(ctx->bad_block_offsets);
free(ctx->buffer);
free(ctx);
return r;
}
/* Return the offset of the first good block at or after pos (which
* might be pos itself).
*/
off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
int i;
for (i = 0; i < ctx->bad_block_count; ++i) {
if (ctx->bad_block_offsets[i] == pos) {
pos += ctx->partition->erase_size;
} else if (ctx->bad_block_offsets[i] > pos) {
return pos;
}
}
return pos;
}

View File

@ -49,6 +49,14 @@ void mtd_read_close(MtdReadContext *);
MtdWriteContext *mtd_write_partition(const MtdPartition *);
ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len);
off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */
off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
int mtd_write_close(MtdWriteContext *);
struct MtdPartition {
int device_index;
unsigned int size;
unsigned int erase_size;
char *name;
};
#endif // MTDUTILS_H_

4
nandroid-md5.sh Normal file
View File

@ -0,0 +1,4 @@
#!/sbin/sh
cd $1
md5sum *img > nandroid.md5
return $?

368
nandroid.c Normal file
View File

@ -0,0 +1,368 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>
#include "bootloader.h"
#include "common.h"
#include "cutils/properties.h"
#include "firmware.h"
#include "install.h"
#include "minui/minui.h"
#include "minzip/DirUtil.h"
#include "roots.h"
#include "recovery_ui.h"
#include "commands.h"
#include "amend/amend.h"
#include "mtdutils/dump_image.h"
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
#include <sys/vfs.h>
#include "extendedcommands.h"
#include "nandroid.h"
int print_and_error(char* message) {
ui_print(message);
return 1;
}
int yaffs_files_total = 0;
int yaffs_files_count = 0;
void yaffs_callback(char* filename)
{
char* justfile = basename(filename);
if (strlen(justfile) < 30)
ui_print(justfile);
yaffs_files_count++;
if (yaffs_files_total != 0)
ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total);
ui_reset_text_col();
}
void compute_directory_stats(char* directory)
{
char tmp[PATH_MAX];
sprintf(tmp, "find %s | wc -l > /tmp/dircount", directory);
__system(tmp);
char count_text[100];
FILE* f = fopen("/tmp/dircount", "r");
fread(count_text, 1, sizeof(count_text), f);
fclose(f);
yaffs_files_count = 0;
yaffs_files_total = atoi(count_text);
ui_reset_progress();
ui_show_progress(1, 0);
}
int nandroid_backup_partition_extended(const char* backup_path, char* root, int umount_when_finished) {
int ret = 0;
char mount_point[PATH_MAX];
translate_root_path(root, mount_point, PATH_MAX);
char* name = basename(mount_point);
struct stat file_info;
mkyaffs2image_callback callback = NULL;
if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
callback = yaffs_callback;
}
ui_print("Backing up %s...\n", name);
if (0 != (ret = ensure_root_path_mounted(root) != 0)) {
ui_print("Can't mount %s!\n", mount_point);
return ret;
}
compute_directory_stats(mount_point);
char tmp[PATH_MAX];
sprintf(tmp, "%s/%s.img", backup_path, name);
ret = mkyaffs2image(mount_point, tmp, 0, callback);
if (umount_when_finished) {
ensure_root_path_unmounted(root);
}
if (0 != ret) {
ui_print("Error while making a yaffs2 image of %s!\n", mount_point);
return ret;
}
return 0;
}
int nandroid_backup_partition(const char* backup_path, char* root) {
return nandroid_backup_partition_extended(backup_path, root, 1);
}
int nandroid_backup(const char* backup_path)
{
ui_set_background(BACKGROUND_ICON_INSTALLING);
if (ensure_root_path_mounted("SDCARD:") != 0)
return print_and_error("Can't mount /sdcard\n");
int ret;
struct statfs s;
if (0 != (ret = statfs("/sdcard", &s)))
return print_and_error("Unable to stat /sdcard\n");
uint64_t bavail = s.f_bavail;
uint64_t bsize = s.f_bsize;
uint64_t sdcard_free = bavail * bsize;
uint64_t sdcard_free_mb = sdcard_free / (uint64_t)(1024 * 1024);
ui_print("SD Card space free: %lluMB\n", sdcard_free_mb);
if (sdcard_free_mb < 150)
ui_print("There may not be enough free space to complete backup... continuing...\n");
char tmp[PATH_MAX];
sprintf(tmp, "mkdir -p %s", backup_path);
__system(tmp);
#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
ui_print("Backing up boot...\n");
sprintf(tmp, "%s/%s", backup_path, "boot.img");
ret = dump_image("boot", tmp, NULL);
if (0 != ret)
return print_and_error("Error while dumping boot image!\n");
ui_print("Backing up recovery...\n");
sprintf(tmp, "%s/%s", backup_path, "recovery.img");
ret = dump_image("recovery", tmp, NULL);
if (0 != ret)
return print_and_error("Error while dumping recovery image!\n");
#endif
if (0 != (ret = nandroid_backup_partition(backup_path, "SYSTEM:")))
return ret;
if (0 != (ret = nandroid_backup_partition(backup_path, "DATA:")))
return ret;
#ifdef HAS_DATADATA
if (0 != (ret = nandroid_backup_partition(backup_path, "DATADATA:")))
return ret;
#endif
struct stat st;
if (0 != stat("/sdcard/.android_secure", &st))
{
ui_print("No /sdcard/.android_secure found. Skipping backup of applications on external storage.\n");
}
else
{
if (0 != (ret = nandroid_backup_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
return ret;
}
if (0 != (ret = nandroid_backup_partition_extended(backup_path, "CACHE:", 0)))
return ret;
if (0 != stat(SDEXT_DEVICE, &st))
{
ui_print("No sd-ext found. Skipping backup of sd-ext.\n");
}
else
{
if (0 != ensure_root_path_mounted("SDEXT:"))
ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.\n");
else if (0 != (ret = nandroid_backup_partition(backup_path, "SDEXT:")))
return ret;
}
ui_print("Generating md5 sum...\n");
sprintf(tmp, "nandroid-md5.sh %s", backup_path);
if (0 != (ret = __system(tmp))) {
ui_print("Error while generating md5 sum!\n");
return ret;
}
sync();
ui_set_background(BACKGROUND_ICON_NONE);
ui_reset_progress();
ui_print("\nBackup complete!\n");
return 0;
}
typedef int (*format_function)(char* root);
static void ensure_directory(const char* dir) {
char tmp[PATH_MAX];
sprintf(tmp, "mkdir -p %s", dir);
__system(tmp);
}
int nandroid_restore_partition_extended(const char* backup_path, const char* root, int umount_when_finished) {
int ret = 0;
char mount_point[PATH_MAX];
translate_root_path(root, mount_point, PATH_MAX);
char* name = basename(mount_point);
char tmp[PATH_MAX];
sprintf(tmp, "%s/%s.img", backup_path, name);
struct stat file_info;
if (0 != (ret = statfs(tmp, &file_info))) {
ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point);
return 0;
}
ensure_directory(mount_point);
unyaffs_callback callback = NULL;
if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
callback = yaffs_callback;
}
ui_print("Restoring %s...\n", name);
/*
if (0 != (ret = ensure_root_path_unmounted(root))) {
ui_print("Can't unmount %s!\n", mount_point);
return ret;
}
*/
if (0 != (ret = format_root_device(root))) {
ui_print("Error while formatting %s!\n", root);
return ret;
}
if (0 != (ret = ensure_root_path_mounted(root))) {
ui_print("Can't mount %s!\n", mount_point);
return ret;
}
if (0 != (ret = unyaffs(tmp, mount_point, callback))) {
ui_print("Error while restoring %s!\n", mount_point);
return ret;
}
if (umount_when_finished) {
ensure_root_path_unmounted(root);
}
return 0;
}
int nandroid_restore_partition(const char* backup_path, const char* root) {
return nandroid_restore_partition_extended(backup_path, root, 1);
}
int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext)
{
ui_set_background(BACKGROUND_ICON_INSTALLING);
ui_show_indeterminate_progress();
yaffs_files_total = 0;
if (ensure_root_path_mounted("SDCARD:") != 0)
return print_and_error("Can't mount /sdcard\n");
char tmp[PATH_MAX];
ui_print("Checking MD5 sums...\n");
sprintf(tmp, "cd %s && md5sum -c nandroid.md5", backup_path);
if (0 != __system(tmp))
return print_and_error("MD5 mismatch!\n");
int ret;
#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
if (restore_boot)
{
ui_print("Erasing boot before restore...\n");
if (0 != (ret = format_root_device("BOOT:")))
return print_and_error("Error while formatting BOOT:!\n");
sprintf(tmp, "flash_image boot %s/boot.img", backup_path);
ui_print("Restoring boot image...\n");
if (0 != (ret = __system(tmp))) {
ui_print("Error while flashing boot image!");
return ret;
}
}
#endif
if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "SYSTEM:")))
return ret;
if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATA:")))
return ret;
#ifdef HAS_DATADATA
if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATADATA:")))
return ret;
#endif
if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
return ret;
if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "CACHE:", 0)))
return ret;
if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "SDEXT:")))
return ret;
sync();
ui_set_background(BACKGROUND_ICON_NONE);
ui_reset_progress();
ui_print("\nRestore complete!\n");
return 0;
}
void nandroid_generate_timestamp_path(char* backup_path)
{
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
if (tmp == NULL)
{
struct timeval tp;
gettimeofday(&tp, NULL);
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
}
else
{
strftime(backup_path, PATH_MAX, "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
}
}
int nandroid_usage()
{
printf("Usage: nandroid backup\n");
printf("Usage: nandroid restore <directory>\n");
return 1;
}
int nandroid_main(int argc, char** argv)
{
if (argc > 3 || argc < 2)
return nandroid_usage();
if (strcmp("backup", argv[1]) == 0)
{
if (argc != 2)
return nandroid_usage();
char backup_path[PATH_MAX];
nandroid_generate_timestamp_path(backup_path);
return nandroid_backup(backup_path);
}
if (strcmp("restore", argv[1]) == 0)
{
if (argc != 3)
return nandroid_usage();
return nandroid_restore(argv[2], 1, 1, 1, 1, 1);
}
return nandroid_usage();
}

9
nandroid.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef NANDROID_H
#define NANDROID_H
int nandroid_main(int argc, char** argv);
int nandroid_backup(const char* backup_path);
int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext);
void nandroid_generate_timestamp_path(char* backup_path);
#endif

72
reboot.c Normal file
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

@ -27,9 +27,9 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include "bootloader.h"
#include "commands.h"
#include "common.h"
#include "cutils/properties.h"
#include "firmware.h"
@ -37,14 +37,21 @@
#include "minui/minui.h"
#include "minzip/DirUtil.h"
#include "roots.h"
#include "recovery_ui.h"
#include "extendedcommands.h"
#include "commands.h"
static const struct option OPTIONS[] = {
{ "send_intent", required_argument, NULL, 's' },
{ "update_package", required_argument, NULL, 'u' },
{ "wipe_data", no_argument, NULL, 'w' },
{ "wipe_cache", no_argument, NULL, 'c' },
{ NULL, 0, NULL, 0 },
};
static int allow_display_toggle = 1;
static const char *COMMAND_FILE = "CACHE:recovery/command";
static const char *INTENT_FILE = "CACHE:recovery/intent";
static const char *LOG_FILE = "CACHE:recovery/log";
@ -130,7 +137,7 @@ fopen_root_path(const char *root_path, const char *mode) {
if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
FILE *fp = fopen(path, mode);
if (fp == NULL) LOGE("Can't open %s\n", path);
if (fp == NULL && root_path != COMMAND_FILE) LOGE("Can't open %s\n", path);
return fp;
}
@ -150,7 +157,9 @@ static void
get_args(int *argc, char ***argv) {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
#ifndef BOARD_HAS_NO_MISC_PARTITION
get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
#endif
if (boot.command[0] != 0 && boot.command[0] != 255) {
LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
@ -160,8 +169,10 @@ get_args(int *argc, char ***argv) {
LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
}
struct stat file_info;
// --- if arguments weren't supplied, look in the bootloader control block
if (*argc <= 1) {
if (*argc <= 1 && 0 != stat("/tmp/.ignorebootmessage", &file_info)) {
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
const char *arg = strtok(boot.recovery, "\n");
if (arg != NULL && !strcmp(arg, "recovery")) {
@ -205,9 +216,22 @@ get_args(int *argc, char ***argv) {
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
set_bootloader_message(&boot);
#endif
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
void
set_sdcard_update_bootloader_message()
{
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
set_bootloader_message(&boot);
}
#endif
// clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read), and
@ -242,10 +266,12 @@ finish_recovery(const char *send_intent)
check_and_fclose(log, LOG_FILE);
}
#ifndef BOARD_HAS_NO_MISC_PARTITION
// Reset the bootloader message to revert to a normal main system boot.
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
set_bootloader_message(&boot);
#endif
// Remove the command file, so recovery won't repeat indefinitely.
char path[PATH_MAX] = "";
@ -258,27 +284,6 @@ finish_recovery(const char *send_intent)
sync(); // For good measure.
}
#define TEST_AMEND 0
#if TEST_AMEND
static void
test_amend()
{
extern int test_symtab(void);
extern int test_cmd_fn(void);
extern int test_permissions(void);
int ret;
LOGD("Testing symtab...\n");
ret = test_symtab();
LOGD(" returned %d\n", ret);
LOGD("Testing cmd_fn...\n");
ret = test_cmd_fn();
LOGD(" returned %d\n", ret);
LOGD("Testing permissions...\n");
ret = test_permissions();
LOGD(" returned %d\n", ret);
}
#endif // TEST_AMEND
static int
erase_root(const char *root)
{
@ -288,77 +293,188 @@ erase_root(const char *root)
return format_root_device(root);
}
static void
prompt_and_wait()
{
char* headers[] = { "Android system recovery utility",
"",
"Use trackball to highlight;",
"click to select.",
"",
NULL };
// these constants correspond to elements of the items[] list.
#define ITEM_REBOOT 0
#define ITEM_APPLY_SDCARD 1
#define ITEM_WIPE_DATA 2
char* items[] = { "reboot system now [Home+Back]",
"apply sdcard:update.zip [Alt+S]",
"wipe data/factory reset [Alt+W]",
static char**
prepend_title(char** headers) {
char* title[] = { EXPAND(RECOVERY_VERSION),
"",
NULL };
ui_start_menu(headers, items);
// count the number of lines in our title, plus the
// caller-provided headers.
int count = 0;
char** p;
for (p = title; *p; ++p, ++count);
for (p = headers; *p; ++p, ++count);
char** new_headers = malloc((count+1) * sizeof(char*));
char** h = new_headers;
for (p = title; *p; ++p, ++h) *h = *p;
for (p = headers; *p; ++p, ++h) *h = *p;
*h = NULL;
return new_headers;
}
int
get_menu_selection(char** headers, char** items, int menu_only) {
// throw away keys pressed previously, so user doesn't
// accidentally trigger menu items.
ui_clear_key_queue();
int item_count = ui_start_menu(headers, items);
int selected = 0;
int chosen_item = -1;
finish_recovery(NULL);
ui_reset_progress();
for (;;) {
// Some users with dead enter keys need a way to turn on power to select.
// Jiggering across the wrapping menu is one "secret" way to enable it.
// We can't rely on /cache or /sdcard since they may not be available.
int wrap_count = 0;
while (chosen_item < 0 && chosen_item != GO_BACK) {
int key = ui_wait_key();
int alt = ui_key_pressed(KEY_LEFTALT) || ui_key_pressed(KEY_RIGHTALT);
int visible = ui_text_visible();
if (key == KEY_DREAM_BACK && ui_key_pressed(KEY_DREAM_HOME)) {
// Wait for the keys to be released, to avoid triggering
// special boot modes (like coming back into recovery!).
while (ui_key_pressed(KEY_DREAM_BACK) ||
ui_key_pressed(KEY_DREAM_HOME)) {
usleep(1000);
int action = device_handle_key(key, visible);
int old_selected = selected;
if (action < 0) {
switch (action) {
case HIGHLIGHT_UP:
--selected;
selected = ui_menu_select(selected);
break;
case HIGHLIGHT_DOWN:
++selected;
selected = ui_menu_select(selected);
break;
case SELECT_ITEM:
chosen_item = selected;
if (ui_get_showing_back_button()) {
if (chosen_item == item_count) {
chosen_item = GO_BACK;
}
}
break;
case NO_ACTION:
break;
case GO_BACK:
chosen_item = GO_BACK;
break;
}
chosen_item = ITEM_REBOOT;
} else if (alt && key == KEY_W) {
chosen_item = ITEM_WIPE_DATA;
} else if (alt && key == KEY_S) {
chosen_item = ITEM_APPLY_SDCARD;
} else if ((key == KEY_DOWN || key == KEY_VOLUMEDOWN) && visible) {
++selected;
selected = ui_menu_select(selected);
} else if ((key == KEY_UP || key == KEY_VOLUMEUP) && visible) {
--selected;
selected = ui_menu_select(selected);
} else if (key == BTN_MOUSE && visible) {
chosen_item = selected;
} else if (!menu_only) {
chosen_item = action;
}
if (chosen_item >= 0) {
// turn off the menu, letting ui_print() to scroll output
// on the screen.
ui_end_menu();
if (abs(selected - old_selected) > 1) {
wrap_count++;
if (wrap_count == 3) {
wrap_count = 0;
if (ui_get_showing_back_button()) {
ui_print("Back menu button disabled.\n");
ui_set_showing_back_button(0);
}
else {
ui_print("Back menu button enabled.\n");
ui_set_showing_back_button(1);
}
}
}
}
switch (chosen_item) {
case ITEM_REBOOT:
return;
ui_end_menu();
ui_clear_key_queue();
return chosen_item;
}
case ITEM_WIPE_DATA:
ui_print("\n-- Wiping data...\n");
erase_root("DATA:");
static void
wipe_data(int confirm) {
if (confirm) {
static char** title_headers = NULL;
if (title_headers == NULL) {
char* headers[] = { "Confirm wipe of all user data?",
" THIS CAN NOT BE UNDONE.",
"",
NULL };
title_headers = prepend_title(headers);
}
char* items[] = { " No",
" No",
" No",
" No",
" No",
" No",
" No",
" Yes -- delete all user data", // [7]
" No",
" No",
" No",
NULL };
int chosen_item = get_menu_selection(title_headers, items, 1);
if (chosen_item != 7) {
return;
}
}
ui_print("\n-- Wiping data...\n");
device_wipe_data();
erase_root("DATA:");
#ifdef HAS_DATADATA
erase_root("DATADATA:");
#endif
erase_root("CACHE:");
erase_root("SDEXT:");
erase_root("SDCARD:/.android_secure");
ui_print("Data wipe complete.\n");
}
static void
prompt_and_wait()
{
char** headers = prepend_title(MENU_HEADERS);
for (;;) {
finish_recovery(NULL);
ui_reset_progress();
allow_display_toggle = 1;
int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0);
allow_display_toggle = 0;
// device-specific code may take some action here. It may
// return one of the core actions handled in the switch
// statement below.
chosen_item = device_perform_action(chosen_item);
switch (chosen_item) {
case ITEM_REBOOT:
return;
case ITEM_WIPE_DATA:
wipe_data(ui_text_visible());
if (!ui_text_visible()) return;
break;
case ITEM_WIPE_CACHE:
if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache"))
{
ui_print("\n-- Wiping cache...\n");
erase_root("CACHE:");
ui_print("Data wipe complete.\n");
ui_print("Cache wipe complete.\n");
if (!ui_text_visible()) return;
break;
}
break;
case ITEM_APPLY_SDCARD:
case ITEM_APPLY_SDCARD:
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
{
ui_print("\n-- Install from sdcard...\n");
#ifndef BOARD_HAS_NO_MISC_PARTITION
set_sdcard_update_bootloader_message();
#endif
int status = install_package(SDCARD_PACKAGE_FILE);
if (status != INSTALL_SUCCESS) {
ui_set_background(BACKGROUND_ICON_ERROR);
@ -366,23 +482,31 @@ prompt_and_wait()
} else if (!ui_text_visible()) {
return; // reboot if logs aren't visible
} else {
ui_print("Install from sdcard complete.\n");
#ifndef BOARD_HAS_NO_MISC_PARTITION
if (firmware_update_pending()) {
ui_print("\nReboot via menu to complete\n"
"installation.\n");
} else {
ui_print("\nInstall from sdcard complete.\n");
}
#else
ui_print("\nInstall from sdcard complete.\n");
#endif
}
break;
}
// if we didn't return from this function to reboot, show
// the menu again.
ui_start_menu(headers, items);
selected = 0;
chosen_item = -1;
finish_recovery(NULL);
ui_reset_progress();
// throw away keys pressed while the command was running,
// so user doesn't accidentally trigger menu items.
ui_clear_key_queue();
}
break;
case ITEM_INSTALL_ZIP:
show_install_update_menu();
break;
case ITEM_NANDROID:
show_nandroid_menu();
break;
case ITEM_PARTITION:
show_partition_menu();
break;
case ITEM_ADVANCED:
show_advanced_menu();
break;
}
}
}
@ -396,6 +520,32 @@ print_property(const char *key, const char *name, void *cookie)
int
main(int argc, char **argv)
{
if (strstr(argv[0], "recovery") == NULL)
{
if (strstr(argv[0], "flash_image") != NULL)
return flash_image_main(argc, argv);
if (strstr(argv[0], "dump_image") != NULL)
return dump_image_main(argc, argv);
if (strstr(argv[0], "erase_image") != NULL)
return erase_image_main(argc, argv);
if (strstr(argv[0], "mkyaffs2image") != NULL)
return mkyaffs2image_main(argc, argv);
if (strstr(argv[0], "unyaffs") != NULL)
return unyaffs_main(argc, argv);
if (strstr(argv[0], "amend"))
return amend_main(argc, argv);
if (strstr(argv[0], "nandroid"))
return nandroid_main(argc, argv);
if (strstr(argv[0], "reboot"))
return reboot_main(argc, argv);
if (strstr(argv[0], "setprop"))
return setprop_main(argc, argv);
return busybox_driver(argc, argv);
}
__system("/sbin/postrecoveryboot.sh");
create_fstab();
int is_user_initiated_recovery = 0;
time_t start = time(NULL);
// If these fail, there's not really anywhere to complain...
@ -404,6 +554,7 @@ main(int argc, char **argv)
fprintf(stderr, "Starting recovery on %s", ctime(&start));
ui_init();
ui_print(EXPAND(RECOVERY_VERSION)"\n");
get_args(&argc, &argv);
int previous_runs = 0;
@ -434,33 +585,57 @@ main(int argc, char **argv)
property_list(print_property, NULL);
fprintf(stderr, "\n");
#if TEST_AMEND
test_amend();
#endif
int status = INSTALL_SUCCESS;
RecoveryCommandContext ctx = { NULL };
if (register_update_commands(&ctx)) {
LOGE("Can't install update commands\n");
}
int status = INSTALL_SUCCESS;
if (update_package != NULL) {
if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR;
status = install_package(update_package);
if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
} else if (wipe_data || wipe_cache) {
if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR;
} else if (wipe_data) {
if (device_wipe_data()) status = INSTALL_ERROR;
if (erase_root("DATA:")) status = INSTALL_ERROR;
if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
} else if (wipe_cache) {
if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
} else {
LOGI("Checking for extendedcommand...\n");
status = INSTALL_ERROR; // No command specified
// we are starting up in user initiated recovery here
// let's set up some default options
signature_check_enabled = 0;
script_assert_enabled = 0;
is_user_initiated_recovery = 1;
ui_set_show_text(1);
if (extendedcommand_file_exists()) {
LOGI("Running extendedcommand...\n");
int ret;
if (0 == (ret = run_and_remove_extendedcommand())) {
status = INSTALL_SUCCESS;
ui_set_show_text(0);
}
else {
handle_failure(ret);
}
} else {
LOGI("Skipping execution of extendedcommand, file not found...\n");
}
}
if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR);
if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait();
#ifndef BOARD_HAS_NO_MISC_PARTITION
// If there is a radio image pending, reboot now to install it.
maybe_install_firmware_update(send_intent);
#endif
// Otherwise, get ready to boot the main system...
finish_recovery(send_intent);
@ -469,3 +644,7 @@ main(int argc, char **argv)
reboot(RB_AUTOBOOT);
return EXIT_SUCCESS;
}
int get_allow_toggle_display() {
return allow_display_toggle;
}

87
recovery_ui.h Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _RECOVERY_UI_H
#define _RECOVERY_UI_H
// Called in the input thread when a new key (key_code) is pressed.
// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
// keys are already pressed. Return true if the text display should
// be toggled.
extern int device_toggle_display(volatile char* key_pressed, int key_code);
// Called in the input thread when a new key (key_code) is pressed.
// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
// keys are already pressed. Return true if the device should reboot
// immediately.
extern int device_reboot_now(volatile char* key_pressed, int key_code);
// Called from the main thread when recovery is waiting for input and
// a key is pressed. key is the code of the key pressed; visible is
// true if the recovery menu is being shown. Implementations can call
// ui_key_pressed() to discover if other keys are being held down.
// Return one of the defined constants below in order to:
//
// - move the menu highlight (HIGHLIGHT_*)
// - invoke the highlighted item (SELECT_ITEM)
// - do nothing (NO_ACTION)
// - invoke a specific action (a menu position: any non-negative number)
extern int device_handle_key(int key, int visible);
// Perform a recovery action selected from the menu. 'which' will be
// the item number of the selected menu item, or a non-negative number
// returned from device_handle_key(). The menu will be hidden when
// this is called; implementations can call ui_print() to print
// information to the screen.
extern int device_perform_action(int which);
// Called when we do a wipe data/factory reset operation (either via a
// reboot from the main system with the --wipe_data flag, or when the
// user boots into recovery manually and selects the option from the
// menu.) Can perform whatever device-specific wiping actions are
// needed. Return 0 on success. The userdata and cache partitions
// are erased after this returns (whether it returns success or not).
int device_wipe_data();
#define NO_ACTION -1
#define HIGHLIGHT_UP -2
#define HIGHLIGHT_DOWN -3
#define SELECT_ITEM -4
#define GO_BACK -5
#define ITEM_REBOOT 0
#define ITEM_APPLY_SDCARD 1
#define ITEM_WIPE_DATA 2
#define ITEM_WIPE_CACHE 3
#define ITEM_INSTALL_ZIP 4
#define ITEM_NANDROID 5
#define ITEM_PARTITION 6
#define ITEM_ADVANCED 7
// Header text to display above the main menu.
extern char* MENU_HEADERS[];
// Text of menu items.
extern char* MENU_ITEMS[];
int
get_menu_selection(char** headers, char** items, int menu_only);
void
set_sdcard_update_bootloader_message();
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

BIN
res/images/icon_error.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

BIN
res/images/icon_installing.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

66
roots.c
View File

@ -21,20 +21,15 @@
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include "mtdutils/mtdutils.h"
#include "mtdutils/mounts.h"
#include "minzip/Zip.h"
#include "roots.h"
#include "common.h"
typedef struct {
const char *name;
const char *device;
const char *device2; // If the first one doesn't work (may be NULL)
const char *partition_name;
const char *mount_point;
const char *filesystem;
} RootInfo;
#include "extendedcommands.h"
/* Canonical pointers.
xxx may just want to use enums
@ -44,21 +39,26 @@ static const char g_raw[] = "@\0g_raw";
static const char g_package_file[] = "@\0g_package_file";
static RootInfo g_roots[] = {
{ "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw },
{ "CACHE:", g_mtd_device, NULL, "cache", "/cache", "yaffs2" },
{ "DATA:", g_mtd_device, NULL, "userdata", "/data", "yaffs2" },
{ "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw },
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file },
{ "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw },
{ "SDCARD:", "/dev/block/mmcblk0p1", "/dev/block/mmcblk0", NULL, "/sdcard", "vfat" },
{ "SYSTEM:", g_mtd_device, NULL, "system", "/system", "yaffs2" },
{ "TMP:", NULL, NULL, NULL, "/tmp", NULL },
{ "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw, NULL },
{ "CACHE:", CACHE_DEVICE, NULL, "cache", "/cache", CACHE_FILESYSTEM, CACHE_FILESYSTEM_OPTIONS },
{ "DATA:", DATA_DEVICE, NULL, "userdata", "/data", DATA_FILESYSTEM, DATA_FILESYSTEM_OPTIONS },
#ifdef HAS_DATADATA
{ "DATADATA:", DATADATA_DEVICE, NULL, "datadata", "/datadata", DATADATA_FILESYSTEM, DATADATA_FILESYSTEM_OPTIONS },
#endif
{ "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw, NULL },
{ "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file, NULL },
{ "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw, NULL },
{ "SDCARD:", SDCARD_DEVICE_PRIMARY, SDCARD_DEVICE_SECONDARY, NULL, "/sdcard", "vfat", NULL },
{ "SDEXT:", SDEXT_DEVICE, NULL, NULL, "/sd-ext", SDEXT_FILESYSTEM, NULL },
{ "SYSTEM:", SYSTEM_DEVICE, NULL, "system", "/system", SYSTEM_FILESYSTEM, SYSTEM_FILESYSTEM_OPTIONS },
{ "MBM:", g_mtd_device, NULL, "mbm", NULL, g_raw, NULL },
{ "TMP:", NULL, NULL, NULL, "/tmp", NULL, NULL },
};
#define NUM_ROOTS (sizeof(g_roots) / sizeof(g_roots[0]))
// TODO: for SDCARD:, try /dev/block/mmcblk0 if mmcblk0p1 fails
static const RootInfo *
const RootInfo *
get_root_info_for_path(const char *root_path)
{
const char *c;
@ -207,6 +207,19 @@ is_root_path_mounted(const char *root_path)
return internal_root_mounted(info) >= 0;
}
static int mount_internal(const char* device, const char* mount_point, const char* filesystem, const char* filesystem_options)
{
if (strcmp(filesystem, "auto") != 0 && filesystem_options == NULL) {
return mount(device, mount_point, filesystem, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
}
else {
char mount_cmd[PATH_MAX];
const char* options = filesystem_options == NULL ? "noatime,nodiratime,nodev" : filesystem_options;
sprintf(mount_cmd, "mount -t %s -o%s %s %s", filesystem, options, device, mount_point);
return __system(mount_cmd);
}
}
int
ensure_root_path_mounted(const char *root_path)
{
@ -247,8 +260,7 @@ ensure_root_path_mounted(const char *root_path)
}
mkdir(info->mount_point, 0755); // in case it doesn't already exist
if (mount(info->device, info->mount_point, info->filesystem,
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
if (mount_internal(info->device, info->mount_point, info->filesystem, info->filesystem_options)) {
if (info->device2 == NULL) {
LOGE("Can't mount %s\n(%s)\n", info->device, strerror(errno));
return -1;
@ -300,7 +312,12 @@ get_root_mtd_partition(const char *root_path)
if (info == NULL || info->device != g_mtd_device ||
info->partition_name == NULL)
{
#ifdef BOARD_HAS_MTD_CACHE
if (strcmp(root_path, "CACHE:") != 0)
return NULL;
#else
return NULL;
#endif
}
mtd_scan_partitions();
return mtd_find_partition_by_name(info->partition_name);
@ -316,17 +333,19 @@ format_root_device(const char *root)
while (*c != '\0' && *c != ':') {
c++;
}
/*
if (c[0] != ':' || c[1] != '\0') {
LOGW("format_root_device: bad root name \"%s\"\n", root);
return -1;
}
*/
const RootInfo *info = get_root_info_for_path(root);
if (info == NULL || info->device == NULL) {
LOGW("format_root_device: can't resolve \"%s\"\n", root);
return -1;
}
if (info->mount_point != NULL) {
if (info->mount_point != NULL && info->device == g_mtd_device) {
/* Don't try to format a mounted device.
*/
int ret = ensure_root_path_unmounted(root);
@ -364,7 +383,6 @@ format_root_device(const char *root)
}
}
}
//TODO: handle other device types (sdcard, etc.)
LOGW("format_root_device: can't handle non-mtd device \"%s\"\n", root);
return -1;
return format_non_mtd_device(root);
}

75
roots.h
View File

@ -20,6 +20,71 @@
#include "minzip/Zip.h"
#include "mtdutils/mtdutils.h"
#ifndef SDCARD_DEVICE_PRIMARY
#define SDCARD_DEVICE_PRIMARY "/dev/block/mmcblk0p1"
#endif
#ifndef SDCARD_DEVICE_SECONDARY
#define SDCARD_DEVICE_SECONDARY "/dev/block/mmcblk0"
#endif
#ifndef SDEXT_DEVICE
#define SDEXT_DEVICE "/dev/block/mmcblk0p2"
#endif
#ifndef SDEXT_FILESYSTEM
#define SDEXT_FILESYSTEM "auto"
#endif
#ifndef DATA_DEVICE
#define DATA_DEVICE g_mtd_device
#endif
#ifndef DATA_FILESYSTEM
#define DATA_FILESYSTEM "yaffs2"
#endif
#ifndef DATADATA_DEVICE
#define DATADATA_DEVICE g_mtd_device
#endif
#ifndef DATADATA_FILESYSTEM
#define DATADATA_FILESYSTEM "yaffs2"
#endif
#ifndef CACHE_DEVICE
#define CACHE_DEVICE g_mtd_device
#endif
#ifndef CACHE_FILESYSTEM
#define CACHE_FILESYSTEM "yaffs2"
#endif
#ifndef SYSTEM_DEVICE
#define SYSTEM_DEVICE g_mtd_device
#endif
#ifndef SYSTEM_FILESYSTEM
#define SYSTEM_FILESYSTEM "yaffs2"
#endif
#ifndef DATA_FILESYSTEM_OPTIONS
#define DATA_FILESYSTEM_OPTIONS NULL
#endif
#ifndef CACHE_FILESYSTEM_OPTIONS
#define CACHE_FILESYSTEM_OPTIONS NULL
#endif
#ifndef DATADATA_FILESYSTEM_OPTIONS
#define DATADATA_FILESYSTEM_OPTIONS NULL
#endif
#ifndef SYSTEM_FILESYSTEM_OPTIONS
#define SYSTEM_FILESYSTEM_OPTIONS NULL
#endif
/* Any of the "root_path" arguments can be paths with relative
* components, like "SYSTEM:a/b/c".
*/
@ -60,4 +125,14 @@ const MtdPartition *get_root_mtd_partition(const char *root_path);
*/
int format_root_device(const char *root);
typedef struct {
const char *name;
const char *device;
const char *device2; // If the first one doesn't work (may be NULL)
const char *partition_name;
const char *mount_point;
const char *filesystem;
const char *filesystem_options;
} RootInfo;
#endif // RECOVERY_ROOTS_H_

18
setprop.c Normal file
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;
}

View File

@ -14,11 +14,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := make-update-script
LOCAL_SRC_FILES := make-update-script.c
include $(BUILD_HOST_EXECUTABLE)
ifneq ($(TARGET_SIMULATOR),true)
include $(CLEAR_VARS)

View File

@ -1,233 +0,0 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "private/android_filesystem_config.h"
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
/*
* Recursively walk the directory tree at <sysdir>/<subdir>, writing
* script commands to set permissions and create symlinks.
* Assume the contents already have the specified default permissions,
* so only output commands if they need to be changed from the defaults.
*
* Note that permissions are set by fs_config(), which uses a lookup table of
* Android permissions. They are not drawn from the build host filesystem.
*/
static void walk_files(
const char *sysdir, const char *subdir,
unsigned default_uid, unsigned default_gid,
unsigned default_dir_mode, unsigned default_file_mode) {
const char *sep = strcmp(subdir, "") ? "/" : "";
char fn[PATH_MAX];
unsigned dir_uid = 0, dir_gid = 0, dir_mode = 0;
snprintf(fn, PATH_MAX, "system%s%s", sep, subdir);
fs_config(fn, 1, &dir_uid, &dir_gid, &dir_mode);
snprintf(fn, PATH_MAX, "%s%s%s", sysdir, sep, subdir);
DIR *dir = opendir(fn);
if (dir == NULL) {
perror(fn);
exit(1);
}
/*
* We can use "set_perm" and "set_perm_recursive" to set file permissions
* (owner, group, and file mode) for individual files and entire subtrees.
* We want to use set_perm_recursive efficiently to avoid setting the
* permissions of every single file in the system image individually.
*
* What we do is recursively set our entire subtree to the permissions
* used by the first file we encounter, and then use "set_perm" to adjust
* the permissions of subsequent files which don't match the first one.
* This is bad if the first file is an outlier, but it generally works.
* Subdirectories can do the same thing recursively if they're different.
*/
int is_first = 1;
const struct dirent *e;
while ((e = readdir(dir))) {
// Skip over "." and ".." entries
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
if (e->d_type == DT_LNK) { // Symlink
// Symlinks don't really have permissions, so this is orthogonal.
snprintf(fn, PATH_MAX, "%s/%s%s%s", sysdir, subdir, sep, e->d_name);
int len = readlink(fn, fn, PATH_MAX - 1);
if (len <= 0) {
perror(fn);
exit(1);
}
fn[len] = '\0';
printf("symlink %s SYSTEM:%s%s%s\n", fn, subdir, sep, e->d_name);
} else if (e->d_type == DT_DIR) { // Subdirectory
// Use the parent directory as the model for default permissions.
// We haven't seen a file, so just make up some file defaults.
if (is_first && (
dir_mode != default_dir_mode ||
dir_uid != default_uid || dir_gid != default_gid)) {
default_uid = dir_uid;
default_gid = dir_gid;
default_dir_mode = dir_mode;
default_file_mode = dir_mode & default_file_mode & 0666;
printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n",
default_uid, default_gid,
default_dir_mode, default_file_mode,
subdir);
}
is_first = 0;
// Recursively handle the subdirectory.
// Note, the recursive call handles the directory's own permissions.
snprintf(fn, PATH_MAX, "%s%s%s", subdir, sep, e->d_name);
walk_files(sysdir, fn,
default_uid, default_gid,
default_dir_mode, default_file_mode);
} else { // Ordinary file
// Get the file's desired permissions.
unsigned file_uid = 0, file_gid = 0, file_mode = 0;
snprintf(fn, PATH_MAX, "system/%s%s%s", subdir, sep, e->d_name);
fs_config(fn, 0, &file_uid, &file_gid, &file_mode);
// If this is the first file, its mode gets to become the default.
if (is_first && (
dir_mode != default_dir_mode ||
file_mode != default_file_mode ||
dir_uid != default_uid || file_uid != default_uid ||
dir_gid != default_gid || file_gid != default_gid)) {
default_uid = dir_uid;
default_gid = dir_gid;
default_dir_mode = dir_mode;
default_file_mode = file_mode;
printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n",
default_uid, default_gid,
default_dir_mode, default_file_mode,
subdir);
}
is_first = 0;
// Otherwise, override this file if it doesn't match the defaults.
if (file_mode != default_file_mode ||
file_uid != default_uid || file_gid != default_gid) {
printf("set_perm %d %d 0%o SYSTEM:%s%s%s\n",
file_uid, file_gid, file_mode,
subdir, sep, e->d_name);
}
}
}
// Set the directory's permissions directly, if they never got set.
if (dir_mode != default_dir_mode ||
dir_uid != default_uid || dir_gid != default_gid) {
printf("set_perm %d %d 0%o SYSTEM:%s\n",
dir_uid, dir_gid, dir_mode, subdir);
}
closedir(dir);
}
/*
* Generate the update script (in "Amend", see commands/recovery/commands.c)
* for the complete-reinstall OTA update packages the build system makes.
*
* The generated script makes a variety of sanity checks about the device,
* erases and reinstalls system files, and sets file permissions appropriately.
*/
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "usage: %s systemdir android-info.txt >update-script\n",
argv[0]);
return 2;
}
// ensure basic recovery script language compatibility
printf("assert compatible_with(\"0.2\") == \"true\"\n");
// if known, make sure the device name is correct
const char *device = getenv("TARGET_DEVICE");
if (device != NULL) {
printf("assert getprop(\"ro.product.device\") == \"%s\" || "
"getprop(\"ro.build.product\") == \"%s\"\n", device, device);
}
// scan android-info.txt to enforce compatibility with the target system
FILE *fp = fopen(argv[2], "r");
if (fp == NULL) {
perror(argv[2]);
return 1;
}
// The lines we're looking for look like:
// version-bootloader=x.yy.zzzz|x.yy.zzzz|...
// or:
// require version-bootloader=x.yy.zzzz|x.yy.zzzz|...
char line[256];
while (fgets(line, sizeof(line), fp)) {
const char *name = strtok(line, "="), *value = strtok(NULL, "|\n");
if (value != NULL &&
(!strcmp(name, "version-bootloader") ||
!strcmp(name, "require version-bootloader"))) {
printf("assert getprop(\"ro.bootloader\") == \"%s\"", value);
while ((value = strtok(NULL, "|\n")) != NULL) {
printf(" || getprop(\"ro.bootloader\") == \"%s\"", value);
}
printf("\n");
}
// We also used to check version-baseband, but we update radio.img
// ourselves, so there's no need.
}
// erase the boot sector first, so if the update gets interrupted,
// the system will reboot into the recovery partition and start over.
printf("format BOOT:\n");
// write the radio image (actually just loads it into RAM for now)
printf("show_progress 0.1 0\n");
printf("write_radio_image PACKAGE:radio.img\n");
// erase and reinstall the system image
printf("show_progress 0.5 0\n");
printf("format SYSTEM:\n");
printf("copy_dir PACKAGE:system SYSTEM:\n");
// walk the files in the system image, set their permissions, etc.
// use -1 for default values to force permissions to be set explicitly.
walk_files(argv[1], "", -1, -1, -1, -1);
// as the last step, write the boot sector.
printf("show_progress 0.2 0\n");
printf("write_raw_image PACKAGE:boot.img BOOT:\n");
// after the end of the script, the radio will be written to cache
// leave some space in the progress bar for this operation
printf("show_progress 0.2 10\n");
return 0;
}

140
ui.c
View File

@ -27,9 +27,19 @@
#include "common.h"
#include "minui/minui.h"
#include "recovery_ui.h"
#ifdef KEY_POWER_IS_SELECT_ITEM
static int gShowBackButton = 1;
#else
static int gShowBackButton = 0;
#endif
#define MAX_COLS 64
#define MAX_ROWS 32
#define MAX_ROWS 32
#define MENU_MAX_COLS 64
#define MENU_MAX_ROWS 250
#define CHAR_WIDTH 10
#define CHAR_HEIGHT 18
@ -44,9 +54,9 @@ static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
static gr_surface gProgressBarEmpty[NUM_SIDES];
static gr_surface gProgressBarFill[NUM_SIDES];
static int ui_has_initialized = 0;
static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
{ &gBackgroundIcon[BACKGROUND_ICON_UNPACKING], "icon_unpacking" },
{ &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
{ &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" },
{ &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING],
@ -89,9 +99,10 @@ static int text_cols = 0, text_rows = 0;
static int text_col = 0, text_row = 0, text_top = 0;
static int show_text = 0;
static char menu[MAX_ROWS][MAX_COLS];
static char menu[MENU_MAX_ROWS][MENU_MAX_COLS];
static int show_menu = 0;
static int menu_top = 0, menu_items = 0, menu_sel = 0;
static int menu_show_start = 0; // this is line which menu display is starting at
// Key event input queue
static pthread_mutex_t key_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
@ -164,10 +175,15 @@ static void draw_text_line(int row, const char* t) {
}
}
#define MENU_TEXT_COLOR 7, 133, 74, 255
#define NORMAL_TEXT_COLOR 200, 200, 200, 255
#define HEADER_TEXT_COLOR NORMAL_TEXT_COLOR
// Redraw everything on the screen. Does not flip pages.
// Should only be called with gUpdateMutex locked.
static void draw_screen_locked(void)
{
if (!ui_has_initialized) return;
draw_background_locked(gCurrentIcon);
draw_progress_locked();
@ -176,29 +192,43 @@ static void draw_screen_locked(void)
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
int i = 0;
int j = 0;
int row = 0; // current row that we are drawing on
if (show_menu) {
gr_color(64, 96, 255, 255);
gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT,
gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1);
gr_color(MENU_TEXT_COLOR);
gr_fill(0, (menu_top + menu_sel - menu_show_start) * CHAR_HEIGHT,
gr_fb_width(), (menu_top + menu_sel - menu_show_start + 1)*CHAR_HEIGHT+1);
for (; i < menu_top + menu_items; ++i) {
gr_color(HEADER_TEXT_COLOR);
for (i = 0; i < menu_top; ++i) {
draw_text_line(i, menu[i]);
row++;
}
if (menu_items - menu_show_start + menu_top >= MAX_ROWS)
j = MAX_ROWS - menu_top;
else
j = menu_items - menu_show_start;
gr_color(MENU_TEXT_COLOR);
for (i = menu_show_start + menu_top; i < (menu_show_start + menu_top + j); ++i) {
if (i == menu_top + menu_sel) {
gr_color(255, 255, 255, 255);
draw_text_line(i, menu[i]);
gr_color(64, 96, 255, 255);
draw_text_line(i - menu_show_start , menu[i]);
gr_color(MENU_TEXT_COLOR);
} else {
draw_text_line(i, menu[i]);
gr_color(MENU_TEXT_COLOR);
draw_text_line(i - menu_show_start, menu[i]);
}
row++;
}
gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
++i;
gr_fill(0, row*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
gr_fb_width(), row*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
}
gr_color(255, 255, 0, 255);
for (; i < text_rows; ++i) {
draw_text_line(i, text[(i+text_top) % text_rows]);
gr_color(NORMAL_TEXT_COLOR);
for (; row < text_rows; ++row) {
draw_text_line(row, text[(row+text_top) % text_rows]);
}
}
}
@ -207,6 +237,7 @@ static void draw_screen_locked(void)
// Should only be called with gUpdateMutex locked.
static void update_screen_locked(void)
{
if (!ui_has_initialized) return;
draw_screen_locked();
gr_flip();
}
@ -215,6 +246,7 @@ static void update_screen_locked(void)
// Should only be called with gUpdateMutex locked.
static void update_progress_locked(void)
{
if (!ui_has_initialized) return;
if (show_text || !gPagesIdentical) {
draw_screen_locked(); // Must redraw the whole screen
gPagesIdentical = 1;
@ -308,20 +340,14 @@ static void *input_thread(void *cookie)
}
pthread_mutex_unlock(&key_queue_mutex);
// Alt+L or Home+End: toggle log display
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
if ((alt && ev.code == KEY_L && ev.value > 0) ||
(key_pressed[KEY_HOME] && ev.code == KEY_END && ev.value > 0)) {
if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
pthread_mutex_lock(&gUpdateMutex);
show_text = !show_text;
update_screen_locked();
pthread_mutex_unlock(&gUpdateMutex);
}
// Green+Menu+Red: reboot immediately
if (ev.code == KEY_DREAM_RED &&
key_pressed[KEY_DREAM_MENU] &&
key_pressed[KEY_DREAM_GREEN]) {
if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
reboot(RB_AUTOBOOT);
}
}
@ -330,6 +356,7 @@ static void *input_thread(void *cookie)
void ui_init(void)
{
ui_has_initialized = 1;
gr_init();
ev_init();
@ -345,7 +372,11 @@ void ui_init(void)
for (i = 0; BITMAPS[i].name != NULL; ++i) {
int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
if (result < 0) {
LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
if (result == -2) {
LOGI("Bitmap %s missing header\n", BITMAPS[i].name);
} else {
LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
}
*BITMAPS[i].surface = NULL;
}
}
@ -460,7 +491,17 @@ void ui_print(const char *fmt, ...)
pthread_mutex_unlock(&gUpdateMutex);
}
void ui_start_menu(char** headers, char** items) {
void ui_reset_text_col()
{
pthread_mutex_lock(&gUpdateMutex);
text_col = 0;
pthread_mutex_unlock(&gUpdateMutex);
}
#define MENU_ITEM_HEADER " - "
#define MENU_ITEM_HEADER_LENGTH strlen(MENU_ITEM_HEADER)
int ui_start_menu(char** headers, char** items) {
int i;
pthread_mutex_lock(&gUpdateMutex);
if (text_rows > 0 && text_cols > 0) {
@ -470,17 +511,28 @@ void ui_start_menu(char** headers, char** items) {
menu[i][text_cols-1] = '\0';
}
menu_top = i;
for (; i < text_rows; ++i) {
for (; i < MENU_MAX_ROWS; ++i) {
if (items[i-menu_top] == NULL) break;
strncpy(menu[i], items[i-menu_top], text_cols-1);
strcpy(menu[i], MENU_ITEM_HEADER);
strncpy(menu[i] + MENU_ITEM_HEADER_LENGTH, items[i-menu_top], text_cols-1 - MENU_ITEM_HEADER_LENGTH);
menu[i][text_cols-1] = '\0';
}
if (gShowBackButton) {
strcpy(menu[i], " - +++++Go Back+++++");
++i;
}
menu_items = i - menu_top;
show_menu = 1;
menu_sel = 0;
menu_sel = menu_show_start = 0;
update_screen_locked();
}
pthread_mutex_unlock(&gUpdateMutex);
if (gShowBackButton) {
return menu_items - 1;
}
return menu_items;
}
int ui_menu_select(int sel) {
@ -489,9 +541,21 @@ int ui_menu_select(int sel) {
if (show_menu > 0) {
old_sel = menu_sel;
menu_sel = sel;
if (menu_sel < 0) menu_sel = 0;
if (menu_sel >= menu_items) menu_sel = menu_items-1;
if (menu_sel < 0) menu_sel = menu_items + menu_sel;
if (menu_sel >= menu_items) menu_sel = menu_sel - menu_items;
if (menu_sel < menu_show_start && menu_show_start > 0) {
menu_show_start = menu_sel;
}
if (menu_sel - menu_show_start + menu_top >= text_rows) {
menu_show_start = menu_sel + menu_top - text_rows + 1;
}
sel = menu_sel;
if (menu_sel != old_sel) update_screen_locked();
}
pthread_mutex_unlock(&gUpdateMutex);
@ -540,3 +604,15 @@ void ui_clear_key_queue() {
key_queue_len = 0;
pthread_mutex_unlock(&key_queue_mutex);
}
void ui_set_show_text(int value) {
show_text = value;
}
void ui_set_showing_back_button(int showBackButton) {
gShowBackButton = showBackButton;
}
int ui_get_showing_back_button() {
return gShowBackButton;
}

72
updater/Android.mk Normal file
View File

@ -0,0 +1,72 @@
# Copyright 2009 The Android Open Source Project
LOCAL_PATH := $(call my-dir)
updater_src_files := \
install.c \
updater.c
#
# Build a statically-linked binary to include in OTA packages
#
include $(CLEAR_VARS)
# Build only in eng, so we don't end up with a copy of this in /system
# on user builds. (TODO: find a better way to build device binaries
# needed only for OTA packages.)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES := $(updater_src_files)
LOCAL_STATIC_LIBRARIES := $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
# named "Register_<libname>()". Here we emit a little C function that
# gets #included by updater.c. It calls all those registration
# functions.
# Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS.
# These libs are also linked in with updater, but we don't try to call
# any sort of registration function for these. Use this variable for
# any subsidiary static libraries required for your registered
# extension libs.
inc := $(call intermediates-dir-for,PACKAGING,updater_extensions)/register.inc
# During the first pass of reading the makefiles, we dump the list of
# extension libs to a temp file, then copy that to the ".list" file if
# it is different than the existing .list (if any). The register.inc
# file then uses the .list as a prerequisite, so it is only rebuilt
# (and updater.o recompiled) when the list of extension libs changes.
junk := $(shell mkdir -p $(dir $(inc));\
echo $(TARGET_RECOVERY_UPDATER_LIBS) > $(inc).temp;\
diff -q $(inc).temp $(inc).list || cp -f $(inc).temp $(inc).list)
$(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS)
$(inc) : $(inc).list
$(hide) mkdir -p $(dir $@)
$(hide) echo "" > $@
$(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@)
$(hide) echo "void RegisterDeviceExtensions() {" >> $@
$(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@)
$(hide) echo "}" >> $@
$(call intermediates-dir-for,EXECUTABLES,updater)/updater.o : $(inc)
LOCAL_C_INCLUDES += $(dir $(inc))
LOCAL_MODULE := updater
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
file := $(PRODUCT_OUT)/utilities/update-binary
ALL_PREBUILT += $(file)
$(file) : $(TARGET_OUT)/bin/updater | $(ACP)
$(transform-prebuilt-to-target)

855
updater/install.c Normal file
View File

@ -0,0 +1,855 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "cutils/misc.h"
#include "cutils/properties.h"
#include "edify/expr.h"
#include "minzip/DirUtil.h"
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "updater.h"
// mount(type, location, mount_point)
//
// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
// type="vfat" location="/dev/block/<whatever>" to mount a device
char* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 3) {
return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
}
char* type;
char* location;
char* mount_point;
if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) {
return NULL;
}
if (strlen(type) == 0) {
ErrorAbort(state, "type argument to %s() can't be empty", name);
goto done;
}
if (strlen(location) == 0) {
ErrorAbort(state, "location argument to %s() can't be empty", name);
goto done;
}
if (strlen(mount_point) == 0) {
ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
goto done;
}
mkdir(mount_point, 0755);
if (strcmp(type, "MTD") == 0) {
mtd_scan_partitions();
const MtdPartition* mtd;
mtd = mtd_find_partition_by_name(location);
if (mtd == NULL) {
fprintf(stderr, "%s: no mtd partition named \"%s\"",
name, location);
result = strdup("");
goto done;
}
if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) {
fprintf(stderr, "mtd mount of %s failed: %s\n",
location, strerror(errno));
result = strdup("");
goto done;
}
result = mount_point;
} else {
if (mount(location, mount_point, type,
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
fprintf(stderr, "%s: failed to mount %s at %s: %s\n",
name, location, mount_point, strerror(errno));
result = strdup("");
} else {
result = mount_point;
}
}
done:
free(type);
free(location);
if (result != mount_point) free(mount_point);
return result;
}
// is_mounted(mount_point)
char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
char* mount_point;
if (ReadArgs(state, argv, 1, &mount_point) < 0) {
return NULL;
}
if (strlen(mount_point) == 0) {
ErrorAbort(state, "mount_point argument to unmount() can't be empty");
goto done;
}
scan_mounted_volumes();
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
if (vol == NULL) {
result = strdup("");
} else {
result = mount_point;
}
done:
if (result != mount_point) free(mount_point);
return result;
}
char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
char* mount_point;
if (ReadArgs(state, argv, 1, &mount_point) < 0) {
return NULL;
}
if (strlen(mount_point) == 0) {
ErrorAbort(state, "mount_point argument to unmount() can't be empty");
goto done;
}
scan_mounted_volumes();
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
if (vol == NULL) {
fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point);
result = strdup("");
} else {
unmount_mounted_volume(vol);
result = mount_point;
}
done:
if (result != mount_point) free(mount_point);
return result;
}
// format(type, location)
//
// type="MTD" location=partition
char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
}
char* type;
char* location;
if (ReadArgs(state, argv, 2, &type, &location) < 0) {
return NULL;
}
if (strlen(type) == 0) {
ErrorAbort(state, "type argument to %s() can't be empty", name);
goto done;
}
if (strlen(location) == 0) {
ErrorAbort(state, "location argument to %s() can't be empty", name);
goto done;
}
if (strcmp(type, "MTD") == 0) {
mtd_scan_partitions();
const MtdPartition* mtd = mtd_find_partition_by_name(location);
if (mtd == NULL) {
fprintf(stderr, "%s: no mtd partition named \"%s\"",
name, location);
result = strdup("");
goto done;
}
MtdWriteContext* ctx = mtd_write_partition(mtd);
if (ctx == NULL) {
fprintf(stderr, "%s: can't write \"%s\"", name, location);
result = strdup("");
goto done;
}
if (mtd_erase_blocks(ctx, -1) == -1) {
mtd_write_close(ctx);
fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
result = strdup("");
goto done;
}
if (mtd_write_close(ctx) != 0) {
fprintf(stderr, "%s: failed to close \"%s\"", name, location);
result = strdup("");
goto done;
}
result = location;
} else {
fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
}
done:
free(type);
if (result != location) free(location);
return result;
}
char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
char** paths = malloc(argc * sizeof(char*));
int i;
for (i = 0; i < argc; ++i) {
paths[i] = Evaluate(state, argv[i]);
if (paths[i] == NULL) {
int j;
for (j = 0; j < i; ++i) {
free(paths[j]);
}
free(paths);
return NULL;
}
}
bool recursive = (strcmp(name, "delete_recursive") == 0);
int success = 0;
for (i = 0; i < argc; ++i) {
if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0)
++success;
free(paths[i]);
}
free(paths);
char buffer[10];
sprintf(buffer, "%d", success);
return strdup(buffer);
}
char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
}
char* frac_str;
char* sec_str;
if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
return NULL;
}
double frac = strtod(frac_str, NULL);
int sec = strtol(sec_str, NULL, 10);
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
free(sec_str);
return frac_str;
}
char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
char* frac_str;
if (ReadArgs(state, argv, 1, &frac_str) < 0) {
return NULL;
}
double frac = strtod(frac_str, NULL);
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
return frac_str;
}
// package_extract_dir(package_path, destination_path)
char* PackageExtractDirFn(const char* name, State* state,
int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
}
char* zip_path;
char* dest_path;
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
// To create a consistent system image, never use the clock for timestamps.
struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
bool success = mzExtractRecursive(za, zip_path, dest_path,
MZ_EXTRACT_FILES_ONLY, &timestamp,
NULL, NULL);
free(zip_path);
free(dest_path);
return strdup(success ? "t" : "");
}
// package_extract_file(package_path, destination_path)
char* PackageExtractFileFn(const char* name, State* state,
int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
}
char* zip_path;
char* dest_path;
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
bool success = false;
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
fprintf(stderr, "%s: no %s in package\n", name, zip_path);
goto done;
}
FILE* f = fopen(dest_path, "wb");
if (f == NULL) {
fprintf(stderr, "%s: can't open %s for write: %s\n",
name, dest_path, strerror(errno));
goto done;
}
success = mzExtractZipEntryToFile(za, entry, fileno(f));
fclose(f);
done:
free(zip_path);
free(dest_path);
return strdup(success ? "t" : "");
}
// symlink target src1 src2 ...
// unlinks any previously existing src1, src2, etc before creating symlinks.
char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc == 0) {
return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
}
char* target;
target = Evaluate(state, argv[0]);
if (target == NULL) return NULL;
char** srcs = ReadVarArgs(state, argc-1, argv+1);
if (srcs == NULL) {
free(target);
return NULL;
}
int i;
for (i = 0; i < argc-1; ++i) {
if (unlink(srcs[i]) < 0) {
if (errno != ENOENT) {
fprintf(stderr, "%s: failed to remove %s: %s\n",
name, srcs[i], strerror(errno));
}
}
if (symlink(target, srcs[i]) < 0) {
fprintf(stderr, "%s: failed to symlink %s to %s: %s\n",
name, srcs[i], target, strerror(errno));
}
free(srcs[i]);
}
free(srcs);
return strdup("");
}
char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
bool recursive = (strcmp(name, "set_perm_recursive") == 0);
int min_args = 4 + (recursive ? 1 : 0);
if (argc < min_args) {
return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
}
char** args = ReadVarArgs(state, argc, argv);
if (args == NULL) return NULL;
char* end;
int i;
int uid = strtoul(args[0], &end, 0);
if (*end != '\0' || args[0][0] == 0) {
ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
goto done;
}
int gid = strtoul(args[1], &end, 0);
if (*end != '\0' || args[1][0] == 0) {
ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
goto done;
}
if (recursive) {
int dir_mode = strtoul(args[2], &end, 0);
if (*end != '\0' || args[2][0] == 0) {
ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
goto done;
}
int file_mode = strtoul(args[3], &end, 0);
if (*end != '\0' || args[3][0] == 0) {
ErrorAbort(state, "%s: \"%s\" not a valid filemode",
name, args[3]);
goto done;
}
for (i = 4; i < argc; ++i) {
dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
}
} else {
int mode = strtoul(args[2], &end, 0);
if (*end != '\0' || args[2][0] == 0) {
ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
goto done;
}
for (i = 3; i < argc; ++i) {
if (chown(args[i], uid, gid) < 0) {
fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n",
name, args[i], uid, gid, strerror(errno));
}
if (chmod(args[i], mode) < 0) {
fprintf(stderr, "%s: chmod of %s to %o failed: %s\n",
name, args[i], mode, strerror(errno));
}
}
}
result = strdup("");
done:
for (i = 0; i < argc; ++i) {
free(args[i]);
}
free(args);
return result;
}
char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
char* key;
key = Evaluate(state, argv[0]);
if (key == NULL) return NULL;
char value[PROPERTY_VALUE_MAX];
property_get(key, value, "");
free(key);
return strdup(value);
}
// file_getprop(file, key)
//
// interprets 'file' as a getprop-style file (key=value pairs, one
// per line, # comment lines and blank lines okay), and returns the value
// for 'key' (or "" if it isn't defined).
char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* buffer = NULL;
char* filename;
char* key;
if (ReadArgs(state, argv, 2, &filename, &key) < 0) {
return NULL;
}
struct stat st;
if (stat(filename, &st) < 0) {
ErrorAbort(state, "%s: failed to stat \"%s\": %s",
name, filename, strerror(errno));
goto done;
}
#define MAX_FILE_GETPROP_SIZE 65536
if (st.st_size > MAX_FILE_GETPROP_SIZE) {
ErrorAbort(state, "%s too large for %s (max %d)",
filename, name, MAX_FILE_GETPROP_SIZE);
goto done;
}
buffer = malloc(st.st_size+1);
if (buffer == NULL) {
ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1);
goto done;
}
FILE* f = fopen(filename, "rb");
if (f == NULL) {
ErrorAbort(state, "%s: failed to open %s: %s",
name, filename, strerror(errno));
goto done;
}
if (fread(buffer, 1, st.st_size, f) != st.st_size) {
ErrorAbort(state, "%s: failed to read %d bytes from %s",
name, st.st_size+1, filename);
fclose(f);
goto done;
}
buffer[st.st_size] = '\0';
fclose(f);
char* line = strtok(buffer, "\n");
do {
// skip whitespace at start of line
while (*line && isspace(*line)) ++line;
// comment or blank line: skip to next line
if (*line == '\0' || *line == '#') continue;
char* equal = strchr(line, '=');
if (equal == NULL) {
ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
name, line, filename);
goto done;
}
// trim whitespace between key and '='
char* key_end = equal-1;
while (key_end > line && isspace(*key_end)) --key_end;
key_end[1] = '\0';
// not the key we're looking for
if (strcmp(key, line) != 0) continue;
// skip whitespace after the '=' to the start of the value
char* val_start = equal+1;
while(*val_start && isspace(*val_start)) ++val_start;
// trim trailing whitespace
char* val_end = val_start + strlen(val_start)-1;
while (val_end > val_start && isspace(*val_end)) --val_end;
val_end[1] = '\0';
result = strdup(val_start);
break;
} while ((line = strtok(NULL, "\n")));
if (result == NULL) result = strdup("");
done:
free(filename);
free(key);
free(buffer);
return result;
}
static bool write_raw_image_cb(const unsigned char* data,
int data_len, void* ctx) {
int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
if (r == data_len) return true;
fprintf(stderr, "%s\n", strerror(errno));
return false;
}
// write_raw_image(file, partition)
char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* partition;
char* filename;
if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
return NULL;
}
if (strlen(partition) == 0) {
ErrorAbort(state, "partition argument to %s can't be empty", name);
goto done;
}
if (strlen(filename) == 0) {
ErrorAbort(state, "file argument to %s can't be empty", name);
goto done;
}
mtd_scan_partitions();
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
if (mtd == NULL) {
fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
result = strdup("");
goto done;
}
MtdWriteContext* ctx = mtd_write_partition(mtd);
if (ctx == NULL) {
fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
name, partition);
result = strdup("");
goto done;
}
bool success;
FILE* f = fopen(filename, "rb");
if (f == NULL) {
fprintf(stderr, "%s: can't open %s: %s\n",
name, filename, strerror(errno));
result = strdup("");
goto done;
}
success = true;
char* buffer = malloc(BUFSIZ);
int read;
while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
int wrote = mtd_write_data(ctx, buffer, read);
success = success && (wrote == read);
if (!success) {
fprintf(stderr, "mtd_write_data to %s failed: %s\n",
partition, strerror(errno));
}
}
free(buffer);
fclose(f);
if (mtd_erase_blocks(ctx, -1) == -1) {
fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
}
if (mtd_write_close(ctx) != 0) {
fprintf(stderr, "%s: error closing write of %s\n", name, partition);
}
printf("%s %s partition from %s\n",
success ? "wrote" : "failed to write", partition, filename);
result = success ? partition : strdup("");
done:
if (result != partition) free(partition);
free(filename);
return result;
}
// write_firmware_image(file, partition)
//
// partition is "radio" or "hboot"
// file is not used until after updater exits
//
// TODO: this should live in some HTC-specific library
char* WriteFirmwareImageFn(const char* name, State* state,
int argc, Expr* argv[]) {
char* result = NULL;
char* partition;
char* filename;
if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
return NULL;
}
if (strlen(partition) == 0) {
ErrorAbort(state, "partition argument to %s can't be empty", name);
goto done;
}
if (strlen(filename) == 0) {
ErrorAbort(state, "file argument to %s can't be empty", name);
goto done;
}
FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe;
fprintf(cmd, "firmware %s %s\n", partition, filename);
printf("will write %s firmware from %s\n", partition, filename);
result = partition;
done:
if (result != partition) free(partition);
free(filename);
return result;
}
extern int applypatch(int argc, char** argv);
// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...)
// apply_patch_check(file, sha1, ...)
// apply_patch_space(bytes)
char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
printf("in applypatchfn (%s)\n", name);
char* prepend = NULL;
if (strstr(name, "check") != NULL) {
prepend = "-c";
} else if (strstr(name, "space") != NULL) {
prepend = "-s";
}
char** args = ReadVarArgs(state, argc, argv);
if (args == NULL) return NULL;
// insert the "program name" argv[0] and a copy of the "prepend"
// string (if any) at the start of the args.
int extra = 1 + (prepend != NULL ? 1 : 0);
char** temp = malloc((argc+extra) * sizeof(char*));
memcpy(temp+extra, args, argc * sizeof(char*));
temp[0] = strdup("updater");
if (prepend) {
temp[1] = strdup(prepend);
}
free(args);
args = temp;
argc += extra;
printf("calling applypatch\n");
fflush(stdout);
int result = applypatch(argc, args);
printf("applypatch returned %d\n", result);
int i;
for (i = 0; i < argc; ++i) {
free(args[i]);
}
free(args);
switch (result) {
case 0: return strdup("t");
case 1: return strdup("");
default: return ErrorAbort(state, "applypatch couldn't parse args");
}
}
char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
char** args = ReadVarArgs(state, argc, argv);
if (args == NULL) {
return NULL;
}
int size = 0;
int i;
for (i = 0; i < argc; ++i) {
size += strlen(args[i]);
}
char* buffer = malloc(size+1);
size = 0;
for (i = 0; i < argc; ++i) {
strcpy(buffer+size, args[i]);
size += strlen(args[i]);
free(args[i]);
}
free(args);
buffer[size] = '\0';
char* line = strtok(buffer, "\n");
while (line) {
fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
"ui_print %s\n", line);
line = strtok(NULL, "\n");
}
fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
return buffer;
}
char* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 1) {
return ErrorAbort(state, "%s() expects at least 1 arg", name);
}
char** args = ReadVarArgs(state, argc, argv);
if (args == NULL) {
return NULL;
}
char** args2 = malloc(sizeof(char*) * (argc+1));
memcpy(args2, args, sizeof(char*) * argc);
args2[argc] = NULL;
fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
pid_t child = fork();
if (child == 0) {
execv(args2[0], args2);
fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
_exit(1);
}
int status;
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0) {
fprintf(stderr, "run_program: child exited with status %d\n",
WEXITSTATUS(status));
}
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "run_program: child terminated by signal %d\n",
WTERMSIG(status));
}
int i;
for (i = 0; i < argc; ++i) {
free(args[i]);
}
free(args);
free(args2);
char buffer[20];
sprintf(buffer, "%d", status);
return strdup(buffer);
}
void RegisterInstallFunctions() {
RegisterFunction("mount", MountFn);
RegisterFunction("is_mounted", IsMountedFn);
RegisterFunction("unmount", UnmountFn);
RegisterFunction("format", FormatFn);
RegisterFunction("show_progress", ShowProgressFn);
RegisterFunction("set_progress", SetProgressFn);
RegisterFunction("delete", DeleteFn);
RegisterFunction("delete_recursive", DeleteFn);
RegisterFunction("package_extract_dir", PackageExtractDirFn);
RegisterFunction("package_extract_file", PackageExtractFileFn);
RegisterFunction("symlink", SymlinkFn);
RegisterFunction("set_perm", SetPermFn);
RegisterFunction("set_perm_recursive", SetPermFn);
RegisterFunction("getprop", GetPropFn);
RegisterFunction("file_getprop", FileGetPropFn);
RegisterFunction("write_raw_image", WriteRawImageFn);
RegisterFunction("write_firmware_image", WriteFirmwareImageFn);
RegisterFunction("apply_patch", ApplyPatchFn);
RegisterFunction("apply_patch_check", ApplyPatchFn);
RegisterFunction("apply_patch_space", ApplyPatchFn);
RegisterFunction("ui_print", UIPrintFn);
RegisterFunction("run_program", RunProgramFn);
}

22
updater/install.h Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _UPDATER_INSTALL_H_
#define _UPDATER_INSTALL_H_
void RegisterInstallFunctions();
#endif

134
updater/updater.c Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "edify/expr.h"
#include "updater.h"
#include "install.h"
#include "minzip/Zip.h"
// Generated by the makefile, this function defines the
// RegisterDeviceExtensions() function, which calls all the
// registration functions for device-specific extensions.
#include "register.inc"
// Where in the package we expect to find the edify script to execute.
// (Note it's "updateR-script", not the older "update-script".)
#define SCRIPT_NAME "META-INF/com/google/android/updater-script"
int main(int argc, char** argv) {
if (argc != 4) {
fprintf(stderr, "unexpected number of arguments (%d)\n", argc);
return 1;
}
char* version = argv[1];
if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') {
// We support version "1" or "2".
fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n",
argv[1]);
return 2;
}
// Set up the pipe for sending commands back to the parent process.
int fd = atoi(argv[2]);
FILE* cmd_pipe = fdopen(fd, "wb");
setlinebuf(cmd_pipe);
// Extract the script from the package.
char* package_data = argv[3];
ZipArchive za;
int err;
err = mzOpenZipArchive(package_data, &za);
if (err != 0) {
fprintf(stderr, "failed to open package %s: %s\n",
package_data, strerror(err));
return 3;
}
const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
if (script_entry == NULL) {
fprintf(stderr, "failed to find %s in %s\n", SCRIPT_NAME, package_data);
return 4;
}
char* script = malloc(script_entry->uncompLen+1);
if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) {
fprintf(stderr, "failed to read script from package\n");
return 5;
}
script[script_entry->uncompLen] = '\0';
// Configure edify's functions.
RegisterBuiltins();
RegisterInstallFunctions();
RegisterDeviceExtensions();
FinishRegistration();
// Parse the script.
Expr* root;
int error_count = 0;
yy_scan_string(script);
int error = yyparse(&root, &error_count);
if (error != 0 || error_count > 0) {
fprintf(stderr, "%d parse errors\n", error_count);
return 6;
}
// Evaluate the parsed script.
UpdaterInfo updater_info;
updater_info.cmd_pipe = cmd_pipe;
updater_info.package_zip = &za;
State state;
state.cookie = &updater_info;
state.script = script;
state.errmsg = NULL;
char* result = Evaluate(&state, root);
if (result == NULL) {
if (state.errmsg == NULL) {
fprintf(stderr, "script aborted (no error message)\n");
fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
} else {
fprintf(stderr, "script aborted: %s\n", state.errmsg);
char* line = strtok(state.errmsg, "\n");
while (line) {
fprintf(cmd_pipe, "ui_print %s\n", line);
line = strtok(NULL, "\n");
}
fprintf(cmd_pipe, "ui_print\n");
}
free(state.errmsg);
return 7;
} else {
fprintf(stderr, "script result was [%s]\n", result);
free(result);
}
mzCloseZipArchive(&za);
free(script);
return 0;
}

28
updater/updater.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _UPDATER_UPDATER_H_
#define _UPDATER_UPDATER_H_
#include <stdio.h>
#include "minzip/Zip.h"
typedef struct {
FILE* cmd_pipe;
ZipArchive* package_zip;
} UpdaterInfo;
#endif

45
utilities/Android.mk Normal file
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.

476
utilities/fix_permissions Normal file
View File

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

BIN
utilities/parted Normal file

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.

View File

@ -17,345 +17,168 @@
#include "common.h"
#include "verifier.h"
#include "minzip/Zip.h"
#include "mincrypt/rsa.h"
#include "mincrypt/sha.h"
#include <netinet/in.h> /* required for resolv.h */
#include <resolv.h> /* for base64 codec */
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Return an allocated buffer with the contents of a zip file entry. */
static char *slurpEntry(const ZipArchive *pArchive, const ZipEntry *pEntry) {
if (!mzIsZipEntryIntact(pArchive, pEntry)) {
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
LOGE("Invalid %.*s\n", fn.len, fn.str);
return NULL;
// Look for an RSA signature embedded in the .ZIP file comment given
// the path to the zip. Verify it matches one of the given public
// keys.
//
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
// or no key matches the signature).
int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) {
ui_set_progress(0.0);
FILE* f = fopen(path, "rb");
if (f == NULL) {
LOGE("failed to open %s (%s)\n", path, strerror(errno));
return VERIFY_FAILURE;
}
int len = mzGetZipEntryUncompLen(pEntry);
char *buf = malloc(len + 1);
if (buf == NULL) {
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
LOGE("Can't allocate %d bytes for %.*s\n", len, fn.len, fn.str);
return NULL;
// An archive with a whole-file signature will end in six bytes:
//
// $ff $ff (2-byte comment size) (2-byte signature start)
//
// (As far as the ZIP format is concerned, these are part of the
// archive comment.) We start by reading this footer, this tells
// us how far back from the end we have to start reading to find
// the whole comment.
#define FOOTER_SIZE 6
if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
fclose(f);
return VERIFY_FAILURE;
}
if (!mzReadZipEntry(pArchive, pEntry, buf, len)) {
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
LOGE("Can't read %.*s\n", fn.len, fn.str);
free(buf);
return NULL;
unsigned char footer[FOOTER_SIZE];
if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
fclose(f);
return VERIFY_FAILURE;
}
buf[len] = '\0';
return buf;
}
struct DigestContext {
SHA_CTX digest;
unsigned *doneBytes;
unsigned totalBytes;
};
/* mzProcessZipEntryContents callback to update an SHA-1 hash context. */
static bool updateHash(const unsigned char *data, int dataLen, void *cookie) {
struct DigestContext *context = (struct DigestContext *) cookie;
SHA_update(&context->digest, data, dataLen);
if (context->doneBytes != NULL) {
*context->doneBytes += dataLen;
if (context->totalBytes > 0) {
ui_set_progress(*context->doneBytes * 1.0 / context->totalBytes);
}
}
return true;
}
/* Get the SHA-1 digest of a zip file entry. */
static bool digestEntry(const ZipArchive *pArchive, const ZipEntry *pEntry,
unsigned *doneBytes, unsigned totalBytes,
uint8_t digest[SHA_DIGEST_SIZE]) {
struct DigestContext context;
SHA_init(&context.digest);
context.doneBytes = doneBytes;
context.totalBytes = totalBytes;
if (!mzProcessZipEntryContents(pArchive, pEntry, updateHash, &context)) {
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
LOGE("Can't digest %.*s\n", fn.len, fn.str);
return false;
if (footer[2] != 0xff || footer[3] != 0xff) {
fclose(f);
return VERIFY_FAILURE;
}
memcpy(digest, SHA_final(&context.digest), SHA_DIGEST_SIZE);
int comment_size = footer[4] + (footer[5] << 8);
int signature_start = footer[0] + (footer[1] << 8);
LOGI("comment is %d bytes; signature %d bytes from end\n",
comment_size, signature_start);
#ifdef LOG_VERBOSE
UnterminatedString fn = mzGetZipEntryFileName(pEntry);
char base64[SHA_DIGEST_SIZE * 3];
b64_ntop(digest, SHA_DIGEST_SIZE, base64, sizeof(base64));
LOGV("sha1(%.*s) = %s\n", fn.len, fn.str, base64);
#endif
if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
// "signature" block isn't big enough to contain an RSA block.
LOGE("signature is too short\n");
fclose(f);
return VERIFY_FAILURE;
}
return true;
}
#define EOCD_HEADER_SIZE 22
// The end-of-central-directory record is 22 bytes plus any
// comment length.
size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
/* Find a /META-INF/xxx.SF signature file signed by a matching xxx.RSA file. */
static const ZipEntry *verifySignature(const ZipArchive *pArchive,
const RSAPublicKey *pKeys, unsigned int numKeys) {
static const char prefix[] = "META-INF/";
static const char rsa[] = ".RSA", sf[] = ".SF";
if (fseek(f, -eocd_size, SEEK_END) != 0) {
LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
fclose(f);
return VERIFY_FAILURE;
}
unsigned int i, j;
for (i = 0; i < mzZipEntryCount(pArchive); ++i) {
const ZipEntry *rsaEntry = mzGetZipEntryAt(pArchive, i);
UnterminatedString rsaName = mzGetZipEntryFileName(rsaEntry);
int rsaLen = mzGetZipEntryUncompLen(rsaEntry);
if (rsaLen >= RSANUMBYTES && rsaName.len > sizeof(prefix) &&
!strncmp(rsaName.str, prefix, sizeof(prefix) - 1) &&
!strncmp(rsaName.str + rsaName.len - sizeof(rsa) + 1,
rsa, sizeof(rsa) - 1)) {
char *sfName = malloc(rsaName.len - sizeof(rsa) + sizeof(sf) + 1);
if (sfName == NULL) {
LOGE("Can't allocate %d bytes for filename\n", rsaName.len);
continue;
}
// Determine how much of the file is covered by the signature.
// This is everything except the signature data and length, which
// includes all of the EOCD except for the comment length field (2
// bytes) and the comment data.
size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
/* Replace .RSA with .SF */
strncpy(sfName, rsaName.str, rsaName.len - sizeof(rsa) + 1);
strcpy(sfName + rsaName.len - sizeof(rsa) + 1, sf);
const ZipEntry *sfEntry = mzFindZipEntry(pArchive, sfName);
unsigned char* eocd = malloc(eocd_size);
if (eocd == NULL) {
LOGE("malloc for EOCD record failed\n");
fclose(f);
return VERIFY_FAILURE;
}
if (fread(eocd, 1, eocd_size, f) != eocd_size) {
LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
fclose(f);
return VERIFY_FAILURE;
}
if (sfEntry == NULL) {
LOGW("Missing signature file %s\n", sfName);
free(sfName);
continue;
}
// If this is really is the EOCD record, it will begin with the
// magic number $50 $4b $05 $06.
if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
eocd[2] != 0x05 || eocd[3] != 0x06) {
LOGE("signature length doesn't match EOCD marker\n");
fclose(f);
return VERIFY_FAILURE;
}
free(sfName);
uint8_t sfDigest[SHA_DIGEST_SIZE];
if (!digestEntry(pArchive, sfEntry, NULL, 0, sfDigest)) continue;
char *rsaBuf = slurpEntry(pArchive, rsaEntry);
if (rsaBuf == NULL) continue;
/* Try to verify the signature with all the keys. */
uint8_t *sig = (uint8_t *) rsaBuf + rsaLen - RSANUMBYTES;
for (j = 0; j < numKeys; ++j) {
if (RSA_verify(&pKeys[j], sig, RSANUMBYTES, sfDigest)) {
free(rsaBuf);
LOGI("Verified %.*s\n", rsaName.len, rsaName.str);
return sfEntry;
}
}
free(rsaBuf);
LOGW("Can't verify %.*s\n", rsaName.len, rsaName.str);
int i;
for (i = 4; i < eocd_size-3; ++i) {
if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b &&
eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
// if the sequence $50 $4b $05 $06 appears anywhere after
// the real one, minzip will find the later (wrong) one,
// which could be exploitable. Fail verification if
// this sequence occurs anywhere after the real one.
LOGE("EOCD marker occurs after start of EOCD\n");
fclose(f);
return VERIFY_FAILURE;
}
}
LOGE("No signature (%d files)\n", mzZipEntryCount(pArchive));
return NULL;
}
#define BUFFER_SIZE 4096
SHA_CTX ctx;
SHA_init(&ctx);
unsigned char* buffer = malloc(BUFFER_SIZE);
if (buffer == NULL) {
LOGE("failed to alloc memory for sha1 buffer\n");
fclose(f);
return VERIFY_FAILURE;
}
/* Verify /META-INF/MANIFEST.MF against the digest in a signature file. */
static const ZipEntry *verifyManifest(const ZipArchive *pArchive,
const ZipEntry *sfEntry) {
static const char prefix[] = "SHA1-Digest-Manifest: ", eol[] = "\r\n";
uint8_t expected[SHA_DIGEST_SIZE + 3], actual[SHA_DIGEST_SIZE];
char *sfBuf = slurpEntry(pArchive, sfEntry);
if (sfBuf == NULL) return NULL;
char *line, *save;
for (line = strtok_r(sfBuf, eol, &save); line != NULL;
line = strtok_r(NULL, eol, &save)) {
if (!strncasecmp(prefix, line, sizeof(prefix) - 1)) {
UnterminatedString fn = mzGetZipEntryFileName(sfEntry);
const char *digest = line + sizeof(prefix) - 1;
int n = b64_pton(digest, expected, sizeof(expected));
if (n != SHA_DIGEST_SIZE) {
LOGE("Invalid base64 in %.*s: %s (%d)\n",
fn.len, fn.str, digest, n);
line = NULL;
}
break;
double frac = -1.0;
size_t so_far = 0;
fseek(f, 0, SEEK_SET);
while (so_far < signed_len) {
int size = BUFFER_SIZE;
if (signed_len - so_far < size) size = signed_len - so_far;
if (fread(buffer, 1, size, f) != size) {
LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
fclose(f);
return VERIFY_FAILURE;
}
SHA_update(&ctx, buffer, size);
so_far += size;
double f = so_far / (double)signed_len;
if (f > frac + 0.02 || size == so_far) {
ui_set_progress(f);
frac = f;
}
}
fclose(f);
free(buffer);
free(sfBuf);
if (line == NULL) {
LOGE("No digest manifest in signature file\n");
return false;
}
const char *mfName = "META-INF/MANIFEST.MF";
const ZipEntry *mfEntry = mzFindZipEntry(pArchive, mfName);
if (mfEntry == NULL) {
LOGE("No manifest file %s\n", mfName);
return NULL;
}
if (!digestEntry(pArchive, mfEntry, NULL, 0, actual)) return NULL;
if (memcmp(expected, actual, SHA_DIGEST_SIZE)) {
UnterminatedString fn = mzGetZipEntryFileName(sfEntry);
LOGE("Wrong digest for %s in %.*s\n", mfName, fn.len, fn.str);
return NULL;
}
LOGI("Verified %s\n", mfName);
return mfEntry;
}
/* Verify all the files in a Zip archive against the manifest. */
static bool verifyArchive(const ZipArchive *pArchive, const ZipEntry *mfEntry) {
static const char namePrefix[] = "Name: ";
static const char contPrefix[] = " "; // Continuation of the filename
static const char digestPrefix[] = "SHA1-Digest: ";
static const char eol[] = "\r\n";
char *mfBuf = slurpEntry(pArchive, mfEntry);
if (mfBuf == NULL) return false;
/* we're using calloc() here, so the initial state of the array is false */
bool *unverified = (bool *) calloc(mzZipEntryCount(pArchive), sizeof(bool));
if (unverified == NULL) {
LOGE("Can't allocate valid flags\n");
free(mfBuf);
return false;
}
/* Mark all the files in the archive that need to be verified.
* As we scan the manifest and check signatures, we'll unset these flags.
* At the end, we'll make sure that all the flags are unset.
*/
unsigned i, totalBytes = 0;
for (i = 0; i < mzZipEntryCount(pArchive); ++i) {
const ZipEntry *entry = mzGetZipEntryAt(pArchive, i);
UnterminatedString fn = mzGetZipEntryFileName(entry);
int len = mzGetZipEntryUncompLen(entry);
// Don't validate: directories, the manifest, *.RSA, and *.SF.
if (entry == mfEntry) {
LOGV("Skipping manifest %.*s\n", fn.len, fn.str);
} else if (fn.len > 0 && fn.str[fn.len-1] == '/' && len == 0) {
LOGV("Skipping directory %.*s\n", fn.len, fn.str);
} else if (!strncasecmp(fn.str, "META-INF/", 9) && (
!strncasecmp(fn.str + fn.len - 4, ".RSA", 4) ||
!strncasecmp(fn.str + fn.len - 3, ".SF", 3))) {
LOGV("Skipping signature %.*s\n", fn.len, fn.str);
} else {
unverified[i] = true;
totalBytes += len;
const uint8_t* sha1 = SHA_final(&ctx);
for (i = 0; i < numKeys; ++i) {
// The 6 bytes is the "$ff $ff (signature_start) (comment_size)" that
// the signing tool appends after the signature itself.
if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
RSANUMBYTES, sha1)) {
LOGI("whole-file signature verified\n");
free(eocd);
return VERIFY_SUCCESS;
}
}
unsigned doneBytes = 0;
char *line, *save, *name = NULL;
for (line = strtok_r(mfBuf, eol, &save); line != NULL;
line = strtok_r(NULL, eol, &save)) {
if (!strncasecmp(line, namePrefix, sizeof(namePrefix) - 1)) {
// "Name:" introducing a new stanza
if (name != NULL) {
LOGE("No digest:\n %s\n", name);
break;
}
name = strdup(line + sizeof(namePrefix) - 1);
if (name == NULL) {
LOGE("Can't copy filename in %s\n", line);
break;
}
} else if (!strncasecmp(line, contPrefix, sizeof(contPrefix) - 1)) {
// Continuing a long name (nothing else should be continued)
const char *tail = line + sizeof(contPrefix) - 1;
if (name == NULL) {
LOGE("Unexpected continuation:\n %s\n", tail);
}
char *concat;
if (asprintf(&concat, "%s%s", name, tail) < 0) {
LOGE("Can't append continuation %s\n", tail);
break;
}
free(name);
name = concat;
} else if (!strncasecmp(line, digestPrefix, sizeof(digestPrefix) - 1)) {
// "Digest:" supplying a hash code for the current stanza
const char *base64 = line + sizeof(digestPrefix) - 1;
if (name == NULL) {
LOGE("Unexpected digest:\n %s\n", base64);
break;
}
const ZipEntry *entry = mzFindZipEntry(pArchive, name);
if (entry == NULL) {
LOGE("Missing file:\n %s\n", name);
break;
}
if (!mzIsZipEntryIntact(pArchive, entry)) {
LOGE("Corrupt file:\n %s\n", name);
break;
}
if (!unverified[mzGetZipEntryIndex(pArchive, entry)]) {
LOGE("Unexpected file:\n %s\n", name);
break;
}
uint8_t expected[SHA_DIGEST_SIZE + 3], actual[SHA_DIGEST_SIZE];
int n = b64_pton(base64, expected, sizeof(expected));
if (n != SHA_DIGEST_SIZE) {
LOGE("Invalid base64:\n %s\n %s\n", name, base64);
break;
}
if (!digestEntry(pArchive, entry, &doneBytes, totalBytes, actual) ||
memcmp(expected, actual, SHA_DIGEST_SIZE) != 0) {
LOGE("Wrong digest:\n %s\n", name);
break;
}
LOGI("Verified %s\n", name);
unverified[mzGetZipEntryIndex(pArchive, entry)] = false;
free(name);
name = NULL;
}
}
if (name != NULL) free(name);
free(mfBuf);
for (i = 0; i < mzZipEntryCount(pArchive) && !unverified[i]; ++i) ;
free(unverified);
// This means we didn't get to the end of the manifest successfully.
if (line != NULL) return false;
if (i < mzZipEntryCount(pArchive)) {
const ZipEntry *entry = mzGetZipEntryAt(pArchive, i);
UnterminatedString fn = mzGetZipEntryFileName(entry);
LOGE("No digest for %.*s\n", fn.len, fn.str);
return false;
}
return true;
}
bool verify_jar_signature(const ZipArchive *pArchive,
const RSAPublicKey *pKeys, int numKeys) {
const ZipEntry *sfEntry = verifySignature(pArchive, pKeys, numKeys);
if (sfEntry == NULL) return false;
const ZipEntry *mfEntry = verifyManifest(pArchive, sfEntry);
if (mfEntry == NULL) return false;
return verifyArchive(pArchive, mfEntry);
free(eocd);
LOGE("failed to verify whole-file signature\n");
return VERIFY_FAILURE;
}

View File

@ -17,14 +17,14 @@
#ifndef _RECOVERY_VERIFIER_H
#define _RECOVERY_VERIFIER_H
#include "minzip/Zip.h"
#include "mincrypt/rsa.h"
/*
* Check the digital signature (as applied by jarsigner) on a Zip archive.
* Every file in the archive must be signed by one of the supplied RSA keys.
/* Look in the file for a signature footer, and verify that it
* matches one of the given keys. Return one of the constants below.
*/
bool verify_jar_signature(const ZipArchive *pArchive,
const RSAPublicKey *pKeys, int numKeys);
int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys);
#define VERIFY_SUCCESS 0
#define VERIFY_FAILURE 1
#endif /* _RECOVERY_VERIFIER_H */